Linux驱动-设备树-获取节点属性of函数分析

设备树节点 相关 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);

参数详解

参数类型说明
nodeconst struct device_node*父节点指针,不能为 NULL
prevstruct 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_nodecompatible属性标准设备查找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 的了解、测试、简单应用,后面实际开发中会经常用到。
  • 只有自己测试验证过,基础打好了后面实际开发才能灵活运用。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值