背景
講完設備樹的有關概念以及語法以后,我們接下來就讓 我們的驅動 使用 設備樹。
ref : 《內核學習筆記14:內核設備樹學習》、《u-boot對設備樹的支持》
測試代碼
本文使用的設備樹節點如下:
/ {
// 專門用於測試dts的示例,沒實例用途
// 名稱可以有“,”、“-”,如“ll,i2c-enable”
myfoo {
compatible = "ll,jimkent-foo";
status = "okay"; // string
enable; // bool,無須值
myvalue = <250>; // 默認是32位,如果使用8位讀取,結果為0
value = /bits/ 8 <88>; // 8位單獨賦值
value16 = /bits/ 16 <166>; // 16位單獨賦值
a-cell = <1 2 3 4 5>; // 數組
// 子節點
foo {
label = "foo";
note = "this is foo";
};
bar {
label = "bar";
note = "this is bar";
};
};
// 其中compatible與驅動使用的名稱必須一致(這樣才能匹配上)。其它內容比較簡單,分別是字符串、布爾類型、不同位數的數值、數組、子節點。
}
驅動實例如下:
/**
* @file foo_drv.c
* @author Late Lee <latelee@163.com>
* @date Wed Jun 7 22:21:19 2019
*
* @brief 測試dts示例
*
* @note 讀取dts的值,學習dts。代碼有部分警告,不影響
*/
#include <linux/module.h>
#include <linux/kernel.h> /**< printk() */
#include <linux/init.h>
#include <linux/types.h> /**< size_t */
#include <linux/errno.h> /**< error codes */
#include <linux/string.h>
#include <linux/of.h>
#include <linux/of_device.h>
static int foo_remove(struct platform_device *dev)
{
printk(KERN_NOTICE "remove...\n");
return 0;
}
static int foo_probe(struct platform_device *dev)
{
int ret = 0;
struct device_node* np = dev->dev.of_node;
struct device_node* child = NULL;
const char* str = NULL;
bool enable = false;
u8 value = 0;
u16 value16 = 0;
u32 value32 = 0;
// 測試dts讀取API
if(np == NULL)
{
pr_info("of_node is NULL\n");
return 0;
}
of_property_read_string(np, "status", &str); // 讀字符串
enable = of_property_read_bool(np, "enable"); // bool類型,可判斷某字段存在不存在
of_property_read_u32(np, "myvalue", &value32); // 一般地,都使用u32讀取數值
of_property_read_u8(np, "value", &value);
of_property_read_u16(np, "value16", &value16);
u32 data[3] = {0};
u32 tag = 0;
// a-cell是一個數組,默認讀第1個。
of_property_read_u32(np, "a-cell", &tag);
// 也可以讀取指定大小的數組(不一定是全部的)
of_property_read_u32_array(np, "a-cell", data, ARRAY_SIZE(data));
printk("of read status: %s enable: %d value: %d %d %d\n", str, enable, value, value16, value32);
printk("of read tag: %d data: %d %d %d\n", tag, data[0], data[1], data[2]);
// 獲取子節點個數
int count = of_get_available_child_count(np);
// 遍歷所有子節點,按格式讀取屬性
int index = 0;
for_each_available_child_of_node(np,child)
{
const char* label = of_get_property(child,"label",NULL) ? : child->name;
const char* note = of_get_property(child,"note",NULL) ? : child->name;
printk("of read: label: %s note: %s\n", label, note);
}
return ret;
}
static struct of_device_id foo_of_match[] = {
{ .compatible = "ll,jimkent-foo", },
{ /* sentinel */ }
};
static struct platform_driver foo_driver = {
.driver = {
.name = "foo",
.of_match_table = of_match_ptr(foo_of_match),
},
.probe = foo_probe,
.remove = foo_remove,
};
static int __init foo_drv_init(void)
{
int ret = 0;
ret = platform_driver_register(&foo_driver);
if (ret)
{
pr_info("platform_driver_register failed!\n");
return ret;
}
pr_info("Init OK!\n");
return ret;
}
static void __exit foo_drv_exit(void)
{
platform_driver_unregister(&foo_driver);
}
module_init(foo_drv_init);
module_exit(foo_drv_exit);
MODULE_AUTHOR("Late Lee");
MODULE_DESCRIPTION("Simple platform driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:foo");
示例的代碼是一個簡單的模板,除了學習dts外,沒什么用處。但是可以以此展開復雜的、有實際用途的驅動。
與以前的platform驅動不同,platform_driver
中指定of_match_table
,foo_of_match
結構體的.compatible
必須與設備樹的compatible
一致。
本驅動涉及到的讀取設備樹節點信息的函數如下,更多函數,參考內核源碼的include/linux/of.h
頭文件:
of_property_read_string // 讀取字符串
of_property_read_bool // 判斷某個字段是否存在,無須賦值
of_property_read_u8 // 讀取8比特
of_property_read_u16 // 讀取16比特
of_property_read_u32 // 讀取32比特
如果存在多個子節點,用of_get_available_child_count
獲取個數(可用於開辟內存),然后調用for_each_available_child_of_node
遍歷所有子節點,注意,of_get_property
與of_property_read_string
有相同效果。只是用法不同而已。