6.等待,只因曾經承諾

6.等待,只因曾經承諾

hub_thread()中還有一個函數沒有講,它就是try_to_freeze(),這是與電源管理相關的函數。對大多數人來說,關於這個函數,瞭解就可以了。

隨着Linux開始支持suspended之後,有人提倡,每一個內核進程都應該在適當的時候,調用try_to_freeze()。什麼意思呢?有這樣一個flag,PF_NOFREEZE,如果你這個進程或者內核線程不想進入suspended狀態,那麼你就可以設置這個flag,正如我們在usb-storage中usb_stor_control_thread()中做的那樣。而對於大多數內核線程來說,目前主流的看法是希望你能在某個地方調用try_to_freeze(),這個函數的作用是檢測一個flag有沒有設置,哪個flag呢,TIF_FREEZE,每個體系結構定義了自己與這有關的flags,比如i386的,include/asm- i386/thread_info.h中:

  1. 126 #define TIF_SYSCALL_TRACE       0       /* syscall trace active */  
  2. 127 #define TIF_NOTIFY_RESUME     1  /* resumption notification requested */  
  3. 128 #define TIF_SIGPENDING          2       /* signal pending */  
  4. 129 #define TIF_NEED_RESCHED        3       /* rescheduling necessary */  
  5. 130 #define TIF_SINGLESTEP  4 /* restore singlestep on return to user mode*/  
  6. 131 #define TIF_IRET                5       /* return with iret */  
  7. 132 #define TIF_SYSCALL_EMU         6       /* syscall emulation active */  
  8. 133 #define TIF_SYSCALL_AUDIT       7       /* syscall auditing active */  
  9. 134 #define TIF_SECCOMP             8       /* secure computing */  
  10. 135 #define TIF_RESTORE_SIGMASK   9 /* restore signal mask in do_signal() */  
  11. 136 #define TIF_MEMDIE              16  
  12. 137 #define TIF_DEBUG               17      /* uses debug registers */  
  13. 138 #define TIF_IO_BITMAP           18      /* uses I/O bitmap */  
  14. 139 #define TIF_FREEZE              19      /* is freezing for suspend */  

一句話,如果你不想支持電源管理,那麼你編譯內核時把CONFIG_PM給關了。不過,有一個問題,USB設備實際上是有節電這個特性的,也就是說USB的各種規範中就有一個suspend和一個resume,也就是掛起和恢復,換而言之,硬件本身有這樣的特性,要是軟件不支持的話寫出來的代碼你敢給客戶用嗎?不過一個利好消息是,除了這裏這個try_to_freeze()比較難一點外,剩下的在USB中出現的電源管理的代碼實際上相對來說不是很難理解,畢竟那些東西和硬件規範是對應的,都有章可循,硬件怎麼規定就怎麼做,所以,不用太擔心。

擺平了外面的這行代碼,於是現在我們安心來看hub_events()了。hub_events()還是來自drivers/usb/core/hub.c,我們一段一段地來看。

  1. 2595 static void hub_events(void)  
  2. 2596 {  
  3. 2597        struct list_head *tmp;  
  4. 2598    struct usb_device *hdev;  
  5. 2599    struct usb_interface *intf;  
  6. 2600    struct usb_hub *hub;  
  7. 2601    struct device *hub_dev;  
  8. 2602    u16 hubstatus;  
  9. 2603    u16 hubchange;  
  10. 2604    u16 portstatus;  
  11. 2605    u16 portchange;  
  12. 2606    int i, ret;  
  13. 2607    int connect_change;  
  14. 2608  
  15. 2609    /*  
  16. 2610     *  We restart the list every time to avoid a deadlock with  
  17. 2611     * deleting hubs downstream from this one. This should be  
  18. 2612     * safe since we delete the hub from the event list.  
  19. 2613     * Not the most efficient, but avoids deadlocks.  
  20. 2614     */  
  21. 2615    while (1) {  
  22. 2616  
  23. 2617        /* Grab the first entry at the beginning of the list */  
  24. 2618        spin_lock_irq(&hub_event_lock);  
  25. 2619        if (list_empty(&hub_event_list)) {  
  26. 2620                spin_unlock_irq(&hub_event_lock);  
  27. 2621            break;  
  28. 2622        }  
  29. 2623  
  30. 2624        tmp = hub_event_list.next;  
  31. 2625        list_del_init(tmp);  
  32. 2626  
  33. 2627        hub = list_entry(tmp, struct usb_hub, event_list);  
  34. 2628        hdev = hub->hdev;  
  35. 2629        intf = to_usb_interface(hub->intfdev);  
  36. 2630        hub_dev = &intf->dev;  
  37. 2631  
  38. 2632        dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",  
  39. 2633                                 hdev->state, hub->descriptor  
  40. 2634                                         ? hub->descriptor->bNbrPorts  
  41. 2635                                         : 0,  
  42. 2636                            /* NOTE: expects max 15 ports... */  
  43. 2637                                 (u16) hub->change_bits[0],  
  44. 2638                                 (u16) hub->event_bits[0]);  
  45. 2639  
  46. 2640            usb_get_intf(intf);  
  47. 2641        spin_unlock_irq(&hub_event_lock);  

2615行,一個while(1)循環;2619行,判斷hub_event_list是否爲空,是不是覺得很有趣?第一次調用這個函數時,hub_event_list就是初值,我們說過初值爲空,所以這裏就是空,即list_empty()返回1,然後break語句跳出while循環。你知道while循環的結尾在哪裏嗎?就是這個hub_events()函數的結尾,也就是說在這裏幾百行的代碼就結束了,我們直接退出這個函數,返回到hub_thread()中,調用wait_event_interruptible()進入睡眠,然後等待有事件發生。

對於Hub來說,當你插入一個設備到Hub口裏,就會觸發一件事件。而第一件事件的發生其實是Hub驅動程序本身的初始化,即我們說過,由於Root Hub的存在,所以hub_probe必然會被調用,確切地說,就是在主機控制器的驅動程序中,一定會調用hub_probe的。如果你問我到底什麼時候會調用,那麼我無可奉告,因爲這是在主機控制器的驅動程序中,不管你的主機控制器是屬於OHCI的、UHCI的,還是EHCI的,最終在它們的初始化代碼中都會調用一個叫做hcd_register_root()的函數,進而轉到usb_register_root_hub(),幾經週轉,最終hub_probe就會被調用。所以你根本不用擔心這個函數什麼時刻會被調用,反正總會有這個時刻。

所以,我們就轉到hub_probe吧,這裏hub_events()只是虛晃一槍,不過你別忘了,等到hub_event_list裏面有東西了之後,我們還會回來的。要知道hub_events()這個函數纔是真正的Hub驅動的核心函數,所有的故事都是在這裏發生的。所以,就像你給了某人一個承諾,承諾你還會回來。有了承諾,等待也被賦予了意義。

最後需要記住的是wait_event_interruptible()的第一個參數是&khubd_wait,關於這個函數我們在usb-storage裏面已經看過多次了,其中khubd_wait定義於drivers/usb/core/hub.c:

  1. 85 /* Wakes up khubd */  
  2. 86 static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); 

這無非就是一個等待隊列頭,所以我們很清楚,將來要喚醒這個睡眠進程的一定是類似這樣的一行代碼:wake_up(&khubd_wait)。沒錯,整個內核代碼中只有一個地方會調用這個代碼,那就是kick_khubd(),不過調用kick_khubd()的地方可不少。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章