Linux驱动编程
[TOC]
一、Linux驱动模型
Linux中的驱动模型遵循一个基本的逻辑:设备和驱动分离。其中设备是一个结构体,用于描述设备的硬件信息,例如LED的设备应当描述LED的引脚。驱动是另一个结构体,用于描述操作,例如LED的驱动应该包含LED的开或关的控制。只有当两个结构体绑定在一起时。驱动就会根据设备描述的信息操作设备。例如有两个LED灯需要控制,那么就应该有两个LED设备和一个LED驱动。当LED1绑定驱动时就会控制LED1的引脚,当LED2绑定驱动时就会控制LED2的引脚。
硬件设备和驱动的描述方式可能不同,但是在Linux的驱动模型中最终都会转化为设备结构体和驱动结构体。并通过匹配机制将设备和驱动绑定在一起。
在Linux设备模型出现后,Linux中只有一种驱动模型(设备-总线-驱动模型)。初学者可能以为设备树等和基本的总线式驱动模型不同,是不同的模型。其实不然,后面进行详细介绍。
1. 设备-总线-驱动模型
该模型是在Linux系统中有一个结构体,结构体两端分别挂设备和驱动。当有新挂载的驱动时将新的驱动与已经挂载的设备相比较,如果合适则将驱动和设备绑定。这个在设备和驱动之间的结构体就是总线。
这个总线和实际硬件中总线没有什么关系。只是将有公共操作的驱动和设备放在同一个总线上,由总线实现这一部分公共操作,就不用在每个驱动中都实现这一部分公共操作。因此在Linux系统中的i2c总线只是总线模型在i2c接口上的实现。在i2c总线中会实现i2c的基本操作,例如i2c读写。挂载在i2c总线上的驱动就不需要再实现i2c的操作。与实际硬件无关的总线:platform总线,该总线与任何硬件设备无关,完全是虚拟的。
在这个模型中设备和驱动在注册到系统之前都需要描述对应的总线。设备和驱动都是结构体,填充数据后通过注册方法挂载到总线上。
设备和驱动是否合适需要通过总线驱动提供的match函数进行比对。因此总线可以决定总线上的设备和驱动的匹配方式。
2. 设备树
设备树是一个文件,文件中描述了所有的硬件信息,包括处理器本身的信息和开发板的硬件信息。因此设备树在不同的开发板、不同的芯片上都是不同的。下面是uart的描述,关于设备树的细节参考设备树语法。设备树中包括了硬件的基本信息,包括设备寄存器的起始地址,使用的中断等。
1 | uart0: uart@11040000 { |
设备树并不是区别与总线模型的另一个模型,而是总线模型的补充。在原来的总线模型中只能通过C源文件中的设备结构体描述设备。这种方式导致每次修改设备信息都需要重新编译。设备树出现后通过设备树描述硬件设备,在系统启动时分析设备树并创建设备结构体。此时结构体中并没有设备的全部信息,但是of_node属性已经被填充,of_node代表当前设备对应的设备树节点。随后系统根据设备树节点的compatible属性与驱动的of_match_table中的compatible属性相匹配,匹配成功则将两者绑定并调用驱动的probe函数,将匹配到的设备传递给probe函数。probe函数通过of_node节点分析当前设备的其他属性并填充设备结构体,以保证后续的操作。
3. id_table
id_table模型通过id_table表进行驱动和设备的匹配,一般通过匹配id_table中的name属性确定是否匹配,id_table允许一个data属性,当前驱动可以同时驱动不同类型的设备时data属性可以用来描述不同设备间的不同。
任何设备和驱动都有其所在的总线,当设备或驱动挂载成功后调用对应总线的match函数进行匹配,而设备树匹配和id_table匹配则是在match函数中实现的。因此具体的匹配顺序需要根据总线的match函数进行分析。
二、具体驱动结构简介
Linux驱动模型的核心结构是device结构体和device_driver结构体。device代表着系统中的设备,device_driver代表着系统中的驱动。其它设备或驱动都是由这两个结构体继承(C语言的方式实现面向对象)而来。
以I2C驱动为例:
1 | struct i2c_driver { |
I2C设备的匹配:
1 | static int i2c_device_match(struct device *dev, struct device_driver *drv) |
如果以上三种方式都不能匹配到,则匹配失败。首先使用的设备树进行匹配,因此在i2c的设备驱动中,设备树中出现节点的compatible与驱动的compatible属性对应即可匹配成功。也可以使用id_table进行匹配。
附录一:Linux驱动的发展历程
(来自网络查询,未经证实)
在2.5版本内引入include/linux/device.h,其中包括device、device_driver结构体。
在2.6.0版本引入bus_type结构体,同时引入cdev
在2.6.14版本引入设备树
字符设备文件、块设备文件是Linux中描述设备的文件
以下文字是对ChatGPT问答的总结
Linux中最早期使用/dev文件夹管理设备,对应的文件系统是devfs。devfs对每一个设备提供一个设备文件,应用层可以通过读写等方式控制外部设备。后续发展的过程中,Linux开始构建驱动模型,并通过/sys(sysfs文件系统)下的文件描述设备。/sys和/dev的区别是,一个设备在/dev下表现为一个设备文件,在/sys表现为一个目录,目录中包括设备的属性状态等被列为单独的文件。
下面是块设备文件sda在/dev中对应的设备文件
1 | brw-rw---- 1 root disk 8, 0 5月 5 08:59 sda |
下面是块设备文件sda在/sys中对应的文件夹
1 | -r--r--r-- 1 root root 4096 5月 5 13:41 alignment_offset |