设备树OF函数:从硬件描述到驱动加载的隐秘桥梁
在嵌入式Linux开发领域,设备树已经彻底改变了硬件描述的方式。它不再需要将硬件信息硬编码到内核中,而是通过一种声明式的语言来描述系统硬件拓扑。然而,设备树本身只是静态的数据结构,真正让这些数据"活起来"的,是Linux内核提供的一系列OF(Open Firmware)函数。这些函数构成了硬件描述与驱动加载之间的关键桥梁,让驱动程序能够动态地获取和解析硬件信息。
对于嵌入式驱动开发者和系统移植工程师来说,深入理解OF函数的工作原理和使用方法至关重要。它们不仅是驱动开发的基础工具,更是实现跨平台兼容性和减少内核冗余代码的核心机制。通过OF函数,驱动程序可以从设备树中提取寄存器地址、中断号、时钟配置等关键资源,实现真正的硬件无关编程。
1. 设备树基础与OF函数架构
设备树本质上是一种树形结构的数据格式,用于描述硬件平台的组成和连接关系。它由节点(node)和属性(property)组成,每个节点代表一个硬件设备或总线,属性则描述该设备的特性和资源。Linux内核在启动过程中会解析设备树二进制文件(DTB),将其转换为内部的device_node结构体链表。
OF函数的设计遵循了清晰的层次结构。在include/linux/of.h中定义了核心的数据结构和基础函数,而更专业的功能则分散在多个头文件中:
#include <linux/of.h> // 核心设备树操作函数
#include <linux/of_address.h> // 地址相关操作
#include <linux/of_irq.h> // 中断相关操作
#include <linux/of_gpio.h> // GPIO相关操作
#include <linux/of_platform.h> // 平台设备相关操作
设备树中的每个节点在内核中都对应一个device_node结构体,其关键成员包括:
struct device_node {
const char *name; // 节点名称
const char *full_name; // 节点全路径名
struct property *properties; // 属性链表
struct device_node *parent; // 父节点
struct device_node *child; // 子节点
struct device_node *sibling; // 兄弟节点
// ... 其他成员
};
属性则由property结构体表示:
struct property {
char *name; // 属性名称
int length; // 属性值长度
void *value; // 属性值指针
struct property *next; // 下一个属性
// ... 其他成员
};
这种数据结构设计使得内核能够高效地遍历和查询设备树信息,为驱动提供所需的硬件配置数据。
2. 节点查找与遍历技术
在驱动开发中,首先需要定位到设备树中对应的节点。Linux内核提供了多种节点查找函数,每种都有其特定的使用场景。
通过路径查找节点是最直接的方式,适用于已知节点完整路径的情况:
struct device_node *np = of_find_node_by_path("/soc/usb@fe380000");
if (!np) {
pr_err("USB node not found\n");
return -ENODEV;
}
通过兼容性查找节点是最常用的方法,它基于设备的兼容性字符串进行匹配:
struct device_node *np = of_find_compatible_node(NULL, NULL, "vendor,usb-device");
if (!np) {
pr_err("Compatible USB device not found\n");
return -ENODEV;
}
对于需要遍历子节点的情况,可以使用迭代方式访问所有子节点:
struct device_node *child;
for_each_child_of_node(parent_node, child) {
const char *node_name = of_node_full_name(child);
pr_info("Found child node: %s\n", node_name);
// 处理每个子节点
}
在实际驱动中,经常需要处理节点引用和phandle解析。设备树允许节点间相互引用,这是通过phandle(指针句柄)实现的:
// 获取被引用的节点
struct device_node *ref_node = of_parse_phandle(np, "phy-handle", 0);
if (!ref_node) {
pr_warn("Failed to get phy handle\n");
} else {
// 使用引用节点
of_node_put(ref_node); // 减少引用计数
}
提示:每次使用
of_find_*系列函数获取节点后,都需要调用of_node_put()来释放引用计数,防止内存泄漏。
节点查找函数的性能特征各不相同,下表对比了主要查找方法的特点:
| 查找函数 | 时间复杂度 | 使用场景 | 推荐指数 |
|---|---|---|---|
| < |

371

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



