详细介绍了设备树(Device Tree)的结构和语法,包括节点的组织方式、属性的类型以及如何使用这些结构和属性来描述硬件设备。以下是对这些知识点的详细分析和介绍,以及相应的代码注释。
DTS结构
设备树的基本单元是节点(node),这些节点被组织成树状结构。每个节点代表一个设备或总线,并包含描述该节点的属性(properties)。以下是设备树结构的关键要素:
- 根节点:设备树的根节点使用
/
表示,它没有父节点。 - 子节点:根节点和其它节点可以包含子节点,形成层次结构。
- 属性:节点中的属性用于描述节点的特性,如设备类型、内存地址、中断号等。
代码示例和注释:
/dts-v1/; // 指定设备树的版本
/ { // 根节点开始
node1 { // 定义一个节点node1
a-string-property = "A string"; // 定义一个字符串类型的属性
a-string-list-property = "first string", "second string"; // 定义一个字符串列表属性
a-byte-data-property = [01 23 34 56]; // 定义一个字节数据属性,常用于存储二进制数据
child-node1 { // node1的子节点
first-child-property; // 子节点的属性,可以不赋值
second-child-property = <1>; // 定义一个单元属性,使用尖括号表示
a-string-property = "Hello, world"; // 另一个字符串属性
};
child-node2 { // node1的另一个子节点,可以为空
};
};
node2 { // 定义另一个节点node2
an-empty-property; // 一个空属性,表示存在但不赋值
a-cell-property = <1 2 3 4>; // 定义一个单元属性列表,每个数字是32位无符号整数
child-node1 { // node2的子节点
// 子节点可以包含更多的属性和子节点
};
};
}; // 根节点结束
DTS语法介绍
DTS语法定义了如何在源文件中表示设备树的结构。以下是DTS语法的关键要素:
- 节点定义:使用花括号
{}
定义节点,节点名称可以包含@
和单元地址。 - 属性定义:在节点内部定义属性,属性值可以是字符串、单元列表或其他数据类型。
- 注释:使用
//
进行单行注释。
代码示例和注释:
/* 定义一个节点,可能代表一个具体的硬件设备或总线 */
node-name@unit-address {
/* 定义一个标签,方便在DTS文件中引用 */
[label:] node-name[@unit-address] {
/* 属性定义 */
[properties definitions]
/* 子节点定义 */
[child nodes]
}
};
/* 基本数据类型示例 */
- 文本字符串:以双引号括起来的字符串,如 "a string"。
- 单元(cells):32位无符号整数,以尖括号括起来,如 <0xbeef 123 0xabcd1234>。
- 二进制数据:以方括号括起来的字节序列,如 [0x01 0x23 0x45 0x67]。
/* 属性可以包含多种类型的数据 */
mixed-property = "a string", [0x01 0x23 0x45 0x67], <0x12345678>;
/* 属性列表,由多个字符串组成 */
string-list = "red fish", "blue fish";
dts的组成
标准属性
compatible
compatible
属性用于指定设备的兼容性字符串,内核通过这些字符串来确定使用哪个驱动程序。
/ {
compatible = "manufacturer,model", "generic,type"; // 定义设备兼容的字符串
};
#address-cells和#size-cells
这两个属性定义了在设备树中表示地址和大小所需的单元数(通常是32位或64位)。
soc {
#address-cells = <1>; // 地址使用1个单元
#size-cells = <1>; // 大小使用1个单元
/* 其它节点和属性 */
};
CPU addressing
CPU节点的地址分配通常很简单,每个CPU都有一个唯一的ID。
cpus {
#address-cells = <1>; // CPU ID使用1个单元
#size-cells = <0>; // CPU没有关联的大小
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0>; // CPU的ID
};
/* 其它CPU节点 */
};
Memory Mapped Devices
内存映射设备需要同时指定基地址和长度。
/ {
#address-cells = <1>;
#size-cells = <1>;
serial@101f0000 {
compatible = "arm,pl011";
reg = <0x101f0000 0x1000>; // 基地址和长度
};
/* 其它内存映射设备 */
};
Non Memory Mapped Devices
非内存映射设备,如I2C设备,通常只分配一个地址,没有长度。
i2c@1,0 {
compatible = "acme,a1234-i2c-bus";
#address-cells = <1>;
#size-cells = <0>;
reg = <1 0 0x1000>; // I2C总线地址
/* 其它非内存映射设备 */
};
Ranges (Address Translation)
ranges
属性用于地址转换,将子地址空间映射到父地址空间。
external-bus {
#address-cells = <2>;
#size-cells = <1>;
ranges = <0 0 0x10100000 0x10000>; // 地址转换规则
/* 子设备 */
};
status
属性表示设备的状态,如"okay"表示设备正常工作,"disabled"表示设备被禁用。
device {
status = "okay"; // 设备状态
};
中断映射
中断映射定义了设备如何连接到中断控制器,以及如何描述中断。
代码示例和注释:
soc {
open-pic {
interrupt-controller; // 声明一个中断控制器
#interrupt-cells = <2>; // 中断控制器需要2个单元来描述一个中断
/* 中断控制器的其他属性 */
};
pci {
#interrupt-cells = <1>; // PCI设备的中断使用1个单元描述
/* PCI设备的其他属性 */
};
/* 其他总线或设备的中断映射 */
};
特殊节点
特殊节点,如aliases
,用于定义节点的别名,简化对节点的引用。
aliases {
ethernet0 = ð0; // 定义ethernet0的别名为eth0
serial0 = &serial0; // 定义serial0的别名为serial0
};
这些知识点构成了设备树的基本语法和结构,允许开发者以一种标准化的方式来描述硬件设备和它们的属性。通过这些描述,Linux内核可以在启动时识别和配置硬件设备。