linux系统中驱动软件架构设计_第1页
linux系统中驱动软件架构设计_第2页
linux系统中驱动软件架构设计_第3页
linux系统中驱动软件架构设计_第4页
linux系统中驱动软件架构设计_第5页
已阅读5页,还剩9页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第第页linux系统中驱动软件架构设计大家好,今天分享一篇(Linux)驱动软件设计思想的文章。由于文章较长,可以先收藏后再慢慢看。

一、Linux驱动的软件架构

1.1出发点

为适应多种体系架构的(硬件),增强系统的可重用和跨平台能力。

1.2分离思想

为达到一个驱动最好一行都不改就可以适用任何硬件平台的目的,将驱动与设备分离开来,驱动只管驱动,设备只管设备,而驱动以某种通用的标准途径去拿板级信息,从而降低驱动与设备的(耦合)程度。

例如在

arch/arm/mach-at91/board-sam9261ek.c

板文件中,将

platform_data

定义了

dm9000_plat_data

结构体,完成定义后,将MAC地址、总线宽度、板上有无EEP(ROM)信息等放入:

static

struct

dm9000_plat_data

dm9000_platdata

=

{

.flags

=

DM9000_PLATF_16BITONLY

|

DM9000_PLATF_NO_EEPROM,};static

struct

platform_device

dm9000_device

=

{

.name

=

"dm9000",

.id

=

0,

.num_resources

=

ARRAY_SIZE(dm9000_resource),

.resource

=

dm9000_resource,

.dev

=

{

.platform_data

=

id_table)

return

platform_match_id(pdrv->id_table,

pdev)

!=

NULL;

/*

fall-back

to

driver

name

match

*/

return

(strcmp(pdev->name,

drv->name)

==

0);}

n_tty.c

n_tty.c:tty线路规程的工作是以特殊的方式格式化从一个用户或者硬件收到的数据,这种格式化常常采用一个协议转换的形式。

3.4.2tty设备的发送/接收流程

发送流程:tty核心从一个用户获取将要发送给一个tty设备的数据,tty核心将数据传递给tty线路规程驱动,接着数据被传递到tty驱动,tty驱动将数据转换为可以发送给硬件的格式。从

tty_driver

操作集

tty_operations

的成员函数write()函数接收3个参数:tty_struct、发送数据指针和发送的字节数。该函数是被

file_operations

的write()成员函数间接触发调用的。接收流程:从tty硬件接收到的数据向上交给tty驱动,接着进入tty线路规程驱动,再进入tty核心,在这里它被一个用户获取。tty驱动一般收到字符后会通过

tty_flip_buffer_push()

将接收缓冲区推到线路规程。

3.4.3串口核心层

尽管一个特定的底层UART设备驱动完全可以遵循上述

tty_driver

的方法来设计,即定义tty_driver并实现

tty_operations

中的成员函数,但是鉴于串口之间的共性,Linux考虑在文件

drivers/tty/serial/serial_core.c

中实现UART设备的通用tty驱动层(称为串口核心层)。这样,UART驱动的主要任务就进一步演变成了实现文件

serial_core.c中定义的一组uart_xxx接口,而不是tty_xxx接口。按照面向对象的思想,可认为tty_driver是字符设备的泛化、serial_core是tty_driver的泛化,而具体的串口驱动又是serial_core的泛化。在串口核心层又定义新的

uart_driver

结构体和其操作集

uart_ops。一个底层的UART驱动需要创建和通过

uart_register_driver()

注册一个

uart_driver

而不是

tty_driver。

struct

uart_driver

{

struct

module

*owner;

const

char

*driver_name;

const

char

*dev_name;

int

major;

int

minor;

int

nr;

struct

console

*cons;

/*

*

these

are

private;

the

low

level

driver

should

not

*

touch

these;

they

should

be

initialised

to

NULL

*/

struct

uart_state

*state;

struct

tty_driver

*tty_driver;};int

uart_register_driver(struct

uart_driver

*drv);void

uart_unregister_driver(struct

uart_driver

*drv);uart_driver结构体在本质上是派生自tty_driver结构体,因此,uart_driver结构体中包含tty_dirver结构体成员。tty_operations

在UART这个层面上也被进一步泛化为

uart_ops:struct

uart_ops

{

unsigned

int

(*tx_empty)(struct

uart_port

*);

void

(*set_mctrl)(struct

uart_port

*,

unsigned

int

mctrl);

unsigned

int

(*get_mctrl)(struct

uart_port

*);

void

(*stop_tx)(struct

uart_port

*);

void

(*start_tx)(struct

uart_port

*);

void

(*send_xchar)(struct

uart_port

*,

char

ch);

void

(*stop_rx)(struct

uart_port

*);

void

(*enable_ms)(struct

uart_port

*);

void

(*break_ctl)(struct

uart_port

*,

int

ctl);

int

(*startup)(struct

uart_port

*);

void

(*shutdown)(struct

uart_port

*);

void

(*flush_buffer)(struct

uart_port

*);

void

(*set_term(ios))(struct

uart_port

*,

struct

ktermios

*new,

struct

ktermios

*old);

void

(*set_ldisc)(struct

uart_port

*,

int

new);

void

(*pm)(struct

uart_port

*,

unsigned

int

state,

unsigned

int

oldstate);

int

(*set_wake)(struct

uart_port

*,

unsigned

int

state);

void

(*wake_peer)(struct

uart_port

*);

/*

*

Return

a

string

describing

the

type

of

the

port

*/

const

char

*(*type)(struct

uart_port

*);

/*

*

Release

IO

and

memory

resources

used

by

the

port.

*

This

includes

iounmap

if

necessary.

*/

void

(*release_port)(struct

uart_port

*);

/*

*

Request

IO

and

memory

resources

used

by

the

port.

*

This

includes

iomapping

the

port

if

necessary.

*/

int

(*request_port)(struct

uart_port

*);

void

(*config_port)(struct

uart_port

*,

int);

int

(*verify_port)(struct

uart_port

*,

struct

serial_struct

*);

int

(*ioctl)(struct

uart_port

*,

unsigned

int,

unsigned

long);#ifdef

CONFIG_CONSOLE_POLL

void

(*poll_put_char)(struct

uart_port

*,

unsigned

char);

int

(*poll_get_char)(struct

uart_port

*);#endif};由于

driver/tty/serial/serial_core.c

是一个

tty_driver

,因此在serial_core.c中,存在一个tty_operations的实例,这个实例的成员函数会进一步调用structuart_ops的成员函数,这样就把file_operaions里的成员函数、tty_operations的成员函数和uart_ops的成员函数串起来。

3.5misc设备驱动

3.6驱动核心层

核心层的3大职责:

对上提供接口。file_operations的读、写、ioctl都被中间层搞定,各种I/O模型也被处理掉了。

中间层实现通用逻辑。可以被底层各种实例共享的代码都被中间层搞定,避免底层重复实现。

对下定义框架。底层的驱动不再需要关心Linux内核VFS的接口和各种可能的I/O模型,而只需处理与具体硬件相关的访问。

这种分层有时候还不是两层,可以有更多层,在软件上呈现为面向对象里类继承和多态的状态。

四、主机驱动与外设驱动分离的设计思想

4.1主机驱动与外设驱动分离

Linux中的SPI、I2C、USB等子系统都是典型的利用主机驱动和外设驱动分离的思想。让主机端只负责产生总线上的传输波形,而外设端只是通过标准的API来让主机端以适当的波形访问自身。涉及4个软件模块:1.主机端的驱动。根据具体的SPI、I2C、USB等控制器的硬件手册,操作具体的控制器,产生总线的各种波形。2.连接主机和外设的纽带。外设不直接调用主机端的驱动来产生波形,而是调用一个标准的API。由这个标准的API把这个波形的传输请求间接“转发”给具体的主机端驱动。最好在这里把关于波形的描述也以某种数据结构标准化。3.外设端的驱动。外设接在SPI、I2C、USB这样的总线上,但是它们本身可以是触摸屏、网卡、声卡或任意一种类型的设备。当这些外设要求SPI、I2C、USB等去访问它的时候,它调用“连接主机和外设的纽带”模块的标准API。4.板级逻辑。用来描述主机和外设是如何互联的,它相当于一个“路由表”。假设板子上有多个SPI控制器和多个SPI外设,那究竟谁接在谁上面?管理互联关系,既不是主机端的责任,也不是外设端的责任,这属于板级逻辑的责任。linux通过上述设计方法,划分为4个轻量级的小模块,各个模块各司其职。

4.2LinuxSPI主机和设备驱动

4.2.1SPI主机驱动

在Linux中,通过

spi_master

结构体来描述一个SPI主动控制器驱动其主要成员由主机控制器的序号、片选数量、SPI模式、(时钟)设置相关函数和数据传输相关函数。文件spi/spi.h

struct

spi_master

{

struct

device

dev;

struct

list_head

list;

/*

other

than

negative

(==

assign

one

dynamically),

bus_num

is

fully

*

board-specific.

usually

that

(sim)plifies

to

being

SOC-specific.

*

example:

one

SOC

has

three

SPI

controllers,

numbered

0..2,

*

and

one

board's

schematics

might

show

it

using

SPI-2.

software

*

would

normally

use

bus_num=2

for

that

controller.

*/

s16

bus_num;

/*

chipselects

will

be

integral

to

many

controllers;

some

others

*

might

use

board-specific

GPIOs.

*/

u16

num_chipselect;

/*

some

SPI

controllers

pose

alignment

requirements

on

DMAable

*

buffers;

let

protocol

drivers

know

about

these

requirements.

*/

u16

dma_alignment;

/*

spi_device.mode

flags

understood

by

this

controller

driver

*/

u16

mode_bits;

/*

other

constraints

relevant

to

this

driver

*/

u16

flags;

#define

SPI_MASTER_HALF_DUPLEX

BIT(0)

/*

can't

do

full

duplex

*/

#define

SPI_MASTER_NO_RX

BIT(1)

/*

can't

do

buffer

read

*/

#define

SPI_MASTER_NO_TX

BIT(2)

/*

can't

do

buffer

write

*/

/*

lock

and

mutex

for

SPI

bus

locking

*/

spinlock_t

bus_lock_spinlock;

struct

mutex

bus_lock_mutex;

/*

flag

indicating

that

the

SPI

bus

is

locked

for

exclusive

use

*/

bool

bus_lock_flag;

/*

Setup

mode

and

clock,

etc

(spi

driver

may

call

many

times).

*

*

IMPORTANT:

this

may

be

called

when

transfers

to

another

*

device

are

active.

DO

NOT

UPDATE

SHARED

REGISTERS

in

ways

*

which

could

break

those

transfers.

*/

int

(*setup)(struct

spi_device

*spi);

/*

bidirectional

bulk

transfers

*

*

+

The

transfer()

method

may

not

sleep;

its

main

role

is

*

just

to

add

the

message

to

the

queue.

*

+

For

now

there's

no

remove-from-queue

operation,

or

*

any

other

request

management

*

+

To

a

given

spi_device,

message

queueing

is

pure

fifo

*

*

+

The

master's

main

job

is

to

process

its

message

queue,

*

selecting

a

chip

then

transferring

data

*

+

If

there

are

multiple

spi_device

children,

the

i/o

queue

*

arbitration

algorithm

is

unspecified

(round

robin,

fifo,

*

priority,

reservations,

preemption,

etc)

*

*

+

Chipselect

stays

active

during

the

entire

message

*

(unless

modified

by

spi_transfer.cs_change

!=

0).

*

+

The

message

transfers

use

clock

and

SPI

mode

parameters

*

previously

established

by

setup()

for

this

device

*/

int

(*transfer)(struct

spi_device

*spi,

struct

spi_message

*mesg);

/*

called

on

release()

to

free

memory

provided

by

spi_master

*/

void

(*cleanup)(struct

spi_device

*spi);};分配、注册和注销SPI主机的API由SPI核心提供:文件

drivers/spi/spi.cstruct

spi_master

*spi_alloc_master(struct

device

*dev,

unsigned

size);int

spi_register_master(struct

spi_master

*master);void

spi_unregister_master(struct

spi_master

*master);SPI主机控制器驱动主体是实现了

spi_master

的transfer()、setup()这样的成员函数。也可能实现

spi_bitbang

的txrx_buf()、setup_transfer()、chipselect()这样的成员函数。

例如在文件

driver/spi/spi_s3c24xx.c

中:

static

int

__init

s3c24xx_spi_probe(struct

platform_device

*pdev){

struct

s3c2410_spi_info

*pdata;

struct

s3c24xx_spi

*hw;

struct

spi_master

*master;

struct

resource

*res;

/*

initialise

fiq

handler

*/

s3c24xx_spi_initfiq(hw);

/*

setup

the

master

state.

*/

/*

the

spi->mode

bits

understood

by

this

driver:

*/

master->mode_bits

=

SPI_CPOL

|

SPI_CPHA

|

SPI_CS_HIGH;

//设置模式

master->num_chipselect

=

hw->pdata->num_cs;

//设置片选序号

master->bus_num

=

pdata->bus_num;

//主机控制器的序号

/*

setup

the

state

for

the

bitbang

driver

*/

hw->bitbang.master

=

hw->master;

hw->bitbang.setup_transfer

=

s3c24xx_spi_setupxfer;

hw->bitbang.chipselect

=

s3c24xx_spi_chipsel;

hw->bitbang.txrx_bufs

=

s3c24xx_spi_txrx;

hw->master->setup

=

s3c24xx_spi_setup;

hw->master->cleanup

=

s3c24xx_spi_cleanup;

}

4.2.2纽带

4.2.3SPI外设驱动

在Linux中,通过spi_driver结构体来描述一个SPI外设驱动,这个外设驱动可以认为是spi_mater的客户端驱动。SPI只是一种总线,spi_driver的作用只是将SPI外设挂接在该总线上,因此在spi_driver的probe()成员函数中,将注册SPI外设本身所属设备驱动的类型。文件

spi/spi.h

中:

struct

spi_driver

{

const

struct

spi_device_id

*id_table;

int

(*probe)(struct

spi_device

*spi);

int

(*remove)(struct

spi_device

*spi);

void

(*shutdown)(struct

spi_device

*spi);

int

(*suspend)(struct

spi_device

*spi,

pm_message_t

mesg);

int

(*resume)(struct

spi_device

*spi);

struct

device_driver

driver;};static

int

spi_drv_probe(struct

device

*dev){

const

struct

spi_driver

*(sd)rv

=

to_spi_driver(dev->driver);

return

sdrv->probe(to_spi_device(dev));}int

spi_register_driver(struct

spi_driver

*sdrv){

sdrv->driver.bus

=

if

(sdrv->probe)

sdrv->be

=

spi_drv_probe;

if

(sdrv->remove)

sdrv->driver.remove

=

spi_drv_remove;

if

(sdrv->shutdown)

sdrv->driver.shutdown

=

spi_drv_shutdown;

return

driver_register(}可看出,spi_driver结构体和platform_driver结构体有极大的相似性,都有prob()、remove()、suspend()、resume()这样的接口和device_driver的实例。(这几乎是一切客户端驱动的常用模板)

在SPI外设驱动中(文件

spi/spi.h

driver/spi/spi.c

):

1.

spi_tansfer

结构体:通过SPI总线进行数据传输的接口。2.

spi_message

结构体:组织一个或多个spi_transfer,从而完成一次完整的SPI传输流程。3.初始化

spi_message:

static

inline

void

spi_message_init(struct

spi_message

*m);4.将

spi_transfer

添加到

spi_message

队列:spi_message_add_tail(struct

spi_transfer

*t,

struct

spi_message

*m);5.

spi_message

的同步传输API,阻塞等待这个消息被处理完:spi_sync(struct

spi_device

*spi,

struct

spi_message

*message);6.

spi_message

的异步传输API,不会阻塞等待这个消息被处理完,但可在spi_message的

complete

字段挂接一个回调函数,当消息被处理完成后,该函数会被调用:spi_async(struct

spi_device

*spi,

struct

spi_message

*message);7.初始化

spi_transfer、spi_message

并进行SPI数据传输的例子,同时

spi_write()

spi_read()

也是SPI核心层的两个通用API,在外设驱动中可直接调用进行简单的纯写、纯读操作:static

inline

intspi_write(struct

spi_device

*spi,

const

void

*buf,

size_t

len){

struct

spi_transfer

t

=

{

.tx_buf

=

buf,

.len

=

len,

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论