linux module_platform_driver宏的使用
module_platform_drivers使用方式
module_platform_driver 是一个宏,用于简化在 Linux 内核模块编程中注册平台设备驱动的过程。
具体使用方法如下:
- 包含相关头文件:
在代码文件的顶部,包含所需的头文件,如:
#include <linux/module.h> #include <linux/platform_device.h>
- 定义平台设备驱动结构体:
创建一个结构体来描述平台设备驱动,包含驱动的名称、设备探测函数、设备注销函数等。例如:
static int my_probe(struct platform_device *pdev) { // 设备探测逻辑 return 0; // 返回0表示成功 } static int my_remove(struct platform_device *pdev) { // 设备注销逻辑 return 0; // 返回0表示成功 } static struct platform_driver my_platform_driver = { .driver = { .name = "my_device", .owner = THIS_MODULE, }, .probe = my_probe, .remove = my_remove, };
- 使用
module_platform_driver
宏注册平台设备驱动模块:
使用module_platform_driver
宏来注册平台设备驱动模块,将上述定义的结构体作为参数传递给宏。例如:
module_platform_driver(my_platform_driver);
通过使用 module_platform_driver 宏,内核会在模块加载时自动注册平台设备驱动,并根据驱动的名称与设备树中的设备节点进行匹配。当匹配成功时,内核将调用驱动结构体中指定的设备探测函数进行设备的初始化。当模块卸载时,内核会自动调用驱动结构体中指定的设备注销函数进行设备的注销。
请注意,上述示例中的函数和结构体名称仅供参考,实际使用时需要根据具体情况进行适配和命名。另外,还需确保内核配置中已启用平台设备支持(CONFIG_OF 和 CONFIG_PLATFORM_DRIVERS)以及相关的依赖项。
先说结论,再看细节
- module_platform_driver对module_init/module_exit进行了封装,简化了模板代码
- module_platform_driver注册的driver在匹配到设备树节点、或者匹配到对应的平台设备时才会执行probe函数
问题:
怎么匹配设备树节点或者设备?
再来看看细节
module_platform_driver
的宏定义在include/linux/platform_device.h
文件
#define module_platform_driver(__platform_driver) \ module_driver(__platform_driver, platform_driver_register, \ platform_driver_unregister)
module_driver
定义在include/linux/device/driver.h
文件
/** * module_driver() - Helper macro for drivers that don't do anything * special in module init/exit. This eliminates a lot of boilerplate. * Each module may only use this macro once, and calling it replaces * module_init() and module_exit(). * * @__driver: driver name * @__register: register function for this driver type * @__unregister: unregister function for this driver type * @...: Additional arguments to be passed to __register and __unregister. * * Use this macro to construct bus specific macros for registering * drivers, and do not use it on its own. */ #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
,这两个函数的作用就是注册和卸载平台驱动。
再来看看platform_driver_register
/* include/linux/platform_device.h */ #define platform_driver_register(drv) \ __platform_driver_register(drv, THIS_MODULE)
https://www.cnblogs.com/hueyxu/p/13659262.html
查看platform设备device和驱动driver
// 里面包含了设备树节点和platform_device_register_data两种方式注册的设备 # /sys/bus/platform/devices // 包含了module_platform_driver注册的驱动 # /sys/bus/platform/drivers
设备驱动模型
Linux内核在2.6版本中引入设备驱动模型,简化了驱动程序的编写。Linux设备驱动模型包含设备(device)、总线(bus)、类(class)和驱动(driver),它们之间相互关联。其中设备(device)和驱动(driver)通过总线(bus)绑定在一起。
Linux内核中,分别用 bus_type、 device_driver和 device结构来描述总线、驱动和设备,结构体定义详见 linux/device.h。设备和对应的驱动必须依附于同一种总线,因此 device_driver和 device结构中都包含 struct bus_type指针。
对于早期的Linux内核(2.6版本以前)来说,通常在驱动代码中xxx_driver 注册过程中调用 probe() 函数来对设备进行初始化。
引入Linux设备驱动模型下,设备和驱动可以分开注册,依赖总线完成相互绑定。系统每注册一个设备的时候,会寻找与之匹配的驱动;相反,系统每注册一个驱动的时候,会寻找与之匹配的设备。这个过程中,设备和驱动的匹配工作由总线完成。
platform总线的注册
platform总线是一种虚拟的总线,与之相对应的是PCI、I2C、SPI等实体总线。引入虚拟platform总线是为了解决某些设备无法直接依附在现有实体总线上的问题。
可以看到platform_bus_init
在do_initcalls
前面完成了初始化。
start_kernel -> rest_init -> kernel_init -> do_basic_setup //这个阶段之前完成了CPU相关初始化,设备还没工作 ->driver_init -> platform_bus_init //注册platform总线 -> do_initcalls // 开始pure_initcall、core_initcall、device_initcall等不同阶段的初始化
platform device和platform driver如何绑定
注册driver时绑定device
如下函数依次递进直接到platform_match
函数 | 解释 |
---|---|
__platform_driver_register | 该函数会对 struct device_driver 的bus、probe、remove等回调函数进行初始化,紧接着调用 driver_register(&globalfifo_driver->driver) |
driver_register | 对bus、probe、remove等回调函数初始化进行判断,保证总线和驱动上相应的函数只能存在一个。bus_add_driver(&(globalfifo_driver.driver)) ,将驱动注册到总线上 |
bus_add_driver | 如果总线使能 drivers_autoprobe ,将调用 driver_attach() 尝试匹配设备 |
driver_attach | driver_attach() 函数找到驱动依附的总线信息,遍历总线上链表 klist_devices 得到当前总线上存在的设备,然后调用 __driver_attach(dev, drv) 函数,尝试将驱动和设备绑定。 |
driver_match_device | driver_match_device(drv, dev) 回调 drv->bus->match() 函数,对于platform_bus为 platform_match() 。 |
注册device时绑定driver
函数 | 解释 |
---|---|
platform_device_register | 主要对 struct device 中基本成员进行初始化,包括 kobject 、 struct device_private 、 struct mutex 等 |
platform_device_add | device_add 添加设备 |
device_add | bus_add_device注册到总线系统中。并建立sysfs的相关目录:总线系统中建立到设备的链接,同时也在设备目录下建立到总线的subsystem链接 |
bus_probe_device | 尝试在总线上寻找可以绑定的驱动。经过层层调用,最终又调用到 driver_match_device() 和 driver_probe_device() 函数,查找总线上能和当前设备匹配的驱动,并将驱动和设备绑定在了一起。 |
device_initial_probe | |
__device_attach | 后面流程和上面一样了 |
platform_match
driver_override 有效时,尝试将驱动名字和 driver_override 匹配
- 基于设备树风格的匹配
- 基于 ACPI 风格的匹配
- 匹配 ID 表
- 匹配 platform_device 设备名和驱动的名字
static int platform_match(struct device *dev, struct device_driver *drv) { struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); /* When driver_override is set, only bind to the matching driver */ if (pdev->driver_override) return !strcmp(pdev->driver_override, drv->name); /* Attempt an OF style match first */ if (of_driver_match_device(dev, drv)) return 1; /* Then try ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; /* Then try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */ return (strcmp(pdev->name, drv->name) == 0); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具