总线驱动设备匹配过程分析

platform bus匹配驱动和设备的函数在文件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);
}

device为设备,driver为驱动
函数可以看出来该操作是用于给设备绑定驱动。另外设备的命名方式为., 驱动命名方式只有name,这也可以说明一个驱动管理多个设备。
首先函数从设备和启动的结构体中,反推找到platform_device和platform_driver,之后正式进入匹配的过程,
1 按照device的driver_override匹配,可见device的driver_override的优先级最高,匹配driver_override的逻辑很简单,就是device->driver_override的名称和驱动的名称是否相等,相等即匹配
2 如果上述1中没有匹配到,则使用设备树的of系列函数进行匹配 (of_driver_match_device)
3 如果of_driver_match_device没有匹配到则使用acpi进行匹配 ( acpi_driver_match_device )
4 按照id table 匹配 (platform_match_id )
5 如果上述都没有匹配到,使用driver name 和 device name 进行匹配
也就是说按照这个优先级进行设备和驱动匹配

对于1和5 没什么好说的,下面我们就对of_driver_match_device, platform_match_id 和 of_driver_match_device 进行说明

先来看of_driver_match_device函数include/linux/of_device.h


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

/**
 * of_match_device - Tell if a struct device matches an of_device_id list
 * @ids: array of of device match structures to search in
 * @dev: the of device structure to match against
 *
 * Used by a driver to check whether an platform_device present in the
 * system is in its list of supported devices.
 */
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);
}

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

of_match_node函数实现在drivers/of/base.c 文件
of_match_device实现在drivers/of/device.c 文件
of_driver_match_device 实现在include/linux/of_device.h 文件

为了弄清楚of_match_node的实现,我们首先需要知道两点,什么是 struct of_device_id *matches 什么是struct device_node *node, 这二者分别嵌在driver和device里面面,定义如下

include/linux/device.h

/**
...
 * @of_node:	Associated device tree node.
 ...
 */
struct device {
	struct device		*parent;

	struct device_private	*p;
	...
	struct device_node	*of_node; /* associated device tree node */
...
}

这部分是设备树相关的知识, 匹配是要匹配什么东西呢,我们在写驱动的时候,一般一个驱动程序可以兼容多个设备,一般情况下这些设备区别不大,在写设备驱动的时候只要少许的if else判断就能支持多种设备,所以linux内核建议这么做

以设备为例子 arch/arm/mach-omap2/display.c

struct device_node * __init omapdss_find_dss_of_node(void)
{
	struct device_node *node;

	node = of_find_compatible_node(NULL, NULL, "ti,omap2-dss");
	if (node)
		return node;

	node = of_find_compatible_node(NULL, NULL, "ti,omap3-dss");
	if (node)
		return node;

	node = of_find_compatible_node(NULL, NULL, "ti,omap4-dss");
	if (node)
		return node;

	node = of_find_compatible_node(NULL, NULL, "ti,omap5-dss");
	if (node)
		return node;

	return NULL;
}

函数omapdss_find_dss_of_node用于加载device node,of_find_compatible_node用于找到和设备兼容的配置

/**
 *	of_find_compatible_node - Find a node based on type and one of the
 *                                tokens in its "compatible" property
 *	@from:		The node to start searching from or NULL, the node
 *			you pass will not be searched, only the next one
 *			will; typically, you pass what the previous call
 *			returned. of_node_put() will be called on it
 *	@type:		The type string to match "device_type" or NULL to ignore
 *	@compatible:	The string to match to one of the tokens in the device
 *			"compatible" list.
 *
 *	Returns a node pointer with refcount incremented, use
 *	of_node_put() on it when done.
 */
struct device_node *of_find_compatible_node(struct device_node *from,
	const char *type, const char *compatible)
{
	struct device_node *np;
	unsigned long flags;

	raw_spin_lock_irqsave(&devtree_lock, flags);
	np = from ? from->allnext : of_allnodes;
	for (; np; np = np->allnext) {
		if (__of_device_is_compatible(np, compatible, type, NULL) &&
		    of_node_get(np))
			break;
	}
	of_node_put(from);
	raw_spin_unlock_irqrestore(&devtree_lock, flags);
	return np;
}

of_allnodes是解析设备树之后生成的所有device node节点,遍历节点要找到与我们兼容的节点,然后这个device node填充到我们的device结构体
drivers/of/base.c

/**
 * __of_device_is_compatible() - Check if the node matches given constraints
 * @device: pointer to node
 * @compat: required compatible string, NULL or "" for any match
 * @type: required device_type value, NULL or "" for any match
 * @name: required node name, NULL or "" for any match
 *
 * Checks if the given @compat, @type and @name strings match the
 * properties of the given @device. A constraints can be skipped by
 * passing NULL or an empty string as the constraint.
 *
 * Returns 0 for no match, and a positive integer on match. The return
 * value is a relative score with larger values indicating better
 * matches. The score is weighted for the most specific compatible value
 * to get the highest score. Matching type is next, followed by matching
 * name. Practically speaking, this results in the following priority
 * order for matches:
 *
 * 1. specific compatible && type && name
 * 2. specific compatible && type
 * 3. specific compatible && name
 * 4. specific compatible
 * 5. general compatible && type && name
 * 6. general compatible && type
 * 7. general compatible && name
 * 8. general compatible
 * 9. type && name
 * 10. type
 * 11. name
 */
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;
}
#define of_compat_cmp(s1, s2, l)	strncmp((s1), (s2), (l))
#define of_prop_cmp(s1, s2)		strcasecmp((s1), (s2))
#define of_node_cmp(s1, s2)		strcmp((s1), (s2))

注释中说找兼容节点,根据compatible字符串找到匹配的节点,使用“”匹配所有,返回0表示没有匹配的节点,返回正数则表示一个分数,分数越高表示越匹配,另外除了compatible字符串,参考的值还有type 和 name, 表示类型和名称,匹配的优先级规则如下

    1. specific compatible && type && name
    1. specific compatible && type
    1. specific compatible && name
    1. specific compatible
    1. general compatible && type && name
    1. general compatible && type
    1. general compatible && name
    1. general compatible
    1. type && name
    1. type
    1. name

相信看了注释和几个宏,这个函数就不用再说明了吧

/**
* struct device_driver - The basic device driver structure
* @name:	Name of the device driver.
* @bus:	The bus which the device of this driver belongs to.
* @owner:	The module owner.
* @mod_name:	Used for built-in modules.
* @suppress_bind_attrs: Disables bind/unbind via sysfs.
* @of_match_table: The open firmware table.
* @acpi_match_table: The ACPI match table.
* @probe:	Called to query the existence of a specific device,
*		whether this driver can work with it, and bind the driver
*		to a specific device.
* @remove:	Called when the device is removed from the system to
*		unbind a device from this driver.
* @shutdown:	Called at shut-down time to quiesce the device.
* @suspend:	Called to put the device to sleep mode. Usually to a
*		low power state.
* @resume:	Called to bring a device from sleep mode.
* @groups:	Default attributes that get created by the driver core
*		automatically.
* @pm:		Power management operations of the device which matched
*		this driver.
* @p:		Driver core's private data, no one other than the driver
*		core can touch this.
*
* The device driver-model tracks all of the drivers known to the system.
* The main reason for this tracking is to enable the driver core to match
* up drivers with new devices. Once drivers are known objects within the
* system, however, a number of other things become possible. Device drivers
* can export information and configuration variables that are independent
* of any specific device.
*/
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;
};

看完了device 的兼容属性,那么driver支持哪些设备呢,这些属性就放在driver的of_match_table中,如下

/**
 * struct device_driver - The basic device driver structure
 * @name:	Name of the device driver.
 * @bus:	The bus which the device of this driver belongs to.
 * @owner:	The module owner.
 * @mod_name:	Used for built-in modules.
 * @suppress_bind_attrs: Disables bind/unbind via sysfs.
 * @of_match_table: The open firmware table.
 * @acpi_match_table: The ACPI match table.
 * @probe:	Called to query the existence of a specific device,
 *		whether this driver can work with it, and bind the driver
 *		to a specific device.
 * @remove:	Called when the device is removed from the system to
 *		unbind a device from this driver.
 * @shutdown:	Called at shut-down time to quiesce the device.
 * @suspend:	Called to put the device to sleep mode. Usually to a
 *		low power state.
 * @resume:	Called to bring a device from sleep mode.
 * @groups:	Default attributes that get created by the driver core
 *		automatically.
 * @pm:		Power management operations of the device which matched
 *		this driver.
 * @p:		Driver core's private data, no one other than the driver
 *		core can touch this.
 *
 * The device driver-model tracks all of the drivers known to the system.
 * The main reason for this tracking is to enable the driver core to match
 * up drivers with new devices. Once drivers are known objects within the
 * system, however, a number of other things become possible. Device drivers
 * can export information and configuration variables that are independent
 * of any specific device.
 */
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;
};

注释也很简单  The open firmware table
我们以goldfish设备为例子,进行说明
drivers/platform/goldfish/goldfish_pipe.c

static const struct of_device_id goldfish_pipe_of_match[] = {
	{ .compatible = "generic,android-pipe", },
	{},
};
static struct platform_driver goldfish_pipe = {
	.probe = goldfish_pipe_probe,
	.remove = goldfish_pipe_remove,
	.driver = {
		.name = "goldfish_pipe",
		.owner = THIS_MODULE,
		.of_match_table = goldfish_pipe_of_match,
		.acpi_match_table = ACPI_PTR(goldfish_pipe_acpi_match),
	}
};

一般兼容属性前边为板子信息,后边为设备信息

现在就可以分析__of_match_node函数了


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

就是找到一个分数最高的来作为匹配的of_device_id, 再来看看

分析完了of(Open Firmware)匹配,我们再来看看acpi的匹配方式

static inline bool acpi_driver_match_device(struct device *dev,
					    const struct device_driver *drv)
{
	return !!acpi_match_device(drv->acpi_match_table, dev);
}

/**
 * acpi_match_device - Match a struct device against a given list of ACPI IDs
 * @ids: Array of struct acpi_device_id object to match against.
 * @dev: The device structure to match.
 *
 * Check if @dev has a valid ACPI handle and if there is a struct acpi_device
 * object for that handle and use that object to match against a given list of
 * device IDs.
 *
 * Return a pointer to the first matching ID on success or %NULL on failure.
 */
const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
					       const struct device *dev)
{
	struct acpi_device *adev;
	acpi_handle handle = ACPI_HANDLE(dev);

	if (!ids || !handle || acpi_bus_get_device(handle, &adev))
		return NULL;

	if (!acpi_companion_match(dev))
		return NULL;

	return __acpi_match_device(adev, ids);
}

首先从device中找到对应的acpi_device。如何将device和acpi_device关联起来我们会单独写一篇文章分析。

找到acpi_device设备后就可以进行匹配了,acpi设备使用两种方式匹配
1 acpi伙伴匹配
2 id方式匹配

先来看acpi伙伴匹配 acpi_companion_match

/*
 * acpi_companion_match() - Can we match via ACPI companion device
 * @dev: Device in question
 *
 * Check if the given device has an ACPI companion and if that companion has
 * a valid list of PNP IDs, and if the device is the first (primary) physical
 * device associated with it.
 *
 * If multiple physical devices are attached to a single ACPI companion, we need
 * to be careful.  The usage scenario for this kind of relationship is that all
 * of the physical devices in question use resources provided by the ACPI
 * companion.  A typical case is an MFD device where all the sub-devices share
 * the parent's ACPI companion.  In such cases we can only allow the primary
 * (first) physical device to be matched with the help of the companion's PNP
 * IDs.
 *
 * Additional physical devices sharing the ACPI companion can still use
 * resources available from it but they will be matched normally using functions
 * provided by their bus types (and analogously for their modalias).
 */
static bool acpi_companion_match(const struct device *dev)
{
	struct acpi_device *adev;
	bool ret;

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

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

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

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

	return ret;
}

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

如果pnp节点没有匹配则使用__acpi_match_device函数匹配acpi_device和驱动支持的id


static const struct acpi_device_id *__acpi_match_device(
	struct acpi_device *device, const struct acpi_device_id *ids)
{
	const struct acpi_device_id *id;
	struct acpi_hardware_id *hwid;

	/*
	 * If the device is not present, it is unnecessary to load device
	 * driver for it.
	 */
	if (!device->status.present)
		return NULL;

	for (id = ids; id->id[0]; id++)
		list_for_each_entry(hwid, &device->pnp.ids, list)
			if (!strcmp((char *) id->id, hwid->id))
				return id;

	return NULL;
}

函数比较acpi_device的pnp id是否与驱动支持的id字符串相等,相等则匹配
我们以goldfish_pipe驱动看看它支持的id是什么样子的

static const struct acpi_device_id goldfish_pipe_acpi_match[] = {
	{ "GFSH0003", 0 },
	{ },
};

直接看id_table把
platform_match_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;
}

也是名字匹配即可

关于id_table 和 of_match_table可以参考https://blog.csdn.net/shenhuxi_yu/article/details/77921608

关键点一句话

驱动程序匹配有几种机制。id_table用于从剥离的设备树条目(没有供应商部分)中查找匹配,而of_match_table用于从完整的设备树条目(具有供应商部分的条目)中查找匹配。
也就是说of_match_table要求匹配的信息更多,所以优先级更高

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值