Linux那些事儿之我是Hub(21)八大重量级函数闪亮登场(五)

Go go go, fire in the hole!

八大函数已经看了一半,剩下一半,Linux十六岁了,十六岁的季节,一半是诗,一半是梦,一首浸透着生命的诗,一个温馨的少年梦.十六岁的天空,一半绚丽,一半深沉,一种缀满五彩缤纷云霞般的绚丽,一种风也洒脱,雨也豪迈的深沉.

让我们继续下一半,我知道你和我一样,也感觉到了一些疲倦,这个时候,正是体现我们作为社会主义有志青年的关键时刻,让我们以黄健翔为榜样,一边高喊张靓颖万岁,一边像男人__一样去战斗!

2514行至2536,整个这一块代码是专门为了处理Hub,网友洞房不败质疑我,这不废话么,现在讲的就是hub驱动,不是处理hub难道还是为了处理显示器的?,我的意思是说,2514行这个if语句判断的是,接在当前Hub端口的设备不是别的普通设备,恰恰也正是另一个Hub,即所谓的级联.udev->bus_mA刚刚在2493行那里设置的,hub那边能够提供的每个端口的电流.在当年那个hub_configure函数里面我们曾经设置了hub->mA_per_port,如果它小于等于100mA,说明设备供电是存在问题的,电力不足.那么这种情况我们首先要判断接入的这个Hub是不是也得靠总线供电,如果是,那就麻烦了.所以这里再次调用usb_get_status(),虽说我们把这个函数列入八大重量级函数之一,但是实际上我们前面已经讲过这个函数了,所以这里不必进入函数内部,只是需要知道usb_get_status这么一执行,正常的话,这个子hub的状态就被记录在devstat里面了,2525行的意思就是如果这个设备不能自力更生,那么我们就打印一条错误信息,然后goto loop_disable,关闭这个端口,结束这次循环.

2529行至2533行又是指示灯相关的代码,当初我们在讲蝴蝶效应的时候就已经说得很清楚了,此刻schedule_delayed_work(&hub->leds,0)函数这么一执行,就意味着当初注册的led_work()函数将会立刻被调用.这个函数其实挺简单的,代码虽然不短,但是都是幼稚的代码.考虑到usb_get_status()我们不用再贴出来了,所以这里就干脆顺便看一下这个幼稚函数吧.

    208 #define LED_CYCLE_PERIOD        ((2*HZ)/3)

    209

    210 static void led_work (struct work_struct *work)

    211 {

    212         struct usb_hub          *hub =

    213                 container_of(work, struct usb_hub, leds.work);

    214         struct usb_device       *hdev = hub->hdev;

    215         unsigned                i;

    216         unsigned                changed = 0;

    217         int                     cursor = -1;

    218

    219         if (hdev->state != USB_STATE_CONFIGURED || hub->quiescing)

    220                 return;

    221

    222         for (i = 0; i < hub->descriptor->bNbrPorts; i++) {

    223                 unsigned        selector, mode;

    224

    225                 /* 30%-50% duty cycle */

    226

    227                 switch (hub->indicator[i]) {

    228                 /* cycle marker */

    229                 case INDICATOR_CYCLE:

    230                         cursor = i;

    231                         selector = HUB_LED_AUTO;

    232                         mode = INDICATOR_AUTO;

    233                         break;

    234                 /* blinking green = sw attention */

    235                 case INDICATOR_GREEN_BLINK:

    236                         selector = HUB_LED_GREEN;

    237                         mode = INDICATOR_GREEN_BLINK_OFF;

    238                         break;

    239                 case INDICATOR_GREEN_BLINK_OFF:

    240                         selector = HUB_LED_OFF;

    241                         mode = INDICATOR_GREEN_BLINK;

    242                         break;

    243                 /* blinking amber = hw attention */

    244                 case INDICATOR_AMBER_BLINK:

    245                         selector = HUB_LED_AMBER;

    246                         mode = INDICATOR_AMBER_BLINK_OFF;

    247                         break;

248                 case INDICATOR_AMBER_BLINK_OFF:

    249                         selector = HUB_LED_OFF;

    250                         mode = INDICATOR_AMBER_BLINK;

    251                         break;

    252                 /* blink green/amber = reserved */

    253                 case INDICATOR_ALT_BLINK:

    254                         selector = HUB_LED_GREEN;

    255                         mode = INDICATOR_ALT_BLINK_OFF;

    256                         break;

    257                 case INDICATOR_ALT_BLINK_OFF:

    258                         selector = HUB_LED_AMBER;

    259                         mode = INDICATOR_ALT_BLINK;

    260                         break;

    261                 default:

    262                         continue;

    263                 }

    264                 if (selector != HUB_LED_AUTO)

    265                         changed = 1;

    266                 set_port_led(hub, i + 1, selector);

    267                 hub->indicator[i] = mode;

    268         }

    269         if (!changed && blinkenlights) {

    270                 cursor++;

    271                 cursor %= hub->descriptor->bNbrPorts;

    272                 set_port_led(hub, cursor + 1, HUB_LED_GREEN);

    273                 hub->indicator[cursor] = INDICATOR_CYCLE;

    274                 changed++;

    275         }

    276         if (changed)

    277                 schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);

    278 }

注意了,刚才我们进入这个函数之前,我们设置了hub->indicator[port1-1]INDICATOR_AMBER_BLINK,而眼下这个函数从222行开始主循环,有多少个端口就循环多少次,即遍历端口.227行就判断了,对于咱们的这个情形,很显然,selector被设置为HUB_LED_AMBER,这个宏的值为1.mode被设置为INDICATOR_AMBER_BLINK_OFF.

然后266,set_port_led(),

    182 /*

    183  * USB 2.0 spec Section 11.24.2.7.1.10 and table 11-7

    184  * for info about using port indicators

    185  */

    186 static void set_port_led(

    187         struct usb_hub *hub,

    188         int port1,

    189         int selector

    190 )

    191 {

    192         int status = set_port_feature(hub->hdev, (selector << 8) | port1,

    193                         USB_PORT_FEAT_INDICATOR);

    194         if (status < 0)

    195                 dev_dbg (hub->intfdev,

    196                         "port %d indicator %s status %d/n",

    197                         port1,

    198                         ({ char *s; switch (selector) {

    199                         case HUB_LED_AMBER: s = "amber"; break;

    200                         case HUB_LED_GREEN: s = "green"; break;

    201                         case HUB_LED_OFF: s = "off"; break;

    202                         case HUB_LED_AUTO: s = "auto"; break;

    203                         default: s = "??"; break;

    204                         }; s; }),

    205                         status);

206 }

看到调用set_port_feature我们就熟悉了,USB_PORT_FEAT_INDICATOR对应usb spec中的PORT_INDICATOR这个feature,

Linux那些事儿之我是Hub(21)八大重量级函数闪亮登场(五)_第1张图片

咱们传递进来的是Amber,Selector1.于是指示灯会亮Amber,即琥珀色.这里我们看到有两个Mode,一个是Automatic,一个是Manual,Automatic就是灯自动闪,自动变化,Manual基本上就是说我们需要用软件来控制灯的闪烁.我们选择的是后者,所以265行我们就设置changed1.这样,我们将走到276, schedule_delayed_work()再次执行,但这次执行的时候,第二个参数不再是0,而是LED_CYCLE_PERIOD,0.66HZ.而我们现在的hub->indicator[port1-1]和刚才进来的时候相反,INDICATOR_AMBER_BLINK_OFF,于是你会发现下次咱们进来又会变成INDICATOR_AMBER_BLINK,如此反复.这就意味着,这个函数接下来将以这个频率被调用,也就是说指示灯将以0.66HZ的频率亮了又灭灭了又亮,一闪一闪亮晶晶.不过我需要说的是,注意我们刚才是如何进入到这个函数的,是因为我们遇见了电力不足的情况进来的,所以这并不是什么好事,通常琥珀色的灯亮了话,说明硬件方面有问题.就比如我们这里的电的问题.spec规定,琥珀色亮而不闪,表明是错误环境,琥珀色又亮又闪,表明硬件有问题,只有绿色才是工作状态,如果绿色闪烁,那说明软件有问题.

接下来我们把第六个函数也讲掉.check_highspeed(),看了名字基本就知道是干嘛的了.usb spec里面规定,一个设备如果能够进行高速传输,那么它就应该在设备描述符里的bcdUSB这一项写上0200H.所以这里的意思就是说如果一个设备可以进行高速传输,但它现在却处于全速传输的状态,并且,highspeed_hubs,这个变量干嘛的?drivers/usb/core/hub.c中定义的一个静态变量.static unsigned highspeed_hubs,不要说你是第一次见它,当年我们在hub_probe()里面就和它有过一面之缘.让我们把思绪拉回到hub_probe中去,当时我们就判断了如果hdev->speedUSB_SPEED_HIGH,highspeed_hubs++,所以现在这里的意思就很明确了,如果有高速的hub,而你又能进行高速传输,而你偏偏还要进行全速传输,那么你就是生得贱!这种情况下,调用check_highspeed.

   2340 static void

   2341 check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1)

   2342 {

   2343         struct usb_qualifier_descriptor *qual;

   2344         int                             status;

   2345

   2346         qual = kmalloc (sizeof *qual, GFP_KERNEL);

   2347         if (qual == NULL)

   2348                 return;

   2349

   2350         status = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0,

   2351                         qual, sizeof *qual);

   2352         if (status == sizeof *qual) {

   2353                 dev_info(&udev->dev, "not running at top speed; "

   2354                         "connect to a high speed hub/n");

   2355                 /* hub LEDs are probably harder to miss than syslog */

   2356                 if (hub->has_indicators) {

   2357                         hub->indicator[port1-1] = INDICATOR_GREEN_BLINK;

   2358                         schedule_delayed_work (&hub->leds, 0);

   2359                 }

   2360         }

   2361         kfree(qual);

   2362 }

还是那句话,有些事情知道多了才觉得太残酷.我原以为我可以瞒过去,看来是不行了, struct usb_qualifier_descriptor这个结构体牵出了另外一个概念,Device Qualifier descriptor.首先这个结构体定义于include/linux/usb/ch9.h:

    348 /* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */

    349 struct usb_qualifier_descriptor {

    350         __u8  bLength;

    351         __u8  bDescriptorType;

    352

    353         __le16 bcdUSB;

    354         __u8  bDeviceClass;

    355         __u8  bDeviceSubClass;

    356         __u8  bDeviceProtocol;

    357         __u8  bMaxPacketSize0;

    358         __u8  bNumConfigurations;

    359         __u8  bRESERVED;

360 } __attribute__ ((packed));

当我们设计usb 2.0的时候我们务必要考虑与过去的usb 1.1的兼容,高速设备如果接在一个旧的hub上面,总不能说用不了吧?所以,如今的高速设备通常是可以工作于高速也可以不工作于高速,即可以调节,接在高速hub上就按高速工作,如果不然,那么就按全速的方式去工作.关键得看环境,放眼古代,三字经中有昔孟母择邻处子不学断机杼的佳话,展望今天,也许我长在中国是一个优秀的大学生,但是也许生在日本我就是一个纯粹的变态,环境改变人嘛.那么这一点是如何实现的呢?首先,在高速和全速下有不同设备配置信息的高速设备必须具有一个device_qualifier描述符,怎么称呼呢,你可以叫它设备限定符描述符,不过这个叫法过于别扭,所以我们直接用英文,就叫device qualifier描述符.它干嘛用的呢?它描述了一个高速设备在进行速度切换时所需改变的信息.比如,一个设备当前工作于全速状态,那么device qualifier中就保存着信息记录这个设备工作在高速状态的信息,反之如果一个设备当前工作于高速状态,那么device qualifier中就包含着这个设备工作于全速状态的信息.

Ok,这里我们看到,首先定义一个device qualifier描述符的指针qual,然后为其申请内存空间,然后usb_get_descriptor去获得这个描述符,这个函数的返回值就是设备返回了多少个bytes.如果的确是device qualifier描述符的大小,那么说明这个设备的确是可以工作在高速状态的,因为全速设备是没有device qualifier,只有具有高速工作能力的设备才具有device qualifier描述符,而对于全速设备,在收到这么一个请求之后,返回的只是错误码.所以这里的意思就是说如果你这个设备确实是能够工作在高速的,然而你却偏偏工作于全速,并且我们刚才调用check_highspeed()之前也看到了,我们已经判断出设备是工作于全速而系统里有高速的hub,那么至少这说明你这个设备不正常,所以剩下的代码就和刚才那个闪琥珀色的道理一样,只不过这次是闪绿灯,通常闪绿灯的意思是软件问题.

好了,又讲完一个函数.至此我们已经过五关斩六将,八个函数讲完了六个.你也许觉得这些函数很枯燥,觉得读代码很辛苦,不过很不幸,我得告诉你,其实真正最重要的函数是第七个.usb_new_device.这个函数一结束你就可以用lsusb命令看到你的设备了.正如<<这个杀手不太冷>>中的对白,人生本就是苦还是只有童年苦?生命就是如此.

 

你可能感兴趣的:(linux,工作,struct,table,Go,Descriptor)