醫生,請把孩子取出來之後,順便給我吸吸脂.
——廣州一婦女在剖腹産手術前對醫生說.
對于控制傳輸,rh_call_control會被調用.我也特别希望能有人給這個函數吸吸脂,我們的上下文是為了擷取裝置描述符,即當初那個usb_get_device_descriptor領着我們來到了這個函數,為了完成這件事情,實際上隻需要很少的代碼,但是rh_call_control這個函數涉及了所有的Root Hub相關的控制傳輸,以至于我們除了把孩子取出來之外,還不得不順便看看其它的代碼.當然了,既然是順便,那麼我們也就不會詳細的去講解每一行.這個函數定義于drivers/usb/core/hcd.c:
343
344 static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
345 {
346 struct usb_ctrlrequest *cmd;
347 u16 typeReq, wValue, wIndex, wLength;
348 u8 *ubuf = urb->transfer_buffer;
349 u8 tbuf [sizeof (struct usb_hub_descriptor)]
350 __attribute__((aligned(4)));
351 const u8 *bufp = tbuf;
352 int len = 0;
353 int patch_wakeup = 0;
354 unsigned long flags;
355 int status = 0;
356 int n;
357
358 cmd = (struct usb_ctrlrequest *) urb->setup_packet;
359 typeReq = (cmd->bRequestType << 8) | cmd->bRequest;
360 wValue = le16_to_cpu (cmd->wValue);
361 wIndex = le16_to_cpu (cmd->wIndex);
362 wLength = le16_to_cpu (cmd->wLength);
363
364 if (wLength > urb->transfer_buffer_length)
365 goto error;
366
367 urb->actual_length = 0;
368 switch (typeReq) {
369
370
371
372
387
388 case DeviceRequest | USB_REQ_GET_STATUS:
389 tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)
390 << USB_DEVICE_REMOTE_WAKEUP)
391 | (1 << USB_DEVICE_SELF_POWERED);
392 tbuf [1] = 0;
393 len = 2;
394 break;
395 case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
396 if (wValue == USB_DEVICE_REMOTE_WAKEUP)
397 device_set_wakeup_enable(&hcd->self.root_hub->dev, 0);
398 else
399 goto error;
400 break;
401 case DeviceOutRequest | USB_REQ_SET_FEATURE:
402 if (device_can_wakeup(&hcd->self.root_hub->dev)
403 && wValue == USB_DEVICE_REMOTE_WAKEUP)
404 device_set_wakeup_enable(&hcd->self.root_hub->dev, 1);
405 else
406 goto error;
407 break;
408 case DeviceRequest | USB_REQ_GET_CONFIGURATION:
409 tbuf [0] = 1;
410 len = 1;
411
412 case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
413 break;
414 case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
415 switch (wValue & 0xff00) {
416 case USB_DT_DEVICE << 8:
417 if (hcd->driver->flags & HCD_USB2)
418 bufp = usb2_rh_dev_descriptor;
419 else if (hcd->driver->flags & HCD_USB11)
420 bufp = usb11_rh_dev_descriptor;
421 else
422 goto error;
423 len = 18;
424 break;
425 case USB_DT_CONFIG << 8:
426 if (hcd->driver->flags & HCD_USB2) {
427 bufp = hs_rh_config_descriptor;
428 len = sizeof hs_rh_config_descriptor;
429 } else {
430 bufp = fs_rh_config_descriptor;
431 len = sizeof fs_rh_config_descriptor;
432 }
433 if (device_can_wakeup(&hcd->self.root_hub->dev))
434 patch_wakeup = 1;
435 break;
436 case USB_DT_STRING << 8:
437 n = rh_string (wValue & 0xff, hcd, ubuf, wLength);
438 if (n < 0)
439 goto error;
440 urb->actual_length = n;
441 break;
442 default:
443 goto error;
444 }
445 break;
446 case DeviceRequest | USB_REQ_GET_INTERFACE:
447 tbuf [0] = 0;
448 len = 1;
449
450 case DeviceOutRequest | USB_REQ_SET_INTERFACE:
451 break;
452 case DeviceOutRequest | USB_REQ_SET_ADDRESS:
453 // wValue == urb->dev->devaddr
454 dev_dbg (hcd->self.controller, "root hub device address %d/n",
455 wValue);
456 break;
457
458
459
460
461
462 case EndpointRequest | USB_REQ_GET_STATUS:
463 // ENDPOINT_HALT flag
464 tbuf [0] = 0;
465 tbuf [1] = 0;
466 len = 2;
467
468 case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
469 case EndpointOutRequest | USB_REQ_SET_FEATURE:
470 dev_dbg (hcd->self.controller, "no endpoint features yet/n");
471 break;
472
473
474
475 default:
476
477 switch (typeReq) {
478 case GetHubStatus:
479 case GetPortStatus:
480 len = 4;
481 break;
482 case GetHubDescriptor:
483 len = sizeof (struct usb_hub_descriptor);
484 break;
485 }
486 status = hcd->driver->hub_control (hcd,
487 typeReq, wValue, wIndex,
488 tbuf, wLength);
489 break;
490 error:
491
492 status = -EPIPE;
493 }
494
495 if (status) {
496 len = 0;
497 if (status != -EPIPE) {
498 dev_dbg (hcd->self.controller,
499 "CTRL: TypeReq=0x%x val=0x%x "
500 "idx=0x%x len=%d ==> %d/n",
501 typeReq, wValue, wIndex,
502 wLength, status);
503 }
504 }
505 if (len) {
506 if (urb->transfer_buffer_length < len)
507 len = urb->transfer_buffer_length;
508 urb->actual_length = len;
509 // always USB_DIR_IN, toward host
510 memcpy (ubuf, bufp, len);
511
512
513 if (patch_wakeup &&
514 len > offsetof (struct usb_config_descriptor,
515 bmAttributes))
516 ((struct usb_config_descriptor *)ubuf)->bmAttributes
517 |= USB_CONFIG_ATT_WAKEUP;
518 }
519
520
521 local_irq_save (flags);
522 spin_lock (&urb->lock);
523 if (urb->status == -EINPROGRESS)
524 urb->status = status;
525 spin_unlock (&urb->lock);
526 usb_hcd_giveback_urb (hcd, urb);
527 local_irq_restore (flags);
528 return 0;
529 }
看到這樣近200行的函數,真是有一種叫天天不靈叫地地不應的感覺.不幸中的萬幸,這個函數的結構還是很清晰的.自上而下的看過來就可以了.
對于控制傳輸,首先要獲得它的setup_packet,來自urb結構體,正如我們當初在usb-storage中看到的那樣.這裡把這個setup_packet賦給cmd指針.然後把其中的各個成員都給取出來,分别放在臨時變量typeReq,wValue,wIndex,wLength中,然後來判斷這個typeReq.
如果是裝置請求并且方向是IN,而且是USB_REQ_GET_STATUS,則,設定len為2.
如果是裝置請求并且方向是OUT,而且是USB_REQ_CLEAR_FEATURE,則如何如何.
如果是裝置請求并且方向是OUT,而且是USB_REQ_SET_FEATURE,則如何如何.
如果是裝置請求并且方向是IN,而且是USB_REQ_GET_CONFIGURATION,則設定len為1.
如果是裝置請求并且方向是OUT,而且是USB_REQ_SET_CONFIGURATION,則啥也不做.
如果是裝置請求并且方向是IN,而且是USB_REQ_GET_DESCRIPTOR,則繼續判斷,wValue到底是什麼來決定究竟是要獲得什麼描述符.如果是USB_DT_DEVICE,則說明要獲得的是裝置描述符,這正是咱們的上下文,而整個這段函數中其它的内容就隻相當于順便看看.(咱們傳遞給usb_get_descriptor的第二個參數就是USB_DT_DEVICE,傳遞給usb_control_msg的第三個參數正是USB_REQ_GET_DESCRIPTOR.)如果是USB_DT_CONFIG,則說明要獲得的是配置描述符,如果是USB_DT_STRING,則說明要獲得的是字元串描述符.實際上,對于Root Hub來說,這些東西都是一樣的,咱們在drivers/usb/core/hcd.c中都預先定義好了,usb2_rh_dev_descriptor是針對usb 2.0的,而usb11_rh_dev_descriptor是針對usb 1.1的,咱們的uhci driver裡面設定了flags的HCD_USB11.
108
109
110
113
114
115
116 #define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff)
117 #define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff)
118
119
120 static const u8 usb2_rh_dev_descriptor [18] = {
121 0x12,
122 0x01,
123 0x00, 0x02,
124
125 0x09,
126 0x00,
127 0x01,
128 0x40,
129
130 0x00, 0x00,
131 0x00, 0x00,
132 KERNEL_VER, KERNEL_REL,
133
134 0x03,
135 0x02,
136 0x01,
137 0x01
138 };
139
140
141
142
143 static const u8 usb11_rh_dev_descriptor [18] = {
144 0x12,
145 0x01,
146 0x10, 0x01,
147
148 0x09,
149 0x00,
150 0x00,
151 0x40,
152
153 0x00, 0x00,
154 0x00, 0x00,
155 KERNEL_VER, KERNEL_REL,
156
157 0x03,
158 0x02,
159 0x01,
160 0x01
161 };
162
163
164
165
166
167
168 static const u8 fs_rh_config_descriptor [] = {
169
170
171 0x09,
172 0x02,
173 0x19, 0x00,
174 0x01,
175 0x01,
176 0x00,
177 0xc0,
182 0x00,
183
184
194
195
196 0x09,
197 0x04,
198 0x00,
199 0x00,
200 0x01,
201 0x09,
202 0x00,
203 0x00,
204 0x00,
205
206
207 0x07,
208 0x05,
209 0x81,
210 0x03,
211 0x02, 0x00,
212 0xff
213 };
214
215 static const u8 hs_rh_config_descriptor [] = {
216
217
218 0x09,
219 0x02,
220 0x19, 0x00,
221 0x01,
222 0x01,
223 0x00,
224 0xc0,
229 0x00,
230
231
241
242
243 0x09,
244 0x04,
245 0x00,
246 0x00,
247 0x01,
248 0x09,
249 0x00,
250 0x00,
251 0x00,
252
253
254 0x07,
255 0x05,
256 0x81,
257 0x03,
258
260 (USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
261 0x0c
262 };
如果是裝置請求且方向為IN,而且是USB_REQ_GET_INTERFACE,則設定len為1
如果是裝置請求且方向為OUT,而且是USB_REQ_SET_INTERFACE,則如何如何.
如果是裝置請求且方向為OUT,而且是USB_REQ_SET_ADDRESS,則如何如何.
如果是端點請求且方向為IN,而且是USB_REQ_GET_STATUS,則如何如何.
如果是端點請求且方向為OUT,而且是USB_REQ_CLEAR_FEATURE或者USB_REQ_SET_FEATURE,則如何如何.
以上這些設定,統統是和usb spec中規定的東西相比對的.
如果是Hub特定的類請求,而且是GetHubStatus或者是GetPortStatus,則設定len為4.
如果是Hub特定的類請求,而且是GetHubDescriptor,則設定len為usb_hub_descriptor結構體的大小.
最後對于Hub特定的類請求需要調用主機控制器驅動程式的hub_control函數,對于uhci_driver來說,這個指針被指派為uhci_hub_control,來自drivers/usb/host/uhci-hub.c:
238
239 static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
240 u16 wIndex, char *buf, u16 wLength)
241 {
242 struct uhci_hcd *uhci = hcd_to_uhci(hcd);
243 int status, lstatus, retval = 0, len = 0;
244 unsigned int port = wIndex - 1;
245 unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port;
246 u16 wPortChange, wPortStatus;
247 unsigned long flags;
248
249 if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead)
250 return -ETIMEDOUT;
251
252 spin_lock_irqsave(&uhci->lock, flags);
253 switch (typeReq) {
254
255 case GetHubStatus:
256 *(__le32 *)buf = cpu_to_le32(0);
257 OK(4);
258 case GetPortStatus:
259 if (port >= uhci->rh_numports)
260 goto err;
261
262 uhci_check_ports(uhci);
263 status = inw(port_addr);
264
265
269 if (to_pci_dev(hcd->self.controller)->vendor ==
270 PCI_VENDOR_ID_VIA)
271 status ^= USBPORTSC_OC;
272
273
274 wPortChange = lstatus = 0;
275 if (status & USBPORTSC_CSC)
276 wPortChange |= USB_PORT_STAT_C_CONNECTION;
277 if (status & USBPORTSC_PEC)
278 wPortChange |= USB_PORT_STAT_C_ENABLE;
279 if ((status & USBPORTSC_OCC) && !ignore_oc)
280 wPortChange |= USB_PORT_STAT_C_OVERCURRENT;
281
282 if (test_bit(port, &uhci->port_c_suspend)) {
283 wPortChange |= USB_PORT_STAT_C_SUSPEND;
284 lstatus |= 1;
285 }
286 if (test_bit(port, &uhci->resuming_ports))
287 lstatus |= 4;
288
289
290 wPortStatus = USB_PORT_STAT_POWER;
291 if (status & USBPORTSC_CCS)
292 wPortStatus |= USB_PORT_STAT_CONNECTION;
293 if (status & USBPORTSC_PE) {
294 wPortStatus |= USB_PORT_STAT_ENABLE;
295 if (status & SUSPEND_BITS)
296 wPortStatus |= USB_PORT_STAT_SUSPEND;
297 }
298 if (status & USBPORTSC_OC)
299 wPortStatus |= USB_PORT_STAT_OVERCURRENT;
300 if (status & USBPORTSC_PR)
301 wPortStatus |= USB_PORT_STAT_RESET;
302 if (status & USBPORTSC_LSDA)
303 wPortStatus |= USB_PORT_STAT_LOW_SPEED;
304
305 if (wPortChange)
306 dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x/n",
307 wIndex, status, lstatus);
308
309 *(__le16 *)buf = cpu_to_le16(wPortStatus);
310 *(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
311 OK(4);
312 case SetHubFeature:
313 case ClearHubFeature:
314 switch (wValue) {
315 case C_HUB_OVER_CURRENT:
316 case C_HUB_LOCAL_POWER:
317 OK(0);
318 default:
319 goto err;
320 }
321 break;
322 case SetPortFeature:
323 if (port >= uhci->rh_numports)
324 goto err;
325
326 switch (wValue) {
327 case USB_PORT_FEAT_SUSPEND:
328 SET_RH_PORTSTAT(USBPORTSC_SUSP);
329 OK(0);
330 case USB_PORT_FEAT_RESET:
331 SET_RH_PORTSTAT(USBPORTSC_PR);
332
333
334 uhci_finish_suspend(uhci, port, port_addr);
335
336
337 uhci->ports_timeout = jiffies + msecs_to_jiffies(50);
338 OK(0);
339 case USB_PORT_FEAT_POWER:
340
341 OK(0);
342 default:
343 goto err;
344 }
345 break;
346 case ClearPortFeature:
347 if (port >= uhci->rh_numports)
348 goto err;
349
350 switch (wValue) {
351 case USB_PORT_FEAT_ENABLE:
352 CLR_RH_PORTSTAT(USBPORTSC_PE);
353
354
355 uhci_finish_suspend(uhci, port, port_addr);
356 OK(0);
357 case USB_PORT_FEAT_C_ENABLE:
358 CLR_RH_PORTSTAT(USBPORTSC_PEC);
359 OK(0);
360 case USB_PORT_FEAT_SUSPEND:
361 if (!(inw(port_addr) & USBPORTSC_SUSP)) {
362
363
364 uhci_finish_suspend(uhci, port, port_addr);
365 } else if (!test_and_set_bit(port,
366 &uhci->resuming_ports)) {
367 SET_RH_PORTSTAT(USBPORTSC_RD);
368
369
373 if (!(inw(port_addr) & USBPORTSC_RD))
374 uhci_finish_suspend(uhci, port,
375 port_addr);
376 else
377
378 uhci->ports_timeout = jiffies +
379 msecs_to_jiffies(20);
380 }
381 OK(0);
382 case USB_PORT_FEAT_C_SUSPEND:
383 clear_bit(port, &uhci->port_c_suspend);
384 OK(0);
385 case USB_PORT_FEAT_POWER:
386
387 goto err;
388 case USB_PORT_FEAT_C_CONNECTION:
389 CLR_RH_PORTSTAT(USBPORTSC_CSC);
390 OK(0);
391 case USB_PORT_FEAT_C_OVER_CURRENT:
392 CLR_RH_PORTSTAT(USBPORTSC_OCC);
393 OK(0);
394 case USB_PORT_FEAT_C_RESET:
395
396 OK(0);
397 default:
398 goto err;
399 }
400 break;
401 case GetHubDescriptor:
402 len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
403 memcpy(buf, root_hub_hub_des, len);
404 if (len > 2)
405 buf[2] = uhci->rh_numports;
406 OK(len);
407 default:
408 err:
409 retval = -EPIPE;
410 }
411 spin_unlock_irqrestore(&uhci->lock, flags);
412
413 return retval;
414 }
服了,徹底服了,變态的函數一個接着一個.莫非這群混蛋寫一個200行的函數就跟我寫一個20行的函數一樣随便?
249行,struct usb_hcd結構體的成員unsigned long flags,咱們當初在usb_add_hcd中調用set_bit函數設定了這麼一個flag,HCD_FLAG_HW_ACCESSIBLE,基本上這個flag在咱們的故事中是被設定了的.另外,struct uhci_hcd結構體有一個成員unsigned int dead,它如果為1就表明控制器挂了.
然後用一個switch來處理hub特定的類請求.OK居然也是一個宏,定義于drivers/usb/host/uhci-hub.c:
78 #define OK(x) len = (x); break
是以如果請求是GetHubStatus,則設定len為4.
如果請求是GetPortStatus,則調用uhci_check_ports.然後讀端口寄存器.USBPORTSC_CSC表示端口連接配接有變化,USBPORTSC_PEC表示Port Enable有變化.USBPORTSC_OCC表示Over Current有變化,struct uhci_hcd的兩個成員,port_c_suspend和resuming_ports都是電源管理相關的.
但無論如何,以上所做的這些都是為了獲得兩個東西,wPortStatus和wPortChange.以此來響應GetPortStatus這個請求.
接下來,SetHubFeature和ClearHubFeature咱們沒啥好說的,不需要做什麼.
但是SetPortFeature就有事情要做了.wValue表明具體是什麼特征.
SET_RH_PORTSTAT這個宏就是專門用于設定Root Hub的端口特征的.
80 #define CLR_RH_PORTSTAT(x) /
81 status = inw(port_addr); /
82 status &= ~(RWC_BITS|WZ_BITS); /
83 status &= ~(x); /
84 status |= RWC_BITS & (x); /
85 outw(status, port_addr)
86
87 #define SET_RH_PORTSTAT(x) /
88 status = inw(port_addr); /
89 status |= (x); /
90 status &= ~(RWC_BITS|WZ_BITS); /
91 outw(status, port_addr)
對于USB_PORT_FEAT_RESET,還需要調用uhci_finish_suspend.
如果是USB_PORT_FEAT_POWER,則什麼也不做,因為UHCI不吃這一套.
如果請求是ClearPortFeature,基本上也是一樣的做法.除了調用的宏變成了CLR_RH_PORTSTAT.
如果請求是GetHubDescriptor,那就滿足它呗.root_hub_hub_des是早就在drivers/usb/host/uhci-hub.c中定義好的:
15 static __u8 root_hub_hub_des[] =
16 {
17 0x09,
18 0x29,
19 0x02,
20 0x0a,
21 0x00,
22 0x01,
23 0x00,
24 0x00,
25 0xff
26 };
回到rh_call_control,switch結束了,下面是判斷status和len.
然後調用usb_hcd_giveback_urb().來自drivers/usb/core/hcd.c:
1373
1385 void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
1386 {
1387 int at_root_hub;
1388
1389 at_root_hub = (urb->dev == hcd->self.root_hub);
1390 urb_unlink (urb);
1391
1392
1394 if (hcd->self.uses_dma && !at_root_hub) {
1395 if (usb_pipecontrol (urb->pipe)
1396 && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
1397 dma_unmap_single (hcd->self.controller, urb->setup_dma,
1398 sizeof (struct usb_ctrlrequest),
1399 DMA_TO_DEVICE);
1400 if (urb->transfer_buffer_length != 0
1401 && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
1402 dma_unmap_single (hcd->self.controller,
1403 urb->transfer_dma,
1404 urb->transfer_buffer_length,
1405 usb_pipein (urb->pipe)
1406 ? DMA_FROM_DEVICE
1407 : DMA_TO_DEVICE);
1408 }
1409
1410 usbmon_urb_complete (&hcd->self, urb);
1411
1412 urb->complete (urb);
1413 atomic_dec (&urb->use_count);
1414 if (unlikely (urb->reject))
1415 wake_up (&usb_kill_urb_queue);
1416 usb_put_urb (urb);
1417 }
1418 EXPORT_SYMBOL (usb_hcd_giveback_urb);
這裡最重要最有意義的一行當然就是1412行,調用urb的complete函數,這正是我們在usb-storage裡期待的那個函數.從此rh_call_control函數也該傳回了,以後裝置驅動又獲得了控制權.事實上令人欣喜的是對于Root Hub,1394行開始的這一段if是不會被執行的,因為at_root_hub顯然是為真.不過就算這段要執行也沒什麼可怕的,無非就是把之前為這個urb建立的dma映射給取消掉.而另一方面,對于Root Hub來說,complete函數基本上是什麼也不做,隻不過是讓咱們再次回到usb_start_wait_urb去,而控制傳輸需要的資料也已經copy到了urb->transfer_buffer中去了. 至此,Root Hub的控制傳輸就算結束了,即我們的usb_get_device_descriptor函數取得了空前絕後的圓滿成功.