在platform_device部分有簡單說明描述設(shè)備有兩種方法:一種是使用platform_device結(jié)構(gòu)體來指定,;另一種是使用設(shè)備樹來描述,。
本篇筆記我們就來簡單地學(xué)習(xí)一下設(shè)備樹的一些知識。
什么是設(shè)備樹
設(shè)備樹簡單理解就是描述設(shè)備信息(資源)的一棵樹。設(shè)備樹(Device Tree)用代碼體現(xiàn)如下:
這些代碼被保存在.dts/dtsi后綴文件中,,也即設(shè)備樹源文件 DTS(DeviceTree Source),。
這些源文件同我們的C代碼一樣,,并不能直接使用的,,而是得經(jīng)過一個編譯過程生成機(jī)器可運(yùn)行的二進(jìn)制文件,,如:
dts文件使用dtc工具編譯生成dtb文件,,這個dtb文件就是內(nèi)核可以使用的文件。例如我們的板子跑起來之后,,我們系統(tǒng)使用的設(shè)備樹文件就存在目錄/boot下:
Linux為什么會引入設(shè)備樹,?
在上一個實(shí)驗(yàn):【Linux筆記】LED驅(qū)動實(shí)驗(yàn)(總線設(shè)備驅(qū)動模型)中我們使用了platform_device結(jié)構(gòu)體來描述led設(shè)備(硬件資源)。既然已經(jīng)有了描述設(shè)備的方法了,,為什么還要引入設(shè)備樹呢,?
因?yàn)長inux內(nèi)核中有很多BSP(板級支持包),不同的BSP會包含著不同的描述設(shè)備的代碼(.c或.h文件),。
隨著芯片的發(fā)展,,Linux內(nèi)核中就包含著越來越多這些描述設(shè)備的代碼,導(dǎo)致Linux內(nèi)核代碼會很臃腫,。
這導(dǎo)致Linux之父Linus 大發(fā)雷霆:"this whole ARM thing is a f*cking pain in the ass",。
因此引入了設(shè)備樹文件,從而可精簡一些臃腫的C代碼,。除此之外,,.dts編譯生成.dtb文件的過程要比.c編譯生成驅(qū)動模塊、加載驅(qū)動模塊的過程要簡單很多,,也更方便我們進(jìn)行開發(fā),。
設(shè)備樹的語法
設(shè)備樹源文件也是需要根據(jù)一定規(guī)則來編寫的,同C語言一樣,,也要遵循一些語法規(guī)則,。下面簡單看一下設(shè)備樹的源碼結(jié)構(gòu)及語法。
先看一個設(shè)備樹示例:
1,、節(jié)點(diǎn)格式
label: node-name@unit-address
其中:
label:標(biāo)號
node-name:節(jié)點(diǎn)名字
unit-address:單元地址
label 是標(biāo)號,,可以省略。label 的作用是為了方便地引用 node,。比如:
可以使用下面 2 種方法來修改 uart@fe001000 這個 node:
2、屬性格式
簡單地說,, properties 就是“name=value”,, value 有多種取值方式。示例:
一個32位的數(shù)據(jù),,用尖括號包圍起來,,如
interrupts = <17 0xc>;
一個64位數(shù)據(jù)(使用2個32位數(shù)據(jù)表示),用尖括號包圍起來,,如:
clock-frequency = <0x00000001 0x00000000>;
有結(jié)束符的字符串,,用雙引號包圍起來,,如:
compatible = "simple-bus";
字節(jié)序列,用中括號包圍起來,,如:
local-mac-address = [00 00 12 34 56 78]; // 每個byte使用2個16進(jìn)制數(shù)來表示
local-mac-address = [000012345678]; // 每個byte使用2個16進(jìn)制數(shù)來表示
可以是各種值的組合,,用逗號隔開,如:
compatible = "ns16550", "ns8250";
example = <0xf00f0000 19>, "a strange property format";
3,、一些標(biāo)準(zhǔn)屬性
?。?) compatible 屬性
“compatible”表示“兼容”,對于某個LED,,內(nèi)核中可能有A,、B、C三個驅(qū)動都支持它,,那可以這樣寫:
led {
compatible = “A”, “B”, “C”;
};
內(nèi)核啟動時,,就會為這個LED按這樣的優(yōu)先順序?yàn)樗业津?qū)動程序:A、B,、C,。
(2)model 屬性
model屬性與compatible屬性有些類似,但是有差別,。compatible屬性是一個字符串列表,,表示可以你的硬件兼容A、B,、C等驅(qū)動,;model用來準(zhǔn)確地定義這個硬件是什么。
比如根節(jié)點(diǎn)中可以這樣寫:
/ {
compatible = "samsung,smdk2440", "samsung,mini2440";
model = "jz2440_v3";
};
它表示這個單板,,可以兼容內(nèi)核中的“smdk2440”,,也兼容“mini2440”。
從compatible屬性中可以知道它兼容哪些板,,但是它到底是什么板,?用model屬性來明確。
?。?)status 屬性
status 屬性看名字就知道是和設(shè)備狀態(tài)有關(guān)的,, status 屬性值也是字符串,字符串是設(shè)備的狀態(tài)信息,,可選的狀態(tài)如下所示:
(4)#address-cells 和#size-cells 屬性
格式:
address-cells:address要用多少個32位數(shù)來表示,;
size-cells:size要用多少個32位數(shù)來表示。
比如一段內(nèi)存,,怎么描述它的起始地址和大?。?/p>
下例中,,address-cells為1,,所以reg中用1個數(shù)來表示地址,,即用0x80000000來表示地址;size-cells為1,,所以reg中用1個數(shù)來表示大小,,即用0x20000000表示大小:
/ {
# address-cells = <1>;
# size-cells = <1>;
memory {
reg = <0x80000000 0x20000000>;
};
};
?。?)reg 屬性
reg屬性的值,,是一系列的“address size”,用多少個32位的數(shù)來表示address和size,,由其父節(jié)點(diǎn)的# address-cells,、#size-cells決定。示例:
/dts-v1/;
/ {
# address-cells = <1>;
# size-cells = <1>;
memory {
reg = <0x80000000 0x20000000>;
};
};
?。?)name 屬性
過時了,,建議不用。它的值是字符串,,用來表示節(jié)點(diǎn)的名字,。在跟platform_driver匹配時,優(yōu)先級最低,。compatible屬性在匹配過程中,,優(yōu)先級最高。
?。?)device_type 屬性
過時了,,建議不用。它的值是字符串,,用來表示節(jié)點(diǎn)的類型,。在跟platform_driver匹配時,優(yōu)先級為中,。compatible屬性在匹配過程中,,優(yōu)先級最高。
3,、常用的節(jié)點(diǎn)
?。?)根節(jié)點(diǎn)
用 / 標(biāo)識根節(jié)點(diǎn),如:
/dts-v1/;
/ {
model = "SMDK24440";
compatible = "samsung,smdk2440";
# address-cells = <1>;
# size-cells = <1>;
};
(2)CPU節(jié)點(diǎn)
一般不需要我們設(shè)置,,在 dtsi 文件中都定義好了,,如:
cpus {
# address-cells = <1>;
# size-cells = <0>;
cpu0: cpu@0 {
.......
}
};
(3)memory 節(jié)點(diǎn)
芯片廠家不可能事先確定你的板子使用多大的內(nèi)存,所以 memory 節(jié)點(diǎn)需要板廠設(shè)置,,比如:
memory {
reg = <0x80000000 0x20000000>;
};
(4)chosen 節(jié)點(diǎn)
我們可以通過設(shè)備樹文件給內(nèi)核傳入一些參數(shù),這要在chosen節(jié)點(diǎn)中設(shè)置bootargs屬性:
chosen {
bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
};
操作設(shè)備樹的函數(shù)
Linux 內(nèi)核給我們提供了一系列的函數(shù)來獲取設(shè)備樹中的節(jié)點(diǎn)或者屬性信息,,這一系列的函數(shù)都有一個統(tǒng)一的前綴“of_”(“open firmware”即開放固件,。),,所以在很多資料里面也被叫做 OF 函數(shù)。
1,、節(jié)點(diǎn)相關(guān)操作函數(shù)
Linux 內(nèi)核使用 device_node 結(jié)構(gòu)體來描述一個節(jié)點(diǎn),,此結(jié)構(gòu)體定義在文件 include/linux/of.h 中,定義如下:
與查找節(jié)點(diǎn)有關(guān)的 OF 函數(shù)有 5 個:
(1) of_find_node_by_name 函數(shù)
of_find_node_by_name 函數(shù)通過節(jié)點(diǎn)名字查找指定的節(jié)點(diǎn),,函數(shù)原型如下:
struct device_node *of_find_node_by_name(struct device_node *from,
const char *name);
(2) of_find_node_by_type 函數(shù)
of_find_node_by_type 函數(shù)通過 device_type 屬性查找指定的節(jié)點(diǎn),,函數(shù)原型如下:
struct device_node *of_find_node_by_type(struct device_node *from, const char *type);
(3) of_find_compatible_node 函數(shù)
of_find_compatible_node 函數(shù)根據(jù) device_type 和 compatible 這兩個屬性查找指定的節(jié)點(diǎn),函數(shù)原型如下:
struct device_node *of_find_compatible_node(struct device_node *from,const char *type,
const char *compatible);
(4)of_find_matching_node_and_match 函數(shù)
of_find_matching_node_and_match 函數(shù)通過 of_device_id 匹配表來查找指定的節(jié)點(diǎn),,函數(shù)原型如下:
struct device_node *of_find_matching_node_and_match(struct device_node *from,const struct of_device_id *matches,const struct of_device_id **match);
?。?)of_find_node_by_path 函數(shù)
of_find_node_by_path 函數(shù)通過路徑來查找指定的節(jié)點(diǎn),函數(shù)原型如下:
inline struct device_node *of_find_node_by_path(const char *path);
2,、提取屬性值的 OF 函數(shù)
Linux 內(nèi)核中使用結(jié)構(gòu)體 property 表示屬性,,此結(jié)構(gòu)體同樣定義在文件 include/linux/of.h 中,內(nèi)容如下:
Linux 內(nèi)核也提供了提取屬性值的 OF 函數(shù) :
(1) of_find_property 函數(shù)
of_find_property 函數(shù)用于查找指定的屬性,,函數(shù)原型如下:
property *of_find_property(const struct device_node *np,const char *name,int *lenp);
?。?)of_property_count_elems_of_size 函數(shù)
of_property_count_elems_of_size 函數(shù)用于獲取屬性中元素的數(shù)量,比如 reg 屬性值是一個數(shù)組,,那么使用此函數(shù)可以獲取到這個數(shù)組的大小,,此函數(shù)原型如下:
int of_property_count_elems_of_size(const struct device_node *np,const char *propname,int elem_size);
(3)讀取 u8,、 u16,、 u32 和 u64 類型的數(shù)組數(shù)據(jù)
(4)讀取 u8,、 u16,、 u32 和 u64 類型屬性值
(5)of_property_read_string 函數(shù)
of_property_read_string 函數(shù)用于讀取屬性中字符串值,函數(shù)原型如下:
int of_property_read_string(struct device_node *np,const char *propname,const char **out_string)
更多信息可以來這里獲取==>>電子技術(shù)應(yīng)用-AET<<
電子技術(shù)應(yīng)用專欄作家 一口Linux
原文鏈接:https://mp.weixin.qq.com/s/BI23d71SJkYPHnzQbFyvpQ