linux驱动学习---有些节点不会生成platform_device,怎么访问它们

这里对应韦东山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");
相关推荐
赵民勇6 小时前
Linux/Unix中install命令全面用法解析
linux·shell
苏宸啊7 小时前
Linux指令篇(一)
linux·运维·服务器
我要升天!8 小时前
Linux中《网络基础》
linux·运维·网络
鸽芷咕9 小时前
【2025年度总结】时光知味,三载同行:落笔皆是沉淀,前行自有光芒
linux·c++·人工智能·2025年度总结
羑悻的小杀马特9 小时前
指尖敲代码,笔尖写成长:2025年度总结与那些没说出口的碎碎念
linux·c++·博客之星·2025年度总结
晴天¥10 小时前
VMware+Oracle linux LVM/非LVM磁盘扩容
linux·运维·服务器
oMcLin10 小时前
如何在Oracle Linux 8.4上搭建并优化Kafka集群,确保高吞吐量的实时数据流处理与消息传递?
linux·oracle·kafka
worilb10 小时前
journalctl 与 tail 使用对比
linux
UIUI11 小时前
list_for_each_entry
linux·数据结构·链表
LeenixP11 小时前
RK3576-Debian12删除userdata分区
linux·运维·服务器·数据库·debian·开发板