你问我这世界,最远的地方在哪里,我将答案抛向蓝天之外落在你心底,你问我这世界,最后的真爱在哪里,我把线索指向大海之外直达我怀里.你问我hub_irq()这个函数,最终是被谁调用,我却只能说我既没有答案也没有线索.当然,你要是问我芙蓉姐姐还能红多久,我倒是可以很爽快的告诉你,你知道永远有多远吗?
我们曾经在hub_configure中讲过中断传输,当时调用了usb_fill_int_urb()函数,并且把hub_irq作为一个参数传递了进去,最终把urb->complete赋值为hub_irq.然后,主机控制器会定期询问hub,每当hub端口上有一个设备插入或者拔除时,它就会向主机控制器打小报告.(怎么哪都有人打小报告啊,当初在Intel不知道哪位哥们缺德,跟老板说我做事情只关注结果不管过程,只要问题解决了就好,根本不管问题是怎么被解决的.害得我在Intel差点没过试用期.)具体来说,从硬件的角度看,就是hub会向host controller返回一些信息,或者说data,这个Data被称作”Hub and Port Status Change Bitmap”,而从软件角度来看,host controller的驱动程序接下来会在处理好这个过程的urb之后,调用该urb的complete函数,对于hub来说,这个函数就是hub_irq().
337 /* completion function, fires on port status changes and various faults */
338 static void hub_irq(struct urb *urb)
339 {
340 struct usb_hub *hub = urb->context;
341 int status;
342 int i;
343 unsigned long bits;
344
345 switch (urb->status) {
346 case -ENOENT: /* synchronous unlink */
347 case -ECONNRESET: /* async unlink */
348 case -ESHUTDOWN: /* hardware going away */
349 return;
350
351 default: /* presumably an error */
352 /* Cause a hub reset after 10 consecutive errors */
353 dev_dbg (hub->intfdev, "transfer --> %d/n", urb->status);
354 if ((++hub->nerrors < 10) || hub->error)
355 goto resubmit;
356 hub->error = urb->status;
357 /* FALL THROUGH */
358
359 /* let khubd handle things */
360 case 0: /* we got data: port status changed */
361 bits = 0;
362 for (i = 0; i < urb->actual_length; ++i)
363 bits |= ((unsigned long) ((*hub->buffer)[i]))
364 << (i*8);
365 hub->event_bits[0] = bits;
366 break;
367 }
368
369 hub->nerrors = 0;
370
371 /* Something happened, let khubd figure it out */
372 kick_khubd(hub);
373
374 resubmit:
375 if (hub->quiescing)
376 return;
377
378 if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
379 && status != -ENODEV && status != -EPERM)
380 dev_err (hub->intfdev, "resubmit --> %d/n", status);
381 }
你问这个参数urb是哪个urb?告诉你,中断传输就是只有一个urb,不是说像bulk传输那样每次开启一次传输都要有申请一个urb,提交urb,对于中断传输,一个urb就可以了,反复利用,所以我们只有一次调用usb_fill_int_urb()函数.这正体现了中断交互的周期性.
340行,当初我们填充urb的时候,urb->context就是赋的hub,所以现在这句话就可以获得我们的那个hub.
345行开始判断urb的状态,前三种都是出错了,直接返回.
351的default和case 0.这段代码是我认为最有技术含量的一段代码.我不知道我是该恨谭浩强该是该恨我自己,我记得我在谭浩强的书上看到的default总是在各个case之后,结果我以为这里default不管case等于0与否都会执行,结果半天没看懂,后来我明白了,其实当urb->status为0的时候,default那一段是不会执行的.
所以这段代码就很好理解了.一开始hub->error为0,hub->nerrors也为0,所以default这一段很明显,goto resubmit,即,我们允许年轻人犯错误,并且可以允许犯十次,错了没有关系,只要不在同一条阴沟里翻船翻十次就可以了,我相信这对大多数人来说都足够了,唯一例外的大概是中国男足了.每次阴沟里翻船,爬起来一看,咦,怎么还是上次那条阴沟啊?resubmit那一段就是重新调用了一次usb_submit_urb()而已.当然,还判断了hub->quisecing.这个变量初始值为1,但是我们前面在hub_activate里把它设置为了0,有一个函数会把它设置为1,这个函数就是hub_quiesce(),而调用后者的只有两个函数,一个是hub_suspend,一个是hub_pre_reset().于是,这里的意思就很明确了,如果hub被挂起了,或者要被reset了,那么就不用重新提交urb了,hub_irq()函数直接返回吧.
再看case 0,urb->status为0,说明这个urb被顺利的处理了,即host controller获得了他想要的数据,即那个”Hub and Port Status Change Bitmap”,因为我们当初调用usb_fill_int_urb的时候,把*hub->buffer传递给了urb->transfer_buffer,所以这个数据现在就在hub->buffer中,我们来看这个bitmap是什么样子,usb spec中给出了这样一幅图:
首先这幅图为我们解答了一个很大的疑惑.当时在hub_events()中您大概还有一些地方不是很清楚.现在我们可以来弄清楚它们了.
我们回过头来看,struct usb_hub中,unsigned long event_bits[1],首先这是一个数组,其次这个数组只有一个元素,而这一个元素恰恰就是对应这里的这个Bitmap,即所谓的位图,每一位都有其作用.一个unsigned long至少4个字节,即32个bits.所以够用了,而我们看到这张图里,bit0和其它的bit是不一样的,bit 0表示Hub有变化,而其它bit则具体表示某一个端口有没有变化,即bit 1表示端口1有变化,bit 2表示端口2有变化,如果一个端口没有变化,对应的那一位就是0.
所以我们可以回到hub_events()函数中来,看看当时我们是如何判断hub->event_bits的,当时我们有这么一小段,
2685 /* deal with port status changes */
2686 for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
2687 if (test_bit(i, hub->busy_bits))
2688 continue;
2689 connect_change = test_bit(i, hub->change_bits);
2690 if (!test_and_clear_bit(i, hub->event_bits) &&
2691 !connect_change && !hub->activating)
2692 continue;
看到了吗?循环指数i从1开始,有多少端口就循环多少次,而对event_bits的测试,即2690判断的是bitmap中bit 1,bit 2,…,bit N,而不需要判断bit 0.
反过来,如果具体每个端口没有变化,而变化的是hub的整体,比如,Local Power有变化,比如Overcurrent有变化,我们则需要判断的是bit 0.即当时我们在hub_events()中看到的下面这段代码.
2776 /* deal with hub status changes */
2777 if (test_and_clear_bit(0, hub->event_bits) == 0)
2778 ; /* do nothing */
2779 else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
2780 dev_err (hub_dev, "get_hub_status failed/n");
2781 else {
2782 if (hubchange & HUB_CHANGE_LOCAL_POWER) {
2783 dev_dbg (hub_dev, "power change/n");
2784 clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
2785 if (hubstatus & HUB_STATUS_LOCAL_POWER)
2786 /* FIXME: Is this always true? */
2787 hub->limited_power = 0;
2788 else
2789 hub->limited_power = 1;
2790 }
2791 if (hubchange & HUB_CHANGE_OVERCURRENT) {
2792 dev_dbg (hub_dev, "overcurrent change/n");
2793 msleep(500); /* Cool down */
2794 clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
2795 hub_power_on(hub);
2796 }
2797 }
而这样我们就很清楚hub_events()的整体思路了,判断每个端口是否有变化,如果有变化就去处理它,没有变化也没有关系,接下来判断是否hub整体上有变化,如果有有变化,那么也去处理它.满足了个人利益,满足了集体利益,这不正体现了我们社会主义制度的优越性么?
关于这个actual_length,其实你不傻的话这个根本就不用我啰嗦了.因为每一个hub的port是不一样的,所以这张bitmap的长度就不一样,比如说你是16个port,那么这个bitmap最多就只要16+1个bit就足够了.而actual_length就是3,即3个bytes.因为3个bytes等于24个bits,足以容纳16+1个bits了.而struct usb_hub中,buffer是这样一个成员,char (*buffer)[8],所以3个bytes就意味着这个buffer的前三个元素里承载着我们的希望.这样我们就不难理解这里这个hub->event_bits是如何变成为这张bitmap的了.
369行,把nerrors清零吧,过去的事情就让它过去吧,让我们忘记过去,展望未来.其实这道理不用我说,小刚那首<<忘记>>就说的很清楚了:有太多往事就别喝下太少酒精,太珍惜生命就别随便掏心,舍不得看破就别张开眼睛,想开心就要舍得伤心;有太多行李就别单独旅行,不能够离开就不要接近,舍不得结束就别开始一段感情,想忘记就要一切归零.谢谢林夕,用这么好的歌词为我们诠释了Linux内核代码.
最关键的当然还是372行,再次调用kick_khubd()函数,于是会再一次触发hub_events().
而hub_irq()函数也就到这里了.这个函数不长,可是很重要,其中最重要的正是最后这句kick_khubd().而这也就是所谓的热插拔的实现路径,要知道我们上次分析kick_khubd的时候是在hub初始化的时候,即那次针对的情况是设备一开始就插在了hub上,而这里再次调用kick_khubd才是真正的在使用过程中,突然间hub有了变化的情况,应该说这个后者才是真正有技术含量的冬冬.