这里对应韦东山linux开发手册11.7.3节内容。进行整理和对api使用方法的学习,以作备忘。如有问题,请大家提醒。
这里主要是找到节点,下一章介绍如何找到属性、获取属性的值。
有些节点不会生成platform_device,怎么访问它们
- 内核启动时会解析 DTB,生成一棵树状结构,每个节点用 struct device_node 表示。
- 对于有 compatible 并注册为设备的节点,内核有可能创建对应的 platform_device;但有些节点只是配置数据,不会生成 platform_device。
- 在驱动中我们可以直接用 of_* 系列函数访问这些节点,读取它们的属性值。
以下的10个api,无论节点是否生成了 platform_device,都可以直接通过它们找到并访问该节点,因为它们是针对 设备树解析后的数据结构(struct device_node) 而不是针对设备模型(struct device / platform_device)的。
示例 DTB
假设我们的 DTS 中有一个不会创建 platform_device 的纯数据节点:
c
/ {
my_config {
setting1 = <100>;
setting2 = "hello";
subnode {
compatible = "myvendor,mysubnode";
value = <42>;
};
};
};
编译成 DTB 后,这个 my_config 节点只有配置信息,不会有相应的驱动绑定。
主找到节点
1. of_find_node_by_path(path)
通过路径查找节点,返回节点指针。
c
struct device_node *np;
np = of_find_node_by_path("/my_config");
if (np)
pr_info("Found node: %s", np->name);
2. of_find_node_by_name(from, name)
从某个起始节点开始按名字查找:
c
struct device_node *np;
np = of_find_node_by_name(NULL, "my_config"); // NULL 表示从整棵树查找
3. of_find_node_by_type(from, type)
按类型属性值查找(需要节点有 device_type 属性)。
查找规则
- 它会从 from 节点开始(from == NULL 表示从整个设备树的第一个节点开始)
顺序地遍历设备树中所有的节点 - 找到第一个 device_type 属性值等于 type 的节点时 → 直接返回这个节点
- 如果没找到符合条件的节点 → 返回 NULL
c
np = of_find_node_by_type(NULL, "memory");
4. of_find_compatible_node(from, type, compat)
按 compatible 字符串匹配:
c
np = of_find_compatible_node(NULL, NULL, "myvendor,mysubnode");
5. of_find_node_by_phandle(handle)
如果你有节点的 phandle,可以直接找:
设备树:
c
clk1: clock@12340000 {
compatible = "myvendor,myclock";
reg = <0x12340000 0x1000>;
phandle = <0x05>;
};
mydev: mydevice@20000000 {
compatible = "myvendor,mydev";
clocks = <&clk1>; // 相当于 clocks = <0x05>;
};
驱动
c
u32 clk_handle;
struct device_node *clk_node, *dev_node;
dev_node = of_find_node_by_path("/mydevice@20000000");
of_property_read_u32(dev_node, "clocks", &clk_handle);
clk_node = of_find_node_by_phandle(clk_handle);
pr_info("Found clock node: %s", clk_node->name);
6. of_get_parent(node)
获取父节点:
c
struct device_node *parent;
parent = of_get_parent(np);
7. of_get_next_parent(node)
获取下一个父节点(往根节点方向逐个遍历):
c
while ((np = of_get_next_parent(np)))
pr_info("Parent: %s", np->name);
8. of_get_next_child(parent, prev)
遍历子节点:
c
struct device_node *child = NULL;
while ((child = of_get_next_child(np, child)))
pr_info("Child: %s", child->name);
9. of_get_next_available_child(parent, prev)
只获取状态为 okay 或无状态属性的子节点:
设备树的 status 属性
- 在设备树里,节点可能有一个 status 属性,用来告诉内核这个设备是否启用。
- 常见取值:
- "okay" → 节点启用
- "disabled" → 节点禁用
- "reserved" → 节点保留(不启用)
- 如果没有 status 属性,内核默认当成启用(相当于 "okay")。
c
struct device_node *child = NULL;
while ((child = of_get_next_available_child(np, child)))
pr_info("Available child: %s", child->name);
10. of_get_child_by_name(parent, name)
从父节点根据名字精确查找子节点:
c
struct device_node *sub;
sub = of_get_child_by_name(np, "subnode");
整体的代码
c
#include <linux/module.h>
#include <linux/of.h>
#include <linux/init.h>
static int __init mydrv_init(void)
{
struct device_node *np, *child;
const char *str;
u32 val;
// 找到 /my_config 节点
np = of_find_node_by_path("/my_config");
if (!np) {
pr_err("my_config node not found!\n");
return -ENODEV;
}
// 读取字符串属性 setting2
if (!of_property_read_string(np, "setting2", &str))
pr_info("setting2 = %s", str);
// 读取整数属性 setting1
if (!of_property_read_u32(np, "setting1", &val))
pr_info("setting1 = %u", val);
// 获取子节点 subnode
child = of_get_child_by_name(np, "subnode");
if (child) {
if (!of_property_read_u32(child, "value", &val))
pr_info("subnode value = %u", val);
}
return 0;
}
static void __exit mydrv_exit(void)
{
pr_info("mydrv exit");
}
module_init(mydrv_init);
module_exit(mydrv_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DTB node access example");