Chinaunix首页 | 论坛 | 博客
  • 博客访问: 478318
  • 博文数量: 285
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 629
  • 用 户 组: 普通用户
  • 注册时间: 2013-10-14 17:53
个人简介

相信自己,快乐每一天

文章分类

全部博文(285)

分类: LINUX

2013-10-28 16:18:58

最近正在学习驱动程序,这里把一些感受写下来,以备回忆,其中的问题还请大家指点一二,文章肯定有不对之处,肯请大家指教和补充,在此,小弟非常感谢。参考的Linux Kernel version 3.10.4。

platform机制分为三个步骤

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成功,则绑定设备到该驱动。

希望大家踊跃批评指正,谢谢。


阅读(1896) | 评论(2) | 转发(2) |
给主人留下些什么吧!~~

zhangjy20083272013-10-30 17:49:15

无论是device还是driver进行注册的时候,它们都会做一件事情:就是先找bus_type,挂在bus上,再去找彼此
(即device->driver,或者driver->device),不对之处,敬请指正。

zhangjy20083272013-10-30 08:59:16

当bus注册不成功的时候,去掉所注册的设备,这又是为什么呢?
个人对这个问题的理解:可能是因为Linux认为或者只允许只有device通过bus才可以与driver进行信息通信的原因吧,如果bus没有注册成功的话,那么系统可能认为device存在着也就没有意义了吧,因此,bus不成功的时候,出于资源利用等系统其它的角度考虑就把device的注册也撤消了吧。

真诚欢迎大家讨论,欢迎大家批评指正,一起学习,共同进步,是你的目标,也是我的目标,希望大家留言进行学习和交流。