当前位置: 首页 > news >正文

Linux驱动开发笔记(五)——设备树(下)——OF函数

一、OF函数定义

第6.8讲 Linux设备树详解-绑定文档以及OF函数_哔哩哔哩_bilibili

《指南》43.9部分

        设备树的功能就是描述设备信息,帮助驱动开发。那么驱动如何获取设备信息?获取这些信息的函数linux直接提供,都定义在linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/include/linux/of.h文件中。这些函数统一以of开头,也称为OF函数。

1.1 查找节点

        设备树上的设备都是一个个节点。要获取某个设备信息,需要先找到这个设备节点。Linux内核使用device_node结构体来描述一个节点:

// 定义在linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/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
};

1.1.1 of_find_node_by_name

        通过名字查找节点。名字是完整的node-name@unit-address,不是只有前半部分node-name,也不是label。

struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
// from:   从哪个节点开始查找。为NULL表示从根节点开始查找整个设备树
// name:   要查找节点的名字。名字不要包含label!   
// return: 找到的节点。返回NULL表示未找到

1.1.2 of_find_node_by_type

        通过device_type类型查找。不过现在device_type已经弃用,这个函数也很少再用。

struct device_node *of_find_node_by_type(struct device_node *from, const char *type);
// from:   从哪个节点开始查找。为NULL表示从根节点开始查找整个设备树
// type:   要查找节点的device_type字符串
// return: 找到的节点。返回NULL表示未找到

1.1.3 of_find_compatible_node

        通过兼容性列表compatible查找

struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compat);
// from:   从哪个节点开始查找。为NULL表示从根节点开始查找整个设备树
// type:   要查找节点的device_type字符串。可以为NULL,表示忽略device_type属性
// compat: 要查找节点的compatible属性列表
// return: 找到的节点。返回NULL表示未找到
// eg: struct device_node *node;node = of_find_compatible_node(NULL, NULL, "arm,cortex-a7"); // 查找整个设备树上兼容arm,cortex-a7的设备节点

1.1.4 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
// return:  找到的节点。返回NULL表示未找到

1.1.5 of_find_node_by_path

        通过路径查找。这个路径指的是设备节点在设备树中的路径,不是文件路径。

static inline struct device_node *of_find_node_by_path(const char *path)
// path:   带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是根节点下backlight节点的全路径。 
// return: 找到的节点。返回NULL表示未找到
//eg:
struct device_node *np;
np = of_find_node_by_path("/soc/aips-bus@02000000/spba-bus@02000000/serial@02020000");

1.2 查找父/子节点

1.2.1 of_get_parent

获取指定节点的父节点

struct device_node *of_get_parent(const struct device_node *node)
// node  : 要查找父节点的节点
// return: 找到的父节点。NULL表示未找到

1.2.2 of_get_next_child

获取指定节点的子节点

struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev)
// node  : 要查找子节点的节点
// prev  : 前一个子节点。一个节点下有很多子节点,可以设置从哪一个子节点开始查找
//         可以设置为NULL,表示从第一个子节点开始
// return: 找到的子节点

1.3 提取属性值

        通过前面两类函数找到了目标节点,现在可以开始获得指定节点的具体属性了。

1.3.1 of_find_property

        查找指定的属性。

static inline struct property *of_find_property(const struct device_node *np,const char *name, int *lenp)
// np    : 节点
// name  : 属性名
// lenp  : 属性值的字节长度,一般写NULL即可
// return: 找到的属性

Linux内核中使用结构体property表示属性,其中property结构为:

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

 1.3.2 of_property_count_elems_of_size

        获取属性中元素的数量。比如reg属性值(如reg = <0x80000000 0x20000000>;)是一个数组,那么使用此函数可以获取到这个数组的大小(2)。

int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size)
// np       : 节点
// proname  : 需要统计元素数量的属性名字
// elem_size:每一个元素的size
// return   : 得到的属性元素数量

1.3.3 of_property_read_u32_index

        用于从属性中获取指定索引的u32类型数据值。

        比如某个属性有多个u32类型的值,那么就可以使用此函数来获取指定索引的数据值。

static inline int of_property_read_u32_index(const struct device_node *np,const char *propname, u32 index, u32 *out_value)
// np       : 节点
// proname  : 要读取的属性名
// index    : 要读取的值的索引
// out_value: 读取到的值
// return   : 0成功。负值失败:-EINVAL属性不存在,-ENODATA表示要读取的数据,-EOVERFLOW属性值列表太小

1.3.4 of_property_read_u8_array

        用于读取一个u8类型数组属性的所有数据。

        类似的函数还有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        : 节点
// propname  : 要读取的属性名
// out_values: 读取到的数组
// sz        : 要读取的数组元素数量
// return    : 0成功。负值失败:-EINVAL属性不存在,-ENODATA没有要读取的数据,-EOVERFLOW属性值列表太小。

1.3.5 of_property_read_u8

        除了数组属性以外,更多的属性还是只有一个整形值。该函数用于读取这种只有一个u8整形值的属性。

        类似的函数还有of_property_read_u16、of_property_read_u32、of_property_read_u64。

int of_property_read_u8_array(const struct device_node *np,const char *propname, u8 *out_values, size_t sz)
// np        : 节点
// propname  : 要读取的属性名
// out_values: 读取到的值
// return    : 0成功。负值失败:-EINVAL属性不存在,-ENODATA没有要读取的数据,-EOVERFLOW属性值列表太小。

1.3.6 of_property_read_string

        用于读取字符串类型属性的值

int of_property_read_string(struct device_node *np,const char *propname, const char **out_string)
// np        : 节点
// propname  : 要读取的属性名
// out_string: 读取到的字符串
// return    : 0成功。负值失败

1.3.7 of_n_addr_cells

        获取#size-cells的值

int of_n_addr_cells(struct device_node *np);
// np    : 节点
// return: 获取到的#size-cells的值

1.3.8 of_n_size_cells

        获取#size-cells的值

int of_n_size_cells(struct device_node *np)、
// np    : 节点
// return: 获取到的#size-cells的值

二、OF函数实际使用

第6.9讲 Linux设备树详解-OF函数操作实验_哔哩哔哩_bilibili

2.1 文件结构

        新建实验4文件夹4_dtsof,直接将之前实验3文件夹3_newchrled里的Makefile、newchrled.c、.vscode复制到新的文件夹里。将newchrled.c改名为dtsof.c。

        用vscode打开4_dtsof,将工作区另存为。

4_DTSOF (工作区)
├── .vscode
│   ├── c_cpp_properties.json
│   └── settings.json
├── 4_dtsof.code-workspace
├── Makefile
└── newchrled.c

        将Makefile中的obj-m修改为obj-m := dtsof.o

2.2 dtsof.c

2.2.1获取backlight的compatible属性值

imx6ull-alientek-emmc.dts中backlight的定义如下:

	backlight {compatible = "pwm-backlight";pwms = <&pwm1 0 5000000>;brightness-levels = <0 4 8 16 32 64 128 255>;default-brightness-level = <6>;status = "okay";};

 编写dtsof.c,获取backlight的compatible属性值:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>/* 模块入口 */
static int __init dtsof_init(void){int ret = 0;struct device_node *backlight_nd; // 节点指针struct property *comppro;         // 属性指针// 查找backlight节点 // 路径为/backlight   定义在linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/boot/dts/imx6ull-alientek-emmc.dtsbacklight_nd = of_find_node_by_path("/backlight"); //路径查找if(backlight_nd == NULL){ // 失败ret = -EINVAL;goto fail_findnd; // 错误处理}// 查找backlight属性comppro = of_find_property(backlight_nd, "compatible", NULL); // 属性名查找if(comppro == NULL){ret = -EINVAL;goto fail_finpro; // 错误处理} else {printk("compatible = %s\r\n",(char*)comppro->value);}return 0;fail_finpro:   // 查找属性失败
fail_findnd:  // 查找节点失败printk("failed\r\n");return ret;
}/* 模块出口 */
static void __exit dtsof_exit(void){}/* 注册入口出口*/
module_init(dtsof_init);
module_exit(dtsof_exit);
MODULE_LICENSE("GPL");

# VSCODE终端
make
sudo cp dtsof.ko /home/for/linux/nfs/rootfs/lib/modules/4.1.15/ -f# 串口
cd lib/modules/4.1.15/
modprobe dtsof.ko
depmod
modprobe dtsof.ko  # 可以看到输出:compatible = pwm-backlight
rmmod dtsof.ko

2.2.2获取backlight的所有属性值

        上面的代码只简单演示了一下使用OF函数获取compatible属性值。要获取backlight的所有属性值,完整代码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/slab.h>/* 模块入口 */
static int __init dtsof_init(void){int ret = 0;struct device_node *backlight_nd;// 节点指针struct property *comppro;        // 保存compatible属性const char* status;              // 保存status属性u32 default_brightness_level;    // 保存default-brightness-level属性u32 count = 0;                   // 保存brightness-levels属性的元素数量u32 *brightness_levels;          // 保存brightness-levels属性的数据u8 i = 0;                        // 给for循环用的// 1.查找backlight节点======================================================================// 路径为/backlight   // 定义在linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/boot/dts/imx6ull-alientek-emmc.dtsbacklight_nd = of_find_node_by_path("/backlight"); //路径查找if(backlight_nd == NULL){ // 失败ret = -EINVAL;goto faile_findnd;}// 2.查找backlight属性======================================================================// 获取compatible属性(属性名查找)comppro = of_find_property(backlight_nd, "compatible", NULL);if(comppro == NULL){ret = -EINVAL;goto fail_finpro;} else {printk("compatible = %s\r\n",(char*)comppro->value);}// 获取status属性(读取字符串)ret = of_property_read_string(backlight_nd, "status", &status);if(ret < 0){goto fail_finprs;} else {printk("status = %s\r\n", status);}// 获取default-brightness-level属性(读取u32)ret = of_property_read_u32(backlight_nd, "default-brightness-level", &default_brightness_level);if(ret < 0){goto fail_read32;} else {printk("default-brightness-level = %d\r\n", default_brightness_level);}// 获取brightness-levels的元素个数(读取元素个数)count = of_property_count_elems_of_size(backlight_nd, "brightness-levels", sizeof(u32));if(count < 0){goto fail_readele;} else {printk("brightness-level elems size = %d\r\n", count);// 获取brightness-levels的数据(读取u32数组)brightness_levels = kmalloc(count * sizeof(u32), GFP_KERNEL); // 内存申请if(!brightness_levels){ goto fail_mem;}ret = of_property_read_u32_array(backlight_nd, "brightness-levels", brightness_levels, count);if(ret < 0){goto fail_readarr; // 错误处理统一放到goto里面去,因此这里不释放内存,而是放到goto去处理} else {printk("brightness-level elems = ");for(i=0;i<count;i++){printk("%d ",brightness_levels[i]);}printk("\r\n");kfree(brightness_levels);}}return 0;/* 错误处理 */
// 这里goto只是演示一下格式,并不详细处理fail_readarr:  // 读取brightness-levels数据失败kfree(brightness_levels); //释放内存
fail_mem:          // 内存分配失败
fail_readele:      // 读取元素数量失败
fail_read32:       // 读取default-brightness-level失败
fail_finprs:       // 读取status失败
fail_finpro:       // 查找compatible失败
faile_findnd:      // 查找节点失败printk("failed\r\n");return ret;
}/* 模块出口 */
static void __exit dtsof_exit(void){}/* 注册入口出口*/
module_init(dtsof_init);
module_exit(dtsof_exit);
MODULE_LICENSE("GPL");

        modprobe以后应当能看到以下内容: 

http://www.dtcms.com/a/301326.html

相关文章:

  • 人社部物联网安装调试员的实训平台
  • RabbitMq 常用命令和REST API
  • 9.SpringBoot Web请求参数绑定方法
  • 盛最多水的容器-leetcode
  • 《Java 程序设计》第 7 章 - 继承与多态
  • 记录几个SystemVerilog的语法——时钟块和进程通信
  • maven聚合工程(多个mudule只编译、打包指定module)
  • JVM类加载机制全流程详解
  • 通过硬编码函数地址并转换为函数指针来调用函数
  • Java#包管理器来时的路
  • Leetcode-3427变长子数组求和
  • Mitk教程案例项目编译
  • 嵌入式——单片机的独立按键
  • 【3DsMax+Zbrush+SP】风格化低多边形模型制作教程——以制作杯子为例——03在Substance Painter中制作材质
  • PyTorch深度学习入门记录3
  • 两数之和 II - 输入有序数组-leetcode
  • 【深度学习系列82】joyagent上手体验
  • 五、搭建springCloudAlibaba2021.1版本分布式微服务-gateway网关
  • 基于大模型的预训练、量化、微调等完整流程解析
  • 【思维链(CoT)技术深度解析】从理论到实践的革命性推理方法
  • pytest中的参数化
  • Python-初学openCV——图像预处理(三)
  • 相对路径遍历(CWE-22)
  • 【杂谈】-被引用即被看见:生成式AI如何改写内容规则
  • vscode找不到python解释器的解决方案
  • 8. 若依框架的AjaxResult
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现PCB上二维码检测识别(C#代码UI界面版)
  • 欢乐的周末 - 华为OD统一考试(JavaScript 题解)
  • RAG(检索增强生成)
  • Vue 四个map的使用方法