宋牧春: Linux设备树文件结构与解析深度分析(2) 【转】

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介:

转自:https://mp.weixin.qq.com/s/WPZSElF3OQPMGqdoldm07A

 

 

 

 

 

 

作者简介

 

宋牧春,linux内核爱好者,喜欢阅读各种开源代码(uboot、linux、ucos、rt-thread等),对于优秀的代码框架及其痴迷。现就职于一家手机研发公司,任职Android BSP开发工程师。

 

正文开始

 

前情提要:

宋牧春: Linux设备树文件结构与解析深度分析(1)

征稿和征稿奖励名单:

Linuxer-"Linux开发者自己的媒体"第二月稿件录取和赠书名单

Linuxer-"Linux开发者自己的媒体"首月稿件录取和赠书名单


 

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属性就是一个二进制属性文件。




本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/8177032.html,如需转载请自行联系原作者

相关文章
|
19小时前
|
Linux 数据处理 调度
深入探索Linux的renice命令:优化进程优先级
`renice`命令在Linux中用于调整运行进程的优先级,范围-20(最高)至19(最低)。它可以实时改变进程的优先级,无需重启,支持按PID、进程组或用户批量修改。例如,`renice -n 10 -p 1234`将PID为1234的进程优先级设为10。使用时要注意不要滥用高优先级,避免频繁修改,了解系统负载,并记录变更,通常需root权限。
|
19小时前
|
Linux 数据处理 Perl
深入探索Linux的rename命令:文件重命名利器
**Linux的`rename`命令是批量重命名文件的利器,基于正则表达式或Perl,支持预览、交互式确认及多种操作模式。常用示例:用`s/ /_/g`替换空格为下划线,`s/$/.txt/`添加扩展名,`s/\..*//`删除扩展名。使用时注意备份、测试命令、权限和正则表达式知识。**
|
1天前
|
缓存 Linux 虚拟化
Linux下top命令指标说明
Linux下top命令指标说明
8 0
|
2天前
|
Linux
Linux的top命令是什么,如何使用
【6月更文挑战第30天】Linux的top命令是什么,如何使用
6 1
|
2天前
|
Linux 数据处理
探索Linux下的readlink命令:解析符号链接的利器
`readlink`命令在Linux中用于揭示符号链接的指向,显示它们所链接的实际文件或目录的路径。它可以显示简洁的绝对路径(-f),处理循环链接(-e),或不加换行符输出(-n)。例如,查看`link.txt`指向:`readlink link.txt`;获取绝对路径:`readlink -f link.txt`。使用时要注意链接是否存在、权限问题和可能的循环链接。
|
2天前
|
Linux 数据处理
探索Linux下的readelf命令:深入了解ELF文件
`readelf`是Linux下分析ELF文件的命令行工具,用于查看文件头、节区、符号表等信息。支持可执行文件、共享库等多种类型。常用选项有`-h`(文件头)、`-l`(程序头)、`-S`(节区)、`-s`(符号表)、`-r`(重定位)和`-d`(动态节区)。结合其他工具如`objdump`,能深入理解二进制文件,助力开发和调试。
|
2天前
|
Linux
常用的Linux系统命令及其使用技巧
常用的Linux系统命令及其使用技巧
|
3天前
|
存储 安全 Linux
深入解析Linux的`read`命令
`read`命令在Linux shell中用于从标准输入读取数据并赋值给变量。它可以用于交互式脚本,提供用户输入或读文件。关键选项包括`-p`(提示用户)、`-r`(禁用转义)、`-s`(静默模式,适合密码)、`-t`(超时)和`-n`(读取特定字符数)。示例包括基本输入、带提示的密码输入和设置超时的输入。注意安全处理密码和验证用户输入。
|
3天前
|
存储 Linux 数据处理
Linux中的raw命令:深入解析与实用指南
Linux的`raw`命令详解:用于直接访问硬件设备,绕过文件系统,提供高灵活性和性能。适用于数据处理,如直接复制文件或设备数据。使用时需谨慎,注意设备理解、数据备份及正确选项选择。结合其他工具可实现更多功能。示例:`raw file1 file2`复制文件,`raw -s 1024 file1 file2`跳过字节复制。
|
3天前
|
Linux 数据处理 vr&ar
Linux下的ranlib命令:静态库文件的索引生成器
`ranlib`是Linux用于加速静态库(.a文件)链接的工具,它生成索引以优化查找目标文件。当链接器处理静态库时,索引能快速定位目标,提升效率。命令如`ranlib libexample.a`创建索引。注意,新工具链可能已自动包含此功能,使用前应确保库文件未含索引,避免重复生成。