linux驱动之设备树的使用讲解,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中最开头的/ {},称为根节点。节点的标准结构是xxx@yyy{…},xxx是节点的名字,

yyy则不是必须的,其值为节点的地址(寄存器地址或其他地址),比如i2c1:i2c@021a0000中的就是一个i2c控制器的寄存器基地址,

rtc: pcf8523@68中的就是这个rtc设备的i2c地址

属性:地址:

有关节点的地址,比如i2c@021a0000,虽然它在名字后面跟了地址,但是正式的设置是在reg属性中设置的

比如:reg = <0x021a0000 0x4000>; reg的格式通常为

,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(兼容性)

相关内容