linux module_platform_driver宏的使用

发布时间 2023-06-01 21:32:26作者: zephyr~

module_platform_drivers使用方式

module_platform_driver 是一个宏,用于简化在 Linux 内核模块编程中注册平台设备驱动的过程。

具体使用方法如下:

  1. 包含相关头文件:
    在代码文件的顶部,包含所需的头文件,如:
#include <linux/module.h>
#include <linux/platform_device.h>
  1. 定义平台设备驱动结构体:
    创建一个结构体来描述平台设备驱动,包含驱动的名称、设备探测函数、设备注销函数等。例如:
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,
};
  1. 使用 module_platform_driver 宏注册平台设备驱动模块:
    使用 module_platform_driver 宏来注册平台设备驱动模块,将上述定义的结构体作为参数传递给宏。例如:

module_platform_driver(my_platform_driver);

通过使用 module_platform_driver 宏,内核会在模块加载时自动注册平台设备驱动,并根据驱动的名称与设备树中的设备节点进行匹配。当匹配成功时,内核将调用驱动结构体中指定的设备探测函数进行设备的初始化。当模块卸载时,内核会自动调用驱动结构体中指定的设备注销函数进行设备的注销。

请注意,上述示例中的函数和结构体名称仅供参考,实际使用时需要根据具体情况进行适配和命名。另外,还需确保内核配置中已启用平台设备支持(CONFIG_OF 和 CONFIG_PLATFORM_DRIVERS)以及相关的依赖项。

先说结论,再看细节

  1. module_platform_driver对module_init/module_exit进行了封装,简化了模板代码
  2. 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_registerplatform_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_initdo_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);
}

Ref

https://blog.csdn.net/m0_37765662/article/details/106490792