【Linux驱动】设备树简介 | 内核对设备树的处理

🐱作者:一只大喵咪1201
🐱专栏:《Linux驱动》
🔥格言:你只管努力,剩下的交给时间!
图

目录

  • 🧲设备树简介
    • 🏹设备树语法
    • 🏹常见节点和属性
    • 🏹编译设备树文件
  • 🧲内核对设备树的处理
    • 🏹会被转换为platform_device的节点
    • 🏹匹配驱动程序
    • 🏹使用没有转换的节点
  • 🧲总结

🧲设备树简介

图
如上图所示,在总线驱动模型中,由platform_device结构体对象来提供不同类型的硬件资源:

  • 代码表某个开发板的board_XXX.c文件中,定义一个或多个platform_device结构体对象来给驱动程序提供硬件资源。
  • 不同开发板的硬件资源是不同的,所以多个开发板就会有多个board_XXX.c文件。

每一个board_XXX.c都需要进行编译,在修改platform_device中的某些资源后也需要重新编译,此时对Linux内核就存在两个影响:

  1. 操作复杂:每次修改或者增加后都需要重新编译,并且还要对驱动程序进行重新装载。
  2. 内核冗余:由于开发板的种类非常多,要想让内核能驱动这些开发板,内核中势必会存在大量的board_XXX_c文件。

对此,Linux之父Linus 大发雷霆:“this whole ARM thing is a f*cking pain in the ass”。认为这些东西都行垃圾,进行了改进,于是Linux内核开始引入了设备树

设备树是一个配置文件,该文件是给内核里的驱动程序指定硬件的信息的,比 如 LED 驱动,在内核的驱动程序里去操作寄存器,但是操作哪一个引脚?这由设备树指定。

  • 设备树的优势在于,它并不属于内核,而是位于内核之外的,而且也不参与编译。

Linux内核在运行时,会从设备树文件中读取设备节点的硬件资源信息,并提供给相应的驱动程序,它完全起到了原本board_XXX.cplatform_device结构体对象的作用,而且还解决了总线模型中存在的问题。

🏹设备树语法

图
如上图所示,之所以叫设备,是因为这些设备节点挂载在系统总线上,形成一个树状结构。

  • root:表示根节点。
  • CPU:这些蓝色框中的设备节点是根节点的子节点。
  • I2C:这些黑色框中的设备节点是子节点的子节点。

某一条支路上的设备节点可以无限挂载下去,而且一个设备节点可以拥有多个子节点,每一个节点都表示一个设备,都包含着硬件资源信息。

怎么描述设备树呢?用设备树文件dts(device tree source),它需要编译成为dtb(device tree blob)文件,内核使用的就是dtb文件。

  • 我们要写的是dts文件,这才是设备树的根本。

图

如上图所示dts文件中的代码,这就是设备树文件,它的语法规则如下:

  • DTS文件布局:
/ {[property definitions];[child nodes];
};

和Linux文件系统一样,/表示根节点,后面跟一对大括号,以封号结束,表示整个设备树。

括号内包含:

  • property definitions(属性)
  • child nodes(子节点)

位于根节点中的属性就是用来描述根节点的。属性和子节点都在[]内,表示可有可无,并不是必须写的,数量也并不是固定的,可以有一个,也可以有多个。

node格式:

设备树中的设备节点,被称为node(基本单元)

[lable:] node_name[@unit-address] {[property definitions];[child nodes];
};

每一个设备节点的组成和根节点类似,也是由属性,子节点和大括号组成:

  • lable:表示该设备节点的标号,可以省略。
  • node_name:表示该设备节点的名称,不能省略。
  • @unit-addrsss:表示该设备节点的地址,可以省略。

就拿上图dts文件中设备节点uart来说,它的名称是uart,标号是uart0,地址是@fe001000,可以使用下面两种方式来修改uart@fe0010000这个node:

  1. 在根节点之外使用label引用node
&uart0 {//修改属性
};
  1. 在根节点之外使用绝对路径
&{/uart@fe001000} {//修改属性
};

从上面两个例子中可以看出,lable的好处是使用起来更方便,而且节点名称后的地址没有实际作用,可以看作是和设备名一起构造出的设备节点名称。

properties(属性)格式:

无论是根节点还是设备节点,都有属性,属性的描述也有一定的规则,简单来说就是property_name = value,也就是属性名称= 属性值

但是属性值value有多种类型:

  • interrupts = <17 0xc>:value值用<>括起来,17和0xc是两个32位的数据:

    • 可以是10进制的,也可以是16进制,重点是尖括号,表示是32位数据。
    • 之间使用空格隔开,可以有多个数据。
  • clock-frequency = <0x00000001 0x00000000>:value是一个64位的数据,需要用到0x000000010x00000000两个cell来表示。

  • compatible = "simple -bus", "A", "B":value有三个字符串,字符串之间用,隔开。

  • local-mac-address = [00 00 12 34 56 78]:value值有多个,全部都是16进制,用两个16进制数来表示一个字节,[]内的数字必然是16进制的。

  • example = <0xf00f0000 19>, "hello":value值有两种类型,之间用,隔开。

🏹常见节点和属性

根节点:

dts文件中必须要有一个根节点:

/dts-v1/
/ {//根节点属性modle = "fsl,mpc8572ds";compatible = "fsl,mpc8572ds", "smdk2410", "mini2440";#address-cells = <1>;#size-cells = <1>
};
  • model:表示使用该设备树文件的开发板是什么型号的,fsl,mpc8572ds表示这是飞思卡尔公司的mpc8572ds开发板。
  • compatible:表示兼容性,表示该设备树兼容fsl,mpc8572dssmdk2410mini2440三种驱动程序,
    • 由于这里是根节点,所以是内核驱动程序,普通设备节点就是该设备的驱动程序。
    • 启动时,会按照先fsl,mpc8572dssmdk2410,最后是mini2440的顺序去寻找驱动程序。

对于这两个属性的值,建议采样这样的形式manufacturer,model,即厂家名,模块名

  • #address-cellscell是一个32位的数值,该属性是说地址address要用多少个32位的数来表示。
  • #size_cells:表示大小size要用多少个32位的数来表示。
/ {#address-cells = <1>;#size-cells = <1>;memory {reg = <0x80000000 0x20000000>;};
};

上例中是描述根节点下一段内存的起始地址和大小,#address-cells大小是1,所以reg属性中用一个数0x80000000来表示这段内存的起始地址。#size-cells大小是1,所以reg中用一个数0x2000000来表示这段内存的大小。

  • reg:表示寄存器地址,在设备树里,可以用来描述一段空间,因为在ARM中,寄存器和内存是统一编址的,所以在访问寄存器和内存是方法上没有区别。
    • reg属性的值,是一系列address size

CPU节点:

cpus {#address-cells = <1>;#size-cells = <0>;cpu0:cpu@0 {.....};
};

CPU节点一般不用我们设置,在dtsi文件中都定义好了,也就是说有人已经写好了,不用我们管。后面会介绍到dtsi文件。

memory节点:

前面在介绍#address-cells#size-cells的时候已经讲解过了,这里就不说了。

chosen节点:

chosen {//chosen属性bootargs = "root=/dev/sda2";};

这是一个虚拟节点,可以通过该节点向内核中传入一些参数,传入的就是属性值。


status属性:

&uart1 {status = "disabled";
};

上例中,可以通过status控制uart1是否使能,如果是disabled的话,就不会创建uart1设备节点。

status常用取值:

value描述
“okay”设备正常运行
“disabled”设备不可操作,但是后面可以恢复工作
“fail”发生了严重错误,需要修复

常用的主要是okaydisabled这两个值。

name属性:

name = "字符串",该属性是用来表示节点名字的,在匹配驱动程序时会用到,但是过时了,使用的不是很多。

device_type:

device_type = "字符串",该属性是用来表示节点类型的,也是在匹配驱动程序时会用到,也过时了,使用的不是很多。

🏹编译设备树文件

一般不会从头写dts文件,而是修改,修改完毕后需要重新编译成dtb文件,先看一下Linux-4.9.88/arch/arm/boot/dts目录下的100ask_imx6ull-14x14.dts设备树文件:

tu
如上图所示,除了有很多节点外,还使用C语言的语法#include包含了imx6ull.dtsi头文件。

  • dtsi设备树文件是别人写好的模板,我们只需要在这个基础上进行修改即可。

进入到内核目录Linux-4.9.88中:

  • 执行指令touch arch/arm/boot/dts/100ask_imx6ull-14x14.dts 修改一下设备树文件的时间。
  • 执行make dtbs V=1编译设备树文件。

使用V=1选项是为了查看编译过程中的打印信息:

图
如上图所示编译过程中的打印信息:

  • 先使用了gcc编译器对dts设备树文件进行了预处理,目的就是将使用C语言#include包含的dtsi头文件复制到dts文件中。
  • 然后再使用scripts/drc/dtc处理dts文件生成dtb文件。

如果只使用dtb编译工具,是不支持#include语法的,只能使用/inculde语法将dtsi文件包含进去。

  • 这也说明了,天下板子一大抄,我们写的dts文件继承了别人写好的dtsi文件。

增加设备树节点:

tu
如上图,在设备树文件中增加BigMiaomi_Node节点,属性为BigMiaomi_test = "A-Big-Miaomi"。然后使用make dtbs指令编译dts文件。编译好以后,将生成的dtb文件放到网络文件系统中。然后启动开发板,并且挂载网络文件系统。

图

如上图,将网络文件系统中的dtb文件拷贝到/boot目录下,覆盖原本的dtb文件,然后执行reboot指令重启开发板。
图

如上图所示,重启后在/sys/firmware/devicetree/base/目录下有多个文件,这里的每个文件都代表一个设备树中的节点,可以看到,我们增加的BigMiaomi_Node也在这里。

查看新增的节点文件,可以看到里面有节点属性BigMiaomi_test和节点名字name两个文件。

图
如上图,使用cat指令查看这两个文件,属性文件中存放的是属性值A-Big-Maiomi,节点名字文件中存放的是节点名字BigMiaomi_Node,和本喵在设备树文件中增加的节点相对应。

  • 对于value是字符串的属性,使用cat指令就可以查看。
  • 对于value是数值的属性,需要使用hemdump指令才能查看。

🧲内核对设备树的处理

设备树是给驱动程序提供资源的,起到总线驱动模型中platform_device结构体对象的作用,那么内核是如何做到的呢?是怎么处理设备树dts文件的呢?

图
如上图所示,设备树文件从dts文件类型开始,处理流程为:

  • dts在Linux服务器上被编译为dtb文件。
  • u-bootdtb文件传给内核。
  • 内核解析dtb文件,把每一个节点都转换为device_node结构体。
  • 对于某些device_node结构体,内核会将其转换为platform_device结构体。

图

如上图所示device_node结构体定义,设备树中的每一个节点都会被内核解释为这样的一个结构体对象,包含成员:

  • name:节点名称,来自name属性。
  • type:节点类型,来自device_type属性。
  • porperties:节点属性链表,包含节点中的多个属性。
  • parent:父节点。
  • child:字节点。

再看属性结构体struct property的定义:

  • name:属性名称。
  • length:属性值的长度(字节)。
  • value:属性值。

假设属性值是一个字符串,那么此时vaule中存放的就是字符串的首地址,通过length可以获得整个字符串。包括数字也是利用这二者求得的。

🏹会被转换为platform_device的节点

虽然所有节点都会被转换为device_node结构体,但是并不是所有节点都会被转换为platform_device结构体,会被转换为platform_device结构体提供硬件资源的节点要有以下特点:

图

用上面设备树文件为例来说明:

  • 根节点下含有compatible属性的节点。

上面设备树文件中的mytesti2cspi节点都会被转换为platform_device结构体,因为它们都包含compatible属性。

  • 必须是根节点的直系子节点符合该条件时才会转换。
  • 含有特定compatible属性值节点的子节点,该值有四种,只要符合一个就可以。
    • 这四个值是compatible = "simple-bus", "simple-mfd", "is", "arm,amba-bu"

上面设备树文件中mytest子节点mytest@0可以转换,因为mytest节点的compatible属性中有"simple-bus"属性,所以它的子节点mytest@0会被转换。

i2c节点的子节点at24c02不会被转换,因为它的父节点的compatible属性中没有那个四个值之一,同样的spi节点的子节点flash@0也不会被转换。

  • 这两个不会被转换的节点分别挂载在i2c总线和spi总线上,它们如何处理完全由父节点i2c控制器和spi控制器决定。

对于at24c02一般会被其父节点i2c处理成i2c_client结构体对象,对于flash@0一般会被其父节点spi处理成spi_device结构体对象。


将设备树中的某些节点转换为platform_device结构体对象后,该结构体中也有提供资源的resource数组,该数组中的资源值来自第一步转换后的device_node结构体中的属性链表properties

图
如上图所示,在platform_device中有一个dev成员,该成员也是一个结构体,里面含有device_node* of_node成员。该成员of_node就表示根节点。

  • 内核设备树文件中的根节点也处理成一个device_node结构体变量。

所以节点转换为platform_device结构后,可以从of_node根节点中找到设备树中任意一个device_node节点,获取它们属性链表中的任意一个属性,然后存放到platform_device结构体中的resource数组里。

  • 节点中的reg属性转换为platform_device后的资源类型是IORESOURCE_MEM类型。
  • 节点中的interrupts属性转换为platform_device后的资源类型是IORESOURCE_IRQ类型。

🏹匹配驱动程序

和总线驱动模型中一样,设备树中的节点被转换成platform_device结构体以后会插入到总线的Dev链表中,此时就会自动去Drv链表中匹配platform_driver,匹配成功后调用驱动程序中的probe函数。

tu
上图所示是用来匹配的paltform_match函数,之前在总线模型中,只讲解了使用1,3,4三步来匹配,没有讲解第二步,因为这一步是在使用了设备树后才会用到的。

第二步匹配过程:

图
如上图,由设备树节点转换后的platform_device结构体对象,通过dev成员中的of_node成员,可以找到插入Dev链表中新节点的device_node结构体对象,得到新节点的属性。

该结构体前面介绍过:

  • name来自设备树节点中的name属性。
  • type来自设备树节点中的device_type属性。
  • properties中存放节点的所有属性。

图
如上图所示,Drv链表中进行匹配的platform_driver结构体,它的device_driver成员就会包含一个数组of_match_table

图
如上图所示是该数组中存放的每个元素的类型定义,包含:

  • name:所支持节点的name属性。
  • type:所支持节点的device_type属性。
  • compatible:所支持节点的compatible属性。

此时Dev链表中有新插入节点的device_node结构体对象,Drv链表中有驱动程序中的device_driver结构体对象。

图
如上图所示,匹配顺序如下:

  1. 新节点中的compatible属性和驱动程序中的compatible进行匹配,成功则返回,失败则进行第二步。
  2. 新节点的device_type属性和驱动程序中的type进行匹配,成功则返回,失败则进行第三步。
  3. 新节点的name属性和驱动程序的name进行匹配,成功则返回,失败则说明设备树无法匹配成功。

而设备树中建议不再使用device_typename属性,所以基本上只使用设备节点的compatible属性来匹配platform_driver

  • 至此,加上总线驱动模型中的1,3,4步,匹配platform_deviceplatform_driver时一共有1,2,3,4步。

🏹使用没有转换的节点

设备树中所有节点都会转换为device_node结构体,但是并不是所有device_node都会转为为platform_device结构体,这些没有转换的结构体我们该怎么使用它们呢?

  • 没有转换的结构体如何使用device_node中的属性呢?

图
如上图所示of_find_node_by_path函数:

  • 功能:根据节点路径获取device_node结构体指针。
  • 形参:要获取节点的绝对路径。
  • 返回值:device_node结构体指针。

通过该函数可以得到没有转换为platform_device节点的device_node,该节点中包含prorerties属性列表。

图
如上图所示of_find_property函数:

  • 功能:获得指定节点np中名为name的属性。
  • 形参:np是指定节点的device_node结构体指针,name是要寻找属性的名称,lenp是属性长度,即它的值长度。
  • 返回值:得到是要寻找属性的struct property结构体指针。

虽然这样类似的函数有很多,但是使用这两个就可以使用没有转换为platform_device的节点了,其他函数在遇到的时候再讲解。

  • 根据节点路径名先获得该节点的device_node结构体指针。
  • 再根据该指针获得指定name的属性。

🧲总结

要明白引入设备树的原因,以及掌握书写设备树的基本语法,知道设备树文件中常见的节点和属性,了解内核对设备树文件的大致处理流程等知识。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

基于ssm的二手商品交易平台+vue论文

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自古…

基于ssm的航空票务推荐系统的设计与实现论文

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;航班信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广大…

Vue.js学习笔记(1)——Visual Studio Code搭建Vue.js框架

1 安装Node.js 1、下载安装包&#xff1a;进入官网&#xff08;https://nodejs.org/en&#xff09;&#xff0c;下载左侧的稳定版。 2、选择安装位置&#xff0c;不用勾选自动安装必要工具。 其他都默认Next。 配置环境&#xff0c;具体参考本文章&#xff1a; https://blo…

用通俗易懂的方式讲解大模型:基于 Langchain 和 ChatChat 部署本地知识库问答系统

之前写了一篇文章介绍基于 LangChain 和 ChatGLM 打造自有知识库问答系统&#xff0c;最近该项目更新了0.2新版本&#xff0c;这个版本与之前的版本差别很大&#xff0c;底层的架构发生了很大的变化。 该项目最早是基于 ChatGLM 这个 LLM&#xff08;大语言模型&#xff09;来…

操作系统:可变分区管理

有作业序列&#xff1a;作业A要求42K&#xff1b;作业B要求27K&#xff0c;作业C要求22K&#xff0c;作业和空闲内存区如下图所示&#xff0c;请画出最佳适应算法空闲队列图&#xff0c;并分析最佳适应算法是否适合该作业系列。 答&#xff1a;最佳适应算法是按照空闲块由小到大…

解决windows系统找不到msvcr100.dll问题,vcomp100.dll缺失的5个解决方法

在日常使用计算机的过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“找不到vcomp100.dll”的错误。那么&#xff0c;vcomp100.dll究竟是什么文件&#xff1f;为什么会出现丢失的情况&#xff1f;本文将为您详细解析vcomp100.dll的作用、丢失原因以及提…

ssm500基于J2EE的仓库管理系统设计与开发论文

摘 要 如今社会上各行各业&#xff0c;都在用属于自己专用的软件来进行工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。互联网的发展&#xff0c;离不开一些新的技术&#xff0c;而新技术的产生往往是为了解决现有问题而产生的。针对于仓库信息…

Java注解以及自定义注解

Java注解以及自定义注解 要深入学习注解&#xff0c;我们就必须能定义自己的注解&#xff0c;并使用注解&#xff0c;在定义自己的注解之前&#xff0c;我们就必须要了解Java为 我们提供的元注解和相关定义注解的语法。 1、注解 1.1 注解的官方定义 注解是一种元数据形式。…

【Git】Git的基本操作

前言 Git是当前最主流的版本管理器&#xff0c;它可以控制电脑上的所有格式的文件。 它对于开发人员&#xff0c;可以管理项目中的源代码文档。&#xff08;可以记录不同提交的修改细节&#xff0c;并且任意跳转版本&#xff09; 本篇博客基于最近对Git的学习&#xff0c;简单介…

2023年03月15日_GPT4的发布会简单介绍

文章目录 各种考试长度限制图像输入功能开发者API定价评估框架1 - 基准测试表现2 - 文本和图像提示3 - 系统消息功能4 - 真实性、稳定性、可靠性 2023年3月15日 今天凌晨呢 万众瞩目的大型多模态模型 GPT-4正式发布 我们先总结一下发布会的重点 首先 这个模型能够接受图像和…

【Leetcode 39】组合总和 —— 回溯法

39. 组合总和 给你一个无重复元素的整数数组candidates和一个目标整数target &#xff0c;找出candidates中可以使数字和为目标数target的 所有不同组合&#xff0c;并以列表形式返回。你可以按**任意顺序 **返回这些组合。 candidates中的同一个数字可以 无限制重复被选取 。…

Vue3-29-路由-编程式导航的基本使用

补充一个知识点 路由配置中的 name 属性 &#xff1a; 可以给你的 路由 指定 name属性&#xff0c;称之为 命名路由。 这个 name 属性 在 编程式导航 传参时有重要的作用。 命名路由的写法如下 &#xff1a; 像指定 path 一样&#xff0c;直接指定一个 name 属性即可。{path:/d…

GBASE南大通用-小内存单机安装GBase 8c分布式数据库实践

* 这种小内存部署方式仅用于分布式数据库个人学习使用&#xff0c;不建议用于其他用途。 随着数据高并发复杂场景业务需求不断增多&#xff0c;信息数据呈现出爆炸式增长、多源多维、数据类型繁复等特征。在这一趋势下&#xff0c;目前分布式数据库因其架构的天然优势&#xf…

开放网络+私有云=?星融元的私有云承载网络解决方案实例

在全世界范围内的云服务市场上&#xff0c;开放网络一直是一个备受关注的话题。相比于传统供应商的网络设备&#xff0c;开放网络具备软硬件解耦、云原生、可选组件丰富等优势&#xff0c;对云服务商和超大型企业有足够的吸引力。 SONiC作为开源的网络操作系统&#xff0c;使得…

2047过滤空格(C语言)

目录 一&#xff1a;题目 二&#xff1a;思路分析 三&#xff1a;代码 一&#xff1a;题目 二&#xff1a;思路分析 1.首先&#xff0c;这道题是一个字符串的问题&#xff0c;我们要先知道字符串存放在char类型的数组中的&#xff0c;并不是一个变量就可直接存放的下一个完整…

了解 NSA 关于管理 OSS 和 SBOM 的最新指南

开源软件很容易受到恶意行为者的攻击&#xff0c;但软件材料清单可以帮助减轻威胁。美国国家安全局的指导为管理生态系统奠定了坚实的基础。 软件供应链安全仍然是网络安全和软件行业的一个关键话题&#xff0c;并且有充分的理由&#xff0c;从针对大型软件供应商的持续攻击到…

vlc 查看音频有没有声音

播放文件或者实时流 播放文件 选择音频文件 打开网络流 输入实时流地址 查看音频是否有声音

[LitCTF 2023]Vim yyds

[LitCTF 2023]Vim yyds wp 题目页面如下&#xff1a; 搜索一番&#xff0c;没有发现任何信息。题目描述中说到了源码泄露&#xff0c;那么先进行目录扫描。 dirsearch 目录扫描 命令&#xff1a; dirsearch -u "http://node4.anna.nssctf.cn:28588/"返回结果&…

Go 1.22新特性前瞻

美国时间2023年12月20日&#xff0c;Go官方宣布Go 1.22rc1发布&#xff0c;开启了为期2个多月的、常规的公测之旅&#xff0c;Go 1.22预计将于2024.2月份正式发布&#xff01; 除了在官网下载Go 1.22rc1版本进行新特性体验之外&#xff0c;我们还可以通过在线的Go Playground选…

黑马程序员SSM框架-Maven进阶

分模块开发与设计 分模块开发意义 分模块开发 依赖管理 依赖传递 依赖传递冲突问题 可以点击红框按钮查看依赖情况。 可选依赖和排除依赖 继承和聚合 聚合 聚合工程开发 继承 聚合和继承的区别 属性 属性的配置与使用 资源文件引用属性 其他属性&#xff08;了解&#xff0…