【嵌入式Linux驅動開發】七、驅動開發的利器 - 設備樹的閃亮登場

  受身無間者永遠不死,壽長乃無間地獄中之大劫。
                      ----電影《無間道》經典臺詞


一、設備樹引入與作用

  上一節在介紹總線設備驅動模型時,提到platform平臺修改引腳時,設備端的代碼需要重新編譯生成platform_device結構體,同時過多的設備對應的.c文件也會造成過多的冗餘代碼,致使以優雅冠稱的Linux臃腫不堪,Linus祖師爺自然要發火,於是就有了設備樹。

  設備樹和總線驅動模型Platform是類似的,都是構造platform_device,不涉及驅動程序核心。而設備樹是如何做到優雅的呢?看下圖左側部分,使用配置文件讓系統生成Platform_device!

在這裏插入圖片描述

  需要牢記的一點是:設備樹不可能用來寫驅動。
  設備樹只是用來給內核裏的驅動程序, 指定硬件的信息。比如 LED 驅動,在內核的驅動程序裏去操作寄存器,但是操作哪一個引腳?這由設備樹指定。
  一個單板啓動時, u-boot 先運行,它的作用是啓動內核。 U-boot 會把內核和設備樹文件都讀入內存,然後啓動內核。在啓動內核時會把設備樹在內存中的地址告訴內核。

二、設備樹的語法

2.1 怎麼描述這棵樹?

  既然稱之爲設備樹,得名原因自然像一個樹的形狀咯,如下圖。

在這裏插入圖片描述
  描述設備樹的文件叫做 DTS(Device Tree Source),它需要編譯爲 dtb(device tree blob)文件供內核使用。所以這裏我們要談的設備樹語法,當然就是DTS語法了!還需要知道的是.dtsi文件,它是芯片的公共設備樹文件(i是include),我們寫的.dts文件會追加到了.dtsi文件中。

我們先來直觀感受一個設備樹實例。

  • 樹狀表達
    在這裏插入圖片描述
  • 代碼表達
    在這裏插入圖片描述

2.2 DTS文件的格式

  • 文件佈局
/dts-v1/; // 表示版本
[memory reservations] // 格式爲: /memreserve/ <address> <length>;
/ {
	[property definitions]
	[child nodes]
};
  • node 的格式
    • 設備樹中的基本單元,被稱爲“node”。
    • label 是標號,可以省略, label 的作用是爲了方便地引用 node。
[label:] node-name[@unit-address] {
	[properties definitions]
	[child nodes]
};

舉例:

/dts-v1/;
/ {
	uart0: uart@fe001000 {
		compatible="ns16550";
		reg=<0xfe001000 0x100>;
	};
};
  • 引用node的兩種方法
// ①、在根節點之外使用 label 引用 node:
&uart0 {
status = “disabled”;
};


// ②、在根節點之外使用全路徑:
&{/uart@fe001000} {
status = “disabled”;
};

2.3 dtsi文件和頭文件

  dts 中可以包含.h 頭文件,也可以包含 dtsi 文件,在.h 頭文件中可以定義一些宏。dts 文件之所以支持“#include”語法,是因爲 arm-linux-gnueabihf-gcc 幫忙。如果只用 dtc(類比gcc) 工具,它是不支持”#include”語法的,只支持“/include”語法。

/dts-v1/;
#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"
/ {
	……
};

2.4 常用的屬性(properties)

  • compatible
    • 根節點下的 compatible 屬性
      • 用來選擇哪一個“machine desc”: 一個內核可以支持machine A,也支持 machine B,內核啓動後會根據根節點的 compatible 屬性找到對應的machine desc (機器描述)結構體,執行其中的初始化函數。
      • compatible 的值,建議取這樣的形式: “manufacturer,model”,即“廠家名,模塊名”。
    • 設備節點下的compatible 屬性
      • 用來表示兼容的驅動。比如對於某個 LED,內核中可能有 A、 B、 C 三個驅動都支持它,那可以這樣寫。內核啓動時,就會爲這個 LED 按這樣的優先順序爲它找到驅動程序: A、 B、 C。
led {
	compatible = “A”, “B”, “C”;
};
  • model

    • model 用來準確地定義這個硬件是什麼
    • 比如一個單板的設備文件的根節點下compatible屬性,可以兼容內核中的“smdk2440”,也兼容“mini2440”。但是它到底是什麼板?用 model 屬性來明確。
    • 比如有 2 款板子配置基本一致, 它們的 compatible 是一樣的,那麼就通過 model 來分辨這 2 款板子
  • status

    • 最後追加的值會覆蓋前面的值,即只有最後一次賦的值纔有效!
    • dtsi 文件中定義了很多設備,但是在你的板子上某些設備是沒有的。這時你可以給這個設備節點添加一個 status 屬性,設置爲“disabled”。
    • 通常使用的屬性值:“okay”和“disabled”

示例

//address-cells 爲 1,所以 reg 中用 1 個數來表示地址,即用 0x80000000 來表示地址
//size-cells 爲 1,所以 reg 中用 1 個數來表示大小,即用 0x20000000 表示大小
/ {
	#address-cells = <1>;
	#size-cells = <1>;
	memory {
		reg = <0x80000000 0x20000000>;
	};
};
  • #address-cells、 #size-cells

    • cell 指一個 32 位的數值
    • address-cells: address 要用多少個 32 位數來表示;size-cells: size 要用多少個 32 位數來表示。
  • reg

    • 本意是 register,用來表示寄存器地址。但在設備樹裏,它可以用來描述一段空間。
    • reg 屬性的值,是一系列的“address size”,用多少個 32 位的數來表示 address 和 size,由其父節點的#address-cells、 #size-cells 決定。

示例:

/dts-v1/;
/ {
	#address-cells = <1>;
	#size-cells = <1>;
	memory {
		reg = <0x80000000 0x20000000>;
	};
};

對於 ARM 系統,寄存器和內存是統一編址的,即訪問寄存器時用某塊地址,訪問內存時用某塊地址,在訪問方法上沒有區別。

  • name(已過時)

    • 它的值是字符串,用來表示節點的名字。在跟 platform_driver 匹配時,優先級最低。compatible 屬性在匹配過程中,優先級最高。
  • device_type(已過時)

    • 它的值是字符串,用來表示節點的類型。在跟 platform_driver 匹配時,優先級爲中。compatible 屬性在匹配過程中,優先級最高。

2.5 屬性格式

  • 設備節點的屬性形式是“name=value”,其中 value 有多種取值方式
    • arrays of cells,1個或多個cell【32 位】,用尖括號< >表示。
      • 如:interrupts = <17 0xc>;clock-frequency = <0x00000001 0x00000000>;
    • string(字符串),用" "表示。
      • 如:compatible = "simple-bus";
    • A bytestring(字節【8位】序列) ,用中括號[ ]表示。
      • 如:local-mac-address = [00 00 12 34 56 78]; // 每個 byte 使用 2 個 16 進制數來表示
    • 當然了也可以是以上三種方式的組合,用,分隔開每種表示方法。
      • 如: example = <0xf00f0000 19>, "a strange property format";

2.6 常用的節點(node)

  • 根節點 /

    • dts 文件中必須有一個根節點
    • 根節點中必須有這4個屬性:#address-cells 、#size-cells、compatible、model
  • CPU節點

    • 一般不需要我們設置,在 dtsi 文件中都定義好了。
  • memory 節點

    • 芯片廠家不可能事先確定你的板子使用多大的內存,所以 memory 節點需要板廠設置.
  • chosen 節點

    • 可以通過設備樹文件給內核傳入一些參數,這要在 chosen 節點中設置 bootargs 屬性。
chosen {
	bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
};

三、內核對設備樹的處理

  • 如何查看設備樹的節點信息

    • 系統啓動後可以在根文件系統中看到設備樹的節點信息:ls /proc/device-tree
  • 從源代碼文件 dts 文件開始,設備樹的處理過程爲:

    • ① dts 在 PC 機上被編譯爲 dtb 文件;
    • ② u-boot 把 dtb 文件傳給內核;
    • ③ 內核解析 dtb 文件,把每一個節點都轉換爲 device_node 結構體
    • ④ 對於某些 device_node 結構體,會被轉換爲 platform_device 結構體
      在這裏插入圖片描述
  • 根節點被保存在全局變量 of_root 中, 從 of_root 開始可以訪問到任意節點。

3.1 誰可以轉換爲 platform_device?

  • 哪些設備樹節點會被轉換爲 platform_device?
    • 情形A - 父節點是根節點:該節點含有compatile 屬性即可轉化爲 platform_device
    • 情形B - 父節點非根節點:需要滿足兩個條件 ①、該節點還有compatile 屬性 ②父節點 compatile 屬性爲下面4個之一:“simple-bus”,“simple-mfd”,“isa”,“arm,amba-bus”,才能轉化爲platform_device。
    • 情形C - 還需要記住一個:總線 I2C、SPI 節點下的子節點:不轉換爲 platform_device
      • 某個總線下的子節點, 應該交給對應的總線驅動程序來處理, 它們不應該被轉換爲platform_device。

示例講解:

/ {
	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>;
		};
	};
	
};

1、/mytest 會被轉換爲 platform_device(依據情形A)。它的子節點/mytest/mytest@0 也會被轉換爲 platform_device(依據情形B)。
2、/i2c 節點一般表示 i2c 控制器, 它會被轉換爲 platform_device, 在內核中有對應的platform_driver。/i2c/at24c02 節點不會被轉換爲 platform_device, 它被如何處理完全由父節點的platform_driver 決定, 一般是被創建爲一個 i2c_client。
3、/spi 節點一般也是用來表示 SPI 控制器, 它會被轉換爲 platform_device,在內核中有對應的 platform_driver。/spi/flash@0 節點不會被轉換爲 platform_device, 它被如何處理完全由父節點的platform_driver 決定, 一般是被創建爲一個 spi_device。

3.2 怎麼轉換爲 platform_device?

  結論如下:

  • A. platform_device 中含有 resource 數組, 它來自 device_node 的 reg, interrupts 屬性;
  • B. platform_device.dev.of_node 指向 device_node, 可以通過它獲得其他屬性

3.3 platform_device 如何與 platform_driver 配對?

  從設備樹轉換得來的 platform_device 會被註冊進內核裏,以後當我們每註冊一個platform_driver 時, 它們就會兩兩確定能否配對,如果能配對成功就調用 platform_driver 的probe 函數。套路是一樣的,我們需要將上一節談到的“匹配規則”再完善一下。源碼如下:

在這裏插入圖片描述

  • ①. 最先比較: platform_device. driver_override 和 platform_driver.driver.name
    • 可以設置 platform_device 的 driver_override,強制選擇某個 platform_driver。
  • ②. 接下來比較:設備樹信息
    • 比較: platform_device. dev.of_node 和 platform_driver.driver.of_match_table。
      • 首先,如果 of_match_table 中含有 compatible 值,就跟 dev 的 compatile 屬性比較,若一致則成功,否則返回失敗;
      • 其次,如果 of_match_table 中含有 type 值,就跟 dev 的 device_type 屬性比較,若一致則成功,否則返回失敗;
      • 最後,如果 of_match_table 中含有 name 值,就跟 dev 的 name 屬性比較,若一致則成功,否則返回失敗。
    • 而設備樹中建議不再使用 devcie_type 和 name 屬性,所以基本上只使用設備節點的compatible 屬性來尋找匹配的 platform_driver
    • 由設備樹節點轉換得來的 platform_device 中,含有一個結構體: of_node,類型如下:
      在這裏插入圖片描述
    • 如果一個 platform_driver 支持設備樹,它的 platform_driver.driver.of_match_table 是一個數組,類型如下:
      在這裏插入圖片描述
  • ③. 然後比較: platform_device. name 和 platform_driver.id_table[i].name
    • Platform_driver.id_table 是“platform_device_id”指針,表示該 drv 支持若干個 device,它裏面列出了各個 device 的{.name, .driver_data}, 其中的“name”表示該 drv 支持的設備的名字, driver_data 是些提供給該 device 的私有數據。
  • ④. 最後比較: platform_device.name 和 platform_driver.driver.name
    • platform_driver.id_table 可能爲空,這時可以根據 platform_driver.driver.name 來尋找同名的 platform_device。

一個圖概括所有的配對過程
在這裏插入圖片描述

3.4 沒有轉換爲 platform_device 的節點,如何使用?

  任意驅動程序裏,都可以直接訪問設備樹。可以使用of相關函數找到節點,讀出裏面的值。具體內容參考4.3。

四、內核裏操作設備樹常用的of函數

4.1 內核中設備樹相關的頭文件介紹

  內核源碼中 include/linux/目錄下有很多 of 開頭的頭文件, of 表示“open firmware”即開
放固件。設備樹的處理過程是: dtb -> device_node -> platform_device

4.1.1 處理 dtb

of_fdt.h 	// dtb 文件的相關操作函數, 我們一般用不到,
			// 因爲 dtb 文件在內核中已經被轉換爲 device_node 樹(它更易於使用)

4.1.2 處理device_node

of.h 			// 提供設備樹的一般處理函數,
				// 比如 of_property_read_u32(讀取某個屬性的 u32 值),
				// of_get_child_count(獲取某個 device_node 的子節點數)
of_address.h 	// 地址相關的函數,
				// 比如 of_get_address(獲得 reg 屬性中的 addr, size 值)
				// of_match_device (從 matches 數組中取出與當前設備最匹配的一項)
of_dma.h 		// 設備樹中 DMA 相關屬性的函數
of_gpio.h 		// GPIO 相關的函數
of_graph.h 		// GPU 相關驅動中用到的函數, 從設備樹中獲得 GPU 信息
of_iommu.h 		// 很少用到
of_irq.h   		// 中斷相關的函數
of_mdio.h  		// MDIO (Ethernet PHY) API
of_net.h 		// OF helpers for network devices.
of_pci.h 		// PCI 相關函數
of_pdt.h 		// 很少用到
of_reserved_mem.h // reserved_mem 的相關函數

4.1.3 處理 platform_device

of_platform.h 	// 把 device_node 轉換爲 platform_device 時用到的函數,
				// 比如 of_device_alloc(根據 device_node 分配設置 platform_device),
				// of_find_device_by_node (根據 device_node 查找到 platform_device),
				// of_platform_bus_probe (處理 device_node 及它的子節點)
of_device.h 	// 設備相關的函數, 比如 of_match_device

4.2 platform_device 相關的函數

  of_platform.h 中聲明瞭很多函數,但是作爲驅動開發者,我們只使用其中的 1、 2 個。其他的都是給內核自己使用的,內核使用它們來處理設備樹,轉換得到 platform_device。

4.2.1 of_find_device_by_node

函數原型爲:

extern struct platform_device *of_find_device_by_node(struct device_node *np);

  設備樹中的每一個節點,在內核裏都有一個 device_node;你可以使用 device_node 去找到對應的 platform_device

4.2.2 platform_get_resource

  這個函數跟設備樹沒什麼關係, 但是設備樹中的節點被轉換爲 platform_device 後, 設備樹中的 reg 屬性、 interrupts 屬性也會被轉換爲“resource”(呼應前面的:怎麼轉換爲 platform_device?)。這時,你可以使用這個函數取出這些資源。
數原型爲:

/**
* platform_get_resource - get a resource for a device
* @dev: platform device
* @type: resource type 	// 取哪類資源? IORESOURCE_MEM、 IORESOURCE_REG
* 						// IORESOURCE_IRQ 等
* @num: resource index 	// 這類資源中的哪一個?
*/
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num);

對於設備樹節點中的 reg 屬性,它屬性 IORESOURCE_MEM 類型的資源;
對於設備樹節點中的 interrupts 屬性,它屬性 IORESOURCE_IRQ 類型的資源。

4.3 有些節點不會生成 platform_device,怎麼訪問它們?

  內核會把 dtb 文件解析出一系列的 device_node 結構體,我們可以直接訪問這些device_node。內核源碼 incldue/linux/of.h 中聲明瞭 device_node 和屬性 property 的操作函數,device_node 和 property 的結構體定義如下:
在這裏插入圖片描述

4.3.1 找到節點

  • of_find_node_by_path

    • 根據路徑找到節點,比如“/”就對應根節點,“/memory”對應 memory 節點。
    • static inline struct device_node *of_find_node_by_path(const char *path);
  • of_find_node_by_name

    • 根據名字找到節點, 節點如果定義了 name 屬性,那我們可以根據名字找到它。
    • extern struct device_node *of_find_node_by_name(struct device_node *from, const char *name);
      • 參數 from 表示從哪一個節點開始尋找,傳入 NULL 表示從根節點開始尋找。
        但是在設備樹的官方規範中不建議使用“name”屬性,所以這函數也不建議使用
  • of_find_node_by_type

    • 根據類型找到節點,節點如果定義了 device_type 屬性,那我們可以根據類型找到它。
    • extern struct device_node *of_find_node_by_type(struct device_node *from, const char *type);
      • 參數 from 表示從哪一個節點開始尋找,傳入 NULL 表示從根節點開始尋找。
        但是在設備樹的官方規範中不建議使用“device_type”屬性,所以這函數也不建議使用
  • of_find_compatible_node

    • 根據 compatible 找到節點,節點如果定義了 compatible 屬性,那我們可以根據
      compatible 屬性找到它。
    • extern struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compat);
      • 參數 from 表示從哪一個節點開始尋找,傳入 NULL 表示從根節點開始尋找。
      • 參數 compat 是一個字符串,用來指定 compatible 屬性的值;
      • 參數 type 是一個字符串,用來指定 device_type 屬性的值,可以傳入 NULL。
  • of_find_node_by_phandle

    • 根據 phandle 找到節點。dts 文件被編譯爲 dtb 文件時,每一個節點都有一個數字 ID,這些數字 ID 彼此不同。可以使用數字 ID 來找到 device_node。這些數字 ID 就是 phandle。
    • extern struct device_node *of_find_node_by_phandle(phandle handle);
      • 參數 from 表示從哪一個節點開始尋找,傳入 NULL 表示從根節點開始尋找。
  • of_get_parent

    • 找到 device_node 的父節點。
    • extern struct device_node *of_get_parent(const struct device_node *node);
      • 參數 from 表示從哪一個節點開始尋找,傳入 NULL 表示從根節點開始尋找。
  • of_get_next_parent

    • 這個函數名比較奇怪,怎麼可能有“next parent”?它實際上也是找到 device_node 的父節點,跟 of_get_parent 的返回結果是一樣的。差別在於它多調用下列函數,把 node 節點的引用計數減少了 1。這意味着調用of_get_next_parent 之後,你不再需要調用 of_node_put 釋放 node 節點。
    • extern struct device_node *of_get_next_parent(struct device_node *node);
      • 參數 from 表示從哪一個節點開始尋找,傳入 NULL 表示從根節點開始尋找。
  • of_get_next_child

    • 取出下一個子節點。
    • extern struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev);
      • 參數 node 表示父節點;prev 表示上一個子節點,設爲 NULL 時表示想找到第 1 個子節點。
      • 不斷調用 of_get_next_child 時,不斷更新 pre 參數, 就可以得到所有的子節點。
  • of_get_next_available_child

    • 取出下一個“可用”的子節點,有些節點的 status 是“disabled”,那就會跳過這些節點。
    • struct device_node *of_get_next_available_child(const struct device_node *node, struct device_node *prev);
      • 參數 node 表示父節點;prev 表示上一個子節點,設爲 NULL 時表示想找到第 1 個子節點。
  • of_get_child_by_name

    • 根據名字取出子節點.

    • extern struct device_node *of_get_child_by_name(const struct device_node *node, const char *name);

      • 參數 node 表示父節點;name 表示子節點的名字。

4.3.2 找到屬性

  內核源碼 incldue/linux/of.h 中聲明瞭 device_node 的操作函數,當然也包括屬性的操作函數。

  • of_find_property
    • 找到節點中的屬性。
    • extern struct property *of_find_property(const struct device_node *np, const char *name, int *lenp);
      • 參數 np 表示節點,我們要在這個節點中找到名爲 name 的屬性。lenp 用來保存這個屬性的長度, 即它的值的長度。

舉個例子,在設備樹中,節點大概是這樣:

xxx_node {
	xxx_pp_name = “hello”;
};

上述節點中,“xxx_pp_name”就是屬性的名字,值的長度是 6

4.3.3 獲取屬性的值

  • of_get_property

    • 根據名字找到節點的屬性,並且返回它的值
    • const void *of_get_property(const struct device_node *np, const char *name, int *lenp)
      • 參數 np 表示節點,我們要在這個節點中找到名爲 name 的屬性,然後返回它的值。lenp 用來保存這個屬性的長度, 即它的值的長度。
  • of_property_count_elems_of_size

    • 根據名字找到節點的屬性,確定它的值有多少個元素(elem)。
    • int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size)
      • 參數 np 表示節點,我們要在這個節點中找到名爲 propname 的屬性,然後返回下列結果:
      • return prop->length / elem_size;

舉個例子,在設備樹中,節點大概是這樣:

xxx_node {
	xxx_pp_name = <0x50000000 1024> <0x60000000 2048>;
};

調用 of_property_count_elems_of_size(np, “xxx_pp_name”, 8)時,返回值是 2 - ?(疑問)
調用 of_property_count_elems_of_size(np, “xxx_pp_name”, 4)時,返回值是 4 - ?(疑問)

  • 讀整數 u32/u64
    • static inline int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value);
    • extern int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value);

舉個例子,在設備樹中,節點大概是這樣

xxx_node {
	name1 = <0x50000000>;
	name2 = <0x50000000 0x60000000>;
};

調用 of_property_read_u32 (np, “name1”, &val)時, val 將得到值 0x50000000;
調用 of_property_read_u64 (np, “name2”, &val)時, val 將得到值0x0x6000000050000000。

  • 讀某個整數 u32/u64
    • extern int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value)

舉個例子,在設備樹中,節點大概是這樣:

xxx_node {
	name2 = <0x50000000 0x60000000>;
};

調用 of_property_read_u32 (np, “name2”, 1, &val)時, val 將得到值 0x0x60000000。?(疑問)

  • 讀數組
int of_property_read_variable_u8_array( const struct device_node *np,
										const char *propname, u8 *out_values,
										size_t sz_min, size_t sz_max);
int of_property_read_variable_u16_array(const struct device_node *np,
										const char *propname, u16 *out_values,
										size_t sz_min, size_t sz_max);
int of_property_read_variable_u32_array(const struct device_node *np,
										const char *propname, u32 *out_values, 
										size_t sz_min, size_t sz_max);
int of_property_read_variable_u64_array(const struct device_node *np,
										const char *propname, u64 *out_values,
										size_t sz_min, size_t sz_max);

舉個例子,在設備樹中,節點大概是這樣

xxx_node {
	name2 = <0x50000012 0x60000034>;
};

上述例子中屬性 name2 的值,長度爲 8。
調用 of_property_read_variable_u8_array (np, “name2”, out_values, 1, 10)時, out_values
中將會保存這 8 個字節: 0x12,0x00,0x00,0x50,0x34,0x00,0x00,0x60。

調用 of_property_read_variable_u16_array (np, “name2”, out_values, 1, 10)時, out_values中將會保存這 4 個 16 位數值: 0x0012, 0x5000,0x0034,0x6000。

總之,這些函數要麼能取到全部的數值,要麼一個數值都取不到;如果值的長度在 sz_min 和 sz_max 之間,就返回全部的數值;否則一個數值都不返回。

  • 讀字符串
    • int of_property_read_string(const struct device_node *np, const char *propname, const char **out_string);
      • 返回節點 np 的屬性(名爲 propname)的值, (*out_string)指向這個值,把它當作字符串。

4.4 如何修改設備樹?

  一個寫得好的驅動程序, 它會盡量確定所用資源。只把不能確定的資源留給設備樹, 讓設備樹來指定。根據原理圖確定"驅動程序無法確定的硬件資源", 再在設備樹文件中填寫對應內容。那麼, 所填寫內容的格式是什麼?

4.4.1 使用芯片廠家提供的工具

  有些芯片,廠家提供了對應的設備樹生成工具,可以選擇某個引腳用於某些功能,就可以自動生成設備樹節點。你再把這些節點複製到內核的設備樹文件裏即可。

4.4.2 看綁定文檔

內核文檔 Documentation/devicetree/bindings/
做得好的廠家也會提供設備樹的說明文檔

4.4.3 參考同類型單板的設備樹文件

4.4.4 網上搜索

4.4.5 實在沒辦法時, 只能去研究驅動源碼


  本節,我們初步介紹了設備樹的引入,設備樹的作用,設備樹的基本語法以及設備樹常用的of函數。關於of函數當然有很多,我們不可能每個都要記住,還是那個原則,Learn by doing,做着學着,做着記着!下一節我們將通過設備樹實際操作LED,來吧,一起感受下設備樹的魅力吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章