天嵌 ARM开发社区

 找回密码
 注册
查看: 2815|回复: 1

关于platform设备驱动中platform_device和platform_driver的注册顺序

[复制链接]
vanbreaker 发表于 2012-3-19 21:29:21 | 显示全部楼层 |阅读模式
对于platform设备的概念在此不再赘述,在这里主要是和大家分享一下刚刚解决的一个问题,在网上的很多资料里说对于Platform设备,必须先注册platform_device然后再注册platform_driver,但我发现的事实似乎不是如此。这个结论是小弟我在一个简单的platform设备测试程序中得到的,platform设备测试程序如下:
plat_dev.c:
  1. #include<linux/module.h>
  2. #include<linux/init.h>
  3. #include<linux/kernel.h>
  4. #include<linux/device.h>
  5. #include<linux/platform_device.h>

  6. struct platform_device my_dev =
  7. {
  8.         .name = "my_dev",
  9.         .id   = -1,
  10. };



  11. static int __init platform_dev_init(void)
  12. {
  13.         int ret;
  14.        
  15.         ret = platform_device_register(&my_dev);

  16.         if(ret)
  17.         {
  18.                 printk(KERN_NOTICE "##my_dev failed to register.\n");
  19.         }
  20.         else
  21.         {
  22.                 printk(KERN_NOTICE "##register my_dev.\n");
  23.         }
  24.         return ret;
  25. }



  26. static void __exit platform_dev_exit(void)
  27. {
  28.         platform_device_unregister(&my_dev);
  29.         printk(KERN_NOTICE "##my_dev unregister.\n");
  30. }



  31. module_init(platform_dev_init);
  32. module_exit(platform_dev_exit);
  33. MODULE_LICENSE("GPL");

复制代码
plat_drv.c:
  1. #include<linux/module.h>
  2. #include<linux/init.h>
  3. #include<linux/kernel.h>
  4. #include<linux/device.h>
  5. #include<linux/platform_device.h>


  6. static int probe_test(struct platform_device *pdev)
  7. {
  8.         printk(KERN_NOTICE "##driver found device.\n");
  9.         return 0;
  10. }



  11. static int remove_test(struct platform_device *pdev)
  12. {
  13.         printk(KERN_DEBUG "##remove device.\n");
  14.         return 0;
  15. }



  16. struct platform_driver my_drv =
  17. {
  18.         .probe = probe_test,
  19.         .remove = remove_test,
  20.         .driver =
  21.         {
  22.                 .name = "my_dev",
  23.                 .owner = THIS_MODULE,
  24.         },       
  25. };


  26. static int __init platform_drv_init(void)
  27. {
  28.         int ret;
  29.        
  30.         ret = platform_driver_register(&my_drv);

  31.         if(ret)
  32.         {
  33.                 printk(KERN_NOTICE "##driver failed to register.\n");
  34.         }
  35.         else
  36.         {
  37.                 printk(KERN_NOTICE "##register driver.\n",ret);
  38.         }
  39.         return ret;
  40. }



  41. static void __exit platform_drv_exit(void)
  42. {
  43.         platform_driver_unregister(&my_drv);
  44.         printk(KERN_NOTICE "##driver unregister.\n");
  45. }



  46. module_init(platform_drv_init);
  47. module_exit(platform_drv_exit);
  48. MODULE_LICENSE("GPL");
复制代码
  1. [root@EmbedSky platform]# insmod plat_drv.ko
  2. ##register driver.
  3. [root@EmbedSky platform]# insmod plat_dev.ko
  4. ##driver found device.
  5. ##register my_dev.
复制代码
虽然测试结果只有寥寥几行,但已足以令我傻眼……对于此我当时有两个疑惑:
1.我是先加载的plat_drv.ko,也就是说相应的platform_device还未被注册,可是驱动却能注册成功。。
2.为何probe在加载plat_dev.ko,也就是说相应的Platform_device被注册时会被调用

这样看似乎是没法看出问题所在的。。。没办法,只能扎进源码里找答案了。。
我首先解决的是第二个问题,跟踪platform_device_register(),在platform_device_register()-->platform_device_add()-->device_add()-->bus_attach_device()-->bus_for_each_drv()中,遍历platform_bus下的klist_drivers,并且通过调用__device_attach()函数进行匹配,在__device_attach()中,如果匹配成功,则调用driver_probe_device()-->really_probe-->driver->probe,在此调用driver中得probe....所以就算是后注册platform_device,只要能够匹配到对应的驱动,驱动中的probe函数也一样能被调用!
然后是第一个问题,跟踪platform_driver_register(),发现在platform_driver_register()-->driver_register()-->bus_add_driver()中有这么一段:
  1. if (drv->bus->p->drivers_autoprobe) {
  2.                 error = driver_attach(drv);/*搜寻匹配的设备与驱动相关联*/
  3.                 if (error)
  4.                         goto out_unregister;
  5.         }
复制代码
driver_attach()的内容:
  1. int driver_attach(struct device_driver *drv)
  2. {
  3.         return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
  4. }
复制代码
__driver_attach的内容:
  1. static int __driver_attach(struct device *dev, void *data)
  2. {
  3.         struct device_driver *drv = data;

  4.         /*
  5.          * Lock device and try to bind to it. We drop the error
  6.          * here and always return 0, because we need to keep trying
  7.          * to bind to devices and some drivers will return an error
  8.          * simply if it didn't support the device.
  9.          *
  10.          * driver_probe_device() will spit a warning if there
  11.          * is an error.
  12.          */

  13.         if (!driver_match_device(drv, dev))
  14.                 return 0;

  15.         if (dev->parent)        /* Needed for USB */
  16.                 down(&dev->parent->sem);
  17.         down(&dev->sem);
  18.         if (!dev->driver)
  19.                 driver_probe_device(drv, dev);
  20.         up(&dev->sem);
  21.         if (dev->parent)
  22.                 up(&dev->parent->sem);

  23.         return 0;
  24. }
复制代码
这个函数是问题的关键,可以发现,无论是否和一个设备匹配,该函数最终的会返回0,而在bus_for_each_dev()中,遍历klist_devices,调用__device_attach和每一个device进行匹配,即使最后找不到匹配的设备,bus_for_each_dev()返回的error值也是0!相关代码:
  1. int bus_for_each_dev(struct bus_type *bus, struct device *start,
  2.                      void *data, int (*fn)(struct device *, void *))
  3. {
  4.         struct klist_iter i;
  5.         struct device *dev;
  6.         int error = 0;

  7.         if (!bus)
  8.                 return -EINVAL;

  9.         klist_iter_init_node(&bus->p->klist_devices, &i,
  10.                              (start ? &start->p->knode_bus : NULL));
  11.         while ((dev = next_device(&i)) && !error)
  12.                 error = fn(dev, data);
  13.         klist_iter_exit(&i);
  14.         return error;
  15. }
复制代码
也就是说最后返回给bus_add_driver()的error值是0,这样即使没有找到匹配的设备也不会导致驱动的注册失败!
得到的结论是:不论是注册device还是driver都会进行匹配,并且在匹配成功的情况下调用driver->probe,也就是说在这一点上,并不一定要先注册设备再注册驱动。。。即使是后注册设备也能调用probe...只是习惯上来说(或者其他我不知道的原因)应该是设备在先驱动在后.
就这样一个比较二的问题还是可以看出,驱动的学习最终还是要落在对内核源码的理解上,要对其内部的细节和原理进行探索,这样自己肚子里才感觉有东西,不能只停留在书本或者网友对其的分析阐述上!以上全属本人愚见,分析不到位之处望各路朋友指出!
TQ-lkp 发表于 2012-3-20 09:31:41 | 显示全部楼层
做驱动要的就是楼主这种精神
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

i.MX8系列ARM cortex A53 M4 工控板上一条 /1 下一条

Archiver|手机版|小黑屋|天嵌 嵌入式开发社区 ( 粤ICP备11094220号 )

GMT+8, 2024-5-29 18:43 , Processed in 1.046875 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表