一、介绍设备树
1.1引入
每个设备文件都会对应一个文件存储硬件相关的信息
没有目录层次结构进行管理
有些驱动文件不需要,依然存放在arch/arm目录下
会导致arch/arm目录下存放大量的垃圾代码
在linux-3.10内核版本之后,linux作者要求arm'社区进行整改
arm社区仿效PowerPC架构进行引入设备树
1.2什么是设备树
在linux-3.10内核版本之后引入设备树,设备树是用来存放硬件相关信息的
设备树本质上就是一个结构体,这种方法进行管理会让目录层次更为清晰
linux操作系统启动时,先下载设备树,下载成功之后,内核进行启动,解析设备树,设别墅解析成功之后,会以树状的形式存放在内存中
驱动工程师需要将硬件相关的信息卸载设备树中,编程时,需要解析设备树中的地址
1.3设备树文件
- 设备树目录:~/linux-5.10.61/arch/arm/boot/dts - 设备树源文件:vi stm32mp157a-fsmp1a.dts - 设备树头文件:vi stm32mp15xx-fsmp1x.dtsi - 编译设备树命令:make dtbs - 下载设备树镜像文件:stm32mp157a-fsmp1a.dtb
二、解析设备树语法
2.1分析语法
/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 = <0x1>; ================> 用尖括号标识无符号32位整数列表
a-string-property = "Hello, world"; ================> 双引号标识字符串属性
};
child-node2 { ========================> child-node2是node1节点的子节点
};
};
node2 { ========================> node2是根节点的子节点
an-empty-property; ========================> 空属性没有实际含义,只起到标识作用
a-cell-property = <0x1 0x2 0x3 0x4>; ================> 用尖括号标识无符号32位整数列表
child-node1 { ========================> child-node1是node2节点的子节点
};
};
}; ========================> 每个节点用花括号包含,并且以分号结束
2.2属性/键值对格式
- 用双引号标识字符串属性:
- string-property = "a string";
- 用尖括号分隔的无符号32位整数列表:
- cell-property = <0xbeef 0x123 0xabcd1234>;
- 用中括号分隔二进制列表数据用:
- binary-property = [01 23 45 67];
- 用逗号分隔混合键值对信息:
- mixed-property = "a string", [01 23 45 67], <0x12345678>;
- 用逗号分隔字符串列表信息:
- string-list = "red fish", "blue fish";
2.3节点格式
- 节点格式:[@
- :是一个简单的ASCII字符串,长度最多为31个字符,标识的设备类型命名
- @:分隔符
- unit-address:地址,地址可省略不写
三、添加设备树节点
3.1添加节点步骤
1.进入设备树目录,打开设备树源文件
2.添加节点信息
3.编译设备树,并且拷贝到~/tftproot目录下
4.打开串口工具,开发板重新上电,加载最新设备树
四、解析设备树
1.linux内核启动成功之后,设备树信息以树状的形式在内存中存储
2.内核通过device_node结构体描述设备节点信息
3.内核通过property结构体描述设备树信息
4.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; //子节点
};
4.2属性结构体
struct property {
char *name; //属性的名字,也可以说键值对对应键的名字
int length; //属性的长度,也可以说键值对长度
void *value; //属性的值,也可以说键值对对应的值的内容
struct property *next; //指向下一个属性相关信息
//在同一节点中,属性构成链表
};
4.3解析节点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:节点名字
返回值:
成功返回节点结构体指针首地址
失败返回NULL
4.4解析属性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
失败返回-1
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:成员的个数
返回值:
成功返回0
失败返回-1
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
失败返回-1
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_value:获取到对应索引号的无符号32位整数属性的内容
返回值:
成功返回0
失败返回-1
五、代码练习
5.1代码1
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
/*
mynode@0x12345678{ //mynode节点名字
mystring = "hello DC24031";//字符串属性
myint = <0x12 0x34 0xabcd>;//无符号32位整数列表
mybin = [01 02 03 04];//二进制列表
myhex = "hello huyue",<0x12ab>,[1a 2b 3c];//混合键值对信息
};
*/
struct device_node* node;
//入口
static int __init demo_init(void)
{
int i = 0;
//通过路径解析节点结构体指针
node = of_find_node_by_path("/mynode@0x12345678");
if(node == NULL){
printk("of find node by path is error\n");
return -EIO;
}
//解析mystring属性
printk("key = %s value = %s\n",node->properties->name,(char*)(node->properties->value));
//解析无符号32位整数列表 完成大小端转换
printk("key = %s value = %#x\n",node->properties->next->name,__be32_to_cpup(node->properties->next->value));
printk("key = %s value = %#x\n",node->properties->next->name,__be32_to_cpup(node->properties->next->value + 4));
printk("key = %s value = %#x\n",node->properties->next->name,__be32_to_cpup(node->properties->next->value + 8));
//解析二进制列表
for(i=0;i<4;i++)
{
printk("key = %s value = %#x\n",node->properties->next->next->name,
*(unsigned char*)(node->properties->next->next->value + i));
}
//解析混合键值对信息
printk("key = %s value = %s\n",node->properties->next->next->next->name,
(char*)(node->properties->next->next->next->value));
printk("key = %s value = %#x\n",node->properties->next->next->next->name,
__be32_to_cpup(node->properties->next->next->next->value + 12));
printk("key = %s value = %#x\n",node->properties->next->next->next->name,
*(unsigned char*)(node->properties->next->next->next->value + 16));
printk("key = %s value = %#x\n",node->properties->next->next->next->name,
*(unsigned char*)(node->properties->next->next->next->value + 17));
printk("key = %s value = %#x\n",node->properties->next->next->next->name,
*(unsigned char*)(node->properties->next->next->next->value + 18));
return 0;
}
//出口
static void __exit demo_exit(void)
{
}
module_init(demo_init); //指定入口地址
module_exit(demo_exit); //指定出口地址
MODULE_LICENSE("GPL"); //遵循GPL协议
5.2代码2
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
/*
mynode@0x12345678{ //mynode节点名字
mystring = "hello DC24031";//字符串属性
myint = <0x12 0x34 0xabcd>;//无符号32位整数列表
mybin = [01 02 03 04];//二进制列表
myhex = "hello huyue",<0x12ab>,[1a 2b 3c];//混合键值对信息
};
*/
struct device_node* node;
struct property* pro;
static int __init demo_init(void)
{
int len;
int i;
//通过节点名字获取节点信息
node = of_find_node_by_name(NULL,"mynode");
if(node == NULL){
printk("of find node by name is error\n");
return -EIO;
}
//获取字符串属性
pro = of_find_property(node,"mystring",&len);
printk("key = %s value = %s\n",pro->name,(char*)pro->value);
//获取无符号32位整数列表
pro = of_find_property(node,"myint",&len);
printk("key = %s value = %#x\n",pro->name,__be32_to_cpup(pro->value));
printk("key = %s value = %#x\n",pro->name,__be32_to_cpup(pro->value+4));
printk("key = %s value = %#x\n",pro->name,__be32_to_cpup(pro->value+8));
//获取二进制列表
pro = of_find_property(node,"mybin",&len);
for(i=0;i<4;i++)
{
printk("key = %s value = %#x\n",pro->name,*(unsigned char*)(pro->value + i));
}
//获取混合键值对信息
pro = of_find_property(node,"myhex",&len);
printk("key = %s value = %s\n",pro->name,(char*)pro->value);
printk("key = %s value = %#x\n",pro->name,__be32_to_cpup(pro->value+12));
printk("key = %s value = %#x\n",pro->name,*(unsigned char*)(pro->value+16));
printk("key = %s value = %#x\n",pro->name,*(unsigned char*)(pro->value+17));
printk("key = %s value = %#x\n",pro->name,*(unsigned char*)(pro->value+18));
return 0;
}
static void __exit demo_exit(void)
{
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
5.3代码3
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
/*
mynode@0x12345678{ //mynode节点名字
mystring = "hello DC24031";//字符串属性
myint = <0x12 0x34 0xabcd>;//无符号32位整数列表
mybin = [01 02 03 04];//二进制列表
myhex = "hello huyue",<0x12ab>,[1a 2b 3c];//混合键值对信息
};
*/
struct device_node* node;
struct property* pro;
static int __init demo_init(void)
{
int i;
int len;
const char *out_string;
u32 out_u32_values[3];
u8 out_u8_values[4];
u32 out_index_value;
//通过节点名字获取节点信息
node = of_find_node_by_name(NULL,"mynode");
if(node == NULL){
printk("of find node by name is error\n");
return -EIO;
}
//获取字符串属性
of_property_read_string(node,"mystring",&out_string);
printk("mysting = %s\n",out_string);
//获取无符号32位整数列表
of_property_read_u32_array(node,"myint",out_u32_values,3);
for(i=0;i<3;i++)
{
printk("myint = %#x\n",out_u32_values[i]);
}
//获取二进制列表
of_property_read_u8_array(node,"mybin",out_u8_values,4);
for(i=0;i<4;i++)
{
printk("mybin = %#x\n",out_u8_values[i]);
}
//获取无符号整数列表下标1对应值的内容
of_property_read_u32_index(node,"myint",1,&out_index_value);
printk("myint = %#x\n",out_index_value);
//获取混合键值对信息
pro = of_find_property(node,"myhex",&len);
printk("key = %s value = %s\n",pro->name,(char*)pro->value);
printk("key = %s value = %#x\n",pro->name,__be32_to_cpup(pro->value+12));
printk("key = %s value = %#x\n",pro->name,*(unsigned char*)(pro->value+16));
printk("key = %s value = %#x\n",pro->name,*(unsigned char*)(pro->value+17));
printk("key = %s value = %#x\n",pro->name,*(unsigned char*)(pro->value+18));
return 0;
}
static void __exit demo_exit(void)
{
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
六、获取子节点信息
6.1添加子节点步骤
1.进入设备树目录,打开设备树源文件
2.添加子节点信息
3.编译设备树,并且拷贝到~/tftpboot目录下
4.开发板一定要重新上电
5.打开串口工具,开发板重新上电,加载最新设备树
6.2获取子节点API接口
struct device_node *of_get_child_by_name(const struct device_node *node,
const char *name)
函数功能:获取子节点信息
参数:
node:父节点结构体指针
name:子节点名字
返回值:
成功返回子节点结构体指针首地址
失败返回NULL
6.3 代码练习
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
/*
mynode@0x12345678{ //mynode节点名字
child-node{ //child-node1是mynode@0x12345678子节点
mystring = "hello DC24031";//字符串属性
myint = <0x12 0x34 0xabcd>;//无符号32位整数列表
mybin = [01 02 03 04];//二进制列表
myhex = "hello huyue",<0x12ab>,[1a 2b 3c];//混合键值对信息
};
};
*/
struct device_node* pnode;
struct device_node* cnode;
struct property* pro;
static int __init demo_init(void)
{
int i;
int len;
const char *out_string;
u32 out_u32_values[3];
u8 out_u8_values[4];
u32 out_index_value;
//通过节点名字获取父节点信息
pnode = of_find_node_by_name(NULL,"mynode");
if(pnode == NULL){
printk("of find node by name is error\n");
return -EIO;
}
//获取子节点结构体指针
cnode = of_get_child_by_name(pnode,"child-node");
if(cnode == NULL){
printk("of get child by name is error\n");
return -EIO;
}
//获取字符串属性
of_property_read_string(cnode,"mystring",&out_string);
printk("mysting = %s\n",out_string);
//获取无符号32位整数列表
of_property_read_u32_array(cnode,"myint",out_u32_values,3);
for(i=0;i<3;i++)
{
printk("myint = %#x\n",out_u32_values[i]);
}
//获取二进制列表
of_property_read_u8_array(cnode,"mybin",out_u8_values,4);
for(i=0;i<4;i++)
{
printk("mybin = %#x\n",out_u8_values[i]);
}
//获取无符号整数列表下标1对应值的内容
of_property_read_u32_index(cnode,"myint",1,&out_index_value);
printk("myint = %#x\n",out_index_value);
//获取混合键值对信息
pro = of_find_property(cnode,"myhex",&len);
printk("key = %s value = %s\n",pro->name,(char*)pro->value);
printk("key = %s value = %#x\n",pro->name,__be32_to_cpup(pro->value+12));
printk("key = %s value = %#x\n",pro->name,*(unsigned char*)(pro->value+16));
printk("key = %s value = %#x\n",pro->name,*(unsigned char*)(pro->value+17));
printk("key = %s value = %#x\n",pro->name,*(unsigned char*)(pro->value+18));
return 0;
}
static void __exit demo_exit(void)
{
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");