《Linux总线、设备与驱动》USB设备发现机制

摘要:
ret=bus_add_driver;//添加到总线}3.总线注册kernel/drivers/base/bus.c[cpp]viewplaincopyintbus_register;三、具体分析情况一:当插入USB设备时USBhost会检测到这一事件;然后通过USBcore去匹配驱动。当守护程序第一次运行或usbport上状态发生变化守护进程被唤醒时,会运行hub_events函数、USB的枚举过程就是由它完成。

说明:本分析基于mstar801平台Linux2.6.35.11内核,其他内核版本仅供参考。

一、程序在内核中的位置

1.usb host做为pci总线下的一个设备存在(嵌入式系统中有可能也会直接挂在CPU上);这部分驱动由厂家实现,本分析以mstar为例。

2.USB总线驱动

kernel/drivers/usb/core/driver.c

  1. EXPORT_SYMBOL_GPL(usb_register_driver);
  2. EXPORT_SYMBOL_GPL(usb_deregister);
  3. EXPORT_SYMBOL_GPL(usb_register_device_driver);
  4. EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
  5. structbus_typeusb_bus_type={
  6. .name="usb",
  7. .match=usb_device_match,
  8. .uevent=usb_uevent,
  9. };
kernel/drivers/usb/core/usb.c
  1. staticint__initusb_init(void){
  2. bus_register(&usb_bus_type);
  3. usb_register_device_driver(&usb_generic_driver,THIS_MODULE);
  4. }
3.uvc camera设备驱动

kernel/drivers/media/video/uvc/uvc_driver.c

  1. usb_register(&uvc_driver.driver);

二、所有总线、设备和驱动的注册函数

1.设备注册

kernel/drivers/base/core.c

  1. intdevice_register(structdevice*dev){
  2. device_initialize(dev);
  3. returndevice_add(dev);
  4. }
  5. intdevice_add(structdevice*dev){//所有的设备注册都需要走这里!!!!!!
  6. error=bus_add_device(dev);
  7. kobject_uevent(&dev->kobj,KOBJ_ADD);//上报uevent事件
  8. bus_probe_device(dev);//添加到总线
  9. }
2.驱动注册

kernel/drivers/base/driver.c

  1. intdriver_register(structdevice_driver*drv){//所有的驱动注册都要走这里!!!!!!!
  2. ret=bus_add_driver(drv);//添加到总线
  3. }
3.总线注册

kernel/drivers/base/bus.c

  1. intbus_register(structbus_type*bus);

三、具体分析

情况一:当插入USB设备时USB host会检测到这一事件;然后通过USB core去匹配驱动。

当守护程序第一次运行(特殊USB设备USB hub就是这种情况)或usb port上状态发生变化(其余所有USB设备插入都是这种情况)守护进程被唤醒时,会运行hub_events函数、USB的枚举过程就是由它完成。

1.USB host部分代码

说明:从硬件层面来看,ehci主控器从PCI总线桥接,是PCI驱动程序实例。

kernel/drivers/usb/host/ehci-hcd.c

  1. module_init(ehci_hcd_init);
  2. #definePCI_DRIVERehci_pci_driver//利用pci中断
  3. #definePLATFORM_DRIVERehci_hcd_mstar_driver//利用定时器轮询
  4. staticint__initehci_hcd_init(void){
  5. #ifdefPLATFORM_DRIVER
  6. platform_driver_register(&PLATFORM_DRIVER);
  7. #endif
  8. #ifdefPCI_DRIVER
  9. pci_register_driver(&PCI_DRIVER);
  10. #endif
  11. }
下边分两种情况:

==============================================

定时器轮询:

kernel/drivers/usb/host/ehci-mstar.c

  1. staticstructplatform_driverehci_hcd_mstar_driver={
  2. .probe=ehci_hcd_mstar_drv_probe,
  3. };
  4. staticintehci_hcd_mstar_drv_probe(structplatform_device*pdev){
  5. usb_ehci_mstar_probe(&ehci_mstar_hc_driver,&hcd,pdev);
  6. }
  7. intusb_ehci_mstar_probe(conststructhc_driver*driver,structusb_hcd**hcd_out,structplatform_device*dev){
  8. usb_create_hcd(driver,&dev->dev,"mstar");
  9. }
kernel/drivers/usb/core/hcd.c
  1. structusb_hcd*usb_create_hcd(conststructhc_driver*driver,structdevice*dev,constchar*bus_name){
  2. returnusb_create_shared_hcd(driver,dev,bus_name,NULL);
  3. }
  4. structusb_hcd*usb_create_shared_hcd(conststructhc_driver*driver,structdevice*dev,constchar*bus_name,structusb_hcd*primary_hcd){
  5. init_timer(&hcd->rh_timer);
  6. hcd->rh_timer.function=rh_timer_func;
  7. }
  8. staticvoidrh_timer_func(unsignedlong_hcd){
  9. usb_hcd_poll_rh_status((structusb_hcd*)_hcd);
  10. }
  11. voidusb_hcd_poll_rh_status(structusb_hcd*hcd){
  12. hcd->driver->hub_status_data(hcd,buffer);
  13. usb_hcd_giveback_urb(hcd,urb,0);
  14. }

===================

当有pci中断发生后:

kernel/drivers/usb/host/ehci-pci.c

  1. staticstructpci_driverehci_pci_driver={
  2. .id_table=pci_ids,
  3. }
  4. staticconststructpci_device_idpci_ids[]={{
  5. .driver_data=(unsignedlong)&ehci_pci_hc_driver,
  6. }
  7. }
  8. staticconststructhc_driverehci_pci_hc_driver={
  9. .irq=ehci_irq,//中断
  10. .hub_status_data=ehci_hub_status_data,
  11. .urb_enqueue=ehci_urb_enqueue,
  12. .urb_dequeue=ehci_urb_dequeue,
  13. }
kernel/drivers/usb/host/ehci-hcd.c
  1. staticirqreturn_tehci_irq(structusb_hcd*hcd){
  2. usb_hcd_poll_rh_status(hcd);
  3. }

kernel/drivers/usb/core/hcd.c

  1. voidusb_hcd_poll_rh_status(structusb_hcd*hcd){
  2. hcd->driver->hub_status_data(hcd,buffer);
  3. usb_hcd_giveback_urb(hcd,urb,0);
  4. }

kernel/drivers/usb/host/ehci-hub.c

  1. staticintehci_hub_status_data(structusb_hcd*hcd,char*buf){
  2. }

=====================================================================

从以上分析可以看出;不论是定时器轮询还是pci中断,最终都会执行usb_hcd_giveback_urb函数:

kernel/drivers/usb/core/hcd.c

  1. voidusb_hcd_giveback_urb(structusb_hcd*hcd,structurb*urb,intstatus){
  2. urb->complete(urb);
  3. }

而上处urv->complete函数其实就是如下的hub_irq函数,后边会分析:

kernel/drivers/usb/core/hub.c

  1. staticvoidhub_irq(structurb*urb){
  2. kick_khubd(hub);
  3. }

2.USB core即USB总线部分代码——可以看到hub是第一个USB设备而且与USB总线密切相关

kernel/drivers/usb/core/usb.c

  1. subsys_initcall(usb_init);
  2. structbus_typeusb_bus_type={
  3. .name="usb",
  4. .match=usb_device_match,
  5. .uevent=usb_uevent,
  6. };
  7. staticint__initusb_init(void){
  8. bus_register(&usb_bus_type);
  9. usb_register_device_driver(&usb_generic_driver,THIS_MODULE);//USB设备驱动,在没有roothub时使用
  10. usb_hub_init();
  11. }
kernel/drivers/usb/core/hub.c
  1. staticstructusb_driverhub_driver={
  2. .name="hub",
  3. .probe=hub_probe,
  4. };
  5. intusb_hub_init(void){
  6. usb_register(&hub_driver);//USB设备驱动,第一个USB设备—roothub
  7. kthread_run(hub_thread,NULL,"khubd");
  8. }

=====================================

插句话:下边就是之前我们说的urv->complete被赋为hub_irq函数的过程;

这里说明一下:hub的探测函数的执行是在守护线程第一次运行时的情况;为什么不需要USB总线轮询后或PCI总线中断后就执行?我们会在后边hub创建线程处看到。

  1. staticinthub_probe(structusb_interface*intf,conststructusb_device_id*id){
  2. hub_configure(hub,endpoint);
  3. }
  4. staticinthub_configure(structusb_hub*hub,structusb_endpoint_descriptor*endpoint){
  5. usb_fill_int_urb(hub->urb,hdev,pipe,*hub->buffer,maxp,hub_irq,hub,endpoint->bInterval);
  6. }
kernel/include/linux/usb.h
  1. staticinlinevoidusb_fill_int_urb(structurb*urb,structusb_device*dev,unsignedintpipe,void*transfer_buffer,intbuffer_length,
  2. usb_complete_tcomplete_fn,void*context,intinterval){
  3. urb->complete=complete_fn;
  4. }
=============================================

kernel/drivers/usb/core/hub.c

这里特别强调:hub设备是第一个USB设备,也是必须的USB设备;它不需要通过USB总线定时器轮询或PCI总线中断来触发。从下边代码也可以看出,在执行第一次hub_events之后(hub驱动的probe函数被执行、urv->complete被赋值hub_irq),该线程才会睡眠!

  1. staticinthub_thread(void*__unused){
  2. do{
  3. hub_events();//重要!最核心部分
  4. wait_event_freezable(khubd_wait,!list_empty(&hub_event_list)||kthread_should_stop());
  5. }while(!kthread_should_stop()||!list_empty(&hub_event_list));
  6. }
  7. //内核守护线程khubd,它被kick_khubd唤醒(当prot上状态发生变化时,USBhost会调用usb_hcd_poll_rh_status去查询usbroothubport状态,并调用hub中的interrupturb的回调函数hub_irq,最终去唤醒usb内核守护线程)、通过自身调用wait_event_freezable进入睡眠。
  8. staticvoidhub_events(void){
  9. if(connect_change)hub_port_connect_change(hub,i,portstatus,portchange);
  10. }
  11. staticvoidhub_port_connect_change(structusb_hub*hub,intport1,u16portstatus,u16portchange){
  12. status=hub_port_init(hub,udev,port1,i);
  13. status=usb_new_device(udev);
  14. }
  15. intusb_new_device(structusb_device*udev){
  16. err=device_add(&udev->dev);
  17. (void)usb_create_ep_devs(&udev->dev,&udev->ep0,udev);
  18. /*
  19. kernel/drivers/usb/core/endpoint.c
  20. intusb_create_ep_devs(structdevice*parent,structusb_host_endpoint*endpoint,structusb_device*udev){
  21. device_register(&ep_dev->dev);
  22. }
  23. */
  24. }
kernel/drivers/base/core.c
  1. intdevice_add(structdevice*dev){//所有的设备注册都需要走这里!!!!!!
  2. error=bus_add_device(dev);
  3. kobject_uevent(&dev->kobj,KOBJ_ADD);//上报uevent事件
  4. bus_probe_device(dev);
  5. }
kernel/drivers/base/bus.c
  1. voidbus_probe_device(structdevice*dev){
  2. ret=device_attach(dev);
  3. }
kernel/drivers/base/dd.c
  1. intdevice_attach(structdevice*dev){
  2. ret=bus_for_each_drv(dev->bus,NULL,dev,__device_attach);
  3. }
kernel/drivers/base/bus.c
  1. intbus_for_each_drv(structbus_type*bus,structdevice_driver*start,void*data,int(*fn)(structdevice_driver*,void*)){
  2. while((drv=next_driver(&i))&&!error)
  3. error=fn(drv,data);
  4. }
kernel/drivers/base/dd.c
  1. staticint__device_attach(structdevice_driver*drv,void*data){
  2. if(!driver_match_device(drv,dev))
  3. return0;
  4. /*
  5. kernel/drivers/base/base.h
  6. staticinlineintdriver_match_device(structdevice_driver*drv,structdevice*dev){
  7. returndrv->bus->match?drv->bus->match(dev,drv):1;
  8. }
  9. kernel/drivers/usb/core/driver.c
  10. staticintusb_device_match(structdevice*dev,structdevice_driver*drv){
  11. intf=to_usb_interface(dev);
  12. usb_drv=to_usb_driver(drv);
  13. if(id)return1;
  14. id=usb_match_dynamic_id(intf,usb_drv);
  15. if(id)return1;
  16. return0;
  17. }
  18. */
  19. returndriver_probe_device(drv,dev);
  20. }
  21. intdriver_probe_device(structdevice_driver*drv,structdevice*dev){
  22. ret=really_probe(dev,drv);
  23. }
  24. staticintreally_probe(structdevice*dev,structdevice_driver*drv){
  25. dev->driver=drv;
  26. if(dev->bus->probe){
  27. ret=dev->bus->probe(dev);
  28. if(ret)gotoprobe_failed;
  29. }elseif(drv->probe){
  30. ret=drv->probe(dev);
  31. if(ret)gotoprobe_failed;
  32. }
  33. }
情况二:当加入USB设备驱动时,也会通过USB core调用mattch函数去匹配设备。

kernel/drivers/media/video/uvc/uvc_driver.c

  1. structuvc_driveruvc_driver={
  2. .driver={
  3. .name="uvcvideo",
  4. .probe=uvc_probe,
  5. .disconnect=uvc_disconnect,
  6. .suspend=uvc_suspend,
  7. .resume=uvc_resume,
  8. .reset_resume=uvc_reset_resume,
  9. .id_table=uvc_ids,
  10. .supports_autosuspend=1,
  11. },
  12. };
  13. module_init(uvc_init);
  14. staticint__inituvc_init(void){
  15. result=usb_register(&uvc_driver.driver);
  16. }
kernel/include/linux/usb.h
  1. staticinlineintusb_register(structusb_driver*driver){
  2. returnusb_register_driver(driver,THIS_MODULE,KBUILD_MODNAME);
  3. }
kernel/drivers/usb/core/driver.c
  1. intusb_register_driver(structusb_driver*new_driver,structmodule*owner,constchar*mod_name){
  2. retval=driver_register(&new_driver->drvwrap.driver);
  3. }
kernel/drivers/base/driver.c
  1. intdriver_register(structdevice_driver*drv){//所有的驱动注册都要走这里!!!!!!!
  2. ret=bus_add_driver(drv);
  3. }
kernel/drivers/base/bus.c
  1. intbus_add_driver(structdevice_driver*drv){
  2. error=driver_attach(drv);
  3. kobject_uevent(&priv->kobj,KOBJ_ADD);
  4. }
kernel/drivers/base/dd.c
  1. intdriver_attach(structdevice_driver*drv){
  2. returnbus_for_each_dev(drv->bus,NULL,drv,__driver_attach);
  3. }
kernel/drivers/base/bus.c
  1. intbus_for_each_dev(structbus_type*bus,structdevice*start,void*data,int(*fn)(structdevice*,void*)){
  2. while((dev=next_device(&i))&&!error)error=fn(dev,data);
  3. }
kernel/drivers/base/dd.c
  1. staticint__driver_attach(structdevice*dev,void*data){
  2. if(!driver_match_device(drv,dev))return0;
  3. /*
  4. kernel/drivers/base/base.h
  5. staticinlineintdriver_match_device(structdevice_driver*drv,structdevice*dev){
  6. returndrv->bus->match?drv->bus->match(dev,drv):1;
  7. }
  8. kernel/drivers/usb/core/driver.c
  9. staticintusb_device_match(structdevice*dev,structdevice_driver*drv){
  10. intf=to_usb_interface(dev);
  11. usb_drv=to_usb_driver(drv);
  12. if(id)return1;
  13. id=usb_match_dynamic_id(intf,usb_drv);
  14. if(id)return1;
  15. return0;
  16. }
  17. */
  18. if(!dev->driver)driver_probe_device(drv,dev);
  19. }
  20. intdriver_probe_device(structdevice_driver*drv,structdevice*dev){
  21. ret=really_probe(dev,drv);
  22. }
  23. staticintreally_probe(structdevice*dev,structdevice_driver*drv){
  24. dev->driver=drv;
  25. if(dev->bus->probe){
  26. ret=dev->bus->probe(dev);
  27. if(ret)gotoprobe_failed;
  28. }elseif(drv->probe){
  29. ret=drv->probe(dev);
  30. if(ret)gotoprobe_failed;
  31. }
  32. }

3.总结

经过分析,总结:

(1).当总线上插入设备、总线会调用设备注册函数device_add/device_register;

(2).当insmod设备驱动、module_init函数里边一定有driver_register;

(3).通过上边分析,如上两个函数最终都会调用到总线驱动的match函数、进行匹配;如USB的总线match函数如下:

kernel/drivers/usb/core/driver.c

  1. structbus_typeusb_bus_type={
  2. .name="usb",
  3. .match=usb_device_match,
  4. .uevent=usb_uevent,
  5. .pm=&usb_bus_pm_ops,
  6. };
  7. staticintusb_device_match(structdevice*dev,structdevice_driver*drv)
  8. {
  9. /*devicesandinterfacesarehandledseparately*/
  10. if(is_usb_device(dev)){
  11. /*interfacedriversnevermatchdevices*/
  12. if(!is_usb_device_driver(drv))
  13. return0;
  14. /*TODO:Addrealmatchingcode*/
  15. return1;
  16. }elseif(is_usb_interface(dev)){
  17. structusb_interface*intf;
  18. structusb_driver*usb_drv;
  19. conststructusb_device_id*id;
  20. /*devicedriversnevermatchinterfaces*/
  21. if(is_usb_device_driver(drv))
  22. return0;
  23. intf=to_usb_interface(dev);
  24. usb_drv=to_usb_driver(drv);
  25. id=usb_match_id(intf,usb_drv->id_table);//USB是匹配驱动中的id_table
  26. if(id)
  27. return1;
  28. id=usb_match_dynamic_id(intf,usb_drv);
  29. if(id)
  30. return1;
  31. }
  32. return0;
  33. }

下边也看看UVC Camera驱动的id_table:

kernel/drivers/media/video/uvc/uvc_driver.c

  1. structuvc_driveruvc_driver={
  2. .driver={
  3. .name="uvcvideo",
  4. .probe=uvc_probe,
  5. .disconnect=uvc_disconnect,
  6. .suspend=uvc_suspend,
  7. .resume=uvc_resume,
  8. .reset_resume=uvc_reset_resume,
  9. .id_table=uvc_ids,
  10. .supports_autosuspend=1,
  11. },
  12. };
  13. staticstructusb_device_iduvc_ids[]={
  14. /*MicrosoftLifecamNX-6000*/
  15. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  16. |USB_DEVICE_ID_MATCH_INT_INFO,
  17. .idVendor=0x045e,
  18. .idProduct=0x00f8,
  19. .bInterfaceClass=USB_CLASS_VIDEO,
  20. .bInterfaceSubClass=1,
  21. .bInterfaceProtocol=0,
  22. .driver_info=UVC_QUIRK_PROBE_MINMAX},
  23. /*MicrosoftLifecamVX-7000*/
  24. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  25. |USB_DEVICE_ID_MATCH_INT_INFO,
  26. .idVendor=0x045e,
  27. .idProduct=0x0723,
  28. .bInterfaceClass=USB_CLASS_VIDEO,
  29. .bInterfaceSubClass=1,
  30. .bInterfaceProtocol=0,
  31. .driver_info=UVC_QUIRK_PROBE_MINMAX},
  32. /*LogitechQuickcamFusion*/
  33. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  34. |USB_DEVICE_ID_MATCH_INT_INFO,
  35. .idVendor=0x046d,
  36. .idProduct=0x08c1,
  37. .bInterfaceClass=USB_CLASS_VENDOR_SPEC,
  38. .bInterfaceSubClass=1,
  39. .bInterfaceProtocol=0},
  40. /*LogitechQuickcamOrbitMP*/
  41. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  42. |USB_DEVICE_ID_MATCH_INT_INFO,
  43. .idVendor=0x046d,
  44. .idProduct=0x08c2,
  45. .bInterfaceClass=USB_CLASS_VENDOR_SPEC,
  46. .bInterfaceSubClass=1,
  47. .bInterfaceProtocol=0},
  48. /*LogitechQuickcamProforNotebook*/
  49. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  50. |USB_DEVICE_ID_MATCH_INT_INFO,
  51. .idVendor=0x046d,
  52. .idProduct=0x08c3,
  53. .bInterfaceClass=USB_CLASS_VENDOR_SPEC,
  54. .bInterfaceSubClass=1,
  55. .bInterfaceProtocol=0},
  56. /*LogitechQuickcamPro5000*/
  57. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  58. |USB_DEVICE_ID_MATCH_INT_INFO,
  59. .idVendor=0x046d,
  60. .idProduct=0x08c5,
  61. .bInterfaceClass=USB_CLASS_VENDOR_SPEC,
  62. .bInterfaceSubClass=1,
  63. .bInterfaceProtocol=0},
  64. /*LogitechQuickcamOEMDellNotebook*/
  65. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  66. |USB_DEVICE_ID_MATCH_INT_INFO,
  67. .idVendor=0x046d,
  68. .idProduct=0x08c6,
  69. .bInterfaceClass=USB_CLASS_VENDOR_SPEC,
  70. .bInterfaceSubClass=1,
  71. .bInterfaceProtocol=0},
  72. /*LogitechQuickcamOEMCiscoVTCameraII*/
  73. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  74. |USB_DEVICE_ID_MATCH_INT_INFO,
  75. .idVendor=0x046d,
  76. .idProduct=0x08c7,
  77. .bInterfaceClass=USB_CLASS_VENDOR_SPEC,
  78. .bInterfaceSubClass=1,
  79. .bInterfaceProtocol=0},
  80. /*AppleBuilt-IniSight*/
  81. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  82. |USB_DEVICE_ID_MATCH_INT_INFO,
  83. .idVendor=0x05ac,
  84. .idProduct=0x8501,
  85. .bInterfaceClass=USB_CLASS_VIDEO,
  86. .bInterfaceSubClass=1,
  87. .bInterfaceProtocol=0,
  88. .driver_info=UVC_QUIRK_PROBE_MINMAX
  89. |UVC_QUIRK_BUILTIN_ISIGHT},
  90. /*GenesysLogicUSB2.0PCCamera*/
  91. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  92. |USB_DEVICE_ID_MATCH_INT_INFO,
  93. .idVendor=0x05e3,
  94. .idProduct=0x0505,
  95. .bInterfaceClass=USB_CLASS_VIDEO,
  96. .bInterfaceSubClass=1,
  97. .bInterfaceProtocol=0,
  98. .driver_info=UVC_QUIRK_STREAM_NO_FID},
  99. /*MT6227*/
  100. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  101. |USB_DEVICE_ID_MATCH_INT_INFO,
  102. .idVendor=0x0e8d,
  103. .idProduct=0x0004,
  104. .bInterfaceClass=USB_CLASS_VIDEO,
  105. .bInterfaceSubClass=1,
  106. .bInterfaceProtocol=0,
  107. .driver_info=UVC_QUIRK_PROBE_MINMAX},
  108. /*Syntek(HPSpartan)*/
  109. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  110. |USB_DEVICE_ID_MATCH_INT_INFO,
  111. .idVendor=0x174f,
  112. .idProduct=0x5212,
  113. .bInterfaceClass=USB_CLASS_VIDEO,
  114. .bInterfaceSubClass=1,
  115. .bInterfaceProtocol=0,
  116. .driver_info=UVC_QUIRK_STREAM_NO_FID},
  117. /*Syntek(SamsungQ310)*/
  118. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  119. |USB_DEVICE_ID_MATCH_INT_INFO,
  120. .idVendor=0x174f,
  121. .idProduct=0x5931,
  122. .bInterfaceClass=USB_CLASS_VIDEO,
  123. .bInterfaceSubClass=1,
  124. .bInterfaceProtocol=0,
  125. .driver_info=UVC_QUIRK_STREAM_NO_FID},
  126. /*AsusF9SG*/
  127. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  128. |USB_DEVICE_ID_MATCH_INT_INFO,
  129. .idVendor=0x174f,
  130. .idProduct=0x8a31,
  131. .bInterfaceClass=USB_CLASS_VIDEO,
  132. .bInterfaceSubClass=1,
  133. .bInterfaceProtocol=0,
  134. .driver_info=UVC_QUIRK_STREAM_NO_FID},
  135. /*Syntek(AsusU3S)*/
  136. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  137. |USB_DEVICE_ID_MATCH_INT_INFO,
  138. .idVendor=0x174f,
  139. .idProduct=0x8a33,
  140. .bInterfaceClass=USB_CLASS_VIDEO,
  141. .bInterfaceSubClass=1,
  142. .bInterfaceProtocol=0,
  143. .driver_info=UVC_QUIRK_STREAM_NO_FID},
  144. /*LenovoThinkpadSL500*/
  145. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  146. |USB_DEVICE_ID_MATCH_INT_INFO,
  147. .idVendor=0x17ef,
  148. .idProduct=0x480b,
  149. .bInterfaceClass=USB_CLASS_VIDEO,
  150. .bInterfaceSubClass=1,
  151. .bInterfaceProtocol=0,
  152. .driver_info=UVC_QUIRK_STREAM_NO_FID},
  153. /*EcammPicoiMage*/
  154. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  155. |USB_DEVICE_ID_MATCH_INT_INFO,
  156. .idVendor=0x18cd,
  157. .idProduct=0xcafe,
  158. .bInterfaceClass=USB_CLASS_VIDEO,
  159. .bInterfaceSubClass=1,
  160. .bInterfaceProtocol=0,
  161. .driver_info=UVC_QUIRK_PROBE_EXTRAFIELDS},
  162. /*BodelinProScopeHR*/
  163. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  164. |USB_DEVICE_ID_MATCH_DEV_HI
  165. |USB_DEVICE_ID_MATCH_INT_INFO,
  166. .idVendor=0x19ab,
  167. .idProduct=0x1000,
  168. .bcdDevice_hi=0x0126,
  169. .bInterfaceClass=USB_CLASS_VIDEO,
  170. .bInterfaceSubClass=1,
  171. .bInterfaceProtocol=0,
  172. .driver_info=UVC_QUIRK_STATUS_INTERVAL},
  173. /*SiGmaMicroUSBWebCamera*/
  174. {.match_flags=USB_DEVICE_ID_MATCH_DEVICE
  175. |USB_DEVICE_ID_MATCH_INT_INFO,
  176. .idVendor=0x1c4f,
  177. .idProduct=0x3000,
  178. .bInterfaceClass=USB_CLASS_VIDEO,
  179. .bInterfaceSubClass=1,
  180. .bInterfaceProtocol=0,
  181. .driver_info=UVC_QUIRK_PROBE_MINMAX
  182. |UVC_QUIRK_IGNORE_SELECTOR_UNIT
  183. |UVC_QUIRK_PRUNE_CONTROLS},
  184. /*GenericUSBVideoClass*/
  185. {USB_INTERFACE_INFO(USB_CLASS_VIDEO,1,0)},
  186. {}
  187. };
(4).如果匹配成功,会执行设备驱动的probe函数。我们关心的设备节点的创建也是在设备驱动的探测函数中被创建(因为这时的设备注册会附带主次设备号,内核通过netlink上报uevent事件后、用户空间的udevd服务会执行mknod创建设备节点)详见Linux驱动中uevent、netlink及kobject初探——kobject部分Linux驱动中uevent、netlink及kobject初探——ueventd部分

三、usb相关结构说明

1.设备描述符

  1. structusb_device_descriptor{
  2. __u8bLength;--描述符长度
  3. __u8bDescriptorType;--描述符类型:设备描述符0x01
  4. __le16bcdUSB;--usb规范版本号
  5. __u8bDeviceClass;--类代码
  6. __u8bDeviceSubClass;--子类代码
  7. __u8bDeviceProtocol;--协议代码
  8. __u8bMaxPacketSize0;--端点0支持最大数
  9. __le16idVendor;--供应商ID
  10. __le16idProduct;--产品ID
  11. __le16bcdDevice;--设备版本号
  12. __u8iManufacturer;--供应商字符串描述符的索引值
  13. __u8iProduct;--产品字符串描述符的索引值
  14. __u8iSerialNumber;--设备序列号
  15. __u8bNumConfigurations;--所支持的配置数
  16. }__attribute__((packed));--结构体字符类型对齐

2.配置描述符

  1. structusb_config_descriptor{
  2. __u8bLength;--描述符长度
  3. __u8bDescriptorType;--描述符类型
  4. __le16wTotalLength;--配置信息的总长度
  5. __u8bNumInterfaces;--所支持的接口数
  6. __u8bConfigurationValue;--配置值
  7. __u8iConfiguration;--字符串描述符的索引值
  8. __u8bmAttributes;--配置特征
  9. __u8bMaxPower;--所需最大的总线电流
  10. }__attribute__((packed));
3.接口描述符
  1. structusb_interface_descriptor{
  2. __u8bLength;
  3. __u8bDescriptorType;
  4. __u8bInterfaceNumber;--接口编号
  5. __u8bAlternateSetting;--备用接口标号
  6. __u8bNumEndpoints;--接口数目
  7. __u8bInterfaceClass;--接口类型
  8. __u8bInterfaceSubClass;--接口子类型
  9. __u8bInterfaceProtocol;--接口所用协议
  10. __u8iInterface;--接口索引字符串数值
  11. }__attribute__((packed));
4.端点描述符
  1. structusb_endpoint_descriptor{
  2. __u8bLength;
  3. __u8bDescriptorType;
  4. __u8bEndpointAddress;--端点号包括传输方向
  5. __u8bmAttributes;--端点属性
  6. __le16wMaxPacketSize;--最大数据包长度
  7. __u8bInterval;--访问间隔
  8. __u8bRefresh;
  9. __u8bSynchAddress;
  10. }__attribute__((packed));

usb总线驱动中对于设备和设备驱动的匹配函数,其实就是上述1和3的匹配过程

见:kernel/drivers/usb/core/driver.c中usb_device_match函数,这部分可以进一步分析;在此、我不再分析。

大致会匹配设备所属类(Input设备?Camera设备?Audio设备?或显示设备等)和VID、PID。

五、urb数据传输分析

未完待续

免责声明:文章转载自《《Linux总线、设备与驱动》USB设备发现机制》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇SC命令---安装、开启、配置、关闭 cmd命令行和bat批处理操作windows服务数字证书格式详细说明【转】下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

LVM 扩容、删除

当发现lv空间不够用时,就需要我们进行对lv的扩容。扩容分两种情况:(lv的有点,支持在线扩容) 1.VG有充足的空间,直接对lv进行扩容; 2.VG空间不足时,我们需要对VG进行扩容,然后再对lv进行扩容 1)首先我们做VG空间充足的情况。直接对lv进行扩容 查看我们的L挂在情况 [root@bogon ~]# df -Th 文件系统...

Linux程序编译链接动态库版本号的问题

不同版本号的动态库可能会不兼容,假设程序在编译时指定动态库是某个低版本号。执行是用的一个高版本号,可能会导致无法执行。 Linux上对动态库的命名採用libxxx.so.a.b.c的格式。当中a代表大版本号号。b代表小版本号号,c代表更小的版本号号。我们以Linux自带的cp程序为例,通过ldd查看其依赖的动态库 $ ldd /bin/cp...

linux系统统计某一字符出现的次数

1、创建测试数据 [root@linuxprobe test]# cat a.txt e i j s e f s d g e d d 2、利用grep命令统计字符e出现的次数 [root@linuxprobe test]# grep -o "e" a.txt | wc -l 3 3、同时统计e和d出现的次数 [root@linuxprobe test]#...

Linux端口转发的几种常用方法

0x00 背景   端口转发是一个常用的功能,不管是在服务器运维还是在***领域,都需要用到。在近期遇到一个问题就是一个服务的端口不能进行配置,但是由于出口硬件防火墙的原因,为了不修改硬件防火墙的策略,所以只能在本地做端口转发。因此尝试和寻找了以下的几种方法。 0x01 iptables和firewall iptables   iptables是我第一个...

maven 常见错误解决方法

1. 最重要的一点,使用国内镜像,比如 oschina.net 的镜像; 搜 jar 推荐:http://maven.outofmemory.cn/,速度极快。 2. 错误:Could not resolve artifact org.apache.maven.archetypes:maven-archetype-quickstart:pom:RELEAS...

dd命令详解

【基本参数详解】 dd 复制    if 源 if=/dev/zero不产生IO of 目的 of=/dev/null不产生IO bs 块大小    count 读写块的数量    iflag/oflag dd做读写测试时,要加两个参数 iflag=nocache 和 oflag=direct 参数。 没有的话dd有...