一、Linux Platform驱动程序框架

一、概述

总线驱动模型包括总线、设备、驱动。总线即连接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

在介绍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 };
kset

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

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

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 }
kobject_add_internal

之前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_init

在注册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 };
platform_bus_type

其认为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 }
platform_match

总结:在linux初始化阶段,会创建device,bus设备文件,之后创建platform_device和platform_bus,并将其挂载在device和bus节点下,而platform设备驱动只需完成device和drv的定义,并将device挂载到platform_device下,driver注册时,platform的platform_match会判断此driver是哪一个设备的驱动。

 

 

一、Linux Platform驱动程序框架,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。