linux驱动-设备树
设备树
-小白总结,谨慎参考
设备树是从软件的角度描述硬件,DTS是设备树源文件。DTC是负责将DTS转换成DTB,DTB是DTS的二进制形式,供机器使用。
设备树,首先是一个树形结构,除了根节点外其他子节点都有唯一的父节点,节点下可以有子节点和属性。属性由名字和值组成。设备树仅仅是软件开发人员为了描述硬件而做的一个近似标识而已。系统中的每个设备都对应着设备树的一个节点。
基于platform总线的驱动分析:
在设备驱动模型中,总线负责将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
1.Platform总线驱动的工作流程:
1.提供并注册platform_device/设备节点
2.提供并注册platform_driver
3.当platform总线内的mach函数会不停的匹配driver和device(老内核是根据driver内的id、name元素;新内核是根据of_match_table中的compatible)
4.一旦匹配成功,则调用driver的probe(探测)函数开始正式执行驱动代码
2. platform总线驱动的独立性和适应性
一个platform总线驱动程序可以对应多个设备,并且设备的变化也不会影响驱动。这是如何实现的呢?
简单的说,这是一种类似传参的机制。设备将底层信息(比如寄存器信息、使用到的中断号、设备名称等)传递给驱动,驱动本身代码不用变,只需要根据参数操作底层,便可适应设备的变化
现代驱动设计理念就是算法和数据分离,驱动源码中不携带数据,只负责算法(对硬件的操作方法),这样最大程度保持驱动的独立性和适应性
具体的实现方法是:老内核中,platform_device包含了一个device结构体,其内部有一个void *platform_data; 用户可以在里面存放各种底层信息。当driver的probe(探测)函数执行时,platform_device会作为参数传进去,这样驱动就能够间接的得到这个void *platform_data,从而据此操作硬件;新内核则直接在设备节点属性中存放数据,驱动通过API读取节点里的数据:
老版本设备注册:
驱动编写:
红线部分:参数信息传递到驱动
3.新内核下的总线驱动:设备树
对于驱动本身来说,主要是platform设备不再需要在mach-xxx中注册,而是直接以节点形式定义在设备树中。platform设备可以直接定义在dts的根节点内。
驱动程序将直接和设备树里的设备节点进行配对,是通过设备节点中的compatible(兼容性)来与设备节点进行配对的。具体方法是定义一个of_match_table,只要里面的compatible与设备节点里的compatible相同,那么就触发probe函数
有关设备的私有数据,新内核不再使用plat_data了,而是直接在节点中定义各种属性,然后在驱动中用特定的API获取,详见设备树详解
4.设备树结构:
基本构造:
{}包围起来的结构称之为节点,dts中最开头的/ {},称为根节点。节点的标准结构是[email protected]{…},xxx是节点的名字,
yyy则不是必须的,其值为节点的地址(寄存器地址或其他地址),比如i2c1:[email protected]中的就是一个i2c控制器的寄存器基地址,
rtc: [email protected]中的就是这个rtc设备的i2c地址
属性:地址:
有关节点的地址,比如[email protected],虽然它在名字后面跟了地址,但是正式的设置是在reg属性中设置的
比如:reg = <0x021a0000 0x4000>; reg的格式通常为<address length>,0x021a0000是寄存器基地址,0x4000是长度。
属性:兼容性:
如果一个节点是设备节点,那么它一定要有compatible(兼容性),因为这将作为 驱动和设备(设备节点)的匹配依据,compatible(兼容性)的值可以有不止一个字符串以满足不同的需求,系统启动后,将根据根节点的compatible来判断cpu信息,并由 此进行初始化
内核与节点的匹配:
首先,内核需要知道dtb文件的地址,这是uboot告诉内核的,内核知晓dtb的文件地址后,那么驱动就可以通过一些API任意获取设备树的内部信息
对于3.x版本之后的内核,platform、i2c、spi等设备不再需要在mach-xxx中注册,驱动程序将直接和设备树里面的设备节点进行匹配,是通过设备节点中的compatible (兼容性)来与设备节点进行匹配的。
常见属性的设置和获取:
当修改或编写驱动时,常常需要修改gpio、时钟、中断等等参数,以前都是在mach-xxx中的device设置的,现在则要在节点里设置,然后驱动用特殊的API来获取
属性的获取常常在probe函数中进行,但是获取属性之前,最重要的是,确定哪个节点触发了驱动。如果一个驱动对应多个节点,那驱动可以通过
int of_device_is_compatible(const struct device_node *device, const char *name)来判断当前节点是否包含指定的compatible(兼容性)