platform总线驱动详解

1.什么是platform总线

 platform总线是一条虚拟总线(只有一条),这类总线没有相应的硬件结构platform_device为相应的设备,platform_driver为相应的驱动。与传统的bus/device/driver机制相比,platform由内核统一进行管理,提高了代码的可移植性和安全性。

 Soc系统中集成的独立外设控制器、挂接在Soc内存空间的外设是不依附于usb、i2c、pci、spi总线的。所以Linux驱动模型为了保持完整性,将这些设备挂在一条虚拟的总线上(platform总线)。

2.总线驱动模型

 在总线设备驱动模型中,需要关注总线、设备和驱动这三个实体、总线会将设备和驱动绑定。

 当向内核注册驱动程序时,要调用platform_driver_register函数将驱动程序注册到总线,将其放入所属总线的drv链表中,注册驱动的时候还会调用所属总线的match函数寻找总线上与之匹配的设备,如果找到想匹配的设备则会调用和对应的probe函数将相应的设备和驱动进行绑定,这个匹配过程是由总线自动完成的。

3.Platform介绍

Platform 平台设备驱动模型的作用是将驱动的实现和资源分离,是一个虚拟的总线平台。这其中存在三个成员platform_bus, platform_deviceplatform_driver

platform_bus:由链表实现,不对应实际的物理总线。

platform_device:驱动的资源比如一些 I/O端口,中断号之类的。

platform_driver:驱动的功能实现比如 注册驱动,实现file_operations 等

3.1 Platform_bus

 Linux系统内核使用bus_type结构体表示总线,此结构体定义在include/linux/device.h,bus_type结构体内如如下:

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

platform总线是bus_type的一个具体实例,定义在文件drivers/base/platform.c,platform总线定义如下:

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match, //用来device与driver之间的匹配
	.uevent		= platform_uevent, //热插拔操作函数
	.pm		= &platform_dev_pm_ops, //休眠唤醒操作集
};

3.2 Platform_device

 设备结构体源码在include/linux/platform_device.h中定义:

struct platform_device {
	const char	*name;  //用来匹配platform->id_table->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;
};

3.3 Platform_driver

 platform_driver结构体表示platform驱动,此结构体定义在文件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;
};

platform_driver除了driver结构体外,还定义了一些函数,这些函数与结构体driver_device中的函数意义一样,只不过它们的输入参数变成了platform_device

总结

platform_driver里的device_driver结构体跟platform_device里的device结构体是用来将设备与驱动进行匹配绑定,而platform_driver中的其他函数是用来匹配后执行的方法。

4.驱动和设备的匹配

 platform_bus_type就是platform平台总线,其中platform_match就是匹配函数。定义在drivers/base/platform.c中

/**
 * platform_match - bind platform device to platform driver.
 * @dev: device.
 * @drv: driver.
 *
 * Platform device IDs are assumed to be encoded like this:
 * "<name><instance>", where <name> is a short description of the type of
 * device, like "pci" or "floppy", and <instance> is the enumerated
 * instance of the device, like '0' or '42'.  Driver IDs are simply
 * "<name>".  So, extract the <name> from the platform_device structure,
 * and compare it against the name of the driver. Return whether they match
 * or not.
 */
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);
}

以上有驱动与设备的匹配有四种方式

4.1 第一种匹配方式

OF类型的匹配,也就是设备树采用的匹配方式of_driver_match_device函数定义在文件include/linux/of_device.h中。device_driver结构体(表示设备驱动)中有个名为of_match_table的成员变量,此成员变量保存着驱动的compatible匹配表,设备树中的每个设备节点的compatible属性会和of_match_table表中的所有成员比较,查看是否有相同的条目,如果有的话就表示设备和此驱动匹配,设备和驱动匹配成功以后probe函数就是执行。

/**
 * of_driver_match_device - Tell if a driver's of_match_table matches a device.
 * @drv: the device_driver structure to test
 * @dev: the device structure to match against
 */
static inline int of_driver_match_device(struct device *dev,
					 const struct device_driver *drv)
{
	return of_match_device(drv->of_match_table, dev) != NULL;
}

device_driver结构体表示如下:

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 */

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

of_match_table就是采用设备树的时候驱动使用的匹配表,此结构体定义在文件inlcude/linux/mod_devicetable.h中

 struct of_device_id {
	 char name[32];
	 char type[32];
	 char compatible[128];
	 const void *data;
};

其中of_match_table成员变量保存着驱动的compatible匹配表。
例如:

4.2 第二种匹配方式

 如果of_driver_match_device没有匹配到则使用acpi进行匹配。

 首先从device中找到对应的acpi_device,找到acpi_device设备后就可以进行匹配了,acpi设备使用两种方式匹配。

1. acpi伙伴匹配
源码路径:drivers/acpi/scan.c

static struct acpi_device *acpi_companion_match(const struct device *dev)
{
	struct acpi_device *adev;
	struct mutex *physical_node_lock;

	adev = ACPI_COMPANION(dev);
	if (!adev)
		return NULL;

	if (list_empty(&adev->pnp.ids))
		return NULL;

	physical_node_lock = &adev->physical_node_lock;
	mutex_lock(physical_node_lock);
	if (list_empty(&adev->physical_node_list)) {
		adev = NULL;
	} else {
		const struct acpi_device_physical_node *node;

		node = list_first_entry(&adev->physical_node_list,
					struct acpi_device_physical_node, node);
		if (node->dev != dev)
			adev = NULL;
	}
	mutex_unlock(physical_node_lock);

	return adev;
}

该函数从acpi_device的pnp节点找到伙伴节点,并判断设备是否为该pnp节点上的第一个物理节点,如果是物理节点则匹配成功。为什么只使用第一个节点,注释上有说明。

4.3 第二种匹配方式

https://blog.csdn.net/weixin_45309916/article/details/118633436

posted @ 2023-08-21 10:11  caseyzz  阅读(370)  评论(0)    收藏  举报