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)八大重量级函数闪亮登场(五)

<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,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)