一、Linux Platform驱动程序框架

转载请注明转自:http://www.cnblogs.com/connectfuture/

一、概述

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

一、Linux Platform驱动程序框架

下图所示,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)