Platform总线设备驱动是如何把设备资源描述结构体(platform_device)与驱动结构体(platform_driver)匹配起来的【函数`platform_match()`详解】

后期补充说明:

2025-03-20 19:55:13 :关于 Platform总线不错的视频总结回顾讲解,请百度网盘搜索“1-3_03_SPI总线设备驱动”,然后从头开始看。

Platform总线设备驱动的匹配规则

首先在 include\linux\device.h中定义了下面这个与Platform总线 (Platform Bus)相关的结构体:

struct bus_type {
	const char		*name;
	const char		*dev_name;
	struct device		*dev_root;
	struct device_attribute	*dev_attrs;	/* use dev_groups instead */
	const struct attribute_group **bus_groups;
	const struct attribute_group **dev_groups;
	const struct attribute_group **drv_groups;

	int (*match)(struct device *dev, struct device_driver *drv);
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
	int (*probe)(struct device *dev);
	int (*remove)(struct device *dev);
	void (*shutdown)(struct device *dev);

	int (*online)(struct device *dev);
	int (*offline)(struct device *dev);

	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);

	const struct dev_pm_ops *pm;

	const struct iommu_ops *iommu_ops;

	struct subsys_private *p;
	struct lock_class_key lock_key;
};

其中的成员struct subsys_private *p;里存储着通过platform_device_register()注册进Platform总线的设备列表和通过 platform_driver_register() 注册进Platform总线的驱动列表。

drivers\base\base.h中定义了结构体subsys_privat,如下:

struct subsys_private {
	struct kset subsys;
	struct kset *devices_kset;
	struct list_head interfaces;
	struct mutex mutex;

	struct kset *drivers_kset;
	struct klist klist_devices;
	struct klist klist_drivers;
	struct blocking_notifier_head bus_notifier;
	unsigned int drivers_autoprobe:1;
	struct bus_type *bus;

	struct kset glue_dirs;
	struct class *class;
};

其成员struct kset *devices_kset;里便存储着所有注册进Platform总线的设备资源描述结构体(platform_device),成员struct kset *drivers_kset;里便存储着所有注册进Platform总线的驱动结构体(platform_driver)。

struct bus_type中的成员match就是对设备资源描述结构体(platform_device)和驱动结构体(platform_driver)进行区配的函数,当有设备资源描述结构体(platform_device)注册进Platform总线或者有驱动结构体(platform_driver)注册进总线时,Platform总线设备驱动便会调用match函数去匹配设备资源描述结构体(platform_device)和驱动结构体(platform_driver)。

具体来说Platform总线设备驱动的区配规则如下:
当一个设备资源描述结构体 (platform_device) 被注册到 Platform 总线时,Platform 总线会将该设备与已经注册到总线的驱动结构体 (platform_driver) 列表进行匹配,尝试找到合适的驱动以绑定该设备。匹配的具体规则是由 platform_driverdriver 字段中定义的 of_match_table 或者 name 来决定的。如果匹配成功,会调用驱动的 probe 函数以初始化设备。

反过来,当一个驱动结构体 (platform_driver) 被注册到 Platform 总线时,Platform 总线会将该驱动与已经注册到总线的设备资源描述结构体 (platform_device) 列表进行匹配,尝试找到合适的设备以绑定该驱动。匹配的具体规则同样由驱动的 of_match_table 或者 name 决定。如果匹配成功,会调用驱动的 probe 函数。

匹配规则的核心逻辑由总线的 match 函数实现,match 函数是总线实现设备与驱动匹配的核心。对于 Platform 总线来说,它定义了如何根据设备和驱动的信息判断两者是否匹配。
所以下面我们来仔细看看 match 函数。

drivers\base\platform.c中初始化了struct bus_type的一个实例:

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};

成员函数platform_match()就是具体的匹配函数,我们可以通过分析它来看它是如何实现匹配的。

通过分析函数platform_match()得出具体的匹配规则

相关代码

函数platform_match()的定义

函数platform_match()的位置在:drivers\base\platform.c,其代码如下:

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);
}

参数中的结构体struct device *dev的定义

函数platform_match()中的参数中涉及到的结构体struct device的定义【位置:include\linux\device.h】

struct device {
	struct device		*parent;

	struct device_private	*p;

	struct kobject kobj;
	const char		*init_name; /* initial name of the device */
	const struct device_type *type;

	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	struct bus_type	*bus;		/* type of bus device is on */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	void		*driver_data;	/* Driver data, set and get with
					   dev_set/get_drvdata */
	struct dev_links_info	links;
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;

#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
	struct irq_domain	*msi_domain;
#endif
#ifdef CONFIG_PINCTRL
	struct dev_pin_info	*pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
	struct list_head	msi_list;
#endif

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	u64		coherent_dma_mask;/* Like dma_mask, but for
					     alloc_coherent mappings as
					     not all hardware supports
					     64 bit addresses for consistent
					     allocations such descriptors. */
	unsigned long	dma_pfn_offset;

	struct device_dma_parameters *dma_parms;

	struct list_head	dma_pools;	/* dma pools (if dma'ble) */

	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
#ifdef CONFIG_DMA_CMA
	struct cma *cma_area;		/* contiguous memory area for dma
					   allocations */
#endif
	/* arch specific additions */
	struct dev_archdata	archdata;

	struct device_node	*of_node; /* associated device tree node */
	struct fwnode_handle	*fwnode; /* firmware device node */

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
	u32			id;	/* device instance */

	spinlock_t		devres_lock;
	struct list_head	devres_head;

	struct klist_node	knode_class;
	struct class		*class;
	const struct attribute_group **groups;	/* optional groups */

	void	(*release)(struct device *dev);
	struct iommu_group	*iommu_group;
	struct iommu_fwspec	*iommu_fwspec;

	bool			offline_disabled:1;
	bool			offline:1;
};

函数体中结构体struct platform_device *pdev的定义

函数platform_match()的函数体中结构体struct platform_device的定义如下【位置:include\linux\platform_device.h】:

struct platform_device {
	const char	*name;
	int		id;
	bool		id_auto;
	struct device	dev;
	u32		num_resources;
	struct resource	*resource;

	const struct platform_device_id	*id_entry;
	char *driver_override; /* Driver name to force a match */

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};

对这个结构体的详细介绍见博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145018442

参数中的结构体struct device_driver *drv的定义

函数platform_match()中的参数中涉及到的结构体device_driver的定义【位置:include\linux\device.h】

struct device_driver {
	const char		*name;
	struct bus_type		*bus;

	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */

	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */
	enum probe_type probe_type;

	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;

	int (*probe) (struct device *dev);
	int (*remove) (struct device *dev);
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;

	const struct dev_pm_ops *pm;

	struct driver_private *p;
};

函数体中结构体struct platform_driver *pdrv的定义

函数platform_match()的函数体中结构体struct platform_driver的定义如下【位置:include\linux\platform_device.h】:

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;
	const struct platform_device_id *id_table;
	bool prevent_deferred_probe;
};

devpdev和关系?drvpdrv的关系?

从上面的结构体定义中我们可以看出:
dev其实是pdev所指向的主结构体的一个成员,可利用带参数宏定义to_platform_device()得到其所在的主结构体的指针pdev;
drv其实是pdrv所指向的主结构体的一个成员,可利用带参数宏定义to_platform_driver得到其所在的主结构体的指针pdrv;

相关代码如下:

	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

代码并不复杂,仔细分析下上面的代码,得出具体的匹配规则如下:

Linux 的 Platform 总线设备驱动采用了一种分层机制,按照优先级逐步尝试设备资源描述结构体(platform_device)与驱动结构体(platform_driver)的匹配,直到找到合适的驱动结构体(platform_driver)为止。以下是具体的匹配步骤和逻辑:

1. 匹配步骤

1.1 Driver Override

  • 条件:设备资源描述结构体(platform_device)的 pdev->driver_override 成员不为空。
  • 逻辑:直接将 pdev->driver_override 指定的驱动名称与驱动结构体(platform_driver)的 drv->name 进行比较。
  • 说明
    • 这是最高优先级的匹配方式。
    • 主要用于手动指定特定驱动与设备绑定,忽略其他匹配方式。
    • 从匹配代码中我们可以看到,只要定义了pdev->driver_override ,那么不管匹配成功没有,都会产生返回值并离开这个函数,这一点要注意。

1.2 OF (Open Firmware) 匹配

  • 条件:设备树(Device Tree, DT)中存在设备与驱动结构体(platform_driver)的绑定信息。
  • 逻辑:调用 of_driver_match_device(dev, drv),通过设备树中的信息匹配设备和驱动结构体(platform_driver)。
  • 说明
    • 如果驱动结构体(platform_driver)提供了 of_match_table,则优先通过设备树的 compatible 属性进行匹配。

1.3 ACPI (Advanced Configuration and Power Interface) 匹配

  • 条件:设备的 ACPI 信息存在。
  • 逻辑:调用 acpi_driver_match_device(dev, drv),通过设备的 ACPI 信息匹配驱动结构体(platform_driver)。
  • 说明
    • 在 ACPI 规范支持的硬件平台上(如 x86),设备和驱动结构体(platform_driver)可以通过 ACPI 描述匹配。

1.4 ID Table 匹配

  • 条件:驱动提供了 ID 表(pdrv->id_table)。
  • 逻辑:调用 platform_match_id(),通过设备的 pdev->name在 ID 表中查找匹配项。
  • 说明
    • 这是比较灵活的匹配方式,驱动可以列出一组支持的设备名。其代码如下:
static const struct platform_device_id *platform_match_id(
			const struct platform_device_id *id,
			struct platform_device *pdev)
{
	while (id->name[0]) {
		if (strcmp(pdev->name, id->name) == 0) {
			pdev->id_entry = id;
			return id;
		}
		id++;
	}
	return NULL;
}

1.5 Driver Name 匹配

  • 条件:前面所有匹配方式都未成功。
  • 逻辑:直接比较设备资源描述结构体(platform_device)的 pdev->name 和驱动结构体(platform_driver)的 drv->name
  • 说明
    • 这是最基础的匹配方式,也是最简单的方式,依赖于设备和驱动名称完全一致。

2. 匹配逻辑顺序的意义

2.1 灵活性与优先级

  • driver_override 提供了最高优先级的匹配,允许用户动态控制设备和驱动的绑定。
  • OF 和 ACPI 匹配适合平台设备通过设备树或 ACPI 描述的硬件,能准确绑定特定设备。
  • ID 表和名称匹配是通用方式,适用于没有设备树或 ACPI 的设备。

2.2 回退机制

  • 匹配按照优先级逐层下降,确保设备尽可能找到匹配的驱动,即使缺少更具体的信息(如 OF 或 ACPI)。

3. 示例解析

假设我们有以下设备资源描述结构体(platform_device)和驱动结构体(platform_driver):

设备

struct platform_device my_device = {
    .name = "my_platform_device",
    .driver_override = "my_override_driver",
};

驱动

static const struct platform_device_id my_id_table[] = {
    { "my_platform_device", 0 },
    { },
};

struct platform_driver my_driver = {
    .driver = {
        .name = "my_platform_device",
    },
    .id_table = my_id_table,
};

匹配过程

  1. Driver Override

    • 设备资源描述结构体(platform_device)的 driver_override 被设置为 "my_override_driver"
    • 如果驱动结构体(platform_driver)的 drv->name 的值为 my_override_driver,将直接匹配,不考虑其他逻辑。
    • 如果没有此驱动,则继续下一步。
  2. OF 匹配

    • 如果设备树中存在设备的 compatible 属性且与驱动结构体(platform_driver)匹配,则直接绑定。
  3. ACPI 匹配

    • 如果设备的 ACPI 描述中匹配当前驱动结构体(platform_driver),则绑定。
  4. ID Table 匹配

    • 检查驱动结构体(platform_driver)的 id_table 中是否包含设备资源描述结构体(platform_device)的 name
    • 在本例中,"my_platform_device"my_id_table 中,因此匹配成功。
  5. Driver Name 匹配

    • 如果以上所有方式均失败,则检查设备资源描述结构体(platform_device) name 是否等于驱动结构体(platform_driver)的 drv->name
    • 在本例中,两者均为 "my_platform_device",所以也会匹配成功。

4. 总结

Platform 总线设备驱动的匹配机制是多层次的,包括:

  1. Driver Override:最高优先级,允许用户手动绑定。
  2. 设备树 (OF) 匹配:适合嵌入式设备和 ARM 平台。
  3. ACPI 匹配:适合 x86 平台。
  4. ID 表匹配:灵活支持多个设备。
  5. 名称匹配:最基础的方式,作为最后的兜底。

这种机制保证了设备和驱动的灵活性和兼容性,能够适应不同的平台和硬件环境。

一个具体的由设备树节点生成的结构体platform_device和驱动结构体platform_driver匹配的例子

详情见我的另一篇博文:
https://blog.youkuaiyun.com/wenhao_ir/article/details/145056170

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昊虹AI笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值