嵌入式Linux设备驱动开发思想进阶之驱动分层与驱动分离

 

咱们在学习I2C、USB、SD驱动时,你们有没有发现一个共性,就是在驱动开发时,每一个驱动都分层三部分,由上到下分别是:框架

一、XXX 设备驱动函数

二、XXX 核心层学习

三、XXX 主机控制器驱动设计

 

而须要咱们编写的主要是设备驱动部分,主机控制器驱动部分也有少许编写,两者进行交互主要时由核心层提供的接口来实现;这样结构清晰,大大地有利于咱们的驱动开发,这其中就是利用了Linux设备驱动开发中两个重要思想,今天咱们来仔细解析一下。对象

 

1、设备驱动的分层思想继承

 

在面向对象的程序设计中,能够为某一类类似的事物定义一个基类,而具体的事物能够继承这个基类中的函数。若是对于继承的这个事物而言,其某函数的实现与基类一致,那它就能够直接继承基类的函数;相反,它能够重载之。这种面向对象的设计思想极大地提升了代码的可重用能力,是对现实世界事物间关系的一种良好呈现。接口

 

Linux内核彻底由C语言和汇编语言写成,可是却频繁用到了面向对象的设计思想。在设备驱动方面,每每为同类的设备设计了一个框架,而框架中的核心层则实现了该设备通用的一些功能。一样的,若是具体的设备不想使用核心层的函数,它能够重载之。举个例子:开发

[cpp] view plain copyinput

1. return_type core_funca(xxx_device * bottom_dev, param1_type param1, param1_type param2)  it

2. {  

3.     if (bottom_dev->funca)  

4.     return bottom_dev->funca(param1, param2);  

5.     /* 核心层通用的funca代码 */  

6.     ...  

7. }  

 

上述core_funca的实现中,会检查底层设备是否重载了funca(),若是重载了,就调用底层的代码,不然,直接使用通用层的。这样作的好处是,核心层的代码能够处理绝大多数该类设备的funca()对应的功能,只有少数特殊设备须要从新实现funca()。

 

再看一个例子:

[cpp] view plain copy

1. return_type core_funca(xxx_device * bottom_dev, param1_type param1, param1_type param2)  

2. {  

3.     /*通用的步骤代码A */  

4.     ...  

5.     bottom_dev->funca_ops1();  

6.     /*通用的步骤代码B */  

7.     ...  

8.     bottom_dev->funca_ops2();  

9.     /*通用的步骤代码C */  

10.     ...  

11.     bottom_dev->funca_ops3();  

12. }  

 

上述代码假定为了实现funca(),对于同类设备而言,操做流程一致,都要通过“通用代码A、底层ops一、通用代码B、底层ops二、通用代码C、底层ops3”这几步,分层设计明显带来的好处是,对于通用代码A、B、C,具体的底层驱动不须要再实现,而仅仅只关心其底层的操做ops一、ops二、ops3。图1明确反映了设备驱动的核心层与具体设备驱动的关系,实际上,这种分层可能只有2层(图1的a),也多是多层的(图1的b)信盈达嵌入式要领吧五六领悟四五吧。

这样的分层化设计在Linux的input、RTC、MTD、I2 C、SPI、TTY、USB等诸多设备驱动类型中家常便饭。

 

2、主机驱动和外设驱动分离思想

主机、外设驱动分离的意义

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

[cpp] view plain copy

1. return_type xxx_write_spi_yyy(...)  

2. {  

3.     xxx_write_spi_host_ctrl_reg(ctrl);  

4.     xxx_ write_spi_host_data_reg(buf);  

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

6.     ...  

7. }  

 

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

[cpp] view plain copy

1. return_type xxx1_write_spi_yyy(...)  

2. {  

3.     xxx1_write_spi_host_ctrl_reg(ctrl);  

4.     xxx1_ write_spi_host_data_reg(buf);  

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

6.     ...  

7. }  

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

 

 

 

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

 

Linux SPI、I2C、USB、ASoC(ALSA SoC)等子系统都典型地利用了这种分离的设计思想。 

相关文章
相关标签/搜索