Linux系统驱动(十)设备树

一、简介

在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

在这里插入图片描述

  • 27
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值