0729_驱动2 设备树

一、介绍设备树

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");

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值