總線設(shè)備驅(qū)動(dòng)模型主要包含總線、設(shè)備、驅(qū)動(dòng)三個(gè)部分,總線可以是一條真實(shí)存在的總線,例如USB、I2C等典型的設(shè)備。但是對(duì)于一些設(shè)備(內(nèi)部的設(shè)備)可能沒有現(xiàn)成的總線。Linux 2.6內(nèi)核中引入了總線設(shè)備驅(qū)動(dòng)模型�?偩€設(shè)備驅(qū)動(dòng)模型與之前的三類驅(qū)動(dòng)(字符、塊設(shè)備、網(wǎng)絡(luò)設(shè)備)沒有必然的聯(lián)系。設(shè)備只是搭載到了總線中。在 linux內(nèi)核中假設(shè)存在一條虛擬總線,稱之為platform總線。
platform總線相比與常規(guī)的總線模型其優(yōu)勢(shì)主要是platform總線是由內(nèi)核實(shí)現(xiàn)的,而不用自己定義總線類型,總線設(shè)備來加載總線。platform總線是內(nèi)核已經(jīng)實(shí)現(xiàn)好的。只需要添加相應(yīng)的platform device和platform driver。具體的實(shí)現(xiàn)過程主要包括如下的過程:
整體而言只需要完成兩個(gè)步驟,也就是設(shè)備的實(shí)現(xiàn)和驅(qū)動(dòng)的實(shí)現(xiàn),每一個(gè)實(shí)現(xiàn)都包括相關(guān)結(jié)構(gòu)體的定義和注冊(cè)。
platform_device注冊(cè)
需要注意的是platform_device 實(shí)質(zhì)上是經(jīng)過處理過的設(shè)備,在platform_device結(jié)構(gòu)體中存在一個(gè)設(shè)備結(jié)構(gòu)體,與之前的設(shè)備存在差別的是引入了設(shè)備資源。這些設(shè)備資源就能實(shí)現(xiàn)對(duì)設(shè)備寄存器,中斷等資源的訪問。平臺(tái)設(shè)備的基本結(jié)構(gòu)體如下:
struct platform_device {
/*設(shè)備名*/
const char * name;
/*設(shè)備ID號(hào)*/int id;
/*結(jié)構(gòu)體包含一個(gè)具體的device結(jié)構(gòu)體*/
struct device dev;/*資源的數(shù)量*/
u32 num_resources;/*資源結(jié)構(gòu)體,用來保存硬件的資源*/
struct resource * resource;/*平臺(tái)設(shè)備的ID*/
struct platform_device_id *id_entry;
};
其中struct device 和 struct resource 是重要的結(jié)構(gòu)體。struct device 在總線設(shè)備驅(qū)動(dòng)模型中已經(jīng)提到了。這次討論一下struct resource。
struct resource {
/*資源的起始值,如果是地址,那么是物理地址,不是虛擬地址*/
resource_size_t start;
/*資源的結(jié)束值,如果是地址,那么是物理地址,不是虛擬地址*/
resource_size_t end;
/*資源名*/
const char *name;
/*資源的標(biāo)示,用來識(shí)別不同的資源*/
unsigned long flags;
/*資源指針,可以構(gòu)成鏈表*/
struct resource *parent, *sibling, *child;
};
platform_device 的注冊(cè)很簡(jiǎn)單,只需要在設(shè)備的初始化函數(shù)中首先定義相應(yīng)的設(shè)備,通常采用函數(shù)platform_device *platform_device_alloc(const char *name, int id)動(dòng)態(tài)申請(qǐng),通常name就是需要申請(qǐng)的設(shè)備名,而id為-1。然后采用
int platform_device_add(struct platform_device *pdev)
或者
int platform_device_register(struct platform_device *pdev)
注冊(cè)定義好的設(shè)備即可。
同樣在退出函數(shù)中釋放注冊(cè)好的設(shè)備即可,可以采用函數(shù):
void platform_device_unregister(struct platform_device *pdev)。
然后一個(gè)平臺(tái)設(shè)備就完成了,不需要像自己實(shí)現(xiàn)模型時(shí)定義相關(guān)的文件屬性等。
設(shè)備資源可以通過相關(guān)函數(shù)得到:
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num)
中斷資源也可以通過:
int platform_get_irq(struct platform_device *dev, unsigned int num)
資源的使用主要是驅(qū)動(dòng)實(shí)現(xiàn)過程中需要使用到的,但是后期的使用一般需要在驅(qū)動(dòng)的probe函數(shù)中實(shí)現(xiàn)申請(qǐng)中斷或者IO內(nèi)存才能使用,而不能直接使用。特別是資源中的地址通常是物理地址,需要通過申請(qǐng)IO內(nèi)存和映射完成物理到虛擬地址的轉(zhuǎn)換,便于進(jìn)程的訪問。
platform_driver注冊(cè)
平臺(tái)驅(qū)動(dòng)結(jié)構(gòu)體platform_driver實(shí)現(xiàn)如下:
struct platform_driver {
/*平臺(tái)驅(qū)動(dòng)需要實(shí)現(xiàn)的相關(guān)函數(shù)操作,
其中的前4個(gè)函數(shù)與最后一個(gè)函數(shù)與device_driver中的函數(shù)是相同的
本質(zhì)是實(shí)現(xiàn)對(duì)device_driver 中相關(guān)函數(shù)的賦值。
*/
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 *);
/*內(nèi)嵌了一個(gè)設(shè)備驅(qū)動(dòng)結(jié)構(gòu)體*/
struct device_driver driver;
/*平臺(tái)設(shè)備ID,這與platform_device中的struct platform_device_id *id_entry是相同的主要是完成總線的匹配操作,platform總線的匹配操作第一匹配要素就是該元素。而不再是簡(jiǎn)單的name選項(xiàng)。
*/
struct platform_device_id *id_table;
};通常驅(qū)動(dòng)的入口函數(shù):
int (*probe)(struct platform_device *);
當(dāng)總線完成了設(shè)備的match 操作以后就會(huì)進(jìn)入驅(qū)動(dòng)中該函數(shù)的運(yùn)行。總線函數(shù)的匹配操作如下:
static int platform_match(struct device *dev, struct device_driver *drv)
{/*得到平臺(tái)設(shè)備的指針*/
struct platform_device *pdev = to_platform_device(dev);/*得到平臺(tái)驅(qū)動(dòng)指針*/
struct platform_driver *pdrv = to_platform_driver(drv);/* match against the id table first */
/*從定義上分析,id_table是首先匹配的對(duì)象,然后才是name的匹配,當(dāng)ID匹配完成時(shí)就說明匹配好了*/
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
從上面的定義可以知道platform總線的匹配函數(shù)手下是比較id_table是匹配的首選項(xiàng)。
probe 函數(shù)稱之為探針函數(shù),用于檢測(cè)總線上有該驅(qū)動(dòng)能夠處理的設(shè)備,而remove函數(shù)則是為了說明總線上該驅(qū)動(dòng)能夠處理的設(shè)備被移除。
因此這兩個(gè)函數(shù)是在平臺(tái)設(shè)備中一定要被實(shí)現(xiàn)的函數(shù)。
其他的函數(shù)則不一樣要求實(shí)現(xiàn)。
平臺(tái)驅(qū)動(dòng)的設(shè)計(jì)主要是完成平臺(tái)驅(qū)動(dòng)結(jié)構(gòu)體的填充和注冊(cè)。
通常的平臺(tái)驅(qū)動(dòng)結(jié)構(gòu)體實(shí)現(xiàn)如下:
static struct platform_driver my_driver =
{
/*平臺(tái)驅(qū)動(dòng)的probe函數(shù)實(shí)現(xiàn)*/
.probe = my_probe,
/*平臺(tái)驅(qū)動(dòng)的remove函數(shù)實(shí)現(xiàn)*/
.remove = my_remove,
/*實(shí)現(xiàn)設(shè)備驅(qū)動(dòng)的name和owner變量*/
.driver =
{
/*該參數(shù)主要實(shí)現(xiàn)總線中的匹配函數(shù)調(diào)用*/
.name = "my_dev",
/*該函數(shù)表示模塊的擁有者*/
.owner = THIS_MODULE,
},
};其中的my_probe和my_remove是自己定義的probe和remove函數(shù)。
最主要的是內(nèi)嵌設(shè)備驅(qū)動(dòng)結(jié)構(gòu)體的填充,主要的填充包括name和owner兩個(gè),當(dāng)然也可以包括其他的。由于沒有填充id_table,那么 name就是總線匹配操作的第一選擇。因此如果沒有填充好id_table,那么name元素是一定要實(shí)現(xiàn)的,不然不能完成相應(yīng)的設(shè)備驅(qū)動(dòng)匹配操作。
完成platform_driver結(jié)構(gòu)體的填充過后就是完成驅(qū)動(dòng)的在初始化階段的注冊(cè)以及退出階段的釋放操作,基本的實(shí)現(xiàn)函數(shù)為:
注冊(cè)函數(shù),通常在驅(qū)動(dòng)初始化函數(shù)中調(diào)用:
int platform_driver_register(struct platform_driver *drv)
釋放函數(shù),通常在驅(qū)動(dòng)退出函數(shù)調(diào)用:
void platform_driver_unregister(struct platform_driver *drv)
完成相關(guān)的注冊(cè)以后總線、設(shè)備、驅(qū)動(dòng)的大概框架就完成啦。
但是這只是常用的框架,還不能在應(yīng)用程序中使用。
基于平臺(tái)驅(qū)動(dòng)的設(shè)備驅(qū)動(dòng)都是基于總線架構(gòu)的,基本的實(shí)現(xiàn)過程與之前的簡(jiǎn)單字符設(shè)備存在較大的差別,主要的區(qū)別在驅(qū)動(dòng)的初始化不在是平臺(tái)設(shè)備驅(qū)動(dòng)的初始化函數(shù)中實(shí)現(xiàn),而是在probe函數(shù)中實(shí)現(xiàn)。而驅(qū)動(dòng)的卸載函數(shù)則是在remove函數(shù)中實(shí)現(xiàn)。probe函數(shù)是平臺(tái)總線實(shí)現(xiàn)匹配以后首先被調(diào)用的函數(shù),因此在其中實(shí)現(xiàn)字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)的初始化是有意義的,這樣的設(shè)備驅(qū)動(dòng)就是基于平臺(tái)總線的設(shè)備驅(qū)動(dòng),便于維護(hù)。
平臺(tái)總線驅(qū)動(dòng)的注冊(cè)過程分析:
int platform_driver_register(struct platform_driver *drv)
{/*第一步,仍然是完成結(jié)構(gòu)體的填充操作*/
/*驅(qū)動(dòng)的總線類型*/
drv->driver.bus = &platform_bus_type;/*將自己定義的probe函數(shù)賦值給平臺(tái)驅(qū)動(dòng)中設(shè)備驅(qū)動(dòng)的probe函數(shù),其他函數(shù)類似*/
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;/*第二步,仍然是完成一般設(shè)備驅(qū)動(dòng)的注冊(cè)操作*/
/*然手就是一般驅(qū)動(dòng)的注冊(cè),這樣就完成了設(shè)備的注冊(cè)*/
return driver_register(&drv->driver);
}/*設(shè)備驅(qū)動(dòng)的probe函數(shù)的賦值過程*/
static int platform_drv_probe(struct device *_dev)
{/*得到設(shè)備對(duì)應(yīng)的平臺(tái)驅(qū)動(dòng)*/
struct platform_driver *drv = to_platform_driver(_dev->driver);/*得到設(shè)備的平臺(tái)設(shè)備*/
struct platform_device *dev = to_platform_device(_dev);/*下面的probe是自己實(shí)現(xiàn)的probe函數(shù)。具體的實(shí)現(xiàn)思路:
根據(jù)一般設(shè)備找對(duì)應(yīng)的平臺(tái)設(shè)備,同時(shí)根據(jù)設(shè)備的驅(qū)動(dòng)找到平臺(tái)驅(qū)動(dòng)。
然后返回平臺(tái)驅(qū)動(dòng)的probe函數(shù)(自己實(shí)現(xiàn)通常是初始化操作)地址。
*/
return drv->probe(dev);
}實(shí)現(xiàn)的總線平臺(tái)驅(qū)動(dòng)模型的最簡(jiǎn)單源碼:
平臺(tái)設(shè)備的實(shí)現(xiàn):device.c
#include<linux/device.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/string.h>
#include<linux/platform_device.h>/*平臺(tái)模型驅(qū)動(dòng)的平臺(tái)設(shè)備對(duì)象*/
static struct platform_device *my_device;/*初始化函數(shù)*/
static int __init my_device_init(void)
{
int ret = 0;/*采用platform_device_alloc分配一個(gè)platform_device對(duì)象
參數(shù)分別為platform_device的name,和id。
*/
my_device = platform_device_alloc("my_dev",-1);/*注冊(cè)設(shè)備,注意不是platform_device_register,將平臺(tái)設(shè)備注冊(cè)到內(nèi)核中*/
ret = platform_device_add(my_device);/*如果出錯(cuò)釋放相關(guān)的內(nèi)存單元*/
if(ret)
{
platform_device_put(my_device);
}return ret;
}/*卸載處理函數(shù)*/
static void __exit my_device_exit(void){
platform_device_unregister(my_device);
}module_init(my_device_init);
module_exit(my_device_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("GP-<gp19861112@yahoo.com.cn>");
平臺(tái)驅(qū)動(dòng)的實(shí)現(xiàn):driver.c
#include<linux/module.h>
#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/platform_device.h>
#include<linux/string.h>
/*平臺(tái)驅(qū)動(dòng)中的probe和remove函數(shù)是必須實(shí)現(xiàn)的函數(shù)*/
/*設(shè)備驅(qū)動(dòng)的探測(cè)函數(shù),主要實(shí)現(xiàn)檢測(cè)總線上是否有該驅(qū)動(dòng)對(duì)應(yīng)的設(shè)備*/
static my_probe(struct device *dev)
{/*
如果添加實(shí)際的設(shè)備到該平臺(tái)總線設(shè)備驅(qū)動(dòng)模型中,則可以在該函數(shù)
中實(shí)現(xiàn)具體的設(shè)備驅(qū)動(dòng)函數(shù)的初始化操作,包括設(shè)備號(hào)的申請(qǐng),設(shè)備
的初始化,添加。自動(dòng)設(shè)備文件創(chuàng)建函數(shù)的添加等操作。
或者是混雜字符設(shè)備的相關(guān)初始化操作。當(dāng)然結(jié)構(gòu)體的相關(guān)處理仍
然采取全局變量的形式。
*/
printk("Driver found devices which this driver can be handle\n");
return 0;
}/*設(shè)備驅(qū)動(dòng)的移除函數(shù),主要檢測(cè)該驅(qū)動(dòng)支持設(shè)備的移除活動(dòng)檢測(cè)*/
static my_remove(struct device *dev)
{/*
如果添加實(shí)際的設(shè)備到該平臺(tái)總線設(shè)備驅(qū)動(dòng)模型中,則可以在該函數(shù)
中實(shí)現(xiàn)具體的設(shè)備的釋放,包括設(shè)備的刪除,設(shè)備號(hào)的注銷等操作。
*/
printk("Driver found device unpluded\n");
return 0;
}static struct platform_driver my_driver =
{
/*平臺(tái)驅(qū)動(dòng)的probe函數(shù)實(shí)現(xiàn)*/
.probe = my_probe,
/*平臺(tái)驅(qū)動(dòng)的remove函數(shù)實(shí)現(xiàn)*/
.remove = my_remove,
/*實(shí)現(xiàn)設(shè)備驅(qū)動(dòng)的name和owner變量*/
.driver =
{
/*該參數(shù)主要實(shí)現(xiàn)總線中的匹配函數(shù)調(diào)用*/
.name = "my_dev",
/*該函數(shù)表示模塊的擁有者*/
.owner = THIS_MODULE,
},
};/*初始化函數(shù)*/
static int __init my_driver_init(void)
{
/*注冊(cè)平臺(tái)驅(qū)動(dòng)*/
return platform_driver_register(&my_driver);
}
/*退出函數(shù)*/
static void __exit my_driver_exit(void)
{
/*注銷平臺(tái)驅(qū)動(dòng)*/
return platform_driver_unregister(&my_driver);
}/*加載和卸載*/
module_init(my_driver_init);
module_exit(my_driver_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("GP-<gp19861112@yahoo.com.cn>");
將一般設(shè)備驅(qū)動(dòng)加入到總線設(shè)備模型中的相關(guān)操作是后期總結(jié)和學(xué)習(xí)的內(nèi)容。設(shè)備驅(qū)動(dòng)實(shí)現(xiàn)的實(shí)現(xiàn)原理還是之前的那些操作,但是初始化和推出函數(shù)發(fā)生了改變。
總結(jié):
platform總線的驅(qū)動(dòng)模型只是在一般總線模型的基礎(chǔ)上做了相關(guān)的延伸,實(shí)質(zhì)上只要弄清除總線模型的一般原理,學(xué)習(xí)platform總線也就簡(jiǎn)單不少。但是畢竟還是學(xué)習(xí)階段。