- 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进行数据传输,主机和外设之 间可以进行任意的组合。

图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所示。

图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非常相似。

[转]Linux主机驱动与外设驱动分离思想相关推荐

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

    - by 宋宝华(Barry Song) 1主机.外设驱动分离的意义 在Linux设备驱动框架的设计中,除了有分层设计实现以外,还有分隔的思想.举一个简单的例子,假设我们要通过SPI总线访问某外设,在 ...

  2. Linux主机驱动与外设驱动分离思想[转宋老师]

    1主机.外设驱动分离的意义 在Linux设备驱动框架的设计中,除了有分层设计实现以外,还有分隔的思想.举一个简单的例子,假设我们要 通过SPI总线访问某外设,在这个访问过程中,要通过操作CPU XXX ...

  3. Linux驱动的软件架构(三):主机驱动与外设驱动分离的设计思想

    <Linux设备驱动开发详解>的学习笔记 1.主机驱动与外设驱动分离 Linux中的SPI.I2C.USB等子系统都利用了典型的把主机驱动和外设驱动分离的想法,让主机端只负责产生总线上的传 ...

  4. Linux主机USB RNDIS网卡驱动实现不完整导致的一例问题

    某通信模块设备,通过USB提供RDNIS和ECM网卡功能.在实际应用中发现,USB RNDIS网卡模式下,当使用AT指令以不同的CID拨号的时候,在Windows主机上能正常拨号成功,但在Linux主 ...

  5. i2c--ioctl--主机控制器驱动(i2c_adapter)--外设驱动(i2c_driver)

    updating... i2c驱动目录 [root@localhost i2c]# pwd /opt/FriendlyArm/mini2440/linux-2.6.32.2/drivers/i2c [ ...

  6. [转]Linux下如何安装软件

    一.解析Linux应用软件安装包  通常Linux应用软件的安装包有三种:  1) tar包,如software-1.2.3-1.tar.gz.它是使用UNIX系统的打包工具tar打包的.  2) r ...

  7. [转载]Linux SWAP 交换分区配置说明

    转自:http://blog.csdn.net/tianlesoftware/article/details/8741873 一.SWAP 说明 1.1 SWAP 概述 当系统的物理内存不够用的时候, ...

  8. Linux 设备驱动开发思想 —— 驱动分层与驱动分离

    前面我们学习I2C.USB.SD驱动时,有没有发现一个共性,就是在驱动开发时,每个驱动都分层三部分,由上到下分别是: 1.XXX 设备驱动 2.XXX 核心层 3.XXX 主机控制器驱动 而需要我们编 ...

  9. svn在linux下的使用(svn命令)[转]

    svn在linux下的使用(svn命令)[转] 原地址:http://www.rjgc.net/control/content/content.php?nid=4418 1.将文件checkout到本 ...

最新文章

  1. SQL Server 查询基础
  2. 微服务架构,如何做分布式,通用缓存机制?
  3. 微处理器 微型计算机系统,作业答案11微处理器微型计算机和微型计算机系统三者之间.DOC...
  4. 从逻辑回归到最大熵模型
  5. Shiro授权流程图
  6. php点击按钮变文字,点击按钮文字变成input框,点击保存变成文字的实现代码
  7. 免疫算法在物流配送中心选址中的应用
  8. 网络安全等级保护三级产品清单整理
  9. 【绕口令】打南边来了个喇嘛(转载)
  10. 《量子信息与量子计算简明教程》绪论
  11. erp352产品安装手册
  12. oppo云服务器怎么卸载,oppo手机小布助手卸载方法_oppo手机小布助手怎么卸载-硬件之家...
  13. C语言递归函数 写一个程序实现一个函数PrintN,使得传入一个正整数为N的参数后,能顺序打印从1到N的全部正整数
  14. 微软云游戏服务器,微软:云游戏服务Project xCloud将扩展游戏服务的数量和地区...
  15. 10个2018年度最佳网站设计实例赏析及原型分享
  16. 台湾最大IC封装厂近三成员工停工14天,安世半导体宣布今日起调涨价格!
  17. python安装cv2模块的方法_Python opencv模块cv2安装和部分函数使用
  18. excel切片器_给我1分钟,让你的Excel表格好看些,再好看些!
  19. XP计算机管理员账户隐藏了怎么办,xp administrator隐藏如何找回解决方案
  20. 计算机辅助绘图考试题,CAD绘图员(往年考试题)练习题

热门文章

  1. HP3777打印机一行清晰一行不清晰
  2. MDK5 nRF BLE(蓝牙低功耗)
  3. python爬虫爬取网易云音乐歌曲_Python网易云音乐爬虫进阶篇
  4. Android无限滑动控件实现
  5. 韦东山freeRTOS系列教程之【第二章】内存管理
  6. 织梦联动添加三级分类后无法添加二级分类的解决方案
  7. 微信私房菜走红 外卖、用户、监管三方皆受伤
  8. 数据分析_Pandas基础练习题9道(附数据)
  9. 数据密集型系统设计:索引及存储(B树、LSM树、OLTP及OLAP)
  10. 07 【动词的形式】verb structure,动词的位置,动词的使用,及物动词和不及物动词,使役动词,感官动词,