设备模型、设备与驱动关联的全过程分析 platform_device platform_driver driver bus关系

来源:互联网 发布:话剧场景 知乎 编辑:程序博客网 时间:2024/05/16 06:37

[cpp]
 view plaincopy
  1. 1.     平台驱动注册过程  
  2.   
  3.     具体的目录如下:  
  4.   
  5. 关于设备模型、设备与驱动关联的全过程分析。... 1  
  6.   
  7. 1.1 at91_i2c_init()函数... 1  
  8.   
  9. 1.2 platform_driver_register()函数... 2  
  10.   
  11. 1.3 driver_register()函数... 4  
  12.   
  13. 1.4 bus_add_driver()函数... 5  
  14.   
  15. 1.5 dd.c文件driver_attach()函数... 7  
[cpp] view plaincopy
  1.   1.1 at91_i2c_init()函数  
  2.     在文件drivers/i2c/busses/i2c-at91.c中,定义了结构体struct platform_driver并进行了初始化, 通过使用module_init()宏进行声明,当模块被加载到内核时会调用 at91_i2c_init()函数。在此函数中,
  3. 调用了platform_driver_register()函数来完成注册。  
[cpp] view plaincopy
  1.  static struct platform_driver at91_i2c_driver = {  
  2.   
  3. .probe= at91_i2c_probe,  
  4.   
  5. .remove = __devexit_p(at91_i2c_remove),  
  6.   
  7. .suspend= at91_i2c_suspend,  
  8.   
  9. .resume= at91_i2c_resume,  
  10.   
  11. .driver= {  
  12.   
  13. .name "at91_i2c",  
  14. .owner= THIS_MODULE,    
  15. },    
  16. };  

  17. static int __init at91_i2c_init(void)  
  18. {  
  19.      return platform_driver_register(&at91_i2c_driver);  
  20. }  
  21.    
  22.   
  23.  
copy
1.2 platform_driver_register()函数 
  1. 在文件drivers/base/platform.c中,实现并导出了platform_driver_register()函数,以便使其他模块中 
  2. 的函数可以调用此函数。它在完成简单的包装后,调用了driver_register()函数,完成了从平台实现到Linux内核实现的过渡。 
  3. cop     
  1. int platform_driver_register(struct platform_driver *drv)
  2. {    /*设置成platform_bus_type这个很重要,因为driver和device是通过bus联系在一起的,具体在本例
  3. 中是通过 platform_bus_type中注册的回调例程和属性来是实现的, driver与device的匹配就是通过
  4. platform_bus_type注册的回到例程platform_match ()来完成的。*/  
  5.   
  6. drv->driver.bus = &platform_bus_type;   
  7.   
  8. //在really_probe函数中,回调了platform_drv_probe函数  
  9. if (drv->probe)   
  10.   
  11. drv->driver.probe = platform_drv_probe;   
  12. if (drv->remove)   
  13. drv->driver.remove = platform_drv_remove;   
  14.   
  15. if (drv->shutdown)   
  16.   
  17. drv->driver.shutdown = platform_drv_shutdown;   
  18.   
  19. if (drv->suspend)   
  20. drv->driver.suspend = platform_drv_suspend;   
  21.   
  22. if (drv->resume)   
  23. drv->driver.resume = platform_drv_resume;   
  24. return driver_register(&drv->driver);   
  25.   
  26. }     
[cpp] view plaincopy
  1. EXPORT_SYMBOL_GPL(platform_driver_register);   
  2. 在platform_driver_register()函数中,对总线附值使用了如下语句,给总线类型和相关操作函数赋值。
  3. drv->driver.bus = &platform_bus_type;    
  4.   
  5.   
  6. struct bus_type platform_bus_type = {  
  7.   
  8.     .name       = "platform",  
  9.   
  10.     .dev_attrs  = platform_dev_attrs,  
  11.   
  12.     .match      = platform_match,  
  13.   
  14.     .uevent     = platform_uevent,  
  15.   
  16.     .suspend    = platform_suspend,  
  17.   
  18.     .suspend_late   = platform_suspend_late,  
  19.   
  20.     .resume_early   = platform_resume_early,  
  21.   
  22.     .resume     = platform_resume,  
  23.   
  24. };  
  25.   
  26. EXPORT_SYMBOL_GPL(platform_bus_type);  
  27.   
  28. 总线bus是联系driver和device的中间枢纽。Device通过所属的bus找到driver.  
  29.   
  30. struct bus_type {  
  31.   
  32.     const char      * name;  
  33.   
  34.   
  35.   
  36.     struct subsystem    subsys;  
  37.   
  38.     struct kset     drivers;  // drivers-〉list链表包含了所有注册到该bus的driver.  
  39.   
  40.     struct kset     devices; // devices ->list链表包含了所有注册到该bus的devices.  
  41.   
  42.     struct klist        klist_devices; //  
  43.   
  44.     struct klist        klist_drivers;  
  45.   
  46.     struct blocking_notifier_head bus_notifier;  
  47.   
  48.     struct bus_attribute    * bus_attrs;  
  49.   
  50.     struct device_attribute * dev_attrs;  
  51.   
  52.     struct driver_attribute * drv_attrs;  
  53.   
  54.     int     (*match)(struct device * dev, struct device_driver * drv);  
  55.   
  56.     int     (*uevent)(struct device *dev, char **envp,  
  57.   
  58.                   int num_envp, char *buffer, int buffer_size);  
  59.   
  60.     int     (*probe)(struct device * dev);  
  61.   
  62.     int     (*remove)(struct device * dev);  
  63.   
  64.     void        (*shutdown)(struct device * dev);  
  65.   
  66.   
  67.   
  68.     int (*suspend)(struct device * dev, pm_message_t state);  
  69.   
  70.     int (*suspend_late)(struct device * dev, pm_message_t state);  
  71.   
  72.     int (*resume_early)(struct device * dev);  
  73.   
  74.     int (*resume)(struct device * dev);  
  75.   
  76. };   

[cpp] view plaincopy
  1. 在此,我们需要关注一下platform_match()和platform_drv_probe()函数。platform_match()   
  2. 函数确定驱动与设备的关联,而platform_drv_probe()函数会在随后介绍的函数中被调用。  
  3. //比较驱动信息中的name与设备信息中的name两者是否一致  
  4. static int platform_match(struct device * dev, struct device_driver * drv)  
  5. {  
  6.   
  7. struct platform_device *pdev = container_of(dev, struct platform_device, dev);  
  8.   
  9. //通过device找到他所属的platform_device  
  10.   
  11. return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) = = 0);  
  12.   
  13. }   
  14.   
  15.   
  16.   
  17.  
  18.   
  19.  
[cpp] view plaincopy
  1. linux kernel源码中有一个神奇的container_of宏,可以根据一个结构体中成员的地址计算出结构体自身的地址。
  2. 完全可以这样理解container_of宏: 
  3. #define container_of(ptr, type, member) (type *)((char *)ptr - offset_of(type,member))  
  4. 对于实现匹配关系的设备和驱动应有如下关系:  
  5.   
  6. platform_device -〉device :该device通过链表连接到BUS上.  
  7.   
  8. platform_driver-〉driver(device_driver类型的结构体):该device通过链表连接到BUS上.  
  9.   
  10. platform_device -〉device->device_driver(指针),该指针指向了platform_driver-〉driver。  
  11.   
  12. 这样platform_device、platform_driver 就通过platform_device -〉device联系在一起了。  
  13.   
  14. 因此我们可以通过platform_device -〉device 找到对应的platform_device和platform_driver。  
  15.   
  16. 这样就很容易理解下面的platform_drv_probe函数了:  
  17.   
  18. static int platform_drv_probe(struct device *_dev)  
  19.   
  20. {  
  21.   
  22.     struct platform_driver *drv = to_platform_driver(_dev->driver);  
  23.   
  24.     struct platform_device *dev = to_platform_device(_dev);  
  25.   
  26.     return drv->probe(dev);//转去执行platform_driver中定义的probe函数。  
  27.   
  28. }。  
  29.   
  30. 对于挂到总线上的设备、驱动是通过:kset、kobject建立各个层次之间的联系。对于kset、kobject。可以参考DDR3设备管理的章节。  
  31.   
  32. 至此对设备(device)、总线(bus)、驱动(device_driver)的关系有了一个大概的了解。  
  33.   
  34. 对于2.6内核中更上面一层的封装:platform_device、platform_driver也有了大概的了解。  
  35.   

  1. 1.3   driver_register()函数  
  2. 在文件drivers/base/driver.c中,实现了driver_register()函数。在此函数中,
  3. 初始化结构体struct device_driver中的klist_device和unloaded字段,通过klist_device字段,  
  4. 可以保存此驱动支持的设备链表,通过“完成”接口机制,完成线程间的同步。
  5. 链表和“完成”接口的详细信息可以参考文献[1]。返回bus_add_driver()函数的运行结果。  
  6. /** *driver_register - register driver with bus * 
  7. @drv:driver to register * 
  8.  
  9. We pass off most of the work to the bus_add_driver() call, * 
  10. since most of the things we have to do deal with the bus structures.  
  11.  
  12. *The one interesting aspect is that we setup drv->unloaded * 
  13. as a completion that gets complete when the driver reference count reaches 0. */   
  14.   
  15. int driver_register(struct device_driver * drv)   
  16. {   
  17.   
  18. //如果总线的方法和设备自己的方法同时存在,将打印告警信息  
  19.   
  20. if ((drv->bus->probe && drv->probe) || (drv->bus->remove && drv->remove) || (drv->bus->shutdown && drv->shutdown))  
  21.   
  22.  {   
  23.   
  24. printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);   
  25.   
  26. }   
  27.   
  28. klist_init(&drv->klist_devices, NULL, NULL); //将driver驱动上的设备链表清空  
  29.   
  30. init_completion(&drv->unloaded);   
  31.   
  32. return bus_add_driver(drv); //将本driver驱动注册登记到drv->bus所在的总线上。  
  33.   
  34. 在内核中以driver成员变量kobject,表示driver.  
  35.   
  36. 所谓的注册即是: driver中的内核成员对象kobject的链表(kobj->entry),  
[cpp] view plaincopy
  1. 加入到bus总线的bus_type ->kset->list 链表中。这样就可以通过总线,找到所有定义在该总线下的kobject(driver).  
  2.   
  3. }   
  4.   
  5. 1.4 bus_add_driver()函数  
  6. 在文件drivers/base/bus.c中实现了bus_add_driver()函数,它通过语句  
[cpp] view plaincopy
  1. klist_add_tail(&drv->knode_bus, &bus->klist_drivers);   
[cpp] view plaincopy
  1. 将驱动信息保存到总线结构中,在设备注册过程中,我们就可以明白此语句的作用了。在此语句之前,调用了driver_attach()函数。  
  2.   
  3. /** *bus_add_driver - Add a driver to the bus. *@drv:driver. * */   
  4.   
  5.   
  6.   
  7. int bus_add_driver(struct device_driver *drv)   
  8. {   
  9.    struct bus_type * bus = get_bus(drv->bus); //获取总线内容,即前面定义的  
  10.   
  11. platform_bus_type  
  12.   
  13. int error = 0;   
  14.   
  15. if (!bus)   
  16. return 0;   
  17.   
  18. pr_debug("bus %s: add driver %s\n", bus->name, drv->name);   
  19.   
  20.  /*kobject_set_name :设置kboj->name[KOBJ_NAME_LEN]数组内容,如果KOBJ_NAME_LEN长度不够,  
[cpp] view plaincopy
  1. 会调用kmalloc申请之后kobj->k_name指针或者指向kboj->name或者指向kmalloc返回地址*/  
  2.   
  3. error = kobject_set_name(&drv->kobj, "%s", drv->name);   
  4.   
  5. //设置kboj->name成员= drv->name  
  6.   
  7. if (error)   
  8.   
  9. goto out_put_bus; //释放总线  
  10.   
  11.   
  12.   
  13. drv->kobj.kset = &bus->drivers;  //设置device_driver结构体的kobj.kset成员变量(是一个kset指针变量)。  
[cpp] view plaincopy
  1. 很重要,它指向总线(bus)的kset成员,bus->drivers成员drivers为kset类型。在platform_driver_register ()  
[cpp] view plaincopy
  1. 函数中有如下总线赋值语句:drv->driver.bus = &platform_bus_type;   
  2.   
  3. platform_bus_type为内核定义的一类总线(bus)。此处的drv->kobj.kset指针指向了总线(bus) 的kset成员。  
[cpp] view plaincopy
  1. 总线的kset成员是kobject的顶层容器,包含了定义在该总线下面所有的kobject。  
  2.   
  3. /******************************************************************/  
  4.   
  5. /* kobject_register()理解:把drv的kobj登记到管理它的bus->kset集合上去。同时再根据层级关系创建相应的目录文件 。  
  6.   
  7. 注册登记该kobj,如果该kobj属于某个kset,那么将自己的entry节点(list_head)挂接到该kset的list链表上,以示自己需要  
[cpp] view plaincopy
  1. 该kset的滋润,同时kobj->parent=&kset->kobj,parent指向kset用来管理自己的kobj  
  2. 如果该kobj的parent已经定义,那么简单的将parent的引用计数加1  
[cpp] view plaincopy
  1. (如果该kobj不属于kset,而属于parent,那么简单的将parent的引用计数加1.)  
  2. 对于kobj属于某个kset的情况,可以实现kset向下查找kobj,也可以实现kobj向上查找kset。  
  3.   
  4. 对于kobj属于某个parent的情况,查找只能是单向的,只能kobj找到parent,parent不能查找该parent挂接的kobj们。  
[cpp] view plaincopy
  1. parent是用来明显建立亲子关系图的标志性变量,当然在kset也能若隐若现的显露出这种关系,但总不如parent正宗和高效。  
[cpp] view plaincopy
  1. 之后调用create_dir()创建该kobj在sysfs中的目录文件  
  2. 最后调用kobject_uevent()将KOBJ_ADD事件通知到用户空间的守护进程*/  
  3.   
  4.   
  5.   
  6. if ((error = kobject_register(&drv->kobj))) //将driver挂到bus总线上。  
  7. goto out_put_bus;   
  8.   
  9. error = driver_attach(drv); //查找所有注册在该bus上的device,当有device的name和driver->name一样时。   view plaincopy
  1. 即找到了该driver对应的设备。在里面调用的really_probe()函数中,实现了设备与驱动的绑定。 
  2. 语句如下:dev->driver = drv;和ret = drv->probe(dev)  
  3.   
  4.   
  5. if (error)   
  6. goto out_unregister;   
  7. klist_add_tail(&drv->knode_bus, &bus->klist_drivers);   
  8. module_add_driver(drv->owner, drv);   
  9.   
  10. /*所以一个驱动需要维持住1个klist链条和一个kobj层次结构--驱动drv->kobj对象,内核一方面使用  
  11. 该kobj在sysfs中建立统一的与该kobj对应的目录对象供用户空间访问,另一方面使用该kobj的引用计数 
  12. 来获悉该kobj设备的繁忙与空闲情况,  
  13. //当本kobj对象的引用计数到达0时,只要其他条件允许,那么说明集成本kobj的结构体对象不再使用,  
  14. 内核得知这个情况很重要,因为这对内核进行进一步的决策提供了详细的证据资料,进而对物理设备进行细致  
  15. 的电源管理成了可能,
  16. //如:当hub1上的所有端口设备都被拔掉之后,hub1就可以安全的进入省电模式了,而这个功能在2.4内核中是找不到的. view plaincopy
  1.  
  2.   
  3. error = driver_add_attrs(bus, drv);   
  4.   
  5. if (error) {   
  6. /* How the hell do we get out of this pickle? Give up */   
  7.   
  8. printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",   
  9. __FUNCTION__, drv->name);   
  10. }   
  11. error = add_bind_files(drv);   
  12. if (error) {   
  13. /* Ditto */   
  14. printk(KERN_ERR "%s: add_bind_files(%s) failed\n",   
  15. __FUNCTION__, drv->name);   
  16. }   
  17. return error;   
  18. out_unregister:   
  19. kobject_unregister(&drv->kobj);   
  20. out_put_bus:   
  21. put_bus(bus);   
  22. return error;   
  23. }   
  24.   
  25.   
  26.   
  27.   
  28. 下面说明设备驱动是如何知道总线对应设备的  
  29.   
  30. 1.5 dd.c文件driver_attach()函数  
  31.   
  32. 在文件drivers/base/dd.c中,实现了设备与驱动交互的核心函数。  
  33.   
  34. 1.5.1 driver_attach()函数  
  35. 函数driver_attach()返回bus_for_each_dev()函数的运行结果。bus_for_each_dev()函数的原型如下:  
  36. int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data,   
  37. int (*fn) (struct device *, void *));  
  38. 该函数迭代了在总线上的每个设备,将相关的device结构传递给fn,同时传递data值。如果start是NULL,  
[cpp] view plaincopy
  1. 将从总线上的第一个设备开始迭代;否则将从start后的第一个设备开始迭代。如果fn返回一个非零值,将停止迭代,  
[cpp] view plaincopy
  1. 而这个值也会从该函数返回(摘自<<Linux设备驱动程序>>第三版)。  
  2. 该函数是如何知道总线上的每个设备的呢?在设备注册过程中,我会详细介绍。  
  3. /* *drivers/base/dd.c - The core device/driver interactions. * * This file contains the (sometimes tricky) code that controls the *interactions between devices and drivers, which primarily includes *driver binding and unbinding. *//** *driver_attach - try to bind driver to devices. *@drv:driver. * *Walk the list of devices that the bus has on it and try to *match the driver with each one.If driver_probe_device() * 
  4. returns 0 and the @dev->driver is set, we've found a *compatible pair. */   
  5.   
  6.   
  7.   
  8. int driver_attach(struct device_driver * drv)   
  9. {   
  10.     return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);   
  11. }   
  12. 1.5.2 __driver_attach()函数  
  13. 函数__driver_attach()在调用driver_probe_device()函数前,需要进行线程间的互斥处理。  
  14. static int __driver_attach(struct device * dev, void * data)   
  15. {   
  16.   
  17. struct device_driver * drv = data;   
  18. /* * 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. */   
  19.   
  20.   
  21.   
  22. if (dev->parent)  
  23.   
  24. down(&dev->parent->sem);   
  25. down(&dev->sem);   
  26.   
  27.   
  28.   
  29. if (!dev->driver)   
  30. driver_probe_device(drv, dev);   
  31.   
  32. up(&dev->sem);   
  33.   
  34. if (dev->parent)   
  35.   
  36. up(&dev->parent->sem);   
  37. return 0;   
  38. }    
  39.   
  40.   
  41.   
  42.   
  43. 1.5.3 driver_probe_device()函数  
  44.   
  45. 在driver_probe_device()函数中,调用了match函数platform_match(),如果它返回0,  
[cpp] view plaincopy
  1. 表示驱动与设备不一致,函数返回;否则,调用really_probe()函数。  
  2. /** * driver_probe_device - attempt to bind device & driver together   
  3. * @drv: driver to bind a device to * @dev: device to try to bind to the driver * * First,   
[cpp] view plaincopy
  1. we call the bus's match function, if one present, which should * compare the device   
[cpp] view plaincopy
  1. IDs the driver supports with the device IDs of the * device. Note we don't do this   
[cpp] view plaincopy
  1. ourselves because we don't know the * format of the ID structures, nor what is to be   
[cpp] view plaincopy
  1. considered a match and * what is not. * * This function returns 1 if a match is found,   
[cpp] view plaincopy
  1. an error if one occurs *(that is not -ENODEV or -ENXIO), and 0 otherwise.   
[cpp] view plaincopy
  1. * * This function must be called with @dev->sem held.  When called for a * USB interface,  
[cpp] view plaincopy
  1.  @dev->parent->sem must be held as well. */   
  2.   
  3.   
  4.   
  5. int driver_probe_device(struct device_driver * drv, struct device * dev)   
  6. {   
  7.   
  8. struct stupid_thread_structure *data;   
  9.     struct task_struct *probe_task;   
  10.     int ret = 0;   
  11.     if (!device_is_registered(dev))   
  12.        return -ENODEV;   
  13.     if (drv->bus->match && !drv->bus->match(dev, drv))   
  14.        goto done;   
  15.     pr_debug("%s: Matched Device %s with Driver %s\n",   
  16.   
  17.         drv->bus->name, dev->bus_id, drv->name);   
  18.     data = kmalloc(sizeof(*data), GFP_KERNEL);   
  19.   
  20.   if (!data)   
  21.        return -ENOMEM;   
  22.     data->drv = drv;   
  23.     data->dev = dev;   
  24.   
  25. if (drv->multithread_probe) {   
  26.        probe_task = kthread_run(really_probe, data,   
  27.                    "probe-%s", dev->bus_id);   
  28.        if (IS_ERR(probe_task))   
  29.            ret = really_probe(data);   
  30.     } else   
  31.        ret = really_probe(data);   
  32. done:   
  33.     return ret;   
  34. }  
  35.   
  36. struct stupid_thread_structure {   
  37.     struct device_driver *drv;   
  38.     struct device *dev;   
  39. };   
  40.   
  41. 1.5.4 really_probe()函数  
  42. 在really_probe()函数中,实现了设备与驱动的绑定。语句如下:dev->driver = drv;和  
  43. ret = drv->probe(dev); probe()函数的实现如下:  
  44. include/linux/platform_device.h  
  45. #define to_platform_device(x) container_of((x), struct platform_device, dev)  
  46. drivers/base/platform.c  
  47. #define to_platform_driver(drv) (container_of((drv), struct platform_driver, driver))  
  48.   
  49.   
  50.   
  51. static int platform_drv_probe(struct device *_dev)   
  52. {   
  53.   
  54. struct platform_driver *drv = to_platform_driver(_dev->driver);   
  55.   
  56.     struct platform_device *dev = to_platform_device(_dev);   
  57.     return drv->probe(dev);   
  58. }   
  59.   
  60. 在此函数中,回调了我们在i2c-at91.c文件中实现的探测函数at91_i2c_probe(),至此,平台驱动的注册过程结束。  
  61. static atomic_t probe_count = ATOMIC_INIT(0);   
  62.   
  63.   
  64.   
  65. static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);   
  66.   
  67.   
  68.   
  69. static int really_probe(void *void_data)   
  70. {   
  71.     struct stupid_thread_structure *data = void_data;   
  72.     struct device_driver *drv = data->drv;   
  73.     struct device *dev = data->dev;   
  74.   
  75. int ret = 0;   
  76.     atomic_inc(&probe_count);   
  77.     pr_debug("%s: Probing driver %s with device %s\n",   
  78.         drv->bus->name, drv->name, dev->bus_id);   
  79.   
  80. WARN_ON(!list_empty(&dev->devres_head));   
  81.   
  82.   
  83.   
  84. dev->driver = drv;   
  85.     if (driver_sysfs_add(dev)) {   
  86.        printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",   
  87.            __FUNCTION__, dev->bus_id);   
  88.        goto probe_failed;   
  89.     }   
  90.   
  91.     if (dev->bus->probe) {   
  92.        ret = dev->bus->probe(dev);   
  93.        if (ret)   
  94.            goto probe_failed;   
  95.     } else if (drv->probe) {   
  96.        ret = drv->probe(dev);   
  97.        if (ret)   
  98.            goto probe_failed;   
  99.     }   
  100.     //设备与驱动绑定后,对系统中已注册的组件进行事件通知。  
  101.     driver_bound(dev);   
  102.     ret = 1;   
  103.     pr_debug("%s: Bound Device %s to Driver %s\n",   
  104.         drv->bus->name, dev->bus_id, drv->name);   
  105.     goto done;   
  106. probe_failed:   
  107.     devres_release_all(dev);   
  108.     driver_sysfs_remove(dev);   
  109.     dev->driver = NULL;   
  110.     if (ret != -ENODEV && ret != -ENXIO) {   
  111.        /* driver matched but the probe failed */   
  112.        printk(KERN_WARNING   
  113.               "%s: probe of %s failed with error %d\n",   
  114.               drv->name, dev->bus_id, ret);   
  115.     }   
  116.    /* * Ignore errors returned by ->probe so that the next driver can try  * its luck. */   
  117.   
  118. ret = 0;   
  119. done:   
  120.     kfree(data);   
  121.     atomic_dec(&probe_count);   
  122.     wake_up(&probe_waitqueue);   
  123.     return ret;   
  124. }