分类: LINUX
2013-10-31 11:41:34
原文地址:platform机制学习总结 作者:zhangjy2008327
1)总线注册阶段:
内核启动初始化时的main.c(/init/main.c)文件中的
kernel_init() ->kernel_init_freeable()->do_basic_setup() -> driver_init() ->platform_bus_init()->{这里platform_bus_init函数分为两个部分,代码如下:
int __init platform_bus_init(void)
{
int error;
/**
* early_platform_cleanup - clean up early platform code
*/
early_platform_cleanup();
error = device_register(&platform_bus);//首先注册一个设备(第一部分),为什么要在这个地方首先注册一个设备呢?
if (error)
return error;
error = bus_register(&platform_bus_type);//在设备注册成功的情况下,再注册bus(第二部分),为什么?二者有什么关系呢?
if (error)
device_unregister(&platform_bus);//当bus注册不成功的时候,去掉所注册的设备,这又是为什么呢?
return error;
}
}//这里很明显不是单独注册bus那么简单了!为什么呢?
//下面是两个分支,而且我觉得有一定的关系,这个关系我说不清楚,请大家指点。
->device_register()->device_add()
-> bus_register(&platform_bus_type)
2)添加设备阶段:
设备注册的时候
Platform_device_register() -> platform_device_add() -> pdev->dev.bus = &platform_bus_type -> device_add(),就这样把设备给挂到虚拟的总线上。
这里有些问题:(1)是谁来调用Platform_device_register()函数呢?(2)这个过程注册的设备与在注册bus的时候注册的设备有什么不同?为什么要分开注册?(3)如果在注册bus的时候,系统中已经执行device_register()成功,并且接着执行bus_register()也成功了,那么当Platform_device_register()再次被调用的时候,会不会出错呢?还请给出详细的原因说明。
3)驱动注册阶段:
Platform_driver_register()
->driver_register() -> bus_add_driver() -> driver_attach()
->bus_for_each_dev(),
对在每个挂在虚拟的platform bus的设备作__driver_attach()->执行driver_match_device()->语句
(drv->bus->match ? drv->bus->match(dev, drv) : 1;)->platform_match(),drv->bus这个指针已经在platform_driver_register()函数注册过了,因此,当代码执行到这里的时候就会调用platform_match()(但是这里还有个问题:就是platform_driver_register()一定要先于drv->bus执行,那么platform_drviver_register()为什么要先于它执行?由谁来调用它?为什么要调用它?等等问题,请大家指点一下吧)。在该版本的源代码里面,可以从四个方面来判断device和driver,具体可参见源代码,每个匹配成功之后都会返回到driver_match_device()里面去继续执行下面的函数,于是转到->driver_probe_device()->really_probe
->查看really_probe()的代码如下:
/***********************************************************************************************/
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
/* If using pinctrl, bind pins now before probing */
ret = pinctrl_bind_pins(dev);
if (ret)
goto probe_failed;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
//其中这段代码将执行真正的probe函数,如果bus上的存在,就用bus上的probe,如果bus上的probe没有定义,就//用driver上的probe,否则就不做任何工作。
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;
}
///
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
dev_set_drvdata(dev, NULL);
if (ret == -EPROBE_DEFER) {
/* Driver requested deferred probing */
dev_info(dev, "Driver %s requests probe deferral\n", drv->name);
driver_deferred_probe_add(dev);
} else if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
} else {
pr_debug("%s: probe of %s rejects match %d\n",
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
/***********************************************************************************************/
/***********************************************************************************************/
另外一个问题是如何调到__driver_attach()这个函数的呢?个人理解,请看bus_for_each_dev()的源代码如下:
/**
* bus_for_each_dev - device iterator.
* @bus: bus type.
* @start: device to start iterating from.
* @data: data for the callback.
* @fn: function to be called for each device.
*
* Iterate over @bus's list of devices, and call @fn for each,
* passing it @data. If @start is not NULL, we use that device to
* begin iterating from.
*
* We check the return of @fn each time. If it returns anything
* other than 0, we break out and return that value.
*
* NOTE: The device that returns a non-zero value is not retained
* in any way, nor is its refcount incremented. If the caller needs
* to retain this data, it should do so, and increment the reference
* count in the supplied callback.
*/
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{//调用的时候从driver_attach传递过来的参数:
// bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus || !bus->p)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);//fn用来承接了传递过来的后面三个参数,这样当代码被执行到至时,就去调用__driver_attach()函数了
klist_iter_exit(&i);
return error;
}
/***********************************************************************************************/
那么__driver_attach()又是如何转移到platform_match()函数的呢?请查看上面->部分。
/***********************************************************************************************/
/***********************************************************************************************/
以下文字摘自网络:
判断drv->bus->match()是否执行成功,此时通过指针执行platform_match-> strncmp(pdev->name , drv->name ,
BUS_ID_SIZE),如果相符就调用really_probe(实际就是执行相应设备的platform_driver->probe(platform_device)。)
开始真正的探测,如果probe成功,则绑定设备到该驱动。
希望大家踊跃批评指正,谢谢。