linux设备树-设备树常用OF操作函数
----------------------------------------------------------------------------------------------------------------------------
内核版本:linux 5.2.8
根文件系统:busybox 1.25.0
u-boot:2016.05
----------------------------------------------------------------------------------------------------------------------------
在Linux内核采用设备树之后,驱动程序需要获取设备树的属性。Linux内核为驱动程序提供了一系列API函数,用于获取设备树的属性值。在Linux内核中,以“of_”开头的函数是设备树API函数。
一、获取设备节点API
在内核中,设备以节点的形式附加到设备树上,因此要获得设备信息,必须先获取设备节点。
Linux内核使用device_node结构体来描述一个设备节点,此结构体定义在文件 include/linux/of.h 中,代码如下:
struct device_node { const char *name; /*节点的名字*/ phandle phandle; const char *full_name; /*节点的全名,node-name[@unit-address]*/ struct fwnode_handle fwnode; struct property *properties; /*节点的属性*/ struct property *deadprops; /* removed properties */ struct device_node *parent; /*父节点*/ struct device_node *child; /*子节点*/ struct device_node *sibling; /*节点的兄弟,即同级节点*/ #if defined(CONFIG_OF_KOBJ) struct kobject kobj; #endif unsigned long _flags; void *data; #if defined(CONFIG_SPARC) unsigned int unique_id; struct of_irq_controller *irq_trans; #endif };
上述数据结构是设备节点结构。让我们来看一下获取设备节点的几个常见函数。
1.1 of_find_node_by_name
of_find_node_by_name函数通过设备节点的名字获取设备节点,函数原型:
struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
其中:
- from:指定要搜索设备节点的起始位置。若为NULL,则从根节点开始搜索;
- name:要查找的设备节点的名称;
成功返回设备节点结构,失败时返回NULL。
1.2 of_find_node_by_type
of_find_node_by_type函数通过设备节点类型获取设备节点,函数原型:
struct device_node *of_find_node_by_type(struct device_node *from, const char *type);
其中:
- from:指定要搜索设备节点的起始位置。若为NULL,则从根节点开始搜索;
- type:要查找的设备节点的类型,device_type属性值;
成功返回设备节点结构,失败返回NULL。
1.3 of_find_compatible_node
of_find_compatible_node函数通过节点的compatible属性和type获取设备节点,函数原型:
struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compat);
其中:
- from参数:指定要搜索设备节点的起始位置。若为NULL,则从根节点开始搜索;
- type参数:要查找的设备节点类型。如果为NULL,则忽略类型限制;
- compatible参数:要查找的设备节点的compatible属性名称;
成功返回设备节点结构,失败时返回NULL。
1.4 of_find_node_by_path
of_find_node_by_path函数通过设备节点路径名获取设备节点,函数定义:
static inline struct device_node *of_find_node_by_path(const char *path) { return of_find_node_opts_by_path(path, NULL); }
其中:
- path参数:设备节点的路径名;
成功返回设备节点结构,失败时返回NULL;
1.5 of_find_matching_node_and_match
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)
其中:
- from: 指定要搜索设备节点的起始位置。若为NULL,则从根节点开始搜索;
- matches: of_device_id 匹配表,也就是在此匹配表里面查找节点;
- match: 找到的匹配的 of_device_id;
成功返回找到的节点,失败时返回NULL。
1.6 of_get_child_by_name
of_get_child_by_name函数用于查找指定设备节点的子节点,函数原型:
struct device_node *of_get_child_by_name(const struct device_node *node, const char *name);
其中:
- node:表示要查询子节点的父节点;
- name:表示要查找的子节点的名称;
函数的主要操作是从node节点的子节点列表中查找名为name的子节点。如果找到匹配的子节点,则将其返回;否则返回NULL。
二、获取父子设备节点API
2.1 of_get_parent
of_find_node_by_path函数用于获取某一节点的父节点,函数原型:
struct device_node *of_get_parent(const struct device_node *node);
其中:
- node参数:要查找父节点的节点;
成功返回父节点的设备节点结构,失败时返回NULL。
2.2 of_get_next_child
of_get_next_child函数可以遍历某一节点的子节点,函数原型:
device_node *of_get_next_child(const struct device_node *node,struct device_node *prev);
其中:
- node参数:父节点;
- prev参数:上一个找到的子节点,即从哪个子节点开始搜索。如果为NULL,表示从第一个子节点开始搜索;
返回值是找到的下一个子节点的设备节点结构。
三、获取设备树属性API
在内核中,设备树中的属性以结构体的形式表示。 Linux 内核中使用结构体property表示属性,此结构体同样定义在文件include/linux/of.h中,内容如下:
struct property { char *name; /* 属性名 */ int length; /* 数据长度 */ void *value; /* 属性数据指针 */ struct property *next; /* 下一个属性 */ };
在该结构体中:
- name表示属性名;
- length表示属性数据的长度;
- value指向属性数据的指针;
- next指向下一个属性。
3.1 of_find_property
of_find_property函数可以在设备节点的属性列表中查找指定的属性,函数原型如下:
struct property *of_find_property(const struct device_node *np,const char *name,int *lenp);
其中:
- np:设备节点;
- name:属性名称;
- lenp:属性长度,单位为字节;
比如下面一段代码,通过of_find_property函数获取设备的属性"linux,gpio-keymap"的值。
of_find_property(client->dev.of_node, "linux,gpio-keymap",&proplen)
3.2 of_property_read_u32_index
of_property_read_u32_index函数可以读取设备树中属性值为32位无符号整数的属性。函数原型:
int of_property_read_u32_index(const struct device_node *np,const char *propname,u32 index, u32 *out_value);
其中:
- np:设备节点;
- propname:属性名称;
- index:索引,指定要读取的属性值的编号;
- out_value:读取出的属性值;
函数返回值:
- 0:读取成功;
- -EINVAL:指定的属性不存在;
- -ENODATA:没有数据可读;
- -EOVERFLOW:属性值列表太小;
例如,以下代码段表示从设备节点nd的名为"my_property"的属性中读取第三个32位无符号整数值:
u32 val; int ret; ret = of_property_read_u32_index(np, "my_property", 2, &val); if (ret) pr_err("Failed to read my_property\n"); else pr_info("my_property[2] = %u\n", val);
3.3 of_property_read_u8/16/32/64_array
这四个是读取设备节点中包含多个无符号数据的属性值的函数。函数原型:
int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz) int of_property_read_u16_array(const struct device_node *np, const char *propname, u16 *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) int of_property_read_u64_array(const struct device_node *np, const char *propname, u64 *out_values, size_t sz)
其中:
- np:设备节点
- propname:属性名称;
- out_values:读出的属性值;
- sz:需要读取的属性值数量;
函数返回值:
- 0:读取成功;
- -EINVAL:指定的属性不存在;
- -ENODATA:没有数据可读;
- -EOVERFLOW:属性值列表太小;
例如,以下代码段表示从设备节点nd的名为"reg"的属性中读取前三个32位无符号整数值:
u32 val[3]; int ret; ret = of_property_read_u32_array(np, "reg", val, 3); if (ret) pr_err("Failed to read reg\n"); else pr_info("reg[0] = 0x%x, reg[1] = 0x%x, reg[2] = 0x%x\n", val[0], val[1], val[2]);
3.4 of_property_read_u8/16/32/64
这四个是读取设备节点中包含单个无符号数据的属性值的函数。函数原型:
int of_property_read_u8(const struct device_node *np, const char *propname, u8 *out_value) int of_property_read_u16(const struct device_node *np, const char *propname, u16 *out_value) int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value) int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value
其中:
- np:设备节点
- propname:属性名称;
- out_value:读出的属性值;
函数返回值:
- 0:读取成功;
- -EINVAL:指定的属性不存在;
- -ENODATA:没有数据可读;
- -EOVERFLOW:属性值列表太小;
3.5 of_property_read_string
of_property_read_string函数用于读取设备节点属性中字符串值,函数原型:
of_property_read_string(const struct device_node *np, const char *propname, const char **out_string);
其中:
- np:设备节点;
- propname:属性名称;
- out_string:读出的字符串;
函数返回值:0表示读取成功,负数表示读取失败。
例如,以下代码段表示从设备节点nd的名为"compatible"的属性中读取一个字符串:
char buf[32]; int ret; ret = of_property_read_string(np, "compatible", buf, sizeof(buf)); if (ret) pr_err("Failed to read compatible\n"); else pr_info("compatible = %s\n", buf);
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 属性值。
3.8 of_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);
其中:
- np:设备节点;
- propname:属性名称;
- elem_size:元素长度;
返回得到的属性元素数量。
3.9 of_property_read_u32_index
of_property_read_u32_index 函数用于从属性中获取指定标号的 u32 类型数据值,比如某个属性有多个u32类型的值,那么就可以使用此函数来获取指定标号的数据值,此函数原型:
of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value);
其中:
- np:设备节点;
- propname:属性名称;
- index:要读取的值标号;
- out_value:读取到的值;
函数返回值:
- 0:读取成功;
- -EINVAL:指定的属性不存在;
- -ENODATA:没有数据可读;
- -EOVERFLOW:属性值列表太小;
3.10 of_property_match_string
of_property_match_string函数用于查找字符串在指定属性值(字符串列表)中出现的索引,函数原型:
int of_property_match_string(const struct device_node *np, const char *propname, const char *string);
其中:
- np:设备节点;
- propname:属性名称;
- string:指向在字符串列表中查找的字符串的指针;
四、其他常用的API
Linux内核提供了大量的API来处理设备树,除了之前介绍过的API,还有大量的API在include/linux/of_xxx头文件中声明:
比如:
of.h // 提供设备树的一般处理函数, 比如 of_property_read_u32(读取某个属性的u32值), *of_get_child_count(获取某个device_node的子节点数) of_address.h // 地址相关的函数, 比如of_get_address(获得reg属性中的addr, size值) of_device.h // 设备相关的函数,比如of_match_device(从matches数组中取出与当前设备最匹配的一项) of_dma.h // 设备树中DMA相关属性的函数 of_gpio.h // GPIO相关的函数 of_graph.h // GPU相关驱动中用到的函数, 从设备树中获得GPU信息 of_iommu.h // 很少用到 of_irq.h // 中断相关的函数 of_mdio.h // MDIO (Ethernet PHY) API of_net.h // OF helpers for network devices. of_pci.h // PCI相关函数 of_pdt.h // 很少用到 of_reserved_mem.h // reserved_mem的相关函数 of_platform.h // 把device_node转换为platform_device时用到的函数
4.1 设备资源
像IIC、SPI 和GPIO等外设都有它们自己的寄存器地址,这些寄存器地址实际上是一组内存空间。Linux 内核提供了一些设备树API函数来获取这些寄存器地址。这些函数一般在include/linux/of_address.h头文件中声明;
Linux内核将寄存器、中断和其他信息描述为一组内存资源,并用resource结构体来表示。 resource 结构体定义在文件include/linux/ioport.h 中,定义如下:
struct resource { resource_size_t start; /*起始地址,对于32位soc,resource_size_t 的数据类型是u32*/ resource_size_t end; /*结束地址*/ const char *name; /*资源的名字*/ unsigned long flags; /*资源标志位,一般表示资源类型,可选的资源标志定义在文件 include/linux/ioport.h,如IORESOURCE_BITS、IORESOURCE_MEM、IORESOURCE_IRQ等 */ struct resource *parent, *sibling, *child; };
flags用于表示资源类型,可以包括以下资源类型:
#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */ #define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */ #define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */ #define IORESOURCE_MEM 0x00000200 #define IORESOURCE_REG 0x00000300 /* Register offsets */ #define IORESOURCE_IRQ 0x00000400 #define IORESOURCE_DMA 0x00000800 #define IORESOURCE_BUS 0x00001000 #define IORESOURCE_PREFETCH 0x00002000 /* No side effects */ #define IORESOURCE_READONLY 0x00004000 #define IORESOURCE_CACHEABLE 0x00008000 #define IORESOURCE_RANGELENGTH 0x00010000 #define IORESOURCE_SHADOWABLE 0x00020000 #define IORESOURCE_SIZEALIGN 0x00040000 /* size indicates alignment */ #define IORESOURCE_STARTALIGN 0x00080000 /* start field is alignment */ #define IORESOURCE_MEM_64 0x00100000 #define IORESOURCE_WINDOW 0x00200000 /* forwarded by bridge */ #define IORESOURCE_MUXED 0x00400000 /* Resource is software muxed */ #define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */ #define IORESOURCE_DISABLED 0x10000000 #define IORESOURCE_UNSET 0x20000000 /* No address assigned yet */ #define IORESOURCE_AUTO 0x40000000 #define IORESOURCE_BUSY 0x80000000 /* Driver has marked this resource busy */
4.1.1 of_address_to_resource
of_address_to_resource函数用于获取设备树节点的内存资源。函数原型如下:
int of_address_to_resource(const struct device_node *np, int index, struct resource *r);
其中:
- np:设备节点;
- index:所需获取的资源的编号;
- r:获取到的资源结构体;
返回值:如果成功则返回 0,否则返回负数。
该函数接受一个指向某个设备树节点的指针作为输入参数,然后返回一个包含该节点所描述的资源信息的struct resource 结构体。该结构体包含了资源的开始地址、结束地址、大小和标志等信息。
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2022-04-20 linux驱动移植-LCD设备驱动