在platform_device部分有简要说明。描述设备有两种方式:一种是使用platform_device结构体来指定;另一种是用设备树来描述。在这篇笔记中,我们将简单了解一些关于设备树的知识。什么是设备树?设备树简单理解就是描述设备信息(资源)的树。设备树(DeviceTree)在代码中具体体现如下:这些代码存放在.dts/dtsi后缀的文件中,即设备树源文件DTS(DeviceTreeSource)。这些源文件和我们的C代码一样,不能直接使用,而是要经过一个编译过程生成机器可运行的二进制文件,比如:dts文件是用dtc工具编译生成dtb文件,以及这个dtb文件是内核可以使用的文件。比如我们的板子运行后,我们系统使用的设备树文件存在于/boot目录下:为什么Linux要引入设备树?在之前的实验:【Linux笔记】LED驱动实验(总线设备驱动模型)中,我们使用了platform_device结构来描述led设备(硬件资源)。既然已经有了描述设备的方式,为什么还要引入设备树呢?因为Linux内核中有很多BSP(boardsupportpackages),不同的BSP会包含不同的描述设备的代码(.c或.h文件)。随着芯片的发展,Linux内核包含越来越多描述这些设备的代码,导致Linux内核代码臃肿。这让Linux之父林纳斯勃然大怒:“这整个ARM的东西真是他妈的蛋疼”。因此引入设备树文件,可以简化一些臃肿的C代码。另外,编译.dts生成.dtb文件的过程比用.c编译生成驱动模块和加载驱动模块的过程简单很多,也更方便我们开发。设备树的语法设备树的源文件也需要按照一定的规则来编写。和C语言一样,它也必须遵循一些语法规则。下面简单看一下设备树的源码结构和语法。先看一个设备树的例子:1.节点格式label:node-name@unit-address其中:label:标签node-name:节点名unit-address:单元地址label是一个标签,可以省略。标签的作用是方便引用节点。例如:可以使用以下两种方法修改uart@fe001000节点:2.属性格式简单来说,属性就是“name=value”,取值的方式有很多种。例:一个32位的数据,用尖括号括起来,如:interrupts=<170xc>;一个64位数据(用两个32位数据表示),用尖括号括起来,如:clock-frequency=<0x000000010x00000000>;带有终止符的字符串,用双引号括起来,例如:compatible="simple-bus";字节序列,用方括号括起来,如:local-mac-address=[000012345678];//每个字节用2个十六进制数表示local-mac-address=[000012345678];//每个字节用2个16进制数表示各种值的组合,用逗号隔开,如:compatible="ns16550","ns8250";example=<0xf00f000019>,"奇怪的属性格式";3、一些标准属性(1)compatible属性:“compatible”就是“兼容”的意思,对于某个LED,内核中可能有A、B、C三个驱动支持它,所以可以这样写:led{compatible="A","B","C";};当内核启动时,它会按照优先级顺序为这个LED寻找驱动:A,B,C。(2)model属性model属性和compatible属性有些相似,但也有区别。compatible属性是一个字符串列表,表示你的硬件兼容A、B、C等驱动;该模型用于准确定义硬件是什么。例如在根节点可以写成:/{compatible="samsung,smdk2440","samsung,mini2440";型号=“jz2440_v3”;};表示此板在内核中兼容“smdk2440”,也兼容“mini2440”。从compatible属性可以知道兼容哪些板卡,但是具体是什么板卡呢?使用模型属性来指定。(3)status属性status属性,看名字就和设备的状态有关。status属性的值也是一个字符串,字符串就是设备的状态信息。可选状态如下:(4)#address-cells和#size-cells属性格式:address-cells:应该用多少个32位数字来表示地址;size-cells:应该用多少个32位数字来表示尺寸。比如一块内存,如何描述它的起始地址和大小?下面的例子中,address-cells为1,所以在reg中用1个数字来表示地址,即用0x80000000来表示地址;size-cells为1,所以在reg中用1个数来表示大小,即用0x20000000来表示大小:/{#address-cells=<1>;#大小单元格=<1>;内存{reg=<0x800000000x20000000>;};};(5)reg属性reg属性的值是一串“地址大小”,用多少个32位数字来表示地址和大小,由其父节点的#address-cells和#size-cells决定。示例:/dts-v1/;/{#地址单元格=<1>;#大小单元格=<1>;内存{reg=<0x800000000x20000000>;};};(7)name属性已经过时,建议Neednot。它的值是一个代表节点名称的字符串。与platform_driver匹配时,优先级最低。兼容属性在匹配过程中具有最高优先级。(8)device_type属性已经过时,不推荐使用。它的值是一个表示节点类型的字符串。与platform_driver匹配时,优先级为中。兼容属性在匹配过程中具有最高优先级。4、常用节点(1)根节点用/来标识根节点,如:/dts-v1/;/{模型=“SMDK24440”;兼容=“三星,smdk2440”;#地址单元格=<1>;#大小单元格=<1>;};(2)CPU节点一般不需要我们设置,在dtsi文件中定义,如:cpus{#address-cells=<1>;#大小单元格=<0>;cpu0:cpu@0{.......}};(3)内存节点芯片厂商无法提前确定你的板子使用多少内存,所以内存节点需要板厂自行设置,例如:memory{reg=<0x800000000x20000000>;};(4)chosennode我们可以通过devicetree文件给内核传递一些参数,需要在chosennode中设置bootargs属性:chosen{bootargs="noinitrdroot=/dev/mtdblock4rwinit=/linuxrcconsole=ttySAC0,115200";};操作设备树的函数Linux内核为我们提供了一系列函数来获取设备树中的节点或属性信息。这一系列函数有一个统一的前缀“of_”(“openfirmware”就是openfirmware.),所以在很多资料中也被称为OF函数。1.节点相关的操作函数Linux内核使用device_node结构体来描述一个节点。这个结构体定义在文件include/linux/of.h中,定义如下:与查找节点相关的OF函数有5个:(1)of_find_node_by_nameof_find_node_by_name函数通过节点名查找指定节点。函数原型如下:structdevice_node*of_find_node_by_name(structdevice_node*from,constchar*name);structdevice_node*of_find_node_by_type(structdevice_node*from,constchar*type);(3)of_find_compatible_node函数of_find_compatible_node函数of_find_compatible_node根据device_type和compatible两个属性搜索指定节点。函数原型如下:structdevice_node*of_find_compatible_node(structdevice,device_node*fromconstchar*type,constchar*compatible);(4)of_find_matching_node_and_match函数of_find_matching_node_and_match函数通过of_device_id匹配表查找指定节点,函数原型如下:(5)of_find_node_by_path函数of_find_node_by_path函数通过路径查找指定节点,函数原型如下:在linestructdevice_node*of_find_node_by_path(constchar*path);2、提取属性值的OF函数在Linux内核中,结构属性用来表示属性。在文件include/linux/of.h中也定义了这个结构体,内容如下:Linux内核也提供了提取属性值的OF函数:(1)of_find_property函数of_find_property函数用于查找指定的财产。函数原型如下:property*of_find_property(conststructdevice_node*np,constchar*name,int*lenp);(2)of_property_count_elems_of_size函数of_property_count_elems_of_size函数用于获取属性中的元素个数。例如,如果reg属性的值是一个数组,则使用此函数获取数组的大小。该函数的原型如下:intof_property_count_elems_of_size(conststructdevice_node*np,constchar*propname,intelem_size);(3)读取u8、u16、u32、u64类型数组数据(4)读取u8、u16、u32、u64类型属性值(5)of_property_read_string函数of_property_read_string函数用于读取属性中的字符串值,函数原型为如下:intof_property_read_string(structdevice_node*np,constchar*propname,constchar**out_string)本文为网络素材,版权归原作者所有,如有侵权请联系删除
