在驅動開發中如果可以使用設備樹進行參數配置而不用頻繁修改源碼時間很方便的事情。
這裏以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