Linux那些事儿之我是Hub(24)所谓的热插拔

你问我这世界,最远的地方在哪里,我将答案抛向蓝天之外落在你心底,你问我这世界,最后的真爱在哪里,我把线索指向大海之外直达我怀里.你问我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之后,调用该urbcomplete函数,对于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的状态,前三种都是出错了,直接返回.

351defaultcase 0.这段代码是我认为最有技术含量的一段代码.我不知道我是该恨谭浩强该是该恨我自己,我记得我在谭浩强的书上看到的default总是在各个case之后,结果我以为这里default不管case等于0与否都会执行,结果半天没看懂,后来我明白了,其实当urb->status0的时候,default那一段是不会执行的.

所以这段代码就很好理解了.一开始hub->error0,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->status0,说明这个urb被顺利的处理了,host controller获得了他想要的数据,即那个”Hub and Port Status Change Bitmap”,因为我们当初调用usb_fill_int_urb的时候,*hub->buffer传递给了urb->transfer_buffer,所以这个数据现在就在hub->buffer,我们来看这个bitmap是什么样子,usb spec中给出了这样一幅图:

Linux那些事儿之我是Hub(24)所谓的热插拔_第1张图片

首先这幅图为我们解答了一个很大的疑惑.当时在hub_events()中您大概还有一些地方不是很清楚.现在我们可以来弄清楚它们了.

我们回过头来看,struct usb_hub,unsigned long event_bits[1],首先这是一个数组,其次这个数组只有一个元素,而这一个元素恰恰就是对应这里的这个Bitmap,即所谓的位图,每一位都有其作用.一个unsigned long至少4个字节,32bits.所以够用了,而我们看到这张图里,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;

看到了吗?循环指数i1开始,有多少端口就循环多少次,而对event_bits的测试,2690判断的是bitmapbit 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,其实你不傻的话这个根本就不用我啰嗦了.因为每一个hubport是不一样的,所以这张bitmap的长度就不一样,比如说你是16port,那么这个bitmap最多就只要16+1bit就足够了.actual_length就是3,3bytes.因为3bytes等于24bits,足以容纳16+1bits.struct usb_hub,buffer是这样一个成员,char (*buffer)[8],所以3bytes就意味着这个buffer的前三个元素里承载着我们的希望.这样我们就不难理解这里这个hub->event_bits是如何变成为这张bitmap的了.

369,nerrors清零吧,过去的事情就让它过去吧,让我们忘记过去,展望未来.其实这道理不用我说,小刚那首<<忘记>>就说的很清楚了:有太多往事就别喝下太少酒精,太珍惜生命就别随便掏心,舍不得看破就别张开眼睛,想开心就要舍得伤心;有太多行李就别单独旅行,不能够离开就不要接近,舍不得结束就别开始一段感情,想忘记就要一切归零.谢谢林夕,用这么好的歌词为我们诠释了Linux内核代码.

最关键的当然还是372,再次调用kick_khubd()函数,于是会再一次触发hub_events().

hub_irq()函数也就到这里了.这个函数不长,可是很重要,其中最重要的正是最后这句kick_khubd().而这也就是所谓的热插拔的实现路径,要知道我们上次分析kick_khubd的时候是在hub初始化的时候,即那次针对的情况是设备一开始就插在了hub,而这里再次调用kick_khubd才是真正的在使用过程中,突然间hub有了变化的情况,应该说这个后者才是真正有技术含量的冬冬.

你可能感兴趣的:(linux,struct,function,测试,buffer,events)