嵌入式Linux设备树常用 of 函数

设备树描述了设备的详细信息, 这些信息包括数字类型的、 字符串类型的、 数组类型的, 我们在编写驱动的时候需要获取到这些信息。 比如设备树使用 reg 属性描述了某个外设的寄存器地址为 0X02005482,长度为 0X400, 我们在编写驱动的时候需要获取到 reg 属性的 0X02005482 和 0X400 这两个值, 然后初始化外设。 Linux 内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息, 这一系列的函数都有一个统一的前缀“ of_” , 所以在很多资料里面也被叫做 OF 函数。 这些 OF 函数原型都定义在include/linux/of.h 文件中。

查找节点的 of 函数
设备都是以节点的形式“挂”到设备树上的, 因此要想获取这个设备的其他属性信息, 必须先获取到这个设备的节点。 Linux 内核使用 device_node 结构体来描述一个节点, 此结构体定义在文件 include/linux/of.h中, 定义如下:

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 属性 */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
};

节点的属性信息里面保存了驱动所需要的内容, 因此对于属性值的提取非常重要, Linux 内核中使用结构体 property 表示属性, 此结构体同样定义在文件 include/linux/of.h 中, 内容如下:

struct property
{char *name; /* 属性名字 */int length; /* 属性长度 */void *value; /* 属性值 */struct property *next; /* 下一个属性 */unsigned long _flags;unsigned int unique_id;struct bin_attribute attr;
};

获得设备树文件节点里面资源的步骤:
步骤一: 查找我们要找的节点。
步骤二: 获取我们需要的属性值。
与查找节点有关的 OF 函数有 3 个, 我们依次来看一下
1.of_find_node_by_path 函数

函数inline struct device_node *of_find_node_by_path(const char *path)
path带有全路径的节点名, 可以使用节点的别名, 比如“/backlight” 就是 backlight 这
个节点的全路径。
返回值成功: 返回找到的节点, 失败返回 NULL。
功能通过节点名字查找指定的节点

2 of_get_parent 函数

函数struct device_node *of_get_parent(const struct device_node *node)
node要查找的父节点的节点。
返回值成功: 找到的节点, 如果为 NULL 表示查找失败。
功能用于获取指定节点的父节点(如果有父节点的话)。

3 of_get_next_child 函数

函数struct device_node *of_get_next_child(const struct device_node *node struct
device_node *prev)
node父节点
prev前一个子节点, 也就是从哪一个子节点开始迭代的查找下一个子节点。 可以设置为 NULL, 表示从第一个子节点开始。
返回值成功: 找到的下一个子节点。 如果为 NULL 表示查找失败。
功能of_get_next_child 函数用迭代的查找子节点

获取属性值的 of 函数
1 of_find_property 函数

函数property *of_find_property(const struct device_node *np,const char *name,int *lenp)
np设备节点
name属性名字
lenp属性值的字节数
返回值找到的属性
功能of_find_property 函数用于查找指定的属性

2 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设备节点
proname要读取的属性名字
out_value读取到的数组值
返回值0, 读取成功, 负值, 读取失败
功能有些属性只有一个整型值, 这四个函数就是用于读取这种只有一个整型值的属性, 分别用
于读取 u8、 u16、 u32 和 u64 类型属性值

3 of_property_read_u8_array 函数
of_property_read_u16_array 函数
of_property_read_u32_array 函数
of_property_read_u64_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设备节点
proname要读取的属性名字
out_value读取到的数组值, 分别为 u8、 u16、 u32 和 u64。
sz要读取的数组元素数量
返回值0, 读取成功, 负值, 读取失败
功能这 4 个函数分别是读取属性中 u8、 u16、 u32 和 u64 类型的数组数据, 比如大多数的 reg 属 性都是数组数据, 可以使用这 4 个函数一次读取出 reg 属性中的所有数据

4 of_property_read_string 函数

函数int of_property_read_string(struct device_node *np,const char *propname,const char
**out_string)
np设备节点
proname要读取的属性名字
out_string读取到的字符串值
返回值0, 读取成功, 负值, 读取失败
功能of_property_read_string 函数用于读取属性中字符串值

5 of_iomap 函数

函数void __iomem *of_iomap(struct device_node *np,int index)
np设备节点
indexreg 属性中要完成内存映射的段, 如果 reg 属性只有一段的话 index 就设置 0。
返回值经过内存映射后的虚拟内存首地址, 如果为 NULL 的话表示内存映射失败
功能of_iomap 函数用于直接内存映射, 以前我们会通过 ioremap 函数来完成物理地址到虚拟地址
的映射。

of 函数实验
这里以 imx6ull 开发板为例, 将编写驱动代码, 思路是当加载驱动的时候来读取属性和值。

driver.c 文件, 代码如下图所示:

#include  //初始化头文件
#include  //最基本的文件, 支持动态添加和卸载模块。
#include //定义结构体表示我们的节点
struct device_node *test_device_node;
static int hello_init(void)
{printk("hello world! \n");/**********添加我们要查找的节点的代码***********/// of_find_node_by_path 函数通过路径查找节点test_device_node = of_find_node_by_path("/test");if (test_device_node == NULL){//判断是否查找节点成功printk("of_find_node_by_path is error \n");return -1;} //打印节点的名字printk("test_device_node name is %s\n", test_device_node->name);return 0;
} static void hello_exit(void)
{printk("gooodbye! \n");
} module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

修改 Makefile 为如下所示:

obj-m += driver.o #先写生成的中间文件的名字是什么, -m 的意思是把我们的驱动编译成模块
KDIR:=/home/topeet/driver/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga/
PWD?=$(shell pwd) #获取当前目录的变量
all:make -C $(KDIR) M=$(PWD) modules #make 会进入内核源码的路径, 然后把当前路径下的代码编译成模块

保存 driver.c 文件, 编译 driver.c 为驱动模块。加载刚刚编译好的 driver.ko, 如下图所示:

由上图可知, 已经查找到设备节点, 并打印了节点的名称。
 

 获取属性内容

在上面代码的基础上进行修改, 代码如下所示:

#include  //初始化头文件
#include  //最基本的文件, 支持动态添加和卸载模块。
#include  //添加头文件//定义长度
int size;
//定义结构体表示我们的节点
struct device_node *test_device_node;
//定义结构体表示我们的节点属性
struct property *test_node_property;static int hello_init(void)
{printk("hello world! \n");/***********添加我们要查找的节点的代码**************************///of_find_node_by_path 函数通过路径查找节点test_device_node = of_find_node_by_path("/test");if (test_device_node == NULL){//判断是否查找节点成功printk("of_find_node_by_path is error \n");return -1;} //打印节点的名字printk("test_device_node name is %s\n", test_device_node->name);/**********获取 compatible 属性内容的代码************************/// of_find_property 函数查找节点属性test_node_property = of_find_property(test_device_node, "compatible", &size);if (test_node_property == NULL){//判断是否查找到节点属性内容printk("test_node_property is error \n");return -1;} //打印属性 compatible 的名字printk("test_node_property name is %s\n", test_node_property->name);//打印属性 compatible 的值printk("test_node_property value is %s\n", (char *)test_node_property->value);return 0;
} static void hello_exit(void)
{printk("gooodbye! \n");
} 
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

保存 driver.c 文件, 编译 driver.c 为驱动模块。加载刚刚编译好的 driver.ko, 如下图所示:

由上图可知, 已经查找到设备节点,并且已经获取到 compatible 属性内容。其他几个函数可以参照上面例子。
 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部