openHarmony南向开发笔记(四)Linux设备树
文章目录
一、什么是设备树
Linux设备树(Device Tree)是Linux内核用来描述硬件信息的一种数据结构,它在内核启动时被用来初始化硬件。
设备树的使用减少了内核对特定硬件的依赖,使得内核更加通用和可移植。它也简化了硬件的初始化过程,因为内核可以通过读取设备树来自动配置硬件,而不需要为每种硬件编写特定的初始化代码。
在实际使用中,设备树文件通常由硬件制造商提供,并在编译内核时集成到内核映像中。内核启动时会读取设备树,根据设备树中的信息来初始化硬件。
二、设备树的组成部分
- 节点(Nodes):设备树由一系列的节点组成,每个节点代表一个硬件设备或一个硬件属性。节点可以包含子节点,形成一个树状结构。
- 属性(Properties):每个节点可以包含一个或多个属性,属性用来描述节点的特性,比如设备的地址、中断号、时钟频率等。
- 兼容性(Compatibility):设备树中的兼容性属性允许内核识别并支持不同类型的硬件设备,即使它们的硬件描述可能有所不同。
- 绑定(Bindings):内核中的驱动程序使用设备树的绑定信息来确定如何初始化和操作硬件设备。驱动程序会查找与设备树中节点的兼容性属性匹配的绑定。
- 设备树源文件(.dts或.dtsi文件):设备树源文件包含了设备树的文本描述,它们通常由硬件制造商提供,并在编译内核时被转换为二进制格式(.dtb文件)。
- 设备树编译器(Device Tree Compiler,DTC):DTC是一个工具,用于将设备树源文件编译成二进制格式,供内核在启动时使用。
- 动态设备树(Dynamic Device Tree):在某些情况下,设备树可以在运行时被修改,以支持热插拔设备或动态配置硬件。
- 设备树的层次结构:设备树通常从根节点开始,然后分支出不同的总线和控制器,最后是具体的设备。
三、设备树基本语法
-
节点定义:节点是设备树的基本单位,代表一个硬件设备或一个硬件属性。节点以标签开始,后跟属性列表,以分号结束。
/ { node_name { // 属性列表 }; };
-
属性:属性是键值对,用于描述节点的特性。属性定义在节点内部,以键名开始,后跟等号和值。
node_name { property_name = <value>; };
-
值:属性的值可以是字符串、整数、地址、引用等。值可以是简单的,也可以是复杂的,如数组或复合值。
node_name { property_name = "string_value"; property_name = <0x1234>; property_name = &label; };
-
引号:字符串值通常用双引号包围,地址值用尖括号包围。
-
地址和大小:地址和大小通常用尖括号包围,表示为32位或64位的十六进制值。
node_name { reg = <0x1000 0x100>; reg = <0x1000 0x100 0x2000 0x200>; };
-
数组:数组值用花括号包围,元素之间用逗号分隔。
node_name { property_name = /bits/ 8 <0x01 0x02 0x03>; };
-
复合值:复合值用花括号包围,可以包含多个属性。
node_name { property_name = <&parent_label child_label>; };
-
注释:DTS 文件支持C风格的注释。
/* 这是一个注释 */ // 这也是一个注释
-
引用:可以使用
&
符号来引用其他节点的标签。&node_name { // 引用节点 };
-
别名:可以使用
phandle
属性来创建别名,方便其他节点引用。node_name { phandle = <0x1234>; };
-
选择和条件:可以使用
#ifdef
、#ifndef
、#if
、#else
和#endif
来包含条件编译的代码。#ifdef CONFIG_OPTION node_name { // 条件代码 }; #endif
-
包含:可以使用
#include
指令来包含其他 DTS 文件。#include "path/to/file.dtsi"
-
路径:可以使用
/
来表示绝对路径,../
来表示父路径。/parent_node/child_node { // 节点定义 };
完整结构
/dts-v1/;
/ {
description = "Example Device Tree for an Embedded System";
version = "1.0";
#address-cells = <1>; // 用于指定地址单元的数量
#size-cells = <1>; // 用于指定大小单元的数量
// CPU节点
cpus {
#address-cells = <1>;
#size-cells = <0>;
CPU0: cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a7";
reg = <0>; // CPU的编号
status = "okay";
};
};
// 内存节点
memory@0 {
device_type = "memory";
reg = <0x00000000 0x40000000>; // 起始地址和大小
};
// 串行控制台
serial@1c0000 {
compatible = "arm,pl011", "arm,primecell";
reg = <0x1c0000 0x1000>; // 基地址和寄存器大小
interrupts = <0 29 4>; // 中断号
clock-frequency = <3686400>; // 时钟频率
status = "okay";
};
// LED设备
leds {
compatible = "gpio-leds";
led0: led@0 {
label = "led0";
gpios = <&gpio 0 1>; // GPIO控制器的路径、GPIO编号、激活状态
status = "okay";
};
};
// GPIO控制器
gpio: gpio@10000000 {
compatible = "arm,gpio-v2";
reg = <0x10000000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
};
};
// 节点标签引用
&serial0 {
status = "okay";
};
四、dts常用符号含义
在Device Tree Source (DTS) 文件中,有一些常用的符号用于表示特定的数据类型和操作。以下是一些常见的符号及其含义:
/
- 表示根节点。&
- 引用符号,用于引用其他节点或属性。<
>
- 用于表示地址或值的集合,通常用于表示内存地址、寄存器地址等。"
- 双引号,用于表示字符串值。;
- 分号,用于结束一个语句或节点定义。,
- 逗号,用于分隔属性列表中的多个属性或数组中的元素。=
- 等号,用于属性赋值。/bits/
- 用于指定位字段的大小和偏移量。&&
- 用于表示一个复合值,通常用于表示一个结构体或多个值的组合。#
- 用于指定属性列表中地址单元和大小单元的数量。@
- 用于表示一个特定的地址或值。()
- 用于指定一个属性的默认值或用于函数调用。{}
- 花括号,用于定义一个属性的值,尤其是当值是一个数组或复合值时。[]
- 方括号,用于指定一个属性的可选值或用于数组索引。/\* ... \*/
- 用于多行注释。// ...
- 用于单行注释。#ifdef
,#ifndef
,#if
,#else
,#endif
- 用于条件编译。#include
- 用于包含其他DTS文件。/phandle
- 用于指定一个节点的持久句柄,通常用于父子节点之间的引用。<...>
- 用于表示一个地址或值的集合,通常用于表示内存地址、寄存器地址等。&...
- 用于引用其他节点的标签。__overlay__
- 用于定义一个设备树的覆盖层,可以动态地修改设备树。__dts__
- 用于定义一个设备树的源文件。__overlay__
- 用于定义一个设备树的覆盖层,可以动态地修改设备树。