宋牧春: Linux設備樹文件結構與解析深度分析(2)

宋牧春: Linux設備樹文件結構與解析深度分析(2)

2017-08-26 宋牧春 Linuxer Linuxer
LinuxDev

發佈Linux內核、應用和驅動開發,Linux運維相關的資訊和學習資料。Linux愛好者的交流學習平臺。

作者簡介

宋牧春,linux內核愛好者,喜歡閱讀各種開源代碼(uboot、linux、ucos、rt-thread等),對於優秀的代碼框架及其癡迷。現就職於一家手機研發公司,任職Android BSP開發工程師。


正文開始


前情提要:

宋牧春: Linux設備樹文件結構與解析深度分析(1)



6. platform_devicedevice_node綁定

經過以上解析,DeviceTree的數據已經全部解析出具體的struct device_nodestruct property結構體,下面需要和具體的device進行綁定。首先講解platform_devicedevice_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結構體,並向平臺設備總線註冊設備信息。如果第一級nodecompatible屬性值等於“simple-bus”、“simple-mfd”或者"arm,amba-bus"的話,kernel會繼續爲當前node的第二級包含compatible屬性的node創建platform_device結構體,並註冊設備。Linux系統下的設備大多都是掛載在平臺總線下的,因此在平臺總線被註冊後,會根據of_root節點的樹結構,去尋找該總線的子節點,所有的子節點將被作爲設備註冊到該總線上。

7. i2c_clientdevice_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_clientname。例如:compatible = maxim,ds1338,i2c_client->name =ds1338”。

8. Device_Treesysfs

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屬性就是一個二進制屬性文件。


精彩文章


宋寶華:Linux的任督二脈——進程調度和內存管理

謝寶友: 手把手教你給Linux內核發patch

邢森: 淺析Linux kernel的閱讀方法

何曄: 當ZYNQ遇到Linux Userspace I/O(UIO)

笨叔叔: 我的Linux內核學習經歷

黃偉亮:ext4文件系統之裸數據的分析實踐

徐西寧: 碼農小馬與Docker不得不說的故事

陳然: 容器生態系統的發展與演變之我見

讓天堂的歸天堂,讓塵土的歸塵土——談Linux的總線、設備、驅動模型

宋寶華:Docker 最初的2小時(Docker從入門到入門)


與其相忘於江湖,不如點擊二維碼關注Linuxer~



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