platform的學習和使用

標籤(空格分隔): 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章