您的位置:首页 > 运维架构 > Linux

linux内核device生成流程

2016-05-13 11:14 399 查看

一、前言

在驱动模型的框架下,设备驱动的开发主要包含以下两个步骤:

步骤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("/");            ---(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)解析:

从前面传入的参数可知,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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息