Linux驅動的platform機制
Linux的這種platform driver機制和傳統的device_driver機制相比,一個十分明顯的優勢在於platform機制將本身的資源註冊進核心,由核心統一管理,在驅動程式中使用這些資源時通過platform_device提供的標準介面進行申請並使用。這樣提高了驅動和資源管理的獨立性,並且擁有較好的可移植性和安全性。下面是SPI驅動層次示意圖,Linux中的SPI匯流排可理解為SPI控制器引出的匯流排:
和傳統的驅動一樣,platform機制也分為三個步驟:
1、匯流排註冊階段:
核心啟動初始化時的main.c檔案中的kernel_init()→do_basic_setup()→driver_init()→platform_bus_init()→bus_register(&platform_bus_type),註冊了一條platform匯流排(虛擬匯流排,platform_bus)。
2、新增裝置階段:
設備註冊的時候Platform_device_register()→platform_device_add()→(pdev→dev.bus = &platform_bus_type)→device_add(),就這樣把裝置給掛到虛擬的總線上。
3、驅動註冊階段:
Platform_driver_register()→driver_register()→bus_add_driver()→driver_attach()→bus_for_each_dev(), 對在每個掛在虛擬的platform bus的裝置作__driver_attach()→driver_probe_device(),判斷drv→bus→match()是否執行成功,此時通過指標執行platform_match→strncmp(pdev→name , drv→name , BUS_ID_SIZE),如果相符就呼叫really_probe(實際就是執行相應裝置的platform_driver→probe(platform_device)。)開始真正的探測,如果probe成功,則繫結裝置到該驅動。
從上面可以看出,platform機制最後還是呼叫了bus_register() , device_add() , driver_register()這三個關鍵的函式。
下面看幾個結構體:
struct platform_device (/include/linux/Platform_device.h)
{
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
Platform_device結構體描述了一個platform結構的裝置,在其中包含了一般裝置的結構體struct device dev;裝置的資源結構體struct resource * resource;還有裝置的名字const char * name。(注意,這個名字一定要和後面platform_driver.driver àname相同,原因會在後面說明。)
該結構體中最重要的就是resource結構,這也是之所以引入platform機制的原因。
struct resource ( /include/linux/ioport.h)
{
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
其中 flags位表示該資源的型別,start和end分別表示該資源的起始地址和結束地址(/include/linux/Platform_device.h):
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 (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
Platform_driver結構體描述了一個platform結構的驅動。其中除了一些函式指標外,還有一個一般驅動的device_driver結構。
名字要一致的原因:
上面說的驅動在註冊的時候會呼叫函式bus_for_each_dev(), 對在每個掛在虛擬的platform bus的裝置作__driver_attach()→driver_probe_device(),在此函式中會對dev和drv做初步的匹配,呼叫的是drv->bus->match所指向的函式。platform_driver_register函式中drv->driver.bus = &platform_bus_type,所以drv->bus->match就為platform_bus_type→match,為platform_match函式,該函式如下:
static int platform_match(struct device * dev, struct device_driver * drv)
{
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}
是比較dev和drv的name,相同則會進入really_probe()函式,從而進入自己寫的probe函式做進一步的匹配。所以dev→name和driver→drv→name在初始化時一定要填一樣的。
不同型別的驅動,其match函式是不一樣的,這個platform的驅動,比較的是dev和drv的名字,還記得usb類驅動裡的match嗎?它比較的是Product ID和Vendor ID。
個人總結Platform機制的好處:
1、提供platform_bus_type型別的匯流排,把那些不是匯流排型的soc裝置都新增到這條虛擬總線上。使得,匯流排——裝置——驅動的模式可以得到普及。
2、提供platform_device和platform_driver型別的資料結構,將傳統的device和driver資料結構嵌入其中,並且加入resource成員,以便於和Open Firmware這種動態傳遞裝置資源的新型bootloader和kernel 接軌。