USB hub驱动分析

 hub驱动主要在drivers/usb/core/hub.c文件中,usb设备的初始化、枚举、插拔检测等一般都是由hub发起的。所以要想了解设备的插拔及枚举过程,分析hub是必须的。

[cpp]  view plain copy
  1. static int __init usb_init(void)  
  2. {  
  3.     int retval;  
  4.     if (nousb) {  
  5.         pr_info("%s: USB support disabled\n", usbcore_name);  
  6.         return 0;  
  7.     }  
  8.   
  9.     retval = usb_debugfs_init();  
  10.     if (retval)  
  11.         goto out;  
  12.   
  13.     retval = bus_register(&usb_bus_type);  
  14.     if (retval)  
  15.         goto bus_register_failed;  
  16.     retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);  
  17.     if (retval)  
  18.         goto bus_notifier_failed;  
  19.     retval = usb_major_init();  
  20.     if (retval)  
  21.         goto major_init_failed;  
  22.     retval = usb_register(&usbfs_driver);  
  23.     if (retval)  
  24.         goto driver_register_failed;  
  25.     retval = usb_devio_init();  
  26.     if (retval)  
  27.         goto usb_devio_init_failed;  
  28.     retval = usbfs_init();  
  29.     if (retval)  
  30.         goto fs_init_failed;  
  31.     retval = usb_hub_init();  
  32.     if (retval)  
  33.         goto hub_init_failed;  
  34.     retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);  
  35.     if (!retval)  
  36.         goto out;  
  37.   
  38.     usb_hub_cleanup();  
  39. hub_init_failed:  
  40.     usbfs_cleanup();  
  41. fs_init_failed:  
  42.     usb_devio_cleanup();  
  43. usb_devio_init_failed:  
  44.     usb_deregister(&usbfs_driver);  
  45. driver_register_failed:  
  46.     usb_major_cleanup();  
  47. major_init_failed:  
  48.     bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);  
  49. bus_notifier_failed:  
  50.     bus_unregister(&usb_bus_type);  
  51. bus_register_failed:  
  52.     usb_debugfs_cleanup();  
  53. out:  
  54.     return retval;  
  55. }  

在USB模块的初始化函数中,会调用usb_hub_init函数对hub进行初始化:

[cpp]  view plain copy
  1. int usb_hub_init(void)  
  2. {  
  3.     if (usb_register(&hub_driver) < 0) {  
  4.         printk(KERN_ERR "%s: can't register hub driver\n",  
  5.             usbcore_name);  
  6.         return -1;  
  7.     }  
  8.   
  9.     khubd_task = kthread_run(hub_thread, NULL, "khubd");  
  10.     if (!IS_ERR(khubd_task))  
  11.         return 0;  
  12.   
  13.     /* Fall through if kernel_thread failed */  
  14.     usb_deregister(&hub_driver);  
  15.     printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);  
  16.   
  17.     return -1;  
  18. }  

在该函数中,创建hub_thread线程:

[cpp]  view plain copy
  1. static int hub_thread(void *__unused)  
  2. {  
  3.     /* khubd needs to be freezable to avoid intefering with USB-PERSIST 
  4.      * port handover.  Otherwise it might see that a full-speed device 
  5.      * was gone before the EHCI controller had handed its port over to 
  6.      * the companion full-speed controller. 
  7.      */  
  8.     set_freezable();  
  9.   
  10.     do {  
  11.         hub_events();  
  12.         wait_event_freezable(khubd_wait,  
  13.                 !list_empty(&hub_event_list) ||  
  14.                 kthread_should_stop());  
  15.     } while (!kthread_should_stop() || !list_empty(&hub_event_list));  
  16.   
  17.     pr_debug("%s: khubd exiting\n", usbcore_name);  
  18.     return 0;  
  19. }  

在hub_thread线程中,调用hub_event函数,这个函数非常重要,是hub驱动的核心函数。

[cpp]  view plain copy
  1. static void hub_events(void)  
  2. {  
  3.     struct list_head *tmp;  
  4.     struct usb_device *hdev;  
  5.     struct usb_interface *intf;  
  6.     struct usb_hub *hub;  
  7.     struct device *hub_dev;  
  8.     u16 hubstatus;  
  9.     u16 hubchange;  
  10.     u16 portstatus;  
  11.     u16 portchange;  
  12.     int i, ret;  
  13.     int connect_change;  
  14.   
  15.     /* 
  16.      *  We restart the list every time to avoid a deadlock with 
  17.      * deleting hubs downstream from this one. This should be 
  18.      * safe since we delete the hub from the event list. 
  19.      * Not the most efficient, but avoids deadlocks. 
  20.      */  
  21.     while (1) {  
  22.   
  23.         /* Grab the first entry at the beginning of the list */  
  24.         spin_lock_irq(&hub_event_lock);  
  25.         if (list_empty(&hub_event_list)) {  
  26.             spin_unlock_irq(&hub_event_lock);  
  27.             break;  
  28.         }  
  29.   
  30.         tmp = hub_event_list.next;  
  31.         list_del_init(tmp);  
  32.   
  33.         hub = list_entry(tmp, struct usb_hub, event_list);  
  34.         kref_get(&hub->kref);  
  35.         spin_unlock_irq(&hub_event_lock);  
  36.   
  37.         hdev = hub->hdev;  
  38.         hub_dev = hub->intfdev;  
  39.         intf = to_usb_interface(hub_dev);  
  40.         dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",  
  41.                 hdev->state, hub->descriptor  
  42.                     ? hub->descriptor->bNbrPorts  
  43.                     : 0,  
  44.                 /* NOTE: expects max 15 ports... */  
  45.                 (u16) hub->change_bits[0],  
  46.                 (u16) hub->event_bits[0]);  
  47.   
  48.         /* Lock the device, then check to see if we were 
  49.          * disconnected while waiting for the lock to succeed. */  
  50.         usb_lock_device(hdev);  
  51.         if (unlikely(hub->disconnected))  
  52.             goto loop_disconnected;  
  53.   
  54.         /* If the hub has died, clean up after it */  
  55.         if (hdev->state == USB_STATE_NOTATTACHED) {  
  56.             hub->error = -ENODEV;  
  57.             hub_quiesce(hub, HUB_DISCONNECT);  
  58.             goto loop;  
  59.         }  
  60.   
  61.         /* Autoresume */  
  62.         ret = usb_autopm_get_interface(intf);  
  63.         if (ret) {  
  64.             dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);  
  65.             goto loop;  
  66.         }  
  67.   
  68.         /* If this is an inactive hub, do nothing */  
  69.         if (hub->quiescing)  
  70.             goto loop_autopm;  
  71.   
  72.         if (hub->error) {  
  73.             dev_dbg (hub_dev, "resetting for error %d\n",  
  74.                 hub->error);  
  75.   
  76.             ret = usb_reset_device(hdev);  
  77.             if (ret) {  
  78.                 dev_dbg (hub_dev,  
  79.                     "error resetting hub: %d\n", ret);  
  80.                 goto loop_autopm;  
  81.             }  
  82.   
  83.             hub->nerrors = 0;  
  84.             hub->error = 0;  
  85.         }  
  86.   
  87.         /* deal with port status changes */  
  88.         for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {  
  89.             if (test_bit(i, hub->busy_bits))  
  90.                 continue;  
  91.             connect_change = test_bit(i, hub->change_bits);  
  92.             if (!test_and_clear_bit(i, hub->event_bits) &&  
  93.                     !connect_change)  
  94.                 continue;  
  95.   
  96.             ret = hub_port_status(hub, i,  
  97.                     &portstatus, &portchange);  
  98.             if (ret < 0)  
  99.                 continue;  
  100.   
  101.             if (portchange & USB_PORT_STAT_C_CONNECTION) {  
  102.                 clear_port_feature(hdev, i,  
  103.                     USB_PORT_FEAT_C_CONNECTION);  
  104.                 connect_change = 1;  
  105.             }  
  106.   
  107.             if (portchange & USB_PORT_STAT_C_ENABLE) {  
  108.                 if (!connect_change)  
  109.                     dev_dbg (hub_dev,  
  110.                         "port %d enable change, "  
  111.                         "status %08x\n",  
  112.                         i, portstatus);  
  113.                 clear_port_feature(hdev, i,  
  114.                     USB_PORT_FEAT_C_ENABLE);  
  115.   
  116.                 /* 
  117.                  * EM interference sometimes causes badly 
  118.                  * shielded USB devices to be shutdown by 
  119.                  * the hub, this hack enables them again. 
  120.                  * Works at least with mouse driver.  
  121.                  */  
  122.                 if (!(portstatus & USB_PORT_STAT_ENABLE)  
  123.                     && !connect_change  
  124.                     && hdev->children[i-1]) {  
  125.                     dev_err (hub_dev,  
  126.                         "port %i "  
  127.                         "disabled by hub (EMI?), "  
  128.                         "re-enabling...\n",  
  129.                         i);  
  130.                     connect_change = 1;  
  131.                 }  
  132.             }  
  133.   
  134.             if (portchange & USB_PORT_STAT_C_SUSPEND) {  
  135.                 struct usb_device *udev;  
  136.   
  137.                 clear_port_feature(hdev, i,  
  138.                     USB_PORT_FEAT_C_SUSPEND);  
  139.                 udev = hdev->children[i-1];  
  140.                 if (udev) {  
  141.                     /* TRSMRCY = 10 msec */  
  142.                     msleep(10);  
  143.   
  144.                     usb_lock_device(udev);  
  145.                     ret = usb_remote_wakeup(hdev->  
  146.                             children[i-1]);  
  147.                     usb_unlock_device(udev);  
  148.                     if (ret < 0)  
  149.                         connect_change = 1;  
  150.                 } else {  
  151.                     ret = -ENODEV;  
  152.                     hub_port_disable(hub, i, 1);  
  153.                 }  
  154.                 dev_dbg (hub_dev,  
  155.                     "resume on port %d, status %d\n",  
  156.                     i, ret);  
  157.             }  
  158.               
  159.             if (portchange & USB_PORT_STAT_C_OVERCURRENT) {  
  160.                 u16 status = 0;  
  161.                 u16 unused;  
  162.   
  163.                 dev_dbg(hub_dev, "over-current change on port "  
  164.                     "%d\n", i);  
  165.                 clear_port_feature(hdev, i,  
  166.                     USB_PORT_FEAT_C_OVER_CURRENT);  
  167.                 msleep(100);    /* Cool down */  
  168.                 hub_power_on(hub, true);  
  169.                 hub_port_status(hub, i, &status, &unused);  
  170.                 if (status & USB_PORT_STAT_OVERCURRENT)  
  171.                     dev_err(hub_dev, "over-current "  
  172.                         "condition on port %d\n", i);  
  173.             }  
  174.   
  175.             if (portchange & USB_PORT_STAT_C_RESET) {  
  176.                 dev_dbg (hub_dev,  
  177.                     "reset change on port %d\n",  
  178.                     i);  
  179.                 clear_port_feature(hdev, i,  
  180.                     USB_PORT_FEAT_C_RESET);  
  181.             }  
  182.             if ((portchange & USB_PORT_STAT_C_BH_RESET) &&  
  183.                     hub_is_superspeed(hub->hdev)) {  
  184.                 dev_dbg(hub_dev,  
  185.                     "warm reset change on port %d\n",  
  186.                     i);  
  187.                 clear_port_feature(hdev, i,  
  188.                     USB_PORT_FEAT_C_BH_PORT_RESET);  
  189.             }  
  190.             if (portchange & USB_PORT_STAT_C_LINK_STATE) {  
  191.                 clear_port_feature(hub->hdev, i,  
  192.                         USB_PORT_FEAT_C_PORT_LINK_STATE);  
  193.             }  
  194.             if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {  
  195.                 dev_warn(hub_dev,  
  196.                     "config error on port %d\n",  
  197.                     i);  
  198.                 clear_port_feature(hub->hdev, i,  
  199.                         USB_PORT_FEAT_C_PORT_CONFIG_ERROR);  
  200.             }  
  201.   
  202.             /* Warm reset a USB3 protocol port if it's in 
  203.              * SS.Inactive state. 
  204.              */  
  205.             if (hub_is_superspeed(hub->hdev) &&  
  206.                 (portstatus & USB_PORT_STAT_LINK_STATE)  
  207.                     == USB_SS_PORT_LS_SS_INACTIVE) {  
  208.                 dev_dbg(hub_dev, "warm reset port %d\n", i);  
  209.                 hub_port_warm_reset(hub, i);  
  210.             }  
  211.   
  212.             if (connect_change)  
  213.                 hub_port_connect_change(hub, i,  
  214.                         portstatus, portchange);  
  215.         } /* end for i */  
  216.   
  217.         /* deal with hub status changes */  
  218.         if (test_and_clear_bit(0, hub->event_bits) == 0)  
  219.             ;   /* do nothing */  
  220.         else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)  
  221.             dev_err (hub_dev, "get_hub_status failed\n");  
  222.         else {  
  223.             if (hubchange & HUB_CHANGE_LOCAL_POWER) {  
  224.                 dev_dbg (hub_dev, "power change\n");  
  225.                 clear_hub_feature(hdev, C_HUB_LOCAL_POWER);  
  226.                 if (hubstatus & HUB_STATUS_LOCAL_POWER)  
  227.                     /* FIXME: Is this always true? */  
  228.                     hub->limited_power = 1;  
  229.                 else  
  230.                     hub->limited_power = 0;  
  231.             }  
  232.             if (hubchange & HUB_CHANGE_OVERCURRENT) {  
  233.                 u16 status = 0;  
  234.                 u16 unused;  
  235.   
  236.                 dev_dbg(hub_dev, "over-current change\n");  
  237.                 clear_hub_feature(hdev, C_HUB_OVER_CURRENT);  
  238.                 msleep(500);    /* Cool down */  
  239.                             hub_power_on(hub, true);  
  240.                 hub_hub_status(hub, &status, &unused);  
  241.                 if (status & HUB_STATUS_OVERCURRENT)  
  242.                     dev_err(hub_dev, "over-current "  
  243.                         "condition\n");  
  244.             }  
  245.         }  
  246.   
  247.  loop_autopm:  
  248.         /* Balance the usb_autopm_get_interface() above */  
  249.         usb_autopm_put_interface_no_suspend(intf);  
  250.  loop:  
  251.         /* Balance the usb_autopm_get_interface_no_resume() in 
  252.          * kick_khubd() and allow autosuspend. 
  253.          */  
  254.         usb_autopm_put_interface(intf);  
  255.  loop_disconnected:  
  256.         usb_unlock_device(hdev);  
  257.         kref_put(&hub->kref, hub_release);  
  258.   
  259.         } /* end while (1) */  
  260. }  
       每当有设备连接到USB接口时,USB总线在查询hub状态信息的时候会触发hub的中断服务程序hub_irq,在该函数中利用kick_khubd将hub结构通过event_list添加到khubd的队列hub_event_list,然后唤醒khubd。进入hub_e vents函数,该函数用来处理khubd事件队列,从khubd的hub_event_list中的每个usb_hub数据结构。该函数中首先判断hub是否出错,然后通过一个for循环来检测每个端口的状态信息。利用usb_port_status获取端口信息,如果发生变化就调用hub_port_connect_change函数来配置端口等。

你可能感兴趣的:(USB)