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中:
- 126 #define TIF_SYSCALL_TRACE 0 /* syscall trace active */
- 127 #define TIF_NOTIFY_RESUME 1 /* resumption notification requested */
- 128 #define TIF_SIGPENDING 2 /* signal pending */
- 129 #define TIF_NEED_RESCHED 3 /* rescheduling necessary */
- 130 #define TIF_SINGLESTEP 4 /* restore singlestep on return to user mode*/
- 131 #define TIF_IRET 5 /* return with iret */
- 132 #define TIF_SYSCALL_EMU 6 /* syscall emulation active */
- 133 #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
- 134 #define TIF_SECCOMP 8 /* secure computing */
- 135 #define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */
- 136 #define TIF_MEMDIE 16
- 137 #define TIF_DEBUG 17 /* uses debug registers */
- 138 #define TIF_IO_BITMAP 18 /* uses I/O bitmap */
- 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,我們一段一段地來看。
- 2595 static void hub_events(void)
- 2596 {
- 2597 struct list_head *tmp;
- 2598 struct usb_device *hdev;
- 2599 struct usb_interface *intf;
- 2600 struct usb_hub *hub;
- 2601 struct device *hub_dev;
- 2602 u16 hubstatus;
- 2603 u16 hubchange;
- 2604 u16 portstatus;
- 2605 u16 portchange;
- 2606 int i, ret;
- 2607 int connect_change;
- 2608
- 2609 /*
- 2610 * We restart the list every time to avoid a deadlock with
- 2611 * deleting hubs downstream from this one. This should be
- 2612 * safe since we delete the hub from the event list.
- 2613 * Not the most efficient, but avoids deadlocks.
- 2614 */
- 2615 while (1) {
- 2616
- 2617 /* Grab the first entry at the beginning of the list */
- 2618 spin_lock_irq(&hub_event_lock);
- 2619 if (list_empty(&hub_event_list)) {
- 2620 spin_unlock_irq(&hub_event_lock);
- 2621 break;
- 2622 }
- 2623
- 2624 tmp = hub_event_list.next;
- 2625 list_del_init(tmp);
- 2626
- 2627 hub = list_entry(tmp, struct usb_hub, event_list);
- 2628 hdev = hub->hdev;
- 2629 intf = to_usb_interface(hub->intfdev);
- 2630 hub_dev = &intf->dev;
- 2631
- 2632 dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
- 2633 hdev->state, hub->descriptor
- 2634 ? hub->descriptor->bNbrPorts
- 2635 : 0,
- 2636 /* NOTE: expects max 15 ports... */
- 2637 (u16) hub->change_bits[0],
- 2638 (u16) hub->event_bits[0]);
- 2639
- 2640 usb_get_intf(intf);
- 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:
- 85 /* Wakes up khubd */
- 86 static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
這無非就是一個等待隊列頭,所以我們很清楚,將來要喚醒這個睡眠進程的一定是類似這樣的一行代碼:wake_up(&khubd_wait)。沒錯,整個內核代碼中只有一個地方會調用這個代碼,那就是kick_khubd(),不過調用kick_khubd()的地方可不少。