linux usb列舉過程分析
阿新 • • 發佈:2019-01-20
當守護程式第一次執行或usb port上狀態發生變化,守護程序被喚醒都會執行hub_events函式,這個函式在usb系統中處理核心位置,usb的列舉過程就是由它完成,usb列舉過程流程圖如圖1所示;
圖1 usb列舉流程圖
由於hub_events函式比較長這裡分幾個部分進行講解:
- static void hub_events(void)
- {
- struct list_head *tmp;
- struct usb_device *hdev;
-
struct usb_interface *intf;
- struct usb_hub *hub;
- struct device *hub_dev;
- u16 hubstatus;
- u16 hubchange;
- u16 portstatus;
- u16 portchange;
- int i, ret;
- int connect_change;
- /*
- * We restart the list every time to avoid a deadlock with
-
* deleting hubs downstream from this one. This should be
- * safe since we delete the hub from the event list.
- * Not the most efficient, but avoids deadlocks.
- */
- while (1) {
- /* Grab the first entry at the beginning of the list */
- spin_lock_irq(&hub_event_lock);
-
if (list_empty(&hub_event_list)) {
- spin_unlock_irq(&hub_event_lock);
- break;
- }
- tmp = hub_event_list.next;
- list_del_init(tmp);
- hub = list_entry(tmp, struct usb_hub, event_list);
- kref_get(&hub->kref);
- spin_unlock_irq(&hub_event_lock);
- hdev = hub->hdev;
- hub_dev = hub->intfdev;
- intf = to_usb_interface(hub_dev);
- dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
- hdev->state, hub->descriptor
- ? hub->descriptor->bNbrPorts
- : 0,
- /* NOTE: expects max 15 ports... */
- (u16) hub->change_bits[0],
- (u16) hub->event_bits[0]);
- /* Lock the device, then check to see if we were
- * disconnected while waiting for the lock to succeed. */
- usb_lock_device(hdev);
- if (unlikely(hub->disconnected))
- goto loop_disconnected;
- /* If the hub has died, clean up after it */
- if (hdev->state == USB_STATE_NOTATTACHED) {
- hub->error = -ENODEV;
- hub_quiesce(hub, HUB_DISCONNECT);
- goto loop;
- }
- /* Autoresume */
- ret = usb_autopm_get_interface(intf);
- if (ret) {
- dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
- goto loop;
- }
- /* If this is an inactive hub, do nothing */
- if (hub->quiescing)
- goto loop_autopm;
- if (hub->error) {
- dev_dbg (hub_dev, "resetting for error %d\n",
- hub->error);
- ret = usb_reset_device(hdev);
- if (ret) {
- dev_dbg (hub_dev,
- "error resetting hub: %d\n", ret);
- goto loop_autopm;
- }
- hub->nerrors = 0;
- hub->error = 0;
- }
hub_events本身也是一個死迴圈,只要條件滿足它便會一直執行。
25-28行,判斷hub_event_list是否為空,如果為空,則跳出迴圈,hub_events執行結束,會進入休眠狀態;
31-35行,從hub_event_list列表中取出某一項,並把它從hub_event_list中刪除,通過list_entry來獲取event_list所對應的hub,並增加hub使用計數;51-85行,做了一些邏輯判斷,判斷hub是否連線,hub上是否有錯誤,如果有錯誤就重啟hub,如果沒有錯誤,接下來就對hub 上的每個port進行掃描,判斷各個port是否發生狀態變化;
- for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
- if (test_bit(i, hub->busy_bits))
- continue;
- connect_change = test_bit(i, hub->change_bits);
- if (!test_and_clear_bit(i, hub->event_bits) &&
- !connect_change)
- continue;
- ret = hub_port_status(hub, i,
- &portstatus, &portchange);
- if (ret < 0)
- continue;
- if (portchange & USB_PORT_STAT_C_CONNECTION) {
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_CONNECTION);
- connect_change = 1;
- }
- if (portchange & USB_PORT_STAT_C_ENABLE) {
- if (!connect_change)
- dev_dbg (hub_dev,
- "port %d enable change, "
- "status %08x\n",
- i, portstatus);
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_ENABLE);
- /*
- * EM interference sometimes causes badly
- * shielded USB devices to be shutdown by
- * the hub, this hack enables them again.
- * Works at least with mouse driver.
- */
- if (!(portstatus & USB_PORT_STAT_ENABLE)
- && !connect_change
- && hdev->children[i-1]) {
- dev_err (hub_dev,
- "port %i "
- "disabled by hub (EMI?), "
- "re-enabling...\n",
- i);
- connect_change = 1;
- }
- }
- if (portchange & USB_PORT_STAT_C_SUSPEND) {
- struct usb_device *udev;
- clear_port_feature(hdev, i,
- USB_PORT_FEAT_C_SUSPEND);
- udev = hdev->children[i-1];
- if (udev) {
- /* TRSMRCY = 10 msec */
- msleep(10);
- usb_lock_device(udev);
- ret = usb_remote_wakeup(hdev->
- children[i-1]);
- usb_unlock_device(udev);
- if (ret < 0)
- connect_change = 1;
- } else {
- ret = -ENODEV;
- hub_port_disable(hub, i, 1);
- }
- dev_dbg (hub_dev,
-
相關推薦
no