上一篇对dts设备树有了初步的了解,完全手写设备树目前还打不到这样的实力,还好的就是技术的开放性,很多设备都有dts模板,可以更改适配。
活动地址:CSDN21天学习挑战赛
学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您:
想系统/深入学习某技术知识点…
一个人摸索学习很难坚持,想组团高效学习…
想写博客但无从下手,急需写作干货注入能量…
热爱写作,愿意让自己成为更好的人…
…
本篇文章讲述的是设备树的解析函数,就是of系列函数,参考网址:
DeviceTree Kernel API — The Linux Kernel documentation
Linux device tree API (programs.wiki)
在学习这部分知识的时候,发现很多人都写好了,资料也特别多,想看源码的话,参考下面的链接:包含了各个版本的linux内核代码,每一个函数参考起来会跳来跳去,充分体现了数据结构组织的好,代码写得少的哲学关系。
of.h - include/linux/of.h - Linux source code (v2.6.39.4) - Bootlin
根据上课所用的教学PPT,大约有这么多函数。
以下内容为翻译的wiki,可以直接看原文,只是为了学习和打卡,望读者见谅。
Linux内核采用设备树后,驱动需要获取设备树的属性。 Linux内核提供了一系列API函数供驱动获取设备树的属性。 Linux 内核中的“Of_” 从设备树 API 开始。
首先看一下device_node结构体定义:
struct device_node {
const char *name;
const char *type;
phandle phandle; // 这是一个u32的数据
char *full_name;
struct property *properties;
struct property *deadprops; /* removed properties */
struct device_node *parent;
struct device_node *child;
struct device_node *sibling;
struct device_node *next; /* next device of same type */
struct device_node *allnext; /* next in list of all nodes */
struct proc_dir_entry *pde; /* this node's proc directory */
struct kref kref;
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
char *path_component_name;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
};
1.获取设备节点API
在内核中,设备是以节点的形式附加到设备树上的,所以要获取设备信息,首先要获取设备节点。
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属性和类型获取设备节点
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)
From:指定从哪里开始搜索设备节点。 如果为 NULL,则从根节点开始搜索。
type:要查找的设备节点的类型。 如果为 NULL,则类型将被忽略。
Compatible:要查找的设备节点的兼容属性名称。
成功返回设备节点结构,失败返回NULL。
1.4、of_find_node_by_path函数
of_find_node_by_path函数通过路径名获取设备节点
struct device_node *of_find_node_by_path(const char *path)
Path: path name
The device node structure is returned successfully, and NULL is returned for failure.
2.获取父子节点API
2.1,of_get_parent function
struct device_node *of_get_parent(const struct device_node *node)
Node:要查找的父节点的节点
该节点的父节点返回成功,失败则返回NULL
2.2,of_get_next_child function
of_get_next_child函数可以循环遍历子节点
struct device_node *of_get_next_child(const struct device_node *node,
struct device_node *prev)
node:父节点
prev:上一个子节点,即从哪个子节点开始搜索。 NULL 表示从第一个开始搜索。
返回值是找到的下一个子节点.
3.提取设备树属性API
设备树中的属性在内核中以结构的形式表示。 下面是属性结构.
struct property {
char *name; // name
int length; // length
void *value; // Attribute value
struct property *next; // Next attribute
unsigned long _flags;
unsigned int unique_id;
struct bin_attribute attr;
};
3.1 of_find_property function
of_find_property function能够获得指定的属性
struct property *of_find_property(const struct device_node *np,
const char *name,
int *lenp)
np: device node
Name: attribute name
lenp: attribute length
example:
of_find_property(client->dev.of_node, "linux,gpio-keymap",&proplen)
上述代码段通过of_find_属性函数获取设备中属性“Linux, GPIO keymap”的值。
3.2,of_property_read_u32_index function
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 表示属性值列表太小。
3.3,of_property_read_u8_array of_property_read_u16_array of_property_read_u32_array of_property_read_u64_array
of_property_read_u8_array
of_property_read_u16_array
of_property_read_u32_array
of_property_read_u64_array
These four functions can read unsigned data at one time, such as address data in reg attribute.
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 表示属性值列表太小。
3.4,of_property_read_u8 of_property_read_u16 of_property_read_u32 of_property_read_u64
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_values:读出值
返回值:0,读取成功,-EINVAL表示该属性不存在,-ENODATA表示不存在
有数据要读取,- EOVERFLOW 表示属性值列表太小。
3.5,of_property_read_string
of_property_read_string is used to get the string
int of_property_read_string(struct device_node *np, const char *propname,
const char **out_string)
np:设备节点
propname:属性名称
out_string:读取字符串
返回值:0,读取成功,负值,读取失败。
3.6,of_n_addr_cells
of_n_addr_cells is used to get the address_cells attribute value
int of_n_addr_cells(struct device_node *np)
np:设备节点
返回值:#address 单元格属性的获取值。
3.7,of_n_size_cells
of_n_size_cells is used to get the size cells attribute value
int of_n_size_cells(struct device_node *np)
np:设备节点
返回值:#size 个单元格属性的获取值。
4.其他常用的设备树API函数
IIC、SPI、GPIO等外设都有自己的寄存器地址,而这些寄存器地址实际上是一组内存空间。 Linux内核提供了设备树的一些API函数来获取这些寄存器地址。
4.1 获取设备资源
Linux内核将寄存器、中断等信息描述为一组内存资源,用一个结构体表示
struct resource {
resource_size_t start; // Starting address
resource_size_t end; // End address
const char *name; // name
unsigned long flags; // attribute
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 */
of_ address_ to_ The resource function is used to obtain resources
int of_address_to_resource(struct device_node *node, int index,
struct resource *r)
节点:设备节点
index:获取的资源数量
r:获取资源结构
返回值:0,成功; 负值,失败。
4.1、of_iomap
of_iomap函数用于映射内存地址,将物理地址映射到虚拟地址。 过去没有设备树的时候,内存地址映射通常需要两步。 首先platform_get_resource获取资源,然后ioremap进行地址映射。 现在有了设备树,可以直接使用of_iomap同时进行资源获取和地址映射。
void __iomem *of_iomap(struct device_node *np, int index)
{
struct resource res;
if (of_address_to_resource(np, index, &res))
return NULL;
return ioremap(res.start, resource_size(&res));
}
np:设备节点
index:你想获取哪个资源
返回值:内存映射后的虚拟内存首地址。 如果为NULL,则表示内存映射失败。
========================================================================
实际上内核写好了这些函数,所以我们学习的时候,可以不求甚解,会用就行。