在設備驅動程序中經常會見到和platform相關的字段,分佈在驅動程序的多個角落,這也是2.6內核中比較重要的一種機制,把它原理弄懂,對以後分析驅動程序很有幫助:在linux2.6設備模型中,關心總線,設備,驅動這三個實體,總線將設備和驅動綁定,在系統每註冊一個設備的時候,會尋找與之匹配的驅動。相反,在系統每註冊一個驅動的時候,尋找與之匹配的設備,匹配是由總線來完成的。
一個現實的Linux 設備和驅動通常都需要掛接在一種總線上,對於本身依附於PCI、USB、I2C、SPI 等的設備而言,這自然不是問題,但是在嵌入式系統裏面,SoC 系統中集成的獨立的外設控制器、掛接在SoC 內存空間的外設等確不依附於此類總線。基於這一背景,Linux 發明了一種虛擬的總線,稱爲platform 總線。SOC系統中集成的獨立外設單元(LCD,RTC,WDT等)都被當作平臺設備來處理,而它們本身是字符型設備。
從Linux2.6內核起,引入一套新的驅動管理和註冊機制:platform_device 和 platform_driver 。Linux 中大部分的設備驅動,都可以使用這套機制,設備用 platform_device 表示;驅動用platform_driver 進行註冊。
一.平臺設備
在Linux設備驅動中,有一類設備被稱爲“平臺設備”,通常把SoC系統中集成的獨立外設單元都當作平臺設備來處理。平臺設備用platform_device結構體來描述,在2.6.32.2內核中定義在include/linux/platform_devide.h中,其結構體如下:
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource; //設備使用的資源
struct platform_device_id *id_entry;
/* arch specific additions */
struct pdev_archdata archdata;
};
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;
};
我們通常關心start、end 和flags 這3 個字段,分別標明資源的開始值、結束值和類型,flags可以爲IORESOURCE_IO、IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA 等。
在Linux中定義了許多平臺設備,比如在:arch/arm/plat-s3c24xx/devs.c中,下面貼出WatchDog的平臺設備定義:
static struct resource s3c_wdt_resource[] = {
[0] = { //IO端口資源
.start = S3C24XX_PA_WATCHDOG,
.end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,
.flags = IORESOURCE_MEM,
},
[1] = { //中斷資源
.start = IRQ_WDT,
.end = IRQ_WDT,
.flags = IORESOURCE_IRQ,
}
};
struct platform_device s3c_device_wdt = {
.name = "s3c2410-wdt",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_wdt_resource),
.resource = s3c_wdt_resource,
};
EXPORT_SYMBOL(s3c_device_wdt);
定義好平臺設備後,在系統中怎麼使用?在arch/arm/mach-s3c2440/mach-smdk2440.c中,這個ARM2440平臺的系統入口文件,系統初始化函數smdk2440_machine_init中使用platform_add_devices函數將一系列的平臺設備添加到系統中。
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
};
static void __init smdk2440_machine_init(void)
{
s3c24xx_fb_set_platdata(&smdk2440_fb_info);
s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
smdk_machine_init();
}
二.平臺驅動
在Linux系統中爲平臺設備定義了平臺驅動platform_driver,平臺驅動結構體中定義了probe、remove、suspend、resume等接口函數來實現驅動。定義在include/linux/platform_devide.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 (*resume)(struct platform_device *);
struct device_driver driver;
struct platform_device_id *id_table;
};
下面是WatchDog的platform_driverde初始化,其中driver結構體中初始化了owner、name變量,這裏的name要和平臺設備的name一致,這樣平臺設備和平臺驅動就關聯起來。
static struct platform_driver s3c2410wdt_driver = {
.probe = s3c2410wdt_probe,
.remove = __devexit_p(s3c2410wdt_remove),
.shutdown = s3c2410wdt_shutdown,
.suspend = s3c2410wdt_suspend,
.resume = s3c2410wdt_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-wdt",
},
};
驅動又是如何實現呢,在驅動初始化module_init()這個宏實現平臺驅動註冊,module_exit()中實現平臺驅動註銷。
static int __init watchdog_init(void)
{
printk(banner);
return platform_driver_register(&s3c2410wdt_driver);
}
static void __exit watchdog_exit(void)
{
platform_driver_unregister(&s3c2410wdt_driver);
}
module_init(watchdog_init);
module_exit(watchdog_exit);