嵌入式核心及驅動開發之學習筆記(十七) 裝置樹的定義規則和獲取方法
概述
在Linux 2.6中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥著大量的垃圾程式碼,相當多數的程式碼只是在描述板級細節,而這些板級細節對於核心來講,不過是垃圾,如板上的platform裝置、resource、i2c_board_info、spi_board_info以及各種硬體platform_data。常見的s3c2410、s3c6410等板級目錄,程式碼量在數萬行。
後來Linux開發社群就開始整改,裝置樹最早用於PowerPC等其他體系架構,ARM架構開發社群就開始採用裝置樹來描述裝置的資訊。
採用Device Tree後,許多硬體的細節可以直接透過它傳遞給Linux,而不再需要在kernel中進行大量的冗餘編碼。
本文重點是如果新增裝置樹節點和程式通過裝置樹節點獲取硬體資訊的方法。關於裝置樹裝置樹更全面的瞭解請參考文章
https://blog.csdn.net/21cnbao/article/details/8457546
執行流程分析
裝置樹原始檔dts被編譯成dtb二進位制檔案,在bootloader執行時傳遞給作業系統,作業系統對其進行解析展開(Flattened),從而產生一個硬體裝置的拓撲圖有了這個拓撲圖,在程式設計的過程中可以直接通過系統提供的介面獲取到裝置樹中的節點和屬性資訊
裝置樹原始檔(dts) --> (DTC編譯) --> 裝置樹二進位制檔案 --> (u-boot載入到記憶體,核心解析展開) --> 樹形結構體
我們只要通過在dts原始檔新增裝置樹節點,最後就可以讀取展開的裝置樹節點了。
節點(node)和屬性(property)
- 節點:節點用以歸類描述一個硬體資訊或是軟體資訊(好比檔案系統的目錄)
- 屬性:節點內描述了一個或多個屬性,屬性是鍵值對(key - value),描述具體的軟/硬資訊
DTS描述鍵值對(key - value)的語法
- 字串資訊 string-property = "a string"
- 32bits無符號整型陣列資訊 cell-property = <0xbeef 123 0xabcd1234>
- 二進位制數陣列 binary-property = [01 23 45 67];
- 混和形式 mixed-property = "a string", [01 23 45 67], <0x12345678>;
- 字串雜湊表 string-list = "red fish", "blue fish";
常見屬性(帶有某種特殊意義的屬性)
compatible = "acme,coyotes-revenge";
#address-cells = <1> 描述子節點reg屬性值的地址表中首地址cell數量
#size-cells = <1>; 描述子節點reg屬性值的地址表中地址長度cell數量
interrupt-controller - 一個空屬性用來宣告這個node接收中斷訊號;
#interrupt-cells - 這是中斷控制器節點的屬性,用來標識這個控制器需要幾個單位做中斷描述符;
interrupt-parent - 標識此裝置節點屬於哪一個中斷控制器,如果沒有設定這個屬性,會自動依附父節點的
interrupts - 一箇中斷識別符號列表,表示每一箇中斷輸出訊號。
獲取裝置樹節點資訊的一些API
在 linux/of.h 檔案中可以看見這些介面函式
1.獲取節點
//通過路徑索引節點
struct device_node *of_find_node_by_path(const char *path)
2.通過節點獲取屬性
//提取屬性的值
struct property *of_find_property(const struct device_node *np,const char *name,int *lenp)
//獲取屬性的整形陣列
int of_property_read_u32_array(const struct device_node *np,const char *propname, u8* out_values, size_t sz)
//獲取屬性中的字串陣列
static inline int of_property_read_string_index(struct device_node *np,const char *propname, int index,const char **out_string)
//從節點中獲取中斷號
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
編譯裝置樹
1.以參考板origen的裝置樹為模板,建立dts檔案
[email protected]:linux-3.14-fs4412# cp arch/arm/boot/dts/exynos4412-origen.dts arch/arm/boot/dts/exynos4412-fs4412.dts
2.修改Makefile的編譯配置規則,使能編譯新檔案
vim arch/arm/boot/dts/Makefile
在`exynos4412-origen.dtb \ `下面新增 `exynos4412-fs4412.dtb \
3.編譯裝置樹動作
[email protected]:linux-3.14-fs4412# make dtbs
4.將編譯後的dtb檔案cp到tftp根目錄下
[email protected]:linux-3.14-fs4412# cp arch/arm/boot/dts/exynos4412-fs4412.dtb /tftpboot/
5.設定板子uboot的啟動引數
FS4412 # set bootcmd tftp 0x41000000 uImage \; tftp 0x42000000 exynos4412-fs4412.dtb \; bootm 0x41000000 - 0x42000000
FS4412 # saveenv
實驗過程
1.新增裝置樹(DTS)節點,描述按鍵資訊
[email protected]{
compatible = "farsight,test";
reg = <0x12345678 0x24
0x87654321 0x24>;
testprop,mytest;
test_list_string = "red fish","fly fish", "blue fish";
interrupt-parent = <&gpx1>;
interrupts = <1 4>;
}
2.將編譯後的裝置樹檔案cp到tftp跟目錄,板子上電後核心會解析 proc/device-tree/ 檢視解析記錄
3.編寫驅動程式碼,API獲取節點-屬性-值,並拿中斷號申請中斷資源
//dt_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#define U32_DATA_LEN 4
static int is_good; //標誌位
static int irqno; //中斷號
irqreturn_t key_irq_handler(int irqno, void *devid); //中斷處理方法
/*
[email protected]{
compatible = "farsight,test";
reg = <0x12345678 0x24
0x87654321 0x24>;
testprop,mytest;
test_list_string = "red fish","fly fish", "blue fish";
interrupt-parent = <&gpx1>;
interrupts = <1 4>;
}
*/
static int __init dt_drv_init(void)
{
struct device_node *np = NULL;
//獲取結點資訊(按照節點路徑查詢)
np = of_find_node_by_path("/[email protected]");
if(np)
{
printk("find test node ok\n");
printk("node name = %s\n", np->name);
printk("node full name = %s\n", np->full_name);
}
else
{
printk("find test node failed\n");
}
//獲取到節點中的屬性
struct property *prop = NULL;
prop = of_find_property(np, "compatible",NULL);
if(prop)
{
printk("find compatible ok\n");
printk("compatible value = %s\n", prop->value);
printk("compatible name = %s\n", prop->name);
}else{
printk("find compatible failed\n");
}
if(of_device_is_compatible(np, "farsight,test"))
{//匹配compatible屬性
printk("we have a compatible named farsight,test\n");
}
u32 regdata[U32_DATA_LEN];
int ret;
//讀取到屬性中的整數的陣列
ret = of_property_read_u32_array(np, "reg", regdata, U32_DATA_LEN);
if(!ret)
{
int i;
for(i=0; i<U32_DATA_LEN; i++)
printk("----regdata[%d] = 0x%x\n", i,regdata[i]);
}else{
printk("get reg data failed\n");
}
const char *pstr[3];
int i;
//讀取到屬性中的字串的陣列
for(i=0; i<3; i++)
{
ret = of_property_read_string_index(np, "test_list_string", i, &pstr[i]);
if(!ret)
{
printk("----pstr[%d] = %s\n", i,pstr[i]);
}else{
printk("get pstr data failed\n");
}
}
// 屬性的值為空,實際可以用於設定標誌
if(of_find_property(np, "testprop,mytest", NULL))
{
is_good = 1;
printk("is_good = %d\n", is_good);
}
// 獲取到中斷的號碼
irqno = irq_of_parse_and_map(np, 0);
printk("-----irqno = %d\n", irqno);
//驗證中斷號碼是否有效
ret = request_irq(irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
"key_irq", NULL);
if(ret)
{
printk("request_irq error\n");
return -EBUSY;
}
return 0;
}
static void __exit dt_drv_exit(void)
{
}
irqreturn_t key_irq_handler(int irqno, void *devid)
{
printk("------------------------key pressed \n");
return IRQ_HANDLED;
}
module_init(dt_drv_init);
module_exit(dt_drv_exit);
MODULE_LICENSE("GPL");
4.實驗結果展示