设备树
1. 设备树介绍
1.1 引入
在linux内核3.10版本之前,arm公司将驱动相关的硬件信息(地址、中断号、i2c从机地址)都存放在arch/arm目录
由于每一个设备都会对应一个文件,进行描述硬件的信息,这个目录下会存放大量的垃圾代码,有些驱动不存在,硬件的信息依然存在这个目录下,会导致这个目录没有层次结构
arm公司效仿PowerPC架构,进行整改
1.2 什么是设备树
在linux内核3.10版本之后,内核中引入了设备树,设备树用来存放硬件相关的信息
它本质上就是一个结构体,这个管理方式会让层次目录更为清晰
这个设备树在内核启动时,会被内核进行解析
驱动工程师需要将所有的硬件相关信息,编写到设备树中
编程时从设备树中获取相关地址信息就可以
1.3 设备树相关文件
设备树目录:linux@ubuntu:~$ cd linux-5.10.61/arch/arm/boot/dts/
设备树源文件:stm32mp157a-fsmp1a.dts
设备树头文件:stm32mp15xx-fsmp1x.dtsi
编写设备树命令:make dtbs
设备树镜像文件:stm32mp157a-fsmp1a.dtb
1.4 设备树语法
设备树由节点和子节点组成
节点中包含属性、键值对信息
1.5 属性/键值对格式(重点)
用双引号标识字符串属性:
string-property = "a string";
用尖括号标识无符号32位整数列表:
cell-property = ;
用中括号分隔二进制列表:
binary-property = [01 23 45 67];
可以使用逗号分隔混合键值对:
mixed-property = "a string", [01 23 45 67], ;
用逗号分隔串属性字符串列表:
string-list = "red fish", "blue fish";
1.6 节点格式(重点)
节点格式:<name>[@<unit-address>]
<name>是一个简单的 ASCII 字符串,长度最多为 31 个字符
节点是根据它所代表的设备类型来命名
@:分隔符
unit-address:地址
2. 添加设备树
2.1 添加步骤
设备树目录:linux@ubuntu:~$ cd linux-5.10.61/arch/arm/boot/dts/
打开设备树源文件:vi stm32mp157a-fsmp1a.dts
添加如下节点信息
编译设备树,并且进行拷贝
3. 设备树结构体
在linux内核解析设备树成功之后,每一个节点会通过device_node结构体进行描述
属性是通过struct property结构体进行描述
在同一个节点中,属性构成链表
3.1 节点结构体
struct device_node { //mynode@0x12345678
const char *name; //节点的名字mynode
const char *full_name; //节点全名字mynode@0x12345678
struct property *properties; //属性结构体
struct device_node *parent; //父节点
struct device_node *child; //子节点
};
3.2 属性结构体
struct property {
char *name; //属性对应的名字,键值对对应的键的名称
int length; //值的长度
void *value; //属性对应的内容,键值对对应的值的内容
struct property *next; //指向同一个节点,下一个属性信息
//在同一个节点中,属性构成链表
};
4. 设备树API接口
4.1 获取节点API接口
#include <linux/of.h>
struct device_node *of_find_node_by_path(const char *path)
函数功能:通过路径获取节点相关信息
参数:
path:节点的路径("/mynode@0x12345678")
返回值:
成功返回节点结构体指针
失败返回NULL
struct device_node *of_find_node_by_name(struct device_node *from,
const char *name);
函数功能:通过节点的名字获取节点相关信息
参数:
from:NULL,表示从根节点出发
name:节点的名字("mynode")
返回值:
成功返回节点结构体指针
失败返回NULL
struct device_node *of_find_compatible_node(struct device_node *from,
const char *type, const char *compat);
函数功能:通过产商固定含义的键,获取节点相关的信息
参数:
from:NULL,表示从根节点出发
type:NUL
compat:固定产商的名称("hqyj,fsmp1a")
返回值:
成功返回节点结构体指针
失败返回NULL
4.2 获取属性API接口
struct property *of_find_property(const struct device_node *np,
const char *name,
int *lenp);
函数功能:根据设备节点的名称,获取属性相关的信息
参数:
np:节点名称
name:属性对应的名称
lenp:值的长度
返回值:
成功返回属性结构体指针首地址
失败返回NULL
int of_property_read_string(const struct device_node *np,
const char *propname,
const char **out_string);
函数功能:通过键值对键的名称,获取字符串属性
参数:
np:节点名称
propname:字符串属性名称
out_string:字符串属性输出内容
返回值:
成功返回0
失败返回错误码
int of_property_read_u32_array(const struct device_node *np,
const char *propname,
u32 *out_values, size_t sz)
函数功能:通过属性名称,获取无符号32位整数列表信息
参数:
np:节点名称
propname:无符号32位整数列表属性名称
out_values:获取到的无符号32位整数列表的信息
sz:无符号32位整数列表的成员个数
返回值:
成功返回0
失败返回错误码
int of_property_read_u8_array(const struct device_node *np,
const char *propname,
u8 *out_values, size_t sz)
函数功能:通过属性名称,获取二进制列表信息
参数:
np:节点名称
propname:二进制列表属性名称
out_values:获取到的二进制列表的信息
sz:二进制列表的成员个数
返回值:
成功返回0
失败返回错误码
int of_property_read_u32_index(const struct device_node *np,
const char *propname,
u32 index, u32 *out_value);
函数功能:通过属性名称,获取无符号32位整数列表对应索引号的信息
参数:
np:节点名称
propname:无符号32位整数列表属性名称
index:索引号
out_values:获取到的无符号32位整数列表的信息
返回值:
成功返回0
失败返回错误码
5. 获取子节点信息
5.1 添加子节点
设备树目录:linux@ubuntu:~$ cd linux-5.10.61/arch/arm/boot/dts/
打开设备树源文件:vi stm32mp157a-fsmp1a.dts
添加如下子节点节点信息
编译设备树,并且进行拷贝
5.2 获取子节点API接口
struct device_node *of_get_child_by_name(const struct device_node *node,
const char *name);
函数功能:获取子节点信息
参数:
node:父节点结构体指针
name:子节点名字
返回值:
成功返回子节点结构体指针首地址
失败返回NULL