Linux内核对设备树的处理
文章目錄
- 1 內(nèi)核對(duì)設(shè)備樹(shù)的處理
- 1.1 dtb 中每一個(gè)節(jié)點(diǎn)都被轉(zhuǎn)換為 device_node 結(jié)構(gòu)體
- 1.2 哪些設(shè)備樹(shù)節(jié)點(diǎn)會(huì)被轉(zhuǎn)換為 platform_device
- 1.3 怎么轉(zhuǎn)換為 platform_device
- 2 platform_device 如何與platform_driver 配對(duì)
- 3 內(nèi)核里操作設(shè)備樹(shù)的常用函數(shù)
- 3.1 內(nèi)核中設(shè)備樹(shù)相關(guān)的頭文件介紹
- 3.1.1 處理 DTB
- 3.1.2 處理 device_node
- 3.1.3 處理 platform_device
- 3.2 platform_device 相關(guān)的函數(shù)
- 3.2.1 of_find_device_by_node
- 3.2.2 platform_get_resource
- 3.3 有些節(jié)點(diǎn)不會(huì)生成 platform_device,怎么訪問(wèn)它們
- 3.3.1 找到節(jié)點(diǎn)
- 3.3.2 找到屬性
- 3.3.3 獲取屬性的值
- 4 怎么修改設(shè)備樹(shù)文件
1 內(nèi)核對(duì)設(shè)備樹(shù)的處理
從源代碼文件 dts 文件開(kāi)始,設(shè)備樹(shù)的處理過(guò)程為:
① dts 在 PC 機(jī)上被編譯為 dtb 文件;
② u-boot 把 dtb 文件傳給內(nèi)核;
③ 內(nèi)核解析 dtb 文件,把每一個(gè)節(jié)點(diǎn)都轉(zhuǎn)換為 device_node 結(jié)構(gòu)體;
④ 對(duì)于某些 device_node 結(jié)構(gòu)體,會(huì)被轉(zhuǎn)換為 platform_device 結(jié)構(gòu)體。
1.1 dtb 中每一個(gè)節(jié)點(diǎn)都被轉(zhuǎn)換為 device_node 結(jié)構(gòu)體
根節(jié)點(diǎn)被保存在全局變量 of_root 中,從 of_root 開(kāi)始可以訪問(wèn)到任意節(jié)點(diǎn)。
1.2 哪些設(shè)備樹(shù)節(jié)點(diǎn)會(huì)被轉(zhuǎn)換為 platform_device
A. 根節(jié)點(diǎn)下含有 compatile 屬性的子節(jié)點(diǎn)。
B. 含有特定 compatile 屬性的節(jié)點(diǎn)的子節(jié)點(diǎn):
如 果 一 個(gè) 節(jié) 點(diǎn) 的 compatile 屬 性 , 它 的 值 是 這 4 者之一: “simple-bus”,“simplemfd”,“isa”,“arm,amba-bus”, 那么它的子結(jié)點(diǎn)(需含 compatile 屬性)也可以轉(zhuǎn)換為platform_device。
C. 總線 I2C、SPI 節(jié)點(diǎn)下的子節(jié)點(diǎn):不轉(zhuǎn)換為 platform_device某個(gè)總線下到子節(jié)點(diǎn),應(yīng)該交給對(duì)應(yīng)的總線驅(qū)動(dòng)程序來(lái)處理, 它們不應(yīng)該被轉(zhuǎn)換為 platform_device。
對(duì)于如下設(shè)備樹(shù)文件:
/ {mytest {compatile = "mytest", "simple-bus";mytest@0 {compatile = "mytest_0";};};i2c {compatile = "samsung,i2c";at24c02 {compatile = "at24c02"; };};spi {compatile = "samsung,spi"; flash@0 {compatible = "winbond,w25q32dw";spi-max-frequency = <25000000>;reg = <0>;};};};比如以下的節(jié)點(diǎn)中:
- /mytest 會(huì)被轉(zhuǎn)換為 platform_device, 因?yàn)樗嫒?#34;simple-bus";它的子節(jié)點(diǎn)/mytest/mytest@0 也會(huì)被轉(zhuǎn)換為 platform_device
- /i2c 節(jié)點(diǎn)一般表示 i2c 控制器, 它會(huì)被轉(zhuǎn)換為 platform_device, 在內(nèi)核中有對(duì)應(yīng)的 platform_driver;
- /i2c/at24c02 節(jié)點(diǎn)不會(huì)被轉(zhuǎn)換為 platform_device, 它被如何處理完全由父節(jié)點(diǎn)的 platform_driver決定, 一般是被創(chuàng)建為一個(gè) i2c_client。
- 類(lèi)似的也有/spi 節(jié)點(diǎn), 它一般也是用來(lái)表示 SPI 控制器, 它會(huì)被轉(zhuǎn)換為 platform_device, 在內(nèi)核中
有對(duì)應(yīng)的 platform_driver; - /spi/flash@0 節(jié)點(diǎn)不會(huì)被轉(zhuǎn)換為 platform_device, 它被如何處理完全由父節(jié)點(diǎn)的 platform_driver決定, 一般是被創(chuàng)建為一個(gè) spi_device。
1.3 怎么轉(zhuǎn)換為 platform_device
內(nèi)核處理設(shè)備樹(shù)的函數(shù)調(diào)用過(guò)程,這里不去分析;我們只需要得到如下結(jié)論:
A. platform_device 中含有 resource 數(shù)組, 它來(lái)自 device_node 的 reg, interrupts 屬性;
B. platform_device.dev.of_node 指向 device_node, 可以通過(guò)它獲得其他屬性。
2 platform_device 如何與platform_driver 配對(duì)
從設(shè)備樹(shù)轉(zhuǎn)換得來(lái)的 platform_device 會(huì)被注冊(cè)進(jìn)內(nèi)核里,以后當(dāng)我們每注冊(cè)一個(gè) platform_driver時(shí),它們就會(huì)兩兩確定能否配對(duì),如果能配對(duì)成功就調(diào)用 platform_driver 的 probe 函數(shù)。套路是一樣的。我們需要將前面講過(guò)的“匹配規(guī)則”再完善一下:
先貼源碼:
1. 最先比較:是否強(qiáng)制選擇某個(gè) driver
比較 platform_device. driver_override 和platform_driver.driver.name,可以設(shè)置 platform_device 的 driver_override,強(qiáng)制選擇某個(gè) platform_driver。
2.然后比較:設(shè)備樹(shù)信息
比較:platform_device. dev.of_node 和 platform_driver.driver.of_match_table。
由設(shè)備樹(shù)節(jié)點(diǎn)轉(zhuǎn)換得來(lái)的 platform_device 中,含有一個(gè)結(jié)構(gòu)體:of_node。
它的類(lèi)型如下:
如果一個(gè) platform_driver 支持設(shè)備樹(shù),它的platform_driver.driver.of_match_table 是一個(gè)數(shù)組,類(lèi)型如下:
使用設(shè)備樹(shù)信息來(lái)判斷 dev 和 drv 是否配對(duì)時(shí),首先,如果 of_match_table 中含有 compatible 值,就跟 dev 的 compatile 屬性比較,若一致則成功,否則返回失敗;
其次,如果 of_match_table 中含有 type 值,就跟 dev 的 device_type 屬性比較,若一致則成功,否則返回失敗;
最后,如果 of_match_table 中含有 name 值,就跟 dev 的 name 屬性比較,若一致則成功,否則返回失敗。
而設(shè)備樹(shù)中建議不再使用 devcie_type 和 name 屬性,所以基本上只使用設(shè)備節(jié)點(diǎn)的 compatible 屬性來(lái)尋找匹配的 platform_driver。
接下來(lái)比較:platform_device_id
比較 platform_device. name 和 platform_driver.id_table[i].name,id_table 中可能有多項(xiàng)。
platform_driver.id_table 是“platform_device_id”指針,表示該 drv 支持若干個(gè) device,它里面列出了各個(gè) device 的{.name, .driver_data},其中的“name”表示該 drv 支持的設(shè)備的名字,driver_data是些提供給該 device 的私有數(shù)據(jù)。
最后比較:platform_device.name 和 platform_driver.driver.name:
platform_driver.id_table 可能為空,這時(shí)可以根據(jù) platform_driver.driver.name 來(lái)尋找同名的 platform_device。
一個(gè)圖概括所有的配對(duì)過(guò)程:
概括出了這個(gè)圖:
沒(méi)有轉(zhuǎn)換為 platform_device 的節(jié)點(diǎn),如何使用?
任意驅(qū)動(dòng)程序里,都可以直接訪問(wèn)設(shè)備樹(shù)。
3 內(nèi)核里操作設(shè)備樹(shù)的常用函數(shù)
內(nèi)核源碼中 include/linux/目錄下有很多 of 開(kāi)頭的頭文件,of 表示“open firmware”即開(kāi)放固件。
3.1 內(nèi)核中設(shè)備樹(shù)相關(guān)的頭文件介紹
設(shè)備樹(shù)的處理過(guò)程是:dtb -> device_node -> platform_device。
3.1.1 處理 DTB
of_fdt.h // dtb 文件的相關(guān)操作函數(shù), 我們一般用不到, // 因?yàn)?dtb 文件在內(nèi)核中已經(jīng)被轉(zhuǎn)換為 device_node 樹(shù)(它更易于使用)3.1.2 處理 device_node
of.h // 提供設(shè)備樹(shù)的一般處理函數(shù), // 比如 of_property_read_u32(讀取某個(gè)屬性的 u32 值), // of_get_child_count(獲取某個(gè) device_node 的子節(jié)點(diǎn)數(shù)) of_address.h // 地址相關(guān)的函數(shù), // 比如 of_get_address(獲得 reg 屬性中的 addr, size 值) // of_match_device (從 matches 數(shù)組中取出與當(dāng)前設(shè)備最匹配的一項(xiàng)) of_dma.h // 設(shè)備樹(shù)中 DMA 相關(guān)屬性的函數(shù) of_gpio.h // GPIO 相關(guān)的函數(shù) of_graph.h // GPU 相關(guān)驅(qū)動(dòng)中用到的函數(shù), 從設(shè)備樹(shù)中獲得 GPU 信息 of_iommu.h // 很少用到 of_irq.h // 中斷相關(guān)的函數(shù) of_mdio.h // MDIO (Ethernet PHY) API of_net.h // OF helpers for network devices. of_pci.h // PCI 相關(guān)函數(shù) of_pdt.h // 很少用到 of_reserved_mem.h // reserved_mem 的相關(guān)函數(shù)3.1.3 處理 platform_device
of_platform.h // 把 device_node 轉(zhuǎn)換為 platform_device 時(shí)用到的函數(shù), // 比如 of_device_alloc(根據(jù) device_node 分配設(shè)置 platform_device), // of_find_device_by_node (根據(jù) device_node 查找到 platform_device),// of_platform_bus_probe (處理 device_node 及它的子節(jié)點(diǎn)) of_device.h // 設(shè)備相關(guān)的函數(shù), 比如 of_match_device3.2 platform_device 相關(guān)的函數(shù)
of_platform.h 中聲明了很多函數(shù),但是作為驅(qū)動(dòng)開(kāi)發(fā)者,我們只使用其中的 1、2 個(gè)。其他的都是給內(nèi)核自己使用的,內(nèi)核使用它們來(lái)處理設(shè)備樹(shù),轉(zhuǎn)換得到 platform_device。
3.2.1 of_find_device_by_node
函數(shù)原型為:
extern struct platform_device *of_find_device_by_node(struct device_node *np);設(shè)備樹(shù)中的每一個(gè)節(jié)點(diǎn),在內(nèi)核里都有一個(gè) device_node;你可以使用 device_node 去找到對(duì)應(yīng)的platform_device。
3.2.2 platform_get_resource
這個(gè)函數(shù)跟設(shè)備樹(shù)沒(méi)什么關(guān)系,但是設(shè)備樹(shù)中的節(jié)點(diǎn)被轉(zhuǎn)換為 platform_device 后,設(shè)備樹(shù)中的 reg 屬性、interrupts 屬性也會(huì)被轉(zhuǎn)換為“resource”。
這時(shí),你可以使用這個(gè)函數(shù)取出這些資源。
函數(shù)原型為:
/** * platform_get_resource - get a resource for a device * @dev: platform device * @type: resource type // 取哪類(lèi)資源?IORESOURCE_MEM、IORESOURCE_REG * // IORESOURCE_IRQ 等 * @num: resource index // 這類(lèi)資源中的哪一個(gè)? */ struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num);對(duì)于設(shè)備樹(shù)節(jié)點(diǎn)中的 reg 屬性,它對(duì)應(yīng) IORESOURCE_MEM 類(lèi)型的資源;
對(duì)于設(shè)備樹(shù)節(jié)點(diǎn)中的 interrupts 屬性,它對(duì)應(yīng)IORESOURCE_IRQ 類(lèi)型的資源。
3.3 有些節(jié)點(diǎn)不會(huì)生成 platform_device,怎么訪問(wèn)它們
內(nèi)核會(huì)把 dtb 文件解析出一系列的 device_node 結(jié)構(gòu)體,我們可以直接訪問(wèn)這些 device_node。
內(nèi)核源碼 incldue/linux/of.h 中聲明了 device_node 和屬性 property 的操作函數(shù),device_node 和property 的結(jié)構(gòu)體定義如下:
3.3.1 找到節(jié)點(diǎn)
a. of_find_node_by_path
根據(jù)路徑找到節(jié)點(diǎn),比如“/”就對(duì)應(yīng)根節(jié)點(diǎn),“/memory”對(duì)應(yīng) memory 節(jié)點(diǎn)。
函數(shù)原型:
b. of_find_node_by_name
根據(jù)名字找到節(jié)點(diǎn),節(jié)點(diǎn)如果定義了 name 屬性,那我們可以根據(jù)名字找到它。
函數(shù)原型:
參數(shù) from 表示從哪一個(gè)節(jié)點(diǎn)開(kāi)始尋找,傳入 NULL 表示從根節(jié)點(diǎn)開(kāi)始尋找。
但是在設(shè)備樹(shù)的官方規(guī)范中不建議使用“name”屬性,所以這函數(shù)也不建議使用。
c. of_find_node_by_type
根據(jù)類(lèi)型找到節(jié)點(diǎn),節(jié)點(diǎn)如果定義了 device_type 屬性,那我們可以根據(jù)類(lèi)型找到它。
函數(shù)原型:
參數(shù) from 表示從哪一個(gè)節(jié)點(diǎn)開(kāi)始尋找,傳入 NULL 表示從根節(jié)點(diǎn)開(kāi)始尋找。
但是在設(shè)備樹(shù)的官方規(guī)范中不建議使用“device_type”屬性,所以這函數(shù)也不建議使用。
d. of_find_compatible_node
根據(jù) compatible 找到節(jié)點(diǎn),節(jié)點(diǎn)如果定義了 compatible 屬性,那我們可以根據(jù) compatible 屬性找到它。
函數(shù)原型:
參數(shù) from 表示從哪一個(gè)節(jié)點(diǎn)開(kāi)始尋找,傳入 NULL 表示從根節(jié)點(diǎn)開(kāi)始尋找。
參數(shù) compat 是一個(gè)字符串,用來(lái)指定 compatible 屬性的值;
參數(shù) type 是一個(gè)字符串,用來(lái)指定 device_type 屬性的值,可以傳入 NULL。
e. of_find_node_by_phandle
根據(jù) phandle 找到節(jié)點(diǎn)。
dts 文件被編譯為 dtb 文件時(shí),每一個(gè)節(jié)點(diǎn)都有一個(gè)數(shù)字 ID,這些數(shù)字 ID 彼此不同。可以使用數(shù)字 ID來(lái)找到 device_node。這些數(shù)字 ID 就是 phandle。
函數(shù)原型:
f. of_get_parent
找到 device_node 的父節(jié)點(diǎn)。
函數(shù)原型:
g. of_get_next_parent
這個(gè)函數(shù)名比較奇怪,怎么可能有“next parent”?
它實(shí)際上也是找到 device_node 的父節(jié)點(diǎn),跟 of_get_parent 的返回結(jié)果是一樣的。
差別在于它多調(diào)用下列函數(shù),把 node 節(jié)點(diǎn)的引用計(jì)數(shù)減少了 1。這意味著調(diào)用 of_get_next_parent 之
后,你不再需要調(diào)用 of_node_put 釋放 node 節(jié)點(diǎn)。
of_node_put(node);
函數(shù)原型:
h. of_get_next_child
取出下一個(gè)子節(jié)點(diǎn)。
函數(shù)原型:
參數(shù) node 表示父節(jié)點(diǎn);
prev 表示上一個(gè)子節(jié)點(diǎn),設(shè)為 NULL 時(shí)表示想找到第 1 個(gè)子節(jié)點(diǎn)。
不斷調(diào)用 of_get_next_child 時(shí),不斷更新 pre 參數(shù),就可以得到所有的子節(jié)點(diǎn)。
i. of_get_next_available_child
取出下一個(gè)“可用”的子節(jié)點(diǎn),有些節(jié)點(diǎn)的 status 是“disabled”,那就會(huì)跳過(guò)這些節(jié)點(diǎn)。
函數(shù)原型:
參數(shù) node 表示父節(jié)點(diǎn);
prev 表示上一個(gè)子節(jié)點(diǎn),設(shè)為 NULL 時(shí)表示想找到第 1 個(gè)子節(jié)點(diǎn)。
j. of_get_child_by_name
根據(jù)名字取出子節(jié)點(diǎn)。
函數(shù)原型:
參數(shù) node 表示父節(jié)點(diǎn);
name 表示子節(jié)點(diǎn)的名字。
3.3.2 找到屬性
內(nèi)核源碼 incldue/linux/of.h 中聲明了 device_node 的操作函數(shù),當(dāng)然也包括屬性的操作函數(shù)。
a. of_find_property
找到節(jié)點(diǎn)中的屬性。
函數(shù)原型:
參數(shù) np 表示節(jié)點(diǎn),我們要在這個(gè)節(jié)點(diǎn)中找到名為 name 的屬性。
lenp 用來(lái)保存這個(gè)屬性的長(zhǎng)度,即它的值的長(zhǎng)度。
在設(shè)備樹(shù)中,節(jié)點(diǎn)大概是這樣:
xxx_node {
xxx_pp_name = “hello”;
};
上述節(jié)點(diǎn)中,“xxx_pp_name”就是屬性的名字,值的長(zhǎng)度是 6。
3.3.3 獲取屬性的值
a. of_get_property
根據(jù)名字找到節(jié)點(diǎn)的屬性,并且返回它的值。
函數(shù)原型:
參數(shù) np 表示節(jié)點(diǎn),我們要在這個(gè)節(jié)點(diǎn)中找到名為 name 的屬性,然后返回它的值。
lenp 用來(lái)保存這個(gè)屬性的長(zhǎng)度,即它的值的長(zhǎng)度。
b. of_property_count_elems_of_size
根據(jù)名字找到節(jié)點(diǎn)的屬性,確定它的值有多少個(gè)元素(elem)。
函數(shù)原型:
參數(shù) np 表示節(jié)點(diǎn),我們要在這個(gè)節(jié)點(diǎn)中找到名為 propname 的屬性,然后返回下列結(jié)果:
return prop->length / elem_size;在設(shè)備樹(shù)中,節(jié)點(diǎn)大概是這樣:
xxx_node {xxx_pp_name = <0x50000000 1024> <0x60000000 2048>; };調(diào)用 of_property_count_elems_of_size(np, “xxx_pp_name”, 8)時(shí),返回值是 2;
調(diào)用 of_property_count_elems_of_size(np, “xxx_pp_name”, 4)時(shí),返回值是 4。
c. 讀整數(shù) u32/u64
函數(shù)原型為:
在設(shè)備樹(shù)中,節(jié)點(diǎn)大概是這樣:
xxx_node {name1 = <0x50000000>;name2 = <0x50000000 0x60000000>; };調(diào)用 of_property_read_u32 (np, “name1”, &val)時(shí),val 將得到值 0x50000000;
調(diào)用 of_property_read_u64 (np, “name2”, &val)時(shí),val 將得到值 0x0x6000000050000000。
d. 讀某個(gè)整數(shù) u32/u64
函數(shù)原型為:
在設(shè)備樹(shù)中,節(jié)點(diǎn)大概是這樣:
xxx_node {name2 = <0x50000000 0x60000000>; };調(diào)用 of_property_read_u32 (np, “name2”, 1, &val)時(shí),val 將得到值 0x0x60000000。
e. 讀數(shù)組
函數(shù)原型為:
在設(shè)備樹(shù)中,節(jié)點(diǎn)大概是這樣:
xxx_node {name2 = <0x50000012 0x60000034>; };上述例子中屬性 name2 的值,長(zhǎng)度為 8。
調(diào)用 of_property_read_variable_u8_array (np, “name2”, out_values, 1, 10)時(shí),out_values 中將會(huì)保存這 8 個(gè)字節(jié): 0x12,0x00,0x00,0x50,0x34,0x00,0x00,0x60。
調(diào)用 of_property_read_variable_u16_array (np, “name2”, out_values, 1, 10)時(shí),out_values中將會(huì)保存這 4 個(gè) 16 位數(shù)值: 0x0012, 0x5000,0x0034,0x6000。
總之,這些函數(shù)要么能取到全部的數(shù)值,要么一個(gè)數(shù)值都取不到;如果值的長(zhǎng)度在 sz_min 和 sz_max 之間,就返回全部的數(shù)值;否則一個(gè)數(shù)值都不返回。
f. 讀字符串
函數(shù)原型為:
返回節(jié)點(diǎn) np 的屬性(名為 propname)的值,(*out_string)指向這個(gè)值,把它當(dāng)作字符串。
4 怎么修改設(shè)備樹(shù)文件
一個(gè)寫(xiě)得好的驅(qū)動(dòng)程序, 它會(huì)盡量確定所用資源。
只把不能確定的資源留給設(shè)備樹(shù), 讓設(shè)備樹(shù)來(lái)指定。
據(jù)原理圖確定"驅(qū)動(dòng)程序無(wú)法確定的硬件資源", 再在設(shè)備樹(shù)文件中填寫(xiě)對(duì)應(yīng)內(nèi)容。那么, 所填寫(xiě)內(nèi)容的格式是什么?
有些芯片,廠家提供了對(duì)應(yīng)的設(shè)備樹(shù)生成工具,可以選擇某個(gè)引腳用于某些功能,就可以自動(dòng)生成設(shè)備樹(shù)節(jié)點(diǎn)。你再把這些節(jié)點(diǎn)復(fù)制到內(nèi)核的設(shè)備樹(shù)文件里即可。
做得好的廠家也會(huì)提供設(shè)備樹(shù)的說(shuō)明文檔。
總結(jié)
以上是生活随笔為你收集整理的Linux内核对设备树的处理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 海尔bios怎么设置u盘启动不了系统 海
- 下一篇: 反编译linux内核_Linux 后台开