Import Usb/UsbBusDxe and Usb/UsbMassStorageDxe into MdeModulePkg.
[people/mcb30/edk2.git] / edk2 / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassCbi.c
1 /** @file\r
2 \r
3 Copyright (c) 2007, Intel Corporation\r
4 All rights reserved. This program and the accompanying materials\r
5 are licensed and made available under the terms and conditions of the BSD License\r
6 which accompanies this distribution.  The full text of the license may be found at\r
7 http://opensource.org/licenses/bsd-license.php\r
8 \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11 \r
12 Module Name:\r
13 \r
14   UsbMassCbi.c\r
15 \r
16 Abstract:\r
17 \r
18   Implementation of the USB mass storage Control/Bulk/Interrupt transpor.\r
19   Notice: it is being obseleted by the standard body in favor of the BOT\r
20   (Bulk-Only Transport).\r
21 \r
22 Revision History\r
23 \r
24 \r
25 **/\r
26 \r
27 #include "UsbMass.h"\r
28 #include "UsbMassCbi.h"\r
29 \r
30 UINTN mUsbCbiInfo  = DEBUG_INFO;\r
31 UINTN mUsbCbiError = DEBUG_ERROR;\r
32 \r
33 STATIC\r
34 EFI_STATUS\r
35 UsbCbiResetDevice (\r
36   IN  VOID                    *Context,\r
37   IN  BOOLEAN                  ExtendedVerification\r
38   );\r
39 \r
40 \r
41 /**\r
42   Initialize the USB mass storage class CBI transport protocol.\r
43   If Context isn't NULL, it will save its context in it.\r
44 \r
45   @param  UsbIo                 The USB IO to use\r
46   @param  Controller            The device controller\r
47   @param  Context               The variable to save context in\r
48 \r
49   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory\r
50   @retval EFI_UNSUPPORTED       The device isn't supported\r
51   @retval EFI_SUCCESS           The CBI protocol is initialized.\r
52 \r
53 **/\r
54 STATIC\r
55 EFI_STATUS\r
56 UsbCbiInit (\r
57   IN  EFI_USB_IO_PROTOCOL   *UsbIo,\r
58   IN  EFI_HANDLE            Controller,\r
59   OUT VOID                  **Context       OPTIONAL\r
60   )\r
61 {\r
62   USB_CBI_PROTOCOL              *UsbCbi;\r
63   EFI_USB_INTERFACE_DESCRIPTOR  *Interface;\r
64   EFI_USB_ENDPOINT_DESCRIPTOR   EndPoint;\r
65   EFI_STATUS                    Status;\r
66   UINT8                         Index;\r
67 \r
68   //\r
69   // Allocate the CBI context\r
70   //\r
71   UsbCbi = AllocateZeroPool (\r
72              sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)\r
73              );\r
74 \r
75   if (UsbCbi == NULL) {\r
76     return EFI_OUT_OF_RESOURCES;\r
77   }\r
78 \r
79   UsbCbi->UsbIo = UsbIo;\r
80 \r
81   //\r
82   // Get the interface descriptor and validate that it is a USB mass\r
83   // storage class CBI interface.\r
84   //\r
85   Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbCbi->Interface);\r
86   if (EFI_ERROR (Status)) {\r
87     goto ON_ERROR;\r
88   }\r
89 \r
90   Interface = &UsbCbi->Interface;\r
91   if ((Interface->InterfaceProtocol != USB_MASS_STORE_CBI0)\r
92       && (Interface->InterfaceProtocol != USB_MASS_STORE_CBI1)) {\r
93     Status = EFI_UNSUPPORTED;\r
94     goto ON_ERROR;\r
95   }\r
96 \r
97   //\r
98   // Locate and save the bulk-in, bulk-out, and interrupt endpoint\r
99   //\r
100   for (Index = 0; Index < Interface->NumEndpoints; Index++) {\r
101     Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);\r
102     if (EFI_ERROR (Status)) {\r
103       continue;\r
104     }\r
105 \r
106     if (USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {\r
107       //\r
108       // Use the first Bulk-In and Bulk-Out endpoints\r
109       //\r
110       if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&\r
111          (UsbCbi->BulkInEndpoint == NULL)) {\r
112 \r
113         UsbCbi->BulkInEndpoint  = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1);\r
114         *UsbCbi->BulkInEndpoint = EndPoint;\r
115       }\r
116 \r
117       if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&\r
118          (UsbCbi->BulkOutEndpoint == NULL)) {\r
119 \r
120         UsbCbi->BulkOutEndpoint   = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 1;\r
121         *UsbCbi->BulkOutEndpoint  = EndPoint;\r
122       }\r
123 \r
124     } else if (USB_IS_INTERRUPT_ENDPOINT (EndPoint.Attributes)) {\r
125       //\r
126       // Use the first interrupt endpoint if it is CBI0\r
127       //\r
128       if ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0) &&\r
129           (UsbCbi->InterruptEndpoint == NULL)) {\r
130 \r
131         UsbCbi->InterruptEndpoint   = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 2;\r
132         *UsbCbi->InterruptEndpoint  = EndPoint;\r
133       }\r
134     }\r
135   }\r
136 \r
137   if ((UsbCbi->BulkInEndpoint == NULL)\r
138       || (UsbCbi->BulkOutEndpoint == NULL)\r
139       || ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0)\r
140           && (UsbCbi->InterruptEndpoint == NULL))) {\r
141     Status = EFI_UNSUPPORTED;\r
142     goto ON_ERROR;\r
143   }\r
144 \r
145   if (Context != NULL) {\r
146     *Context = UsbCbi;\r
147   } else {\r
148     gBS->FreePool (UsbCbi);\r
149   }\r
150   return EFI_SUCCESS;\r
151 \r
152 ON_ERROR:\r
153   gBS->FreePool (UsbCbi);\r
154   return Status;\r
155 }\r
156 \r
157 \r
158 \r
159 /**\r
160   Send the command to the device using class specific control transfer.\r
161 \r
162   @param  UsbCbi                The USB CBI protocol\r
163   @param  Cmd                   The high level command to transfer to device\r
164   @param  CmdLen                The length of the command\r
165   @param  Timeout               The time to wait the command to finish\r
166 \r
167   @retval EFI_SUCCESS           The command is transferred to device\r
168   @retval Others                The command failed to transfer to device\r
169 \r
170 **/\r
171 STATIC\r
172 EFI_STATUS\r
173 UsbCbiSendCommand (\r
174   IN USB_CBI_PROTOCOL       *UsbCbi,\r
175   IN UINT8                  *Cmd,\r
176   IN UINT8                  CmdLen,\r
177   IN UINT32                 Timeout\r
178   )\r
179 {\r
180   EFI_USB_DEVICE_REQUEST  Request;\r
181   EFI_STATUS              Status;\r
182   UINT32                  TransStatus;\r
183   UINTN                   DataLen;\r
184   INTN                    Retry;\r
185 \r
186   //\r
187   // Fill in the device request, CBI use the "Accept Device-Specific\r
188   // Cmd" (ADSC) class specific request to send commands\r
189   //\r
190   Request.RequestType = 0x21;\r
191   Request.Request     = 0;\r
192   Request.Value       = 0;\r
193   Request.Index       = UsbCbi->Interface.InterfaceNumber;\r
194   Request.Length      = CmdLen;\r
195 \r
196   Status              = EFI_SUCCESS;\r
197   Timeout             = Timeout / USB_MASS_STALL_1_MS;\r
198 \r
199   for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {\r
200     //\r
201     // Use the UsbIo to send the command to the device\r
202     //\r
203     TransStatus = 0;\r
204     DataLen     = CmdLen;\r
205 \r
206     Status = UsbCbi->UsbIo->UsbControlTransfer (\r
207                               UsbCbi->UsbIo,\r
208                               &Request,\r
209                               EfiUsbDataOut,\r
210                               Timeout,\r
211                               Cmd,\r
212                               DataLen,\r
213                               &TransStatus\r
214                               );\r
215     //\r
216     // The device can fail the command by STALL the control endpoint.\r
217     // It can delay the command by NAK the data or status stage, this\r
218     // is a "class-specific exemption to the USB specification". Retry\r
219     // if the command is NAKed.\r
220     //\r
221     if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {\r
222       continue;\r
223     }\r
224 \r
225     break;\r
226   }\r
227 \r
228   return Status;\r
229 }\r
230 \r
231 \r
232 /**\r
233   Transfer data between the device and host. The CBI contains three phase,\r
234   command, data, and status. This is data phase.\r
235 \r
236   @param  UsbCbi                The USB CBI device\r
237   @param  DataDir               The direction of the data transfer\r
238   @param  Data                  The buffer to hold the data\r
239   @param  TransLen              The expected transfer length\r
240   @param  Timeout               The time to wait the command to execute\r
241 \r
242   @retval EFI_SUCCESS           The data transfer succeeded\r
243   @retval Others                Failed to transfer all the data\r
244 \r
245 **/\r
246 STATIC\r
247 EFI_STATUS\r
248 UsbCbiDataTransfer (\r
249   IN USB_CBI_PROTOCOL         *UsbCbi,\r
250   IN EFI_USB_DATA_DIRECTION   DataDir,\r
251   IN OUT UINT8                *Data,\r
252   IN OUT UINTN                *TransLen,\r
253   IN UINT32                   Timeout\r
254   )\r
255 {\r
256   EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;\r
257   EFI_STATUS                  Status;\r
258   UINT32                      TransStatus;\r
259   UINTN                       Remain;\r
260   UINTN                       Increment;\r
261   UINT8                       *Next;\r
262   UINTN                       Retry;\r
263 \r
264   //\r
265   // It's OK if no data to transfer\r
266   //\r
267   if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {\r
268     return EFI_SUCCESS;\r
269   }\r
270 \r
271   //\r
272   // Select the endpoint then issue the transfer\r
273   //\r
274   if (DataDir == EfiUsbDataIn) {\r
275     Endpoint = UsbCbi->BulkInEndpoint;\r
276   } else {\r
277     Endpoint = UsbCbi->BulkOutEndpoint;\r
278   }\r
279 \r
280   Next    = Data;\r
281   Remain  = *TransLen;\r
282   Retry   = 0;\r
283   Status  = EFI_SUCCESS;\r
284   Timeout = Timeout / USB_MASS_STALL_1_MS;\r
285 \r
286   //\r
287   // Transfer the data, if the device returns NAK, retry it.\r
288   //\r
289   while (Remain > 0) {\r
290     TransStatus = 0;\r
291 \r
292     if (Remain > (UINTN) USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize) {\r
293       Increment = USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize;\r
294     } else {\r
295       Increment = Remain;\r
296     }\r
297 \r
298     Status = UsbCbi->UsbIo->UsbBulkTransfer (\r
299                               UsbCbi->UsbIo,\r
300                               Endpoint->EndpointAddress,\r
301                               Next,\r
302                               &Increment,\r
303                               Timeout,\r
304                               &TransStatus\r
305                               );\r
306     if (EFI_ERROR (Status)) {\r
307       if (TransStatus == EFI_USB_ERR_NAK) {\r
308         //\r
309         // The device can NAK the host if either the data/buffer isn't\r
310         // aviable or the command is in-progress. The data can be partly\r
311         // transferred. The transfer is aborted if several succssive data\r
312         // transfer commands are NAKed.\r
313         //\r
314         if (Increment == 0) {\r
315           if (++Retry > USB_CBI_MAX_RETRY) {\r
316             goto ON_EXIT;\r
317           }\r
318 \r
319         } else {\r
320           Next   += Increment;\r
321           Remain -= Increment;\r
322           Retry   = 0;\r
323         }\r
324 \r
325         continue;\r
326       }\r
327 \r
328       //\r
329       // The device can fail the command by STALL the bulk endpoint.\r
330       // Clear the stall if that is the case.\r
331       //\r
332       if (TransStatus == EFI_USB_ERR_STALL) {\r
333         UsbClearEndpointStall (UsbCbi->UsbIo, Endpoint->EndpointAddress);\r
334       }\r
335 \r
336       goto ON_EXIT;\r
337     }\r
338 \r
339     Next += Increment;\r
340     Remain -= Increment;\r
341   }\r
342 \r
343 ON_EXIT:\r
344   *TransLen -= Remain;\r
345   return Status;\r
346 }\r
347 \r
348 \r
349 /**\r
350   Get the result of high level command execution from interrupt\r
351   endpoint. This function returns the USB transfer status, and\r
352   put the high level command execution result in Result.\r
353 \r
354   @param  UsbCbi                The USB CBI protocol\r
355   @param  Timeout               The time to wait the command to execute\r
356   @param  Result                GC_TODO: add argument description\r
357 \r
358   @retval EFI_SUCCESS           The high level command execution result is\r
359                                 retrieved in Result.\r
360   @retval Others                Failed to retrieve the result.\r
361 \r
362 **/\r
363 STATIC\r
364 EFI_STATUS\r
365 UsbCbiGetStatus (\r
366   IN  USB_CBI_PROTOCOL        *UsbCbi,\r
367   IN  UINT32                  Timeout,\r
368   OUT USB_CBI_STATUS          *Result\r
369   )\r
370 {\r
371   UINTN                     Len;\r
372   UINT8                     Endpoint;\r
373   EFI_STATUS                Status;\r
374   UINT32                    TransStatus;\r
375   INTN                      Retry;\r
376 \r
377   Endpoint  = UsbCbi->InterruptEndpoint->EndpointAddress;\r
378   Status    = EFI_SUCCESS;\r
379   Timeout   = Timeout / USB_MASS_STALL_1_MS;\r
380 \r
381   //\r
382   // Attemp to the read the result from interrupt endpoint\r
383   //\r
384   for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {\r
385     TransStatus = 0;\r
386     Len         = sizeof (USB_CBI_STATUS);\r
387 \r
388     Status = UsbCbi->UsbIo->UsbSyncInterruptTransfer (\r
389                               UsbCbi->UsbIo,\r
390                               Endpoint,\r
391                               Result,\r
392                               &Len,\r
393                               Timeout,\r
394                               &TransStatus\r
395                               );\r
396     //\r
397     // The CBI can NAK the interrupt endpoint if the command is in-progress.\r
398     //\r
399     if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {\r
400       continue;\r
401     }\r
402 \r
403     break;\r
404   }\r
405 \r
406   return Status;\r
407 }\r
408 \r
409 \r
410 /**\r
411   Execute USB mass storage command through the CBI0/CBI1 transport protocol\r
412 \r
413   @param  Context               The USB CBI device\r
414   @param  Cmd                   The command to transfer to device\r
415   @param  CmdLen                The length of the command\r
416   @param  DataDir               The direction of data transfer\r
417   @param  Data                  The buffer to hold the data\r
418   @param  DataLen               The length of the buffer\r
419   @param  Timeout               The time to wait\r
420   @param  CmdStatus             The result of the command execution\r
421 \r
422   @retval EFI_SUCCESS           The command is executed OK and result in CmdStatus.\r
423   @retval EFI_DEVICE_ERROR      Failed to execute the command\r
424 \r
425 **/\r
426 STATIC\r
427 EFI_STATUS\r
428 UsbCbiExecCommand (\r
429   IN  VOID                    *Context,\r
430   IN  VOID                    *Cmd,\r
431   IN  UINT8                   CmdLen,\r
432   IN  EFI_USB_DATA_DIRECTION  DataDir,\r
433   IN  VOID                    *Data,\r
434   IN  UINT32                  DataLen,\r
435   IN  UINT32                  Timeout,\r
436   OUT UINT32                  *CmdStatus\r
437   )\r
438 {\r
439   USB_CBI_PROTOCOL          *UsbCbi;\r
440   USB_CBI_STATUS            Result;\r
441   EFI_STATUS                Status;\r
442   UINTN                     TransLen;\r
443 \r
444   *CmdStatus  = USB_MASS_CMD_SUCCESS;\r
445   UsbCbi      = (USB_CBI_PROTOCOL *) Context;\r
446 \r
447   //\r
448   // Send the command to the device. Return immediately if device\r
449   // rejects the command.\r
450   //\r
451   Status = UsbCbiSendCommand (UsbCbi, Cmd, CmdLen, Timeout);\r
452   if (EFI_ERROR (Status)) {\r
453     DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiSendCommand (%r)\n",Status));\r
454     return Status;\r
455   }\r
456 \r
457   //\r
458   // Transfer the data, return this status if no interrupt endpoint\r
459   // is used to report the transfer status.\r
460   //\r
461   TransLen = (UINTN) DataLen;\r
462 \r
463   Status   = UsbCbiDataTransfer (UsbCbi, DataDir, Data, &TransLen, Timeout);\r
464   if (UsbCbi->InterruptEndpoint == NULL) {\r
465     DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiDataTransfer (%r)\n",Status));\r
466     return Status;\r
467   }\r
468 \r
469   //\r
470   // Get the status, if that succeeds, interpret the result\r
471   //\r
472   Status = UsbCbiGetStatus (UsbCbi, Timeout, &Result);\r
473   if (EFI_ERROR (Status)) {\r
474     DEBUG ((mUsbCbiError, "UsbCbiExecCommand: UsbCbiGetStatus (%r)\n",Status));\r
475     return EFI_DEVICE_ERROR;\r
476   }\r
477 \r
478   if (UsbCbi->Interface.InterfaceSubClass == USB_MASS_STORE_UFI) {\r
479     //\r
480     // For UFI device, ASC and ASCQ are returned.\r
481     //\r
482     if (Result.Type != 0) {\r
483       *CmdStatus = USB_MASS_CMD_FAIL;\r
484     }\r
485 \r
486   } else {\r
487     //\r
488     // Check page 27, CBI spec 1.1 for vaious reture status.\r
489     //\r
490     switch (Result.Value & 0x03) {\r
491     case 0x00:\r
492       //\r
493       // Pass\r
494       //\r
495       *CmdStatus = USB_MASS_CMD_SUCCESS;\r
496       break;\r
497 \r
498     case 0x02:\r
499       //\r
500       // Phase Error, response with reset. Fall through to Fail.\r
501       //\r
502       UsbCbiResetDevice (UsbCbi, FALSE);\r
503 \r
504     case 0x01:\r
505       //\r
506       // Fail\r
507       //\r
508       *CmdStatus = USB_MASS_CMD_FAIL;\r
509       break;\r
510 \r
511     case 0x03:\r
512       //\r
513       // Persistent Fail, need to send REQUEST SENSE.\r
514       //\r
515       *CmdStatus = USB_MASS_CMD_PERSISTENT;\r
516       break;\r
517     }\r
518   }\r
519 \r
520   return EFI_SUCCESS;\r
521 }\r
522 \r
523 \r
524 /**\r
525   Call the Usb mass storage class transport protocol to\r
526   reset the device. The reset is defined as a Non-Data\r
527   command. Don't use UsbCbiExecCommand to send the command\r
528   to device because that may introduce recursive loop.\r
529 \r
530   @param  Context               The USB CBI device protocol\r
531 \r
532   @retval EFI_SUCCESS           the device is reset\r
533   @retval Others                Failed to reset the device\r
534 \r
535 **/\r
536 STATIC\r
537 EFI_STATUS\r
538 UsbCbiResetDevice (\r
539   IN  VOID                    *Context,\r
540   IN  BOOLEAN                  ExtendedVerification\r
541   )\r
542 {\r
543   UINT8                     ResetCmd[USB_CBI_RESET_CMD_LEN];\r
544   USB_CBI_PROTOCOL          *UsbCbi;\r
545   USB_CBI_STATUS            Result;\r
546   EFI_STATUS                Status;\r
547   UINT32                    Timeout;\r
548 \r
549   UsbCbi = (USB_CBI_PROTOCOL *) Context;\r
550 \r
551   //\r
552   // Fill in the reset command.\r
553   //\r
554   SetMem (ResetCmd, USB_CBI_RESET_CMD_LEN, 0xFF);\r
555 \r
556   ResetCmd[0] = 0x1D;\r
557   ResetCmd[1] = 0x04;\r
558   Timeout     = USB_CBI_RESET_TIMEOUT / USB_MASS_STALL_1_MS;\r
559 \r
560   //\r
561   // Send the command to the device. Don't use UsbCbiExecCommand here.\r
562   //\r
563   Status = UsbCbiSendCommand (UsbCbi, ResetCmd, USB_CBI_RESET_CMD_LEN, Timeout);\r
564   if (EFI_ERROR (Status)) {\r
565     return Status;\r
566   }\r
567 \r
568   //\r
569   // Just retrieve the status and ignore that. Then stall\r
570   // 50ms to wait it complete\r
571   //\r
572   UsbCbiGetStatus (UsbCbi, Timeout, &Result);\r
573   gBS->Stall (50 * 1000);\r
574 \r
575   //\r
576   // Clear the Bulk-In and Bulk-Out stall condition and\r
577   // init data toggle.\r
578   //\r
579   UsbClearEndpointStall (UsbCbi->UsbIo, UsbCbi->BulkInEndpoint->EndpointAddress);\r
580   UsbClearEndpointStall (UsbCbi->UsbIo, UsbCbi->BulkOutEndpoint->EndpointAddress);\r
581   return Status;\r
582 }\r
583 \r
584 \r
585 /**\r
586   Clean up the CBI protocol's resource\r
587 \r
588   @param  Context               The CBI protocol\r
589 \r
590   @retval EFI_SUCCESS           The resource is cleaned up.\r
591 \r
592 **/\r
593 STATIC\r
594 EFI_STATUS\r
595 UsbCbiFini (\r
596   IN  VOID                   *Context\r
597   )\r
598 {\r
599   gBS->FreePool (Context);\r
600   return EFI_SUCCESS;\r
601 }\r
602 \r
603 USB_MASS_TRANSPORT\r
604 mUsbCbi0Transport = {\r
605   USB_MASS_STORE_CBI0,\r
606   UsbCbiInit,\r
607   UsbCbiExecCommand,\r
608   UsbCbiResetDevice,\r
609   UsbCbiFini\r
610 };\r
611 \r
612 USB_MASS_TRANSPORT\r
613 mUsbCbi1Transport = {\r
614   USB_MASS_STORE_CBI1,\r
615   UsbCbiInit,\r
616   UsbCbiExecCommand,\r
617   UsbCbiResetDevice,\r
618   UsbCbiFini\r
619 };\r