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()的地方可不少。