宋牧春: Linux設備樹文件結構與解析深度分析(2)
作者簡介
宋牧春,linux內核愛好者,喜歡閱讀各種開源代碼(uboot、linux、ucos、rt-thread等),對於優秀的代碼框架及其癡迷。現就職於一家手機研發公司,任職Android BSP開發工程師。
正文開始
前情提要:
6. platform_device和device_node綁定
經過以上解析,DeviceTree的數據已經全部解析出具體的struct device_node和struct property結構體,下面需要和具體的device進行綁定。首先講解platform_device和device_node的綁定過程。在arch/arm/kernel/setup.c文件中,customize_machine()函數負責填充struct platform_device結構體。函數調用過程如圖8所示。
圖8 platform_device生成流程圖
代碼分析如下:
const struct of_device_id of_default_bus_match_table[] = { { .compatible = "simple-bus", }, { .compatible = "simple-mfd", }, #ifdef CONFIG_ARM_AMBA { .compatible = "arm,amba-bus", }, #endif /* CONFIG_ARM_AMBA */ {} /* Empty terminated list */ };
int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent) { struct device_node *child; int rc = 0;
/* 獲取根節點 */ root = root ? of_node_get(root) : of_find_node_by_path("/"); if (!root) return -EINVAL;
/* 爲根節點下面的每一個節點創建platform_device結構體 */ for_each_child_of_node(root, child) { rc = of_platform_bus_create(child, matches, lookup, parent, true); if (rc) { of_node_put(child); break; } } /* 更新device_node flag標誌位 */ of_node_set_flag(root, OF_POPULATED_BUS);
of_node_put(root); return rc; }
static int of_platform_bus_create(struct device_node *bus, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent, bool strict) { const struct of_dev_auxdata *auxdata; struct device_node *child; struct platform_device *dev; const char *bus_id = NULL; void *platform_data = NULL; int rc = 0;
/* 只有包含"compatible"屬性的node節點纔會生成相應的platform_device結構體 */ /* Make sure it has a compatible property */ if (strict && (!of_get_property(bus, "compatible", NULL))) { return 0; } /* 省略部分代碼 */ /* *針對節點下面得到status = "ok"或者status = "okay"或者不存在status屬性的 *節點分配內存並填充platform_device結構體 */ dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); if (!dev || !of_match_node(matches, bus)) return 0;
/* 遞歸調用節點解析函數,爲子節點繼續生成platform_device結構體,前提是父節點 *的“compatible” = “simple-bus”,也就是匹配of_default_bus_match_table結構體中的數據 */ for_each_child_of_node(bus, child) { 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_populate()函數執行完畢,kernel就爲DTB中所有包含compatible屬性名的第一級node創建platform_device結構體,並向平臺設備總線註冊設備信息。如果第一級node的compatible屬性值等於“simple-bus”、“simple-mfd”或者"arm,amba-bus"的話,kernel會繼續爲當前node的第二級包含compatible屬性的node創建platform_device結構體,並註冊設備。Linux系統下的設備大多都是掛載在平臺總線下的,因此在平臺總線被註冊後,會根據of_root節點的樹結構,去尋找該總線的子節點,所有的子節點將被作爲設備註冊到該總線上。
7. i2c_client和device_node綁定
經過customize_machine()函數的初始化,DTB已經轉換成platform_device結構體,這其中就包含i2c adapter設備,不同的SoC需要通過平臺設備總線的方式自己實現i2c adapter設備的驅動。例如:i2c_adapter驅動的probe函數中會調用i2c_add_numbered_adapter()註冊adapter驅動,函數流執行如圖9所示。
圖9 i2c_client綁定流程
在of_i2c_register_devices()函數內部便利i2c節點下面的每一個子節點,併爲子節點(status = “disable”的除外)創建i2c_client結構體,並與子節點的device_node掛接。其中i2c_client的填充是在i2c_new_device()中進行的,最後device_register()。在構建i2c_client的時候,會對node下面的compatible屬性名稱的廠商名字去除作爲i2c_client的name。例如:compatible = “maxim,ds1338”,則i2c_client->name =“ds1338”。
8. Device_Tree與sysfs
kernel啓動流程爲start_kernel()→rest_init()→kernel_thread():kernel_init()→do_basic_setup()→driver_init()→of_core_init(),在of_core_init()函數中在sys/firmware/devicetree/base目錄下面爲設備樹展開成sysfs的目錄和二進制屬性文件,所有的node節點就是一個目錄,所有的property屬性就是一個二進制屬性文件。
精彩文章
何曄:
當ZYNQ遇到Linux Userspace I/O(UIO)
讓天堂的歸天堂,讓塵土的歸塵土——談Linux的總線、設備、驅動模型
宋寶華:Docker 最初的2小時(Docker從入門到入門)
與其相忘於江湖,不如點擊二維碼關注Linuxer~