[Linux] Linux内核of.h函数详细介绍

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思维导图

该思维导图清晰地展示了 of.h API 的四大功能模块。一个典型的设备驱动初始化流程会遵循图中所示的顺序:

  1. 节点操作:首先使用 of_find_compatible_node 等函数在设备树中找到驱动所管理的设备节点。
  2. 属性读取:找到节点后,使用 of_property_read_* 系列函数读取设备的各种配置属性,如寄存器地址、中断信息等。
  3. 资源映射
    • 对于内存资源,使用 of_iomap 将物理地址映射为内核可访问的虚拟地址。
    • 对于中断资源,使用 of_irq_get 获取Linux内核的中断号。
  4. 资源管理:整个过程都需要注意使用 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_putfor_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 内核驱动工程师与设备树交互的主要工具。掌握这些函数对于编写不依赖硬编码、可移植性强的嵌入式设备驱动至关重要。核心流程通常是:

  1. 查找节点: 使用 of_find_compatible_node 或类似函数。
  2. 获取资源
    • IO 内存: 使用 of_iomap
    • 中断: 使用 of_irq_get
    • 其他属性: 使用 of_property_read_* 系列函数。
  3. 管理引用: 记住对 device_node 使用 of_node_put
  4. 注册设备: 将获取到的资源信息填充到平台设备(platform_device)或字符设备等结构中,并完成驱动的注册。

研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

极客不孤独

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

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

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

打赏作者

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

抵扣说明:

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

余额充值