




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第第页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. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 装配式行业追溯软件
- 安徽省定远县育才中学2024-2025学年高三第一次综合测试数学试题试卷含解析
- 大连工业大学《建筑设计1》2023-2024学年第二学期期末试卷
- 辽宁省辽阳市太子河区2025届数学四年级第二学期期末经典试题含解析
- 江西航空职业技术学院《工程数学概率论》2023-2024学年第一学期期末试卷
- 北京信息科技大学《工程管理导论》2023-2024学年第二学期期末试卷
- 长春金融高等专科学校《钢结构设计与施工》2023-2024学年第二学期期末试卷
- 吉林省四平一中等2024-2025学年高三下期末考试英语试题(B卷)含解析
- 2025年哈三中高三下学期开学考试数学试题文试题含解析
- 江苏省无锡市宜兴市周铁区市级名校2024-2025学年初三中考模拟冲刺卷(提优卷)(四)化学试题含解析
- 基于SWOT分析的义乌市现代物流业发展研究
- 基于自适应滤波对音频信号的处理详解
- 油浸式变压器工艺文件汇编
- 并网前设备电气试验继电保护整定通讯联调完整资料
- 南方科技大学机试样题练习南方科技大学样卷
- 电子公章模板
- 北京广安门中医院门诊楼层分布图
- 法定代表人登记表
- 钻孔灌注桩施工组织设计
- 足球比赛登记表
- 《三字经》全文打印版
评论
0/150
提交评论