火影 发表于 2024-7-28 19:34:08

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发

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为驱动模块,如下图所示:
https://i-blog.csdnimg.cn/direct/2876c2b1eeb243139a7c5178a344a753.png
驱动编译完,我们通过nfs将编译好的驱动步伐加载模块。我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示: 
https://i-blog.csdnimg.cn/direct/832350227b664bf38a441f6413473d1b.png
由上图可知,我们已经查找到装备节点,并打印了节点的名称。
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为驱动模块,如下图所示:
https://i-blog.csdnimg.cn/direct/ef1dfeff2565411d8f635d034bdb09db.png
驱动编译完,我们通过nfs将编译好的驱动步伐加载模块,我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示: 
https://i-blog.csdnimg.cn/direct/904e20dee5144f9281cb114927eca7d2.png
由上图可知,我们已经查找到装备节点,而且已经获取到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 = {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 is 0x%8x\n", out_values);
    printk("out_values is 0x%8x\n", out_values);
    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为驱动模块,如下图所示:
https://i-blog.csdnimg.cn/direct/033893fd226b40a391eac84057a62693.png
驱动编译完,我们通过nfs将编译好的驱动步伐加载模块,我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示: 
https://i-blog.csdnimg.cn/direct/3273c3f4f4fc445cb37dfcb50c0d38e8.png
由上图可知,我们已经查找到装备节点,而且已经获取到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 = {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 is 0x%8x\n", out_values);
    printk("out_values is 0x%8x\n", out_values);
    //打印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为驱动模块,如下图所示:
https://i-blog.csdnimg.cn/direct/01ba61f3880c473dbb55772f51577409.png
驱动编译完,我们通过nfs将编译好的驱动步伐加载模块,然后我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示: 
https://i-blog.csdnimg.cn/direct/a38da9a734ef429e8372805212c082cb.png
由上图可知,我们已经查找到装备节点,而且已经获取到status属性。


 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发