一、概述
总线驱动模型包括总线、设备、驱动。总线即连接CPU、DDR、EMMC以及各外设部件的一组信号线,按照功能、传输方式(串行还是并行)等可以划分为好几类总线。linux设备和驱动通常需要挂接在一种总线上,典型的如USB、PCI设备。而有些设备并不依赖于特定的总线,如codec设备,基于此,linux引入了Platform驱动程序框架,其虚拟了一根设备总线,称之为platform总线,相应的设备和驱动称为platform device和platform driver。不需要自己定义总线类型,总线设备来加载总线,只需要完成platform的设备和驱动定义及加载即可。
二、Linux Platform创建
在kernel初始化阶段会创建好platform框架。其调用路径为 start_kernel->reset_init->kernel_init->do_basic_setup->driver_init, 在driver_init中完成platform总线的初始化。
1 /** 2 * driver_init - initialize driver model. 3 * 4 * Call the driver model init functions to initialize their 5 * subsystems. Called early from init/main.c. 6 */ 7 void __init driver_init(void) 8 { 9 /* These are the core pieces */ 10 devtmpfs_init(); 11 devices_init(); 12 buses_init(); 13 classes_init(); 14 firmware_init(); 15 hypervisor_init(); 16 17 /* These are also core pieces, but must come after the 18 * core core pieces. 19 */ 20 platform_bus_init(); 21 system_bus_init(); 22 cpu_dev_init(); 23 memory_dev_init(); 24 }
在介绍driver_init函数前,简单的先介绍下kobject和kset对象。
linux中有设备文件这个概念,所有设备都具有对应的文件节点。创建的所有设备、总线都会关联到文件系统中去。这里借用了面向对象的思想,linux将设备、总线抽象成kobj对象,一组类似功能的kobj对象又被抽象成kset,kobj对象与文件对应,kset与目录结构对应,这样就形成了设备文件的树状结构。
kset数据结构定义:
1 struct kset { 2 struct list_head list;/* 具有相同性质的kobj集合*/ 3 spinlock_t list_lock; 4 struct kobject kobj;/*代表了本层kset,是list中所有kobj的父类*/ 5 struct kset_uevent_ops *uevent_ops;?* 处理事件的回调函数*/ 6 };
kobject数据结构定义:
1 struct kobject { 2 const char *name; 3 struct list_head entry; 4 struct kobject *parent;/* 父节点*/ 5 struct kset *kset; /* this->kobj->parent所在的kset */ 6 struct kobj_type *ktype; 7 struct sysfs_dirent *sd; 8 struct kref kref; 9 unsigned int state_initialized:1; 10 unsigned int state_in_sysfs:1; 11 unsigned int state_add_uevent_sent:1; 12 unsigned int state_remove_uevent_sent:1; 13 unsigned int uevent_suppress:1; 14 };
kobject和kset关系图示,转自:http://blog.csdn.net/lijierson8/article/details/5939165
下图所示,kobj:camera,alarm等挂在kset:platform上,而kset:platform本身又是个kobj其挂在devices中。
在driver_init中,首先对device初始化,其中创建了kset:devices,由上图可知,其挂载在sys目录下。
1 int __init devices_init(void) 2 { 3 /* kset_uevent_ops设备添加移除时通知用户态程序处理*/ 4 devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); 5 if (!devices_kset) 6 return -ENOMEM; 7 dev_kobj = kobject_create_and_add("dev", NULL); 8 if (!dev_kobj) 9 goto dev_kobj_err; 10 sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj); 11 if (!sysfs_dev_block_kobj) 12 goto block_kobj_err; 13 sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj); 14 if (!sysfs_dev_char_kobj) 15 goto char_kobj_err; 16 17 return 0; 18 19 char_kobj_err: 20 kobject_put(sysfs_dev_block_kobj); 21 block_kobj_err: 22 kobject_put(dev_kobj); 23 dev_kobj_err: 24 kset_unregister(devices_kset); 25 return -ENOMEM; 26 }
device_init->......->kobject_add_internal,此函数中会调用create_dir(kobj),将kobj与文件系统关联起来。
1 static int kobject_add_internal(struct kobject *kobj) 2 { 3 int error = 0; 4 struct kobject *parent; 5 6 if (!kobj) 7 return -ENOENT; 8 9 if (!kobj->name || !kobj->name[0]) { 10 WARN(1, "kobject: (%p): attempted to be registered with empty " 11 "name!\n", kobj); 12 return -EINVAL; 13 } 14 15 parent = kobject_get(kobj->parent); 16 17 /* join kset if set, use it as parent if we do not already have one */ 18 if (kobj->kset) { 19 if (!parent) 20 parent = kobject_get(&kobj->kset->kobj); 21 /* 将其加入起所在的kset */ 22 kobj_kset_join(kobj); 23 /* 之前parent是空,则从前指定parent*/ 24 kobj->parent = parent; 25 } 26 27 pr_debug("kobject: ‘%s‘ (%p): %s: parent: ‘%s‘, set: ‘%s‘\n", 28 kobject_name(kobj), kobj, __func__, 29 parent ? kobject_name(parent) : "<NULL>", 30 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>"); 31 32 error = create_dir(kobj); 33 if (error) { 34 kobj_kset_leave(kobj); 35 kobject_put(parent); 36 kobj->parent = NULL; 37 38 /* be noisy on error issues */ 39 if (error == -EEXIST) 40 printk(KERN_ERR "%s failed for %s with " 41 "-EEXIST, don‘t try to register things with " 42 "the same name in the same directory.\n", 43 __func__, kobject_name(kobj)); 44 else 45 printk(KERN_ERR "%s failed for %s (%d)\n", 46 __func__, kobject_name(kobj), error); 47 dump_stack(); 48 } else 49 kobj->state_in_sysfs = 1; 50 51 return error; 52 }
之前kset:device初始化好后,接着platform_bus_init即将虚拟的platform_device挂接在kset:device上,虚拟的platform_bus挂接在kset:bus上。
1 int __init platform_bus_init(void) 2 { 3 int error; 4 5 early_platform_cleanup(); 6 7 error = device_register(&platform_bus); 8 if (error) 9 return error; 10 error = bus_register(&platform_bus_type); 11 if (error) 12 device_unregister(&platform_bus); 13 return error; 14 }
在注册platform_bus时,其数据结构中platform_match回调函数,会判断drv和device是否匹配。
1 struct bus_type platform_bus_type = { 2 .name = "platform", 3 .dev_attrs = platform_dev_attrs, 4 .match = platform_match, 5 .uevent = platform_uevent, 6 .pm = &platform_dev_pm_ops, 7 };
其认为drv和device名称一致即匹配成功。
1 static int platform_match(struct device *dev, struct device_driver *drv) 2 { 3 struct platform_device *pdev = to_platform_device(dev); 4 struct platform_driver *pdrv = to_platform_driver(drv); 5 6 /* match against the id table first */ 7 if (pdrv->id_table) 8 return platform_match_id(pdrv->id_table, pdev) != NULL; 9 10 /* fall-back to driver name match */ 11 return (strcmp(pdev->name, drv->name) == 0); 12 }
总结:在linux初始化阶段,会创建device,bus设备文件,之后创建platform_device和platform_bus,并将其挂载在device和bus节点下,而platform设备驱动只需完成device和drv的定义,并将device挂载到platform_device下,driver注册时,platform的platform_match会判断此driver是哪一个设备的驱动。