【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的属性。

🧲总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于CNN神经网络的手写字符识别实验报告

作业要求 具体实验内容根据实际情况自拟&#xff0c;可以是传统的BP神经网络&#xff0c;Hopfield神经网络&#xff0c;也可以是深度学习相关内容。 数据集自选&#xff0c;可以是自建数据集&#xff0c;或MNIST&#xff0c;CIFAR10等公开数据集。 实验报告内容包括但不限于&am…

基于metersphere和supper-jacoco 测试覆盖率落地实践

一、背景及目标 背景 1、技术研发流程为测试 提供冒烟用例-开发根据用例自测-提测-开始测试&#xff0c;这一套流程&#xff0c;但是中间开发是否真实执行冒烟&#xff0c;测试并不知晓&#xff0c;而且测试提供冒烟用例是否符合标准也没法进行量化 2、公司产品属于saas产品&…

从0开始界面设计师 Qt Designer

QT程序界面的 一个个窗口、控件&#xff0c;就是像上面那样用相应的代码创建出来的。 但是&#xff0c;把你的脑海里的界面&#xff0c;用代码直接写出来&#xff0c;是有些困难的。 很多时候&#xff0c;运行时呈现的样子&#xff0c;不是我们要的。我们经常还要修改代码调整界…

2023年的Android开发:演进之年

2023年的Android开发&#xff1a;演进之年 在2023年&#xff0c;安卓开发迎来了许多新功能和里程碑&#xff0c;让我们来看看其中的一些关键功能。 Jetpack Compose 1.5.7 Jetpack Compose是一个用于构建安卓用户界面的工具&#xff0c;从Jetpack Compose 1.0到Jetpack Comp…

GBASE南大通用-GBase 8s分片表操作 提升大数据处理性能

目录 一、GBase 8s分片表的优势 二、六种分片方法 轮转 1.轮转法 基于表达式分片 2.基本表达式 3.Mod运算表达式 4.Remainder关键字方式 5.List方式 6.interval 固定间隔 三、分片表的索引 1.创建索引的注意事项 2.detach索引替代delete功能展现 3.在现有分片表上增加一个新…

C语言之指针和数组

指针和数组虽然是不同的东西&#xff0c;但却有着千丝万缕的关系&#xff0c;下面就让我们逐一了解吧&#xff01; 指针和数组 数组名原则上会被解释为指向该数组起始元素的指针。 也就是说。如果a是数组&#xff0c;那么表达式a的值就是a[0]的值&#xff0c;即与&a[0]一…

TikTok真题第9天 | 163.缺失的区间、1861.旋转箱子、2217.找到指定长度的回文数

163.缺失的区间 题目链接&#xff1a;163.missing-ranges 解法&#xff1a; 基本逻辑是&#xff0c;依次遍历nums中的所有的元素&#xff0c;判断这个元素&#xff08;right&#xff09;和上一个元素&#xff08;left&#xff09;的差值是否>2&#xff0c;如果是&#xf…

Cisco模拟器-跨交换机实现VLAN

计要求将两台相互连接的交换机上的VLAN号全局使用&#xff0c;技术上可以使用TRUNK技术的数据包标记功能来实现。 通过设计&#xff0c;可以对多台交换机进行整合&#xff0c;提高网络设备的利用率、降低网络工程的成本&#xff0c;同时也可以简化网络配置。 交换机0配置&…

【privateGPT】使用privateGPT训练您自己的LLM

了解如何在不向提供商公开您的私人数据的情况下训练您自己的语言模型 使用OpenAI的ChatGPT等公共人工智能服务的主要担忧之一是将您的私人数据暴露给提供商的风险。对于商业用途&#xff0c;这仍然是考虑采用人工智能技术的公司最大的担忧。 很多时候&#xff0c;你想创建自己…

.FileZilla的使用和主动模式被动模式介绍

FileZilla的使用和主动模式被动模式介绍 1.FileZilla的使用和主动模式被动模式介绍1.安装下载2.新建组和用户2.1打开后出现如下界面2.2点击编辑打开组这个选项2.3点击添加组以后&#xff0c;点击确认2.4输入组的名称&#xff0c;列如我输入的niyin2.5点击用户选项2.6像上面一样…

Winform RDLC报表(数据库连接、报表函数使用、动态表头)

文章目录 NuGet安装库数据库连接报表设计报表引用添加报表 数据集设计方法一手动添加方法二——连接数据库添加 关联报表与数据集表格数据与数据集数据设计表格格式、字体设计报表数据字段绑定 Winform 使用报表控件数据库填充数据集从数据库获取与数据源相同字段的数据 动态表…

关于求定积分的反函数的导数【认清原函数x变量和反函数x变量】

如图碰到该题该怎么解&#xff1f; 在纸上按①②③的顺序写出这个&#xff0c;其中①是最主要的 第②步和第③步就是在用反函数时要用到的逻辑思维&#xff0c;不是一起用的&#xff0c;你需要用②才去用②&#xff0c;你需要用③才去用③ 在纸上先写出第①步&#xff0c;即 其…

【Linux操作系统】探秘Linux奥秘:操作系统的入门与实战

&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《操作系统实验室》&#x1f516;诗赋清音&#xff1a;柳垂轻絮拂人衣&#xff0c;心随风舞梦飞。 山川湖海皆可涉&#xff0c;勇者征途逐星辉。 目录 &#x1fa90;1 初识Linux OS …

css实现一个斑马条纹动画,实现一个理发店门口的小转转,进度条动画同理!

css实现一个斑马条纹动画&#xff0c;实现一个理发店门口的小转转 前置基础知识 css背景background的重复渐变属性repeating-linear-gradient() 该属性类似于linear-gradient(),但他会在整个方向上重复渐变以覆盖整个容器 一、先写一个普通渐变例子linear-gradient() &…

leetcode贪心算法题总结(三)

本章目录 1.合并区间2.无重叠区间3.用最少数量的箭引爆气球4.整数替换5.俄罗斯套娃信封问题6.可被三整除的最大和7.距离相等的条形码8.重构字符串 1.合并区间 合并区间 class Solution { public:vector<vector<int>> merge(vector<vector<int>>&…

ZigBee案例笔记 - 无线点灯

文章目录 无线点灯实验概述工程关键字工程文件夹介绍Basic RF软件设计框图简单说明工程操作Basic RF启动流程Basic RF发送流程Basic RF接收流程 无线点灯案例无线点灯现象 无线点灯实验概述 ZigBee无线点灯实验&#xff08;即Basic RF工程&#xff09;&#xff0c;由TI公司提供…

第一讲:BeanFactory和ApplicationContext

BeanFactory和ApplicationContext 什么是BeanFactory 它是ApplicationContext的父接口它才是Spring的核心容器&#xff0c;主要的ApplicationContext实现都组合了它的功能 BeanFactory能做什么? 表面上看BeanFactory的主要方法只有getBean()&#xff0c;实际上控制反转、基…

Spring-5-切入点的高级使用

Spring提供了两个额外的Pointcut实现&#xff0c;分别是ComposablePointcut和ControlFlowPointcut,它们提供了所需的灵活性。 使用控制流切入点 由ControlFlowPointcut类实现的Spring控制流切入点类似于许多其他AOP实现中可用的cflow构造&#xff0c;尽管功能上没有那么强大。…

(self-supervised learning)Event Camera Data Pre-training

Publisher: ICCV 2023 MOTIVATION OF READING: 自监督学习、稀疏事件 NILM link: https://arxiv.org/pdf/2301.01928.pdf Code: GitHub - Yan98/Event-Camera-Data-Pre-training 1. Overview Contributions are summarized as follows: 1. A self-supervised framework f…

“产品经理必懂的关键术语“

产品经理是现代企业中非常重要的一个角色&#xff0c;他们负责制定产品策略、规划产品开发流程、管理产品质量和用户反馈等等。然而&#xff0c;对于产品经理来说&#xff0c;了解并掌握相关的专业术语是非常重要的。本篇文章会介绍一些产品经理需要掌握的专业术语&#xff0c;…