痛苦之旅二:linux总线设备驱动模型之匹配函数

本小节想重点说一下platform device和platform driver是如何匹配上的

一. platform_match

函数位置:drivers/base/platform.c

函数描述:platform device平台设备ID按照如下格式:,其中name代表该平台设备类型的简述,例如pci等等;instance为该平台设备的编号,例如0 1 2等等;platform driver平台驱动的ID都是按照name来编排的,所以按照platform的name和driver的name来进行驱动与设备的匹配

函数返回值:返回1表示match上了,0表示没有match上

函数如何被调用:系统为platform bus定义了一个实例叫做platform_bus_type

在系统每次注册一个platform device时,该函数被调用寻找匹配的platform driver;

在系统每次注册一个platform driver时,该函数被调用寻找匹配的platform device

/* @match:	Called, perhaps multiple times, whenever a new device or driver
 *		    is added for this bus. It should return a positive value if the
 *		    given device can be handled by the given driver and zero
 *		    otherwise. It may also return error code if determining that
 *		    the driver supports the device is not possible. In case of
 *		    -EPROBE_DEFER it will queue the device for deferred probing.
 */
struct bus_type platform_bus_type = {
	.name		= "platform", /* The name of the bus */
	.dev_groups	= platform_dev_groups,
	.match		= platform_match, 
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};
/**
 * platform_match - bind platform device to platform driver.
 * @dev: device.
 * @drv: driver.
 *
 * Platform device IDs are assumed to be encoded like this:
 * "", where  is a short description of the type of
 * device, like "pci" or "floppy", and  is the enumerated
 * instance of the device, like '0' or '42'.  Driver IDs are simply
 * "".  So, extract the  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);
}

二. match的四种方式

匹配platform device和platform driver有四种可能性:

1.基于设备树方式的匹配(4.x内核)

我做过实验,我自己的i2c代码是基于设备树方式来匹配的,所以着重介绍这种方式

/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

2.基于ACPI方式的匹配

/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

3.匹配ID表

/* Then try to match against the id table */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

4.直接名字匹配

return (strcmp(pdev->name, drv->name) == 0);

三. 基于设备树方式的匹配:of_driver_match_device

我们首先看到of_driver_match_device的行为与宏CONFIG_OF有关系,那么CONFIG_OF是什么呢?

看stack overflow网站的信息:https://www.baidu.com/link?url=yIjMMvVM3ewUYoFYi0gV-JNgeDU1tp4E2xktFQQKf2ZciOnillmovkeFGZUC3K1mwF0fE9jfrDWwRu6hFp--WZ-gWVLTBiW3sIJNrSET8lT6CYgzuTyjt_fYS6zBVLtV&wd=&eqid=cbc9d3fa002e98e5000000035f1c4879

痛苦之旅二:linux总线设备驱动模型之匹配函数_第1张图片

#ifdef CONFIG_OF
/**
 * 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;
}
#else /* CONFIG_OF */

static inline int of_driver_match_device(struct device *dev,
					 const struct device_driver *drv)
{
	return 0;
}
#endif /* CONFIG_OF */
const struct of_device_id *of_match_device(const struct of_device_id *matches,
					   const struct device *dev)
{
	if ((!matches) || (!dev->of_node))
		return NULL;
	return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);
/**
 * of_match_node - Tell if an device_node has a matching of_match structure
 *	@matches:	array of of device match structures to search in
 *	@node:		the of device structure to match against
 *
 *	Low level utility function used by device matching.
 */
const struct of_device_id *of_match_node(const struct of_device_id *matches,
					 const struct device_node *node)
{
	const struct of_device_id *match;
	unsigned long flags;
 
	raw_spin_lock_irqsave(&devtree_lock, flags);
	match = __of_match_node(matches, node);
	raw_spin_unlock_irqrestore(&devtree_lock, flags);
	return match;
}
EXPORT_SYMBOL(of_match_node);
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
					   const struct device_node *node)
{
	const struct of_device_id *best_match = NULL;
	int score, best_score = 0;
 
	if (!matches)
		return NULL;
 
	for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
		score = __of_device_is_compatible(node, matches->compatible,
						  matches->type, matches->name);
		if (score > best_score) {
			best_match = matches;
			best_score = score;
		}
	}
 
	return best_match;
}
static int __of_device_is_compatible(const struct device_node *device,
				     const char *compat, const char *type, const char *name)
{
	struct property *prop;
	const char *cp;
	int index = 0, score = 0;
 
	/* Compatible match has highest priority */
	if (compat && compat[0]) {
		prop = __of_find_property(device, "compatible", NULL);
		for (cp = of_prop_next_string(prop, NULL); cp;
		     cp = of_prop_next_string(prop, cp), index++) {
			if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
				score = INT_MAX/2 - (index << 2);
				break;
			}
		}
		if (!score)
			return 0;
	}
 
	/* Matching type is better than matching name */
	if (type && type[0]) {
		if (!device->type || of_node_cmp(type, device->type))
			return 0;
		score += 2;
	}
 
	/* Matching name is a bit better than not */
	if (name && name[0]) {
		if (!device->name || of_node_cmp(name, device->name))
			return 0;
		score++;
	}
 
	return score;
}

整个的调用链如下所示,可以发现最终决定比较的函数为:__of_device_is_compatible,位于drivers/of/base.c中,进而可以看到利用device_driver->of_match_table->compatible成员与device->of_node->properties->name为compatible的properties节点的value成员做对比,最终比较的函数为of_compat_cmp,也就是strcasecmp()

即是说:是利用"compatible"来匹配的,即设备树加载之后,内核会自动把设备树节点转换成 platform_device这种格式,同时把名字放到of_node这个地方,而驱动部分的compatible是在调用platform_driver_register()之前的struct platform_driver *drv->driver.of_match_table->compatible[128]中写好了的

of_driver_match_device()
    of_match_device()
        of_match_node()
             __of_match_node()
                __of_device_is_compatible()
                    if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
static int __of_device_is_compatible(const struct device_node *device,
				     const char *compat, const char *type, const char *name)
{
	struct property *prop;
	const char *cp;
	int index = 0, score = 0;

	/* Compatible match has highest priority */
	if (compat && compat[0]) {
		prop = __of_find_property(device, "compatible", NULL);
		for (cp = of_prop_next_string(prop, NULL); cp;
		     cp = of_prop_next_string(prop, cp), index++) {
			if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
				score = INT_MAX/2 - (index << 2);
				break;
			}
		}
		if (!score)
			return 0;
	}

	/* Matching type is better than matching name */
	if (type && type[0]) {
		if (!device->type || of_node_cmp(type, device->type))
			return 0;
		score += 2;
	}

	/* Matching name is a bit better than not */
	if (name && name[0]) {
		if (!device->name || of_node_cmp(name, device->name))
			return 0;
		score++;
	}

	return score;
}

注意:下一阶段我们会看,是如何调用到匹配函数的以及匹配后又会做什么事

你可能感兴趣的:(Linux基础)