`
tudusi
  • 浏览: 1057327 次
文章分类
社区版块
存档分类
最新评论

Linux主机驱动与外设驱动分离思想

 
阅读更多
- by 宋宝华(Barry Song)
1主机、外设驱动分离的意义

在Linux设备驱动框架的设计中,除了有分层设计实现以外,还有分隔的思想。举一个简单的例子,假设我们要通过SPI总线访问某外设,在这个访问过程中,要通过操作CPU XXX上的SPI控制器的寄存器来达到访问SPI外设YYY的目的,最简单的方法是:

return_type xxx_write_spi_yyy(...)

{

xxx_write_spi_host_ctrl_reg(ctrl);

xxx_ write_spi_host_data_reg(buf);

while(!(xxx_spi_host_status_reg()&SPI_DATA_TRANSFER_DONE));

...

}

如果按照这种方式来设计驱动,结果是对于任何一个SPI外设来讲,它的驱动代码都是CPU相关的。也就是说,当然用在CPU XXX上的时候,它访问XXX的SPI主机控制寄存器,当用在XXX1的时候,它访问XXX1的SPI主机控制寄存器:

return_type xxx1_write_spi_yyy(...)

{

xxx1_write_spi_host_ctrl_reg(ctrl);

xxx1_ write_spi_host_data_reg(buf);

while(!(xxx1_spi_host_status_reg()&SPI_DATA_TRANSFER_DONE));

...

}

这显然是不能接受的,因为这意味着外设YYY用在不同的CPU XXX和XXX1上的时候需要不同的驱动。那么,我们可以用如图12.4的思想对主机控制器驱动和外设驱动进行分离。这样的结构是,外设a、b、c的驱动与主机控制器A、B、C的驱动不相关,主机控制器驱动不关心外设,而外设驱动也不关心主机,外设只是访问核心层的通用的API进行数据传输,主机和外设之间可以进行任意的组合。

clip_image001

图12.4 Linux设备驱动的主机、外设驱动分离

如果我们不进行如图12.4的主机和外设分离,外设a、b、c和主机A、B、C进行组合的时候,需要9个不同的驱动。设想一共有m个主机控制器,n个外设,分离的结果是需要m+n个驱动,不分离则需要m*n个驱动。

Linux SPI、I2C、USB、ASoC(ALSA SoC)等子系统都典型地利用了这种分离的设计思想,在本章我们先以简单一些的SPI为例,而I2C、USB、ASoC等则在后续章节会进行详细介绍。

2 Linux SPI主机和设备驱动

SPI(同步外设接口)是由摩托罗拉公司开发的全双工同步串行总线,其接口由MISO(串行数据输入),MOSI(串行数据输出),SCK(串行移位时钟),SS(从使能信号)四种信号构成,SS决定了唯一的与主设备通信的从设备,主设备通过产生移位时钟来发起通讯。通讯时,数据由MOSI输出,MISO输入,数据在时钟的上升或下降沿由MOSI输出,在紧接着的下降或上升沿由MISO读入,这样经过8/16次时钟的改变,完成8/16位数据的传输。

SPI模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性(CPOL)和相位(CPHA)可以进行配置。如果 CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI接口时序如图12.5所示。

clip_image003

图12.5 SPI总线时序

在Linux中,用代码清单12.12的spi_master结构体来描述一个SPI主机控制器驱动,其主要成员是主机控制器的序号(系统中可能存在多个SPI主机控制器)、片选数量、SPI模式和时钟设置用到的函数、数据传输用到的函数等。

代码清单12.12 spi_master结构体

1 struct spi_master {

2 struct device dev;

3 s16 bus_num;

4 u16 num_chipselect;

5

6 /* 设置模式和时钟 */

7 int (*setup)(struct spi_device *spi);

8

9 /* 双向数据传输 */

10 int (*transfer)(struct spi_device *spi,

11 struct spi_message *mesg);

12

13 void (*cleanup)(struct spi_device *spi);

14 };

分配、注册和注销SPI主机的API由SPI核心提供:

struct spi_master * spi_alloc_master(struct device *host, unsigned size);

int spi_register_master(struct spi_master *master);

void spi_unregister_master(struct spi_master *master);

在Linux中,用代码清单12.13的spi_driver结构体来描述一个SPI外设驱动,可以认为是spi_master的client驱动。

代码清单12.13 spi_driver结构体

1 struct spi_driver {

2 int (*probe)(struct spi_device *spi);

3 int (*remove)(struct spi_device *spi);

4 void (*shutdown)(struct spi_device *spi);

5 int (*suspend)(struct spi_device *spi, pm_message_t mesg);

6 int (*resume)(struct spi_device *spi);

7 struct device_driver driver;

8 };

可以看出,spi_driver结构体和platform_driver结构体有极大的相似性,都有probe()、remove()、suspend()、resume()这样的接口。是的,这几乎是一切client驱动的习惯模板。

在SPI外设驱动中,当透过SPI总线进行数据传输的时候,使用了一套与CPU无关的统一的接口。这套接口的第1个关键数据结构就是spi_transfer,它用于描述SPI传输,如代码清单12.14。

代码清单12.14 spi_transfer结构体

1 struct spi_transfer {

2 const void *tx_buf;

3 void *rx_buf;

4 unsigned len;

5

6 dma_addr_t tx_dma;

7 dma_addr_t rx_dma;

8

9 unsigned cs_change:1;

10 u8 bits_per_word;

11 u16 delay_usecs;

12 u32 speed_hz;

13

14 struct list_head transfer_list;

15 };

而一次完整的SPI传输流程可能不只包含1次spi_transfer,它可能包含1个或多个spi_transfer,这些spi_transfer最终通过spi_message组织在一起,其定义如代码清单12.15。

代码清单12.15 spi_message结构体

1 struct spi_message {

2 struct list_head transfers;

3

4 struct spi_device *spi;

5

6 unsigned is_dma_mapped:1;

7

8 /* 完成被一个callback报告 */

9 void (*complete)(void *context);

10 void *context;

11 unsigned actual_length;

12 int status;

13

14 struct list_head queue;

15 void *state;

16 };

通过spi_message_init()可以初始化spi_message,而将spi_transfer添加到spi_message队列的方法则是:

void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m);

发起一次spi_message的传输有同步和异步两种方式,使用同步API时,会阻塞等待这个消息被处理完。同步操作时使用的API是:

int spi_sync(struct spi_device *spi, struct spi_message *message);

使用异步API时,不会阻塞等待这个消息被处理完,但是可以在spi_message的complete字段挂接一个回调函数,当消息被处理完成后,该函数会被调用。异步操作时使用的API是:

int spi_async(struct spi_device *spi, struct spi_message *message);

代码清单12.16是非常典型的初始化spi_transfer、spi_message并进行SPI数据传输的例子,同时它们也是SPI核心层的2个通用API,在SPI外设驱动中可以直接调用它们进行写和读操作。

代码清单12.16 SPI传输实例spi_write()、spi_read() API

1 static inline int

2 spi_write(struct spi_device *spi, const u8 *buf, size_t len)

3 {

4 struct spi_transfer t = {

5 .tx_buf = buf,

6 .len = len,

7 };

8 struct spi_message m;

9

10 spi_message_init(&m);

11 spi_message_add_tail(&t, &m);

12 return spi_sync(spi, &m);

13 }

14

15 static inline int

16 spi_read(struct spi_device *spi, u8 *buf, size_t len)

17 {

18 struct spi_transfer t = {

19 .rx_buf = buf,

20 .len = len,

21 };

22 struct spi_message m;

23

24 spi_message_init(&m);

25 spi_message_add_tail(&t, &m);

26 return spi_sync(spi, &m);

27 }

LDD6410开发板所使用的S3C6410的SPI主机控制器驱动位于drivers/spi/spi_s3c.h和drivers/spi/spi_s3c.c这2个文件,其主体是实现了spi_master的setup()、transfer()等成员函数。

SPI外设驱动遍布于内核的drivers、sound的各个子目录之下,SPI只是一种总线,spi_driver的作用只是将SPI外设挂接在该总线上,因此在spi_driver的probe()成员函数中,将注册SPI外设本身所属设备驱动的类型。

和platform_driver对应着一个platform_device一样,spi_driver也对应着一个spi_device;platform_device需要在BSP的板文件中添加板信息数据,而spi_device也同样需要。spi_device的板信息用spi_board_info结构体描述,该结构体记录SPI外设使用的主机控制器序号、片选序号、数据比特率、SPI传输模式(即CPOL、CPHA)等。如诺基亚770上2个SPI设备的板信息数据如代码清单12.17,位于板文件arch/arm/mach-omap1/board-nokia770.c。

代码清单12.17诺基亚770板文件中的spi_board_info

1 static struct spi_board_info nokia770_spi_board_info[] __initdata = {

2 [0] = {

3 .modalias = "lcd_mipid",

4 .bus_num = 2, /* 用到的SPI主机控制器序号 */

5 .chip_select = 3, /* 使用哪一号片选 */

6 .max_speed_hz = 12000000, /* SPI数据传输比特率 */

7 .platform_data = &nokia770_mipid_platform_data,

8 },

9 [1] = {

10 .modalias = "ads7846",

11 .bus_num = 2,

12 .chip_select = 0,

13 .max_speed_hz = 2500000,

14 .irq = OMAP_GPIO_IRQ(15),

15 .platform_data = &nokia770_ads7846_platform_data,

16 },

17 };

在Linux启动过程中,在机器的init_machine()函数中,会通过如下语句注册这些spi_board_info:

spi_register_board_info(nokia770_spi_board_info,

ARRAY_SIZE(nokia770_spi_board_info));

这一点和启动时通过platform_add_devices()添加platform_device非常相似。

分享到:
评论

相关推荐

    S3C2410Linux下各外设驱动程序

    包含Linux2.4内核源代码,以及S3C2410Linux下个外设驱动程序,包括串口、SPI、IIC

    Linux常见驱动源码分析(kernel hacker修炼之道全集)--李万鹏

    Linux驱动修炼之道-RTC子系统框架与源码分析.pdf Linux驱动修炼之道-platform.pdf Linux驱动修炼之道-LCD背光与gpio控制.pdf Linux驱动修炼之道-INPUT子系统(下).pdf Linux驱动修炼之道-INPUT子系统(上...

    linux设备驱动详解视频(宋宝华)百度网盘下载

    宋宝华视频讲解linux驱动开发,下载文件为百度网盘地址,输入密码获取视频

    嵌入式Linux的USB驱动设计.pdf

    Linux中USB设备驱动程序框架包括USB设备驱动程序、USB主机驱动程序和USB核心驱动程序三个部分。USB设备驱动程序是负责管理USB设备的驱动程序,USB主机驱动程序是负责管理USB主机的驱动程序,而USB核心驱动程序是负责...

    linux SPI设备注册和驱动小结

    SPI协议数据传输具体的逻辑,详细介绍SPI总线架构! 知识回顾:Linux主机驱动和外设驱动分离思想

    基于IP的Linux外设驱动生成.pdf

    "基于IP的Linux外设驱动生成" 本文主要介绍了基于IP的Linux外设驱动生成方法,该方法可以提高Linux操作系统驱动程序的开发效率和可复用性。在SOPC(System On a Programmable Chip)系统中,IP组件可以由供应商提供...

    嵌入式Linux下SPI总线驱动程序设计.pdf

    该总线对内对外隐藏了对端的不确定性,使得外设驱动与主机控制器驱动不相关,实现主机和外设任意组合的功能。 SPI虚拟总线结构是在Linux非总线结构的驱动中,用户应用程序访问SPI物理设备需要经过内核空间的相关...

    Linux 蓝牙协议栈的USB+设备驱动

    给出实现蓝牙设备驱动的重要数据结构和流程,并总结Linux 下开发蓝牙USB 设备驱动的一般方法和关键技术。 关键词:Linux 系统;蓝牙协议栈;设备驱动 USB Device Driver for Linux Bluetooth Stack LIANG Jun-xue...

    基于zynq平台linux外设驱动分析-内核部分

    首先声明文档是自己原创的,通过刻苦阅读linux源代码和翻阅xilinx zynq datasheet写出来的总结文档,对于想了解基于zynq的linux外设驱动的童鞋,有一定帮助,由于是呕心原创,所以分数较高,望见谅

    Linux的键盘驱动与Qt_E的键盘映射.pdf

    "Linux 键盘驱动与 Qt/E 键盘映射" Linux 操作系统作为一个开源的操作系统,广泛应用于嵌入式系统、消费类电子、工业控制、航空航天、汽车电子、医疗保健、网络通信等领域。嵌入式系统由硬件平台和软件平台两部分...

    基于嵌入式Linux系统的驱动程序设计.pdf

    Linux与嵌入式处理器的结合正日益被人们看好,Linux具有内核小、效率高、源代码开放等优点,还内含了完整的TCP/IP网络协议,很适合作为嵌入式操作系统使用。 嵌入式Linux操作系统与外设通信是通过设备驱动程序来...

    嵌入式Linux中NAND Flash设备驱动研究.pdf

    "嵌入式Linux中NAND Flash设备驱动研究" 本文主要研究了嵌入式Linux中NAND Flash设备驱动的设计和实现。NAND Flash是一种非易失性存储介质,具有速度快、体积小、存储密度高和容量大的优点,适用于大量数据的存储。...

    Stellaris外设驱动库

    Stellaris外设驱动库 Stellaris外设驱动库 Stellaris外设驱动库

    Linux下USB设备驱动程序的编写.pdf

    Linux 下 USB 设备驱动程序的编写 本文主要介绍了 Linux 下 USB 设备驱动程序的编写,包括驱动程序的结构、编写方法和实现细节。同时,还介绍了 Linux 操作系统的发展历程、USB 设备的特点和驱动程序的重要性。 ...

    基于IP的Linux外设驱动生成

    基于IP的Linux外设驱动生成,一篇论文。讲解的不是很详细,但是主要的部分已经讲的很到位了

    基于netlink的linux服务器集群统一外设事件监听机制.pdf

    基于netlink的linux服务器集群统一外设事件监听机制.pdf

    Linux系统PCI设备驱动程序开发.pdf

    1. PCI 局部总线规范的概念和特点:PCI 局部总线规范是一种替代 ISA 总线的技术规范,其目标是在计算机与外设之间提供性能更好的数据传输,使数据传输独立于 CPU 体系平台,取消硬件跳线和手工配置,实现即插即用的...

    linux下USB大容量存储设备驱动分析与实现.pdf

    Linux 下 USB 大容量存储设备驱动分析与实现 本文对 Linux 下 USB 大容量存储设备驱动进行了深入分析和实现,涵盖了 USB 子系统架构、USB 协议、USB 设备驱动、USB 主控制器驱动等知识点。通过对 Linux USB 子系统...

    SATA及ADMA控制器Linux操作系统驱动的设计与实现.pdf

    "SATA及ADMA控制器Linux操作系统驱动的设计与实现" 该论文设计和实现了一种新型存储外设的操作系统驱动,该存储系统采用SATA磁盘及ADMA控制器并基于Linux系统。该系统着重于如何驱动SATA磁盘及ADMA控制器,并实现了...

Global site tag (gtag.js) - Google Analytics