1. 程式人生 > >linux核心device生成流程

linux核心device生成流程

一、前言

在驅動模型的框架下,裝置驅動的開發主要包含以下兩個步驟: 步驟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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

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

    of_node_put(root);
    return rc;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

程式碼(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;
}

程式碼(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;
}