设备树节点 相关 of 函数
前言
我们前面一致再看设备树关联的内容:设备树语法、设备树节点、设备树配置、设备树中设备和驱动匹配等。 总言之 设备数就是来描述设备属性的配置文件、映射文件。 那么就像服务器、前端开发那样 js、json、dom 最终都是需要解析拿到节点数据的。前面也初步了解了 device_node 等基本知识。 这里其实就是获取 节点的相关知识,同时也理解相关的获取属性的相关知识。
一、参考资料
驱动-设备树和驱动自动匹配
<linux/of_device.h>头文件
linux设备树-设备树常用OF操作函数
内核对设备树的处理与使用
Linux 内核:利用of_函数读取设备树结点/属性信息
二、of 函数获取节点和属性基础
of.h 头文件
<linux/of.h> 是 Linux 内核中处理设备树(Device Tree)与平台设备(platform device)交互的核心头文件,主要提供设备树节点与平台设备之间的转换和匹配功能。
核心功能
- 设备树节点 ↔ 平台设备转换
将设备树中描述的硬件节点(device_node)转换为内核中的平台设备(platform_device)
处理内存资源、IRQ等硬件资源的映射 - 平台设备操作
提供平台设备创建、注册和查找的接口
处理设备树节点与平台设备之间的关联关系
提供了节点属性结构体-property-device_node
这个 device_node 、property 不就是我们在driver 层 和device 匹配后,获取设备树里面的节点和属性的描述嘛

of 函数获取节点 device_node
device_node 结构体定义在 <linux/of.h> 中,表示设备树中的一个节点(Device Tree Node),每个设备树节点在内核中都会对应一个 device_node 实例。
结构体初识
struct device_node {
const char *name; // 节点名称(对应设备树中的 `label`)
const char *type; // 设备类型(已逐渐被 compatible 取代)
phandle phandle; // 节点的唯一标识符
const char *full_name; // 节点的完整路径名
struct property *properties; // 节点属性链表
struct device_node *parent; // 父节点
struct device_node *child; // 子节点
struct device_node *sibling; // 兄弟节点
// 其他内部管理成员...
};
其实这部分知识,之前在 驱动-设备树和驱动自动匹配 里面就初步了解过了,为什么系统里面会有这个 device_node 等基础知识。
我们看看实际中,of 有哪些api 获取device_node,如下代码补全可以看到有些api,我们将举例说明。

新增的测试节点
/{
topeet{
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
myLed{
compatible = "my devicetree";
reg = <0xFDD60000 0x00000004>;
};
};
};
三、of 函数 节点相关API
of_find_node_by_name
函数原型如下:
struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
参数说明:
- from: 开始搜索的节点指针,通常设置为 NULL 表示从根节点开始搜索
- name: 要查找的节点名称
返回值:
- 成功: 返回找到的设备节点指针 (struct device_node *)
- 失败: 返回 NULL
使用说明:
- 该函数用于在设备树中通过节点名称查找特定的节点
- 节点名称是指在设备树中对应节点的 name 属性
- 使用完毕后不需要手动释放返回的 device_node 结构体
- 该函数会遍历整个设备树,直到找到匹配的节点或遍历完所有节点
device_node 属性蛮多的,我们获取名字先

示例代码:
还是在上一节我们分析 设备树和driver 自动匹配demo 里面修改,如下:
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
struct device_node *mydevice_node;
// 平台设备的初始化函数
static int my_platform_probe(struct platform_device *pdev)
{
printk(KERN_INFO "my_platform_probe: Probing platform device\n");
// 添加设备特定的操作
// ...
mydevice_node = of_find_node_by_name(NULL, "topeet");
printk("my_platform_probe mydevice_node->name is %s\n", mydevice_node->name);
return 0;
}
// 平台设备的移除函数
static int my_platform_remove(struct platform_device *pdev)
{
printk(KERN_INFO "my_platform_remove: Removing platform device\n");
// 清理设备特定的操作
// ...
return 0;
}
static const struct of_device_id of_match_table_id[] = {
{.compatible = "my devicetree"}, // 匹配设备树中的 compatible
{/* 结束标志 */}};
// 定义平台驱动结构体
static struct platform_driver my_platform_driver = {
.probe = my_platform_probe,
.remove = my_platform_remove,
.driver = {
.name = "my_platform_device",
.owner = THIS_MODULE,
.of_match_table = of_match_table_id,
},
};
// 模块初始化函数
static int __init my_platform_driver_init(void)
{
int ret;
// 注册平台驱动
ret = platform_driver_register(&my_platform_driver);
if (ret)
{
printk(KERN_ERR "Failed to register platform driver\n");
return ret;
}
printk(KERN_INFO "my_platform_driver: Platform driver initialized\n");
return 0;
}
// 模块退出函数
static void __exit my_platform_driver_exit(void)
{
// 注销平台驱动
platform_driver_unregister(&my_platform_driver);
printk(KERN_INFO "my_platform_driver: Platform driver exited\n");
}
module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wfc");
其中核心代码如下;
mydevice_node = of_find_node_by_name(NULL, "topeet");
printk("my_platform_probe mydevice_node->name is %s\n", mydevice_node->name);
make 编译,push .ko 文件到系统中,然后insmod 加载驱动查看效果:

of_find_node_by_path
of_find_node_by_path 是 Linux 设备树(Device Tree)驱动开发中通过完整路径查找设备树节点的函数,比 of_find_node_by_name 更精确。
函数原型:
struct device_node *of_find_node_by_path(const char *path);
参数说明:
path: 要查找的节点路径字符串
返回值
- 成功: 返回找到的设备节点指针 (struct device_node *)
- 失败: 返回 NULL
使用说明
-
该函数通过节点在设备树中的完整路径来查找特定节点
-
路径格式可以是:
绝对路径:以 / 开头,如 /soc/usb
别名路径:以 & 开头,如 &i2c1(对应设备树中的 aliases 部分)
- 使用完毕后不需要手动释放返回的 device_node 结构体
- 查找效率高于 of_find_node_by_name,特别适用于已知完整路径的情况
注意事项:
-
路径查找比名称查找更精确,推荐优先使用
-
通过别名查找(&i2c1)是最常用的方式,因为:
不依赖具体硬件布局
设备树修改时驱动代码不需要改动 -
在较新内核版本中,也可以考虑使用 of_find_node_opts_by_path 函数,它提供了更多选项
-
查找失败时应进行错误处理,避免空指针访问
性能考虑
- 路径查找比名称查找更快,因为设备树内部对路径查找有优化
- 对于频繁访问的节点,可以考虑在驱动初始化时缓存节点指针
示例:
还是参考上面的代码,直接修改 代码,如下:
mydevice_node = of_find_node_by_path("/topeet/myLed");
printk("my_platform_probe mydevice_node->111 =>name is %s\n", mydevice_node->name);

of_get_parent
of_get_parent 是 Linux 设备树(Device Tree) API 中用于获取指定节点的父节点的函数。
函数原型
struct device_node *of_get_parent(const struct device_node *node);
参数说明
- node: 当前设备树节点指针 (不能为 NULL)
返回值
- 成功: 返回父节点的指针 (struct device_node *)
- 失败或没有父节点: 返回 NULL
功能描述
该函数用于在设备树层次结构中向上遍历,获取指定节点的直接父节点。设备树是一种树状结构,每个节点(除了根节点)都有一个父节点。
示例如下:
mydevice_node = of_get_parent(mydevice_node);
printk("my_platform_probe mydevice_node->2222 =>name is %s\n", mydevice_node->name);

of_get_next_child
of_get_next_child 是 Linux 设备树(Device Tree)操作中的核心函数,用于遍历设备树节点的子节点。
函数定义
struct device_node *of_get_next_child(const struct device_node *node,
struct device_node *prev);
参数详解
| 参数 | 类型 | 说明 |
|---|---|---|
| node | const struct device_node* | 父节点指针,不能为 NULL |
| prev | struct device_node* | 前一个已遍历的子节点指针,首次调用应设为 NULL |
返回值
- 成功:返回下一个子节点的有效指针
- 结束:返回 NULL 表示没有更多子节点
- 错误:返回 NULL(需结合上下文判断)
遍历机制
- 首次调用:prev = NULL,返回第一个子节点
- 后续调用:传入前一个子节点指针,返回下一个兄弟节点
- 终止条件:返回 NULL 表示遍历完成
使用模式对比
- 传统模式
struct device_node *child = NULL;
while ((child = of_get_next_child(parent, child))) {
// 处理子节点
}
- 内核推荐模式
struct device_node *child;
for_each_child_of_node(parent, child) {
// 处理子节点
// 宏内自动处理引用计数
}
实际应用案例
案例1:查找特定子节点
struct device_node *find_child_by_name(struct device_node *parent,
const char *name) {
struct device_node *child;
for_each_child_of_node(parent, child) {
if (of_node_name_eq(child, name))
return child;
}
return NULL;
}
案例2:统计子节点数量
int count_children(struct device_node *parent) {
int count = 0;
struct device_node *child;
for_each_child_of_node(parent, child) {
count++;
}
return count;
}
相关函数对比
| 函数 | 方向 | 特点 |
|---|---|---|
| of_get_next_child | 向下 | 遍历子节点 |
| of_get_parent | 向上 | 获取直接父节点 |
| of_get_next_parent | 向上 | 循环获取父节点 |
| of_get_next_available_child | 向下 | 仅遍历可用(非状态禁用)子节点 |
这里蛮多知识点的,我们还是举例测试一下简单使用:
// 获取子节点
mydevice_node = of_get_next_child(mydevice_node, NULL);
printk(" of_get_next_child 3333 node is %s\n", mydevice_node->name);

of_find_compatible_node
of_find_compatible_node 是 Linux 设备树驱动开发中最核心的节点查找函数之一,专门用于通过 compatible 属性匹配设备树节点。
函数原型
struct device_node *of_find_compatible_node(
struct device_node *from, // 起始搜索节点(NULL表示从根开始)
const char *type, // 节点类型过滤(可NULL)
const char *compatible); // 要匹配的compatible字符串
参数深度说明:
- from:支持断点续查,首次调用传NULL,后续可传前次找到的节点
- type:设备类型过滤(如"i2c"、"spi"等),实际匹配节点的device_type属性
- compatible:支持完整格式"vendor,device-model",必须精确匹配
匹配流程
- 遍历设备树所有节点
- 检查节点是否包含compatible属性
- 对比属性值是否包含指定字符串
- 如果type非空,额外检查device_type属性
比如如下一个简单的设备树,可以对照参考 是如何查找和过滤的:
camera0: camera@1a {
compatible = "sony,imx258", "vendor,camera-sensor";
device_type = "i2c";
reg = <0x1a>;
};
使用方法
- 基础查找
// 查找首个匹配节点
struct device_node *np = of_find_compatible_node(NULL, NULL, "sony,imx258");
- 查找所有匹配节点
// 查找所有同类型设备
struct device_node *np = NULL;
while ((np = of_find_compatible_node(np, NULL, "vendor,camera-sensor"))) {
// 处理每个相机节点
}
- 带类型过滤
// 只查找I2C类型的相机
np = of_find_compatible_node(NULL, "i2c", "sony,imx258");
扩展对比:相关查找函数
| 函数 | 查找依据 | 适用场景 | 效率 |
|---|---|---|---|
| of_find_compatible_node | compatible属性 | 标准设备查找 | O(n) |
| of_find_node_by_path | 完整路径 | 已知精确路径 | O(1) |
| of_find_node_by_name | 节点名 | 简单设备查找 | O(n) |
| of_find_matching_node | 多个compatible | 多型号设备支持 | O(n) |
示例,还是举例一个简单的测试,验证下:
/{
topeet{
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
myLed{
compatible = "my devicetree";
reg = <0xFDD60000 0x00000004>;
};
};
};
// 使用compatible值查找节点
mydevice_node = of_find_compatible_node(NULL, NULL, "my devicetree");
printk("mydevice node 444 is %s\n", mydevice_node->name);

四、of 函数 属性相关API
of_find_property 函数
of_property_count_elems_of_size 函数
of_property_read_u32_index 函数
of_property_read_u64_index 函数
of_property_read_variable_u32_array 函数
of_property_read_string 函数
对于节点的测试,分析了蛮多的,其实就是对于api 的理解和在实际场景中根据需求灵活应用,这里直接给出示例和测试结果。
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
struct device_node *mydevice_node;
int num;
u32 value_u32;
u64 value_u64;
u32 out_value[2];
const char *value_compatible;
struct property *my_property;
// 平台设备的初始化函数
static int my_platform_probe(struct platform_device *pdev)
{
printk(KERN_INFO "my_platform_probe: Probing platform device\n");
// 添加设备特定的操作
// ...
mydevice_node = of_find_node_by_name(NULL, "topeet");
printk("my_platform_probe mydevice_node->name is %s\n", mydevice_node->name);
mydevice_node = of_find_node_by_path("/topeet/myLed");
printk("my_platform_probe mydevice_node->111 =>name is %s\n", mydevice_node->name);
mydevice_node = of_get_parent(mydevice_node);
printk("my_platform_probe mydevice_node->2222 =>name is %s\n", mydevice_node->name);
// 获取子节点
mydevice_node = of_get_next_child(mydevice_node, NULL);
printk(" of_get_next_child 3333 node is %s\n", mydevice_node->name);
// 使用compatible值查找节点
mydevice_node = of_find_compatible_node(NULL, NULL, "my devicetree");
printk("mydevice node 444 is %s\n", mydevice_node->name);
mydevice_node = of_find_node_by_name(NULL, "myLed");
// 获取 reg 属性的元素数量
num = of_property_count_elems_of_size(mydevice_node, "reg", 4);
printk("reg num is %d\n", num);
// 读取 reg 属性的值
of_property_read_u32_index(mydevice_node, "reg", 0, &value_u32);
of_property_read_u64_index(mydevice_node, "reg", 0, &value_u64);
printk("value u32 is 0x%X\n", value_u32);
printk("value u64 is 0x%llx\n", value_u64);
// 读取 reg 属性的变长数组
of_property_read_variable_u32_array(mydevice_node, "reg", out_value, 1, 2);
printk("out_value[0] is 0x%X\n", out_value[0]);
printk("out_value[1] is 0x%X\n", out_value[1]);
// 读取 compatible 属性的字符串值
of_property_read_string(mydevice_node, "compatible", &value_compatible);
printk("compatible value is %s\n", value_compatible);
return 0;
}
// 平台设备的移除函数
static int my_platform_remove(struct platform_device *pdev)
{
printk(KERN_INFO "my_platform_remove: Removing platform device\n");
// 清理设备特定的操作
// ...
return 0;
}
static const struct of_device_id of_match_table_id[] = {
{.compatible = "my devicetree"}, // 匹配设备树中的 compatible
{/* 结束标志 */}};
// 定义平台驱动结构体
static struct platform_driver my_platform_driver = {
.probe = my_platform_probe,
.remove = my_platform_remove,
.driver = {
.name = "my_platform_device",
.owner = THIS_MODULE,
.of_match_table = of_match_table_id,
},
};
// 模块初始化函数
static int __init my_platform_driver_init(void)
{
int ret;
// 注册平台驱动
ret = platform_driver_register(&my_platform_driver);
if (ret)
{
printk(KERN_ERR "Failed to register platform driver\n");
return ret;
}
printk(KERN_INFO "my_platform_driver: Platform driver initialized\n");
return 0;
}
// 模块退出函数
static void __exit my_platform_driver_exit(void)
{
// 注销平台驱动
platform_driver_unregister(&my_platform_driver);
printk(KERN_INFO "my_platform_driver: Platform driver exited\n");
}
module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wfc");

总结
- 这里对于of 函数 api 的了解、测试、简单应用,后面实际开发中会经常用到。
- 只有自己测试验证过,基础打好了后面实际开发才能灵活运用。

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



