Linux 設備驅動開發詳解:基于最新的Linux 4.0內核, 機械工業出版社, 宋寶華, 2015
1. 設備樹的起源
? 背景: ARM架構中大量板級代碼冗余,硬編碼在mach-xxx
目錄,設備樹(Device Tree)引入結構化描述硬件。
? 目的: 減少內核冗余代碼,通過設備樹文件(.dts)傳遞硬件信息,由Bootloader加載至內核。
? 核心思想: 設備樹以節點和屬性形式描述硬件拓撲,如CPU、內存、外設、中斷等。
2. 設備樹的組成與結構
2.1 核心文件
? DTS (Device Tree Source): 文本格式描述硬件,支持.dtsi
包含(類似C頭文件)。
? DTC (Device Tree Compiler): 編譯工具,將DTS轉換為DTB。
? DTB (Device Tree Blob): 二進制文件,由內核解析。
? 綁定文檔(Binding): 說明節點屬性規范(如Documentation/devicetree/bindings
)。
2.2 設備樹結構
? 節點(Node): 表示設備或總線,如根節點/
,子節點cpu@0
。
? 屬性(Property): 鍵值對描述硬件特性,如reg = <地址 長度>
、interrupts
。
? 標簽(Label)與Phandle: 通過&label
引用節點,如&gpio0
表示GPIO控制器。
2.3 設備樹示例
/ {compatible = "acme,coyotes-revenge"; // 根節點兼容性#address-cells = <1>; // 地址占1個cell#size-cells = <1>; // 長度占1個cellcpus {cpu@0 { compatible = "arm,cortex-a9"; reg = <0>; };cpu@1 { compatible = "arm,cortex-a9"; reg = <1>; };};serial@101f0000 {compatible = "arm,pl011";reg = <0x101f0000 0x1000>; // 寄存器地址和長度interrupts = <1 0>; // 中斷號及觸發方式};
};
3. 關鍵概念解析
3.1 兼容性(Compatible)
? 根節點兼容性: 匹配機器類型,如vexpress-v2p-ca9
。
// 內核中匹配設備
if (of_machine_is_compatible("arm,vexpress")) { ... }
? 設備節點兼容性: 驅動匹配依據,如compatible = "arm,pl011"
。
3.2 地址編碼
? reg屬性: 格式reg = <地址1 長度1 地址2 長度2 ...>
。
? #address-cells和#size-cells: 定義子節點地址/長度的cell數量。
external-bus {#address-cells = <2>; // 地址占2個cell(片選+偏移)#size-cells = <1>; // 長度占1個cellethernet@0,0 { reg = <0 0 0x1000>; };
};
3.3 中斷連接
? 中斷控制器: 聲明interrupt-controller
和#interrupt-cells
。
? 中斷屬性: 使用interrupt-parent
和interrupts
指定中斷號和觸發方式。
intc: interrupt-controller@10140000 {compatible = "arm,pl190";#interrupt-cells = <2>; // 2個cell(中斷號+標志)
};
serial@101f0000 {interrupts = <1 0>; // 中斷號1,觸發方式0
};
3.4 GPIO、時鐘、Pinmux
? GPIO控制器: 聲明gpio-controller
和#gpio-cells
。
? GPIO使用: 通過gpios
屬性引用控制器。
gpio@101f3000 {gpio-controller;#gpio-cells = <2>; // GPIO號+極性
};
button {gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
};
4. BSP和驅動的變更
4.1 平臺設備的替代
? 舊方式: 手動注冊platform_device
,硬編碼資源。
? 新方式: 設備樹自動展開platform_device
,資源來自.dts
。
4.2 驅動匹配機制
? OF匹配表: 驅動通過.of_match_table
匹配設備節點。
static const struct of_device_id my_drv_of_match[] = {{ .compatible = "vendor,device" },{},
};
MODULE_DEVICE_TABLE(of, my_drv_of_match);
4.3 平臺數據的屬性化
? 舊方式: 通過platform_data
結構傳遞數據。
? 新方式: 從設備樹屬性讀取,如of_property_read_u32()
。
// 讀取屬性值示例
of_property_read_u32(np, "clock-frequency", &clk_freq);
4.4 實例:GPIO按鍵驅動
gpio-keys {compatible = "gpio-keys";button {label = "Up";gpios = <&gpio0 1 0>;linux,code = <KEY_UP>;};
};
驅動通過OF API解析屬性:
of_get_gpio_flags(pp, 0, &flags); // 獲取GPIO號和極性
of_property_read_u32(pp, "linux-code", &key_code); // 讀取鍵值
5. 常用OF API
? 節點操作:
? of_find_compatible_node()
: 查找兼容節點。
? of_get_child_count()
: 獲取子節點數量。
? 屬性讀取:
? of_property_read_u32_array()
: 讀取32位數組。
? of_property_read_string()
: 讀取字符串。
? 資源解析:
? of_get_named_gpio()
: 獲取GPIO號。
? of_irq_get()
: 獲取中斷號。
6. 總結
? 設備樹優勢:解耦硬件描述與內核代碼,提升可維護性。
? 核心元素:節點、屬性、兼容性、地址編碼、中斷連接。
? 驅動適配:通過OF匹配表和API解析設備樹數據。
通過設備樹,ARM Linux實現了硬件描述的標準化,降低了BSP開發復雜度,成為嵌入式開發的必備知識。