程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

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-30I*B1
2024-01-28*兴20
2024-02-01QYing20
2024-02-11*督6
2024-02-18一*x1
2024-02-20c*l18.88
2024-01-01*I5
2024-04-08*程150
2024-04-18*超20
2024-04-26.*V30
2024-05-08D*W5
2024-05-29*辉20
2024-05-30*雄10
2024-06-08*:10
2024-06-23小狮子666
2024-06-28*s6.66
2024-06-29*炼1
2024-06-30*!1
2024-07-08*方20
2024-07-18A*16.66
2024-07-31*北12
2024-08-13*基1
2024-08-23n*s2
2024-09-02*源50
2024-09-04*J2
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-26B*h10
2024-09-3010
2024-10-02M*i1
2024-10-14*朋10
2024-10-22*海10
2024-10-23*南10
2024-10-26*节6.66
2024-10-27*o5
2024-10-28W*F6.66
2024-10-29R*n6.66
2024-11-02*球6
2024-11-021*鑫6.66
2024-11-25*沙5
2024-11-29C*n2.88
posted @   大奥特曼打小怪兽  阅读(864)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
历史上的今天:
2022-04-20 linux驱动移植-LCD设备驱动
如果有任何技术小问题,欢迎大家交流沟通,共同进步

公告 & 打赏

>>

欢迎打赏支持我 ^_^

最新公告

程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)。

了解更多

点击右上角即可分享
微信分享提示