Linux内核of.h函数详细介绍
最近在做linux驱动开发,天天跟设备树打交道,就详细介绍一下 Linux 内核中非常重要的头文件
of.h吧。这个头文件是 Open Firmware (OF) 设备树接口的核心,用于在 Linux 内核中实现与设备树(Device Tree)的交互。
文章目录
概述
of.h 位于 include/linux/of.h。设备树是一种描述硬件配置的数据结构,它使得内核不再需要硬编码硬件的详细信息(特别是在 ARM、RISC-V 等嵌入式架构上)。of.h 提供了一整套丰富的 API(函数、宏、数据结构)来解析设备树源文件(.dts)被编译后形成的设备树 blob(.dtb),并在内核驱动中访问节点、属性及其值。
核心数据结构是 struct device_node,它代表设备树中的一个节点。
主要函数分类与详细介绍
这是根据 Linux 内核头文件 of.h 中主要函数和功能整理的思维导图。

该思维导图清晰地展示了 of.h API 的四大功能模块。一个典型的设备驱动初始化流程会遵循图中所示的顺序:
- 节点操作:首先使用
of_find_compatible_node等函数在设备树中找到驱动所管理的设备节点。 - 属性读取:找到节点后,使用
of_property_read_*系列函数读取设备的各种配置属性,如寄存器地址、中断信息等。 - 资源映射:
- 对于内存资源,使用
of_iomap将物理地址映射为内核可访问的虚拟地址。 - 对于中断资源,使用
of_irq_get获取Linux内核的中断号。
- 对于内存资源,使用
- 资源管理:整个过程都需要注意使用
of_node_put来正确管理节点引用计数,防止内存泄漏。
这张图可以作为开发时的快速参考,帮助你迅速定位所需的函数。
下面,我们将函数按功能分为几个类别进行介绍。
1. 设备树节点(Device Node)相关函数
这些函数用于查找、遍历和引用计数管理设备树中的节点。
a. 节点查找
of_find_node_by_name(struct device_node *from, const char *name)
- 原型:
struct device_node *of_find_node_by_name(struct device_node *from, const char *name); - 参数:
from: 开始搜索的节点。如果为NULL,则从根节点开始搜索。name: 要匹配的节点名称(即name属性,例如"interrupt-controller")。
- 作用: 根据节点的
name属性在整个设备树中查找节点。返回找到的节点的指针。已废弃,因为设备树规范推荐使用compatible属性而非name来标识设备类型,但内核中仍有部分代码使用。 - 注意: 返回的节点指针会增加引用计数,使用完后必须调用
of_node_put来减少计数。
of_find_node_by_type(struct device_node *from, const char *type)
- 原型:
struct device_node *of_find_node_by_type(struct device_node *from, const char *type); - 参数:
from: 开始搜索的节点。type: 要匹配的节点device_type属性(例如"serial")。
- 作用: 根据节点的
device_type属性查找节点。同样已废弃,不推荐使用。 - 注意: 同样需要
of_node_put。
of_find_node_by_path(const char *path)
- 原型:
struct device_node *of_find_node_by_path(const char *path); - 参数:
path: 节点的完整路径(例如"/soc/usb@fe800000")或别名(例如"duart0",需要of_alias_scan先解析别名)。
- 作用: 通过节点的完整路径来查找节点。这是获取根节点或已知绝对路径节点的可靠方法。
- 注意: 需要
of_node_put。
of_find_compatible_node(struct device_node *from, const char *type, const char *compat)
- 原型:
struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compat); - 参数:
from: 开始搜索的节点。type: 要匹配的device_type属性(可设为NULL以忽略此条件)。compat: 要匹配的compatible属性字符串(例如"fsl,imx6q-uart")。
- 作用: 最常用的查找函数之一。根据设备的
compatible属性来查找节点。这是现代设备驱动绑定设备的标准方式。 - 注意: 需要
of_node_put。
of_find_node_by_phandle(phandle handle)
- 原型:
struct device_node *of_find_node_by_phandle(phandle handle); - 参数:
handle: 要查找的节点的 phandle(一个唯一的整数标识符)。
- 作用: 通过 phandle 直接查找对应的节点。phandle 常用于属性中引用其他节点(例如
interrupt-parent = <&intc>;,&intc就是 phandle 的引用)。 - 注意: 需要
of_node_put。
of_find_matching_node_and_match(struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match)
- 原型:
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); - 参数:
from: 开始搜索的节点。matches: 一个of_device_id列表,用于匹配节点的compatible属性。match: 输出参数,指向匹配成功的of_device_id条目。
- 作用: 遍历设备树,寻找与
matches列表中任一条目匹配的节点。这是平台驱动或设备驱动核心使用的底层函数,of_find_compatible_node是其简化版。 - 注意: 需要
of_node_put。
b. 节点遍历与引用计数
of_get_parent(const struct device_node *node)
- 原型:
struct device_node *of_get_parent(const struct device_node *node); - 参数:
node: 要获取父节点的节点。
- 作用: 获取指定节点的父节点。
- 注意: 返回的父节点指针增加了引用计数,使用后需
of_node_put。
of_get_next_child(const struct device_node *node, struct device_node *prev)
- 原型:
struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev); - 参数:
node: 父节点。prev: 前一个子节点。如果为NULL,则获取第一个子节点。
- 作用: 迭代遍历一个节点的所有子节点。通常用在循环中。
struct device_node *child; for_each_child_of_node(parent, child) { // 这个宏就是基于 of_get_next_child 实现的 // 处理 child } - 注意: 返回的子节点增加了引用计数,循环退出后不需要再对
child调用of_node_put(for_each_child_of_node宏内部已处理),但如果提前跳出循环,必须手动调用。
of_node_get(struct device_node *node) / of_node_put(struct device_node *node)
- 原型:
struct device_node *of_node_get(struct device_node *node);和void of_node_put(struct device_node *node); - 参数:
node: 目标节点。
- 作用: 管理节点对象的引用计数。
of_node_get增加计数,of_node_put减少计数。当计数减为 0 时,对象会被释放。几乎所有返回device_node指针的函数都会增加其引用计数,因此在使用完节点后,必须调用of_node_put来避免内存泄漏。
2. 属性(Property)相关函数
这些函数用于读取设备树节点中的属性值。
a. 属性查找与存在性检查
of_find_property(const struct device_node *np, const char *name, int *lenp)
- 原型:
struct property *of_find_property(const struct device_node *np, const char *name, int *lenp); - 参数:
np: 目标节点。name: 属性名。lenp: 输出参数,用于存储属性值的长度(字节数)。可为NULL。
- 作用: 查找节点中是否存在指定属性,并返回该属性结构体。通常用于检查属性是否存在。
of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size)
- 原型:
int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size); - 参数:
np: 目标节点。propname: 属性名。elem_size: 每个数组元素的大小(例如,sizeof(u32))。
- 作用: 获取属性值中数组元素的个数。例如,对于
reg = <0x1000 0x100 0x2000 0x200>;,调用of_property_count_elems_of_size(np, “reg”, sizeof(u32))将返回 4。 - 返回: 成功返回元素个数,失败返回负的错误码。
of_property_read_bool(const struct device_node *np, const char *propname)
- 原型:
bool of_property_read_bool(const struct device_node *np, const char *propname); - 参数:
np: 目标节点。propname: 属性名。
- 作用: 检查一个属性是否存在。如果存在则返回
true,否则返回false。常用于检查布尔属性(例如status = “disabled”;的存在性本身就有意义)。
b. 属性值读取
of_property_read_u8/u16/u32/u64(const struct device_node *np, const char *propname, u8/u16/u32/u64 *out_value)
- 原型:
int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value);(以及其他类型) - 参数:
np: 目标节点。propname: 属性名。out_value: 输出参数,用于存储读取到的值。
- 作用: 从属性中读取一个指定类型的标量值。属性值可以是单个单元格(cell)或数组的第一个元素。
- 返回: 成功返回 0,失败返回负的错误码。
of_property_read_u8/u16/u32/u64_array(const struct device_node *np, const char *propname, u8/u16/u32/u64 *out_values, size_t sz)
- 原型:
int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz); - 参数:
np: 目标节点。propname: 属性名。out_values: 输出参数,指向存储数组值的缓冲区。sz: 期望读取的元素数量。
- 作用: 从属性中读取一个指定类型的数组值。例如读取
reg属性。 - 返回: 成功返回 0,失败返回负的错误码。
of_property_read_string(const struct device_node *np, const char *propname, const char **out_string)
- 原型:
int of_property_read_string(const struct device_node *np, const char *propname, const char **out_string); - 参数:
np: 目标节点。propname: 属性名。out_string: 输出参数,指向字符串的指针。
- 作用: 从属性中读取一个字符串值。注意,
out_string指向的是设备树 blob 中的原始字符串,不需要手动释放。 - 返回: 成功返回 0,失败返回负的错误码。
of_property_read_string_array(const struct device_node *np, const char *propname, const char **out_strings, size_t sz)
- 原型:
int of_property_read_string_array(const struct device_node *np, const char *propname, const char **out_strings, size_t sz); - 作用: 读取一个字符串列表属性(例如
dma-names = “tx”, “rx”;)。 - 返回: 成功返回读取到的字符串数量,失败返回负的错误码。
3. 地址转换(Address Translation)函数
设备树中的地址通常是相对于总线或父节点的。这些函数用于将其转换为 CPU 可用的物理地址或内核虚拟地址。
of_translate_address(struct device_node *dev, const __be32 *in_addr)
- 原型:
u64 of_translate_address(struct device_node *dev, const __be32 *in_addr); - 参数:
dev: 需要转换地址的设备节点。in_addr: 指向要转换的地址值的指针(通常是reg属性数组中的某个元素)。
- 作用: 底层函数。根据设备节点的
ranges属性,将设备节点地址空间中的地址转换到父节点(通常是 CPU)的地址空间。返回转换后的物理地址(OF_BAD_ADDR表示失败)。
of_iomap(struct device_node *np, int index)
- 原型:
void __iomem *of_iomap(struct device_node *np, int index); - 参数:
np: 目标设备节点。index:reg属性中地址的索引(从 0 开始)。
- 作用: 极其常用。它相当于
of_translate_address+ioremap。它解析节点的reg属性,完成地址转换,并将其映射到内核的虚拟地址空间。返回映射后的内核虚拟地址(IO内存指针),驱动可以直接用readl/writel等函数访问。 - 注意: 使用完后通常不需要显式解除映射,由设备模型管理。
of_address_to_resource(struct device_node *dev, int index, struct resource *r)
- 原型:
int of_address_to_resource(struct device_node *dev, int index, struct resource *r); - 参数:
dev: 目标设备节点。index:reg属性中地址的索引。r: 输出参数,填充好的 resource 结构体(包含起始地址、大小、标志等)。
- 作用: 从设备的
reg属性中提取地址信息,并将其填充到标准的struct resource中。这个resource可以用于request_mem_region等函数。
4. 中断(Interrupt)相关函数
of_irq_get(struct device_node *dev, int index)
- 原型:
int of_irq_get(struct device_node *dev, int index); - 参数:
dev: 目标设备节点。index:interrupts属性中中断说明符的索引(从 0 开始)。
- 作用: 解析设备的
interrupts属性,将其转换为 Linux 内核的虚拟中断号(virq)。这是现代驱动获取中断号的推荐方式。 - 返回: 成功返回中断号(>=0),失败返回负的错误码。
of_irq_get_byname(struct device_node *dev, const char *name)
- 原型:
int of_irq_get_byname(struct device_node *dev, const char *name); - 参数:
dev: 目标设备节点。name: 中断名称(需要设备树中使用interrupt-names属性指定)。
- 作用: 通过名称(而非索引)来获取中断号。适用于有多个中断源的设备。
of_irq_to_resource(struct device_node *dev, int index, struct resource *r)
- 原型:
struct resource *of_irq_to_resource(struct device_node *dev, int index, struct resource *r); - 作用: 类似于
of_address_to_resource,但用于中断。它将中断信息填充到resource结构中。
5. 其他常用函数和宏
for_each_child_of_node(parent, child)
- 作用: 一个用于安全遍历所有子节点的宏。它自动处理引用计数,在循环结束后释放最后一个子节点的引用。
for_each_available_child_of_node(parent, child)
- 作用: 类似于
for_each_child_of_node,但只遍历那些status属性不为"disabled"的可用子节点。
for_each_property_of_node(node, prop)
- 作用: 遍历一个节点的所有属性。
of_device_is_compatible(const struct device_node *device, const char *compat)
- 作用: 检查设备节点是否与给定的兼容字符串匹配。
of_alias_get_id(struct device_node *np, const char *stem)
- 作用: 获取设备的别名 ID。例如,对于
serial0: serial@fe800000,调用of_alias_get_id(serial0_node, “serial”)将返回 0。常用于获取序列号。
总结
of.h 提供的 API 是 Linux 内核驱动工程师与设备树交互的主要工具。掌握这些函数对于编写不依赖硬编码、可移植性强的嵌入式设备驱动至关重要。核心流程通常是:
- 查找节点: 使用
of_find_compatible_node或类似函数。 - 获取资源:
- IO 内存: 使用
of_iomap。 - 中断: 使用
of_irq_get。 - 其他属性: 使用
of_property_read_*系列函数。
- IO 内存: 使用
- 管理引用: 记住对
device_node使用of_node_put。 - 注册设备: 将获取到的资源信息填充到平台设备(
platform_device)或字符设备等结构中,并完成驱动的注册。
研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)


被折叠的 条评论
为什么被折叠?



