Linux内核在采用设备树后,驱动程序就需要获取设备树的属性,Linux内核为此提供了一系列的API函数给驱动程序获取设备树属性。Linux内核中以"of_"开头为设备树API。

1、获取设备节点API
在内核中设备都是以节点的形式挂接在设备树上,所以要想获取设备信息,就要先获取设备节点。

struct device_node {
	const char *name; // 节点名字
	const char *type; // 节点类型
	phandle phandle;
	const char *full_name; // 节点全名
	struct fwnode_handle fwnode;

	struct	property *properties;
	struct	property *deadprops;	/* removed properties */
	struct	device_node *parent; // 父节点
	struct	device_node *child;  // 子节点
	struct	device_node *sibling; // 兄弟节点
	struct	kobject kobj;
	unsigned long _flags;
	void	*data;
#if defined(CONFIG_SPARC)
	const char *path_component_name;
	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时从根节点开始寻找。
name:要寻找的设备节点类型名字。
成功返回设备节点结构体,失败返回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 *compatible)

from:指定从哪里开始寻找设备节点,为NULL时从根节点开始寻找。
type:要寻找的设备节点类型,为NULL则忽略type。
compatible:要寻找的设备节点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:路径名
成功返回设备节点结构体,失败返回NULL。

2、获取父节点和子节点API

  • 2.1、of_get_parent函数
struct device_node *of_get_parent(const struct device_node *node)

node:要查找的父节点的节点
成功返回该节点的父节点,失败返回NULL

  • 2.2、of_get_next_child函数
    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; // 名字
	int	length;    // 长度
	void	*value; // 属性值
	struct property *next; // 下一个属性
	unsigned long _flags;
	unsigned int unique_id;
	struct bin_attribute attr;
};

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(client->dev.of_node, "linux,gpio-keymap",&proplen)

上述代码段通过of_find_property函数获取设备中"linux,gpio-keymap"这个属性的值。

  • 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 表示属性值列表太小。

  • 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
这四个函数都可以一次性读取无符号类型的数据,比如像reg属性中的地址数据。

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用于获取字符串
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用于获取address_cells属性值

int of_n_addr_cells(struct device_node *np)

np:设备节点
返回值: 获取到的#address-cells 属性值。

  • 3.7、of_n_size_cells

of_n_size_cells用于获取size-cells属性值

int of_n_size_cells(struct device_node *np)

np:设备节点
返回值: 获取到的#size-cells 属性值。

4、其他常用的设备树API函数
像IIC、SPI、GPIO等外设都有自己的寄存器地址,而这些寄存器地址其实就是一组内存空间,Linux内核提供了一些设备树的API函数来获取这些寄存器地址。

4.1、获取设备资源
Linux内核把寄存器、中断等信息描述成一组内存资源,用一个结构体来表示

struct resource {
	resource_size_t start; // 起始地址
	resource_size_t end;   // 结束地址
	const char *name;      // 名字
	unsigned long flags;   // 属性
	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_resource函数用来获取资源

int of_address_to_resource(struct device_node *node, int index,
			   struct resource *r)

node:设备节点
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 的话表示内存映射失败。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐