ARM Linux 驱动开发篇---Linux 设备树之查找节点的 OF 函数-- Ubuntu20.04

🎬 渡水无言个人主页渡水无言

专栏传送门 : 《linux专栏》《嵌入式linux驱动开发》

⭐️流水不争先,争的是滔滔不绝

📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生

| 省级优秀毕业生获得者 | csdn新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生

在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连

目录

前言

[一、查找节点的 OF 函数](#一、查找节点的 OF 函数)

[1.1、核心结构体:device_node 与 property](#1.1、核心结构体:device_node 与 property)

[1.1.1、 device_node结构体(描述设备树节点)](#1.1.1、 device_node结构体(描述设备树节点))

1.1.2、property:描述节点属性

1.2、of_find_node_by_name函数

1.3、of_find_node_by_type函数

1.4、of_find_compatible_node函数

1.5of_find_matching_node_and_match函数

[1.6、of_find_node_by_path 函数](#1.6、of_find_node_by_path 函数)

[二、查找父/子节点的 OF 函数](#二、查找父/子节点的 OF 函数)

[2.1、of_get_parent 函数](#2.1、of_get_parent 函数)

[2.2、of_get_next_child 函数](#2.2、of_get_next_child 函数)

[三、提取属性值的 OF 函数](#三、提取属性值的 OF 函数)

3.1、of_find_property函数

[3.2of_property_count_elems_of_size 函数](#3.2of_property_count_elems_of_size 函数)

3.3、数组属性读取函数

[3.4、of_property_read_string 函数](#3.4、of_property_read_string 函数)

[3.6、of_n_addr_cells 函数](#3.6、of_n_addr_cells 函数)

[3.7、of_n_size_cells 函数](#3.7、of_n_size_cells 函数)

总结


前言

设备树的核心价值是 "解耦硬件描述与驱动代码",但驱动最终需要从设备树中读取硬件信息(如 GPIO、寄存器地址)才能完成初始化。Linux 内核提供了一套OF函数,专门用于解析设备树节点、提取属性值、完成地址映射等操作。

本文基于 IMX6ULL 开发板,系统讲解设备树开发中最常用的 OF 函数,涵盖节点查找、父子节点遍历、属性值提取、地址映射四大核心场景,是驱动工程师必备的核心知识点。


一、 查找节点的OF函数

在学习OF函数前,先掌握两个基础结构体 ------ 它们是驱动与设备树交互的 "桥梁"。

1.1、核心结构体:device_node 与 property

1.1.1、 device_node结构体(描述设备树节点)

Linux 内核用device_node结构体描述设备树中的一个节点,定义在include/linux/of.h

复制代码
struct device_node {
    const char *name;          /* 节点名字 */
    const char *type;          /* 设备类型(device_type属性) */
    phandle phandle;
    const char *full_name;     /* 节点全名(含路径) */
    struct fwnode_handle fwnode;

    struct property *properties; /* 节点的属性列表 */
    struct property *deadprops;  /* 已移除的属性 */
    struct device_node *parent;  /* 父节点 */
    struct device_node *child;   /* 子节点 */
    struct device_node *sibling; /* 兄弟节点 */
    struct kobject kobj;
    unsigned long _flags;
    void *data;
#if defined(CONFIG_SPARC)
    const char *path_component_name;
    unsigned int unique_id;
    struct of_irq_controller *irq_trans;
#endif
};

驱动中所有节点操作,本质都是对device_node结构体的读写。

1.1.2、property:描述节点属性

设备树节点的每一个属性都对应一个property结构体:

复制代码
struct property {
    char *name;        /* 属性名 */
    int length;        /* 属性值长度(字节) */
    void *value;       /* 属性值 */
    struct property *next; /* 下一个属性 */
    unsigned long _flags;
    unsigned int unique_id;
    struct bin_attribute attr;
};

提取属性值的核心,就是获取property结构体中的value字段。

接下来就讲一下核心的查找函数。

1.2、of_find_node_by_name函数

通过节点的name属性查找,适用于节点名唯一的场景:

复制代码
struct device_node *of_find_node_by_name(struct device_node *from, const char *name);

from:起始查找节点(NULL 表示从根节点开始)。
name:要查找的节点名。
返回值:找到的节点(NULL 表示失败)。

流程如下图所示:

示例 :查找名为leds的节点:

复制代码
struct device_node *led_node = of_find_node_by_name(NULL, "leds");
if (led_node == NULL) {
    printk("led node not found!\r\n");
    return -EINVAL;
}

1.3、of_find_node_by_type函数

通过节点的device_type属性查找:

复制代码
struct device_node *of_find_node_by_type(struct device_node *from, const char *type);

type:device_type属性值。

其他参数 / 返回值同 。

流程如下图所示:

1.4、of_find_compatible_node函数

通过device_type + compatible属性查找,是驱动开发中使用频率最高的函数:

复制代码
struct device_node *of_find_compatible_node(struct device_node *from, 
                                           const char *type, 
                                           const char *compatible);

type:device_type属性值(NULL 表示忽略);

compatible:要匹配的compatible属性值;

示例 :查找兼容属性为gpio-leds的节点:

复制代码
struct device_node *led_node = of_find_compatible_node(NULL, NULL, "gpio-leds");

1.5of_find_matching_node_and_match函数

通过of_device_id匹配表批量查找,适用于驱动支持多款硬件的场景:

复制代码
struct device_node *of_find_matching_node_and_match(struct device_node *from, 
                                                  const struct of_device_id *matches,
                                                  const struct of_device_id **match);

matches:of_device_id匹配表;
match:输出匹配成功的of_device_id;

1.6、of_find_node_by_path****函数

of_find_node_by_path 函数通过路径来查找指定的节点,函数原型如下:

复制代码
inline struct device_node *of_find_node_by_path(const char *path)

函数参数和返回值含义如下:

path:带有全路径的节点名,可以使用节点的别名,比如"/backlight"就是 backlight 这个节点的全路径。

返回值:找到的节点,如果为 NULL 表示查找失败。

二、查找父**/子节点的OF****函数**

Linux 内核提供了几个查找节点对应的父节点或子节点的 OF 函数,我们依次来看一下。

2.1、of_get_parent****函数

of_get_parent 函数用于获取指定节点的父节点 ( 如果有父节点的话 ) ,函数原型如下:

复制代码
struct device_node *of_get_parent(const struct device_node *node)

函数参数和返回值含义如下:
node:要查找的父节点的节点。

**返回值:**找到的父节点。

2.2、of_get_next_child****函数

of_get_next_child 函数用迭代的方式查找子节点,函数原型如下:

复制代码
struct device_node *of_get_next_child(const struct device_node *node, 
                                     struct device_node *prev);

node:父节点;

prev:上一个子节点(NULL 表示从第一个开始);

返回值:下一个子节点(NULL 表示遍历完成)。

三、提取属性值的OF函数

节点的属性信息里面保存了驱动所需要的内容,因此对于属性值的提取非常重要,Linux 内核中使用结构体 property 表示属性,此结构体同样定义在文件 include/linux/of.h 中,内容如下:

复制代码
struct property {
    char    *name;          /* 属性名字   */
    int length;             /* 属性长度   */
    void    *value;         /* 属性值     */
    struct property *next;  /* 下一个属性 */
    unsigned long _flags;
    unsigned int unique_id;
    struct bin_attribute attr;
};

Linux 内核也提供了提取属性值的 OF 函数,我们依次来看一下。

3.1、of_find_property函数

查找节点中的指定属性,返回property结构体:

复制代码
struct property *of_find_property(const struct device_node *np, 
                                 const char *name, 
                                 int *lenp);

np:设备节点;

name:属性名;

lenp:输出属性值长度;

返回值:找到的属性(NULL 表示失败)。

3.2of_property_count_elems_of_size****函数

of_property_count_elems_of_size 函数用于获取属性中元素的数量,比如 reg 属性值是一个
数组,那么使用此函数可以获取到这个数组的大小,此函数原型如下:

复制代码
int of_property_count_elems_of_size(const struct device_node *np, 
                                   const char *propname, 
                                   int elem_size);

elem_size:单个元素的长度(如 u32 类型传 4);

返回值:元素数量(负值表示失败)。
示例 :统计 reg属性的 u32 元素数量:

复制代码
int count = of_property_count_elems_of_size(led_node, "reg", 4);

3.3、数组属性读取函数

内核提供了 4 组函数,分别读取 u8/u16/u32/u64 类型的数组属性:

复制代码
// 读取u8数组
int of_property_read_u8_array(const struct device_node *np, 
                             const char *propname, 
                             u8 *out_values, 
                             size_t sz);
// 读取u16数组
int of_property_read_u16_array(...);
// 读取u32数组(最常用,如reg属性)
int of_property_read_u32_array(...);
// 读取u64数组
int of_property_read_u64_array(...);

sz:要读取的元素数量;

返回值:0 成功,负值失败(-EINVAL = 属性不存在,-ENODATA = 无数据)。

3.4、of_property_read_string****函数

of_property_read_string 函数用于读取属性中字符串值,函数原型如下:

复制代码
int of_property_read_string(struct device_node *np, 
                           const char *propname, 
                           const char **out_string);

函数参数和返回值含义如下:

np:设备节点。

proname: 要读取的属性名字。

out_string:读取到的字符串值。

**返回值:**0,读取成功,负值,读取失败。

3.6、of_n_addr_cells****函数

of_n_addr_cells 函数用于获取 #address-cells 属性值,函数原型如下:

复制代码
int of_n_addr_cells(struct device_node *np)

函数参数和返回值含义如下:

np:设备节点。

**返回值:**获取到的#address-cells 属性值。

3.7、of_n_size_cells****函数

of_size_cells 函数用于获取 #size-cells 属性值,函数原型如下:

复制代码
int of_n_size_cells(struct device_node *np)

函数参数和返回值含义如下:

np:设备节点。

**返回值:**获取到的#size-cells 属性值


总结

本期博客系统讲解了设备树开发中最常用的 OF 函数。

相关推荐
奇特認2 小时前
LVS(Linux virual server)四层负载均衡实验
linux·运维·lvs
cyber_两只龙宝2 小时前
Keepalived+LVS--实现IPVS的高可用+高性能的双主双业务架构详细配置流程及解析
linux·运维·集群·lvs·高性能·keepalived·高可用
吕司2 小时前
Linux——System V 共享内存
linux·运维·服务器
芥子沫2 小时前
Windows 命令行和 Linux 差在哪里?
linux·命令行
IvanCodes2 小时前
七、Linux Shell 与脚本基础
linux·云计算
_OP_CHEN2 小时前
【Linux系统编程】(三十七)信号捕捉全链路拆解|从内核态切换到 sigaction 实战
linux·运维·操作系统·进程·c/c++·信号·信号捕捉
S-码农2 小时前
Linux 进程间通信 —— 匿名管道和命名管道
linux
混分巨兽龙某某2 小时前
基于ESP32_CAM与Qt Creator的智能视频监控项目(代码开源)
qt·嵌入式·视频监控·esp32_cam
71ber3 小时前
RHCSE 实战笔记:Keepalived 企业级高可用集群深度解析
linux·服务器·keepalived