文章目录
- 全系列传送门
- 常用名词解释
- DT:Device Tree
- FDT: Flattened Device Tree
- device tree source(dts)
- device tree source, includeDTB(dtsi)
- device tree blob(dtb)
- device tree compoler(dtc)
- 设备树基本语法
- 设备树基本框架
- 设备树语法
- 节点
- 节点名称
- 节点别名
- 节点的引用
- 属性
- 设备树添加自定义节点
- 查看节点
- 编写测试dts文件
- 设备树中常用的of操作函数
- device_node结构体描述节点
- property结构体描述属性
- 设备树文件节点里面资源的步骤
- 步骤一:查找我们要找的节点
- 步骤二:获取我们需要的属性值
- of_iomap函数映射虚拟地址
- 查找节点
- of_find_node_by_path
- 结果验证
- 获取节点compatible属性
- of_find_property
- 结果验证
- 获取节点reg属性
- of_property_read_u32_array
- 结果验证
- 获取属性中字符串值
- of_property_read_string
- 结果验证
- driver测试代码
常用名词解释
DT:Device Tree
设备树
FDT: Flattened Device Tree
展开设备树
开放固件,设备树起源于OF,所以我们在设备树中可以看到很多有of字母的函数
device tree source(dts)
设备树代码
device tree source, includeDTB(dtsi)
更通用的设备树代码,也就是相同芯片但不同平台都可以使用的代码
device tree blob(dtb)
编译之后得到的DTB文件
device tree compoler(dtc)
设备树编译器
设备树基本语法
设备树基本框架
- 设备树从根节点开始,每一个设备都是一个节点
- 节点和节点之间可以互相嵌套,形成父子关系
- 设备的属性用key-value对(键值对)来描述,每个属性用分号来结束
设备树语法
节点
所以要从根节点开始看,从左到右,从上往下
节点名称
节点别名
节点的引用
属性
设备树添加自定义节点
查看节点
/proc/device-tree
cat model
这里的model打印信息就是我们的dtb文件中的定义的model信息,描述板子信息
通过这种方法,我们可以验证设备书中有没有成功添加进入我们的节点
例如,我们想要看我们添加的gpio的节点信息,进入到gpios文件夹
cd gpios
然后查看
cat compatible
和我们的添加的节点信息一致,就可以判断节点添加正确
除了通过上面的方法查看,还可以通过下面的方法
/sys/firmware/devicetree/base
编写测试dts文件
在我们的imx6q-c-sabresd.dts文件中,添加我们的测试代码
test1:test{ // test1就是test的别名
#address-cells = <1>;
#size-cells = <1>;
compatible = "test"; // 相当于总线模型中用于匹配的name
reg = <0x20ac000 0x0000004>;
status = "okay";
};
然后这里只需要重新编译设备树就可以了
make dtbs
这样是编译所有的dts文件,也可以指定文件编译
make imx6q-c-sabresd.dtb
通过tftp下载过去dtb文件,然后启动
tftp方法见文章
uboot使用tftp网络启动加载zImage、dtb到内存,文件系统本地启动(通用!!!)
进入到开发板,可以看到我们的test文件夹,这就是我们刚才新建的一个子节点
打开test文件夹,可以看到我们刚才写入的信息都在里面
这样我们的设备节点就准备好了,剩下的就是等待驱动来跟我们的设备节点匹配,匹配就是通过我们的 compatible名字来进行匹配
现在我们需要修改compatible属性的名称,那么需要怎么修改呢?
一般不要直接修改,而是通过引用来进行修改
代码示例如下:
&gpio_user {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_user>;
fsl,user;
status = "okay";
test1:test{ // test1就是test的别名
#address-cells = <1>;
#size-cells = <1>;
compatible = "test"; // 相当于总线模型中用于匹配的name
reg = <0x20ac000 0x0000004>;
};
};
&test1{ // 直接引用别名就可以了
compatible = "test12345"; // 这样就可以覆盖掉之前的内容
status = "okay"; // 或者添加之前没有的内容
};
然后查看修改后的结果
设备树中常用的of操作函数
现在我们使用设备树来描述硬件信息,Linux给我们提供了一系列的函数来设置节点信息,这个of.h
文件在这个目录下
/include/linux/of.h
都是以of开头的函数
device_node结构体描述节点
struct device_node {
const char *name;
const char *type;
phandle phandle;
const char *full_name;
struct fwnode_handle fwnode;
struct property *properties;
struct property *deadprops; /* removed properties */
struct device_node *parent;
struct device_node *child;
struct device_node *sibling;
struct kobject kobj;
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
const char *path_component_name;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
};
property结构体描述属性
struct property {
char *name;
int length;
void *value;
struct property *next;
unsigned long _flags;
unsigned int unique_id;
struct bin_attribute attr;
};
设备树文件节点里面资源的步骤
步骤一:查找我们要找的节点
步骤二:获取我们需要的属性值
inline struct device_node *of_find_node_by_path(const char *path)
of_iomap函数映射虚拟地址
查找节点
of_find_node_by_path
代码中使用函数of_find_node_by_path来找打节点的路径,并提取节点的信息
从dts文件中,可以看到我们的test节点是在gpios节点下
经过板子验证,我们的设备树的节点test确实在gpio节点下,并且test节点的name就是我们的test
我们在driver,c文件中的代码也是跟我们的设备树节点设置一致,路径是“/gpios/test”
,我们只需要验证结果中能否正常输出test名字就可以了
结果验证
发送到开发板安装验证
获取节点compatible属性
of_find_property
/********获取节点的属性***********/
test_node_property = of_find_property(test_device_node, "compatible", &size);
if(test_node_property == NULL) {
printk("of_find_node_by_path error!!!\n");
return -1;
}else {
printk("test_node_property name is %s\n", test_node_property->name);
printk("test_node_property value is %s\n", (char *)test_node_property->value);
}
结果验证
获取节点reg属性
of_property_read_u32_array
因为reg有两个u32类型的数,所以我们使用读u32类型数组的函数of_property_read_u32_array
ret = of_property_read_u32_array(test_device_node, "reg", out_values, 2);
if(ret < 0) {
printk("of_property_read_u32_array error!!!\n");
return -1;
}else{
printk("out_values[0] is 0x%08x\n", out_values[0]);
printk("out_values[1] is 0x%08x\n", out_values[1]);
}
结果验证
获取属性中字符串值
of_property_read_string
okay是一个字符串信息,所以使用函数of_property_read_string获得
/********获取节点status的属性***********/
ret = of_property_read_string(test_device_node, "status", &str);
if(ret < 0) {
printk("of_property_read_string error!!!\n");
return -1;
}else{
printk("of_property_read_string status is 0%s\n", str);
}
return 0;
结果验证
driver测试代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
int size;
u32 out_values[2] = {0};
const char *str;
struct device_node *test_device_node;
struct property *test_node_property;
static int driver_init(void)
{
int ret = 0;
printk("driver_init ok!!!\n"); // 在内核中无法使用c语言库,所以不用printf
/********查找指定路径的节点***********/
test_device_node = of_find_node_by_path("/gpios/test");
if(test_device_node == NULL) {
printk("of_find_node_by_path error!!!\n");
return -1;
}else {
printk("of_find_node_by ok!!!\n");
printk("test_device_node name is %s\n", test_device_node->name);
}
/********获取节点compatible的属性***********/
test_node_property = of_find_property(test_device_node, "compatible", &size);
if(test_node_property == NULL) {
printk("of_find_node_by_path error!!!\n");
return -1;
}else {
printk("test_node_property name is %s\n", test_node_property->name);
printk("test_node_property value is %s\n", (char *)test_node_property->value);
}
/********获取节点reg的属性***********/
ret = of_property_read_u32_array(test_device_node, "reg", out_values, 2);
if(ret < 0) {
printk("of_property_read_u32_array error!!!\n");
return -1;
}else{
printk("out_values[0] is 0x%08x\n", out_values[0]);
printk("out_values[1] is 0x%08x\n", out_values[1]);
}
/********获取节点status的属性***********/
ret = of_property_read_string(test_device_node, "status", &str);
if(ret < 0) {
printk("of_property_read_string error!!!\n");
return -1;
}else{
printk("of_property_read_string status is 0%s\n", str);
}
return 0;
}
static void driver_exit(void)
{
printk("driver_exit bye!!!\n");
}
module_init(driver_init);
module_exit(driver_exit);
MODULE_LICENSE("GPL"); //声明模块拥有开源许可