標籤(空格分隔): linux子系統 platform平臺框架
http://blog.csdn.net/ufo714/article/details/8595021
platform的靈魂是:device(設備)driver(驅動)platform_bus(platform總線),其特點是設備,驅動分層動態的管理和加載
其中platform_bus是一個虛擬的總線,當我們將設備和驅動註冊到虛擬總線上(內核)時,如果該設備是該驅動的設備,該驅動是該設備的驅動,在他們註冊時,會互相尋找
一次對方(只在註冊的時候尋找一次,找完了就玩了)。這個找的過程是platform_bus來完成的,我們暫不管他如何讓尋找。如果device和driver中的name這個字符串是想相同的話
platform_bus就會調用driver中的.probe函數.這個匹配到調用probe的過程是自動的,有總線自己完成。這個過程從註冊開始,從probe結束
設備和驅動的關係是多對一的關係,即多個相同設備可使用一個driver,靠device(設備)中的id號來區別
platform的使用其實就四步:
1)初始化 resource 結構變量
2)初始化 platform_device 結構變量
3)向系統註冊設備:platform_device_register。
4)想系統註冊驅動:[platform_driver_register()]
drvier和device匹配的方法有3種:
* 當一個設備註冊的時候,他會在總線上尋找匹配的driver,platform device一般在系統啓動很早的時候就註冊了
* 當一個驅動註冊[platform_driver_register()]的時候,他會遍歷所有總線上的設備來尋找匹配,在啓動的過程驅動的註冊一般比較晚,或者在模塊載入的時候
* 當一個驅動註冊[platform_driver_probe()]的時候, 功能上和使用platform_driver_register()是一樣的,唯一的區別是它不能被以後其他的device probe了,也就是說這個driver只能和 一個device綁定
eg:定義一個driver
static struct platform_driver test_platform_driver = {
.probe = dev_test_probe,
.remove = dev_test_remove,
.suspend = dev_test_suspend,
.resume = dev_test_resume,
.driver = {
.owner = THIS_MODULE,
.name = "cx2837_test",
},
};
定義一個device (設備)
static struct platform_device dev_fb0 = {
.name = "cx2837_test",
.id = 1,
};
下面看上三種使用platform的模型
1.
static struct platform_driver test_platform_driver = {
.probe = dev_test_probe,
.remove = dev_test_remove,
.suspend = dev_test_suspend,
.resume = dev_test_resume,
.driver = {
.owner = THIS_MODULE,
.name = "cx2837_test",
},
}; //先在文件中定一個驅動結構體
__init()
{
struct platform_device * test_platform_device; //定一個device設備結構體
for(i=0;i<MAX_TUNER_SUPPORT_NUM;i++)
{
suspend_output_string("Function device_alloc");
test_platform_device = platform_device_alloc("cx2837_test",i);//初始化這個結構體,初始化形式多樣,包括上面的直接初始化方式也可以,name要保持和driver中的name一致。
if(tets_platform_device == NULL)
{
TEST_PRINTF("[%s]line=%d,fail to alloc device\n", __FUNCTION__, __LINE__);
return ERR_NO_MEM;
}
test_platform_device->dev.platform_data = NULL;
platform_device_add(test_platform_device);//將在這個設備註冊到內核中,如果註冊成功,則總線將按照你註冊的名字去找相同名字的驅動(注意這裏即我說的註冊的時候即是尋找的時候),此時只找一次,找不到就找不到,此時總線中沒有一個相同名字的驅動被註冊,所以找不到,我們接着往下看;
}
platform_driver_register(&test_platform_driver);//此時向內核註冊驅動,如果註冊成功,則總線將按照你註冊的名字去找相同名字的設備,此時也是隻找一次,但此時我們之前在總線中註冊了一個相同name的device設備,所以找到了,匹配成功,調用driver中的probe函數做你想做的事情。
}
2.
static struct platform_driver test_platform_driver = {
.probe = dev_test_probe,
.remove = dev_test_remove,
.suspend = dev_test_suspend,
.resume = dev_test_resume,
.driver = {
.owner = THIS_MODULE,
.name = "cx2837_test",
},
}; //先在文件中定一個驅動結構體
__init()
{
//注意這次我們的順序是反的
platform_driver_register(&test_platform_driver);//此時向內核註冊驅動,如果註冊成功,則總線將按照你註冊的名字去找相同名字的設備,此時也是隻找一次,此時只找一次,找不到就找不到,此時總線中沒有一個相同名字的設備被註冊,所以找不到,我們接着往下看;
struct platform_device * test_platform_device; //定一個device設備結構體
for(i=0;i<MAX_TUNER_SUPPORT_NUM;i++)
{
suspend_output_string("Function device_alloc");
test_platform_device = platform_device_alloc("cx2837_test",i);//初始化這個結構體,初始化形式多樣,包括上面的直接初始化方式也可以,name要保持和driver中的name一致。
if(test_platform_device == NULL)
{
TEST_PRINTF("[%s]line=%d,fail to alloc test device\n", __FUNCTION__, __LINE__);
return ERR_NO_MEM;
}
test_platform_device->dev.platform_data = NULL;
platform_device_add(test_platform_device);//將在這個設備註冊到內核中,如果註冊成功,則總線將按照你註冊的名字去找相同名字的驅動(注意這裏即我說的註冊的時候即是尋找的時候),此時只找一次,找不到就找不到,但此時我們之前在總線中註冊了一個相同name的driver驅動,所以找到了,匹配成功,調用driver中的probe函數做你想做的事情。
}
對於1,2的總結:這兩種只是註冊的順序不一樣而已,主要是爲了說明設備和驅動的匹配條件,從上面可以看出,不管誰註冊,只要註冊就會去尋找一次,當兩個都註冊以後,匹配成功,就會調用
probe函數
3.static struct platform_device uart8250_device = {
.name = "se_uart",
.id = -1,
};
/* really no use !!*/
#if 0
static struct platform_device hcd_device = {
.name = "hcd",
.id = -2,
};
#endif
/* FB platformat device0 for the GMA1.
support CLUT8, ARGB1555 and ARGB8888 */
static struct platform_device dev_fb0 = {
.name = "dev_fb",
.id = 1,
};//先在文件中把我們需要的所有設備提前在一個文件中初始化好。
static struct platform_device *nxm_platform_devices[] __initdata = {
&uart8250_device,
&usb_gadget_device,
&usb1_ohci_device,
&usb1_ehci_device,
.
.
};//然後找一個數組把這些設備裝起來
platform_add_devices(nxm_platform_devices, ARRAY_SIZE(nxm_platform_devices));//然後使用這個函數批量註冊這些設備
對於驅動,後面我們用到什麼驅動就註冊哪個驅動,只要名字一樣就可以了。
到這裏,我們明白了設備和驅動的調用方式,那麼問題來了,這個匹配成功後自動調用的probe函數是幹什麼用的 ?
probe(struct platform_device *)
1.
platform_device 中有個device結構體成員,這個結構體有一個platform_data這個空指針。
我們可以利用這個傳進來的參數中的這個指針指向本地的數據結構,這樣就可以把本地的數據和設備參數掛起來了。
2.
我們在probe中註冊字符設備包括註冊file_operation這個結=結構體,從而去提供read,ioct等些io接口
platform框架:
static struct platform_driver test_platform_driver = {
.probe = dev_test_probe,
.remove = dev_test_remove,
.suspend = dev_test_suspend,
.resume = dev_test_resume,
.driver = {
.owner = THIS_MODULE,
.name = "cx2837_test",
},
};
static int dev_test_probe(struct platform_device * pdev)
{在這個函數中我們實現字符設備的註冊,提供哪些IOCTL接口
}
static int dev_test_remove(struct platform_device * pdev)
{在這個函數中我們註銷在probe中申請的資源
}
module_platform_driver(test_platform_driver);//使用這個宏來註冊driver結構體,這個宏不需要我們關心設備的註冊和註銷。即不需要
platform_driver_register(&test_platform_driver);和platform_driver_unregister(&test_platform_driver);這兩個函數了。所以我們也就不需要使用
module_init(cxd2837_test_init);
module_exit(cxd2837_test_exit);這兩個函數了,init的工作放在了probe中,exit的工作放在了remove中了
MODULE_AUTHOR("Leo");
MODULE_DESCRIPTION("SONY CXD2837 driver");
但是這樣子我們註冊了驅動,但設備在哪裏註冊呢,設備我們可以在其他早期加載的文件中使用上述3中描述的方法統一註冊設備,其實最新的內核我們都是用
device tree的方式去註冊設備了,就不用這種方式了
在使用中突然發現,我註冊了三個同名(同name)設備,只是註冊id不一樣,但我的驅動只註冊了一次同名driver,啓動後發現probe被調用了3次。傳入的id分別是
註冊時的三個不同的id。這也恰好驗證了多個device對應一個driver這種情況,platform bus是依據device和driver的name去匹配的,註冊了幾次,驅動就會被匹配幾次。
驅動中去區別同名device時就是靠的傳入的不同的device id