一.Platform設備驅動概念
主要講解平台設備驅動的模型和基本概念,同時因為驅動加載的方式有動態加載和靜態加載兩種方式,這裡我們分別對動態加載和靜態加載兩種情況下,如何使用平台設備和驅動加以敘述。最後使用mini2440開發板,運用Platform和device_attribute機制,編寫按鍵驅動代碼和測試代碼。
我們知道linux內核中常見的的總線有I2C總線,PCI總線,串口總線,SPI總線,PCI總線,CAN總線,單總線等,所以有些設備和驅動就可以掛在這些總線上,然後通過總線上的match進行設備和驅動的匹配。但是有的設備並不屬於這些常見總線,所以我們引入了一種虛擬總線,也就是platform總線的概念,對應的設備叫做platform設備,對應的驅動叫做platform驅動。當然引入platform的概念,可以做的與板子相關的代碼和驅動的代碼分離,使得驅動有更好的可擴展性和跨平台性。
1.Platform總線
struct bus_type platform_bus_type = {
.name = "platform", //名
.dev_attrs = platform_dev_attrs, //屬性
.match = platform_match, //設備和驅動的匹配函數
.uevent = platform_uevent, //卸載處理
.pm = &platform_dev_pm_ops, //電源管理
};
我們看看設備和驅動的匹配函數match
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev); //獲得平台設備
struct platform_driver *pdrv = to_platform_driver(drv); //獲得平台驅動
if (pdrv->id_table) //如果平台驅動有支持項,進入platform_match_id
return platform_match_id(pdrv->id_table, pdev) != NULL;
return (strcmp(pdev->name, drv->name) == 0); //沒有支持項,則老實匹配名字
}
通過上面這個match函數我們知道,如果驅動中定義了驅動支持項,那麼在總線執行match函數中,就會將驅動支持項中每一個名字和設備名字匹配,看看是否匹配成功。如果驅動沒有設置支持項,就會把驅動的名字和設備的名字匹配,如果一樣,則匹配成功。
2.Platform設備
struct platform_device {
const char * name; //名
int id;
struct device dev; //內嵌設備
u32 num_resources; //資源個數
struct resource * resource; //資源結構體
struct platform_device_id *id_entry;
struct pdev_archdata archdata;
};
我們重點來看看platform_device中資源結構體的定義
struct resource {
resource_size_t start; //起始地址
resource_size_t end; //結束地址
const char *name; //名
unsigned long flags; //標號
struct resource *parent, *sibling, *child;
};
對於這個資源結構體中的flags標號可以有IORESOURCE_IO、IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA四種選擇,重點是申請內存(IORESOURCE_MEM)和申請中斷號(IORESOURCE_IRQ)用的比較多。
2.1Platform設備的靜態加載
所謂的靜態加載,就是把platform設備編譯進內核,對於platform_device的定義常常在BSP中實現,我們這裡拿Mini2440舉例,看看對於的BSP文件mach-smdk2440.c
struct platform_device s3c_device_lcd = {
.name = "s3c2410-lcd",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_lcd_resource),
.resource = s3c_lcd_resource,
.dev = {
.dma_mask = &s3c_device_lcd_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
這是基於Mini2440的LCD平台設備在BSP文件中的定義,那麼我們怎麼把它加入內核呢?
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd, //添加LCD平台設備
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
};
嗯,原來我們建立了一個platform_device數組,然後把LCD的platform_device添加到這個數組中,那麼這個platform_device數組怎麼注冊到內核的呢?
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();
}
看到了吧,在smdk2440_machine_init中,我們調用了platform_add_devices函數來把platform_device注冊到內核,再繼續跟蹤下platform_add_devices
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]); //注冊設備
break;
}
}
return ret;
}
好了,到此為止,我們已經看到了如果添加platform_device,以及這個platform_device又是如何被注冊到內核的全過程。