基於platform機制的驅動模型

 1、  哪些適用於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的根本目的是爲了統一管理系統的外設資源,爲驅動程序提供統一的接口來訪問系統資源,將驅動和資源分離,提高程序的可移植性。

2、    基於platform總線的驅動開發流程
基於Platform總線的驅動開發流程如下:
•    定義初始化platform bus
•    定義各種platform devices
•    註冊各種platform devices
•    定義相關platform driver
•    註冊相關platform driver
•    操作相關設備
3、   何謂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操作方法進行匹配。
 
4    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裏實現驅動特定的功能。
5、    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. struct platform_device {
  2.           const char * name; //定義平臺設備的名稱,此處設備的命名應和相應驅動程序命名一致

  3.         int id;
  4.         struct device dev;
  5.          u32 num_resources;
  6.           struct resource * resource; //定義平臺設備的資源
  7.   };
在這個結構裏封裝了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中,定義原型如下:
  1. struct resource {
  2.           resource_size_t start; //定義資源的起始地址
  3.           resource_size_t end; //定義資源的結束地址
  4.         const char *name; //定義資源的名稱
  5.           unsigned long flags; 定義資源的類型,比如MEM,IO,IRQ,DMA類型
  6.           struct resource *parent, *sibling, *child;
  7.   };
這個結構表示設備所擁有的資源,即I/O端口、I/O映射內存、中斷及DMA等。這裏的地址指的是物理地址。
6、device_register和platform_device_register

 
  1. int device_register(struct device *dev)
  2.  {
  3.          device_initialize(dev);
  4.          return device_add(dev);
  5.  }
  6. //初始化一個設備,然後加入到系統中

  7. int platform_device_register(struct platform_device *pdev)
  8.  {
  9.          device_initialize(&pdev->dev);
  10.          return platform_device_add(pdev);
  11. }
  12.  EXPORT_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總線上。
device_register()和platform_device_register()都會首先初始化設備
區別在於第二步:其實platform_device_add()包括device_add(),不過要先註冊resources,然後將設備掛接到特定的platform總線。
 
7    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. struct platform_driver {
  2.           int (*probe)(struct platform_device *);
  3.           int (*remove)(struct platform_device *);
  4.          void (*shutdown)(struct platform_device *);
  5.           int (*suspend)(struct platform_device *, pm_message_t state);
  6.           int (*suspend_late)(struct platform_device *, pm_message_t state);
  7.           int (*resume_early)(struct platform_device *);
  8.           int (*resume)(struct platform_device *);
  9.           struct device_driver driver;
  10.   };

可見,它包含了設備操作的幾個功能函數,同時包含了一個device_driver結構,說明device_driver是platform_driver的基類。驅動程序中需要初始化這個變量。下面看一下這個變量的定義,位於http://lxr.linux.no/#linux+v2.6.25/include/linux/device.h#L121中:
  1. struct device_driver {
  2.          const char *name;
  3.         struct bus_type *bus;
  4.           struct module *owner;
  5.          const char *mod_name; /* used for built-in modules */
  6.           int (*probe) (struct device *dev);
  7.          int (*remove) (struct device *dev);
  8.          void (*shutdown) (struct device *dev);
  9.          int (*suspend) (struct device *dev, pm_message_t state);
  10.          int (*resume) (struct device *dev);
  11.          struct attribute_group **groups;
  12.           struct driver_private *p;
  13.  };
device_driver提供了一些操作接口,但其並沒有實現,相當於一些虛函數,由派生類platform_driver進行重載,無論何種類型的driver都是基於device_driver派生而來的,具體的各種操作都是基於統一的基類接口的,這樣就實現了面向對象的設計。

需要注意這兩個變量:name和owner。其作用主要是爲了和相關的platform_device關聯起來,owner的作用是說明模塊的所有者,驅動程序中一般初始化爲THIS_MODULE。

device_driver結構中也有一個name變量。platform_driver從字面上來看就知道是設備驅動。設備驅動是爲誰服務的呢?當然是設備了。內核正是通過這個一致性來爲驅動程序找到資源,即 platform_device中的resource。
8    driver_register 和platform_driver_register

內核提供的platform_driver結構體的註冊函數爲platform_driver_register(),其原型定義在http://lxr.linux.no/#linux+v2.6.25/drivers/base/platform.c#L458文件中,具體實現代碼如下:

  1. int platform_driver_register(struct platform_driver *drv)
  2.  {
  3.          drv->driver.bus = &platform_bus_type;
  4.    /*設置成platform_bus_type這個很重要,因爲driver和device是通過bus聯繫在一起的,具體在本例中是通過 platform_bus_type中註冊的回調例程和屬性來是實現的, driver與device的匹配就是通過 platform_bus_type註冊的回調例程platform_match ()來完成的。*/

  5.          if (drv->probe)
  6.                  drv->driver.probe = platform_drv_probe;
  7.  //在really_probe函數中,回調了platform_drv_probe函數

  8.         if (drv->remove)
  9.                  drv->driver.remove = platform_drv_remove;
  10.          if (drv->shutdown)
  11.                  drv->driver.shutdown = platform_drv_shutdown;
  12.          if (drv->suspend)
  13.                  drv->driver.suspend = platform_drv_suspend;
  14.          if (drv->resume)
  15.                  drv->driver.resume = platform_drv_resume;
  16.          return driver_register(&drv->driver);
  17.  }
  18.  EXPORT_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://blog.csdn.net/sailor_8318/article/details/5267698
發佈了6 篇原創文章 · 獲贊 38 · 訪問量 36萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章