module_platform_driver 與 module_init

        在linux內核源碼中,我們經常看到module_platform_driver 與 module_init這兩個宏定義,有時候在這個驅動中用module_platform_driver,有時候用module_init,那這兩個宏定義之間有什麼差異嗎?還是說可以隨便用呢?這就需要我們旭跟蹤代碼,來看看這兩個宏定義到底什麼東西?

       首先,介紹下module_init,module_init對於做驅動的人應該不陌生,linux內核是一個宏內核,宏內核就是驅動和內核打包在一起的。由於驅動是作爲內核模塊掛載在內核上的,而內核對於模塊的接口就是module_init和module_exit,所以你要加載一個內核模塊的時候,必須使用module_init來進行,而卸載一個內核模塊的時候,必須使用module_exit來進行。例如:

module_init(xxxx_init);       //加載xxxx模塊

module_exit(xxxx_exit);     //卸載xxxx模塊

        那是不是就有一個疑問了,既然是所有模塊都是通過這兩個函數來進行加載和卸載的,那應該所有驅動都用module_init函數啊,爲什麼還會有module_platform_driver?這不是多此一舉嗎?醒醒吧,少年,Linux社區那麼多大神在更新,會出現這種錯誤嗎?

       那麼我們先看看module_platform_driver的宏定義是個啥?

#define module_platform_driver(__platform_driver) \
	module_driver(__platform_driver, platform_driver_register, \
			platform_driver_unregister)
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
	return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
	__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

       從代碼中看到,module_platform_driver 追根溯源,發現最終還是調用了module_init,但是,又不僅僅是調用了module_init,還調用了platform_driver_register和platform_driver_unregister,這兩個函數的作用就是註冊和卸載平臺驅動。

        一般某個設備或某個控制器掛載在處理器上時,肯定是通過某種總線來連接的,比喻說常見總線有SPI總線、IIC總線等等,但控制器一般是通過內部總線掛載處理器端的,對於這一類設備,linux抽象出了一個平臺總線,來包含所有的處理器總線掛載的設備。而這類總線需要註冊到內核中去,就需要用platform_driver_register來實現,平臺總線是抽象出來的,所以所有通過總線直接連在處理器上的設備是不需要關心平臺總線怎麼運作的,因此這個平臺總線的註冊和註銷都是通用的,所以在加載總線設備驅動時,直接調用module_platform_driver,就可以將平臺驅動註冊函數和卸載函數、以及總線設備加載一次性運行完,避免了總線驅動在每次加載驅動時都需要手動註冊平臺總線。

        其實說了這麼多,module_platform_driver就是對module_init進一步的封裝,在module_init之外添加了一些功能,對於平臺總線設備而言,直接調用module_platform_driver就可以避免在module_init函數中去註冊平臺驅動了,使得平臺設備驅動的加載變得更方便了。

       那是不是說module_init可以與module_platform_driver就可以通用呢?當然不是,能用module_platform_driver的地方肯定可以用module_init,但能用module_init卻不一定能用module_platform_driver,這主要看設備是不是通過平臺總線的方式掛載處理器上的。

函數名 非平臺總線設備 平臺總線設備
module_platform_driver 不可用 可用
module_init 可用 可用

對於平臺總線設備,可以用兩種方法來加載

static int __init xxxx_init(void)
{
	return platform_driver_register(&xxxx_driver);
}

static void __exit xxxx_exit(void)
{
	platform_driver_unregister(&xxxx_driver);
}

module_init(xxxx_init);
module_exit(xxxx_exit);
module_platform_driver(xxxx_driver);

這兩種方式的效果是一樣的,但明顯module_platform_driver比較簡潔。

但對於其他總線的設備,就不能使用module_platform_driver了,必須使用module_init,在module_init函數中再註冊一遍其他總線,例如SPI總線設備。

static int __init spidev_init(void)
{
	int status;

	/* Claim our 256 reserved device numbers.  Then register a class
	 * that will key udev/mdev to add/remove /dev nodes.  Last, register
	 * the driver which manages those device numbers.
	 */
	BUILD_BUG_ON(N_SPI_MINORS > 256);
	status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
	if (status < 0)
		return status;

	spidev_class = class_create(THIS_MODULE, "spidev");
	if (IS_ERR(spidev_class)) {
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
		return PTR_ERR(spidev_class);
	}

	status = spi_register_driver(&spidev_spi_driver);
	if (status < 0) {
		class_destroy(spidev_class);
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
	}
	return status;
}
module_init(spidev_init);

static void __exit spidev_exit(void)
{
	spi_unregister_driver(&spidev_spi_driver);
	class_destroy(spidev_class);
	unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
module_exit(spidev_exit);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章