【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十二章 设备树常用of函数

i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口一应俱全。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相关历程,支持8路PDM接口、5路SAI接口、2路Speaker。系统支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9系统。适用于智能充电桩,物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网应用、

【公众号】迅为电子

【粉丝群】258811263(加群获取驱动文档+例程)


第五十二章 设备树常用of函数

本章导读

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

52.1章节讲解了设备树中常用的of函数

52.2章节在52.1章节的基础上设计了四个小实验,分别来获取查找的设备节点,获取属性内容,获取reg属性,获取status属性。

本章内容对应视频讲解链接(在线观看):

设备树中常用的of操作函数  https://www.bilibili.com/video/BV1Vy4y1B7ta?p=27

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\010-设备树常用OF函数”路径下。

52.1 设备树常用of函数

52.1.1 查找节点的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 函数用迭代的查找子节点

52.1.2 获取属性值的of函数

与获取属性值的 OF 函数有 5个,我们依次来看一下。

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  函数 

函数

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 函数用于读取属性中字符串值

52.2 of函数实验

这里我们以iTOP-IMX8MM开发板为例,我们将编写驱动代码,思路是当我们加载驱动的时候来读取属性和值。我们在Ubuntu的/home/topeet/imx8mm/10/目录下新建driver.c文件。将Makefile文件和build.sh文件拷贝到driver.c同级目录下,修改Makefile为如下所示:

obj-m += led.o
KDIR:=/home/topeet/linux/linux-imx
PWD?=$(shell pwd)
all:
	make -C $(KDIR) M=$(PWD) modules ARCH=arm64
clean:
	make -C $(KDIR) M=$(PWD) clean

52.2.1 查找的节点

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\010-设备树常用OF函数\001”路径下。

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

/*
 * @Author: topeet
 * @Description: of函数实验查找设备节点
 */
#include <linux/init.h>   //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/of.h>

//定义结构体表示我们的节点
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("goodbye \n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

保存driver.c文件,编译driver.c为驱动模块,如下图所示:

驱动编译完,我们通过nfs将编译好的驱动程序加载模块。我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示: 

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

52.2.2 获取属性内容

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\010-设备树常用OF函数\002”路径下。

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

/*
 * @Author: topeet
 * @Description: of函数实验获取节点属性
 */
#include <linux/init.h>   //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/of.h>

//定义结构体表示我们的节点
struct device_node *test_device_node;

//定义结构体表示我们的节点属性
struct property *test_node_property;
//定义长度
int size;

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;
    }
    

    /**********获取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;
    }
    //打印节点的名字
    printk("test_device_node name is %s\n", test_device_node->name);
    //打印属性compatible的名字
    printk("test_node_property value is %s\n", (char *)test_node_property->value);

    return 0;
}

static void hello_exit(void)
{
    printk("goodbye \n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

保存driver.c文件,编译driver.c为驱动模块,如下图所示:

驱动编译完,我们通过nfs将编译好的驱动程序加载模块,我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示: 

由上图可知,我们已经查找到设备节点,并且已经获取到compatible属性内容。

52.2.3 获取reg属性

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\010-设备树常用OF函数\003”路径下。

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

/*
 * @Author: topeet
 * @Description: of函数实验获取reg属性
 */
#include <linux/init.h>   //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/of.h>

//定义结构体表示我们的节点
struct device_node *test_device_node;

//定义结构体表示我们的节点属性
struct property *test_node_property;
//定义长度
int size;
u32 out_values[2] = {0};

static int hello_init(void)
{
    int ret;
    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;
    }

    /**********获取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;
    }

    /**********获取reg属性内容的代码************/
    ret = of_property_read_u32_array(test_device_node, "reg", out_values, 2);
    if (ret < 0)
    {
        //打印获取失败
        printk("of_property_read_u32_array is error \n");
        return -1;
    }

    //打印节点的名字
    printk("test_device_node name is %s\n", test_device_node->name);
    //打印属性compatible的名字
    printk("test_node_property value is %s\n", (char *)test_node_property->value);
    //打印获取reg属性内容
    printk("out_values[0] is 0x%8x\n", out_values[0]);
    printk("out_values[1] is 0x%8x\n", out_values[1]);
    return 0;
}

static void hello_exit(void)
{
    printk("goodbye \n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

保存driver.c文件,编译driver.c为驱动模块,如下图所示:

驱动编译完,我们通过nfs将编译好的驱动程序加载模块,我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示: 

由上图可知,我们已经查找到设备节点,并且已经获取到reg属性的值。

52.2.4 获取status属性

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\010-设备树常用OF函数\004”路径下。

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

/*
 * @Author: topeet
 * @Description: of函数实验获取status属性
 */
#include <linux/init.h>   //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/of.h>

//定义结构体表示我们的节点
struct device_node *test_device_node;

//定义结构体表示我们的节点属性
struct property *test_node_property;
//定义长度
int size;
u32 out_values[2] = {0};

const char *str;

static int hello_init(void)
{
    int ret;
    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;
    }

    /**********获取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;
    }

    /**********获取reg属性内容的代码************/
    ret = of_property_read_u32_array(test_device_node, "reg", out_values, 2);
    if (ret < 0)
    {
        //打印获取失败
        printk("of_property_read_u32_array is error \n");
        return -1;
    }

    /**********获取status属性内容的代码*********/
    ret = of_property_read_string(test_device_node, "status", &str);
    if (ret < 0)
    {
        //打印获取失败
        printk("of_property_read_string is error  \n");
        return -1;
    }

    //打印节点的名字
    printk("test_device_node name is %s\n", test_device_node->name);
    //打印属性compatible的名字
    printk("test_node_property value is %s\n", (char *)test_node_property->value);
    //打印获取reg属性内容
    printk("out_values[0] is 0x%8x\n", out_values[0]);
    printk("out_values[1] is 0x%8x\n", out_values[1]);
    //打印status属性
    printk("status is %s \n", str);

    return 0;
}

static void hello_exit(void)
{
    printk("goodbye \n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

保存driver.c文件,编译driver.c为驱动模块,如下图所示:

驱动编译完,我们通过nfs将编译好的驱动程序加载模块,然后我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示: 

由上图可知,我们已经查找到设备节点,并且已经获取到status属性。

 

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值