首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

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

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

八大函数已经看了一半,剩下一半,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) {
    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 = 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,


咱们传递进来的是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命令看到你的设备了.正如<<这个杀手不太冷>>中的对白,人生本就是苦还是只有童年苦?生命就是如此.
返回列表