当前位置: 首页 > news >正文

驱动-设备树-基本语法

提示:了解驱动设备树相关基础语法

文章目录

  • 前言
  • 参考资料
  • 一、设备树的编译
    • 编译器dtc-dts和dtb 编译和反编译
      • dts 通过 dtc 编译生成dtb
      • dtb 通过 dtc 编译生成dts
  • 二、设备树语法
    • 根节点
    • 子节点
    • reg - address-cells和size-cells 属性节点
      • reg
      • address-cells和size-cells
    • model 属性节点
    • status 属性节点
    • compatible 属性节点
    • aliases属性节点
    • chosen 属性节点
      • 典型应用场景
      • 与内核的交互
      • 注意事项
    • device_type 属性节点
      • 常见用途
      • 内存节点标识
      • 网络设备标识
  • 总结


前言

  • 我理解设备树就是一个配置文件,如上层应用或者服务器开发中的配置文件,太像了,只是语法不一样,描述的内容都是底层相关的,属性不一样。
  • 基于属性、语法稍微不一样,刚接触时候确实很难理解。

多看看,多接触,多理解,这样才能为后续开发打好基础。

参考资料

dtc dts/dtsi dtb的关系
设备树中的dtc dts/dtsi dtb的关系梳理
设备树基本语法
Linux设备树(Device Tree)机制

一、设备树的编译

大家可以参考下上面参考资料,这里面涉及到TS、 DTSI、 DTB 和 DTC 四个名词。
这里直接用一个图来描述吧,如下:
在这里插入图片描述

简单讲:

  • dts 就是设备树的源文件,dtsi 是dts 的头文件可以让所有的dts 文件引用。 dtc 是编译器,dtb 是编译后的文件。
  • 从dts/dtsi 经过dtc 生成了 dtb ,当然 dtb 也可以通过dtc 反编译生成dtc 。

在理解dtc 之前,我么先写一个最简单的设备树吧

/dts-v1/;
/{};

编译器dtc-dts和dtb 编译和反编译

dtc 模块在源码位置
不管什么平台产品,dtc 是补一个设备树编译器模块,位置如下:它本身就是一个模块,里面有MakeFile 编译文件和dtc 可执行文件

kernel/scripts/dtc

在这里插入图片描述

dts 通过 dtc 编译生成dtb

命令如下:

dtc -I dts -O dtb -o output.dtb input.dts
  • input.dts是输入的设备树源文件, output.dtb是编译后的二进制设备树文件。
    编译器会验证设备树源文件的语法和语义, 生成与硬件描述相对应的设备树表示形式。
    自己实际测试,如下:
    在这里插入图片描述

dtb 通过 dtc 编译生成dts

指令如下: 发现和编译指令基本一致

dtc -I dtb -O dts -o output.dts input.dtb
  • input.dtb 是输入的二进制设备树文件, output.dts 是反编译后的设备树源文件。
    反编译器会将二进制设备树文件解析并还原为文本形式的设备树源文件, 使其可读性更

自己测试如下:
在这里插入图片描述

二、设备树语法

根节点

这里直接给出根节点代码 配置如下:

/dts-v1/;   // 设备树版本信息 - 特别注意,这里有个 分好
/{          // 注意这里有个 / 下划线// 根节点开始
/*
在这里可以添加注释, 描述根节点的属性和配置
*/};             // 注意这里有个分号 

根节点应该闭着眼睛就能写出来才对,但是初学者,一定要多练习,出个错太正常了。

子节点

子节点标准如下:

[label:] node-name@[unit-address] {
[properties definitions]
[child nodes]
};
  • 节点标签(Label) (可选) : 节点标签是一个可选的标识符, 用于在设备树中引用
    该节点。 标签允许其他节点直接引用此节点, 以便在设备树中建立引用关系。

  • 节点名称(Node Name) : 节点名称是一个字符串, 用于唯一标识该节点在设备树
    中的位置。 节点名称通常是硬件设备的名称, 但必须在设备树中是唯一的。

  • 单元地址(Unit Address) ( 可选) : 单元地址用于标识设备的实例。 它可以是一个
    整数、 一个十六进制值或一个字符串, 具体取决于设备的要求。 单元地址的目的是区分相同类
    型的设备的不同实例, 例如在下图( 55-6) 中名为 cpu 的节点通过它们的单元地址值 0 和 1 来
    区分, 名称为 ethernet 的节点通过其单元地址值 fe002000 和 fe003000 来区分

  • 属性定义( Properties Definitions) : 属性定义是一组键值对, 用于描述设备的配置
    和特性。 属性可以根据设备的需求进行定义, 例如寄存器地址、 中断号、 时钟频率等, 关于这
    些属性会在后面的小节中进行讲解

  • 子节点( Child Nodes) : 子节点是当前节点的子项, 用于进一步描述硬件设备的子
    组件或配置。 子节点可以包含自己的属性定义和更深层次的子节点, 形成设备树的层次结构。

分析: 其实代码和对应的属性都解释的很清楚了,如果还是很难理解的话,下面分析下:
比如

  • 比如 子设备是一个gpio 子节点:那么简单的子节点应这样定义了就可以了。
/dts-v1/;
/{gpio{    // gpio 就是 node-name};
};

问题:系统gpio 很多很多的,同一个级别有不能相同标签,那么可以加一个内存地址来区别,如下。

  • 添加内存地址
/dts-v1/;
/{gpio@0001110001{    // gpio 就是 node-name   // @0001110001 是内存地址};
};
问题? 虽然 子节点名称+子节点内存地址区分了 子节点,但是好认吗?  实际开发配置过程中,面对 那么多的gpio 关联的节点,看内存地址茫然... 实际这个地址标志一点用都没有,区别子节点而已。 那么 就勇哥标签区分吧- 通过标签来一目了然区别,可以理解为分类吧。 比如 gpio 是什么gpio 呢?  led ? screen ?  可以一目了然区分开来```java
/dts-v1/;
/{led:gpio@0001110001{  // led 就是标签   gpio 就是node-name  地址就是:0001110001};
};

reg - address-cells和size-cells 属性节点

reg属性描述设备占用的地址空间资源,格式通常为:

reg

reg = <地址1 长度1 [地址2 长度2] ...>;

例如

reg = <0x10000000 0x1000>; // 设备从0x10000000开始,占用0x1000字节

这个 reg 属性值就是一个数组,起始地址和地址的总大小,组成的一个数组。 这个数组不一定同时存在 起始地址和内存地址大小的。

address-cells和size-cells

address-cells和size-cells 属性值存在一定是和reg 依赖的,有 reg 地方不一定有 address-cells和size-cells 属性,但是有 address-cells和size-cells 属性地方一定有reg 属性配置。

这两个属性定义reg字段中地址和长度所占的cell数(32位为一个cell):值得就是起始地址和地址大小的位数吧。 那么在 reg 属性数组就是根据address-cell 和 size-cells 划定范围的。

  • #address-cells: 指定地址部分的cell数
  • #size-cells: 指定长度部分的cell数

举例如下:

/dts-v1/;
/{ 
node1{node1{#address-cells = <1>;      // 地址盒子大小是1#size-cells = <1>;          // size 盒子大小是1 node1-child {reg = <0x02200000 0x4000>;     // // 根据 address-cells  size-cells 划定范围,那么reg 里面一个起始地址,一个地址总长达};};
};
node2{#address-cells = <2>;   // 地址盒子大小是2 #size-cells = <0>;      // size 盒子大小是0 node2-child {reg = <0x00 0x01>;    // 根据 address-cells  size-cells 划定范围,那么reg 里面表示的全部都是地址};
};}

model 属性节点

model 属性用于描述设备的型号或者名称。 它通常作为设备节点的一个属性,用来提供关于设备的标识信息。 model 属性是可选的, 但在实际应用中经常被使用

/dts-v1/;
/{  
model = "My Device XYZ";
node1{node1{#address-cells = <1>;#size-cells = <1>;node1-child {reg = <0x02200000 0x4000>;};};
};
node2{#address-cells = <2>;#size-cells = <0>;node2-child {reg = <0x00 0x01>;     };
};led:gpio@0001110001{};
};

status 属性节点

status属性表示设备状态,常用值:

  • “okay”: 设备启用
  • “disabled”: 设备禁用
  • “fail”: 设备存在但初始化失败
  • “fail-sss”: 带错误原因的设备失败

compatible 属性节点

在设备树中, compatible 属性用于描述设备的兼容性信息。 它是设备树中重要的属性之一,用于识别设备节点与驱动程序之间的匹配关系。compatible 属性的值是一个字符串或字符串列表, 用于指定设备节点与相应的驱动程序或设备描述符兼容的规则。 通常, compatible 属性的值由设备的厂商定义, 并且在设备树中使用。
以下是一些常见的 compatible 属性值的示例:
(1) 单个字符串值: 例如 “vendor,device”, 用于指定设备节点与特定厂商的特定设备兼容。
(2) 字符串列表: 例如 [“vendor,device1”, “vendor,device2”], 用于指定设备节点与多个设
备兼容, 通常用于设备节点具有多种变体或配置。
(3) 通配符匹配: 例如 “vendor,*”, 用于指定设备节点与特定厂商的所有设备兼容, 不考
虑具体的设备标识。

上面是一些概念性内容,好像不咋理解,没关系 举例一个实际相关的案例:
compatible 用来和驱动进行匹配,匹配成功以后会执行驱动中的probe 函数。

举例:

my_device {
compatible = "vendor,device";
// 其他属性和子节点的定义
};

在匹配时候会优先使用第一个值 vendor 进行匹配,如果没有就会使用第二个值 device 进行匹配。 回想一下 驱动-平台总线-probe 中,platform 平台总线的device 和 driver 是怎么匹配的,这里是通过设备树 这个属性进行匹配。

aliases属性节点

字如其名,别名的意思。 aliases 节点是 Linux 设备树(DTS)中的一个特殊节点,用于为设备树中的其他节点定义别名(简称)。这些别名提供了一种引用长路径节点的简便方式。

实践代码如下:

/dts-v1/;
/{ model = "My Device XYZ";aliases {gpio0001 = &led;serial0 = &uart0;ethernet0 = &eth0;mmc0 = &sdmmc;mmc2 = "/mmc2@12360000";};node1{node1{#address-cells = <1>;#size-cells = <1>;node1-child {reg = <0x02200000 0x4000>;};};
};
node2{#address-cells = <2>;#size-cells = <0>;node2-child {reg = <0x00 0x01>;     };
};led:gpio@0001110001{};uart0: serial@12340000 {compatible = "vendor,uart";reg = <0x12340000 0x1000>;};eth0: ethernet@12350000 {compatible = "vendor,eth";reg = <0x12350000 0x1000>;};sdmmc: mmc@12360000 {compatible = "vendor,sdmmc";reg = <0x12360000 0x1000>;};mmc2@12360000 {compatible = "vendor,sdmmc";reg = <0x12360000 0x1000>;};};

实践经验如下:

  • 它一定是根节点下面的一个节点,一般放到最前面或者最后面。
  • aliases 节点中定义的别名只在设备树内部可见, 不能在设备树之外引用。它们主要用于设备树的内部组织和引用, 以提高可读性和可维护性
  • 自己实践发现,别名里面的属性后面的属性值,就是子节点的标签;对于没有标签的 别名如何写属性呢? 就写完整路径即可,用引号括起来。

chosen 属性节点

chosen 节点是设备树中的一个特殊节点, 用于传递和存储系统引导和配置的相关信息。
它位于设备树的根部, 并具有路径/chosen。

chosen 节点通常包含以下子节点和属性:

  • (1) bootargs: 用于存储引导内核时传递的命令行参数。 它可以包含诸如内核参数、 设备
    树参数等信息。 在引导过程中, 操作系统或引导加载程序可以读取该属性来获取启动参数。
  • (2) stdout-path: 用于指定用于标准输出的设备路径。 在引导过程中, 操作系统可以使
    用该属性来确定将控制台输出发送到哪个设备, 例如串口或显示屏。北京迅为电子有限公司 iTOP-3568 开发板驱动开发指南
    日期:2022-1-17 471 www.topeetboard.com
  • ( 3) firmware-name: 用于指定系统固件的名称。 它可以用于标识所使用的引导加载程序
    或固件的类型和版本。
  • ( 4) linux,initrd-start 和 linux,initrd-end: 这些属性用于指定 Linux 内核初始化 RAM 磁盘
    ( initrd) 的起始地址和结束地址。 这些信息在引导过程中被引导加载程序使用, 以将 initrd 加
    载到内存中供内核使用。
  • ( 5) 其他自定义属性: chosen 节点还可以包含其他自定义属性, 用于存储特定于系统引
    导和配置的信息。 这些属性的具体含义和用法取决于设备树的使用和上下文。

常见属性

属性描述
bootargs内核启动参数,相当于传统命令行参数
stdout-path指定标准输出设备节点
stdin-path指定标准输入设备节点
linux,initrd-startinitramfs的起始物理地址
linux,initrd-endinitramfs的结束物理地址
linux,usable-memory-range指定内核可用的内存范围

典型应用场景

  • 嵌入式系统启动配置:
chosen {bootargs = "console=ttyAMA0,115200 root=/dev/nfs nfsroot=192.168.1.1:/nfsroot ip=192.168.1.2:192.168.1.1::255.255.255.0::eth0:off";
};
  • 调试配置:
chosen {bootargs = "console=ttyS0,115200 earlyprintk kgdboc=ttyS0,115200";
};
  • 内存限制配置
chosen {linux,usable-memory-range = <0x80000000 0x20000000>;
};

与内核的交互

内核在启动时会解析chosen节点中的信息:

early_init_dt_scan_chosen() 函数处理bootargsearly_init_dt_scan_stdout() 函数处理stdout-path

其他属性由相应的驱动或子系统处理

注意事项

  • chosen节点不描述实际硬件,而是提供配置信息
  • 引导加载程序通常会修改或添加chosen节点内容
  • 某些属性有特定格式要求,如stdout-path需要引用有效的设备节点
  • 在设备树覆盖(DT overlay)中也可以修改chosen节点

device_type 属性节点

  • device_type 是设备树中一个较旧的属性,在现代设备树中使用频率已降低,但在某些特定场景下仍然有用。
  • device_type 属性最初用于描述节点的基本类别或类型,帮助操作系统识别设备的基本功能。

基本用法:

node-name {device_type = "typename";// 其他属性...
};

常见用途

  • CPU节点标识
cpus {#address-cells = <1>;#size-cells = <0>;cpu@0 {device_type = "cpu";compatible = "arm,cortex-a9";reg = <0>;};
};

内存节点标识

memory@80000000 {device_type = "memory";reg = <0x80000000 0x40000000>; // 1GB内存
};

网络设备标识

ethernet@12340000 {device_type = "network";compatible = "vendor,eth";reg = <0x12340000 0x1000>;
};

常见的设备类型包括但不限于:

  • cpu: 表示中央处理器。
  • memory: 表示内存设备。
  • display: 表示显示设备, 如液晶显示屏。
  • serial: 表示串行通信设备, 如串口。北京迅为电子有限公司 iTOP-3568 开发板驱动开发指南
  • ethernet: 表示以太网设备。
  • usb: 表示通用串行总线设备。
  • i2c: 表示使用 I2C (Inter-Integrated Circuit) 总线通信的设备。
  • spi: 表示使用 SPI (Serial Peripheral Interface) 总线通信的设备。
  • gpio: 表示通用输入/输出设备。
  • pwm: 表示脉宽调制设备。

些只是一些常见的设备类型示例, 实际上, 设备类型可以根据具体的硬件和设备树的使用情况进行自定义和扩展。 根据设备类型, 操作系统或其他软件可以加载适当的驱动程序、 配置设备资源、 建立设备之间的连接等

总结

  • 这里只是对设备树知识的基本了解、设备树语法、节点属性基本了解
  • 这些都是基础中的基础,了解和熟悉基础知识后 后面才能延伸扩展自己的知识技能,基础打牢
http://www.dtcms.com/a/299936.html

相关文章:

  • Python爬虫实战:诗词名句网《三国演义》全集
  • 服务器:数字世界的隐形引擎
  • 《基于雅可比矢量近似的EIT触觉传感灵敏度非均匀校正》论文解读
  • ESP32实战:5分钟实现PC远程控制LED灯
  • C++类和对象(三)
  • IC测试之pogo pin学习与总结-20250726
  • 进制定义与转换详解
  • 1.Java发展简史与设计哲学
  • 最优估计准则与方法(5)加权最小二乘估计(WLS)_学习笔记
  • 360° 外壁镜头:小物体环外侧检测的创新突破
  • Python day25
  • MySQL中的 redolog
  • 连锁店铺巡查二维码的应用
  • 单片机CPU内部的定时器——滴答定时器
  • 智慧水库边缘计算技术路线与框架设计
  • 21-ospf多区域
  • Python编程:初入Python魔法世界
  • Java 面向对象之方法与方法重载:从基础到实践
  • Go 多模块仓库标签管理教程
  • 详解Aerospike数据库在Linux系统上的安装流程
  • LLM中 词嵌入向量中的正负值表示什么含义
  • Aerospike与Redis深度对比:从架构到性能的全方位解析
  • 【HTML】<script>元素中的 defer 和 async 属性详解
  • 认识泛型、泛型类和泛型接口
  • 服务器生成图片
  • 力扣872. 叶子相似的树
  • Ubuntu系统安装EasyTier组网
  • rt-thread 5.2.1 基于at-start-f437开发过程记录
  • Flutter开发 BUG 记录 (持续更新)
  • [特殊字符] 嵌入式队列精要指南:数据流的艺术与实战