1. 程式人生 > >Linux驅動的platform機制

Linux驅動的platform機制

    最近在看SPI、I2C這樣簡單點的匯流排驅動程式,從Linux2.6起,核心引入了一套新的驅動管理和註冊機制:Platform_device和Platform_driver。現在Linux中大部分的裝置驅動都可以使用這套機制,匯流排為platform_bus,裝置用platform_device表示,驅動用platform_driver進行註冊。

    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 接軌。