LV10-08-设备树-01-设备树基础知识
本文主要是设备树基础知识的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows | windows11 |
Ubuntu | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
Linux开发板 | 华清远见 底板: FS4412_DEV_V5 核心板: FS4412 V2 |
u-boot | 2013.01 |
点击查看本文参考资料
参考方向 | 参考原文 |
--- | --- |
点击查看相关文件下载
文件 | 下载链接 |
--- | --- |
一、设备树简介
1. 什么是设备树
先来看一张图,自己照着网上画的,比较丑哈,自己凑活看。
设备树是一种描述硬件信息的数据结构,Linux
内核运行时可以通过设备树将硬件信息直接传递给Linux
内核,而不再需要在Linux
内核中包含大量的冗余编码。
设备树(Device Tree
),将这个词分开就是“设备”和“树”,描述设备树的文件叫做 DTS
(DeviceTree Source
),这个DTS
文件采用树形结构描述板级设备,也就是开发板上的设备信息,比如CPU
数量、 内存基地址、 IIC
接口上接了哪些设备、 SPI
接口上接了哪些设备等等。
2. dts
、dtb
和dtsi
与设备树相关的文件有三种:
dts
:device tree source
,设备树源文件。dtsi
:device tree source include
,类似于头文件,包含一些公共的信息,可被其它设备树文件引用(就是可以被dts
文件包含用)。dtb
:device tree binary
,是将dts
设备树源文件 编译以后得到的二进制文件。
我们知道将.c
文件编译为.o
需要用到 gcc
编译器,那么将.dts
编译为.dtb
需要什么工具呢?需要用到DTC
工具。 DTC
工具源码在 Linux
内核的scripts/dtc
目录下 。
3. 在系统中的体现
Linux
内核启动的时候会解析设备树中各个节点的信息,并且在根文件系统的/proc/device-tree
目录下根据节点名字创建不同目录,例如
1 | cd /proc/device-tree/ |
/proc/device-tree
就是根节点/
下的所有属性了子节点,我们再进入chosen
节点:
1 | cd chosen/ |
我们再打开下边这个文件:
1 | linux-3.14/arch/arm/boot/dts/exynos4412-origen.dts |
找到memory
节点,信息如下:
1 | chosen { |
二、基本语法
1. dtsi
头文件
和 C
语言一样,设备树也支持头文件,设备树的头文件扩展名为.dtsi
,一个 dts
文件包含其他dtsi
文件的一般格式如下:
1 |
一般.dtsi
文件用于描述 SOC
的内部外设信息,比如 CPU
架构、主频、外设寄存器地址范围,比如 UART
、 IIC
等等。
【注意事项】在.dts
设备树文件中,可以通过#include
来引用.h
、.dtsi
和.dts
文件。只是,我们在编写设备树头文件的时候最好选择.dtsi
后缀。
2. 设备节点
2.1 根节点
设备树是采用树形结构来描述板子上的设备信息的文件,每个设备都是一个节点,叫做设备节点,每个节点都通过一些属性信息来描述节点信息,属性就是键—值对。例如,
1 | /* 节选自:linux-3.14/arch/arm/boot/dts/exynos4412-origen.dts */ |
/
:是根节点,每个设备树文件只有一个根节点。一般来说.dtsi
和.dts
文件都会有一个/
根节点,因为这两个/
根节点的内容会合并成一个根节点。
2.2 特殊节点
2.2.1 /memory
所有设备树文件的必需节点,它定义了系统物理内存的layout
,例如,
1 | memory { |
2.2.2 /chosen
该节点并不是一个真实的设备,主要是用于传递内核启动时使用的参数parameter
,例如,
1 | chosen { |
前边我们已将查看过设备树的这个节点在系统中的体现了:
1 | ls -lh /proc/device-tree/chosen/ |
但是我们会发现,设备树文件中的参数是很少的,只有"console=ttySAC2,115200"
,那么其他的是哪里来的呢?
学习uboot
的时候我们知道, uboot
在启动 Linux
内核的时候会将 bootargs
的值传递给 Linux
内核, bootargs
会作为 Linux
内核的命令行参数, Linux
内核启动的时候会打印出命令行参数(也就是 uboot
传递进来的 bootargs
的值)。那么这两者有什么关系呢?
我们在uboot
的查找chosen
这个字符串,会找到common/fdt_support.c
文件中有这么一个函数:
1 | int fdt_chosen(void *fdt, int force) |
所以实际上就是在uboot
中的相关函数修改了设备树的这个节点,实际的流程是这样的(引用的正点原子驱动开发手册中的一张图):
2.2.3 /cpus
多核CPU
支持,/cpus
节点下有1
个或多个cpu
子节点,cpu
子节点中用reg
属性用来标明自己是哪一个cpu
,所以 /cpus
中有以下2
个属性:
1 |
2.2.4 aliases
aliases
的意思是“别名”,因此 aliases
节点的主要功能就是定义别名,定义别名的目的就是为了方便访问节点。不过我们一般会在节点命名的时候会加上label
,然后通过&label
来访问节点,这样也很方便。
2.3 节点语法
节点的语法格式如下:
1 | [label:] node-name[@unit-address] { |
label
:可选项,节点别名,为了缩短节点访问路径,后续节点中可以使用&label
来表示引用指定节点。node-name
:节点的名字。unit-address
:设备地址,一般填写该设备寄存器组或内存块的首地址。properties definitions
:属性定义。child nodes
:子节点。
2.4 节点属性语法
节点中各个属性的基本语法格式如下:
1 | [label:] property-name = value; |
属性可以无值,对于有值的属性,可以有三种取值:
arrays of cells
,1
个或多个32
位数据,64
位数据使用2
个32
位数据表示,数据之间用空格分隔,用尖括号表示(< >
)。string
,就是字符串, 用双引号表示(" "
)。bytestring
,1
个或多个字节,通过空格分隔,用方括号表示([]
)。- 用
,
分隔的多值。
3. 数据类型
在设备树的源码中常用的几种数据类型有以下几种:
- 字符串
1 | compatible = "arm,cortex-a7"; /* 设置 compatible 属性的值为字符串"arm,cortex-a7" */ |
32
位无符号整数
1 | reg = <0>; /* 设置 reg 属性的值为 0 */ |
- 字符串列表
1 | compatible = "insignal,origen4412", "samsung,exynos4412"; |
三、标准属性
节点是由一堆的属性组成,节点都是具体的设备,不同的设备需要的属性不同,用户可以自定义属性。除了用户自定义属性,有很多属性是标准属性, Linux
下的很多外设驱动都会使用这些标准属性。
1. compatible
compatible
属性也叫做“兼容性”属性,这是非常重要的一个属性。compatible
属性的值是一个字符串列表, 该属性用于将设备和驱动绑定起来。字符串列表用于选择设备所要使用的驱动程序, compatible
属性的值格式如下所示:
1 | compatible = "manufacturer,model"; |
manufacturer
:表示厂商。model
:一般是模块对应的驱动名字。
一般驱动程序文件都会有一个 OF
匹配表,此 OF
匹配表保存着一些 compatible
值,如果设备节点的 compatible
属性值和 OF
匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动。
根节点下的compatible
属性,用来指定内核中哪个machine_desc
可以支持本设备,即描述其兼容哪些平台 。l例如,
1 | compatible = "insignal,origen4412", "samsung,exynos4412"; |
通过根节点的 compatible
属性可以知道我们所使用的设备,一般第一个值描述了所使用的硬件设备名字,比如这里使用是origen4412
这个设备,第二个值描述了设备所使用的SOC
,比如这里使用的是exynos4412
这颗 SOC
。 Linux
内核会通过根节点的 compoatible
属性查看是否支持此设备,如果支持的话设备就会启动Linux
内核。
2. model
model
属性值也是一个字符串,一般 model
属性描述设备模块信息,比如名字 :
1 | model = "Insignal Origen evaluation board based on Exynos4412"; |
根节点下的model
可以用于区分不同的板子,比如有2
款板子配置基本一致, 它们的compatible
是一样的,那么就通过model
来分辨这2
款板子。
3. status
status
属性看名字就知道是和设备状态有关的,status
属性值也是字符串,字符串是设备的状态信息,可选的状态如下表:
值 | 描述 |
"okay" | 表明设备是可操作的。 |
"disabled" | 表明设备当前是不可操作的,但是在未来可以变为可操作的,比如热插拔设备插入以后。 至于 disabled 的具体含义还要看设备的绑定文档。 |
"fail" | 表明设备不可操作,设备检测到了一系列的错误,而且设备也不大可能变得可操作。 |
"fail-sss" | 含义和"fail"相同,后面的 sss 部分是检测到的错误内容。 |
4. 子节点地址信息
4.1 #address-cells
该属性的值为无符号32
位整数,可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。该属性的值决定了子节点 reg
属性中地址信息所占用的字长(32
位),意思就是在子节点的reg
属性中, 使用多少个u32
整数来描述地址(address
) 。一般使用格式为:
1 |
4.2 #size-cells
该属性的值也为无符号32
位整数,可以用在任何拥有子节点的设备中,用于描述子节点的地址信息。该属性的值决定了子节点 reg
属性中长度信息所占的字长(32
位),意思就是在子节点的reg
属性中, 使用多少个u32
整数来描述大小(size
) 。一般使用格式为:
1 |
4.3 reg
reg
属性是用来描述子结点的地址信息的,一般格式如下:
1 | reg = <address1 length1 [address2 length2] [address3 length3] [... ...]>; |
每个address length
组合表示一个地址范围,其中address
是起始地址, length
是地址长度, #address-cells
表明 address
这个数据所占用的字长, #size-cells
表明 length
这个数据所占用的字长。
4.4 使用实例
4.4.1 实例1
1 | spi4 { |
#address-cells = <1>;
这一句表示spi4
的子节点reg
属性中起始地址所占用的字长为1
。
#size-cells = <0>;
这一句表示spi4
的子节点reg
属性中地址长度所占用的字长为 0
。
reg = <0>;
子节点 gpio_spi: gpio_spi@0
的 reg
属性值为 <0>
,因为父节点设置了#addresscells = <1>
和#size-cells = <0>
,因此 addres=0
,没有 length
的值,相当于设置了起始地址,而没有设置地址长度。
4.4.2 实例2
1 | aips3: aips-bus@02200000 { |
#address-cells = <1>;
这一句表示aips3
的子节点reg
属性中起始地址所占用的字长为1
。
#size-cells = <1>;
这一句表示aips3
的子节点reg
属性中地址长度所占用的字长为 1
。
reg = <0x02280000 0x4000>;
子节点dcp: dcp@02280000
的reg
属性值为<0x02280000 0x4000>
,因为父节点设置了#address-cells = <1>
, #size-cells = <1>
, address= 0x02280000
,length= 0x4000
,相当于设置了起始地址为0x02280000
,地址长度为0x40000
。
5. ranges
ranges
属性值可以为空或者按照(child-bus-address,parent-bus-address
,length
)格式编写的数字矩阵, ranges
是一个地址映射/转换表, ranges
属性每个项目由子地址、父地址和地址空间长度这三部分组成:
child-bus-address
子总线地址空间的物理地址,由父节点的#address-cells
确定此物理地址所占用的字长。
parent-bus-address
父总线地址空间的物理地址,同样由父节点的#address-cells
确定此物理地址所占用的字长。
length
子地址空间的长度,由父节点的#size-cells
确定此地址长度所占用的字长。
【注意事项】如果 ranges
属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换。
6. name
name
属性值为字符串, name
属性用于记录节点名字, name
属性已经被弃用,不推荐使用name
属性,一些老的设备树文件可能会使用此属性。
7. device_type
device_type
属性值为字符串, IEEE 1275
会用到此属性,用于描述设备的 FCode
,但是设备树没有 FCode
,所以此属性也被抛弃了。此属性只能用于 cpu
节点或者 memory
节点。
四、常用属性
1. phandle
数字形式的节点标识,在后续节点中属性值性质表示某节点时,可以引用对应节点,例如:
1 | pic@10000000 { |
2. reg
reg
属性:表示内存区域region
,一般格式如下:
1 | reg = <address1 length1 [address2 length2] [address3 length3]>; |
#address-cells
:reg
属性中, 使用多少个u32
整数来描述地址(address
),一般格式如下:
1 |
#size-cells
:reg
属性中, 使用多少个u32
整数来描述大小(size
),一般格式如下:
1 |
3. compatible
驱动和设备(设备节点)的匹配依据,compatible
(兼容性)的值可以有不止一个字符串以满足不同的需求,一般格式如下:
1 | compatible = "字符串1","字符串2",...; |
4. 中断相关属性
4.1 中断控制器节点
interrupt-controller
一个无值空属性,用来声明这个node
接收中断信号,表示该节点是一个中断控制器。
#interrupt-cells
这是中断控制器节点的属性,用来标识这个控制器需要几个单位做中断描述符。用来描述子节点中interrupts
属性使用了父节点中的interrupts
属性的具体的哪个值。
1 | interrupts = <中断域 中断号 触发方式> /* 父节点的 #interrupt-cells 属性的值是3 */ |
4.2 中断源设备节点
interrupt-parent
标识此设备节点属于哪一个中断控制器,如果没有设置这个属性,会自动依附父节点的,一般格式如下:
1 | interrupt-parent = <引用某中断控制器节点> |
interrupts
一个中断标识符列表,表示每一个中断输出信号,一般格式如下:
1 | interrupts = <中断域 中断号 触发方式> /* 父节点的 #interrupt-cells 属性的值是3 */ |
这里的中断号指的是组内中断号,触发方式对应数字定义在linux
内核源码的这个文件中:
1 | include/dt-bindings/interrupt-controller/irq.h |
宏定义如下所示:
1 |
4.3 使用实例
我们可以打开linux
内核源码中的这个文件:
1 | arch/arm/boot/dts/exynos4.dtsi |
该文件中对exynos4412
的中断控制器(GIC
)节点描述如下:
1 | gic: interrupt-controller@10490000 { |
我们可以再找一个引用了中断控制器节点的子节点,我们打开这个文件:
1 | arch/arm/boot/dts/exynos4x12-pinctrl.dtsi |
该文件中有什么一个子节点,这里边就包含了一些中断号:
1 | gpx0: gpx0 { |
5. gpio
相关属性
5.1 GPIO
控制器
gpio-controller
无值空属性,用来说明该节点描述的是一个gpio
控制器。
#gpio-cells
用来表示要用几个cell
描述一个GPIO
引脚。
5.2 GPIO
使用者节点
一般语法格式如下:
1 | xxx-gpio = <&引用GPIO控制器 GPIO标号 工作模式> |
- 工作模式:
1
,低电平有效GPIO_ACTIVE_HIGH
。
0
,高电平有效GPIO_ACTIVE_LOW。
五、常用接口
设备树描述了设备的详细信息,这些信息包括数字类型的、字符串类型的、数组类型的,我们在编写驱动的时候需要获取到这些信息。
比如设备树使用reg
属性描述了某个外设的寄存器地址为0X02005482
,长度为 0X400
,我们在编写驱动的时候需要获取到 reg
属性的0X02005482
和0X400
这两个值,然后初始化外设。
Linux
内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀of_
,所以在很多资料里面也被叫做OF
函数。这些OF
函数原型都定义在linux
内核源码的该文件中:
1 | include/linux/of.h |
1. 三个结构体
1.1 struct device_node
这个结构体定义在include/linux/of.h
文件中,结构体成员如下:
1 | struct device_node { |
1.2 struct property
节点的属性信息里面保存了驱动所需要的内容, Linux
内核中使用结构体 property
表示属性:
1 | struct property{ |
1.3 struct resource
IIC
、 SPI
、 GPIO
等这些外设都有对应的寄存器,这些寄存器其实就是一组内存空间, Linux
内核使用 resource
结构体来描述一段内存空间,resource
翻译出来就是资源,因此用resource
结构体描述的都是设备资源信息,resource
结构体定义在linux
内核源码的文件include/linux/ioport.h
中,定义如下 :
1 | /* |
【成员说明】
对于 32
位的 SOC
来说, resource_size_t
是 u32
类型的。
start
:表示开始地址。end
:表示结束地址。name
:是这个资源的名字。flags
: 是资源标志位,一般表示资源类型,可选的资源标志定义在文件include/linux/ioport.h
中。一般来说常见的资源标志就是IORESOURCE_MEM
、IORESOURCE_REG
和IORESOURCE_IRQ
等。
2. 查找节点的函数
2.1 of_find_node_by_name()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_find_node_by_name -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数通过节点名字查找指定的节点。
【函数参数】
from
:struct device_node *
类型,开始查找的节点,如果为NULL
表示从根节点开始查找整个设备树。name
:char *
类型,要查找的节点名字。
【返回值】struct device_node *
类型,成功返回找到的节点,如果为NULL
表示查找失败 。
【使用格式】none
【注意事项】none
2.2 of_find_node_by_type ()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_find_node_by_type -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数通过device_type
属性查找指定的节点 。
【函数参数】
from
:struct device_node *
类型,开始查找的节点,如果为NULL
表示从根节点开始查找整个设备树。name
:char *
类型,要查找的节点对应的type
字符串,也就是device_type
属性值。
【返回值】struct device_node *
类型,成功返回找到的节点,如果为NULL
表示查找失败 。
【使用格式】none
【注意事项】none
2.3 of_find_compatible_node ()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_find_compatible_node -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数根据device_type
和compatible
这两个属性查找指定的节点。
【函数参数】
from
:struct device_node *
类型,开始查找的节点,如果为NULL
表示从根节点开始查找整个设备树 。name
:char *
类型,要查找的节点对应的type
字符串,也就是device_type
属性值,可以为NULL
,表示忽略掉device_type
属性 。compatible
:char *
类型,要查找的节点所对应的compatible
属性列表。
【返回值】struct device_node *
类型,成功返回找到的节点,如果为NULL
表示查找失败 。
【使用格式】none
【注意事项】none
2.4 of_find_matching_node_and_match()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_find_matching_node_and_match -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数通过of_device_id
匹配表来查找指定的节点。
【函数参数】
from
:struct device_node *
类型,开始查找的节点,如果为NULL
表示从根节点开始查找整个设备树。matches
:struct of_device_id *
类型,of_device_id 匹配表,也就是在此匹配表里面查找节点。match
:struct of_device_id *
类型,找到的匹配的of_device_id
。
【返回值】struct device_node *
类型,成功返回找到的节点,如果为NULL
表示查找失败 。
【使用格式】none
【注意事项】none
2.5 of_find_node_by_path()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_find_node_by_path -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数通过路径来查找指定的节点。
【函数参数】
path
:char *
类型,带有全路径的节点名,可以使用节点的别名,比如/backlight
就是backlight
这个节点的全路径。
【返回值】struct device_node *
类型,成功返回找到的节点,如果为NULL
表示查找失败 。
【使用格式】none
【注意事项】none
3. 查找父子节点的函数
3.1 of_get_parent()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_get_parent -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数获取指定节点的父节点(如果有父节点的话) 。
【函数参数】
node
:struct device_node *
类型,要查找的父节点的节点。
【返回值】struct device_node *
类型,成功返回找到的父节点,如果为NULL
表示查找失败 。
【使用格式】none
【注意事项】none
3.2 of_get_next_child()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_get_next_child -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数用迭代的方式查找子节点。
【函数参数】
node
:struct device_node *
类型,父节点。prev
:struct device_node *
类型,前一个子节点,也就是从哪一个子节点开始迭代地查找下一个子节点。可以设置为NULL
,表示从第一个子节点开始。
【返回值】struct device_node *
类型,成功返回找到的下一个子节点,如果为NULL
表示查找失败 。
【使用格式】none
【注意事项】none
4. 提取属性的函数
4.1 of_find_property()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_find_property -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数提取指定属性的值 。
【函数参数】
np
:struct device_node *
类型,设备节点指针。name
:char *
类型,属性名字。lenp
:int *
类型,属性值的字节数。
【返回值】struct property *
类型,成功返回属性值的首地址,失败返回NULL
。
【使用格式】none
【注意事项】none
4.2 of_property_count_elems_of_size()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_property_count_elems_of_size -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数用于获取属性中元素的数量,比如reg
属性值是一个数组,那么使用此函数可以获取到这个数组的大小。
【函数参数】
node
:struct device_node *
类型,设备节点。propname
:char *
类型,需要统计元素数量的属性名字。elem_size
:int
类型,元素长度。
【返回值】int
类型,得到的属性元素数量。
【使用格式】none
【注意事项】none
4.3 of_property_read_u32_index()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_property_read_u32_index -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数用于从属性中获取指定标号的u32
类型数据值(无符号32
位),比如某个属性有多个u32
类型的值,那么就可以使用此函数来获取指定标号的数据值。
【函数参数】
node
:struct device_node *
类型,设备节点。propname
:char *
类型,要读取的属性名字。index
:u32
类型,要读取的值标号。out_value
:u32
类型,读取到的值。
【返回值】int
类型,得到的属性元素数量。
【使用格式】none
【注意事项】none
4.4 获取属性数组数据
有四个函数分别用于获取u8
、 u16
、 u32
和 u64
类型的数组数据:
1 | /* 需包含的头文件 */ |
【函数说明】这 4
个函数分别是读取属性中u8
、u16
、u32
和u64
类型的数组数据,比如大多数的reg
属性都是数组数据,可以使用这 4
个函数一次读取出reg
属性中的所有数据。
【函数参数】
np
:struct device_node *
类型,设备节点。propname
:char *
类型,要读取的属性名字。out_values
:表示读取到的数组值 ,分别为u8
、u16
、u32
和u64
。sz
:size_t
类型,要读取的数组元素数量。
【返回值】int
类型。读取成功返回0
,读取失败返回一个负值。 -EINVAL
表示属性不存在, -ENODATA
表示没有要读取的数据, -EOVERFLOW
表示属性值列表太小。
【使用格式】none
【注意事项】none
4.5 获取属性数值
有四个函数分别用于获取u8
、 u16
、 u32
和 u64
类型的数值数据:
1 | /* 需包含的头文件 */ |
【函数说明】这 4
个函数分别是读取属性中u8
、u16
、u32
和u64
类型的数值数据。
【函数参数】
np
:struct device_node *
类型,设备节点。propname
:char *
类型,要读取的属性名字。out_values
:表示读取到的数值 ,分别为u8
、u16
、u32
和u64
。
【返回值】int
类型。读取成功返回0
,读取失败返回一个负值。
【使用格式】none
【注意事项】none
4.6 获取属性字符串值
4.6.1 of_property_read_string()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_property_read_string -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数用于读取属性中字符串值。
【函数参数】
np
:struct device_node *
类型,设备节点指针。propname
:char *
类型,要读取的属性名字。out_string
:char **
类型,读取到的字符串值。
【返回值】int
类型,成功返回0
,失败返回负数,负数的绝对值是错误码 。
【使用格式】这里写一个简单实例,主要是说明第三个参数的定义使用方式:
1 | /* 设备树节点 */ |
【注意事项】none
4.7 of_n_addr_cells()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_n_addr_cells -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数用于获取#address-cells
属性值。
【函数参数】
np
:struct device_node *
类型,设备节点指针。
【返回值】int
类型,获取到的#address-cells
属性值。
【使用格式】none
【注意事项】none
4.8 of_n_size_cells()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_n_size_cells -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数用于获取#size-cells
属性值。
【函数参数】
np
:struct device_node *
类型,设备节点指针。
【返回值】int
类型,获取到的#size-cells
属性值。
【使用格式】none
【注意事项】none
5. 其他常用of
函数
5.1 of_device_is_compatible()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_device_is_compatible -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数用于查看节点的compatible
属性是否有包含compat
指定的字符串,也就是检查设备节点的兼容性。
【函数参数】
device
:struct device_node *
类型,设备节点指针。compat
:char *
类型,要查看的字符串。
【返回值】int
类型,返回0
表示节点的compatible
属性中不包含 compat
指定的字符串; 返回一个正数表示节点的compatible
属性中包含 compat
指定的字符串。
【使用格式】none
【注意事项】none
5.2 of_get_address()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_get_address -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数用于获取地址相关属性,主要是reg
或者assigned-addresses
属性值。
【函数参数】
dev
:struct device_node *
类型,设备节点指针。index
:int
类型,要读取的地址标号。size
:u64 *
类型,地址长度 。flags
:unsigned int *
类型,参数,比如IORESOURCE_IO
、IORESOURCE_MEM
等。
【返回值】__be32 *
类型,成功返回读取到的地址数据首地址,读取失败返回NULL
。
【使用格式】none
【注意事项】none
5.3 of_translate_address()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_translate_address -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数用于将从设备树读取到的地址转换为物理地址。
【函数参数】
dev
:struct device_node *
类型,设备节点指针。in_addr
:__be32 *
类型,要转换的地址。
【返回值】u64
类型,得到的物理地址,如果为OF_BAD_ADDR
的话表示转换失败。
【使用格式】none
【注意事项】none
5.4 of_property_read_bool()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_property_read_bool -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数用于判断指定的属性是否存在。
【函数参数】
dev
:struct device_node *
类型,设备节点指针。propname
:char *
类型,要判断是否存在的的属性名称。
【返回值】int
类型,若属性存在则返回true
,不存在则返回false
。
【使用格式】none
【注意事项】none
5.5 of_address_to_resource()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_address_to_resource -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】前边已经介绍过struct resource
结构体了,函数看名字像是从设备树里面提取资源值,但是本质上就是将 reg
属性值,然后将其转换为 resource
结构体类型。
【函数参数】
dev
:struct device_node *
类型,设备节点指针。index
:int
类型,地址资源标号。r
:struct resource *
类型(前边有介绍),得到的resource
类型的资源值。
【返回值】int
类型,成功返回0
,失败返回一个负数。
【使用格式】none
【注意事项】none
5.6 of_iomap()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_iomap -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数用于直接内存映射。不使用设备树的话,会使用ioremap()
函数来完成物理地址到虚拟地址的映射,采用设备树以后就可以直接通过of_iomap
函数来获取内存地址所对应的虚拟地址,不需要使用ioremap
函数了。当然了,我们也可以使用ioremap()
函数来完成物理地址到虚拟地址的内存映射,只是在采用设备树以后,大部分的驱动都使用of_iomap
函数了。 of_iomap
函数本质上也是将 reg
属性中地址信息转换为虚拟地址,如果 reg
属性有多段的话,可以通过 index
参数指定要完成内存映射的是哪一段 。
【函数参数】
np
:struct device_node *
类型,设备节点指针。index
:int
类型,reg
属性中要完成内存映射的段,如果reg
属性只有一段的话index
就设置为0
。
【返回值】void __iomem *
类型,成功映射的话返回经过内存映射后的虚拟内存首地址,内存映射失败返回NULL
。
【使用格式】none
【注意事项】none
5.7 of_get_named_gpio()
我们使用以下命令查询一下函数所在头文件:
1 | grep of_get_named_gpio -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数从设备树中提取gpio
口。
【函数参数】
np
:struct device_node *
类型,设备节点指针。propname
:char *
类型,包含要获取GPIO
信息的属性名。index
:int
类型,GPIO
索引,因为一个属性里面可能包含多个GPIO
,此参数指定要获取哪个GPIO
的编号,如果只有一个GPIO
信息的话此参数为0
。
【返回值】int
类型,返回一个正数表示获取到了GPIO
编号,获取失败返回一个负数。
【使用格式】none
【注意事项】none
5.8 irq_of_parse_and_map()
我们使用以下命令查询一下函数所在头文件:
1 | grep irq_of_parse_and_map -r -n ~/5linux/linux-3.14/include |
经过查找,我们可以得到如下信息:
1 | /* 需包含的头文件 */ |
【函数说明】该函数获得设备树中的中断号并进行映射。
【函数参数】
node
:struct device_node *
类型,设备节点指针。index
:int
类型,GPIO
索引,因为一个属性里面可能包含多个GPIO
,此参数指定要获取哪个GPIO
的编号,如果只有一个GPIO
信息的话此参数为0
。
【返回值】int
类型,成功返回中断号,失败返回一个负数,绝对值表示错误码。
【使用格式】none
【注意事项】none