最全Linux驱动开发八股文(四)
你好,我是拉依达。
这是我的Linux驱动开发八股文详细解析系列。
本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。
全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料。
现重新对内容进行整理,希望可以帮助到更多学习嵌入式的同学。
5.4 OF操作函数
Linux 内核提供了一系列的函数来获取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀“of_” (称为OF 函数)
查找节点
Linux 内核使用 device_node 结构体来描述一个节点:
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; /* 子节点
...
}
-
通过节点名字查找指定的节点:of_find_node_by_name
struct device_node *of_find_node_by_name(struct device_node *from,const char *name)
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。 name:要查找的节点名字。 返回值: 找到的节点,如果为 NULL 表示查找失败。
-
通过 device_type 属性查找指定的节点:of_find_node_by_type
struct device_node *of_find_node_by_type(struct device_node *from, const char *type)
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。 type:要查找的节点对应的 type 字符串, device_type 属性值。 返回值: 找到的节点,如果为 NULL 表示查找失败
-
通过device_type 和 compatible两个属性查找指定的节点:of_find_compatible_node
struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible)
from:开始查找的节点,如果为 NULL 表示从根节点开始查找整个设备树。 type:要查找的节点对应的 type 字符串,device_type 属性值,可以为 NULL compatible: 要查找的节点所对应的 compatible 属性列表。 返回值: 找到的节点,如果为 NULL 表示查找失败
-
通过 of_device_id 匹配表来查找指定的节点:of_find_matching_node_and_match
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 表示查找失败
-
通过路径来查找指定的节点:of_find_node_by_path
inline struct device_node *of_find_node_by_path(const char *path)
path:设备树节点中绝对路径的节点名,可以使用节点的别名 返回值: 找到的节点,如果为 NULL 表示查找失败
获取属性值
Linux 内核中使用结构体 property 表示属性
struct property {
char *name; /* 属性名字 */
int length; /* 属性长度 */
void *value; /* 属性值 */
struct property *next; /* 下一个属性 */
unsigned long _flags;
unsigned int unique_id;
struct bin_attribute attr;
}
-
查找指定的属性:of_find_property
property *of_find_property(const struct device_node *np, const char *name, int *lenp)
np:设备节点。 name: 属性名字。 lenp:属性值的字节数,一般为NULL 返回值: 找到的属性。
-
获取属性中元素的数量(数组):of_property_count_elems_of_size
int of_property_count_elems_of_size(const struct device_node *np, const char *propname int elem_size)
np:设备节点。 proname: 需要统计元素数量的属性名字。 elem_size:元素长度。 返回值: 得到的属性元素数量
-
从属性中获取指定标号的 u32 类型数据值:of_property_read_u32_index
int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value)
np:设备节点。 proname: 要读取的属性名字。 index:要读取的值标号。 out_value:读取到的值 返回值: 0 读取成功; 负值: 读取失败, -EINVAL 表示属性不存在 -ENODATA 表示没有要读取的数据, -EOVERFLOW 表示属性值列表太小
-
读取属性中 u8、 u16、 u32 和 u64 类型的数组数据
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)
np:设备节点。 proname: 要读取的属性名字。 out_value:读取到的数组值,分别为 u8、 u16、 u32 和 u64。 sz: 要读取的数组元素数量。 返回值: 0:读取成功; 负值: 读取失败 -EINVAL 表示属性不存在 -ENODATA 表示没有要读取的数据 -EOVERFLOW 表示属性值列表太小
-
读取属性中字符串值: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,读取成功,负值,读取失败
-
获取 #address-cells 属性值:of_n_addr_cells ,获取 #size-cells 属性值:of_size_cells 。
int of_n_addr_cells(struct device_node *np) int of_n_size_cells(struct device_node *np)
np:设备节点。 返回值: 获取到的#address-cells 属性值。 返回值: 获取到的#size-cells 属性值。
-
内存映射 of_iomap 函数用于直接内存映射,前面通过 ioremap 函数来完成物理地址到虚拟地址的映射,采用设备树以后就可以直接通过 of_iomap 函数来获取内存地址所对应的虚拟地址。这样就不用再去先获取reg属性值,再用属性值映射内存。
of_iomap 函数本质上也是将 reg 属性中地址信息转换为虚拟地址,如果 reg 属性有多段的话,可以通过 index 参数指定要完成内存映射的是哪一段, of_iomap 函数原型如下:
void __iomem *of_iomap(struct device_node *np, int index)
np:设备节点。 index: reg 属性中要完成内存映射的段,如果 reg 属性只有一段的话 index 就设置为 0。 返回值: 经过内存映射后的虚拟内存首地址,如果为 NULL 的话表示内存映射失败。
例
#if 1 /* 1、寄存器地址映射 */ IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]); SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]); SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]); GPIO1_DR = ioremap(regdata[6], regdata[7]); GPIO1_GDIR = ioremap(regdata[8], regdata[9]); #else //第一对:起始地址+大小 -->映射 这样就不用获取reg的值 IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0); SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1); SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2); GPIO1_DR = of_iomap(dtsled.nd, 3); GPIO1_GDIR = of_iomap(dtsled.nd, 4); #endif
of 函数在 led_init() 中应用
int ret;
u32 regdate[14];
const char *str;
struct property *proper;
/* 1 、获取设备节点: */
dtb_led.nd = of_find_node_by_path("/songwei_led");
if(dtb_led.nd == NULL)
{
printk("songwei_led node can not found!\r\n");
return -EINVAL;
}else
{
printk("songwei_led node has been found!\r\n");
}
/* 2 、获取 compatible 属性内容 */
proper = of_find_property(dtb_led.nd ,"compatible",NULL);
if(proper == NULL)
{
printk("compatible property find failed\r\n");
} else
{
printk("compatible = %s\r\n", (char*)proper->value);
}
/* 3 、获取 status 属性内容 */
ret = of_property_read_string(dtb_led.nd, "status", &str);
if(ret < 0)
{
printk("status read failed!\r\n");
}else
{
printk("status = %s\r\n",str);
}
/* 4 、获取 reg 属性内容 */
ret = of_property_read_u32_array(dtb_led.nd, "reg", regdate, 10);
if(ret < 0)
{
printk("reg property read failed!\r\n");
}else
{
u8 i = 0;
printk("reg data:\r\n");
for(i = 0; i < 10; i++)
printk("%#X ", regdate[i]);
printk("\r\n");
}
#嵌入式##校招##八股文##Linux##linux驱动#你好,我是拉依达。 这是我的Linux驱动开发八股文详细解析系列。 本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。 全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料。 现在我重新对内容进行整理,已专栏的形式发布在牛客上,希望可以帮助到更多学习嵌入式的同学。