最全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驱动开发八股文 文章被收录于专栏

你好,我是拉依达。 这是我的Linux驱动开发八股文详细解析系列。 本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。 全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料。 现在我重新对内容进行整理,已专栏的形式发布在牛客上,希望可以帮助到更多学习嵌入式的同学。

全部评论

相关推荐

1 收藏 评论
分享
牛客网
牛客企业服务