詳解Linux2.6內核中基於platform機制的驅動模型

原文地址: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/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結構體的原型定義,在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/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);

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/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綁定起來。

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綁定起來。

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);
 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}

263,如果該設備尚沒有匹配的driver,則嘗試匹配。

http://lxr.linux.no/#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總線,其匹配過程如下:
http://lxr.linux.no/#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接口。
http://lxr.linux.no/#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}
 154

如果bus和driver同時具備probe方法,則優先調用總線的probe函數。否則調用device_driver的probe函數,此probe函數是經過各種類型的driver重載的函數,這就實現了利用基類的統一方法來實現不同的功能。對於platform_driver來說,其就是:
http://lxr.linux.no/#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
•    操作相關設備

 
圖 platform機制開發驅動流程

以S3C24xx平臺爲例,來簡單講述下platform驅動的實現流程。
8.1    初始化platform_bus
Platform總線的初始化是在platform_bus_init()完成的,代碼如下:
http://lxr.linux.no/#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之前初始化,那麼這是如何實現的呢?

http://lxr.linux.no/#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

http://lxr.linux.no/#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
http://lxr.linux.no/#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。
http://lxr.linux.no/#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 <[email protected]> */
 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)
http://lxr.linux.no/#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修飾的函數。

http://lxr.linux.no/#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

各個段的優先級由鏈接腳本定義
http://lxr.linux.no/#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 = .;

http://lxr.linux.no/#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。

http://lxr.linux.no/#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
http://lxr.linux.no/#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如下:
http://lxr.linux.no/#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設備的機制。

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/sailor_8318/archive/2010/01/29/5267698.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章