Linux嵌入式驱动开发10——设备树开发详解

文章目录

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

全系列传送门

Linux嵌入式驱动开发01——第一个驱动Hello World(附源码)

Linux嵌入式驱动开发02——驱动编译到内核

Linux嵌入式驱动开发03——杂项设备驱动(附源码)

Linux嵌入式驱动开发04——应用层和内核层数据传输

Linux嵌入式驱动开发05——物理地址到虚拟地址映射

Linux嵌入式驱动开发06——第一个相对完整的驱动实践编写

Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)

Linux嵌入式驱动开发08——字符设备(步步为营)

Linux嵌入式驱动开发09——平台总线详解及实战

Linux嵌入式驱动开发10——设备树开发详解

Linux嵌入式驱动开发11——平台总线模型修改为设备树实例

Linux嵌入式驱动开发12——pinctl和gpio子系统实践操作

Linux嵌入式驱动开发13——ioctl接口(gpio控制使用)

Linux嵌入式驱动开发14——中断的原理以及按键中断的实现(tasklet中断下文)

Linux嵌入式驱动开发15——等待队列和工作队列

Linux嵌入式驱动开发16——按键消抖实验(内核定时器)

Linux嵌入式驱动开发17——输入子系统

Linux嵌入式驱动开发18——I2C通信

常用名词解释

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嵌入式驱动开发10——设备树开发详解_第1张图片

设备树基本语法

设备树基本框架

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

设备树语法

节点

Linux嵌入式驱动开发10——设备树开发详解_第2张图片
Linux嵌入式驱动开发10——设备树开发详解_第3张图片
所以要从根节点开始看,从左到右,从上往下

节点名称

Linux嵌入式驱动开发10——设备树开发详解_第4张图片

节点别名

Linux嵌入式驱动开发10——设备树开发详解_第5张图片

节点的引用

Linux嵌入式驱动开发10——设备树开发详解_第6张图片
Linux嵌入式驱动开发10——设备树开发详解_第7张图片

属性

Linux嵌入式驱动开发10——设备树开发详解_第8张图片
Linux嵌入式驱动开发10——设备树开发详解_第9张图片
Linux嵌入式驱动开发10——设备树开发详解_第10张图片

设备树添加自定义节点

查看节点

/proc/device-tree

在这里插入图片描述

 cat model

在这里插入图片描述
这里的model打印信息就是我们的dtb文件中的定义的model信息,描述板子信息
Linux嵌入式驱动开发10——设备树开发详解_第11张图片
通过这种方法,我们可以验证设备书中有没有成功添加进入我们的节点

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

cd gpios

在这里插入图片描述
然后查看

cat compatible

Linux嵌入式驱动开发10——设备树开发详解_第12张图片
和我们的添加的节点信息一致,就可以判断节点添加正确
Linux嵌入式驱动开发10——设备树开发详解_第13张图片

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

/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";
	};

Linux嵌入式驱动开发10——设备树开发详解_第14张图片
然后这里只需要重新编译设备树就可以了

make dtbs

Linux嵌入式驱动开发10——设备树开发详解_第15张图片
这样是编译所有的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";					// 或者添加之前没有的内容
};

Linux嵌入式驱动开发10——设备树开发详解_第16张图片
然后查看修改后的结果
Linux嵌入式驱动开发10——设备树开发详解_第17张图片

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

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

/include/linux/of.h

Linux嵌入式驱动开发10——设备树开发详解_第18张图片
都是以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;
};

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

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

Linux嵌入式驱动开发10——设备树开发详解_第19张图片

Linux嵌入式驱动开发10——设备树开发详解_第20张图片
Linux嵌入式驱动开发10——设备树开发详解_第21张图片

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

Linux嵌入式驱动开发10——设备树开发详解_第22张图片

inline struct device_node *of_find_node_by_path(const char *path)

Linux嵌入式驱动开发10——设备树开发详解_第23张图片
Linux嵌入式驱动开发10——设备树开发详解_第24张图片
Linux嵌入式驱动开发10——设备树开发详解_第25张图片

Linux嵌入式驱动开发10——设备树开发详解_第26张图片
Linux嵌入式驱动开发10——设备树开发详解_第27张图片

of_iomap函数映射虚拟地址

Linux嵌入式驱动开发10——设备树开发详解_第28张图片

查找节点

of_find_node_by_path

代码中使用函数of_find_node_by_path来找打节点的路径,并提取节点的信息
Linux嵌入式驱动开发10——设备树开发详解_第29张图片
从dts文件中,可以看到我们的test节点是在gpios节点下
在这里插入图片描述
经过板子验证,我们的设备树的节点test确实在gpio节点下,并且test节点的name就是我们的test
在这里插入图片描述
我们在driver,c文件中的代码也是跟我们的设备树节点设置一致,路径是“/gpios/test”,我们只需要验证结果中能否正常输出test名字就可以了
Linux嵌入式驱动开发10——设备树开发详解_第30张图片

结果验证

发送到开发板安装验证
在这里插入图片描述

获取节点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嵌入式驱动开发10——设备树开发详解_第31张图片

获取节点reg属性

of_property_read_u32_array

因为reg有两个u32类型的数,所以我们使用读u32类型数组的函数of_property_read_u32_array
Linux嵌入式驱动开发10——设备树开发详解_第32张图片

    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嵌入式驱动开发10——设备树开发详解_第33张图片

获取属性中字符串值

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;

结果验证

Linux嵌入式驱动开发10——设备树开发详解_第34张图片

driver测试代码

#include 
#include 
#include 

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

你可能感兴趣的:(i.MX6,linux,编程语言)