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

  1. http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L609

  2. 609struct bus_type platform_bus_type = {

  3. 610 .name = "platform",

  4. 611 .dev_attrs = platform_dev_attrs,

  5. 612 .match = platform_match,

  6. 613 .uevent = platform_uevent,

  7. 614 .suspend = platform_suspend,

  8. 615 .suspend_late = platform_suspend_late,

  9. 616 .resume_early = platform_resume_early,

  10. 617 .resume = platform_resume,

  11. 618};

  12. 619EXPORT_SYMBOL_GPL(platform_bus_type);

  13. http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L55

  14. 55struct bus_type {

  15. 56 const char *name;

  16. 57 struct bus_attribute *bus_attrs;

  17. 58 struct device_attribute *dev_attrs;

  18. 59 struct driver_attribute *drv_attrs;

  19. 60

  20. 61 int (*match)(struct device *dev, struct device_driver *drv);

  21. 62 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

  22. 63 int (*probe)(struct device *dev);

  23. 64 int (*remove)(struct device *dev);

  24. 65 void (*shutdown)(struct device *dev);

  25. 66

  26. 67 int (*suspend)(struct device *dev, pm_message_t state);

  27. 68 int (*suspend_late)(struct device *dev, pm_message_t state);

  28. 69 int (*resume_early)(struct device *dev);

  29. 70 int (*resume)(struct device *dev);

  30. 71

  31. 72 struct bus_type_private *p;

  32. 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中,定義原型如下:

 

  1. 16struct platform_device {

  2. 17 const char * name; //定義平臺裝置的名稱,此處裝置的命名應和相應驅動程式命名一致

  3. 18 int id;

  4. 19 struct device dev;

  5. 20 u32 num_resources;

  6. 21 struct resource * resource; //定義平臺裝置的資源

  7. 22};



在這個結構裡封裝了struct device及struct resource。可知:platform_device由device派生而來,是一種特殊的device。

下面來看一下platform_device結構體中最重要的一個成員struct resource * resource。struct resource被定義在

  1. http://lxr.linux.no/#linux+v2.6.25/include/linux/ioport.h#L18中,定義原型如下:

  2. 14/*

  3. 15 * Resources are tree-like, allowing

  4. 16 * nesting etc..

  5. 17 */

  6. 18struct resource {

  7. 19 resource_size_t start; //定義資源的起始地址

  8. 20 resource_size_t end; //定義資源的結束地址

  9. 21 const char *name; //定義資源的名稱

  10. 22 unsigned long flags; 定義資源的型別,比如MEM,IO,IRQ,DMA型別

  11. 23 struct resource *parent, *sibling, *child;

  12. 24};



這個結構表示裝置所擁有的資源,即I/O埠、I/O對映記憶體、中斷及DMA等。這裡的地址指的是實體地址。

另外還需要注意platform_device中的device結構,它詳細描述了裝置的情況,其為所有裝置的基類,定義如下:

  1. http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L422

  2. 422struct device {

  3. 423 struct klist klist_children;

  4. 424 struct klist_node knode_parent; /* node in sibling list */

  5. 425 struct klist_node knode_driver;

  6. 426 struct klist_node knode_bus;

  7. 427 struct device *parent;

  8. 428

  9. 429 struct kobject kobj;

  10. 430 char bus_id[BUS_ID_SIZE]; /* position on parent bus */

  11. 431 struct device_type *type;

  12. 432 unsigned is_registered:1;

  13. 433 unsigned uevent_suppress:1;

  14. 434

  15. 435 struct semaphore sem; /* semaphore to synchronize calls to

  16. 436 * its driver.

  17. 437 */

  18. 438

  19. 439 struct bus_type *bus; /* type of bus device is on */

  20. 440 struct device_driver *driver; /* which driver has allocated this

  21. 441 device */

  22. 442 void *driver_data; /* data private to the driver */

  23. 443 void *platform_data; /* Platform specific data, device

  24. 444 core doesn't touch it */

  25. 445 struct dev_pm_info power;

  26. 446

  27. 447#ifdef CONFIG_NUMA

  28. 448 int numa_node; /* NUMA node this device is close to */

  29. 449#endif

  30. 450 u64 *dma_mask; /* dma mask (if dma'able device) */

  31. 451 u64 coherent_dma_mask;/* Like dma_mask, but for

  32. 452 alloc_coherent mappings as

  33. 453 not all hardware supports

  34. 454 64 bit addresses for consistent

  35. 455 allocations such descriptors. */

  36. 456

  37. 457 struct device_dma_parameters *dma_parms;

  38. 458

  39. 459 struct list_head dma_pools; /* dma pools (if dma'ble) */

  40. 460

  41. 461 struct dma_coherent_mem *dma_mem; /* internal for coherent mem

  42. 462 override */

  43. 463 /* arch specific additions */

  44. 464 struct dev_archdata archdata;

  45. 465

  46. 466 spinlock_t devres_lock;

  47. 467 struct list_head devres_head;

  48. 468

  49. 469 /* class_device migration path */

  50. 470 struct list_head node;

  51. 471 struct class *class;

  52. 472 dev_t devt; /* dev_t, creates the sysfs "dev" */

  53. 473 struct attribute_group **groups; /* optional groups */

  54. 474

  55. 475 void (*release)(struct device *dev);

  56. 476};

  57. 477




3    device_register和platform_device_register
 

  1. http://lxr.linux.no/#linux+v2.6.25/drivers/base/core.c#L881

  2. 870/**

  3. 871 * device_register - register a device with the system.

  4. 872 * @dev: pointer to the device structure

  5. 873 *

  6. 874 * This happens in two clean steps - initialize the device

  7. 875 * and add it to the system. The two steps can be called

  8. 876 * separately, but this is the easiest and most common.

  9. 877 * I.e. you should only call the two helpers separately if

  10. 878 * have a clearly defined need to use and refcount the device

  11. 879 * before it is added to the hierarchy.

  12. 880 */

  13. 881int device_register(struct device *dev)

  14. 882{

  15. 883 device_initialize(dev);

  16. 884 return device_add(dev);

  17. 885}

  18. 初始化一個裝置,然後加入到系統中。

  19. http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L325

  20. 316/**

  21. 317 * platform_device_register - add a platform-level device

  22. 318 * @pdev: platform device we're adding

  23. 319 */

  24. 320int platform_device_register(struct platform_device *pdev)

  25. 321{

  26. 322 device_initialize(&pdev->dev);

  27. 323 return platform_device_add(pdev);

  28. 324}

  29. 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總線上。
 

  1. 229/**

  2. 230 * platform_device_add - add a platform device to device hierarchy

  3. 231 * @pdev: platform device we're adding

  4. 232 *

  5. 233 * This is part 2 of platform_device_register(), though may be called

  6. 234 * separately _iff_ pdev was allocated by platform_device_alloc().

  7. 235 */

  8. 236int platform_device_add(struct platform_device *pdev)

  9. 237{

  10. 238 int i, ret = 0;

  11. 239

  12. 240 if (!pdev)

  13. 241 return -EINVAL;

  14. 242

  15. 初始化裝置的parent為platform_bus,初始化驅備的匯流排為platform_bus_type。

  16. 243 if (!pdev->dev.parent)

  17. 244 pdev->dev.parent = &platform_bus;

  18. 245

  19. 246 pdev->dev.bus = &platform_bus_type;

  20. 247

  21. /*++++++++++++++

  22. The platform_device.dev.bus_id is the canonical name for the devices.

  23. It's built from two components:

  24. * platform_device.name ... which is also used to for driver matching.

  25. * platform_device.id ... the device instance number, or else "-1"

  26. to indicate there's only one.

  27. These are concatenated, so name/id "serial"/0 indicates bus_id "serial.0", and

  28. "serial/3" indicates bus_id "serial.3"; both would use the platform_driver

  29. named "serial". While "my_rtc"/-1 would be bus_id "my_rtc" (no instance id)

  30. and use the platform_driver called "my_rtc".

  31. ++++++++++++++*/

  32. 248 if (pdev->id != -1)

  33. 249 snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,

  34. 250 pdev->id);

  35. 251 else

  36. 252 strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);

  37. 253

  38. 設定裝置struct device 的bus_id成員,留心這個地方,在以後還需要用到這個的。

  39. 254 for (i = 0; i < pdev->num_resources; i++) {

  40. 255 struct resource *p, *r = &pdev->resource[i];

  41. 256

  42. 257 if (r->name == NULL)

  43. 258 r->name = pdev->dev.bus_id;

  44. 259

  45. 260 p = r->parent;

  46. 261 if (!p) {

  47. 262 if (r->flags & IORESOURCE_MEM)

  48. 263 p = &iomem_resource;

  49. 264 else if (r->flags & IORESOURCE_IO)

  50. 265 p = &ioport_resource;

  51. 266 }

  52. //resources分為兩種IORESOURCE_MEM和IORESOURCE_IO

  53. //CPU對外設IO埠實體地址的編址方式有兩種:I/O對映方式和記憶體對映方式

  54. 267

  55. 268 if (p && insert_resource(p, r)) {

  56. 269 printk(KERN_ERR

  57. 270 "%s: failed to claim resource %d/n",

  58. 271 pdev->dev.bus_id, i);

  59. 272 ret = -EBUSY;

  60. 273 goto failed;

  61. 274 }

  62. 275 }

  63. 276

  64. 277 pr_debug("Registering platform device '%s'. Parent at %s/n",

  65. 278 pdev->dev.bus_id, pdev->dev.parent->bus_id);

  66. 279

  67. 280 ret = device_add(&pdev->dev);

  68. 281 if (ret == 0)

  69. 282 return ret;

  70. 283

  71. 284 failed:

  72. 285 while (--i >= 0)

  73. 286 if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))

  74. 287 release_resource(&pdev->resource[i]);

  75. 288 return ret;

  76. 289}

  77. 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中,程式碼如下:

  1. 48 struct platform_driver {

  2. 49 int (*probe)(struct platform_device *);

  3. 50 int (*remove)(struct platform_device *);

  4. 51 void (*shutdown)(struct platform_device *);

  5. 52 int (*suspend)(struct platform_device *, pm_message_t state);

  6. 53 int (*suspend_late)(struct platform_device *, pm_message_t state);

  7. 54 int (*resume_early)(struct platform_device *);

  8. 55 int (*resume)(struct platform_device *);

  9. 56 struct device_driver driver;

  10. 57};



可見,它包含了裝置操作的幾個功能函式,同時包含了一個device_driver結構,說明device_driver是platform_driver的基類。驅動程式中需要初始化這個變數。下面看一下這個變數的定義,位於http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L121中:
 

  1. 121struct device_driver {

  2. 122 const char *name;

  3. 123 struct bus_type *bus;

  4. 124

  5. 125 struct module *owner;

  6. 126 const char *mod_name; /* used for built-in modules */

  7. 127

  8. 128 int (*probe) (struct device *dev);

  9. 129 int (*remove) (struct device *dev);

  10. 130 void (*shutdown) (struct device *dev);

  11. 131 int (*suspend) (struct device *dev, pm_message_t state);

  12. 132 int (*resume) (struct device *dev);

  13. 133 struct attribute_group **groups;

  14. 134

  15. 135 struct driver_private *p;

  16. 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檔案中,具體實現程式碼如下:

  1. 439/**

  2. 440 * platform_driver_register

  3. 441 * @drv: platform driver structure

  4. 442 */

  5. 443int platform_driver_register(struct platform_driver *drv)

  6. 444{

  7. 445 drv->driver.bus = &platform_bus_type;

  8. /*設定成platform_bus_type這個很重要,因為driver和device是通過bus聯絡在一起的,具體在本例中是通過 platform_bus_type中註冊的回撥例程和屬性來是實現的, driver與device的匹配就是通過 platform_bus_type註冊的回撥例程platform_match ()來完成的。*/

  9. 446 if (drv->probe)

  10. 447 drv->driver.probe = platform_drv_probe;

  11. //在really_probe函式中,回調了platform_drv_probe函式

  12. 448 if (drv->remove)

  13. 449 drv->driver.remove = platform_drv_remove;

  14. 450 if (drv->shutdown)

  15. 451 drv->driver.shutdown = platform_drv_shutdown;

  16. 452 if (drv->suspend)

  17. 453 drv->driver.suspend = platform_drv_suspend;

  18. 454 if (drv->resume)

  19. 455 drv->driver.resume = platform_drv_resume;

  20. 456 return driver_register(&drv->driver);

  21. 457}

  22. 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()函式。
 

  1. 209/**

  2. 210 * driver_register - register driver with bus

  3. 211 * @drv: driver to register

  4. 212 *

  5. 213 * We pass off most of the work to the bus_add_driver() call,

  6. 214 * since most of the things we have to do deal with the bus

  7. 215 * structures.

  8. 216 */

  9. 217int driver_register(struct device_driver *drv)

  10. 218{

  11. 219 int ret;

  12. 220

  13. //如果匯流排的方法和裝置自己的方法同時存在,將列印告警資訊,對於platform bus,其沒有probe等介面

  14. 221 if ((drv->bus->probe && drv->probe) ||

  15. 222 (drv->bus->remove && drv->remove) ||

  16. 223 (drv->bus->shutdown && drv->shutdown))

  17. 224 printk(KERN_WARNING "Driver '%s' needs updating - please use "

  18. 225 "bus_type methods/n", drv->name);

  19. 226 ret = bus_add_driver(drv);

  20. 227 if (ret)

  21. 228 return ret;

  22. 229 ret = driver_add_groups(drv, drv->groups);

  23. 230 if (ret)

  24. 231 bus_remove_driver(drv);

  25. 232 return ret;

  26. 233}

  27. 234EXPORT_SYMBOL_GPL(driver_register);



226        其主要將驅動掛接到總線上,通過匯流排來驅動裝置。
 

  1. 644/**

  2. 645 * bus_add_driver - Add a driver to the bus.

  3. 646 * @drv: driver.

  4. 647 */

  5. 648int bus_add_driver(struct device_driver *drv)

  6. 649{

  7. 650 struct bus_type *bus;

  8. 651 struct driver_private *priv;

  9. 652 int error = 0;

  10. 653

  11. 654 bus = bus_get(drv->bus);

  12. 655 if (!bus)

  13. 656 return -EINVAL;

  14. 657

  15. 658 pr_debug("bus: '%s': add driver %s/n", bus->name, drv->name);

  16. 659

  17. 660 priv = kzalloc(sizeof(*priv), GFP_KERNEL);

  18. 661 if (!priv) {

  19. 662 error = -ENOMEM;

  20. 663 goto out_put_bus;

  21. 664 }

  22. 665 klist_init(&priv->klist_devices, NULL, NULL);

  23. 666 priv->driver = drv;

  24. 667 drv->p = priv;

  25. 668 priv->kobj.kset = bus->p->drivers_kset;

  26. 669 error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,

  27. 670 "%s", drv->name);

  28. 671 if (error)

  29. 672 goto out_unregister;

  30. 673

  31. 674 if (drv->bus->p->drivers_autoprobe) {

  32. 675 error = driver_attach(drv);

  33. 676 if (error)

  34. 677 goto out_unregister;

  35. 678 }

  36. 679 klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

  37. 680 module_add_driver(drv->owner, drv);

  38. 681

  39. 682 error = driver_create_file(drv, &driver_attr_uevent);

  40. 683 if (error) {

  41. 684 printk(KERN_ERR "%s: uevent attr (%s) failed/n",

  42. 685 __FUNCTION__, drv->name);

  43. 686 }

  44. 687 error = driver_add_attrs(bus, drv);

  45. 688 if (error) {

  46. 689 /* How the hell do we get out of this pickle? Give up */

  47. 690 printk(KERN_ERR "%s: driver_add_attrs(%s) failed/n",

  48. 691 __FUNCTION__, drv->name);

  49. 692 }

  50. 693 error = add_bind_files(drv);

  51. 694 if (error) {

  52. 695 /* Ditto */

  53. 696 printk(KERN_ERR "%s: add_bind_files(%s) failed/n",

  54. 697 __FUNCTION__, drv->name);

  55. 698 }

  56. 699

  57. 700 kobject_uevent(&priv->kobj, KOBJ_ADD);

  58. 701 return error;

  59. 702out_unregister:

  60. 703 kobject_put(&priv->kobj);

  61. 704out_put_bus:

  62. 705 bus_put(bus);

  63. 706 return error;

  64. 707}



如果總線上的driver是自動probe的話,則將該總線上的driver和device繫結起來。
 

  1. http://lxr.linux.no/#linux+v2.6.25/drivers/base/dd.c#L285

  2. 272/**

  3. 273 * driver_attach - try to bind driver to devices.

  4. 274 * @drv: driver.

  5. 275 *

  6. 276 * Walk the list of devices that the bus has on it and try to

  7. 277 * match the driver with each one. If driver_probe_device()

  8. 278 * returns 0 and the @dev->driver is set, we've found a

  9. 279 * compatible pair.

  10. 280 */

  11. 281int driver_attach(struct device_driver *drv)

  12. 282{

  13. 283 return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

  14. 284}

  15. 285EXPORT_SYMBOL_GPL(driver_attach);




掃描該總線上的每一個裝置,將當前driver和總線上的裝置進行match,如果匹配成功,則將裝置和driver繫結起來。
 

  1. 246static int __driver_attach(struct device *dev, void *data)

  2. 247{

  3. 248 struct device_driver *drv = data;

  4. 249

  5. 250 /*

  6. 251 * Lock device and try to bind to it. We drop the error

  7. 252 * here and always return 0, because we need to keep trying

  8. 253 * to bind to devices and some drivers will return an error

  9. 254 * simply if it didn't support the device.

  10. 255 *

  11. 256 * driver_probe_device() will spit a warning if there

  12. 257 * is an error.

  13. 258 */

  14. 259

  15. 260 if (dev->parent) /* Needed for USB */

  16. 261 down(&dev->parent->sem);

  17. 262 down(&dev->sem);

  18. 263 if (!dev->driver)

  19. 264 driver_probe_device(drv, dev);

  20. 265 up(&dev->sem);

  21. 266 if (dev->parent)

  22. 267 up(&dev->parent->sem);

  23. 268

  24. 269 return 0;

  25. 270}



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

  1. http://lxr.linux.no/#linux+v2.6.25/drivers/base/dd.c#L187

  2. 170/**

  3. 171 * driver_probe_device - attempt to bind device & driver together

  4. 172 * @drv: driver to bind a device to

  5. 173 * @dev: device to try to bind to the driver

  6. 174 *

  7. 175 * First, we call the bus's match function, if one present, which should

  8. 176 * compare the device IDs the driver supports with the device IDs of the

  9. 177 * device. Note we don't do this ourselves because we don't know the

  10. 178 * format of the ID structures, nor what is to be considered a match and

  11. 179 * what is not.

  12. 180 *

  13. 181 * This function returns 1 if a match is found, -ENODEV if the device is

  14. 182 * not registered, and 0 otherwise.

  15. 183 *

  16. 184 * This function must be called with @dev->sem held. When called for a

  17. 185 * USB interface, @dev->parent->sem must be held as well.

  18. 186 */

  19. 187int driver_probe_device(struct device_driver *drv, struct device *dev)

  20. 188{

  21. 189 int ret = 0;

  22. 190

  23. 191 if (!device_is_registered(dev))

  24. 192 return -ENODEV;

  25. 193 if (drv->bus->match && !drv->bus->match(dev, drv))

  26. 194 goto done;

  27. 195

  28. 196 pr_debug("bus: '%s': %s: matched device %s with driver %s/n",

  29. 197 drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);

  30. 198

  31. 199 ret = really_probe(dev, drv);

  32. 200

  33. 201done:

  34. 202 return ret;

  35. 203}




193,如果該總線上的裝置需要進行匹配,則驗證是否匹配。對於platform匯流排,其匹配過程如下:

  1. http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L555

  2. 542/**

  3. 543 * platform_match - bind platform device to platform driver.

  4. 544 * @dev: device.

  5. 545 * @drv: driver.

  6. 546 *

  7. 547 * Platform device IDs are assumed to be encoded like this:

  8. 548 * "<name><instance>", where <name> is a short description of the type of

  9. 549 * device, like "pci" or "floppy", and <instance> is the enumerated

  10. 550 * instance of the device, like '0' or '42'. Driver IDs are simply

  11. 551 * "<name>". So, extract the <name> from the platform_device structure,

  12. 552 * and compare it against the name of the driver. Return whether they match

  13. 553 * or not.

  14. 554 */

  15. 555static int platform_match(struct device *dev, struct device_driver *drv)

  16. 556{

  17. 557 struct platform_device *pdev;

  18. 558

  19. 559 pdev = container_of(dev, struct platform_device, dev);

  20. 560 return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);

  21. 561}




560,簡單的進行字串匹配,這也是我們強調platform_device和platform_driver中的name屬性需要一致的原因。

匹配成功後,則呼叫probe介面。

  1. http://lxr.linux.no/#linux+v2.6.25/drivers/base/dd.c#L101

  2. 98static atomic_t probe_count = ATOMIC_INIT(0);

  3. 99static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);

  4. 100

  5. 101static int really_probe(struct device *dev, struct device_driver *drv)

  6. 102{

  7. 103 int ret = 0;

  8. 104

  9. 105 atomic_inc(&probe_count);

  10. 106 pr_debug("bus: '%s': %s: probing driver %s with device %s/n",

  11. 107 drv->bus->name, __FUNCTION__, drv->name, dev->bus_id);

  12. 108 WARN_ON(!list_empty(&dev->devres_head));

  13. 109

  14. 110 dev->driver = drv;

  15. 111 if (driver_sysfs_add(dev)) {

  16. 112 printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n",

  17. 113 __FUNCTION__, dev->bus_id);

  18. 114 goto probe_failed;

  19. 115 }

  20. 116

  21. 117 if (dev->bus->probe) {

  22. 118 ret = dev->bus->probe(dev);

  23. 119 if (ret)

  24. 120 goto probe_failed;

  25. 121 } else if (drv->probe) {

  26. 122 ret = drv->probe(dev);

  27. 123 if (ret)

  28. 124 goto probe_failed;

  29. 125 }

  30. 126

  31. 127 driver_bound(dev);

  32. 128 ret = 1;

  33. 129 pr_debug("bus: '%s': %s: bound device %s to driver %s/n",

  34. 130 drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);

  35. 131 goto done;

  36. 132

  37. 133probe_failed:

  38. 134 devres_release_all(dev);

  39. 135 driver_sysfs_remove(dev);

  40. 136 dev->driver = NULL;

  41. 137

  42. 138 if (ret != -ENODEV && ret != -ENXIO) {

  43. 139 /* driver matched but the probe failed */

  44. 140 printk(KERN_WARNING

  45. 141 "%s: probe of %s failed with error %d/n",

  46. 142 drv->name, dev->bus_id, ret);

  47. 143 }

  48. 144 /*

  49. 145 * Ignore errors returned by ->probe so that the next driver can try

  50. 146 * its luck.

  51. 147 */

  52. 148 ret = 0;

  53. 149done:

  54. 150 atomic_dec(&probe_count);

  55. 151 wake_up(&probe_waitqueue);

  56. 152 return ret;

  57. 153}

  58. 154




如果bus和driver同時具備probe方法,則優先呼叫匯流排的probe函式。否則呼叫device_driver的probe函式,此probe函式是經過各種型別的driver過載的函式,這就實現了利用基類的統一方法來實現不同的功能。對於platform_driver來說,其就是:

  1. http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L394

  2. 394static int platform_drv_probe(struct device *_dev)

  3. 395{

  4. 396 struct platform_driver *drv = to_platform_driver(_dev->driver);

  5. 397 struct platform_device *dev = to_platform_device(_dev);

  6. 398

  7. 399 return drv->probe(dev);

  8. 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()完成的,程式碼如下:

  1. http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L621

  2. 26struct device platform_bus = {

  3. 27 .bus_id = "platform",

  4. 28};

  5. 29EXPORT_SYMBOL_GPL(platform_bus);

  6. 621int __init platform_bus_init(void)

  7. 622{

  8. 623 int error;

  9. 624

  10. 625 error = device_register(&platform_bus);

  11. 626 if (error)

  12. 627 return error;

  13. 628 error = bus_register(&platform_bus_type);

  14. 629 if (error)

  15. 630 device_unregister(&platform_bus);

  16. 631 return error;

  17. 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
 

  1. 14/**

  2. 15 * driver_init - initialize driver model.

  3. 16 *

  4. 17 * Call the driver model init functions to initialize their

  5. 18 * subsystems. Called early from init/main.c.

  6. 19 */

  7. 20void __init driver_init(void)

  8. 21{

  9. 22 /* These are the core pieces */

  10. 23 devices_init();

  11. 24 buses_init();

  12. 25 classes_init();

  13. 26 firmware_init();

  14. 27 hypervisor_init();

  15. 28

  16. 29 /* These are also core pieces, but must come after the

  17. 30 * core core pieces.

  18. 31 */

  19. 32 platform_bus_init();

  20. 33 system_bus_init();

  21. 34 cpu_dev_init();

  22. 35 memory_dev_init();

  23. 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

  1. 724/*

  2. 725 * Ok, the machine is now initialized. None of the devices

  3. 726 * have been touched yet, but the CPU subsystem is up and

  4. 727 * running, and memory and process management works.

  5. 728 *

  6. 729 * Now we can finally start doing some real work..

  7. 730 */

  8. 731static void __init do_basic_setup(void)

  9. 732{

  10. 733 /* drivers will send hotplug events */

  11. 734 init_workqueues();

  12. 735 usermodehelper_init();

  13. 736 driver_init();

  14. 737 init_irq_proc();

  15. 738 do_initcalls();

  16. 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中定義了系統的資源,是一個高度可移植的檔案,大部分板級資源都在這裡集中定義。
 

  1. 274/* I2C */

  2. 275

  3. 276static struct resource s3c_i2c_resource[] = {

  4. 277 [0] = {

  5. 278 .start = S3C24XX_PA_IIC,

  6. 279 .end = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,

  7. 280 .flags = IORESOURCE_MEM,

  8. 281 },

  9. 282 [1] = {

  10. 283 .start = IRQ_IIC,

  11. 284 .end = IRQ_IIC,

  12. 285 .flags = IORESOURCE_IRQ,

  13. 286 }

  14. 287

  15. 288};

  16. 289

  17. 290struct platform_device s3c_device_i2c = {

  18. 291 .name = "s3c2410-i2c",

  19. 292 .id = -1,

  20. 293 .num_resources = ARRAY_SIZE(s3c_i2c_resource),

  21. 294 .resource = s3c_i2c_resource,

  22. 295};

  23. 296

  24. 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將系統資源組織起來,統一註冊進核心。
 

  1. 151static struct platform_device *smdk2440_devices[] __initdata = {

  2. 152 &s3c_device_usb,

  3. 153 &s3c_device_lcd,

  4. 154 &s3c_device_wdt,

  5. 155 &s3c_device_i2c,

  6. 156 &s3c_device_iis,

  7. 157};

  8. 166static void __init smdk2440_machine_init(void)

  9. 167{

  10. 168 s3c24xx_fb_set_platdata(&smdk2440_fb_info);

  11. 169

  12. 170 platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));

  13. 171 smdk_machine_init();

  14. 172}

  15. 173

  16. 174MACHINE_START(S3C2440, "SMDK2440")

  17. 175 /* Maintainer: Ben Dooks <[email protected]> */

  18. 176 .phys_io = S3C2410_PA_UART,

  19. 177 .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

  20. 178 .boot_params = S3C2410_SDRAM_PA + 0x100,

  21. 179

  22. 180 .init_irq = s3c24xx_init_irq,

  23. 181 .map_io = smdk2440_map_io,

  24. 182 .init_machine = smdk2440_machine_init,

  25. 183 .timer = &s3c24xx_timer,

  26. 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

  1. 786arch_initcall(customize_machine);

  2. 787

  3. 788void __init setup_arch(char **cmdline_p)

  4. 789{

  5. 790 struct tag *tags = (struct tag *)&init_tags;

  6. 791 struct machine_desc *mdesc;

  7. 792 char *from = default_command_line;

  8. 793

  9. 794 setup_processor();

  10. 795 mdesc = setup_machine(machine_arch_type);

  11. //根據machine id獲得移植時定義的machine desc結構

  12. 796 machine_name = mdesc->name;

  13. 797

  14. 798 if (mdesc->soft_reboot)

  15. 799 reboot_setup("s");

  16. 800

  17. 801 if (__atags_pointer)

  18. 802 tags = phys_to_virt(__atags_pointer);

  19. 803 else if (mdesc->boot_params)

  20. 804 tags = phys_to_virt(mdesc->boot_params);

  21. 805

  22. 806 /*

  23. 807 * If we have the old style parameters, convert them to

  24. 808 * a tag list.

  25. 809 */

  26. 810 if (tags->hdr.tag != ATAG_CORE)

  27. 811 convert_to_tag_list(tags);

  28. 812 if (tags->hdr.tag != ATAG_CORE)

  29. 813 tags = (struct tag *)&init_tags;

  30. 814

  31. 815 if (mdesc->fixup)

  32. 816 mdesc->fixup(mdesc, tags, &from, &meminfo);

  33. 817

  34. 818 if (tags->hdr.tag == ATAG_CORE) {

  35. 819 if (meminfo.nr_banks != 0)

  36. 820 squash_mem_tags(tags);

  37. 821 save_atags(tags);

  38. 822 parse_tags(tags);

  39. 823 }

  40. 824

  41. 825 init_mm.start_code = (unsigned long) &_text;

  42. 826 init_mm.end_code = (unsigned long) &_etext;

  43. 827 init_mm.end_data = (unsigned long) &_edata;

  44. 828 init_mm.brk = (unsigned long) &_end;

  45. 829

  46. 830 memcpy(boot_command_line, from, COMMAND_LINE_SIZE);

  47. 831 boot_command_line[COMMAND_LINE_SIZE-1] = '/0';

  48. 832 parse_cmdline(cmdline_p, from);

  49. 833 paging_init(&meminfo, mdesc);

  50. 834 request_standard_resources(&meminfo, mdesc);

  51. 835

  52. 836#ifdef CONFIG_SMP

  53. 837 smp_init_cpus();

  54. 838#endif

  55. 839

  56. 840 cpu_init();

  57. 841

  58. 842 /*

  59. 843 * Set up various architecture-specific pointers

  60. 844 */

  61. 845 init_arch_irq = mdesc->init_irq;

  62. 846 system_timer = mdesc->timer;

  63. 847 init_machine = mdesc->init_machine;

  64. //對init_machine指標賦值

  65. 848

  66. 849#ifdef CONFIG_VT

  67. 850#if defined(CONFIG_VGA_CONSOLE)

  68. 851 conswitchp = &vga_con;

  69. 852#elif defined(CONFIG_DUMMY_CONSOLE)

  70. 853 conswitchp = &dummy_con;

  71. 854#endif

  72. 855#endif

  73. 856}

  74. 777static void (*init_machine)(void) __initdata;

  75. 778

  76. 779static int __init customize_machine(void)

  77. 780{

  78. 781 /* customizes platform devices, or adds new ones */

  79. 782 if (init_machine)

  80. 783 init_machine();

  81. 784 return 0;

  82. 785}

  83. 786arch_initcall(customize_machine);



arch_initcall將customize_machine放在特定的段中,系統將在某個地方執行所有的arch_initcall修飾的函式。

http://lxr.linux.no/#linux+v2.6.25/include/linux/init.h#L182

  1. 152#ifndef MODULE //非可載入模組,即編譯連結進核心的程式碼

  2. 153

  3. 154#ifndef __ASSEMBLY__

  4. 155

  5. 156/* initcalls are now grouped by functionality into separate

  6. 157 * subsections. Ordering inside the subsections is determined

  7. 158 * by link order.

  8. 159 * For backwards compatibility, initcall() puts the call in

  9. 160 * the device init subsection.

  10. 161 *

  11. 162 * The `id' arg to __define_initcall() is needed so that multiple initcalls

  12. 163 * can point at the same handler without causing duplicate-symbol build errors.

  13. 164 */

  14. 165

  15. 166#define __define_initcall(level,fn,id) /

  16. 167 static initcall_t __initcall_##fn##id __used /

  17. 168 __attribute__((__section__(".initcall" level ".init"))) = fn

  18. 169

  19. 170/*

  20. 171 * A "pure" initcall has no dependencies on anything else, and purely

  21. 172 * initializes variables that couldn't be statically initialized.

  22. 173 *

  23. 174 * This only exists for built-in code, not for modules.

  24. 175 */

  25. 176#define pure_initcall(fn) __define_initcall("0",fn,0)

  26. 177

  27. 178#define core_initcall(fn) __define_initcall("1",fn,1)

  28. 179#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)

  29. 180#define postcore_initcall(fn) __define_initcall("2",fn,2)

  30. 181#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)

  31. 182#define arch_initcall(fn) __define_initcall("3",fn,3)

  32. 183#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)

  33. 184#define subsys_initcall(fn) __define_initcall("4",fn,4)

  34. 185#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)

  35. 186#define fs_initcall(fn) __define_initcall("5",fn,5)

  36. 187#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)

  37. 188#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)

  38. 189#define device_initcall(fn) __define_initcall("6",fn,6)

  39. 190#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)

  40. 191#define late_initcall(fn) __define_initcall("7",fn,7)

  41. 192#define late_initcall_sy