详解Linux2.6内核中基于platform机制的驱动模型-

详解Linux2.6内核中基于platform机制的驱动模型-1

原文:http://blog.csdn.net/sailor_8318/archive/2010/01/29/5267698.aspx

【摘要】本文以Linux 2.6.25 内核为例,分析了基于platform总线的驱动模型。首先介绍了Platform总线的基本概念,接着介绍了platform device和platform driver的定义和加载过程,分析了其与基类device 和driver的派生关系及在此过程中面向对象的设计思想。最后以ARM S3C2440中I2C控制器为例介绍了基于platform总线的驱动开发流程。

【关键字】platform_bus, platform_device, resource , platform_driver, file_operations

目录

1    何谓platform bus?    2
2    device和platform_device    3
3    device_register和platform_device_register    5
4    device_driver和platform driver    8
5    driver_register 和platform_driver_register    10
6    bus、device及driver三者之间的关系    17
7    哪些适用于plarform驱动?    18
8    基于platform总线的驱动开发流程    18
8.1    初始化platform_bus    19
8.2    定义platform_device    22
8.3    注册platform_device    22
8.4    定义platform_driver    28
8.5    注册platform_driver    29
8.6    操作设备    32

1    何谓platform bus?
Linux系统中许多部分对设备是如何链接的并不感兴趣,但是他们需要知道哪些类型的设备是可以使用的。设备模型提供了一种机制来对设备进行分类,在更高的功能层面上描述这些设备,并使得这些设备对用户空间可见。因此从2.6内核开始引入了设备模型。

总线是处理器和一个或多个设备之间的通道,在设备模型中, 所有的设备都通过总线相连。总线可以相互插入。设备模型展示了总线和它们所控制的设备之间的实际连接。

Platform总线是2.6 kernel中最近引入的一种虚拟总线,主要用来管理CPU的片上资源,具有更好的移植性,因此在2.6 kernel中,很多驱动都用platform改写了。

platform_bus_type的定义如下:
http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L609 
609struct bus_type platform_bus_type = {
 610        .name           = "platform",
 611        .dev_attrs      = platform_dev_attrs,
 612        .match          = platform_match,
 613        .uevent         = platform_uevent,
 614        .suspend        = platform_suspend,
 615        .suspend_late   = platform_suspend_late,
 616        .resume_early   = platform_resume_early,
 617        .resume         = platform_resume,
 618};
 619EXPORT_SYMBOL_GPL(platform_bus_type);

http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L55
  55struct bus_type {
  56        const char              *name;
  57        struct bus_attribute    *bus_attrs;
  58        struct device_attribute *dev_attrs;
  59        struct driver_attribute *drv_attrs;
  60
  61        int (*match)(struct device *dev, struct device_driver *drv);
  62        int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
  63        int (*probe)(struct device *dev);
  64        int (*remove)(struct device *dev);
  65        void (*shutdown)(struct device *dev);
  66
  67        int (*suspend)(struct device *dev, pm_message_t state);
  68        int (*suspend_late)(struct device *dev, pm_message_t state);
  69        int (*resume_early)(struct device *dev);
  70        int (*resume)(struct device *dev);
  71
  72        struct bus_type_private *p;
  73};

总线名称是"platform",其只是bus_type的一种,定义了总线的属性,同时platform_bus_type还有相关操作方法,如挂起、中止、匹配及hotplug事件等。

总线bus是联系driver和device的中间枢纽。Device通过所属的bus找到driver,由match操作方法进行匹配。

 
Bus、driver及devices的连接关系

2    device和platform_device
Plarform device会有一个名字用于driver binding(在注册driver的时候会查找driver的目标设备的bus位置,这个过程称为driver binding),另外IRQ以及地址空间等资源也要给出 。

platform_device结构体用来描述设备的名称、资源信息等。该结构被定义在http://lxr.linux.no/#linux+v2.6.25/include/linux/platform_device.h#L16中,定义原型如下:

  16struct platform_device {
  17        const char      * name; //定义平台设备的名称,此处设备的命名应和相应驱动程序命名一致

  18        int             id;
  19        struct device   dev;
  20        u32             num_resources;
  21        struct resource * resource;  //定义平台设备的资源
  22};

在这个结构里封装了struct device及struct resource。可知:platform_device由device派生而来,是一种特殊的device。

下面来看一下platform_device结构体中最重要的一个成员struct resource * resource。struct resource被定义在http://lxr.linux.no/#linux+v2.6.25/include/linux/ioport.h#L18中,定义原型如下:
  14/*
  15 * Resources are tree-like, allowing
  16 * nesting etc..
  17 */
  18struct resource {
  19        resource_size_t start;  //定义资源的起始地址
  20        resource_size_t end;  //定义资源的结束地址
  21        const char *name; //定义资源的名称
  22        unsigned long flags; 定义资源的类型,比如MEM,IO,IRQ,DMA类型
  23        struct resource *parent, *sibling, *child;
  24};

这个结构表示设备所拥有的资源,即I/O端口、I/O映射内存、中断及DMA等。这里的地址指的是物理地址。

另外还需要注意platform_device中的device结构,它详细描述了设备的情况,其为所有设备的基类,定义如下:
http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L422
422struct device {
 423        struct klist            klist_children;
 424        struct klist_node       knode_parent;   /* node in sibling list */
 425        struct klist_node       knode_driver;
 426        struct klist_node       knode_bus;
 427        struct device           *parent;
 428
 429        struct kobject kobj;
 430        char    bus_id[BUS_ID_SIZE];    /* position on parent bus */
 431        struct device_type      *type;
 432        unsigned                is_registered:1;
 433        unsigned                uevent_suppress:1;
 434
 435        struct semaphore        sem;    /* semaphore to synchronize calls to
 436                                         * its driver.
 437                                         */
 438
 439        struct bus_type *bus;           /* type of bus device is on */
 440        struct device_driver *driver;   /* which driver has allocated this
 441                                           device */
 442        void            *driver_data;   /* data private to the driver */
 443        void            *platform_data; /* Platform specific data, device
 444                                           core doesn\'t touch it */
 445        struct dev_pm_info      power;
 446
 447#ifdef CONFIG_NUMA
 448        int             numa_node;      /* NUMA node this device is close to */
 449#endif
 450        u64             *dma_mask;      /* dma mask (if dma\'able device) */
 451        u64             coherent_dma_mask;/* Like dma_mask, but for
 452                                             alloc_coherent mappings as
 453                                             not all hardware supports
 454                                             64 bit addresses for consistent
 455                                             allocations such descriptors. */
 456
 457        struct device_dma_parameters *dma_parms;
 458
 459        struct list_head        dma_pools;      /* dma pools (if dma\'ble) */
 460
 461        struct dma_coherent_mem *dma_mem; /* internal for coherent mem
 462                                             override */
 463        /* arch specific additions */
 464        struct dev_archdata     archdata;
 465
 466        spinlock_t              devres_lock;
 467        struct list_head        devres_head;
 468
 469        /* class_device migration path */
 470        struct list_head        node;
 471        struct class            *class;
 472        dev_t                   devt;   /* dev_t, creates the sysfs "dev" */
 473        struct attribute_group  **groups;       /* optional groups */
 474
 475        void    (*release)(struct device *dev);
 476};
 477

3    device_register和platform_device_register

http://lxr.linux.no/#linux+v2.6.25/drivers/base/core.c#L881
 870/**
 871 * device_register - register a device with the system.
 872 * @dev: pointer to the device structure
 873 *
 874 * This happens in two clean steps - initialize the device
 875 * and add it to the system. The two steps can be called
 876 * separately, but this is the easiest and most common.
 877 * I.e. you should only call the two helpers separately if
 878 * have a clearly defined need to use and refcount the device
 879 * before it is added to the hierarchy.
 880 */
 881int device_register(struct device *dev)
 882{
 883        device_initialize(dev);
 884        return device_add(dev);
 885}
初始化一个设备,然后加入到系统中。

http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L325
316/**
 317 * platform_device_register - add a platform-level device
 318 * @pdev: platform device we\'re adding
 319 */
 320int platform_device_register(struct platform_device *pdev)
 321{
 322        device_initialize(&pdev->dev);
 323        return platform_device_add(pdev);
 324}
 325EXPORT_SYMBOL_GPL(platform_device_register);

我们看到注册一个platform device分为了两部分,初始化这个platform_device,然后将此platform_device添加到platform总线中。输入参数platform_device可以是静态的全局设备。

另外一种机制就是动态申请platform_device_alloc一个platform_device设备,然后通过platform_device_add_resources及platform_device_add_data等添加相关资源和属性。

无论哪一种platform_device,最终都将通过platform_device_add这册到platform总线上。

229/**
 230 * platform_device_add - add a platform device to device hierarchy
 231 * @pdev: platform device we\'re adding
 232 *
 233 * This is part 2 of platform_device_register(), though may be called
 234 * separately _iff_ pdev was allocated by platform_device_alloc().
 235 */
 236int platform_device_add(struct platform_device *pdev)
 237{
 238        int i, ret = 0;
 239
 240        if (!pdev)
 241                return -EINVAL;
 242
初始化设备的parent为platform_bus,初始化驱备的总线为platform_bus_type。
 243        if (!pdev->dev.parent)
 244                pdev->dev.parent = &platform_bus;
 245
 246        pdev->dev.bus = &platform_bus_type;
 247
/*++++++++++++++
The platform_device.dev.bus_id is the canonical name for the devices.
It\'s built from two components:

* platform_device.name ... which is also used to for driver matching.
* platform_device.id ... the device instance number, or else "-1"
to indicate there\'s only one.

These are concatenated, so name/id "serial"/0 indicates bus_id "serial.0", and
"serial/3" indicates bus_id "serial.3"; both would use the platform_driver
named "serial". While "my_rtc"/-1 would be bus_id "my_rtc" (no instance id)
and use the platform_driver called "my_rtc".
++++++++++++++*/
 248        if (pdev->id != -1)
 249                snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,
 250                         pdev->id);
 251        else
 252                strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
 253
设置设备struct device 的bus_id成员,留心这个地方,在以后还需要用到这个的。
 254        for (i = 0; i < pdev->num_resources; i++) {
 255                struct resource *p, *r = &pdev->resource[i];
 256
 257                if (r->name == NULL)
 258                        r->name = pdev->dev.bus_id;
 259
 260                p = r->parent;
 261                if (!p) {
 262                        if (r->flags & IORESOURCE_MEM)
 263                                p = &iomem_resource;
 264                        else if (r->flags & IORESOURCE_IO)
 265                                p = &ioport_resource;
 266                }
//resources分为两种IORESOURCE_MEM和IORESOURCE_IO
 //CPU对外设IO端口物理地址的编址方式有两种:I/O映射方式和内存映射方式
 267
 268                if (p && insert_resource(p, r)) {
 269                        printk(KERN_ERR
 270                               "%s: failed to claim resource %d\",
 271                               pdev->dev.bus_id, i);
 272                        ret = -EBUSY;
 273                        goto failed;
 274                }
 275        }
 276
 277        pr_debug("Registering platform device \'%s\'. Parent at %s\",
 278                 pdev->dev.bus_id, pdev->dev.parent->bus_id);
 279
 280        ret = device_add(&pdev->dev);
 281        if (ret == 0)
 282                return ret;
 283
 284 failed:
 285        while (--i >= 0)
 286                if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
 287                        release_resource(&pdev->resource[i]);
 288        return ret;
 289}
 290EXPORT_SYMBOL_GPL(platform_device_add);

由platform_device_register和platform_device_add的实现可知,device_register()和platform_device_register()都会首先初始化设备
区别在于第二步:其实platform_device_add()包括device_add(),不过要先注册resources,然后将设备挂接到特定的platform总线。

4    device_driver和platform driver
Platform device是一种device自己是不会做事情的,要有人为它做事情,那就是platform driver。platform driver遵循linux系统的driver model。对于device的discovery/enumerate都不是driver自己完成的而是由由系统的driver注册机制完成。driver编写人员只要将注册必须的数据结构初始化并调用注册driver的kernel API就可以了。

接下来来看platform_driver结构体的原型定义,在http://lxr.linux.no/#linux+v2.6.25/include/linux/platform_device.h#L48中,代码如下:
48 struct platform_driver {
  49        int (*probe)(struct platform_device *);
  50        int (*remove)(struct platform_device *);
  51        void (*shutdown)(struct platform_device *);
  52        int (*suspend)(struct platform_device *, pm_message_t state);
  53        int (*suspend_late)(struct platform_device *, pm_message_t state);
  54        int (*resume_early)(struct platform_device *);
  55        int (*resume)(struct platform_device *);
  56        struct device_driver driver;
  57};

可见,它包含了设备操作的几个功能函数,同时包含了一个device_driver结构,说明device_driver是platform_driver的基类。驱动程序中需要初始化这个变量。下面看一下这个变量的定义,位于http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L121中:
 
121struct device_driver {
 122        const char              *name;
 123        struct bus_type         *bus;
 124
 125        struct module           *owner;
 126        const char              *mod_name;      /* used for built-in modules */
 127
 128        int (*probe) (struct device *dev);
 129        int (*remove) (struct device *dev);
 130        void (*shutdown) (struct device *dev);
 131        int (*suspend) (struct device *dev, pm_message_t state);
 132        int (*resume) (struct device *dev);
 133        struct attribute_group **groups;
 134
 135        struct driver_private *p;
 136};

device_driver提供了一些操作接口,但其并没有实现,相当于一些虚函数,由派生类platform_driver进行重载,无论何种类型的driver都是基于device_driver派生而来的,具体的各种操作都是基于统一的基类接口的,这样就实现了面向对象的设计。

需要注意这两个变量:name和owner。其作用主要是为了和相关的platform_device关联起来,owner的作用是说明模块的所有者,驱动程序中一般初始化为THIS_MODULE。

device_driver结构中也有一个name变量。platform_driver从字面上来看就知道是设备驱动。设备驱动是为谁服务的呢?当然是设备了。内核正是通过这个一致性来为驱动程序找到资源,即 platform_device中的resource。

5    driver_register 和platform_driver_register

内核提供的platform_driver结构体的注册函数为platform_driver_register(),其原型定义在http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L458文件中,具体实现代码如下:
439/**
 440 * platform_driver_register
 441 * @drv: platform driver structure
 442 */
 443int platform_driver_register(struct platform_driver *drv)
 444{
 445        drv->driver.bus = &platform_bus_type;
   /*设置成platform_bus_type这个很重要,因为driver和device是通过bus联系在一起的,具体在本例中是通过  platform_bus_type中注册的回调例程和属性来是实现的, driver与device的匹配就是通过 platform_bus_type注册的回调例程platform_match ()来完成的。*/

 446        if (drv->probe)
 447                drv->driver.probe = platform_drv_probe;
 //在really_probe函数中,回调了platform_drv_probe函数

448        if (drv->remove)
 449                drv->driver.remove = platform_drv_remove;
 450        if (drv->shutdown)
 451                drv->driver.shutdown = platform_drv_shutdown;
 452        if (drv->suspend)
 453                drv->driver.suspend = platform_drv_suspend;
 454        if (drv->resume)
 455                drv->driver.resume = platform_drv_resume;
 456        return driver_register(&drv->driver);
 457}
 458EXPORT_SYMBOL_GPL(platform_driver_register);

不要被上面的platform_drv_XXX吓倒了,它们其实很简单,就是将struct device转换为struct platform_device和struct platform_driver,然后调用platform_driver中的相应接口函数。那为什么不直接调用platform_drv_XXX等接口呢?这就是Linux内核中面向对象的设计思想。

device_driver提供了一些操作接口,但其并没有实现,相当于一些虚函数,由派生类platform_driver进行重载,无论何种类型的driver都是基于device_driver派生而来的,device_driver中具体的各种操作都是基于统一的基类接口的,这样就实现了面向对象的设计。

在文件http://lxr.linux.no/#linux+v2.6.25/drivers/base/driver.c#L234中,实现了driver_register()函数。

209/**
 210 * driver_register - register driver with bus
 211 * @drv: driver to register
 212 *
 213 * We pass off most of the work to the bus_add_driver() call,
 214 * since most of the things we have to do deal with the bus
 215 * structures.
 216 */
 217int driver_register(struct device_driver *drv)
 218{
 219        int ret;
 220
//如果总线的方法和设备自己的方法同时存在,将打印告警信息,对于platform bus,其没有probe等接口
 221        if ((drv->bus->probe && drv->probe) ||
 222            (drv->bus->remove && drv->remove) ||
 223            (drv->bus->shutdown && drv->shutdown))
 224                printk(KERN_WARNING "Driver \'%s\' needs updating - please use "
 225                        "bus_type methods\", drv->name);
 226        ret = bus_add_driver(drv);
 227        if (ret)
 228                return ret;
 229        ret = driver_add_groups(drv, drv->groups);
 230        if (ret)
 231                bus_remove_driver(drv);
 232        return ret;
 233}
 234EXPORT_SYMBOL_GPL(driver_register);

226        其主要将驱动挂接到总线上,通过总线来驱动设备。

644/**
 645 * bus_add_driver - Add a driver to the bus.
 646 * @drv: driver.
 647 */
 648int bus_add_driver(struct device_driver *drv)
 649{
 650        struct bus_type *bus;
 651        struct driver_private *priv;
 652        int error = 0;
 653
 654        bus = bus_get(drv->bus);
 655        if (!bus)
 656                return -EINVAL;
 657
 658        pr_debug("bus: \'%s\': add driver %s\", bus->name, drv->name);
 659
 660        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 661        if (!priv) {
 662                error = -ENOMEM;
 663                goto out_put_bus;
 664        }
 665        klist_init(&priv->klist_devices, NULL, NULL);
 666        priv->driver = drv;
 667        drv->p = priv;
 668        priv->kobj.kset = bus->p->drivers_kset;
 669        error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
 670                                     "%s", drv->name);
 671        if (error)
 672                goto out_unregister;
 673
 674        if (drv->bus->p->drivers_autoprobe) {
 675                error = driver_attach(drv);
 676                if (error)
 677                        goto out_unregister;
 678        }
 679        klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
 680        module_add_driver(drv->owner, drv);
 681
 682        error = driver_create_file(drv, &driver_attr_uevent);
 683        if (error) {
 684                printk(KERN_ERR "%s: uevent attr (%s) failed\",
 685                        __FUNCTION__, drv->name);
 686        }
 687        error = driver_add_attrs(bus, drv);
 688        if (error) {
 689                /* How the hell do we get out of this pickle? Give up */
 690                printk(KERN_ERR "%s: driver_add_attrs(%s) failed\",
 691                        __FUNCTION__, drv->name);
 692        }
 693        error = add_bind_files(drv);
 694        if (error) {
 695                /* Ditto */
 696                printk(KERN_ERR "%s: add_bind_files(%s) failed\",
 697                        __FUNCTION__, drv->name);
 698        }
 699
 700        kobject_uevent(&priv->kobj, KOBJ_ADD);
 701        return error;
 702out_unregister:
 703        kobject_put(&priv->kobj);
 704out_put_bus:
 705        bus_put(bus);
 706        return error;
 707}

如果总线上的driver是自动probe的话,则将该总线上的driver和device绑定起来。

http://lxr.linux.no/#linux+v2.6.25/drivers/base/dd.c#L285
272/**
 273 * driver_attach - try to bind driver to devices.
 274 * @drv: driver.
 275 *
 276 * Walk the list of devices that the bus has on it and try to
 277 * match the driver with each one.  If driver_probe_device()
 278 * returns 0 and the @dev->driver is set, we\'ve found a
 279 * compatible pair.
 280 */
 281int driver_attach(struct device_driver *drv)
 282{
 283        return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
 284}
 285EXPORT_SYMBOL_GPL(driver_attach);

扫描该总线上的每一个设备,将当前driver和总线上的设备进行match,如果匹配成功,则将设备和driver绑定起来。

你可能感兴趣的:(详解Linux2.6内核中基于platform机制的驱动模型-)