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; }
相关文章推荐
- Linux Kernel 4.0 RC5 发布!
- Linux 自检和 SystemTap
- 一张图看尽 Linux 内核运行原理
- Linux内核链表实现过程
- PHP内核探索之解释器的执行过程
- 深入理解PHP内核(二)之SAPI探究
- Android中Android Virtual Device(AVD)使用教程
- C++中Semaphore内核对象用法实例
- 一张图看尽Linux内核运行原理
- 深入理解PHP内核(一)
- PHP内核探索之变量
- 深入php内核之php in array
- Apache No space left on device: mod_rewrite: could not create rewrite_log_lock Configuration Failed
- 浅谈Linux内核创建新进程的全过程
- S3C6410 看门狗驱动程序代码
- ARM11 paltform驱动代码完成,最简单的测试直接在装载设备中运行,实现秒读
- 字符设备驱动程序中重要的三个数据结构file_operations、inode、file
- 开源监控工具Hyperic 的 Network Device配置过程
- Linux设备驱动并发控制详解(自旋锁,信号量)
- 修改内核 内存分配 root、文件系统和内核镜像的位置