本文已参与「新人创作礼」活动,一起开启掘金创作之路。
在驱动开发中如果可以使用设备树进行参数配置而不用频繁修改源码时间很方便的事情。 这里以I2C设备举例,记录一下。
一、设备树修改
1、添加设备节点
由于要使用的设备挂载在I2C下,所以要先找到指定的I2C节点,然后添加。 设备树中定义:
&i2c1 {
modle1@15 {
compatible = "company name,modle";
reg = <0x15>;
marray = <1 2 3 4 5 6>;
mstring = "this is modle";
mint = <5866>;
};
};
解释:
&i2c1 {
节点名@地址 {
匹配属性名(必有不能改)compatible = "company name,modle";
地址属性名(必有不能改)reg = <0x15>;
数组属性名(自定义)marray = <1 2 3 4 5 6>;
字符串属性名(自定义)mstring = "this is modle";
数字属性名(自定义)mint = <5866>;
};
};
2、编译替换
将设备树编译成**.dtb**文件然后进行替换,这里就不多说了。
二、驱动中读取属性
正常获取属性都是在驱动通过 compatible 这个属性匹配上之后,probe 函数被调用时获取设备树的节点。
int iic_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int i;
struct device dev; /* 设备对象 */
const char *mstr;
unsigned int mint;
unsigned int marray[10];
dev = client->dev;
memset(marray, 0, sizeof(marray));
of_property_read_u32_array(dev.of_node, "marray", marray, 6); /* 读取节点中属性名为 marray 的数组 */
of_property_read_string(dev.of_node, "mstring", &mstr); /* 读取节点中属性名为 mstring 的字符串 */
of_property_read_u32(dev.of_node, "mint", &mint); /* 读取节点中属性名为 mint 的数字 */
PRINT("%s ------ addr = %x\n", __FUNCTION__, client->addr); /* 打印设备地址,即 reg 属性的值 */
PRINT("%s ------ of_node->name = %s\n", __FUNCTION__, dev.of_node->name); /* 打印节点名称 */
for (i = 0; i < 10; i++) {
PRINT("%s ------ marray[%d] = %d\n", __FUNCTION__, i, marray[i]);
}
PRINT("%s ------ mstr = %s\n", __FUNCTION__, mstr);
PRINT("%s ------ mint = %d\n", __FUNCTION__, mint);
return 0;
}
三、函数介绍
这里就只记录三种,获取数字、获取字符串、获取数组,相似的函数使用起来都大同小异。 驱动中获取设备数文件属性的函数都在头文件 \kernel\include\linux\of.h 中有声明。
1、of_property_read_u32_array
- 获取无字符的32位整型数组。
/* 原型 */
static inline int of_property_read_u32_array(const struct device_node *np,
const char *propname, u32 *out_values, size_t sz)
参数解释:
- 参数1 - 设备节点:当驱动和设备树中的节点匹配上时,可以通过 struct device 结构体中的成员 of_node 获取节点。
- 参数2 - 属性名称:要获取的属性名称的字符串。
- 参数3 - 数组地址:用于装结果的数组地址。
- 参数4 - 数组长度:这个长度不能大于设备树中数组长度(可小于等于),否则或读不出数据。
2、of_property_read_string
- 获取字符串。
/* 原型 */
static inline int of_property_read_string(struct device_node *np,
const char *propname, const char **out_string)
参数解释:
- 参数1 - 设备节点:当驱动和设备树中的节点匹配上时,可以通过 struct device 结构体中的成员 of_node 获取节点。
- 参数2 - 属性名称:要获取的属性名称的字符串。
- 参数3 - 字符串变量地址指针:要先定义一个 const char * 的变量,然后将该变量再取地址传入。
3、of_property_read_u32
- 获取一个32位无符号数字。
/* 原型 */
static inline int of_property_read_u32(const struct device_node *np,
const char *propname, u32 *out_value)
参数解释:
- 参数1 - 设备节点:当驱动和设备树中的节点匹配上时,可以通过 struct device 结构体中的成员 of_node 获取节点。
- 参数2 - 属性名称:要获取的属性名称的字符串。
- 参数3 - 数字变量地址指针:要定义一个 unsigned int 变量,然后将该变量的地址传入。
四、驱动测试代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
/*************************************************************************************************/
// 局部宏定义
/*************************************************************************************************/
#define EN_DEBUG 1 /* 调试信息开关 */
#if EN_DEBUG
#define PRINT(x...) printk(KERN_EMERG x) /* 设置为紧急信息等级 */
#else
#define PRINT(x...)
#endif
/*************************************************************************************************/
// 用来匹配设备的id
/*************************************************************************************************/
static const struct of_device_id of_device_match[] = {
{.compatible = "company name,modle"},
{}
};
/*************************************************************************************************/
// 设备类型id列表
/*************************************************************************************************/
const struct i2c_device_id i2c_device_table[] = {
{"modle", 0},
{},
};
/**************************************************************************************************
** 功能: 本驱动的探针函数,当驱动加载时被调用
** 参数: 无
** 返回: 无
**************************************************************************************************/
int iic_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int i;
struct device dev; /* 设备对象 */
const char *mstr;
unsigned int mint;
unsigned int marray[10];
dev = client->dev;
memset(marray, 0, sizeof(marray));
of_property_read_u32_array(dev.of_node, "marray", marray, 4); /* 读取节点中属性名为 marray 的数组 */
of_property_read_string(dev.of_node, "mstring", &mstr); /* 读取节点中属性名为 mstring 的字符串 */
of_property_read_u32(dev.of_node, "mint", &mint); /* 读取节点中属性名为 mint 的数字 */
PRINT("%s ------ addr = %x\n", __FUNCTION__, client->addr); /* 打印设备地址,即 reg 属性的值 */
PRINT("%s ------ of_node->name = %s\n", __FUNCTION__, dev.of_node->name); /* 打印节点名称 */
for (i = 0; i < 10; i++) {
PRINT("%s ------ marray[%d] = %d\n", __FUNCTION__, i, marray[i]);
}
PRINT("%s ------ mstr = %s\n", __FUNCTION__, mstr);
PRINT("%s ------ mint = %d\n", __FUNCTION__, mint);
return 0;
}
/**************************************************************************************************
** 功能: 本驱动的移除函数,当驱动卸载时被调用
** 参数: 无
** 返回: 无
**************************************************************************************************/
int iic_remove(struct i2c_client *client)
{
return 0;
}
/* i2c驱动对象 */
struct i2c_driver iic_driver = {
.probe = iic_probe,
.remove = iic_remove,
.driver = {
.name = "iic_dev_name",
.of_match_table = of_match_ptr(of_device_match),
},
.id_table = i2c_device_table,
};
/**************************************************************************************************
** 函数名称: drv_init
** 功能: 驱动初始化函数,在加载时被调用
** 参数: 无
** 返回: 无
**************************************************************************************************/
static int __init drv_init(void)
{
PRINT("%s ------ \n", __FUNCTION__);
i2c_register_driver(THIS_MODULE, &iic_driver); /* 注册i2c驱动 */
return 0;
}
/**************************************************************************************************
** 函数名称: drv_exit
** 功能描述: i2c驱动退出函数,在卸载时被调用
** 参数: 无
** 返回: 无
**************************************************************************************************/
static void __exit drv_exit(void)
{
PRINT("%s ------ \n", __FUNCTION__);
i2c_del_driver(&iic_driver); /* 移除i2c驱动 */
}
module_init(drv_init); /* 模块初始化 */
module_exit(drv_exit); /* 模块卸载 */
MODULE_AUTHOR("hrx"); /* 模块作者 */
MODULE_DESCRIPTION("Linux Driver"); /* 模块描述 */
MODULE_VERSION("1.0.0"); /* 模块版本 */
MODULE_LICENSE("GPL"); /* 模块遵守的License */
五、测试结果
root@imx6qsabresd:/tmp# insmod devtree_demo.ko
[ 1073.108993] drv_init ------
[ 1073.112013] iic_probe ------ addr = 15
[ 1073.115819] iic_probe ------ of_node->name = modle1
[ 1073.120710] iic_probe ------ marray[0] = 1
[ 1073.124848] iic_probe ------ marray[1] = 2
[ 1073.128955] iic_probe ------ marray[2] = 3
[ 1073.133056] iic_probe ------ marray[3] = 4
[ 1073.137185] iic_probe ------ marray[4] = 0
[ 1073.141289] iic_probe ------ marray[5] = 0
[ 1073.145414] iic_probe ------ marray[6] = 0
[ 1073.149520] iic_probe ------ marray[7] = 0
[ 1073.153621] iic_probe ------ marray[8] = 0
[ 1073.157865] iic_probe ------ marray[9] = 0
[ 1073.161973] iic_probe ------ mstr = this is modle
[ 1073.166705] iic_probe ------ mint = 5866