在 Linux 内核中,设备树(Device Tree) 的操作通过一组 of_ 开头的函数(OF 表示 Open Firmware,即设备树标准来源)实现。这些函数定义在 include/linux/of.h 和 drivers/of/ 目录下,用于解析设备树节点、属性及硬件资源。以下是常用函数的分类总结,基本上分两类,一类寻找节点及父子兄弟关系,一类读取节点属性,还有一种是获取remote-endpoint的跨节点操作;
核心数据结构 device_node:
struct device_node {
const char *name; //节点名字
phandle phandle; //节点phandle,u32类型
const char *full_name; //节点全路径名字
struct fwnode_handle fwnode;
struct property *properties;//节点内所有属性链表
struct property *deadprops; /* removed properties */ // 被标记/delete-property/属性
struct device_node *parent;
struct device_node *child;
struct device_node *sibling; //并行的、同层次的几个兄弟节点链表
#if defined(CONFIG_OF_KOBJ)
struct kobject kobj;//对接驱动模型、sysfs需要的kobj
#endif
unsigned long _flags;//表明节点处理状态
void *data;
};
1. 设备树节点操作
(1) 节点查找与遍历
| 函数 | 作用 | 示例 |
|---|---|---|
of_find_node_by_name(root, name) | 通过节点名查找节点 | node = of_find_node_by_name(NULL, "memory"); |
of_find_node_by_path(path) | 通过完整路径查找节点 | node = of_find_node_by_path("/soc/usb"); |
of_find_compatible_node(root, compat) | 通过 compatible 字符串查找节点 | node = of_find_compatible_node(NULL, NULL, "ti,omap3-uart"); |
of_get_parent(node) | 获取父节点 | parent = of_get_child_by_name(node, "interrupt-controller"); |
of_get_next_child(parent, prev) | 遍历子节点(需循环) | while ((child = of_get_next_child(parent, child)) != NULL) { ... } |
of_get_next_available_child() | 遍历可用的子节点(跳过 status = "disabled" 的节点) |
(2) 节点状态检查
| of_device_is_available(node) | 检查节点是否可用(status = "okay")
比如 | if (of_device_is_available(node)) { ... } |
| of_node_name_eq(node, name) | 比较节点名是否匹配
比如| if (of_node_name_eq(node, "uart0")) { ... } |
2. 属性(Property)操作
(1) 读取属性值
| 函数 | 作用 | 示例 |
|---|---|---|
of_property_read_u32(node, prop, &val) | 读取 32 位整数属性 | of_property_read_u32(node, "clock-frequency", &clk); |
of_property_read_u64() | 读取 64 位整数属性 | |
of_property_read_string() | 读取字符串属性 | of_property_read_string(node, "compatible", &str); |
of_property_read_bool() | 检查布尔属性是否存在(如 "enable") | if (of_property_read_bool(node, "dma-coherent")) { ... } |
of_property_count_elems_of_size() | 获取属性中元素的数量(如数组长度) | len = of_property_count_elems_of_size(node, "reg", sizeof(u32)); |
(2) 特殊属性处理
| of_get_address() | 解析 reg 属性中的地址和大小(用于寄存器映射) | addr = of_get_address(node, 0, &size, &flags); |
| of_get_mac_address() | 读取 MAC 地址属性(如 "local-mac-address") | of_get_mac_address(node, mac_addr); |
| of_irq_get(node, index) | 解析中断号(interrupts 属性) | irq = of_irq_get(node, 0); |
3. 资源管理(寄存器、中断、DMA等)
| 函数 | 作用 |
|---|---|
of_iomap(node, index) | 映射寄存器地址到内核虚拟地址(替代 ioremap) |
of_dma_configure() | 配置 DMA 参数(如 dma-ranges) |
of_clk_get(node, index) | 获取时钟资源 |
of_reset_control_get() | 获取复位控制器 |
示例(映射寄存器):
void __iomem *regs = of_iomap(node, 0);
if (!regs) {
pr_err("Failed to map registers\n");
return -ENXIO;
}
4. 设备树与平台设备
| 函数 | 作用 |
|---|---|
of_platform_populate(parent) | 根据设备树节点自动创建平台设备 |
of_find_device_by_node(node) | 通过节点找到关联的 platform_device |
5. 常用工具宏
| 宏 | 作用 |
|---|---|
for_each_child_of_node(parent, child) | 循环遍历所有子节点 |
for_each_property_of_node(node, prop) | 循环遍历节点所有属性 |
of_node_put(node) | 减少节点引用计数(防止内存泄漏) |
6. 实际应用示例
(1) 解析 UART 设备节点
struct device_node *node;
u32 baud_rate;
node = of_find_compatible_node(NULL, NULL, "ns16550a");
if (!node) {
pr_err("UART node not found\n");
return -ENODEV;
}
if (of_property_read_u32(node, "current-speed", &baud_rate))
baud_rate = 115200; // 默认值
pr_info("UART baud rate: %d\n", baud_rate);
of_node_put(node);
(2) 处理中断
int irq = of_irq_get(node, 0);
if (irq <= 0) {
dev_err(dev, "Failed to get IRQ\n");
return irq ? : -ENXIO;
}
总结
- 核心头文件:
#include <linux/of.h> - 关键设计思想:通过设备树解耦硬件描述与驱动代码,提高可移植性。
- 调试工具:
- 查看设备树:
cat /proc/device-tree或dtc -I fs /proc/device-tree - 内核配置:确保
CONFIG_OF启用。
- 查看设备树:
这些函数是 Linux 驱动开发中操作设备树的核心接口,熟练掌握后可高效编写与硬件无关的驱动代码。
2405

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



