文章目录
- 一、简介
- 二、设备树语法
- (一)设备树的组成
- 1. 节点的组成
- (1)节点的别名
- (2)节点可以被引用
- (3)同名节点的合并
- 2. 属性的组成
- (1)值的字符串表示形式
- (2)值的无符号32位数的表示形式
- (3)值的16进制表示的单字节的数的形式
- (4)混合类型的值的表示形式
- (5)reg描述属性的用法
- 3. 设备树语法示例
- (二)添加一个设备树节点
- (1)添加自己的设备树节点
- (2)编译设备树
- (3)拷贝生成的设备树文件以及镜像文件到tftpboot文件中
- (4)切换到/proc/device-tree目录下
- (5)cd进入mynode节点
- 二、驱动获取设备树的信息
- (一)设备树节点相关结构体
- 1. device_node结构体
- 2. property结构体
- (二)获取节点的函数接口
- 1. 接口
- 2. 代码示例
- 3. 描述节点名
一、简介
在linux内核启动时才会解析dtb
3.10版本之后才有设备树
设备树帮助文档官网:https://elinux.org/Device_Tree_Usage
二、设备树语法
(一)设备树的组成
设备树由节点和属性组成的树状结构,节点可以包含属性和子节点,属性就是键值对
1. 节点的组成
<name>[@<unit-address>]
name :代表节点的类型名,不是设备名,它是有字符串组成的,最多不超过31个字符(超过的字符就不再进行区分了)
<unit-address> :如果节点内描述的有地址,这个对应的就是它的地址
eg:
serial@101f1000{};
//串行设备,地址是101f1000
(1)节点的别名
/{
node:mynode@0x12345678{};
};
别名node在编译阶段使用,编译后就会被替换
(2)节点可以被引用
引用节点必须放在根节点之外
解析时会将其引用节点和被引用节点合并。
同名的属性,后者的值会将前面的值覆盖掉;
不同的属性,后者会添加到前者中;
/{
node:mynode@0x12345678{};
};
&node{
};
(3)同名节点的合并
同级路径下同名节点会合并为一个节点,同名的属性,后者的值会将前面的值覆盖掉
2. 属性的组成
属性是简单的键值对,值可以为空,也可以是字节流
- 注:所有语句结尾要加分号
;
(1)值的字符串表示形式
string-property = "a string";
值用双引号""
引起来,多个字符串使用逗号,分隔
(2)值的无符号32位数的表示形式
cell-property = <0xbeee 123 0xabcd1234>
值用尖括号<>
引起来,多个值用空格分隔
(3)值的16进制表示的单字节的数的形式
binary-property = [00 01 23 45 67];
值用方括号[]
引起来,多个值被空格分隔,使用十六进制的数表示
只能表示单字节的数
注:前面的0
不能省略
(4)混合类型的值的表示形式
mixed-property = "a string", [01 23 45 67], <0x12345678>;
多个值使用逗号,
分隔
(5)reg描述属性的用法
/ {
#address-cells = <1>; //修饰子节点一个元素组成中的地址成员个数
#size-cells = <1>; //修饰子节点一个元素组成中的长度成员个数
serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000 >; //地址,长度
};
};
/ {
#address-cells = <1>; //修饰子节点一个元素组成中的地址成员个数
#size-cells = <0>; //修饰子节点一个元素组成中的长度成员个数
serial@101f0000 {
compatible = "arm,pl011";
reg = <0x40 >; //地址(比如i2c从机地址就可以这样写)
};
};
3. 设备树语法示例
/dts-v1/; //设备树的版本号
/ { // /{};这是设备树的根节点
node1 { // node1是根节点的子节点
a-string-property = "A string"; //属性
a-string-list-property = "first string", "second string";
a-byte-data-property = [01 23 34 56];
child-node1 { //child-node1是node1的子节点
first-child-property; //空属性,只起到标识作用
second-child-property = <1>;
a-string-property = "Hello, world";
};
child-node2 { //child-node2是node1的子节点
};
};
node2 { // node2是根节点的子节点
an-empty-property; //空属性,只起到标识作用
a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */
child-node1 { //child-node1是node2的子节点
};
};
};
- 相同路径的同名节点会合并到一起
(二)添加一个设备树节点
(1)添加自己的设备树节点
linux-5.10.61/arch/arm/boot/dts/stm32mp157a-fsmp1a.dtb
//在根节点下,添加如下信息,此处的代码仅为体现语法,并无实际意义
mynode@0x12345678{
a-string = "hello drivers";
a-uint = <0x11223344 0xaabbccdd>;
a-uchar = [00 0c 29 b2 d3 0c];
a-mixdata = "ethar",<0x12345678>,[11 22 33];
};
(2)编译设备树
切换到家目录下,执行make dtbs
(3)拷贝生成的设备树文件以及镜像文件到tftpboot文件中
重启开发板,使其重新解析设备树文件
(4)切换到/proc/device-tree目录下
自己的节点是添加在根目录下的,因此在此目录下可以直接找到
(5)cd进入mynode节点
可以看到自己的节点中的节点信息
二、驱动获取设备树的信息
(一)设备树节点相关结构体
1. device_node结构体
设备树的每个节点内核解析后都会创建出来一个device_node的结构体,用来描述节点
struct device_node {
const char *name; //节点名 "mynode"
const char *full_name; //节点全名 "mynode@0x12345678"
struct property *properties; //属性链表的链表头
struct device_node *parent; //指向父节点的指针
struct device_node *child; //指向子节点
struct device_node *sibling; //指向兄弟节点
}
2. property结构体
在同一个节点中,每个属性都通过一个property描述,所有的property构成一条单链表
struct property {
char *name; //键
int length; //值的长度,单位字节
void *value; //值
struct property *next; //指向下一个属性的指针
}
(二)获取节点的函数接口
1. 接口
#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
2. 代码示例
#include <linux/module.h>
#include <linux/init.h>
#include <linux/of.h> //设备树文件相关头文件
//设备文件的节点信息
//mynode@0x12345678{
// a-string = "hello drivers";
// a-uint = <0x11223344 0xaabbccdd>;
// a-uchar = [00 0c 29 b2 d3 0c];
// a-mixdata = "ethar",<0x12345678>,[11 22 33];
// };
static int __init mynode_init(void){
struct device_node *mynode;
mynode = of_find_node_by_path("/mynode@0x12345678");
if(NULL == mynode){
pr_err("of_find_node_by_path error");
return -ENODATA;
}
printk("nodename=%s{\n%s=%s\n%s=%#x,%#x\n}\n",mynode->name,\
mynode->properties->name,(char *)mynode->properties->value,\
mynode->properties->next->name,*(unsigned int *)mynode->properties->next->value,\
*(((unsigned int *)mynode->properties->next->value) + 1));
return 0;
}
static void __exit mynode_exit(void){
}
module_init(mynode_init);
module_exit(mynode_exit);
MODULE_LICENSE("GPL");
- 注:在设备树中unsigned int数据存储的时候采用的是大端
3. 描述节点名
compatible="厂商,设备名";
一般放在节点开头,可以通过compatible属性来获取节点
struct device_node *of_find_compatible_node(
struct device_node *from,
const char *type,
const char *compat)
功能:通过compatilbe获取节点
参数:
@from:填写NULL,代表从根节点开始解析
@type:写成NULL
@compat:"厂商,设备名"
返回值:成功返回节点的首地址,失败返回NULL