设备树常用OF操作函数总结
设备树是Linux内核中描述硬件信息的关键机制,驱动开发需频繁通过OF函数(前缀`of_`)从设备树提取节点、属性及资源信息。这些函数统一定义在`include/linux/of.h`,核心作用是实现驱动与硬件信息的解耦。以下按功能分类梳理常用OF函数,结合驱动开发场景说明用法。
一、查找设备节点:定位硬件对应的树节点
设备树以“节点”为基本单位描述硬件,驱动需先获取目标节点,才能进一步读取属性。常用节点查找函数如下:
1. 按节点名查找:`of_find_node_by_name`
通过节点名称定位节点,支持从指定起点遍历。
原型:`struct device_node *of_find_node_by_name(struct device_node *from, const char *name)`
- `from`:起始查找节点,设为`NULL`时从根节点全局搜索;
- `name`:目标节点的名称;
- 返回值:找到的节点指针,失败返回`NULL`。
2. 按`device_type`属性查找:`of_find_node_by_type`(已弃用)
依赖节点的`device_type`属性匹配类型,因兼容性问题已逐步被`compatible`属性替代。
原型:
`struct device_node *of_find_node_by_type(struct device_node *from, const char *type)`
- `type`:目标节点的`device_type`属性值;
- 返回值:匹配的节点,失败返回`NULL`。
3. 按`compatible`属性查找:`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`:可设为`NULL`,忽略`device_type`属性;
- `compatible`:节点的兼容性字符串列表(如`"vendor,device"`);
- 返回值:匹配的节点,失败返回`NULL`。
4. 按路径查找:`of_find_node_by_path`
通过节点的全路径(含别名)直接定位,路径格式如`/soc/i2c@40003000`或`/backlight`(别名)。
原型:`struct device_node *of_find_node_by_path(const char *path)`
- `path`:节点的完整路径或别名;
- 返回值:找到的节点,失败返回`NULL`。
5. 按匹配表查找:`of_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`:输出匹配到的具体条目;
- 返回值:匹配的节点,失败返回`NULL`。
二、查找父/子节点:遍历节点层级关系
设备树节点存在父子层级,驱动有时需遍历或获取关联节点,常用函数:
1. 获取父节点:`of_get_parent`
直接获取指定节点的父节点(若存在)。
原型:
`struct device_node *of_get_parent(const struct device_node *node)`
- `node`:待获取父节点的目标节点;
- 返回值:父节点指针,无父节点时返回`NULL`。
2. 迭代查找子节点:`of_get_next_child`
以迭代方式遍历子节点,支持从指定子节点开始查找下一个。
原型:`struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)`
- `node`:父节点;
- `prev`:前一个子节点,设为`NULL`时从第一个子节点开始;
- 返回值:下一个子节点,无更多子节点时返回`NULL`。
三、提取属性值:读取驱动所需的硬件信息
节点的属性(如`reg`、`clock-frequency`)是驱动的核心数据,需通过OF函数提取。Linux用`property`结构体描述属性,核心提取函数如下:
1. 基础属性查找:`of_find_property`
用于查找指定属性,获取属性的基本信息(如长度)。
原型:`struct property *of_find_property(const struct device_node *np, const char *name, int *lenp)`
- `np`:目标节点;
- `name`:属性名称;
- `lenp`:输出属性值的字节数;
- 返回值:找到的属性指针,失败返回`NULL`。
2. 统计元素数量:`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)`
- `propname`:目标属性名称;
- `elem_size`:单个元素的字节大小(如`reg`元素为4字节时设为4);
- 返回值:元素数量,失败返回负值。
3. 读取整型数据
驱动中最常用的属性读取方式,支持单个值和数组:
- 单个值读取:`of_property_read_u8/u16/u32/u64`
用于读取仅含一个整数值的属性(如`clock-frequency`)。
原型示例:`int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value)`
- `out_value`:输出读取到的值;
- 返回值:0成功,负值失败(如`-EINVAL`表示属性不存在,`-ENODATA`表示无数据)。
- 数组读取:`of_property_read_u8_array/u16_array/u32_array/u64_array`
用于读取数组类型属性(如`reg`),一次性提取所有元素。
原型示例:`int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz)`
- `out_values`:存储数组值的缓冲区;
- `sz`:要读取的元素个数;
- 返回值:0成功,负值失败。
- 指定索引读取:`of_property_read_u32_index`
当属性含多个值时,读取指定索引的元素(如`reg`的第2个地址)。
原型:`int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value)`
- `index`:目标元素的索引;
- 返回值:同单个值读取。
4. 读取字符串属性:`of_property_read_string`
用于读取字符串类型属性(如`compatible`、`label`)。
原型:`int of_property_read_string(struct device_node *np, const char *propname, const char out_string)`
- `out_string`:输出读取到的字符串指针;
- 返回值:0成功,负值失败。
5. 获取地址/尺寸单元数:`of_n_addr_cells`/`of_n_size_cells`
用于解析`reg`属性的地址和长度,需结合父节点的`#address-cells`和`#size-cells`属性。
- `of_n_addr_cells(np)`:获取节点的`#address-cells`值(决定`reg`中地址的字节数);
- `of_n_size_cells(np)`:获取节点的`#size-cells`值(决定`reg`中长度的字节数)。
四、其他常用OF函数:处理硬件资源
驱动需处理内存、中断、GPIO等硬件资源,以下函数直接关联资源解析:
1. 检查兼容性:`of_device_is_compatible`
判断节点的`compatible`属性是否包含指定字符串,用于驱动与设备的兼容性匹配。
原型:`int of_device_is_compatible(const struct device_node *device, const char *compat)`
- `compat`:待匹配的字符串;
- 返回值:>0表示匹配,0表示不匹配,负值失败。
2. 获取地址资源:`of_get_address`
从`reg`或`assigned-addresses`属性中提取地址信息,返回设备树格式的地址(大端)。
原型:`const __be32 *of_get_address(struct device_node *dev, int index, unsigned int size, unsigned int *flags)`
- `index`:`reg`属性的第几组地址(从0开始);
- `size`:地址长度(由`#size-cells`决定);
- `flags`:输出资源标志(如`IORESOURCE_MEM`);
- 返回值:地址指针,失败返回`NULL`。
3. 地址转换:`of_translate_address`
将设备树中的地址(大端格式)转换为CPU可识别的物理地址。
原型:`u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)`
- `in_addr`:`of_get_address`返回的地址;
- 返回值:物理地址,失败返回`OF_BAD_ADDR`。
4. 生成资源结构体:`of_address_to_resource`
将`reg`属性解析为Linux内核的`resource`结构体,直接用于设备资源注册(如平台设备的资源列表)。
原型:`int of_address_to_resource(struct device_node *dev, int index, struct resource *r)`
- `index`:`reg`属性的第几组资源;
- `r`:输出的`resource`结构体;
- 返回值:0成功,负值失败。
5. 内存映射:`of_iomap`
直接将`reg`属性的物理地址映射为虚拟地址,替代传统的`ioremap`,简化驱动开发。
原型:`void __iomem *of_iomap(struct device_node *np, int index)`
- `index`:`reg`属性的第几组地址(仅一组时设为0);
- 返回值:映射后的虚拟地址,失败返回`NULL`。
五、驱动开发核心要点
1. 节点获取:驱动的`probe`函数中,可通过`pdev->dev.of_node`直接获取匹配的设备节点,无需手动查找。
2. 属性读取:优先使用`of_property_read_*`系列函数,避免直接操作`property`结构体,保证兼容性。