详解Linux2.6内核中基于platform机制的驱动模型<强烈推荐,这是一篇学习驱动模型千年一遇的好文章>

来源:互联网 发布:手机淘宝查看历史评价 编辑:程序博客网 时间:2024/04/30 08:17

【摘要】本文以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的定义如下:
#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);

#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结构体用来描述设备的名称、资源信息等。该结构被定义在

#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被定义在#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结构,它详细描述了设备的情况,其为所有设备的基类,定义如下:
#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

#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}
初始化一个设备,然后加入到系统中。

#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/n",
 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/n",
 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结构体的原型定义,在

#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的基类。驱动程序中需要初始化这个变量。下面看一下这个变量的定义,位于

#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(),其原型定义在 #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中具体的各种操作都是基于统一的基类接口的,这样就实现了面向对象的设计。

在文件#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/n", 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);

 


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/n", 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/n",
 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/n",
 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/n",
 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绑定起来。

#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绑定起来。

246static int __driver_attach(struct device *dev, void *data)
 247{
 248        struct device_driver *drv = data;
 249
 250        /*
 251         * Lock device and try to bind to it. We drop the error
 252         * here and always return 0, because we need to keep trying
 253         * to bind to devices and some drivers will return an error
 254         * simply if it didn't support the device.
 255         *
 256         * driver_probe_device() will spit a warning if there
 257         * is an error.
 258         */
 259
 260        if (dev->parent)        /* Needed for USB */
 261                down(&dev->parent->sem);
 262        down(&dev->sem);

 


                 //如果该设备尚没有匹配的driver,则尝试匹配。
 263        if (!dev->driver)
 264                driver_probe_device(drv, dev);
 265        up(&dev->sem);
 266        if (dev->parent)
 267                up(&dev->parent->sem);
 268
 269        return 0;
 270}

 


#linux+v2.6.25/drivers/base/dd.c#L187
170/**
 171 * driver_probe_device - attempt to bind device & driver together
 172 * @drv: driver to bind a device to
 173 * @dev: device to try to bind to the driver
 174 *
 175 * First, we call the bus's match function, if one present, which should
 176 * compare the device IDs the driver supports with the device IDs of the
 177 * device. Note we don't do this ourselves because we don't know the
 178 * format of the ID structures, nor what is to be considered a match and
 179 * what is not.
 180 *
 181 * This function returns 1 if a match is found, -ENODEV if the device is
 182 * not registered, and 0 otherwise.
 183 *
 184 * This function must be called with @dev->sem held.  When called for a
 185 * USB interface, @dev->parent->sem must be held as well.
 186 */
 187int driver_probe_device(struct device_driver *drv, struct device *dev)
 188{
 189        int ret = 0;
 190
 191        if (!device_is_registered(dev))
 192                return -ENODEV;
 193        if (drv->bus->match && !drv->bus->match(dev, drv))
 194                goto done;
 195
 196        pr_debug("bus: '%s': %s: matched device %s with driver %s/n",
 197                 drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);
 198
 199        ret = really_probe(dev, drv);
 200
 201done:
 202        return ret;
 203}

193,如果该总线上的设备需要进行匹配,则验证是否匹配。对于platform总线,其匹配过程如下:
#linux+v2.6.25/drivers/base/platform.c#L555
542/**
 543 * platform_match - bind platform device to platform driver.
 544 * @dev: device.
 545 * @drv: driver.
 546 *
 547 * Platform device IDs are assumed to be encoded like this:
 548 * "<name><instance>", where <name> is a short description of the type of
 549 * device, like "pci" or "floppy", and <instance> is the enumerated
 550 * instance of the device, like '0' or '42'.  Driver IDs are simply
 551 * "<name>".  So, extract the <name> from the platform_device structure,
 552 * and compare it against the name of the driver. Return whether they match
 553 * or not.
 554 */
 555static int platform_match(struct device *dev, struct device_driver *drv)
 556{
 557        struct platform_device *pdev;
 558
 559        pdev = container_of(dev, struct platform_device, dev);
 560        return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
 561}

560,简单的进行字符串匹配,这也是我们强调platform_device和platform_driver中的name属性需要一致的原因。

匹配成功后,则调用probe接口。
#linux+v2.6.25/drivers/base/dd.c#L101
  98static atomic_t probe_count = ATOMIC_INIT(0);
  99static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
 100
 101static int really_probe(struct device *dev, struct device_driver *drv)
 102{
 103        int ret = 0;
 104
 105        atomic_inc(&probe_count);
 106        pr_debug("bus: '%s': %s: probing driver %s with device %s/n",
 107                 drv->bus->name, __FUNCTION__, drv->name, dev->bus_id);
 108        WARN_ON(!list_empty(&dev->devres_head));
 109
 110        dev->driver = drv;
 111        if (driver_sysfs_add(dev)) {
 112                printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n",
 113                        __FUNCTION__, dev->bus_id);
 114                goto probe_failed;
 115        }
 116
 117        if (dev->bus->probe) {
 118                ret = dev->bus->probe(dev);
 119                if (ret)
 120                        goto probe_failed;
 121        } else if (drv->probe) {
 122                ret = drv->probe(dev);
 123                if (ret)
 124                        goto probe_failed;
 125        }
 126
 127        driver_bound(dev);
 128        ret = 1;
 129        pr_debug("bus: '%s': %s: bound device %s to driver %s/n",
 130                 drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);
 131        goto done;
 132
 133probe_failed:
 134        devres_release_all(dev);
 135        driver_sysfs_remove(dev);
 136        dev->driver = NULL;
 137
 138        if (ret != -ENODEV && ret != -ENXIO) {
 139                /* driver matched but the probe failed */
 140                printk(KERN_WARNING
 141                       "%s: probe of %s failed with error %d/n",
 142                       drv->name, dev->bus_id, ret);
 143        }
 144        /*
 145         * Ignore errors returned by ->probe so that the next driver can try
 146         * its luck.
 147         */
 148        ret = 0;
 149done:
 150        atomic_dec(&probe_count);
 151        wake_up(&probe_waitqueue);
 152        return ret;
 153}

 

 

如果bus和driver同时具备probe方法,则优先调用总线的probe函数。否则调用device_driver的probe函数,此probe 函数是经过各种类型的driver重载的函数,这就实现了利用基类的统一方法来实现不同的功能。对于platform_driver来说,其就是:
#linux+v2.6.25/drivers/base/platform.c#L394
394static int platform_drv_probe(struct device *_dev)
 395{
 396        struct platform_driver *drv = to_platform_driver(_dev->driver);
 397        struct platform_device *dev = to_platform_device(_dev);
 398
 399        return drv->probe(dev);
 400}

然后调用特定platform_driver所定义的操作方法,这个是在定义某个platform_driver时静态指定的操作接口。

至此,platform_driver成功挂接到platform bus上了,并与特定的设备实现了绑定,并对设备进行了probe处理。

 


6    bus、device及driver三者之间的关系
在数据结构设计上,总线、设备及驱动三者相互关联。

platform device包含device,根据device可以获得相应的bus及driver。

设备添加到总线上后形成一个双向循环链表,根据总线可以获得其上挂接的所有device,进而获得了 platform device。根据device也可以获得驱动该总线上所有设备的相关driver。

platform driver包含driver,根据driver可以获得相应的bus,进而获得bus上所有的device,进一步获得platform device,根据name对driver与platform device进行匹配,匹配成功后将device与相应的driver关联起来,即实现了platform device和platform driver的关联。

匹配成功后调用driver的probe进而调用platform driver的probe,在probe里实现驱动特定的功能。

7    哪些适用于plarform驱动?
platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,这样拥有更好的可移植性。platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。Platform driver通过platform bus获取platform_device。

通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独立的资源(地址总线和IRQs),都可以用 platform_driver来管理,而timer,irq等小系统之内的设备则最好不用platfrom_driver机制。

platform_device最大的特定是CPU直接寻址设备的寄存器空间,即使对于其他总线设备,设备本身的寄存器无法通过CPU总线访问,但总线的controller仍然需要通过platform bus来管理。

总之,platfrom_driver的根本目的是为了统一管理系统的外设资源,为驱动程序提供统一的接口来访问系统资源,将驱动和资源分离,提高程序的可移植性。

8    基于platform总线的驱动开发流程
基于Platform总线的驱动开发流程如下:
•    定义初始化platform bus
•    定义各种platform devices
•    注册各种platform devices
•    定义相关platform driver
•    注册相关platform driver
•    操作相关设备

 

 

以S3C24xx平台为例,来简单讲述下platform驱动的实现流程。
8.1    初始化platform_bus
Platform总线的初始化是在platform_bus_init()完成的,代码如下:
#linux+v2.6.25/drivers/base/platform.c#L621
  26struct device platform_bus = {
  27        .bus_id         = "platform",
  28};
  29EXPORT_SYMBOL_GPL(platform_bus);

621int __init platform_bus_init(void)
 622{
 623        int error;
 624
 625        error = device_register(&platform_bus);
 626        if (error)
 627                return error;
 628        error =  bus_register(&platform_bus_type);
 629        if (error)
 630                device_unregister(&platform_bus);
 631        return error;
 632}

该函数创建了一个名为 “platform”的设备,后续platform的设备都会以此为parent。在sysfs中表示为:所有platform类型的设备都会添加在 platform_bus所代表的目录下,即 /sys/devices/platform下面。
-sh-3.1# ls /sys/devices/platform/  
Fixed MDIO bus.0     fsl-i2c.0            serial8250
fsl-ehci.0           fsl-i2c.1            serial8250.0
fsl-gianfar.0        mpc83xx_spi.0        uevent
fsl-gianfar.1        mpc83xx_wdt.0
fsl-gianfar_mdio.-5  power

-sh-3.1# ls /sys/
block/    class/    firmware/ kernel/   power/   
bus/      devices/  fs/       module/  
-sh-3.1# ls /sys/bus/
i2c/         of_platform/ pci_express/ scsi/        usb/        
mdio_bus/    pci/         platform/    spi/        
-sh-3.1# ls /sys/bus/i2c/
devices/           drivers_autoprobe  uevent            
drivers/           drivers_probe   

-sh-3.1# ls /sys/bus/platform/devices/
Fixed MDIO bus.0/    fsl-gianfar_mdio.-5/ mpc83xx_wdt.0/
fsl-ehci.0/          fsl-i2c.0/           serial8250/
fsl-gianfar.0/       fsl-i2c.1/           serial8250.0/
fsl-gianfar.1/       mpc83xx_spi.0/      
-sh-3.1# ls /sys/bus/platform/drivers
drivers/           drivers_autoprobe  drivers_probe     
-sh-3.1# ls /sys/bus/platform/drivers/
fsl-ehci/         fsl-gianfar_mdio/ mpc83xx_spi/      serial8250/
fsl-gianfar/      fsl-i2c/          mpc83xx_wdt/    

platform_bus必须在系统注册任何platform driver和platform device之前初始化,那么这是如何实现的呢?

#linux+v2.6.25/drivers/base/init.c

  14/**
  15 * driver_init - initialize driver model.
  16 *
  17 * Call the driver model init functions to initialize their
  18 * subsystems. Called early from init/main.c.
  19 */
  20void __init driver_init(void)
  21{
  22        /* These are the core pieces */
  23        devices_init();
  24        buses_init();
  25        classes_init();
  26        firmware_init();
  27        hypervisor_init();
  28
  29        /* These are also core pieces, but must come after the
  30         * core core pieces.
  31         */
  32        platform_bus_init();
  33        system_bus_init();
  34        cpu_dev_init();
  35        memory_dev_init();
  36}

init/main.c
start_kernel  》 rest_init  》 kernel_init  》 do_basic_setup》driver_init 》platform_bus_init

#linux+v2.6.25/drivers/base/init.c#L32
724/*
 725 * Ok, the machine is now initialized. None of the devices
 726 * have been touched yet, but the CPU subsystem is up and
 727 * running, and memory and process management works.
 728 *
 729 * Now we can finally start doing some real work..
 730 */
 731static void __init do_basic_setup(void)
 732{
 733        /* drivers will send hotplug events */
 734        init_workqueues();
 735        usermodehelper_init();
 736        driver_init();
 737        init_irq_proc();
 738        do_initcalls();
 739}

platform driver和platform device的初始化是在do_initcalls中进行的。

8.2    定义platform_device
#linux+v2.6.25/arch/arm/plat-s3c24xx/devs.c#L276中定义了系统的资源,是一个高度可移植的文件,大部分板级资源都在这里集中定义。

274/* I2C */
 275
 276static struct resource s3c_i2c_resource[] = {
 277        [0] = {
 278                .start = S3C24XX_PA_IIC,
 279                .end   = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
 280                .flags = IORESOURCE_MEM,
 281        },
 282        [1] = {
 283                .start = IRQ_IIC,
 284                .end   = IRQ_IIC,
 285                .flags = IORESOURCE_IRQ,
 286        }
 287
 288};
 289
 290struct platform_device s3c_device_i2c = {
 291        .name             = "s3c2410-i2c",
 292        .id               = -1,
 293        .num_resources    = ARRAY_SIZE(s3c_i2c_resource),
 294        .resource         = s3c_i2c_resource,
 295};
 296
 297EXPORT_SYMBOL(s3c_device_i2c);

设备名称为s3c2410-i2c,“-1”只有一个i2c设备,两个资源s3c_i2c_resource,分别为i2c控制器的寄存器空间和中断信息。

8.3    注册platform_device

定义了platform_device后,需要添加到系统中,就可以调用函数platform_add_devices。
#linux+v2.6.25/arch/arm/mach-s3c2440/mach-smdk2440.c

smdk2440_devices将系统资源组织起来,统一注册进内核。

151static struct platform_device *smdk2440_devices[] __initdata = {
 152        &s3c_device_usb,
 153        &s3c_device_lcd,
 154        &s3c_device_wdt,
 155        &s3c_device_i2c,
 156        &s3c_device_iis,
 157};

166static void __init smdk2440_machine_init(void)
 167{
 168        s3c24xx_fb_set_platdata(&smdk2440_fb_info);
 169
 170        platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
 171        smdk_machine_init();
 172}
 173
 174MACHINE_START(S3C2440, "SMDK2440")
 175        /* Maintainer: Ben Dooks <ben@fluff.org> */
 176        .phys_io        = S3C2410_PA_UART,
 177        .io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
 178        .boot_params    = S3C2410_SDRAM_PA + 0x100,
 179
 180        .init_irq       = s3c24xx_init_irq,
 181        .map_io         = smdk2440_map_io,
 182        .init_machine   = smdk2440_machine_init,
 183        .timer          = &s3c24xx_timer,
 184MACHINE_END

170        platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
将系统所有资源注册进系统,在此之前platform bus需要初始化成功,否则无法将platform devices挂接到platform bus上。为了保证platform drive初始化时,相关platform资源已经注册进系统,smdk2440_machine_init需要很早执行,而其作为平台初始化 init_machine 时,将优先于系统所有驱动的初始化。

其调用顺序如下:
start_kernel》setup_arch》init_machine》arch_initcall(customize_machine)
#linux+v2.6.25/arch/arm/kernel/setup.c#L788
786arch_initcall(customize_machine);
 787
 788void __init setup_arch(char **cmdline_p)
 789{
 790        struct tag *tags = (struct tag *)&init_tags;
 791        struct machine_desc *mdesc;
 792        char *from = default_command_line;
 793
 794        setup_processor();
 795        mdesc = setup_machine(machine_arch_type);
//根据machine id获得移植时定义的machine desc结构
 796        machine_name = mdesc->name;
 797
 798        if (mdesc->soft_reboot)
 799                reboot_setup("s");
 800
 801        if (__atags_pointer)
 802                tags = phys_to_virt(__atags_pointer);
 803        else if (mdesc->boot_params)
 804                tags = phys_to_virt(mdesc->boot_params);
 805
 806        /*
 807         * If we have the old style parameters, convert them to
 808         * a tag list.
 809         */
 810        if (tags->hdr.tag != ATAG_CORE)
 811                convert_to_tag_list(tags);
 812        if (tags->hdr.tag != ATAG_CORE)
 813                tags = (struct tag *)&init_tags;
 814
 815        if (mdesc->fixup)
 816                mdesc->fixup(mdesc, tags, &from, &meminfo);
 817
 818        if (tags->hdr.tag == ATAG_CORE) {
 819                if (meminfo.nr_banks != 0)
 820                        squash_mem_tags(tags);
 821                save_atags(tags);
 822                parse_tags(tags);
 823        }
 824
 825        init_mm.start_code = (unsigned long) &_text;
 826        init_mm.end_code   = (unsigned long) &_etext;
 827        init_mm.end_data   = (unsigned long) &_edata;
 828        init_mm.brk        = (unsigned long) &_end;
 829
 830        memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
 831        boot_command_line[COMMAND_LINE_SIZE-1] = '/0';
 832        parse_cmdline(cmdline_p, from);
 833        paging_init(&meminfo, mdesc);
 834        request_standard_resources(&meminfo, mdesc);
 835
 836#ifdef CONFIG_SMP
 837        smp_init_cpus();
 838#endif
 839
 840        cpu_init();
 841
 842        /*
 843         * Set up various architecture-specific pointers
 844         */
 845        init_arch_irq = mdesc->init_irq;
 846        system_timer = mdesc->timer;
 847        init_machine = mdesc->init_machine;
//对init_machine指针赋值
 848
 849#ifdef CONFIG_VT
 850#if defined(CONFIG_VGA_CONSOLE)
 851        conswitchp = &vga_con;
 852#elif defined(CONFIG_DUMMY_CONSOLE)
 853        conswitchp = &dummy_con;
 854#endif
 855#endif
 856}

777static void (*init_machine)(void) __initdata;
 778
 779static int __init customize_machine(void)
 780{
 781        /* customizes platform devices, or adds new ones */
 782        if (init_machine)
 783                init_machine();
 784        return 0;
 785}
 786arch_initcall(customize_machine);
arch_initcall将customize_machine放在特定的段中,系统将在某个地方运行所有的arch_initcall修饰的函数。

#linux+v2.6.25/include/linux/init.h#L182
152#ifndef MODULE  //非可加载模块,即编译链接进内核的代码
 153
 154#ifndef __ASSEMBLY__
 155
 156/* initcalls are now grouped by functionality into separate
 157 * subsections. Ordering inside the subsections is determined
 158 * by link order.
 159 * For backwards compatibility, initcall() puts the call in
 160 * the device init subsection.
 161 *
 162 * The `id' arg to __define_initcall() is needed so that multiple initcalls
 163 * can point at the same handler without causing duplicate-symbol build errors.
 164 */
 165
 166#define __define_initcall(level,fn,id) /
 167        static initcall_t __initcall_##fn##id __used /
 168        __attribute__((__section__(".initcall" level ".init"))) = fn
 169
 170/*
 171 * A "pure" initcall has no dependencies on anything else, and purely
 172 * initializes variables that couldn't be statically initialized.
 173 *
 174 * This only exists for built-in code, not for modules.
 175 */
 176#define pure_initcall(fn)               __define_initcall("0",fn,0)
 177
 178#define core_initcall(fn)               __define_initcall("1",fn,1)
 179#define core_initcall_sync(fn)          __define_initcall("1s",fn,1s)
 180#define postcore_initcall(fn)           __define_initcall("2",fn,2)
 181#define postcore_initcall_sync(fn)      __define_initcall("2s",fn,2s)
 182#define arch_initcall(fn)               __define_initcall("3",fn,3)
 183#define arch_initcall_sync(fn)          __define_initcall("3s",fn,3s)
 184#define subsys_initcall(fn)             __define_initcall("4",fn,4)
 185#define subsys_initcall_sync(fn)        __define_initcall("4s",fn,4s)
 186#define fs_initcall(fn)                 __define_initcall("5",fn,5)
 187#define fs_initcall_sync(fn)            __define_initcall("5s",fn,5s)
 188#define rootfs_initcall(fn)             __define_initcall("rootfs",fn,rootfs)
 189#define device_initcall(fn)             __define_initcall("6",fn,6)
 190#define device_initcall_sync(fn)        __define_initcall("6s",fn,6s)
 191#define late_initcall(fn)               __define_initcall("7",fn,7)
 192#define late_initcall_sync(fn)          __define_initcall("7s",fn,7s)
 193
 194#define __initcall(fn) device_initcall(fn)
 195
 196#define __exitcall(fn) /
 197        static exitcall_t __exitcall_##fn __exit_call = fn
 198
。。。。。。。。。
 239#endif /* __ASSEMBLY__ */
 240
 241/**
 242 * module_init() - driver initialization entry point
 243 * @x: function to be run at kernel boot time or module insertion
 244 *
 245 * module_init() will either be called during do_initcalls() (if
 246 * builtin) or at module insertion time (if a module).  There can only
 247 * be one per module.
 248 */
 249#define module_init(x)  __initcall(x);
 250
 251/**
 252 * module_exit() - driver exit entry point
 253 * @x: function to be run when driver is removed
 254 *
 255 * module_exit() will wrap the driver clean-up code
 256 * with cleanup_module() when used with rmmod when
 257 * the driver is a module.  If the driver is statically
 258 * compiled into the kernel, module_exit() has no effect.
 259 * There can only be one per module.
 260 */
 261#define module_exit(x)  __exitcall(x);
 262
 263#else /* MODULE */

各种xx_core_initcall被定义到了不同的分级的段中
所以arch_initcall == __initcall_fn3 它将被链接器放于section  .initcall3.init. 中

module_init()==__initcall(fn)==device_initcall(fn)== __initcall_fn6

各个段的优先级由链接脚本定义
#linux+v2.6.25/include/asm-generic/vmlinux.lds.h#L328
#define INITCALLS       /
   *(.initcall0.init)      /
   *(.initcall0s.init)      /
   *(.initcall1.init)      /
   *(.initcall1s.init)      /
   *(.initcall2.init)      /
   *(.initcall2s.init)      /
   *(.initcall3.init)      /
   *(.initcall3s.init)      /
   *(.initcall4.init)      /
   *(.initcall4s.init)      /
   *(.initcall5.init)      /
   *(.initcall5s.init)      /
 *(.initcallrootfs.init)      /
   *(.initcall6.init)      /
   *(.initcall6s.init)      /
   *(.initcall7.init)      /
   *(.initcall7s.init)

这个__initcall_start是在文件arch/xxx/kernel/vmlinux.lds.S定义的:
__initcall_start = .;
   INITCALLS
  __initcall_end = .;

#linux+v2.6.25/init/main.c#L664
664static void __init do_initcalls(void)
 665{
 666        initcall_t *call;
 667        int count = preempt_count();
 668
 669        for (call = __initcall_start; call < __initcall_end; call++) {
.。。。。
 682
 683                result = (*call)();
 684
。。。 }              
 720        /* Make sure there is no pending stuff from the initcall sequence */
 721        flush_scheduled_work();
 722}

因此__initcall_fnx,数字越小,越先被调用,故arch_initcall优先于module_init所修饰的函数。

arch_initcall修饰的函数的调用顺序如下:
start_kernel  》 rest_init(在setup_arch之后)  》 kernel_init  》 do_basic_setup》do_initcalls(在driver_init()之后),因为platform_bus_init在此之前已经初始化完毕了,便可将设备挂接到总线上了。

8.4    定义platform_driver
Platform bus和设备都定义好了后,需要定义一个platform driver用来驱动此设备。

对于设备来说:
290struct platform_device s3c_device_i2c = {
 291        .name             = "s3c2410-i2c",
 292        .id               = -1,
 293        .num_resources    = ARRAY_SIZE(s3c_i2c_resource),
 294        .resource         = s3c_i2c_resource,
 295};
 296
 297EXPORT_SYMBOL(s3c_device_i2c);

根据platform总线上device和driver的匹配规则可知,I2C 的platform driver的名字是s3c2410-i2c。

#linux+v2.6.25/drivers/i2c/busses/i2c-s3c2410.c#L1
903/* device driver for platform bus bits */
 904
 905static struct platform_driver s3c2410_i2c_driver = {
 906        .probe          = s3c24xx_i2c_probe,
 907        .remove         = s3c24xx_i2c_remove,
 908        .resume         = s3c24xx_i2c_resume,
 909        .driver         = {
 910                .owner  = THIS_MODULE,
 911                .name   = "s3c2410-i2c",
 912        },
 913};

8.5    注册platform_driver
#linux+v2.6.25/drivers/i2c/busses/i2c-s3c2410.c#L1

925static int __init i2c_adap_s3c_init(void)
 926{
 927        int ret;
 928
 929        ret = platform_driver_register(&s3c2410_i2c_driver);
 930        if (ret == 0) {
 931                ret = platform_driver_register(&s3c2440_i2c_driver);
 932                if (ret)
 933                        platform_driver_unregister(&s3c2410_i2c_driver);
 934        }
 935
 936        return ret;
 937}
 938

945module_init(i2c_adap_s3c_init);
 946module_exit(i2c_adap_s3c_exit);

在i2c_adap_s3c_init中注册s3c2410_i2c_driver,那么i2c_adap_s3c_init何时执行的呢?module_init(i2c_adap_s3c_init)表明其存放在initcall段,调用顺序如下:
init/main.c
start_kernel  》 rest_init  》 kernel_init  》 do_basic_setup》do_initcalls,因为platform_bus_init在此之前已经初始化完毕了,且设备已经注册到内核中了,驱动将和内核绑定,并最终调用s3c24xx_i2c_probe。

748/* s3c24xx_i2c_probe
 749 *
 750 * called by the bus driver when a suitable device is found
 751*/
 752
 753static int s3c24xx_i2c_probe(struct platform_device *pdev)
 754{
 755        struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
 756        struct resource *res;
 757        int ret;
 758
 759        /* find the clock and enable it */
 760
 761        i2c->dev = &pdev->dev;
 762        i2c->clk = clk_get(&pdev->dev, "i2c");
 763        if (IS_ERR(i2c->clk)) {
 764                dev_err(&pdev->dev, "cannot get clock/n");
 765                ret = -ENOENT;
 766                goto err_noclk;
 767        }
 768
 769        dev_dbg(&pdev->dev, "clock source %p/n", i2c->clk);
 770
 771        clk_enable(i2c->clk);
 772
 773        /* map the registers */
 774
 775        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 776        if (res == NULL) {
 777                dev_err(&pdev->dev, "cannot find IO resource/n");
 778                ret = -ENOENT;
 779                goto err_clk;
 780        }
 781
 782        i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
 783                                         pdev->name);
 784
 785        if (i2c->ioarea == NULL) {
 786                dev_err(&pdev->dev, "cannot request IO/n");
 787                ret = -ENXIO;
 788                goto err_clk;
 789        }
 790
 791        i2c->regs = ioremap(res->start, (res->end-res->start)+1);
 792
 793        if (i2c->regs == NULL) {
 794                dev_err(&pdev->dev, "cannot map IO/n");
 795                ret = -ENXIO;
 796                goto err_ioarea;
 797        }
 798
 799        dev_dbg(&pdev->dev, "registers %p (%p, %p)/n", i2c->regs, i2c->ioarea, res);
 800
 801        /* setup info block for the i2c core */
 802
 803        i2c->adap.algo_data = i2c;
 804        i2c->adap.dev.parent = &pdev->dev;
 805
 806        /* initialise the i2c controller */
 807
 808        ret = s3c24xx_i2c_init(i2c);
 809        if (ret != 0)
 810                goto err_iomap;
 811
 812        /* find the IRQ for this unit (note, this relies on the init call to
 813         * ensure no current IRQs pending
 814         */
 815
 816        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 817        if (res == NULL) {
 818                dev_err(&pdev->dev, "cannot find IRQ/n");
 819                ret = -ENOENT;
 820                goto err_iomap;
 821        }
 822
 823        ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,
 824                          pdev->name, i2c);
 825
 826        if (ret != 0) {
 827                dev_err(&pdev->dev, "cannot claim IRQ/n");
 828                goto err_iomap;
 829        }
 830
 831        i2c->irq = res;
 832               
 833        dev_dbg(&pdev->dev, "irq resource %p (%lu)/n", res,
 834                (unsigned long)res->start);
 835
 836        ret = i2c_add_adapter(&i2c->adap);
 837        if (ret < 0) {
 838                dev_err(&pdev->dev, "failed to add bus to i2c core/n");
 839                goto err_irq;
 840        }
 841
 842        platform_set_drvdata(pdev, i2c);
 843
 844        dev_info(&pdev->dev, "%s: S3C I2C adapter/n", i2c->adap.dev.bus_id);
 845        return 0;
 846
 847 err_irq:
 848        free_irq(i2c->irq->start, i2c);
 849
 850 err_iomap:
 851        iounmap(i2c->regs);
 852
 853 err_ioarea:
 854        release_resource(i2c->ioarea);
 855        kfree(i2c->ioarea);
 856
 857 err_clk:
 858        clk_disable(i2c->clk);
 859        clk_put(i2c->clk);
 860
 861 err_noclk:
 862        return ret;
 863}

当进入probe函数后,需要获取设备的资源信息,常用获取资源的函数主要是:
struct resource * platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);
根据参数type所指定类型,例如IORESOURCE_MEM,来获取指定的资源。
struct int platform_get_irq(struct platform_device *dev, unsigned int num);
获取资源中的中断号。
struct resource * platform_get_resource_byname(struct platform_device *dev, unsigned int type, char *name);
根据参数name所指定的名称,来获取指定的资源。
int platform_get_irq_byname(struct platform_device *dev, char *name);
根据参数name所指定的名称,来获取资源中的中断号。

此probe函数获取物理IO空间,通过request_mem_region和ioremap等操作物理地址转换成内核中的虚拟地址,初始化I2C控制器,通过platform_get_irq或platform_get_resource得到设备的中断号以后,就可以调用request_irq函数来向系统注册中断,并将此I2C控制器添加到系统中。

8.6    操作设备
进行了platform_device_register 和platform_driver_register后,驱动的相应信息就出现在sys目录的相应文件夹下,然后,我们该如何调用设备呢??怎么对设备进行打开读写等操作呢???

Platform总线只是为了方便管理挂接在CPU总线上的设备,与用户空间的交互,如读写还是需要利用file_operations。当然如果此platform设备无需和用户空间交互,则无需file_operations实例。

对于I2C总线来说,其file_operations如下:
#linux+v2.6.25/drivers/i2c/i2c-core.c#L461
 478static const struct file_operations i2cdev_fops = {
 479        .owner          = THIS_MODULE,
 480        .llseek         = no_llseek,
 481        .read           = i2cdev_read,
 482        .write          = i2cdev_write,
 483        .ioctl          = i2cdev_ioctl,
 484        .open           = i2cdev_open,
 485        .release        = i2cdev_release,
 486};

其和platform bus的区别在于,platform bus提供机制访问I2C 控制器本身的资源,而I2C总线提供访问I2C 控制器上挂接的I2C设备的机制。



转自:http://blog.csdn.net/zhengmeifu/article/details/6124558

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 顺产侧切两个月之后同房感染怎么办 顺产40天还有暗红色的恶露怎么办 来姨妈了没带卫生棉条去游泳怎么办 母猪肚子里面的小猪下不出来怎么办 刚生小猪的母猪肚子胀怎么办 吃了两天中药肚子还疼怎么办 怀孕期间垫了脚够东西怎么办 月经期垫卫生巾有边红肿有疹怎么办 四十天拉今天恶露特别多怎么办 顺产侧切出院几天后伤口裂开怎么办 产后十几撕裂用卫生巾疼怎么办 婴儿绑肚脐的棉黏在肚脐上怎么办 割完双眼皮第五天了很痒怎么办 自体脂肪丰胸做完半年有团块怎么办 假体隆胸一个月了躺着睡很硬怎么办 假体隆胸半月俩胸大小不一样怎么办 假体隆胸拆线后还是起不来床怎么办 假体隆胸术后6天 天天胀痛怎么办 阴部大腿根长了个疙瘩有点痛怎么办 加盟费交了总部不做事怎么办 仓鼠从桌子摔下来后走路别扭怎么办 习惯了一个人天天找你聊天怎么办 来完月经外阴湿疹就严重怎么办 宝宝不肯喝开水尿黄黄的怎么办 做阴超做一半发现忘记换套了怎么办 穿裙子时拉链总往下掉怎么办 子宫内膜厚姨妈来的久怎么办 胸小胸罩往上跑肩带往两边掉怎么办 白衣服弄上姨妈血了洗了变黄怎么办 货物丢失了我感觉没丢这么多怎么办 想穿短裙 但是膝盖怕凉怎么办 被老公看到内裤很脏还有屎怎么办 排卵期同房了不知道怀没怀孕怎么办 清理空调时湿纸巾被卷进去怎么办 超市买的尿不湿质量太差怎么办 把卫生巾和衣服一起洗了怎么办 全面屏面对vo华为手机怎么办屏 雅漾喷雾的喷头坏了怎么办 悦诗风吟水里面有小颗粒怎么办 林肯mkz钥匙锁在车内怎么办 八四消毒液弄到衣服上怎么办