1. 程式人生 > >Linux 裝置樹詳解

Linux 裝置樹詳解

本文基於天嵌E9V3開發板,詳解裝置樹的規則和用法。

一、基本概念

DTS即Device Tree Source,是一個文字形式的檔案,用於描述硬體資訊,包括CPU的數量和類別、記憶體基地址和大小、中斷控制器、匯流排和橋、外設、時鐘和GPIO控制器等。
DTB即Device Tree Blob,是一個二進位制形式的檔案,由linux核心識別,為其中的裝置匹配合適的驅動程式。
DTC即Device Tree Compiler,將適合人類閱讀和編輯的DTS檔案編譯成適合機器處理的DTB檔案。
編譯核心的時候會同時使用DTC 將DTS編譯成DTB,天嵌E9V3使用的DTS檔案e9v3-sabresd.dts位於/arch/arm/boot/dts目錄下。
在這裡插入圖片描述


如上圖所示,bootloader讀取dtb檔案放入RAM中,並將存放地址告訴linux核心,核心啟動以後從該地址讀取相應的裝置資訊,匹配平臺和裝置驅動。

二、E9V3裝置樹總覽

linux中的一個dts檔案對應一個machine, 不同的machine可能使用相同的SOC,只是對外設的使用不同,這些不同的dts檔案勢必包含很多相同的內容,為了簡化,可以把公用的部分提煉為dtsi檔案。
e9v3-sabresd.dts包含dtsi的結構如下:
在這裡插入圖片描述

列出各個檔案中的節點,如下圖所示,是不是有點像有很多分支的樹?
在這裡插入圖片描述

三、裝置樹編寫規則

裝置樹由一個一個的節點組成,每個裝置樹有且僅有一個根節點,節點可以包含子節點。

1、節點名稱
基本的節點名格式如下:
[email protected]
其中node-name由字母、數字和一些特殊字元構成的字串,長度不超過31個字元,可自定義,但為了可讀性,spec中規定了一些約定成熟的名稱,比如cpus, memory, bus,clock等。
unit-address為節點的地址,通常為暫存器的首地址,比如imx6q datasheet中uart1的暫存器地址範圍為0202_0000~0202_3FFF,在定義uart1節點時,對應的unit-address為0202_0000:
uart1: [email protected] {

}
有些節點沒有對應的暫存器,則unit-address可省略,節點名只由node-name組成,比如cpus:
cpus {

}
根節點的名稱比較特殊,由一個斜槓組成:
/{

}

2、label標籤

三、裝置與驅動的匹配

linux核心啟動以後,先解析並註冊dts中的裝置,然後再註冊驅動,比較驅動中的compatible 屬性和裝置中的compatible 屬性,或者比較兩者的name屬性,如果一致則匹配成功。
1、解析dtb
在start_kernel() --> setup_arch(0 --> unflatten_device_tree() --> __unflatten_device_tree()函式中掃描dtb,並轉換成節點是device_node的樹狀結構。
注:程式碼基於linux4.1.15核心(下同)

static void __unflatten_device_tree()
{
    ...
	/* First pass, scan for size */
	start = 0;
	size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true);
	size = ALIGN(size, 4);
    ...
	/* Second pass, do actual unflattening */
	start = 0;
	unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);
   ...
}

2. 註冊dts裝置

imx6q_init_machine() --> of_platform_populate()。
在of_platform_populate()中迴圈掃描根節點下的各節點:

int of_platform_populate()
{
    ...
	for_each_child_of_node(root, child) {
		rc = of_platform_bus_create(child, matches, lookup, parent, true);
     }
     ...
}
static int of_platform_bus_create()
{
    ...
	/* Make sure it has a compatible property */
	if (strict && (!of_get_property(bus, "compatible", NULL))) {
		pr_debug("%s() - skipping %s, no compatible prop\n",
			 __func__, bus->full_name);
		return 0;
	}

	auxdata = of_dev_lookup(lookup, bus);
	if (auxdata) {
		bus_id = auxdata->name;
		platform_data = auxdata->platform_data;
	}
    ...
	dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
	if (!dev || !of_match_node(matches, bus))
		return 0;
    如果節點有子節點,則遞迴呼叫of_platform_bus_create()掃描節點的子節點:
	for_each_child_of_node(bus, child) {
		pr_debug("   create child: %s\n", child->full_name);
		rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	of_node_set_flag(bus, OF_POPULATED_BUS);
	return rc;
}

最終呼叫of_platform_device_create_pdata() —> of_device_add() 註冊裝置並新增到對應的連結串列中。

3、註冊驅動
Linux註冊驅動的函式為driver_register(),或者其包裝函式如platform_driver_register(),而driver_register()或者其包裝函式一般在驅動的初始化函式xxx_init()中呼叫。
驅動初始化函式xxx_init()被呼叫的路勁為:
start_kernel() --> rest_init() --> Kernel_init() --> kernel_init_freeable() --> do_basic_setup() --> do_initcalls:
在這裡插入圖片描述

簡而言之,在start_kernel()中呼叫driver_register()註冊驅動程式。

4、匹配裝置
追蹤driver_register()函式,driver_register() --> bus_add_driver() --> driver_attach() --> __driver_attach:

static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;
	if (!driver_match_device(drv, dev))
		return 0;

	if (dev->parent)	/* Needed for USB */
		device_lock(dev->parent);
	device_lock(dev);
	if (!dev->driver)
		driver_probe_device(drv, dev);
	device_unlock(dev);
	if (dev->parent)
		device_unlock(dev->parent);
	return 0;
}

driver_match_device()中尋找匹配的裝置,如果匹配成功則執行驅動的probe函式。
driver_match_device()最終會呼叫平臺的匹配函式platform_match():

static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}

從程式碼中可以看出, platform_match()會採用多種方法進行匹配:

  1. of_driver_match_device將根據驅動程式of_match_table中的compatible屬性,與裝置中的compatible屬性進行比對。
  2. 其次呼叫acpi_driver_match_device()進行匹配。
  3. 如果前2種方法都沒有匹配的,最後比對裝置和驅動的name字串是否一致。

以GPIO-key為例,裝置和驅動匹配示意圖如下:
在這裡插入圖片描述

相關推薦

linux裝置-韋東山-專題視訊課程

linux裝置樹詳解—150人已學習 課程介紹        現在的linux核心(Linux3.X)都已支援裝置樹機制(dts),不管你是玩核心還是玩驅動,一定會碰到裝置樹,而網上雖然有很多部落格,但都講的不夠清晰,看了還是不懂,半桶水,學員急需一套講解裝置樹比較透徹的課程

Linux裝置

裝置樹詳解在Linux3.x版本後,arch/arm/plat-xxx和arch/arm/mach-xxx中,描述板級細節的程式碼(比如platform_device、i2c_board_info等)被大量取消,取而代之的是裝置樹,其目錄位於arch/arm/boot/dts

Linux 裝置

本文基於天嵌E9V3開發板,詳解裝置樹的規則和用法。 一、基本概念 DTS即Device Tree Source,是一個文字形式的檔案,用於描述硬體資訊,包括CPU的數量和類別、記憶體基地址和大小、中斷控制器、匯流排和橋、外設、時鐘和GPIO控制器等。 DTB即

Linux DTS(Device Tree Source)裝置

一.什麼是DTS?為什麼要引入DTS? DTS即Device Tree Source 裝置樹原始碼, Device Tree是一種描述硬體的資料結構,它起源於 OpenFirmware (OF)。 在Linux 2.6中,ARM架構的板極硬體細節過多地被硬編碼在arc

Linux DTS(Device Tree Source)裝置之一(背景基礎知識篇)

本系列導航: 一.什麼是DTS?為什麼要引入DTS? DTS即Device Tree Source 裝置樹原始碼, Device Tree是一種描述硬體的資料結構,它起源於 OpenFirmware (OF)。 在Linux 2.6中,ARM架構的板極硬體細節過

Linux DTS(Device Tree Source)裝置之二(dts匹配及發揮作用的流程篇)

本系列導航: 有上一篇文章,我們瞭解了dts的背景知識和相關基礎,這次我們對應實際裝置進行一下相關分析。 DTS裝置樹的匹配過程 一個dts檔案確定一個專案,多個專案可以包含同一個dtsi檔案。找到該專案對應的dts檔案即找到了該裝置樹的根節點。 kernel

高通平臺msm8953 Linux DTS(Device Tree Source)裝置之二(DTS裝置匹配過程)

本系列導航:有上一篇文章,我們瞭解了dts的背景知識和相關基礎,這次我們對應實際裝置進行一下相關分析。DTS裝置樹的匹配過程一個dts檔案確定一個專案,多個專案可以包含同一個dtsi檔案。找到該專案對應的dts檔案即找到了該裝置樹的根節點。kernel\arch\arm\bo

裝置

轉載地址:http://blog.csdn.net/qq_28992301/article/details/53321610在Linux3.x版本後,arch/arm/plat-xxx和arch/arm/mach-xxx中,描述板級細節的程式碼(比如platform_devi

裝置 (借點引用, &... , 結構, 節點屬性設定如gpio的上拉,下拉,io中斷設定等 )

轉載於 : http://blog.csdn.net/qq_28992301/article/details/53321610 在Linux3.x版本後,arch/arm/plat-xxx和arch/arm/mach-xxx中,描述板級細節的程式碼(比如platform

Linux裝置語法【轉】

轉自:https://www.cnblogs.com/xiaojiang1025/p/6131381.html 概念 Linux核心從3.x開始引入裝置樹的概念,用於實現驅動程式碼與裝置資訊相分離。在裝置樹出現以前,所有關於裝置的具體資訊都要寫在驅動裡,一旦外圍裝置變化,驅動程式碼就要重寫。引入了裝置樹之

Linux裝置語法

概念 Linux核心從3.x開始引入裝置樹的概念,用於實現驅動程式碼與裝置資訊相分離。在裝置樹出現以前,所有關於裝置的具體資訊都要寫在驅動裡,一旦外圍裝置變化,驅動程式碼就要重寫。引入了裝置樹之後,驅動程式碼只負責處理驅動的邏輯,而關於裝置的具體資訊存放到裝置樹檔案中,

Linux驅動】Linux裝置語法

1 概念Linux核心從3.x開始引入裝置樹的概念,用於實現驅動程式碼與裝置資訊相分離。在裝置樹出現以前,所有關於裝置的具體資訊都要寫在驅動裡,一旦外圍裝置變化,驅動程式碼就要重寫。引入了裝置樹之後,驅動程式碼只負責處理驅動的邏輯,而關於裝置的具體資訊存放到裝置樹檔案中,這樣,如果只是硬體介面資訊的變化而沒有

linux裝置dts移植

摘 要:裝置樹的引入減少了核心為支援新硬體而需要的改變,提高程式碼重用,加速了Linux支援包的開發,使得單個核心映象能支援多個系統。作為U-Boot 和Linux 核心之間的動態介面,本文闡述了裝置樹的資料儲存格式以及原始碼描述語法,進而分析了U-Boot 對扁平設備樹的

linux裝置dts一之移植

支援包的開發,使得單個核心映象能支援多個系統。作為U-Boot 和Linux 核心之間的動態 介面,本文闡述了裝置樹的資料儲存格式以及原始碼描述語法,進而分析了U-Boot 對扁平設 備樹的支援設定,Linux 核心對裝置樹的解析流程。 關鍵詞:扁平裝置樹; DTS; PowerPC; Linux IBM、S

《Linux4.0裝置驅動開發》筆記--第十八章:ARM Linux裝置

18.1 ARM裝置樹簡介 裝置舒適一種描述印鑑的資料結構,它起源於OpenFirmware(OF) 採用裝置樹前後對比: 採用裝置樹之前:ARM架構的板極硬體細節過多的被硬編碼在arch/arm/plat-xxx和arch/arm/mach-xxx中

Linux裝置驅動(一)

    請求佇列跟蹤等候的塊I/O請求,它儲存用於描述這個裝置能夠支援的請求的型別資訊、它們的最大大小、多少不同的段可進入一個請求、硬體扇區大小、對齊要求等引數,其結果是:如果請求佇列被配置正確了,它不會交給該裝置一個不能處理的請求。     請求佇列還實現一個插入介面,這個介面允許使用多個I/O排程器,I/

Linux驅動開發》——LCD裝置驅動重要資料結構及驅動框架

核心檔案:/drivers/video/fbmem.c 18.2.3.Linux幀緩衝相關資料結構與函式          1. fb_info結構體(最關鍵) /* struct fb_info 結構體 */ struct fb_info {     int node;

Linux啟動流程

linux 詳解 啟動流程 grub mbr 內核 linux啟動流程第一部分 Linux啟動基礎知識1.1 linux centos6.8啟動流程圖 BIOS加電自檢à加載MBRà加載啟動grubà加載內核à啟動/sbin/i

Linux netstat命令,高級面試必備

bytes tool head osi ngs 進行 pen 通信 詳細信息 簡介 Netstat 命令用於顯示各種網絡相關信息,如網絡連接,路由表,接口狀態 (Interface Statistics),masquerade 連接,多播成員 (Multicast Mem

linux top 命令

ctrl+ 一次 所有 使用方法 ase 隱藏 統計 ini 前臺 top命令是Linux下常用的性能分析工具,能夠實時顯示系統中各個進程的資源占用狀況,類似於Windows的任務管理器。下面詳細介紹它的使用方法。top - 01:06:48 up 1:22, 1 user