平台驱动注册函数: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",},
{ },
};