Linux驱动开发——设备树随记

Linux驱动开发——设备树随记

前言

在嵌入式Linux这块,对设备树一直都没怎么去了解,一直是模模糊糊的。所以最近也是被老大赶鸭子上架,快速跟着正点原子的驱动开发的课程学了一下。感觉对设备树的认识也是更清晰了一点。同样借着此篇博客记录了一下我的理解。起一个备忘的作用也希望能帮到其他人。

正文

其实类比理解的话DTS相当于.c源文件,文件描述板级设备信息。一个平台或机器对应一个.dts源文件。

  • DTI相当于c语言的头文件。

  • DTC相当于于gcc,可以将dts文件编译生成dtb文件

  • DTB相当于二进制.o文件,由DTC将DTS编译生成。

严格来讲,DTI是描述芯片以及芯片周围的一些外设(片上外设)的,比如:CPU的一些参数、总线、总线上的中断控制器、时钟、GPIO的参数、UART控制器、I2C控制器、SPI控制器等等。这些东西都是和芯片强绑定的。只要是你用IMX6ULL这颗芯片,那么它的片上外设就是这些。不存在不同。

而DTS则会描述具体的片外外设的一些参数信息。比如这个外设接在哪个GPIO口?这个外设要设置什么样的GPIO属性?等等。片外外设是围绕着IMX6ULL这颗芯片来设计不同的板载。比如利用IMX6ULL芯片设计出一个路由器、摄像头、交换机等。因为共用一个芯片。所以它们一定会使用同一个DTI。

一个节点名(node name)命名形如name@unit_addr,从命名上可以分成两个部分:@前面代表name(可重复)、@后面代表该节点外设在内存当中对应的首地址。特别的,如下所示name之前有个冒号和简称。冒号前面的称为标签(也可以理解为别名,不可重复),可以代替节点名来访问改节点。当节点代表一个设备时,比如一个I2C设备,@后面的数字代表设备的从机地址。

/{intc: interrupt-controller@00a01000 {/* ... */};
}

使用&符号可以向标签所代表的节点当中添加一些所需要设置的属性。比如

在开发板启动后,可以在文件系统当中,看到设备树的一些信息。在目录/proc/device-tree下,使用文件树的方式构建了设备树(节点作为目录、属性作为文件)。

两个特殊的节点:aliases和chosen

对于aliases节点其实翻译过来就是别名,以imx6ull.dtsi文件为例:

/ {aliases {gpio0 = &gpio1;gpio1 = &gpio2;i2c0 = &i2c1;i2c1 = &i2c2;serial0 = &uart1;serial1 = &uart2;serial2 = &uart3;serial7 = &uart8;/* ... */};soc {aips1 {gpio1: gpio@0209c000 {/* ... */};gpio2: gpio@020a0000 {/* ... */};/* ... */}}
}

可以看到,其实就是为各个标签起了一个别名。但是这就有一个疑问:标签和别名之间的区别是什么?

根据其他人提供的线索去查阅文档:https://elinux.org/Device_Tree_Mysteries#Label_vs_aliases_node_property

其中关键的一段话是:The aliases are not used directly in the device tree source, but are instead dereferenced by the Linux kernel. When a path is provided to of_find_node_by_path() or of_find_node_opts_by_path(), if the path does not begin with a “/” then the first element of the path must be a property name in the “/aliases” node. That element is replaced with the full path from the alias.

简单来讲,DTS、DTI文件无法使用别名,只能使用标签。标签最终会被解释为节点的绝对路径。而别名是被内核所使用的,当内核调用of_find_node_by_pathof_find_node_opts_by_path函数时,如果提供的的节点的路径不是绝对路径的话,就会把它视作在aliases节点下定义的别名,通过别名来获得节点的绝对路径。

对于chosen节点,查阅正点原子的IMX6ULL驱动开发指南得43.6.2小结得知,Uboot在启动内核前会向chosen添加一个bootargs属性,其内容为Uboot环境变量当中的bootargs的值。同时,bootargs也会作为内核启动的cmdline参数。

标准属性

compatible属性

compatible属性会维护一个形如:manufacture,model的驱动兼容列表,驱动程序会根据该列表判断是否与设备兼容。

例如现在有一个设备节点compatible属性值如下:

compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";

根据正点原子手册描述得知:上述compatible属性值有两个,分别为“fsl,imx6ul-evk-wm8960”和“fsl,imx-audio-wm8960”,其中“fsl”表示厂商是飞思卡尔,“imx6ul-evk-wm8960”和“imx-audio-wm8960”表示驱动模块名字。sound这个设备首先使用第一个兼容值在 Linux 内核里面查找,看看能不能找到与之匹配的驱动文件,如果没有找到的话就使用第二个兼容值查。

一般驱动程序文件都会有一个 OF 匹配表,此 OF 匹配表保存着一些 compatible 值,如果设备节点的 compatible 属性值和 OF 匹配表中的何一个值相等,那么就表示设备可以使用这个驱动。

model属性

代表设备名

status属性

表示设备可操作的状态:

含义
okay表示设备是可操作的
disable设备当前不可操作,但未来可能可操作,比如那些热插拔的设备
fail/fail-xxx设备出错了

reg、#address-cells和#size-cells 属性

#address-cells 和#size-cells 这两个属性可以用在任
何拥有子节点的设备中。一般和reg属性配合使用使用。reg属性一般格式如下:

reg = <address1 length1 address2 length2 address3 length3……>

当父节点定义了#address-cells和#size-cells 属性,子节点在定义reg属性时一个小单元就受到父节点定义的#address-cells和#size-cells 属性的约束,比如当父节点定义#address-cells为2、#size-cells为1时,就说明子节点reg属性当中一个单元由:两个地址 + 一个长度组成,当然典型的#address-cells和#size-cells 属性的值分别为1、1,这样reg值就和上面代码块所展示的一样。

这里还是一三个示例来说明一下:

对于#address-cells为1,#size-cells为0的情况:

spi4 {compatible = "spi-gpio";#address-cells = <1>;#size-cells = <0>;gpio_spi: gpio_spi@0 {compatible = "fairchild,74hc595";reg = <0>;};
};

表示gpio_spi当中的reg属性只有address值。

reg = <address1 address2 address3 ……>

对于#address-cells为1,#size-cells为1的情况:

spba-bus@02000000 {compatible = "fsl,spba-bus", "simple-bus";#address-cells = <1>;#size-cells = <1>;reg = <0x02000000 0x40000>;ranges;ecspi1: ecspi@02008000 {#address-cells = <1>;#size-cells = <0>;compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";reg = <0x02008000 0x4000>;status = "disabled";};
}

表示ecspi1当中的reg属性一个单元组成是:address length。

reg = <address1 length1 address2 length2 address3 length3……>

对于#address-cells为2,#size-cells为1的情况:

external-bus {#address-cells = <2>#size-cells = <1>;...ethernet@0,0 {compatible = "smc,smc91c111";reg = <0 0 0x1000>;};i2c@1,0 {compatible = "acme,a1234-i2c-bus";#address-cells = <1>;#size-cells = <0>;reg = <1 0 0x1000>;};flash@2,0 {compatible = "samsung,k8f1315ebm", "cfi-flash";reg = <2 0 0x4000000>;};};    

表示i2c当中的reg属性一个单元组成是:address address length。

reg = <address1_1 address1_2 length1 address2_1 address2_2 length2 address3_1 address3_2 length3……>

特别注意的是#address-cells、#size-cells定义的都是子节点的reg规则,而不是本节点!!!

其他属性

对于range属性,IMX6ULL设备树当中是没有使用的(有,但都为空),它的值一般格式为:

<child-bus-address,parent-bus-address,length>

当父节点定义此属性时,代表将子节点从child-bus-address地址开始,映射到父节点起始地址parent-bus-address处,并映射length这么长的一个范围。

对于name属性:name 属性值为字符串,name 属性用于记录节点名字,name 属性已经被弃用,不推荐使用name 属性,一些老的设备树文件可能会使用此属性

device_type属性:属性值为字符串,IEEE 1275 会用到此属性,用于描述设备的 FCode,但是设备树没有 FCode,所以此属性也被抛弃了。此属性只能用于 cpu 节点或者 memory 节点。imx6ull.dtsi 的 cpu0 节点用到了此属性,内容如下所示:

cpu0: cpu@0 {compatible = "arm,cortex-a7";device_type = "cpu";
}

对于根节点的compatible属性:。Linux 内核会通过根节点的 compoatible 属性查看是否支持此设备,如果支持的话才会启动 Linux 内核。

OF函数 —— 驱动和设备树交互的桥梁

OF函数定义的头文件:include/linux/of.h

查找节点的 OF 函数

  • struct device_node *of_find_node_by_name(struct device_node *from, const char *name)

    描述:通过节点名字查找指定的节点。

    • from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
    • name:要查找的节点名字。
    • 返回值:找到的节点,如果为 NULL 表示查找失败。
  • struct device_node *of_find_node_by_type(struct device_node *from, const char *type)

    描述:通过 device_type 属性查找指定的节点。(因为device_type用到很少,所以该函数用的也很少)

    • from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
    • type:要查找的节点对应的 type 字符串,也就是 device_type 属性值。
    • 返回值:找到的节点,如果为 NULL 表示查找失败。
  • struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible)

    描述:根据 device_type 和 compatible 这两个属性查找指定的节点。

    • from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
    • type:要查找的节点对应的 type 字符串,也就是 device_type 属性值,可以为 NULL,表示忽略掉 device_type 属性。
    • compatible:要查找的节点所对应的 compatible 属性列表。
    • 返回值:找到的节点,如果为 NULL 表示查找失败
  • struct device_node *of_find_matching_node_and_match(struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match)

    描述:通过 of_device_id 匹配表来查找指定的节点。

    • from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。
    • matches:of_device_id 匹配表,也就是在此匹配表里面查找节点。
    • match:找到的匹配的 of_device_id。
    • 返回值:找到的节点,如果为 NULL 表示查找失败
  • inline struct device_node *of_find_node_by_path(const char *path)

    描述:通过路径来查找指定的节点。

    • path:带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这个节点的全路径。
    • 返回值:找到的节点,如果为 NULL 表示查找失败。
  • struct device_node *of_get_parent(const struct device_node *node)

    描述:用于获取指定节点的父节点(如果有父节点的话)。

    • node:要查找的父节点的节点。
    • 返回值:找到的父节点。
  • struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)

    描述:数用迭代的方式查找子节点。

    • node:父节点。
    • prev:前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。可以设置为NULL,表示从第一个子节点开始。
    • 返回值:找到的下一个子节点。

获取属性值的 OF 函数

设备树的属性在内核当中以一个结构体的形式存在,它的定义如下:

struct property {char *name; /* 属性名字 */int length; /* 属性长度 */void *value; /* 属性值 */struct property *next; /* 下一个属性 */unsigned long _flags;unsigned int unique_id;struct bin_attribute attr;
};
  • property *of_find_property(const struct device_node *np, const char *name, int *lenp)

    描述:查找指定节点的属性名为name的属性值。

    • np:设备节点。
    • name: 属性名字。
    • lenp:属性值的字节数。
    • 返回值:找到的属性。
  • int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size)

    描述:获取属性中元素的数量,比如 reg 属性值是一个数组,那么使用此函数可以获取到这个数组的大小。

    • np:设备节点。
    • proname: 需要统计元素数量的属性名字。
    • elem_size:元素长度。
    • 返回值:得到的属性元素数量。
  • int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value)

    描述:从属性中获取指定标号的 u32 类型数据值(无符号 32位),比如某个属性有多个 u32 类型的值,那么就可以使用此函数来获取指定标号的数据值。

    • np:设备节点。
    • proname: 要读取的属性名字。
    • index:要读取的值标号。
    • out_value:读取到的值
      返回值:0 读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。
  • int of_property_read_ux_array(const struct device_node *np, const char *propname, ux *out_values, size_t sz)(x = 8, 16, 32, 64)

    描述:可以读取属性中 u8、u16、u32 和 u64 类型的数组数据,比如大多数的 reg 属性都是数组数据,可以使用这 4 个函数一次读取出 reg 属性中的所有数据。

    • np:设备节点。
    • proname: 要读取的属性名字。
    • out_value:读取到的数组值,分别为 u8、u16、u32 和 u64。
    • sz:要读取的数组元素数量。
    • 返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。
  • int of_property_read_ux(const struct device_node *np, const char *propname, ux *out_value)(x = 8, 16, 32, 64)

    描述:是用于读取这种只有一个整形值的属性,可以读取 u8、u16、u32 和 u64 类型属性值。

    • np:设备节点。
    • proname: 要读取的属性名字。
    • out_value:读取到的数组值。
    • 返回值:0,读取成功,负值,读取失败,-EINVAL 表示属性不存在,-ENODATA 表示没有要读取的数据,-EOVERFLOW 表示属性值列表太小。
  • int of_property_read_string(struct device_node *np, const char *propname, const char **out_string)

    描述:读取属性中字符串值。

    • np:设备节点。
    • proname: 要读取的属性名字。
    • out_string:读取到的字符串值。
    • 返回值:0,读取成功,负值,读取失败。
  • int of_n_addr_cells(struct device_node *np)int of_n_size_cells(struct device_node *np)

    描述:分别可以获取设备的#address-cells、#size-cells属性值。

    • np:设备节点。
    • 返回值:#address-cells、#size-cells属性值。
  • int of_device_is_compatible(const struct device_node *device, const char *compat)

    描述:查看节点的 compatible 属性是否有包含 compat 指定的字符串。

    • device:设备节点。
    • compat:要查看的字符串。
    • 返回值:0,节点的 compatible 属性中不包含 compat 指定的字符串;正数,节点的 compatible
    • 属性中包含 compat 指定的字符串。

了解完操作设备树的OF函数之后,其实我们就应该知道,所谓设备树的属性,除了常用的reg之外,在设备树文件当中,存在很多其他的一些厂商自定义的属性,这些属性专门为他们的芯片/设备服务的。我们也可以为一个节点自定义一个属性。最开始接触设备树的时候,因为没有任何单片机的基础,对于设备树文件当中出现的GPIO、I2C、SPI、PWM、UART等陌生的词汇没有任何概念,在有了一点单片机的基础后,再来看设备树这些概念,其实就很好理解了,对于某一个节点,为什么要有这些属性都大概能知道其原因。


本章完结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/59865.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Uni-APP+Vue3+鸿蒙 开发菜鸟流程

参考文档 文档中心 运行和发行 | uni-app官网 AppGallery Connect DCloud开发者中心 环境要求 Vue3jdk 17 Java Downloads | Oracle 中国 【鸿蒙开发工具内置jdk17&#xff0c;本地不使用17会报jdk版本不一致问题】 开发工具 HBuilderDevEco Studio【目前只下载这一个就…

ubuntu 16.04 中 VS2019 跨平台开发环境配置

su 是 “switch user” 的缩写&#xff0c;表示从当前用户切换到另一个用户。 sudo 是 “superuser do” 的缩写&#xff0c;意为“以超级用户身份执行”。 apt 是 “Advanced Package Tool” 的缩写&#xff0c;Ubuntu中用于软件包管理的命令行工具。 1、为 root 用户设置密码…

Java集合ConcurrentHashMap——针对实习面试

目录 Java集合ConcurrentHashMapConcurrentHashMap的特性是什么&#xff1f;HashMap和ConcurrentHashMap的区别&#xff1f;说说ConcurrentHashMap的底层实现 Java集合ConcurrentHashMap ConcurrentHashMap的特性是什么&#xff1f; 线程安全性 多线程并发读写安全&#xff1a…

游戏引擎学习第16天

视频参考:https://www.bilibili.com/video/BV1mEUCY8EiC/ 这些字幕讨论了编译器警告的概念以及如何在编译过程中启用和处理警告。以下是字幕的内容摘要&#xff1a; 警告的定义&#xff1a;警告是编译器用来告诉你某些地方可能存在问题&#xff0c;尽管编译器不强制要求你修复…

【题目3】C++类的设计——07年复试笔试题

【题目】07年C复试笔试真题 定义一个处理日期的类TDate&#xff0c;它有3个私有数据成员&#xff1a;Month,Day,Year和若干共有成员函数&#xff0c;实现如下要求[附条件解读] ①构造函数重载→创建无参构造函数有参构造函数 ②成员函数设置缺省参数→与④一同可用set()在类中实…

【STL】set,multiset,map,multimap的介绍以及使用

关联式容器 在C的STL中包含序列式容器和关联式容器 1.关联式容器&#xff1a;它里面存储的是元素本身&#xff0c;其底层是线性序列的数据结构&#xff0c;比如&#xff1a;vector&#xff0c;list&#xff0c;deque&#xff0c;forward_list(C11)等 2.关联式容器里面储存的…

VUE+SPRINGBOOT实现邮箱注册、重置密码、登录功能

随着互联网的发展&#xff0c;网站用户的管理、触达、消息通知成为一个网站设计是否合理的重要标志。目前主流互联网公司都支持手机验证码注册、登录。但是手机短信作为服务端网站是需要付出运营商通信成本的&#xff0c;而邮箱的注册、登录、重置密码&#xff0c;无疑成为了这…

ARM(安谋) China处理器

0 Preface/Foreword 0.1 参考博客 Cortex-M23/M33与STAR-MC1星辰处理器 ARM China&#xff0c;2018年4月established&#xff0c;独立运行。 1 处理器类型 1.1 周易AIPU 1.2 STAR-MC1&#xff08;星辰处理器&#xff09; STAT-MC1&#xff0c;主要为满足AIOT应用性能、功…

拉取docker镜像应急方法

发现许多docker hub镜像网址速度也慢得发指啦&#xff0c;如果想速度快点&#xff0c;可以考虑买个按量计费的公有云服务器&#xff0c;用他们的内网镜像&#xff0c;然后再导出&#xff0c;然后传到本地。 开通服务器 可以考虑个开通最低配的&#xff0c;这里我用的是腾讯的…

论文解读《Personalized LoRA for Human-Centered Text Understanding》

引言&#xff1a;感觉这篇蛮不错的&#xff0c;读一读。学一学如何在 LLMs&#xff08;文中说的是PLMs&#xff0c;不过我觉得可以理解为 LLMs&#xff09; 的结构上做改进 ✅ NLP 研 2 选手的学习笔记 笔者简介&#xff1a;Wang Linyong&#xff0c;NPU&#xff0c;2023级&…

SpringBoot+React养老院管理系统 附带详细运行指导视频

文章目录 一、项目演示二、项目介绍三、运行截图四、主要代码1.入住合同文件上传2.添加和修改套餐的代码3.查看入住记录代码 一、项目演示 项目演示地址&#xff1a; 视频地址 二、项目介绍 项目描述&#xff1a;这是一个基于SpringBootReact框架开发的养老院管理系统。首先…

【C++】红黑树封装map—set

1 .关联式容器 C中的map是标准模板库&#xff08;STL&#xff09;中的一种关联容器&#xff0c;它存储的是键值对&#xff08;key-value pairs&#xff09;&#xff0c;其中每个键都是唯一的。 键值对&#xff1a; 用来表示具有一一对应关系的一种结构&#xff0c;该结构中一…

药房智链:中药实验管理的供应链优化

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了中药实验管理系统的开发全过程。通过分析中药实验管理系统管理的不足&#xff0c;创建了一个计算机管理中药实验管理系统的方案。文章介绍了中药实验管理系统的系…

Unity学习---IL2CPP打包时可能遇到的问题

写这篇主要是怕自己之后打包的时候出问题不知道怎么搞&#xff0c;所以记录一下。 问题一&#xff1a;类型裁剪 IL2CPP打包后会自动对Unity工程的dll进行裁剪&#xff0c;将代码中没有引用到的类型裁剪掉。特别是通过反射等方式调用一些类的时候&#xff0c;很容易出问题。 …

商城小程序的流程渠道拓展

传统印象里&#xff0c;小程序的开发制作似乎很难&#xff0c;尤其是商城类型且功能体系完善的&#xff0c;事实也确实如此&#xff0c;没有较高的技术和成本投入或团队各个流程的专业人员合作&#xff0c;很难开发出来成品&#xff0c;或者质量较低。 当然对于大公司来说&…

Linux网络:守护进程

Linux网络&#xff1a;守护进程 会话进程组会话终端 守护进程setsiddaemon 在创建一个网络服务后&#xff0c;往往这个服务进程是一直运行的。但是对于大部分进程来说&#xff0c;如果退出终端&#xff0c;这个终端上创建的所有进程都会退出&#xff0c;这就导致进程的生命周期…

基于gradio+networkx库对图结构进行可视化展示

前言 在gradio框架下对蛋白质-蛋白质相互作用网络&#xff08;PPI网络&#xff09;进行可视化&#xff0c;并将其在网页前端进行展示。 方法 其实很简单 可以直接使用networkx画图后保存图片&#xff0c;然后使用Gradio框架的image组件进行展示即可。 但实际上gradio还配置…

【大数据学习 | HBASE高级】hive操作hbase

一般在查询hbase的数据的时候我们可以直接使用hbase的命令行或者是api进行查询就行了&#xff0c;但是在日常的计算过程中我们一般都不是为了查询&#xff0c;都是在查询的基础上进行二次计算&#xff0c;所以使用hbase的命令是没有办法进行数据计算的&#xff0c;并且对于hbas…

Python小游戏28——水果忍者

首先&#xff0c;你需要安装Pygame库。如果你还没有安装&#xff0c;可以使用以下命令进行安装&#xff1a; 【bash】 pip install pygame 《水果忍者》游戏代码&#xff1a; 【python】 import pygame import random import sys # 初始化Pygame pygame.init() # 设置屏幕尺寸 …

基于SpringBoot的养老院管理系统+文档

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…