文章目录

  • 全系列传送门
  • 常用名词解释
  • 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)

设备树编译器

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_编程语言

设备树基本语法

设备树基本框架

  1. 设备树从根节点开始,每一个设备都是一个节点
  2. 节点和节点之间可以互相嵌套,形成父子关系
  3. 设备的属性用key-value对(键值对)来描述,每个属性用分号来结束

设备树语法

节点

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_02


嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_linux_03


所以要从根节点开始看,从左到右,从上往下

节点名称

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_04

节点别名

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_驱动开发_05

节点的引用

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_linux_06


嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_07

属性

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_编程语言_08


嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_设备树_09


嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_10

设备树添加自定义节点

查看节点

/proc/device-tree

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_设备树_11

cat model

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_设备树_12


这里的model打印信息就是我们的dtb文件中的定义的model信息,描述板子信息

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_linux_13


通过这种方法,我们可以验证设备书中有没有成功添加进入我们的节点

例如,我们想要看我们添加的gpio的节点信息,进入到gpios文件夹

cd gpios

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_linux_14


然后查看

cat compatible

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_设备树_15


和我们的添加的节点信息一致,就可以判断节点添加正确

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_驱动开发_16

除了通过上面的方法查看,还可以通过下面的方法

/sys/firmware/devicetree/base

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_17


嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_linux_18

编写测试dts文件

在我们的imx6q-c-sabresd.dts文件中,添加我们的测试代码

test1:test{							// test1就是test的别名
		#address-cells = <1>;
		#size-cells = <1>;

		compatible = "test";			// 相当于总线模型中用于匹配的name

		reg = <0x20ac000 0x0000004>;
		status = "okay";
	};

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_驱动开发_19


然后这里只需要重新编译设备树就可以了

make dtbs

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_设备树_20


这样是编译所有的dts文件,也可以指定文件编译

make imx6q-c-sabresd.dtb

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_设备树_21


通过tftp下载过去dtb文件,然后启动

tftp方法见文章

uboot使用tftp网络启动加载zImage、dtb到内存,文件系统本地启动(通用!!!)

进入到开发板,可以看到我们的test文件夹,这就是我们刚才新建的一个子节点

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_linux_22


打开test文件夹,可以看到我们刚才写入的信息都在里面

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_驱动开发_23


这样我们的设备节点就准备好了,剩下的就是等待驱动来跟我们的设备节点匹配,匹配就是通过我们的 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";					// 或者添加之前没有的内容
};

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_驱动开发_24


然后查看修改后的结果

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_linux_25

设备树中常用的of操作函数

现在我们使用设备树来描述硬件信息,Linux给我们提供了一系列的函数来设置节点信息,这个of.h文件在这个目录下

/include/linux/of.h

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_26


都是以of开头的函数

device_node结构体描述节点

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_驱动开发_27

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结构体描述属性

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_设备树_28

struct property {
        char    *name;
        int     length;
        void    *value;
        struct property *next;
        unsigned long _flags;
        unsigned int unique_id;
        struct bin_attribute attr;
};

设备树文件节点里面资源的步骤

步骤一:查找我们要找的节点

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_29

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_编程语言_30


嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_31

步骤二:获取我们需要的属性值

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_32

inline struct device_node *of_find_node_by_path(const char *path)

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_驱动开发_33


嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_34


嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_linux_35

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_36


嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_驱动开发_37

of_iomap函数映射虚拟地址

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_设备树_38

查找节点

of_find_node_by_path

代码中使用函数of_find_node_by_path来找打节点的路径,并提取节点的信息

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_编程语言_39


从dts文件中,可以看到我们的test节点是在gpios节点下

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_40


经过板子验证,我们的设备树的节点test确实在gpio节点下,并且test节点的name就是我们的test

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_编程语言_41


我们在driver,c文件中的代码也是跟我们的设备树节点设置一致,路径是“/gpios/test”,我们只需要验证结果中能否正常输出test名字就可以了

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_linux_42

结果验证

发送到开发板安装验证

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_驱动开发_43

获取节点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);
    }

结果验证

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_linux_44

获取节点reg属性

of_property_read_u32_array

因为reg有两个u32类型的数,所以我们使用读u32类型数组的函数of_property_read_u32_array

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_45

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]);
    }

结果验证

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_46

获取属性中字符串值

of_property_read_string

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_嵌入式_47


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;

结果验证

嵌入式linux设备直接reboot设备无反应 嵌入式linux设备驱动开发_驱动开发_48

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");              //声明模块拥有开源许可