一、前言

在驱动模型的框架下,设备驱动的开发主要包含以下两个步骤:
步骤1:分配一个struct device类型的变量,填充必要的信息后,把它注册到内核中。
步骤2:分配一个struct device_driver类型的变量,填充必要的信息后,把它注册到内核中。
上述两个步骤完成后,内核会在合适的时机(注册device、注册device_driver等)执行probe等回调函数,那么每个设备对应的struct device结构是何时创建的呢?本文主要针对这个问题分析。
注:本文涉及的代码基于linux 3.10版本

二、device初始化流程

对于平台设备等无法热插拔的设备,必须在内核初始化的时候就生成对应的struct device结构体。而对于挂在真正总线(如i2c、usb等)上的设备,struct device结构体由对应的总线驱动在初始化或设备热插拔时生成。本文只对前一种设备做介绍。
内核启动过程中,会调用arm64_device_init函数,代码如下:

const struct of_device_id of_default_bus_match_table[] = {
    { .compatible = "simple-bus", },
#ifdef CONFIG_ARM_AMBA
    { .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
    {} /* Empty terminated list */
};
static int __init arm64_device_init(void)
{
    of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
    return 0;
}
arch_initcall_sync(arm64_device_init);

从arch_initcall_sync的宏定义我们可以知道,arch_initcall_sync的优先级是比较高的,相比于驱动的初始化要早,也就是说在驱动的初始化前,内核已经对平台设备进行了初始化

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("/");            ---(1if (!root)
        return -EINVAL;

    for_each_child_of_node(root, child) {                                   ---(2)
        rc = of_platform_bus_create(child, matches, lookup, parent, true);  ---(3if (rc)
            break;
    }

    of_node_put(root);
    return rc;
}
代码(1)解析:

从前面传入的参数可知,root=NULL,因此执行流程of_find_node_by_path,of_find_node_by_path函数代码如下:

struct device_node *of_find_node_by_path(const char *path)
{
    struct device_node *np = of_allnodes;
    unsigned long flags;

    raw_spin_lock_irqsave(&devtree_lock, flags);
    for (; np; np = np->allnext) {
        if (np->full_name && (of_node_cmp(np->full_name, path) == 0)
            && of_node_get(np))
            break;
    }
    raw_spin_unlock_irqrestore(&devtree_lock, flags);
    return np;
}

of_allnodes我在linux内核device tree的初始化流程中有讲到,就是dts的根节点。

代码(2)解析:

遍历dts中根节点下面的所有子节点。

代码(3)解析:

of_platform_bus_create函数代码如下:

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 */
    if (strict && (!of_get_property(bus, "compatible", NULL))) { //忽略没有compatible属性的节点
        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;
    }

    if (of_device_is_compatible(bus, "arm,primecell")) { //特殊节点处理,不深入
        of_amba_device_create(bus, bus_id, platform_data, parent);
        return 0;
    }
    /*这个函数是真正生成struct device的地方*/
    dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);  
    /*如果compatible属性不是"simple-bus"和"arm,amba-bus"则在返回,不继续遍历子节点。这里我的理解是"simple-bus"和"arm,amba-bus"这两种设备不具备热插拔能力,因此在这里就先创建了struct device*/
    if (!dev || !of_match_node(matches, bus))
        return 0;
    /*对于"simple-bus"和"arm,amba-bus"设备要继续遍历子节点,并创建对应的 struct device*/
    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;
        }
    }
    return rc;
}

具体创建device的代码在of_platform_device_create_pdata中,代码如下:

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;

    if (!of_device_is_available(np))
        return NULL;

    /* of_device_alloc除了分配struct platform_device的内存,还分配了该platform device需要的resource的内存(参考struct platform_device 中的resource成员)。当然,这就需要解析该device node的interrupt资源以及memory address资源,这些资源的原始数据都来自dtb中。*/
    dev = of_device_alloc(np, bus_id, parent);
    if (!dev)
        return NULL;

#if defined(CONFIG_MICROBLAZE)
    dev->archdata.dma_mask = 0xffffffffUL;
#endif
    dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
    if (!dev->dev.dma_mask)
        dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
    dev->dev.bus = &platform_bus_type;
    dev->dev.platform_data = platform_data;

    of_reserved_mem_device_init(&dev->dev);

    if (of_device_add(dev) != 0) {//把这个device加入到设备模型中,后续驱动注册的时候就可以匹配到了
        platform_device_put(dev);
        of_reserved_mem_device_release(&dev->dev);
        return NULL;
    }

    return dev;
}
Logo

更多推荐