【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.mzph.cn/news/586285.shtml

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

相关文章

LOAM: Lidar Odometry and Mapping in Real-time 论文阅读

论文链接 LOAM: Lidar Odometry and Mapping in Real-time 0. Abstract 提出了一种使用二维激光雷达在6自由度运动中的距离测量进行即时测距和建图的方法 距离测量是在不同的时间接收到的&#xff0c;并且运动估计中的误差可能导致生成的点云的错误配准 本文的方法在不需要高…

Educational cf 160的B题

Problem - B - Codeforces 找到最小操作次数&#xff0c;使得子串对应位与原来字符串对应位不相同。 交换是没有代价的&#xff0c;但是删除有代价。 首先复制两个一模一样的串&#xff0c;我们把下面作为固定串&#xff0c;然后对串中0和1的个数进行计数&#xff0c;由于我…

私有部署ELK,搭建自己的日志中心(六)-- 引入kafka对采集日志进行削峰填谷

一、背景 首先&#xff0c;要说明一点&#xff0c;elk日志中心&#xff0c;是可以缺少kafka组件的。 其次&#xff0c;如果是研发环境下&#xff0c;机器资源紧张的情况下&#xff0c;也是可不部署kafka。 最后&#xff0c;因为kafka的部署是可以独立的&#xff0c;所以本文将…

介绍一款PDF在线工具

PDF是我们日常工作中的一种常见格式&#xff0c;其处理也是我们工作的重要基础性环节&#xff0c;一款可靠的处理工具显得十分重要。 完全免费、易于使用、丰富的PDF处理工具&#xff0c;包括&#xff1a;合并、拆分、压缩、转换、旋转和解锁PDF文件&#xff0c;以及给PDF文件…

IntelliJ IDEA常用快捷键

【1】创建内容&#xff08;新建&#xff09;&#xff1a;altinsert 【2】main方法&#xff1a;psvm 【3】输出语句&#xff1a;sout 【4】复制行&#xff1a;ctrld 【5】删除行&#xff1a;ctrly&#xff08;很多编辑器ctrly是前进操作&#xff0c;如果选择 Delete Line&…

05-C++ 类和对象-继承

类与对象-03 继承与派生 1. 继承的概念 c最重要的特征是代码重用&#xff0c;通过继承机制可以利用已有的数据类型&#xff0c;来定义新的数据类型&#xff0c;新的类不仅拥有旧类的成员&#xff0c;还拥有新定义的成员。 一个 B 类继承于 A 类&#xff0c;或称从类 A 派生…

数字人私人定制

数字人是什么&#xff1f; 在回答这个问题之前&#xff0c;我们先回答另一个问题&#xff0c;人如何与人工智能交流&#xff1f;目前可以通过文字、语音、电脑屏幕、手机屏幕、平板、虚拟现实设备等和人工智能交流&#xff0c;为了得到更好的交流体验&#xff0c;人工智能必然…

php 8.4 xdebug扩展编译安装方法

最新版php8.4 xdebug扩展只能通过编译方式安装, pecl是安装不了的, 编译方法如下 下载最新版xdebug git clone https://github.com/xdebug/xdebug.git 却换入xdebug目录执行编译安装xdebug cd xdebug phpize./configure --enable-xdebugmakemake install3. 配置启用xdebug 这…

使用element中el-cascader级联选择器实现省市区街道筛选(非动态加载)

<template><el-form ref"form" :model"form" label-width"80px"><el-form-item label"地址:" prop"addressList"><el-cascaderv-model"form.addressList":props"props":options&q…

Pandas教程(一)—— 数据结构

前言 Pandas是贯穿数据分析的主要工具之一&#xff0c;它经常和其他数值计算工具一起使用&#xff08;例如&#xff1a;Numpy、SciPy和matplotlib&#xff09;。尽管pandas采用了很多NumPy的代码风格&#xff0c;但二者最大的区别是&#xff1a;pandas主要用于处理表格型或异质…

GBASE南大通用-GBase 8s数据库日志模式及切换

一、 GBase 8s数据库共有以下 4 种日志模式&#xff1a;无日志模式、缓冲日志模式、无缓冲日志模式、ANSI 模式。详细介绍如下&#xff1a; 1、无日志模式&#xff08;Non logging&#xff09;&#xff1a; 采用无日志模式时&#xff0c;所有 DML 操作都不会被记录到日志中&…

IP地理位置定位技术基本原理

IP地理位置定位技术的基本原理是基于IP地址的特性。每个IP地址在网络中都有一个与之对应的地理位置信息&#xff0c;这是通过IP地址数据库来确定的。这个数据库由ISP&#xff08;Internet Service Provider&#xff09;或其它一些机构维护&#xff0c;其中包含了每个IP地址的地…

链表精选题集

目录 1 链表翻转 题目链接&#xff1a; 解题&#xff1a; 试错版&#xff1a; 2 找中间节点 题目链接: 题解&#xff1a; 3 找倒数第k个节点 题目链接&#xff1a; 题解&#xff1a; 4 将两个升序链表合并为一个升序链表 题目链接&#xff1a; 题解&#xff1a; …

tmux 包的介绍及使用

tmux 本博文参照 https://blog.csdn.net/qq_43912191/article/details/123214679 对 tmux 进行总结和归纳。 tmux&#xff08;Terminal Multiplexer&#xff09;是一款命令行下的终端复用软件&#xff0c;用于在一个终端窗口中运行多个终端会话&#xff0c;并且可以在各会话之…

第六课:冷战和消费主义、个人计算机革命、图形用户界面(GUI)及3D图形

第六课&#xff1a;冷战和消费主义、个人计算机革命、图形用户界面&#xff08;GUI&#xff09;及3D图形 第二十四章&#xff1a;冷战和消费主义本课概括&#xff1a;政府和消费者推动了计算机的发展 第二十五章&#xff1a;个人计算机革命本集概括&#xff1a;继续讲计算机发展…

AJAX: 整理2:学习原生的AJAX,这边借助express框架

1. npm install express 终端直接安装 2. 测试案例&#xff1a;Hello World&#xff01; 新建一个express.js的文件&#xff0c;写入下方的内容 // 1. 引入express const express require(express)// 2. 创建服务器 const app express()// 3.创建路由规则 // request 是对请…

mxxWechatBot微信机器人V2版本文档说明

大家伙&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 先看这里 一、前言二、mxxWechatBot流程图三、怎么使用&#xff1f; 一、前言 经过不断地探索与研究&#xff0c;mxxWechatBot正式上线&#xff0c;届时全面开放使用。 mxxWechatBot&am…

【动态规划精选题目】3、简单多状态模型

此动态规划系列主要讲解大约10个系列【后续持续更新】 本篇讲解简单多状态模型中的9道经典题&#xff0c;会在讲解题目同时给出AC代码 目录 1、按摩师 2、力扣198:打家劫舍1 3、打家劫舍II 4、删除并获得点数 5、 粉刷房子 6、力扣309:买卖股票的最佳时机含冷冻期 7、 买…

【数据库系统概论】第7章-数据库设计

文章目录 7.1 数据库设计概述7.2 需求分析7.2.1 需求分析的任务7.2.2 需求分析的难点7.2.2 需求分析的方法7.2.3 数据字典 7.3 概念结构设计7.3.1 概念模型7.3.2 E-R模型7.3.3 概念结构设计 7.4 逻辑结构设计7.4.1 E-R图向关系模型的转换7.4.2 数据模型的优化7.4.3 设计用户子模…

【教程】标注工具Labelimg的安装与使用

图片标注主要是为了建立自己的数据集&#xff0c;便于进行更深度的学习训练。本篇文章将对一款十分好用的图片标注工具labelimg进行介绍&#xff0c;重点介绍其安装以及使用的过程。 - 什么是labelimg labelimg 是一个可视化的图像标定工具。它是用Python编写的&#xff0c;并…