[概述]
一個現實的Linux設備和驅動通常都需要掛接在一種總線上,對於本身依附於PCI、USB、IIC、SPI等的設備而言,這自然不是問題,但是在嵌入式系統中,SoC系統中集成的獨立的外設控制器,掛接在SoC內存空間的外設(IIC,RTC,SPI,LCD,看門狗)等卻不依附於此類總線。基於這一背景,Linux發明了一種虛擬的總線,稱為platform總線,相應的設備稱為platformdevice,而驅動稱為platform driver。
注意,所謂的platform device並不是與字符設備,塊設備和網絡設備並列的概念,而是Linux系統提供的一種附加手段。
[Platform的兩大好處]
1、使得設備被掛接在一個總線上,因此,符合Linux2.6的設備模型。其結果是,配套的sysfs節點、設備電源管理都成為可能;
2、 隔離BSP和驅動。在BSP中定義platform設備和設備使用的資源、設備的具體配置信息,而在驅動中,只需要通過通用的API去獲取資源和數據,做到了板相關代碼和驅動代碼的分離,使得驅動具有更好的可擴展性和跨平台性。
[platform_device& platform_driver]
- struct platform_device {
- constchar * name; /* 設備名www.linuxidc.com */
- int id; /* 設備ID */
- structdevice dev; /* 內嵌的設備結構體www.linuxidc.com */
- u32 num_resources; /* 設備使用各類資源數量 */
- structresource * resource; /* 資源 */
-
- structplatform_device_id *id_entry;
-
- /*arch specific additions */
- structpdev_archdata archdata;
- };
Platform_device通常在BSP的板文件中實現,在板文件中,將platform_device歸納為一個數組,最終調用platform_add_device()函數統一注冊。
- struct resource {
- resource_size_tstart;
- resource_size_tend;
- constchar *name;
- unsignedlong flags;
- structresource *parent, *sibling, *child;
- };
Platform_device的資源由resource來描述,通常只要關心start、end、flags,分別表示開始值,結束值和類型。Start和end的含義會隨著flags而變更,當flags為IORESOURCE_MEM時,start和end分別表示該platform_device占據內存的開始地址和結束地址;當flags為IORESOURCE_IRQ時,start和end分別表示該platform_device使用的中斷號的開始值和結束值。
對resource的定義通常也在BSP的板文件中進行,而我們在設備驅動中可以用platform_get_resource()來獲取resource。
- struct platform_driver {
- int(*probe)(struct platform_device *);
- int(*remove)(struct platform_device *);
- void(*shutdown)(struct platform_device *);
- int(*suspend)(struct platform_device *, pm_message_t state);
- int(*resume)(struct platform_device *);
- structdevice_driver driver;
- structplatform_device_id *id_table;
- };
Platform_driver中包含了probe(),remove(),shutdown(),suspend(),resume()函數,通常是由驅動來實現。如果bus中定義了probe,remove,shutdown函數,會優先調用。
[platform_bus_type& 驅動和設備匹配方式]
- struct bus_type platform_bus_type = {
- .name = "platform",
- .dev_attrs = platform_dev_attrs,
- .match = platform_match,
- .uevent = platform_uevent,
- .pm = &platform_dev_pm_ops,
- };
Platform總線並沒有對bus_type進行封裝,總線維護了兩條鏈klist_devices和klist_drivers(這兩條鏈表在成員subsys_private下):
Klist_devices 用來連接所有的platformdevice,調用platform_device_register函數時,會把設備掛接到klist_devices鏈表上;
Klist_drivers 用來連接所有的platformdriver,調用platform_driver_register函數時,會把驅動掛接到klist_drivers鏈表上。
驅動和設備通過match函數來進行匹配
- static int platform_match(struct device*dev, struct device_driver *drv)
- {
- structplatform_device *pdev = to_platform_device(dev);
- structplatform_driver *pdrv = to_platform_driver(drv);
-
- /*Attempt an OF style match first */
- if(of_driver_match_device(dev, drv))
- return1;
-
- /*Then try to match against the id table */
- if(pdrv->id_table)
- returnplatform_match_id(pdrv->id_table, pdev) != NULL;
-
- /*fall-back to driver name match */
- return(strcmp(pdev->name, drv->name) == 0);
- }