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

ARM Linux 驱动开发篇---Linux 设备树之查找节点的 OF 函数-- Ubuntu20.04
🎬 渡水无言个人主页渡水无言

专栏传送门: 《linux专栏》   《嵌入式linux驱动开发》
⭐️流水不争先,争的是滔滔不绝

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

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

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

目录

前言

一、查找节点的 OF 函数

1.1、核心结构体:device_node 与 property

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 函数

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

2.1、of_get_parent 函数

2.2、of_get_next_child 函数

三、提取属性值的 OF 函数

3.1、of_find_property函数

3.2of_property_count_elems_of_size 函数

3.3、数组属性读取函数

3.4、of_property_read_string 函数

3.6、of_n_addr_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 函数。

Read more

HDFS数据块机制深度解析:块大小设计与存储哲学

HDFS数据块机制深度解析:块大小设计与存储哲学

HDFS数据块机制深度解析:块大小设计与存储哲学 * 引言:块——HDFS存储的核心抽象 * 一、HDFS默认块大小 * 1.1 版本演进与默认值 * 1.2 查看和验证块大小 * 1.3 配置文件中的设置 * 二、为什么HDFS采用块存储? * 2.1 核心设计思想 * 2.2 详细解析:为什么块存储如此重要? * **2.2.1 减少寻址开销,提升I/O效率** * **2.2.2 支持超大文件,超越单机限制** * **2.2.3 简化存储设计,降低元数据复杂度** * **2.2.4 便于数据复制,增强容错性** * **2.2.5 支持数据本地性,

By Ne0inhk
【算法通关指南:数据结构与算法篇】二叉树相关算法题:1.二叉树深度 2.求先序排列

【算法通关指南:数据结构与算法篇】二叉树相关算法题:1.二叉树深度 2.求先序排列

🔥小龙报:个人主页 🎬作者简介:C++研发,嵌入式,机器人方向学习者 ❄️个人专栏:《算法通关指南》 ✨ 永远相信美好的事情即将发生 文章目录 * 前言 * 一、二叉树深度 * 2.1题目 * 2.2 算法原理 * 2.3代码 * 二、 求先序排列 * 3.1题目 * 3.2 算法原理 * 3.3代码 * 总结与每日励志 前言 本专栏聚焦算法题实战,系统讲解算法模块:以《c++编程》,《数据结构和算法》《基础算法》《算法实战》 等几个板块以题带点,讲解思路与代码实现,帮助大家快速提升代码能力ps:本章节题目分两部分,比较基础笔者只附上代码供大家参考,其他的笔者会附上自己的思考和讲解,希望和大家一起努力见证自己的算法成长 一、二叉树深度 2.

By Ne0inhk
【3D图像算法技术】如何在Blender中对复杂物体进行有效减面?

【3D图像算法技术】如何在Blender中对复杂物体进行有效减面?

在Blender中对复杂物体进行减面(也称为“简化模型”)是平衡Web游戏性能与视觉效果的核心步骤。Web游戏受限于浏览器渲染能力和网络传输效率,通常要求模型面数尽可能低(一般单个模型面数控制在1万面以内,复杂场景需更低),但需保留关键视觉特征(如轮廓、结构细节)。以下是具体流程及算法化实现思路: 一、复杂物体减面的核心流程(手动操作) 1. 准备与分析阶段 * 模型检查:删除冗余数据(如隐藏顶点、孤立顶点、重复材质),确保模型是“流形”(无破面、非流形边)。 * 结构分析:识别模型的“关键区域”(如角色面部、物体轮廓、高曲率细节)和“可简化区域”(如平坦表面、被遮挡部分)。 * 示例:角色模型中,面部和手部是关键区域(需保留细节),背部或衣物内侧是可简化区域。 2. 分阶段减面(核心步骤) 根据模型复杂度,采用“先整体简化,再局部修复”的策略,优先使用Blender内置工具:

By Ne0inhk
贪心算法篇——万千抉择中的唯一考量,最优解追寻的跬步累积(1)

贪心算法篇——万千抉择中的唯一考量,最优解追寻的跬步累积(1)

文章目录 * 引言:在选择的海洋中 * 贪心算法的哲学:局部最优,全球最优 * 贪心算法的经典应用 * 贪心算法的局限与挑战 * 结语:智者的选择,最优的未来 引言:在选择的海洋中 在人生的旅途上,每个人都要面临无数的选择。每一个选择,都是一次抉择;每一次抉择,都是命运的交汇点。数学与计算机科学的世界里,贪心算法正是对这种“选择”的一种深刻体现。在一系列的选择面前,贪心算法如同一位睿智的旅行者,始终秉持着最优的哲学:每一次决策都应基于局部最优,以期在最后抵达全局最优的境地。 贪心算法(Greedy Algorithm),正如其名所示,是一种每次都选择当前看起来最优解的算法。这种算法策略简单却充满智慧,常常能够解决很多看似复杂的问题。它通过一种局部的、贪婪的方式,一步步走向最终解。然而,正如智慧的旅行者需要对道路有所预见一样,贪心算法也有其适用的范围,只有在满足某些条件时,它才能发挥出最优解的魅力。 在这篇报告中,我们将深入探讨贪心算法的基本理念、适用范围、经典应用,并通过具体的代码示例,揭开这一算法的神秘面纱。 贪心算法的哲学:

By Ne0inhk