八大函数已经看了一半,剩下一半,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,
<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 415.5pt; HEIGHT: 218.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image001.emz"></imagedata></shape>
咱们传递进来的是Amber,即Selector为1.于是指示灯会亮Amber,即琥珀色.这里我们看到有两个Mode,一个是Automatic,一个是Manual,Automatic就是灯自动闪,自动变化,而Manual基本上就是说我们需要用软件来控制灯的闪烁.我们选择的是后者,所以265行我们就设置changed为1.这样,我们将走到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->speed是USB_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命令看到你的设备了.正如<<这个杀手不太冷>>中的对白,人生本就是苦还是只有童年苦?生命就是如此.