找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

巢课
电巢直播8月计划
查看: 137|回复: 3
打印 上一主题 下一主题

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

[复制链接]

114

主题

136

帖子

1000

积分

四级会员(40)

Rank: 4Rank: 4Rank: 4Rank: 4

积分
1000
跳转到指定楼层
1#
发表于 2017-11-3 10:03 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您!

您需要 登录 才可以下载或查看,没有帐号?注册

x
我们在学习I2CUSBSD驱动时,大家有没有发现一个共性,就是在驱动开发时,每个驱动都分层三部分,由上到下分别是:
1XXX 设备驱动
2XXX 核心层
3XXX 主机控制器驱动
而需要我们编写的主要是设备驱动部分,主机控制器驱动部分也有少量编写,二者进行交互主要时由核心层提供的接口来实现;这样结构清晰,大大地有利于我们的驱动开发,这其中就是利用了Linux设备驱动开发中两个重要思想,今天我们来仔细解析一下。
一、设备驱动的分层思想
在面向对象的程序设计中,可以为某一类相似的事物定义一个基类,而具体的事物可以继承这个基类中的函数。如果对于继承的这个事物而言,其某函数的实现与基类一致,那它就可以直接继承基类的函数;相反,它可以重载之。这种面向对象的设计思想极大地提高了代码的可重用能力,是对现实世界事物间关系的一种良好呈现。
Linux内核完全由C语言和汇编语言写成,但是却频繁用到了面向对象的设计思想。在设备驱动方面,往往为同类的设备设计了一个框架,而框架中的核心层则实现了该设备通用的一些功能。同样的,如果具体的设备不想使用核心层的函数,它可以重载之。举个例子:
[cpp] view plain copy
1. return_type core_funca(xxx_device * bottom_dev, param1_type param1, param1_type param2)  
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、底层ops1、通用代码B、底层ops2、通用代码C、底层ops3”这几步,分层设计明显带来的好处是,对于通用代码ABC,具体的底层驱动不需要再实现,而仅仅只关心其底层的操作ops1ops2ops3。图1明确反映了设备驱动的核心层与具体设备驱动的关系,实际上,这种分层可能只有2层(图1a),也可能是多层的(图1b信盈达嵌入式要领吧五六领悟四五吧
这样的分层化设计在LinuxinputRTCMTDI2 CSPITTYUSB等诸多设备驱动类型中屡见不鲜。
二、主机驱动和外设驱动分离思想
主机、外设驱动分离的意义
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上的时候,它访问XXXSPI主机控制寄存器,当用在XXX1的时候,它访问XXX1SPI主机控制寄存器:
[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 XXXXXX1上的时候需要不同的驱动。那么,我们可以用如图的思想对主机控制器驱动和外设驱动进行分离。这样的结构是,外设abc的驱动与主机控制器ABC的驱动不相关,主机控制器驱动不关心外设,而外设驱动也不关心主机,外设只是访问核心层的通用的API进行数据传输,主机和外设之间可以进行任意的组合。
file:///C:\Users\郭晓娟\AppData\Local\Temp\ksohtml\wps1C9.tmp.png
如果我们不进行上图的主机和外设分离,外设abc和主机ABC进行组合的时候,需要9个不同的驱动。设想一共有m个主机控制器,n个外设,分离的结果是需要m+n个驱动,不分离则需要m*n个驱动。
Linux SPII2CUSBASoCALSA SoC)等子系统都典型地利用了这种分离的设计思想。
5 X# p+ J7 w2 x  S# m: e
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏 支持!支持! 反对!反对!

97

主题

1291

帖子

5876

积分

EDA365版主(50)

Rank: 5

积分
5876
2#
发表于 2017-11-3 10:07 | 只看该作者
郭晓娟是亮点

点评

支持!: 5.0
支持!: 5
有可能是個老太婆喔~^_^  发表于 2017-11-3 15:11

0

主题

44

帖子

276

积分

三级会员(30)

Rank: 3Rank: 3Rank: 3

积分
276
3#
发表于 2017-11-6 10:36 | 只看该作者
楼主我来顶你
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

巢课

技术风云榜

关于我们|手机版|EDA365 ( 粤ICP备18020198号 )

GMT+8, 2024-11-27 15:16 , Processed in 0.060985 second(s), 35 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表