platform总线驱动的注册过程

平台驱动注册函数:platform_driver_register位于include\linux\platform_device.h

#define platform_driver_register(drv) \
	__platform_driver_register(drv, THIS_MODULE)
int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;
	drv->driver.probe = platform_drv_probe;
	drv->driver.remove = platform_drv_remove;
	drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}

其实总的来说platform设备驱动的注册和一般驱动的注册非常像,从上面的代码来看仅仅就是对owner、bus等几个变量重新赋值了而已。
driver_register -> bus_add_driver

int bus_add_driver(struct device_driver *drv)
{
	.......
	/*驱动私有数据的回指指针,驱动也指向私有数据*/
	priv->driver = drv;
	drv->p = priv;
	priv->kobj.kset = bus->p->drivers_kset;
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	if (error)
		goto out_unregister;
	/*这个成员已经在注册总线bus_register的时候已经被设置成1,所以说都会执行到driver_attach函数*/
	if (drv->bus->p->drivers_autoprobe) {
		error = driver_attach(drv);
		if (error)
			goto out_unregister;
	}
	........

bus_add_driver -> driver_attach -> bus_for_each_dev

int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus)
		return -EINVAL;
	/*如需了解,需要看klist相关的源码,klist是专门为驱动添加的一种链表*/
	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	/* 在这里需要进行设备和驱动的匹配,遍历总线上面所有的设备device
	* 对于总线上的所有device,都会执行fn函数也就是__driver_attach,
	* 如果成功,则返回1,循环终止,如果失败返回0,则匹配下一个
	*/
	while ((dev = next_device(&i)) && !error)
		error = fn(dev, data);
	klist_iter_exit(&i);
	return error;
}

让我们看看__driver_attach这个函数:

static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;

	/*
	 * Lock device and try to bind to it. We drop the error
	 * here and always return 0, because we need to keep trying
	 * to bind to devices and some drivers will return an error
	 * simply if it didn't support the device.
	 *
	 * driver_probe_device() will spit a warning if there
	 * is an error.
	 */
	/*使用总线匹配设备和驱动,如果总线的match函数存在,则调用这个
	* 函数,否则,直接返回1.一般match函数都要存在,这样才合理
	*/
	if (!driver_match_device(drv, dev))
		return 0;

	if (dev->parent)	/* Needed for USB */
		down(&dev->parent->sem);
	down(&dev->sem);
	if (!dev->driver)
		driver_probe_device(drv, dev); //驱动的probe函数在这里面执行
	up(&dev->sem);
	if (dev->parent)
		up(&dev->parent->sem);

	return 0;
}

对于驱动和设备是否能匹配上,就要看匹配的具体过程,在driver_match_device函数里面,使用的是bus->match函数,对于platform_bus,这个函数的定义如下:

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

可以看到,驱动和设备的匹配有四种情况,只要有一种匹配到了,就说明成功了,来看看哪四种情况:

  • 第一种:of_driver_match_device(dev, drv)
    跟踪此函数,最后调用__of_device_is_compatible
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_id 结构体的三种成员匹配

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

compatible优先级最高,最先匹配,匹配设备的compatible属性;type其次,匹配device->type,最后匹配name,注意这里不是driver的name,而是of_device_id 的name,driver有一个of_device_id 结点.

  • 第二种:acpi_driver_match_device(dev, drv)这种没怎么见过,不分析
  • 第三种:platform_match_id
    这种看函数名,明显就是和platform本身相关的,定义在,用于比较的也是platform_device里面的一个结构体.具体不深入过程比较简单.
  • 第四种:就是匹配设备和驱动的名称

由上面的匹配过程可知匹配compatible属性优先级是最高的,在平常,也是使用这个属性匹配.
说完匹配过程,就要说驱动探测设备的过程了,回到__driver_attach函数,这个函数里面有这一句:

if (!dev->driver)
		driver_probe_device(drv, dev);

跟进去得到这样的结果:

	if (dev->bus->probe) {
		ret = dev->bus->probe(dev);
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}

我们看到probe又有两种过程,真够折腾的,但是还好,platform总线的probe成员并没有赋值,因此直接就运行驱动的probe函数.

经过上面一大串的分析,终于运行到了驱动的probe函数.在实际的开发中,只要注意一点就行了,即保证驱动和设备能够匹配上,成功加载上驱动的probe函数,因此一定要使得设备的compatible属性和驱动相同.配置如下:

static struct platform_driver qpnp_leds_driver = {
	.driver		= {
		.name		= "leds", //仅仅只有这个属性也能匹配的上
		.of_match_table	= match_table,
	},
	.probe		= leds_probe,
	.remove		= leds_remove,
};

static const struct of_device_id match_table[] = {
	{ .compatible = "leds",},
	{ },
};

猜你喜欢

转载自blog.csdn.net/whoami_I/article/details/87877595
今日推荐