Device Tree (四) - device_node -> platform_device

一,调用到of_platform_populate的流程

kernel V5.10:
start_kernel(void)
----setup_arch(&command_line);
--------setup_machine_fdt(__fdt_pointer); /* D:\work\source_code\msm-kernel\msm_kernel\arch\arm64\kernel\setup.c */
--------unflatten_device_tree(); /* D:\work\source_code\msm-kernel\msm_kernel\drivers\of\fdt.c */
----arch_call_rest_init();
--------rest_init();
------------kernel_thread(kernel_init, NULL, CLONE_FS);
----------------kernel_init_freeable();
--------------------do_basic_setup();
------------------------driver_init();
----------------------------firmware_init(); /* D:\work\source_code\msm-kernel\msm_kernel\drivers\base\firmware.c */
--------------------------------firmware_kobj = kobject_create_and_add("firmware", NULL);
----------------------------of_core_init();
--------------------------------of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
--------------------------------__of_attach_node_sysfs(np);
--------------------------------proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
----------------------------platform_bus_init();
------------------------do_initcalls();
--------------------------------do_initcall_level(level, command_line);
--------------------------------arch_initcall_sync(of_platform_default_populate_init);
------------------------------------of_platform_populate(root, of_default_bus_match_table, lookup, parent); /* D:\work\source_code\msm-kernel\msm_kernel\drivers\of\platform.c */

二,Device_Tree与sysfs

在of_core_init()函数中在/sys/firmware/devicetree/base目录下面为设备树展开成sysfs的目录和二进制属性文件,所有的node节点就是一个目录,所有的property属性就是一个二进制属性文件。

1, 创建/sys/firmware 目录

int __init firmware_init(void)
{
    firmware_kobj = kobject_create_and_add("firmware", NULL);
    if (!firmware_kobj)
        return -ENOMEM;
    return 0;
}

2, of_core_init

void __init of_core_init(void)
{
    struct device_node *np;

    /* Create the kset, and register existing nodes */
    mutex_lock(&of_mutex);
    //创建/sys/firmware/devicetree目录
    of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj);
    if (!of_kset) {
        mutex_unlock(&of_mutex);
        pr_err("failed to register existing nodes\n");
        return;
    }

    for_each_of_allnodes(np) {
        //为设备树展开成sysfs的目录和二进制属性文件
        __of_attach_node_sysfs(np);
        if (np->phandle && !phandle_cache[of_phandle_cache_hash(np->phandle)])
            phandle_cache[of_phandle_cache_hash(np->phandle)] = np;
    }
    mutex_unlock(&of_mutex);

    /* Symlink in /proc as required by userspace ABI */
    if (of_root)
        //创建软链接 /proc/device-tree -> /sys/firmware/devicetree/base
        proc_symlink("device-tree", NULL, "/sys/firmware/devicetree/base");
}

3, __of_attach_node_sysfs

int __of_attach_node_sysfs(struct device_node *np)
{
    const char *name;
    struct kobject *parent;
    struct property *pp;
    int rc;

    if (!IS_ENABLED(CONFIG_SYSFS) || !of_kset)
        return 0;

    // kobj.kset
    np->kobj.kset = of_kset;
    if (!np->parent) {
        /* Nodes without parents are new top level trees */
        //如果device_node的parent为空,这个device_node作为根节点,根的名字为base
        name = safe_name(&of_kset->kobj, "base");
        parent = NULL;
    } else {
        //非根节点的名字
        name = safe_name(&np->parent->kobj, kbasename(np->full_name));
        parent = &np->parent->kobj;
    }
    if (!name)
        return -ENOMEM;

    //在父亲节点的目录下,为node创建目录
    rc = kobject_add(&np->kobj, parent, "%s", name);
    kfree(name);
    if (rc)
        return rc;

    //为property创建二进制属性文件
    for_each_property_of_node(np, pp)
        __of_add_property_sysfs(np, pp);

    of_node_get(np);
    return 0;
}

4,__of_add_property_sysfs

int __of_add_property_sysfs(struct device_node *np, struct property *pp)
{
    int rc;

    /* Important: Don't leak passwords */
    bool secure = strncmp(pp->name, "security-", 9) == 0;

    if (!IS_ENABLED(CONFIG_SYSFS))
        return 0;

    //判断node已经被绑定 node->kobj.state_in_sysfs
    if (!of_kset || !of_node_is_attached(np))
        return 0;

    sysfs_bin_attr_init(&pp->attr);
    pp->attr.attr.name = safe_name(&np->kobj, pp->name);
    pp->attr.attr.mode = secure ? 0400 : 0444;
    pp->attr.size = secure ? 0 : pp->length;
    pp->attr.read = of_node_property_read;
    
    //在所属节点的目录中,为property创建二进制属性文件
    rc = sysfs_create_bin_file(&np->kobj, &pp->attr);
    WARN(rc, "error adding attribute %s to node %pOF\n", pp->name, np);
    return rc;
}

三,of_platform_populate执行流程

1, 整体流程

D:\work\source_code\msm-kernel\msm_kernel\drivers\of\platform.c


                            of_platform_default_populate_init()
                                        |
                            of_platform_default_populate();
                                        |
                            of_platform_populate();
                                        |
                            of_platform_bus_create()
                _____________________|_________________
                |                                      |
        of_platform_device_create_pdata()       of_platform_bus_create()
        _________________|____________________
       |                                      |
of_device_alloc()                        of_device_add()       

2, 关键代码分析

2.1,of_platform_default_populate
int of_platform_default_populate(struct device_node *root,
                 const struct of_dev_auxdata *lookup,
                 struct device *parent)
{
    return of_platform_populate(root, of_default_bus_match_table, lookup,
                    parent);
}
EXPORT_SYMBOL_GPL(of_platform_default_populate);
2.2, of_platform_populate
/**
* of_platform_populate() - Populate platform_devices from device tree data
* @root: parent of the first level to probe or NULL for the root of the tree
* @matches: match table, NULL to use the default
* @lookup: auxdata table for matching id and platform_data with device nodes
* @parent: parent to hook devices from, NULL for toplevel
*
* Similar to of_platform_bus_probe(), this function walks the device tree
* and creates devices from nodes.  It differs in that it follows the modern
* convention of requiring all device nodes to have a 'compatible' property,
* and it is suitable for creating devices which are children of the root
* node (of_platform_bus_probe will only create children of the root which
* are selected by the @matches argument).
*
* New board support should be using this function instead of
* of_platform_bus_probe().
*
* Returns 0 on success, < 0 on failure.
*/
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;

    pr_debug("%s()\n", __func__);
    pr_debug(" starting at: %pOF\n", root);

    //遍历根节点下的子节点
    device_links_supplier_sync_state_pause();
    for_each_child_of_node(root, child) {
        //将device_node转化为platform_device
        rc = of_platform_bus_create(child, matches, lookup, parent, true);
        if (rc) {
            of_node_put(child);
            break;
        }
    }
    device_links_supplier_sync_state_resume();

    //设置已经被转化的flag
    of_node_set_flag(root, OF_POPULATED_BUS);

    of_node_put(root);
    return rc;
}
EXPORT_SYMBOL_GPL(of_platform_populate);
2.3, of_platform_bus_create
/**
* of_platform_bus_create() - Create a device for a node and its children.
* @bus: device node of the bus to instantiate
* @matches: match table for bus nodes
* @lookup: auxdata table for matching id and platform_data with device nodes
* @parent: parent for new device, or NULL for top level.
* @strict: require compatible property
*
* Creates a platform_device for the provided device_node, and optionally
* recursively create devices for all the child nodes.
*/
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;

    /* Make sure it has a compatible property */
    //节点中必须有compatible属性
    if (strict && (!of_get_property(bus, "compatible", NULL))) {
        pr_debug("%s() - skipping %pOF, no compatible prop\n",
             __func__, bus);
        return 0;
    }

    /* Skip nodes for which we don't want to create devices */
    //在of_skipped_node_table中的节点不会被转化
    if (unlikely(of_match_node(of_skipped_node_table, bus))) {
        pr_debug("%s() - skipping %pOF node\n", __func__, bus);
        return 0;
    }

    //判断节点是否已经被转化
    if (of_node_check_flag(bus, OF_POPULATED_BUS)) {
        pr_debug("%s() - skipping %pOF, already populated\n",
            __func__, bus);
        return 0;
    }

    auxdata = of_dev_lookup(lookup, bus);
    if (auxdata) {
        bus_id = auxdata->name;
        platform_data = auxdata->platform_data;
    }

    if (of_device_is_compatible(bus, "arm,primecell")) {
        /*
         * Don't return an error here to keep compatibility with older
         * device tree files.
         */
        of_amba_device_create(bus, bus_id, platform_data, parent);
        return 0;
    }

    //创建,初始化,注册一个device
    dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
    //当某个根子节点的compatible属性为"simple-bus"、"simple-mfd"、"isa"、"arm,amba-bus"时,当前节点中的子节点将会被转换成platform_device节点
    if (!dev || !of_match_node(matches, bus))
        return 0;

    //转化当前节点中的子节点
    for_each_child_of_node(bus, child) {
        pr_debug("   create child: %pOF\n", child);
        rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
        if (rc) {
            of_node_put(child);
            break;
        }
    }
    //设置node已经被转化的flag
    of_node_set_flag(bus, OF_POPULATED_BUS);
    return rc;
}
2.4,of_platform_device_create_pdata
/**
* of_platform_device_create_pdata - Alloc, initialize and register an of_device
* @np: pointer to node to create device for
* @bus_id: name to assign device
* @platform_data: pointer to populate platform_data pointer with
* @parent: Linux device model parent device.
*
* Returns pointer to created platform device, or NULL if a device was not
* registered.  Unavailable devices will not get registered.
*/
static struct platform_device *of_platform_device_create_pdata(
                    struct device_node *np,
                    const char *bus_id,
                    void *platform_data,
                    struct device *parent)
{
    struct platform_device *dev;

    //判断设备是否可用 status = okay,并且没有被转化过
    if (!of_device_is_available(np) ||
        of_node_test_and_set_flag(np, OF_POPULATED))
        return NULL;

    //创建并初始化一个platform_device
    dev = of_device_alloc(np, bus_id, parent);
    if (!dev)
        goto err_clear_flag;

    dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
    if (!dev->dev.dma_mask)
        dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
    //device的bus_type
    dev->dev.bus = &platform_bus_type;
    //device的platform_data
    dev->dev.platform_data = platform_data;
    of_msi_configure(&dev->dev, dev->dev.of_node);

    //会调用到device_add(&ofdev->dev),将新转化来的设备添加到设备层次结构中
    if (of_device_add(dev) != 0) {
        platform_device_put(dev);
        goto err_clear_flag;
    }
    return dev;

err_clear_flag:
    of_node_clear_flag(np, OF_POPULATED);
    return NULL;
}
2.5,of_device_alloc
/**
* of_device_alloc - Allocate and initialize an of_device
* @np: device node to assign to device
* @bus_id: Name to assign to the device.  May be null to use default name.
* @parent: Parent device.
*/
struct platform_device *of_device_alloc(struct device_node *np,
                  const char *bus_id,
                  struct device *parent)
{
    struct platform_device *dev;
    int rc, i, num_reg = 0, num_irq;
    struct resource *res, temp_res;

    dev = platform_device_alloc("", PLATFORM_DEVID_NONE);
    if (!dev)
        return NULL;

    /* count the io and irq resources */
    //统计reg属性的数量
    while (of_address_to_resource(np, num_reg, &temp_res) == 0)
        num_reg++;
    //统计中断irq属性的数量
    num_irq = of_irq_count(np);

    /* Populate the resource table */
    //根据num_irq和num_reg的数量申请相应struct resource内存空间
    if (num_irq || num_reg) {
        res = kcalloc(num_irq + num_reg, sizeof(*res), GFP_KERNEL);
        if (!res) {
            platform_device_put(dev);
            return NULL;
        }

        //设置platform_device中num_resources成员
        dev->num_resources = num_reg + num_irq;
        //设置platfrom_device中的resource成员
        dev->resource = res;
        //将device_node中的reg属性转换成platform_device中的struct resource成员
        for (i = 0; i < num_reg; i++, res++) {
            rc = of_address_to_resource(np, i, res);
            WARN_ON(rc);
        }
        //将device_node中的irq属性转换成platform_device中的struct resource成员
        if (of_irq_to_resource_table(np, res, num_irq) != num_irq)
            pr_debug("not all legacy IRQ resources mapped for %pOFn\n",
                 np);
    }

    //将platform_device的dev.of_node成员指针指向device_node
    dev->dev.of_node = of_node_get(np);
    //将platform_device的dev.fwnode成员指针指向device_node的fwnode成员
    dev->dev.fwnode = &np->fwnode;
    //设备parent为platform_bus, struct device platform_bus
    dev->dev.parent = parent ? : &platform_bus;

    //构造platform device ->device 的名字
    if (bus_id)
        dev_set_name(&dev->dev, "%s", bus_id);
    else
        of_device_make_bus_id(&dev->dev);

    return dev;
}
EXPORT_SYMBOL(of_device_alloc);

首先,函数先统计设备树中reg属性和中断irq属性的个数,然后分别为它们申请内存空间,链入到platform_device中的struct resources成员中。除了设备树中"reg"和"interrupt"属性之外,还有可选的"reg-names"和"interrupt-names"这些io中断资源相关的设备树节点属性也在这里被转换。

将相应的设备树节点生成的device_node节点链入到platform_device的dev.of_node中。

四,什么样的device_node会被解析成platform_device

首先,对于所有的device_node,如果要转换成platform_device,必须满足以下条件:

  •  一般情况下,只对设备树中根的子节点进行转换,也就是子节点的子节点并不处理。但是存在一种特殊情况,就是当某个根子节点的compatible属性为"simple-bus"、"simple-mfd"、"isa"、"arm,amba-bus"时,当前节点中的子节点将会被转换成platform_device节点。
  • 节点中必须有compatible属性。
const struct of_device_id of_default_bus_match_table[] = {
    { .compatible = "simple-bus", },
    { .compatible = "simple-mfd", },
    { .compatible = "isa", },
#ifdef CONFIG_ARM_AMBA
    { .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
    {} /* Empty terminated list */
};

五,DeviceTree Kernel API

kernel中获取dts的资源可以参考如下链接提供的API函数:

DeviceTree Kernel API — The Linux Kernel documentation

参考链接:

https://www.cnblogs.com/downey-blog/p/10486568.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/272771.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

部署高斯喷射项目gaussian-splatting

硬件要求 支持 CUDA 的 GPU&#xff0c;具有 7.0 的计算能力24 GB VRAM 软件要求 Conda用于 PyTorch 扩展的 C 编译器&#xff08;Visual Studio 2019&#xff09; CUDA SDK 11 for PyTorch 扩展&#xff0c;在 Visual Studio 之后安装C 编译器和 CUDA SDK 必须兼容 拉取源码 …

NetSuite多脚本性能研究

在项目中&#xff0c;随着复杂度的提升&#xff0c;客制脚本以及各类SuiteAPP的应用&#xff0c;导致某个对象上挂载的脚本大量增加&#xff0c;最终导致了性能问题。表现在保存单据时时间过长&#xff0c;严重影响人机界面的用户感受。基于此问题&#xff0c;我们开展了NetSui…

zookeeper快速入门五:用zookeeper实现服务注册与发现中心

系列&#xff1a; zookeeper快速入门一&#xff1a;zookeeper安装与启动-CSDN博客 zookeeper快速入门二&#xff1a;zookeeper基本概念-CSDN博客 zookeeper快速入门三&#xff1a;zookeeper的基本操作 zookeeper快速入门四&#xff1a;在java客户端中操作zookeeper-CSDN博客…

【数字孪生】Nginx发布数字孪生三维建模模型服务及调用方法

【数字孪生】Nginx发布数字孪生三维建模模型服务及调用方法 一、需求二、实施步骤2.1 准备模型文件2.1.1 3D tiles模型2.1.2 3D Tiles标准文件格式 2.2 配置nginx server块2.2.1 Nginx能干啥 2.3 访问 三、实现效果 一、需求 利用三维渲染引擎Cesium加载3D tiles模型。 二、实…

基于Linux内核的socket编程(TCP)的C语言示例

原文地址&#xff1a;https://www.geeksforgeeks.org/socket-programming-cc/ 服务端&#xff1a; #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <unistd.h>#…

常用的 C# 第三方开发库

C# 的第三方开发库丰富多样&#xff0c;涵盖了各种领域&#xff0c;包括图形界面、数据处理、网络通讯、游戏开发等。以下是一些常用的 C# 第三方开发库&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。…

.Net使用ElasticSearch

文章目录 前言主体内容一.Kibana中ElasticSearch的基础操作1.GET&#xff08;查询&#xff09;1.POST&#xff08;新增&#xff09;1.PUT&#xff08;修改&#xff09;1.DELET&#xff08;删除&#xff09; 二.在.Net中&#xff0c;对ElasticSearch进行基础操作1.DotNet连接Ela…

(黑马出品_高级篇_03)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式

&#xff08;黑马出品_高级篇_03&#xff09;SpringCloudRabbitMQDockerRedis搜索分布式 微服务技术——多级缓存 今日目标1.什么是多级缓存2.JVM进程缓存2.1.导入案例2.1.1.安装MySQL2.1.1.1.准备目录2.1.1.2.运行命令2.1.1.3.修改配置 2.1.1.4.…

ICBatlas数据库-转录组免疫检查点阻断疗法数据

ICBatlas: A Comprehensive Resource for Depicting Immune Checkpoint Blockade Therapy Characteristics from Transcriptome Profiles 介绍&#xff1a;在线ICBatlas (hust.edu.cn) 检查点阻断 &#xff08;ICB&#xff09; 疗法为多种癌症类型提供了显着的临床益处。目前…

使用BBDown下载bilibili视频的方法

一款命令行式哔哩哔哩下载器. Bilibili Downloader. 下载地址 https://github.com/nilaoda/BBDown 功能 番剧下载(Web|TV|App) 课程下载(Web) 普通内容下载(Web|TV|App) 合集/列表/收藏夹/个人空间解析 多分P自动下载 选择指定分P进行下载 选择指定清晰度进行下载 下载外挂字幕…

sentinel黑白名单权限控制

黑白名单权限控制 规则配置 规则创建 创建一个 AuthorityRule 规则对象三个关键要素 setStrategy: 黑白名单类型setResource: 规则和资源的绑定关系setLimitApp: 限制的来源 调用 AuthorityRuleManager.loadRules()加载规则 监听器实例化和管理 AuthorityPropertyListener…

物联网协议模块快速选择实用入门

本文针对非物联网通讯专业的开发者提供快捷入门介绍&#xff0c;从而迅速进入开发阶段。涉及具体性能参数有不严谨处&#xff0c;还请结合实际项目考量。 按传输距离分类综合特性对比运营方式对比场景应用案例 按传输距离分类 综合特性对比 运营方式对比 场景应用案例

在Docker上传我们自己的镜像(以springboot项目为例)

首先确定好在我们的centOS服务器上已经安装并配置好docker 配置自己的springboot镜像并运行 获取springboot的jar包 maven clean--》mavenue package --》复制target目录下生成的jar包 在服务器选择一个文件夹上传jar包&#xff0c;我这里选用的文件夹叫做/opt/dockertest…

Gin 框架中实现路由的几种方式介绍

本文将为您详细讲解 Gin 框架中实现路由的几种方式&#xff0c;并给出相应的简单例子。Gin 是一个高性能的 Web 框架&#xff0c;用于构建后端服务。在 Web 应用程序中&#xff0c;路由是一种将客户端请求映射到特定处理程序的方法。以下是几种常见的路由实现方式&#xff1a; …

【C语言】linux内核软中断

一、什么是软中断&#xff1f; 内核中的软中断&#xff08;Softirqs&#xff09;和任务下半部&#xff08;Tasklets&#xff09;是Linux内核中用于在中断上下文之外处理中断服务的一种底层机制。这些机制解决了不能在中断服务例程&#xff08;ISR&#xff09;中执行耗时操作或…

主播美颜技术原理与应用探究:美颜SDK的前沿技术解析

美颜SDK的出现和不断进步&#xff0c;起到了举足轻重的作用。本文将深入探讨主播美颜技术的原理与应用&#xff0c;以及美颜SDK的前沿技术解析。 一、美颜技术的背后原理 美颜技术的实现并非简单的图像处理&#xff0c;而是涉及到计算机视觉、图像处理、人工智能等多个领域的综…

python for循环打印字符串、指定区间范围数字以及打印区间数字内的奇数和偶数

1.一串字符&#xff0c;循环打印&#xff1a; 源码&#xff1a; strings"Python" n0 print("字符长度为&#xff1a;"str(len(strings))) for a in strings: nn1 print ("第"str(n)"位是&#xff1a;"a)打印结果&#xff1a…

docxTemplater——从word模板生成docx文件

官网文档&#xff1a;Get Started (Browser) | docxtemplater 官网在线演示&#xff1a;Demo of Docxtemplater with all modules active | docxtemplater 源码&#xff1a;https://github.com/open-xml-templating/docxtemplater 不仅可以处理word&#xff08;免费&#xf…

Jetson orin nano 8G设置ROS下launch文件开机自启动脚本文件

一、新建xxx.sh文件&#xff0c;将以下文件复制&#xff0c;需要根据自己的工作空间修改 ###gmapping with abot### gnome-terminal --window -e bash -c "source ~/cwkj_ws/devel/setup.bash;roslaunch robot_bringup px4.launch; exec bash" \ --tab -e bash -c &…

废狗的Python连接MySQL之旅

我想本地用python连接mysql中的表&#xff0c;但是我不会。。。。 MySQL 安装mysql MySQL下载与安装-CSDN博客 安装可视化界面navicat 链接&#xff1a;https://pan.baidu.com/s/1esCF7W1Xwh1kIiw0OmKtjg 提取码&#xff1a;f4s0 开启mysql服务 net start mysql //开启 …
最新文章