Linux kernel 中模块化的平台驱动代码介绍
介紹
在linux kernel中通過module_platform_driver來實現模塊化平臺驅動。大量的設備驅動程序都基于該種方式來實現,使用頻次非常的高,在linux kernel 5.4.124的代碼中搜索module_platform_driver共有2356次引用。
這個宏的使用方式大相徑庭,有一套成熟的代碼書寫方式,將驅動程序入口符號作為宏的參數,基本格式如下:
歷史
它的定義在include/linux/platform_device.h中,從文件的名字來看可知它存在的意義是基于platform_device的。platform_device.h這個文件在2005年的linux-2.6.15就存在了。
platform_device.h在創建初期并沒有現在這么多豐富的功能,通過platform_xxx_register來注冊驅動和設備,并沒有提供module_platform_driver這個輔助宏。
/**?platform_device.h?-?generic,?centralized?driver?model**?Copyright?(c)?2001-2003?Patrick?Mochel?<mochel@osdl.org>**?This?file?is?released?under?the?GPLv2**?See?Documentation/driver-model/?for?more?information.*/#ifndef?_PLATFORM_DEVICE_H_ #define?_PLATFORM_DEVICE_H_#include?<linux/device.h>struct?platform_device?{const?char?*?name;u32??id;struct?device?dev;u32??num_resources;struct?resource?*?resource; };#define?to_platform_device(x)?container_of((x),?struct?platform_device,?dev)extern?int?platform_device_register(struct?platform_device?*); extern?void?platform_device_unregister(struct?platform_device?*);extern?struct?bus_type?platform_bus_type; extern?struct?device?platform_bus;extern?struct?resource?*platform_get_resource(struct?platform_device?*,?unsigned?int,?unsigned?int); extern?int?platform_get_irq(struct?platform_device?*,?unsigned?int); extern?struct?resource?*platform_get_resource_byname(struct?platform_device?*,?unsigned?int,?char?*); extern?int?platform_get_irq_byname(struct?platform_device?*,?char?*); extern?int?platform_add_devices(struct?platform_device?**,?int);extern?struct?platform_device?*platform_device_register_simple(char?*,?unsigned?int,?struct?resource?*,?unsigned?int);extern?struct?platform_device?*platform_device_alloc(const?char?*name,?unsigned?int?id); extern?int?platform_device_add_resources(struct?platform_device?*pdev,?struct?resource?*res,?unsigned?int?num); extern?int?platform_device_add_data(struct?platform_device?*pdev,?void?*data,?size_t?size); extern?int?platform_device_add(struct?platform_device?*pdev); extern?void?platform_device_put(struct?platform_device?*pdev);struct?platform_driver?{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?(*resume)(struct?platform_device?*);struct?device_driver?driver; };extern?int?platform_driver_register(struct?platform_driver?*); extern?void?platform_driver_unregister(struct?platform_driver?*);#define?platform_get_drvdata(_dev)?dev_get_drvdata(&(_dev)->dev) #define?platform_set_drvdata(_dev,data)?dev_set_drvdata(&(_dev)->dev,?(data))#endif?/*?_PLATFORM_DEVICE_H_?*/platform_xxx_register這類宏在linux kernel 5.4.124中也在使用。
不能通過引用計數少或者版本迭代的原因來評價這兩類宏誰好誰壞,各自有各自的應用場景。當使用platform_xxx_register時,基本格式也是比較固定的,例如:
static?int?__init?ehci_platform_init(void) {if?(usb_disabled())return?-ENODEV;ehci_init_driver(&ehci_platform_hc_driver,?&platform_overrides);return?platform_driver_register(&ehci_mv_driver); } module_init(ehci_platform_init);static?void?__exit?ehci_platform_cleanup(void) {platform_driver_unregister(&ehci_mv_driver); } module_exit(ehci_platform_cleanup);MODULE_DESCRIPTION("Marvell?EHCI?driver"); MODULE_AUTHOR("Chao?Xie?<chao.xie@marvell.com>"); MODULE_AUTHOR("Neil?Zhang?<zhangwm@marvell.com>"); MODULE_ALIAS("mv-ehci"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(of,?ehci_mv_dt_ids);從2012年linux kernel 3.xx開始增加了module_platform_driver這個宏,一直延續至今。從module_platform_driver的定義處可以發現,它是platform_driver_register的一個封裝應用。
#define?module_platform_driver(__platform_driver)?\module_driver(__platform_driver,?platform_driver_register,?\platform_driver_unregister)存在的意義和原理
正如前面介紹的module_init這個宏,在使用它的時候要定義兩個函數以及生命兩個宏。而使用了module_platform_driver這個宏之后,只需要一行代碼就可以實現這些功能。將module_platform_driver這個宏展開之后,就是module_init這一部分代碼內容。
#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);驅動的注冊與卸載方法采用了platform.c中提供的通用API。
/***?__platform_driver_register?-?register?a?driver?for?platform-level?devices*?@drv:?platform?driver?structure*?@owner:?owning?module/driver*/ int?__platform_driver_register(struct?platform_driver?*drv,struct?module?*owner) {drv->driver.owner?=?owner;drv->driver.bus?=?&platform_bus_type;drv->driver.probe?=?platform_drv_probe;drv->driver.remove?=?platform_drv_remove;drv->driver.shutdown?=?platform_drv_shutdown;return?driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(__platform_driver_register);module_init這個宏在include/linux/module.h中定義,在kernel初始化過程中調用do_initcall()或插入驅動ko文件時得到執行。每個驅動模塊僅需實現一個module_init與module_exit即可。驅動代碼在使用module_platform_driver注冊驅動時,經過編譯后的文件內容如下:
module_init宏最終是調用了__initcall(x),定義了程序鏈接時的初始化等級為1。
#define?module_init(x)?__initcall(x); #define?__initcall(fn)?__define_initcall("1",?fn)關于initcall:
#ifdef?CONFIG_HAVE_ARCH_PREL32_RELOCATIONS #define?___define_initcall(fn,?id,?__sec)???\__ADDRESSABLE(fn)?????\asm(".section?\""?#__sec?".init\",?\"a\"?\n"?\"__initcall_"?#fn?#id?":???\n"?\".long?"?#fn?"?-?.???\n"?\".previous?????\n"); #else #define?___define_initcall(fn,?id,?__sec)?\static?initcall_t?__initcall_##fn##id?__used?\__attribute__((__section__(#__sec?".init")))?=?fn; #endif而通過module_init定義的驅動API編譯后的符號表示都增加了initcall的前綴
最后,透過一張圖看清module_platform_driver聲明的驅動調用流程:
END
推薦閱讀:
專輯|Linux文章匯總
專輯|程序人生
專輯|C語言
我的知識小密圈
關注公眾號,后臺回復「1024」獲取學習資料網盤鏈接。
歡迎點贊,關注,轉發,在看,您的每一次鼓勵,我都將銘記于心~
嵌入式Linux
微信掃描二維碼,關注我的公眾號
總結
以上是生活随笔為你收集整理的Linux kernel 中模块化的平台驱动代码介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 炒股手记.
- 下一篇: 免费下载收费音乐教程,亲测有效