很多人心中都有過一個問題What is the difference between Platform driver and normal device driver?,簡單的來說Platform devices就non-discoverable,也就是device本身沒辦法跟系統說"我在這裡",典型的就是I2C device,它不會通知kernel"我在這裡",通常是預先知道有個I2C device在那裡,再由software設定好,這類non-discoverable device就適用Platform devices架構來寫。
platform device會被connect在platform bus上,而platform bus是一個虛擬的bus(pseudo-bus),這樣可以讓整個架構platform driver符合Linux的標準driver model。
這篇會根據The platform device API教導如何寫一個簡單的Platform devices,基本上最基本的platform device只需要name,因為platform bus會根據platform device與platform driver的name是否match執行driver的probe(),而最簡單的platform driver只需要name,跟probe()與remove()即可。
#include <linux/module.h> #include <linux/platform_device.h> MODULE_AUTHOR("Brook"); MODULE_DESCRIPTION("Kernel module for demo"); MODULE_LICENSE("GPL"); #define DEVNAME "brook" #define DYN_ALLOC 1 static struct platform_device brook_device = { .name = DEVNAME, }; static int brook_probe(struct platform_device *pdev) { pr_info("%s(#%d)\n", __func__, __LINE__); return 0; } static int brook_remove(struct platform_device *pdev) { pr_info("%s(#%d)\n", __func__, __LINE__); return 0; } static struct platform_driver brook_driver = { .driver = { .name = DEVNAME, .owner = THIS_MODULE, }, .probe = brook_probe, .remove = brook_remove, }; static int __init brook_init(void) { int err; pr_info("%s(#%d)\n", __func__, __LINE__); err = platform_device_register(&brook_device); if (err) { pr_err("%s(#%d): platform_device_register failed(%d)\n", __func__, __LINE__, err); return err; } err = platform_driver_register(&brook_driver); if (err) { dev_err(&(brook_device.dev), "%s(#%d): platform_driver_register fail(%d)\n", __func__, __LINE__, err); goto dev_reg_failed; } return err; dev_reg_failed: platform_device_unregister(&brook_device); return err; } module_init(brook_init); static void __exit brook_exit(void) { dev_info(&(brook_device.dev), "%s(#%d)\n", __func__, __LINE__); platform_device_unregister(&brook_device); platform_driver_unregister(&brook_driver); } module_exit(brook_exit);
使用platform_device_register()會導致"brook.0" does not have a release() function, it is broken and must be fixed.的OOPS,可以改用platform_device_alloc() + platform_device_add(),platform_device_alloc()裡面就會做pa->pdev.dev.release = platform_device_release。
#include <linux/module.h> #include <linux/platform_device.h> MODULE_AUTHOR("Brook"); MODULE_DESCRIPTION("Kernel module for demo"); MODULE_LICENSE("GPL"); #define DEVNAME "brook" #define DYN_ALLOC 1 static struct platform_device *brook_device; static int brook_probe(struct platform_device *pdev) { pr_info("%s(#%d)\n", __func__, __LINE__); return 0; } static int brook_remove(struct platform_device *pdev) { pr_info("%s(#%d)\n", __func__, __LINE__); return 0; } static struct platform_driver brook_driver = { .driver = { .name = DEVNAME, .owner = THIS_MODULE, }, .probe = brook_probe, .remove = brook_remove, }; static int __init brook_init(void) { int err; pr_info("%s(#%d)\n", __func__, __LINE__); /* using platform_device_alloc() + platform_device_add() * instead of platform_device_register() to avoid the OOPS, * "Device 'brook.0' does not have a release() function, * it is broken and must be fixed." */ brook_device = platform_device_alloc(DEVNAME, 0); if (!brook_device) { pr_err("%s(#%d): platform_device_alloc fail\n", __func__, __LINE__); return -ENOMEM; } err = platform_device_add(brook_device); if (err) { pr_err("%s(#%d): platform_device_add failed\n", __func__, __LINE__); goto dev_add_failed; } err = platform_driver_register(&brook_driver); if (err) { dev_err(&(brook_device->dev), "%s(#%d): platform_driver_register fail(%d)\n", __func__, __LINE__, err); goto dev_reg_failed; } return err; dev_add_failed: platform_device_put(brook_device); dev_reg_failed: platform_device_unregister(brook_device); return err; } module_init(brook_init); static void __exit brook_exit(void) { dev_info(&(brook_device->dev), "%s(#%d)\n", __func__, __LINE__); platform_device_unregister(brook_device); platform_driver_unregister(&brook_driver); } module_exit(brook_exit);
-
參考資料:
- Documentation/driver-model
- What is the difference between Platform driver and normal device driver?
- The platform device API
- Linux Kernel architecture for device drivers
- platform_driver_register()--如何match之后调用probe
- Improved dynamically allocated platform_device interface