1.linux 设备树相关的数据结构
Linux设备树(Device Tree)是一种描述硬件配置的树形数据结构,通过节点(Node)和属性(Property)的层级关系,抽象化表示CPU、内存、总线、外设等硬件信息。其核心数据结构及实现逻辑如下:
一、设备树节点(Device Tree Node)
设备树由节点构成,每个节点表示一个硬件设备或逻辑组件。节点可嵌套子节点,形成树状结构。
1. 节点结构
- 节点命名:格式为
node-name@unit-address,其中node-name为设备类型(如cpu、i2c),unit-address为设备地址(可选)。 - 根节点:唯一根节点
/,描述整个系统信息(如compatible属性定义硬件平台)。 - 子节点:例如CPU节点、内存节点、外设节点等。
2. 内核表示
在Linux内核中,节点通过struct device_node表示,关键字段包括:
name: 节点名称。parent: 父节点指针。child: 子节点链表。properties: 属性链表(struct property)。full_name: 节点完整路径(如/soc/i2c@021a0000)。
二、属性(Property)
属性是键值对,描述节点配置信息。属性值可以是字符串、整数、字节数组等。
1. 标准属性示例
compatible:驱动匹配关键属性,格式为"厂商,型号"(如compatible = "fsl,imx6ull-gpmi-nand")。reg:设备地址及长度,格式为<address length>,受父节点#address-cells和#size-cells影响。interrupts:中断号及触发方式(如interrupts = <0 2 4>表示IRQ 0,边沿触发)。status:设备状态("okay"启用,"disabled"禁用)。
2. 自定义属性
用户可定义任意属性(如custom-data = <0x12 0x34>),驱动通过of_property_read_u32_array等API读取。
3. 内核表示
属性通过struct property表示,关键字段包括:
name: 属性名。length: 属性值长度。value: 属性值指针(字节数组)。
三、设备树二进制格式(DTB)
设备树源文件(.dts)编译为二进制文件(.dtb),内核启动时解析。DTB结构包括:
- Header:魔数(
0xd00dfeed)、总大小、结构块偏移等。 - Structure Block:线性化节点数据,包含节点开始/结束标记、属性数据。
- Strings Block:所有属性名的字符串表,通过偏移量引用。
四、内核解析流程
- Bootloader传递:U-Boot将DTB加载到内存,通过寄存器(如ARM的
r2)传递地址给内核。 - 解压与校验:内核验证DTB完整性,解压为内存中的设备树结构。
- 遍历与初始化:
- 使用
of_find_node_by_path、of_get_property等API遍历节点和属性。 - 根据
compatible属性匹配驱动,调用probe函数初始化设备。
- 使用
五、关键数据结构关系图
设备树结构
├─ / (根节点)
│ ├─ compatible = "vendor,platform"
│ └─ memory@80000000
│ ├─ device_type = "memory"
│ └─ reg = <0x80000000 0x40000000>
├─ soc (SoC节点)
│ ├─ #address-cells = <1>
│ ├─ #size-cells = <1>
│ └─ i2c@021a0000
│ ├─ compatible = "fsl,imx6q-i2c"
│ └─ reg = <0x021a0000 0x4000>
└─ chosen (启动参数节点)
└─ bootargs = "console=ttyS0,115200"
六、操作API示例
#include <linux/of.h>
// 查找节点
struct device_node *np = of_find_node_by_path("/soc/i2c@021a0000");
// 读取属性
const u32 *reg;
u32 reg_len;
of_property_read_u32_array(np, "reg", reg, ®_len);
// 遍历子节点
for_each_child_of_node(np, child) {
// 处理子节点
}
总结
设备树通过节点和属性抽象硬件,内核通过struct device_node和struct property解析配置,实现驱动与硬件解耦。这种设计使得同一内核支持多硬件平台,无需修改内核代码,仅需更换设备树文件。
2.设备树相关的结构体
Linux设备树(Device Tree)通过一组核心数据结构抽象硬件配置,使内核与硬件解耦。以下是其关键结构体及作用详解:
一、核心结构体:struct device_node
表示设备树中的一个节点,描述一个硬件设备或逻辑组件。
字段解析
| 字段 | 说明 | 示例 |
|---|---|---|
name | 节点名称(不含@unit-address) | cpu、i2c |
type | 设备类型 | memory、serial |
full_name | 节点完整路径 | /soc/i2c@021a0000 |
properties | 属性链表指针 | 存储compatible、reg等属性 |
parent/child/sibling | 树形结构指针 | 用于遍历父子/兄弟节点 |
phandle | 唯一句柄 | 用于跨节点引用(如中断控制器) |
内核操作
- 解析函数:
__unflatten_device_tree()将DTB解析为device_node树。 - 遍历API:
struct device_node *child = NULL; for_each_child_of_node(parent_node, child) { // 遍历子节点 }
二、属性结构体:struct property
描述设备树节点的配置信息(键值对)。
字段解析
| 字段 | 说明 | 示例值 |
|---|---|---|
name | 属性名 | compatible、reg |
value | 属性值(字节数组) | "fsl,imx6ull-gpmi-nand"、<0x80000000 0x40000000> |
length | 属性值长度 | strlen("fsl,imx6ull-gpmi-nand") |
标准属性示例
compatible:驱动匹配关键属性,格式为"厂商,型号"。reg:设备地址及长度,受父节点#address-cells和#size-cells影响。interrupts:中断号及触发方式(如<0 2 4>表示IRQ 0,边沿触发)。
API操作
// 读取属性值
const u32 *reg_val;
u32 reg_len;
of_property_read_u32_array(np, "reg", reg_val, ®_len);
// 读取字符串属性
const char *model;
of_property_read_string(np, "model", &model);
三、通用硬件节点标识符:struct fwnode_handle
抽象设备树节点,支持跨硬件描述接口(如ACPI)操作。
应用场景
- 在驱动中动态获取硬件配置(如SPI/GPIO参数),无需硬编码内核。
- 示例:通过
fwnode_handle获取I2C控制器地址。
四、设备树二进制格式(DTB)结构
内核启动时解析的二进制格式,包含以下部分:
- Header
- 魔数(
0xd00dfeed)、总大小、结构块偏移等。
- 魔数(
- Structure Block
- 线性化节点数据,包含节点开始/结束标记(
OF_DT_BEGIN_NODE/OF_DT_END_NODE)。 - 属性数据格式:
[属性值长度][属性名偏移][属性值]。
- 线性化节点数据,包含节点开始/结束标记(
- Strings Block
- 存储所有属性名的字符串表,通过偏移量引用。
五、内核解析流程
-
Bootloader传递
U-Boot将DTB加载到内存,地址通过寄存器(如ARM的r2)传递给内核。 -
解压与校验
内核验证DTB完整性,解压为内存中的设备树结构。 -
遍历与初始化
- 使用
of_find_node_by_path、of_get_property等API遍历节点和属性。 - 根据
compatible属性匹配驱动,调用probe函数初始化设备。
- 使用
六、代码示例
#include <linux/of.h>
// 查找I2C控制器节点
struct device_node *i2c_node = of_find_node_by_path("/soc/i2c@021a0000");
// 读取reg属性
const u32 *reg;
u32 reg_len;
of_property_read_u32_array(i2c_node, "reg", reg, ®_len);
// 遍历子节点(如I2C设备)
struct device_node *child;
for_each_child_of_node(i2c_node, child) {
// 处理子节点(如传感器、EEPROM)
}
总结
设备树通过struct device_node和struct property抽象硬件,内核通过解析DTB构建树形结构,驱动通过compatible属性匹配并初始化设备。struct fwnode_handle提供跨硬件接口的抽象能力,增强代码复用性。此设计使同一内核支持多硬件平台,无需修改内核代码,仅需更换设备树文件。
Linux设备树数据结构及内核解析
794

被折叠的 条评论
为什么被折叠?



