当你失败的时候,身边会有一群关心你的人,他们会问你发生什么事,听听你的失败经验,然后心满意足地离开。
---- 小新
一.驱动的分离与分隔
(1)分离
对于 Linux
这样一个成熟、庞大、复杂的操作系统,代码的重用性非常重要,否则的话就
会在
Linux
内核中存在大量无意义的重复代码。尤其是驱动程序,因为驱动程序占用了
Linux
内核代码量的大头,如果不对驱动程序加以管理,任由重复的代码肆意增加,那么用不了多久
Linux
内核的文件数量就庞大到无法接受的地步。
显然在 Linux
驱 动程序中这种写法是不推荐的,最好的做法就是每个平台的 I2C
控制器都提供一个统一的接口 (也叫做主机驱动
)
,每个设备的话也只提供一个驱动程序
(
设备驱动
)
,每个设备通过统一的
I2C 接口驱动来访问,这样就可以大大简化驱动文件。
(2)分隔
分隔,也就是将主机驱动和设备驱动分隔开来,比如 I2C
、
SPI
等等都会采 用驱动分隔的方式来简化驱动的开发。在实际的驱动开发中,一般 I2C
主机控制器驱动已经由 半导体厂家编写好了,而设备驱动一般也由设备器件的厂家编写好了,我们只需要提供设备信 息即可,比如 I2C
设备的话提供设备连接到了哪个
I2C
接口上,
I2C
的速度是多少等等。相当 于将设备信息从设备驱动中剥离开来,驱动使用标准方法去获取到设备信息(
比如从设备树中获 取到设备信息)
,然后根据获取到的设备信息来初始化设备。 这样就相当于驱动只负责驱动, 设备只负责设备,想办法将两者进行匹配即可。这个就是 Linux
中的总线
(bus)
、驱动
(driver)
和 设备(device)
模型,也就是常说的驱动分离。总线就是驱动和设备信息的月老,负责给两者牵线搭桥。
如图所示:
二.驱动分层
Linux 下的驱动往往也是分层的,分层的目 的也是为了在不同的层处理不同的内容。以其他书籍或者资料常常使用到的input(
输入子系统, 后面会有专门的章节详细的讲解)
为例,简单介绍一下驱动的分层。
input
子系统负责管理所有 跟输入有关的驱动,包括键盘、鼠标、触摸等,最底层的就是设备原始驱动,负责获取输入设 备的原始值,获取到的输入事件上报给 input
核心层。
input
核心层会处理各种
IO
模型,并且提 供 file_operations
操作集合。我们在编写输入设备驱动的时候只需要处理好输入事件的上报即 可,至于如何处理这些上报的输入事件那是上层去考虑的,我们不用管。可以看出借助分层模 型可以极大的简化我们的驱动编写,对于驱动编写来说非常的友好。
三.Platform 平台驱动模型
设备驱动模型中,需关心总线、设备和驱动这 3
个实体,总线将设备和驱动绑定。 在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
平台总线模型就是把原来的驱 动 C
文件给分成了俩个
C
文件,一个是
device.c
,一个是
driver.c
把稳定不变的放在
driver.c
里面,需要变得就放在了 device.c
里面。
1. 平台总线模型就很好地解决了这俩个问题
(1
)可以提高代码的重用性
(2
)减少重复性代码。
2.平台总线模型将设备代码和驱动代码分离
如图所示:
3.Platform内核源码
22 struct platform_device {
23 const char *name;
24 int id;
25 bool id_auto;
26 struct device dev;
27 u32 num_resources;
28 struct resource *resource;
29
30 const struct platform_device_id *id_entry;
31 char *driver_override; /* Driver name to force a match */
32
33 /* MFD cell pointer */
34 struct mfd_cell *mfd_cell;
35
36 /* arch specific additions */
37 struct pdev_archdata archdata;
38 };
四.编写 probe 函数的思路
(1
)从
device.c
里面获得硬件资源,因为我们的平台总线将驱动拆成了俩部分,第一部分是
device.c
,另一 部分是 driver.c
。那么匹配成功了之后,
driver.c
要从
device.c
中获得硬件资源,那么
driver.c
就是在
probe
函 数中获得的。
(2
)获得硬件资源之后,就可以在
probe
函数中注册杂项
/
字符设备,完善
file_operation
结构体,并生成设备节点。
获得硬件资源有两种方法:
方法一: 直接获取,不推荐
下面是一段
device.c的内容:
struct resource beep_res[] = {
[0] ={
.start = 0x020AC000,
.end = 0x020AC003,
.flags = IORESOURCE_MEM,
.name = "GPIO5_DR", }
};
struct platform_device beep_device = {
.name = "beep_test",
.id = -1,
.resource=beep_res,
.num_resources =ARRAY_SIZE(beep_res),
.dev = {
.release = beep_release
}
};
在
driver.c直接通过指针的方式结构体访问里面的参数:
int beep_probe(struct platform_device *pdev){
printk("beep_probe\n");
printk("beep_res is %s\n",pdev->resource[0].name);
return 0;
}
方法二:使用函数来获取资源
使用函数来获取资源: