Import Usb/UsbBusDxe and Usb/UsbMassStorageDxe into MdeModulePkg.
[people/mcb30/edk2.git] / edk2 / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassBot.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   UsbMassBot.c\r
15 \r
16 Abstract:\r
17 \r
18   Implementation of the USB mass storage Bulk-Only Transport protocol.\r
19 \r
20 Revision History\r
21 \r
22 \r
23 **/\r
24 \r
25 #include "UsbMass.h"\r
26 #include "UsbMassBot.h"\r
27 \r
28 UINTN mUsbBotInfo  = DEBUG_INFO;\r
29 UINTN mUsbBotError = DEBUG_ERROR;\r
30 \r
31 STATIC\r
32 EFI_STATUS\r
33 UsbBotResetDevice (\r
34   IN  VOID                    *Context,\r
35   IN  BOOLEAN                  ExtendedVerification\r
36   );\r
37 \r
38 \r
39 /**\r
40   Initialize the USB mass storage class BOT transport protocol.\r
41   It will save its context which is a USB_BOT_PROTOCOL structure\r
42   in the Context if Context isn't NULL.\r
43 \r
44   @param  UsbIo                 The USB IO protocol to use\r
45   @param  Controller            The controller to init\r
46   @param  Context               The variable to save the context to\r
47 \r
48   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory\r
49   @retval EFI_UNSUPPORTED       The transport protocol doesn't support the device.\r
50   @retval EFI_SUCCESS           The device is supported and protocol initialized.\r
51 \r
52 **/\r
53 STATIC\r
54 EFI_STATUS\r
55 UsbBotInit (\r
56   IN  EFI_USB_IO_PROTOCOL       * UsbIo,\r
57   IN  EFI_HANDLE                Controller,\r
58   OUT VOID                      **Context OPTIONAL\r
59   )\r
60 {\r
61   USB_BOT_PROTOCOL              *UsbBot;\r
62   EFI_USB_INTERFACE_DESCRIPTOR  *Interface;\r
63   EFI_USB_ENDPOINT_DESCRIPTOR   EndPoint;\r
64   EFI_STATUS                    Status;\r
65   UINT8                         Index;\r
66 \r
67   //\r
68   // Allocate the BOT context, append two endpoint descriptors to it\r
69   //\r
70   UsbBot = AllocateZeroPool (\r
71              sizeof (USB_BOT_PROTOCOL) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)\r
72              );\r
73   if (UsbBot == NULL) {\r
74     return EFI_OUT_OF_RESOURCES;\r
75   }\r
76 \r
77   UsbBot->UsbIo = UsbIo;\r
78 \r
79   //\r
80   // Get the interface descriptor and validate that it\r
81   // is a USB MSC BOT interface.\r
82   //\r
83   Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbBot->Interface);\r
84 \r
85   if (EFI_ERROR (Status)) {\r
86     DEBUG ((mUsbBotError, "UsbBotInit: Get invalid BOT interface (%r)\n", Status));\r
87     goto ON_ERROR;\r
88   }\r
89 \r
90   Interface = &UsbBot->Interface;\r
91 \r
92   if (Interface->InterfaceProtocol != USB_MASS_STORE_BOT) {\r
93     Status = EFI_UNSUPPORTED;\r
94     goto ON_ERROR;\r
95   }\r
96 \r
97   //\r
98   // Locate and save the first bulk-in and bulk-out endpoint\r
99   //\r
100   for (Index = 0; Index < Interface->NumEndpoints; Index++) {\r
101     Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);\r
102 \r
103     if (EFI_ERROR (Status) || !USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {\r
104       continue;\r
105     }\r
106 \r
107     if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&\r
108        (UsbBot->BulkInEndpoint == NULL)) {\r
109 \r
110       UsbBot->BulkInEndpoint  = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1);\r
111       *UsbBot->BulkInEndpoint = EndPoint;\r
112     }\r
113 \r
114     if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&\r
115        (UsbBot->BulkOutEndpoint == NULL)) {\r
116 \r
117       UsbBot->BulkOutEndpoint   = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1) + 1;\r
118       *UsbBot->BulkOutEndpoint  = EndPoint;\r
119     }\r
120   }\r
121 \r
122   if ((UsbBot->BulkInEndpoint == NULL) || (UsbBot->BulkOutEndpoint == NULL)) {\r
123     DEBUG ((mUsbBotError, "UsbBotInit: In/Out Endpoint invalid\n"));\r
124     Status = EFI_UNSUPPORTED;\r
125     goto ON_ERROR;\r
126   }\r
127 \r
128   //\r
129   // The USB BOT protocol uses dCBWTag to match the CBW and CSW.\r
130   //\r
131   UsbBot->CbwTag = 0x01;\r
132 \r
133   if (Context != NULL) {\r
134     *Context = UsbBot;\r
135   } else {\r
136     gBS->FreePool (UsbBot);\r
137   }\r
138 \r
139   return EFI_SUCCESS;\r
140 \r
141 ON_ERROR:\r
142   gBS->FreePool (UsbBot);\r
143   return Status;\r
144 }\r
145 \r
146 \r
147 /**\r
148   Send the command to the device using Bulk-Out endpoint\r
149 \r
150   @param  UsbBot                The USB BOT device\r
151   @param  Cmd                   The command to transfer to device\r
152   @param  CmdLen                the length of the command\r
153   @param  DataDir               The direction of the data\r
154   @param  TransLen              The expected length of the data\r
155 \r
156   @retval EFI_NOT_READY         The device return NAK to the transfer\r
157   @retval EFI_SUCCESS           The command is sent to the device.\r
158   @retval Others                Failed to send the command to device\r
159 \r
160 **/\r
161 STATIC\r
162 EFI_STATUS\r
163 UsbBotSendCommand (\r
164   IN USB_BOT_PROTOCOL         *UsbBot,\r
165   IN UINT8                    *Cmd,\r
166   IN UINT8                    CmdLen,\r
167   IN EFI_USB_DATA_DIRECTION   DataDir,\r
168   IN UINT32                   TransLen\r
169   )\r
170 {\r
171   USB_BOT_CBW               Cbw;\r
172   EFI_STATUS                Status;\r
173   UINT32                    Result;\r
174   UINTN                     DataLen;\r
175   UINTN                     Timeout;\r
176 \r
177   ASSERT ((CmdLen > 0) && (CmdLen <= USB_BOT_MAX_CMDLEN));\r
178 \r
179   //\r
180   // Fill in the CSW. Only the first LUN is supported now.\r
181   //\r
182   Cbw.Signature = USB_BOT_CBW_SIGNATURE;\r
183   Cbw.Tag       = UsbBot->CbwTag;\r
184   Cbw.DataLen   = TransLen;\r
185   Cbw.Flag      = ((DataDir == EfiUsbDataIn) ? 0x80 : 0);\r
186   Cbw.Lun       = 0;\r
187   Cbw.CmdLen    = CmdLen;\r
188 \r
189   ZeroMem (Cbw.CmdBlock, USB_BOT_MAX_CMDLEN);\r
190   CopyMem (Cbw.CmdBlock, Cmd, CmdLen);\r
191 \r
192   Result        = 0;\r
193   DataLen       = sizeof (USB_BOT_CBW);\r
194   Timeout       = USB_BOT_CBW_TIMEOUT / USB_MASS_STALL_1_MS;\r
195 \r
196   //\r
197   // Use the UsbIo to send the command to the device. The default\r
198   // time out is enough.\r
199   //\r
200   Status = UsbBot->UsbIo->UsbBulkTransfer (\r
201                             UsbBot->UsbIo,\r
202                             UsbBot->BulkOutEndpoint->EndpointAddress,\r
203                             &Cbw,\r
204                             &DataLen,\r
205                             Timeout,\r
206                             &Result\r
207                             );\r
208   //\r
209   // Respond to Bulk-Out endpoint stall with a Reset Recovery,\r
210   // see the spec section 5.3.1\r
211   //\r
212   if (EFI_ERROR (Status)) {\r
213     if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL) && DataDir == EfiUsbDataOut) {\r
214       UsbBotResetDevice (UsbBot, FALSE);\r
215     } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {\r
216       Status = EFI_NOT_READY;\r
217     }\r
218   }\r
219 \r
220   return Status;\r
221 }\r
222 \r
223 \r
224 /**\r
225   Transfer the data between the device and host. BOT transfer\r
226   is composed of three phase, command, data, and status.\r
227 \r
228   @param  UsbBot                The USB BOT device\r
229   @param  DataDir               The direction of the data\r
230   @param  Data                  The buffer to hold data\r
231   @param  TransLen              The expected length of the data\r
232   @param  Timeout               The time to wait the command to complete\r
233 \r
234   @retval EFI_SUCCESS           The data is transferred\r
235   @retval Others                Failed to transfer data\r
236 \r
237 **/\r
238 STATIC\r
239 EFI_STATUS\r
240 UsbBotDataTransfer (\r
241   IN USB_BOT_PROTOCOL         *UsbBot,\r
242   IN EFI_USB_DATA_DIRECTION   DataDir,\r
243   IN OUT UINT8                *Data,\r
244   IN OUT UINTN                *TransLen,\r
245   IN UINT32                   Timeout\r
246   )\r
247 {\r
248   EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;\r
249   EFI_STATUS                  Status;\r
250   UINT32                      Result;\r
251 \r
252   //\r
253   // It's OK if no data to transfer\r
254   //\r
255   if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {\r
256     return EFI_SUCCESS;\r
257   }\r
258 \r
259   //\r
260   // Select the endpoint then issue the transfer\r
261   //\r
262   if (DataDir == EfiUsbDataIn) {\r
263     Endpoint = UsbBot->BulkInEndpoint;\r
264   } else {\r
265     Endpoint = UsbBot->BulkOutEndpoint;\r
266   }\r
267 \r
268   Result  = 0;\r
269   Timeout = Timeout / USB_MASS_STALL_1_MS;\r
270 \r
271   Status = UsbBot->UsbIo->UsbBulkTransfer (\r
272                             UsbBot->UsbIo,\r
273                             Endpoint->EndpointAddress,\r
274                             Data,\r
275                             TransLen,\r
276                             Timeout,\r
277                             &Result\r
278                             );\r
279   if (EFI_ERROR (Status)) {\r
280     DEBUG ((mUsbBotError, "UsbBotDataTransfer: (%r)\n", Status));\r
281     if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {\r
282       DEBUG ((mUsbBotError, "UsbBotDataTransfer: DataIn Stall\n"));\r
283       UsbClearEndpointStall (UsbBot->UsbIo, Endpoint->EndpointAddress);\r
284     } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {\r
285       Status = EFI_NOT_READY;\r
286     }\r
287   }\r
288 \r
289   return Status;\r
290 }\r
291 \r
292 \r
293 /**\r
294   Get the command execution status from device. BOT transfer is\r
295   composed of three phase, command, data, and status.\r
296   This function return the transfer status of the BOT's CSW status,\r
297   and return the high level command execution result in Result. So\r
298   even it returns EFI_SUCCESS, the command may still have failed.\r
299 \r
300   @param  UsbBot                The USB BOT device\r
301   @param  TransLen              The expected length of the data\r
302   @param  Timeout               The time to wait the command to complete\r
303   @param  CmdStatus             The result of the command execution.\r
304 \r
305   @retval EFI_DEVICE_ERROR      Failed to retrieve the command execute result\r
306   @retval EFI_SUCCESS           Command execute result is retrieved and in the\r
307                                 Result.\r
308 \r
309 **/\r
310 STATIC\r
311 EFI_STATUS\r
312 UsbBotGetStatus (\r
313   IN  USB_BOT_PROTOCOL      *UsbBot,\r
314   IN  UINT32                TransLen,\r
315   OUT UINT8                 *CmdStatus\r
316   )\r
317 {\r
318   USB_BOT_CSW               Csw;\r
319   UINTN                     Len;\r
320   UINT8                     Endpoint;\r
321   EFI_STATUS                Status;\r
322   UINT32                    Result;\r
323   EFI_USB_IO_PROTOCOL       *UsbIo;\r
324   UINT32                    Index;\r
325   UINTN                     Timeout;\r
326 \r
327   *CmdStatus = USB_BOT_COMMAND_ERROR;\r
328   Status     = EFI_DEVICE_ERROR;\r
329   Endpoint   = UsbBot->BulkInEndpoint->EndpointAddress;\r
330   UsbIo      = UsbBot->UsbIo;\r
331   Timeout    = USB_BOT_CSW_TIMEOUT / USB_MASS_STALL_1_MS;\r
332 \r
333   for (Index = 0; Index < USB_BOT_GET_STATUS_RETRY; Index++) {\r
334     //\r
335     // Attemp to the read CSW from bulk in endpoint\r
336     //\r
337     ZeroMem (&Csw, sizeof (USB_BOT_CSW));\r
338     Result = 0;\r
339     Len    = sizeof (USB_BOT_CSW);\r
340     Status = UsbIo->UsbBulkTransfer (\r
341                       UsbIo,\r
342                       Endpoint,\r
343                       &Csw,\r
344                       &Len,\r
345                       Timeout,\r
346                       &Result\r
347                       );\r
348     if (EFI_ERROR(Status)) {\r
349       DEBUG ((mUsbBotError, "UsbBotGetStatus (%r)\n", Status));\r
350       if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {\r
351         DEBUG ((mUsbBotError, "UsbBotGetStatus: DataIn Stall\n"));\r
352         UsbClearEndpointStall (UsbIo, Endpoint);\r
353       }\r
354       continue;\r
355     }\r
356 \r
357     if (Csw.Signature != USB_BOT_CSW_SIGNATURE) {\r
358       //\r
359       // Invalid Csw need perform reset recovery\r
360       //\r
361       DEBUG ((mUsbBotError, "UsbBotGetStatus: Device return a invalid signature\n"));\r
362       Status = UsbBotResetDevice (UsbBot, FALSE);\r
363     } else if (Csw.CmdStatus == USB_BOT_COMMAND_ERROR) {\r
364       //\r
365       // Respond phase error need perform reset recovery\r
366       //\r
367       DEBUG ((mUsbBotError, "UsbBotGetStatus: Device return a phase error\n"));\r
368       Status = UsbBotResetDevice (UsbBot, FALSE);\r
369     } else {\r
370 \r
371       *CmdStatus = Csw.CmdStatus;\r
372       break;\r
373     }\r
374   }\r
375   //\r
376   //The tag is increased even there is an error.\r
377   //\r
378   UsbBot->CbwTag++;\r
379 \r
380   return Status;\r
381 }\r
382 \r
383 \r
384 /**\r
385   Call the Usb mass storage class transport protocol to issue\r
386   the command/data/status circle to execute the commands\r
387 \r
388   @param  Context               The context of the BOT protocol, that is,\r
389                                 USB_BOT_PROTOCOL\r
390   @param  Cmd                   The high level command\r
391   @param  CmdLen                The command length\r
392   @param  DataDir               The direction of the data transfer\r
393   @param  Data                  The buffer to hold data\r
394   @param  DataLen               The length of the data\r
395   @param  Timeout               The time to wait command\r
396   @param  CmdStatus             The result of high level command execution\r
397 \r
398   @retval EFI_DEVICE_ERROR      Failed to excute command\r
399   @retval EFI_SUCCESS           The command is executed OK, and result in CmdStatus\r
400 \r
401 **/\r
402 STATIC\r
403 EFI_STATUS\r
404 UsbBotExecCommand (\r
405   IN  VOID                    *Context,\r
406   IN  VOID                    *Cmd,\r
407   IN  UINT8                   CmdLen,\r
408   IN  EFI_USB_DATA_DIRECTION  DataDir,\r
409   IN  VOID                    *Data,\r
410   IN  UINT32                  DataLen,\r
411   IN  UINT32                  Timeout,\r
412   OUT UINT32                  *CmdStatus\r
413   )\r
414 {\r
415   USB_BOT_PROTOCOL          *UsbBot;\r
416   EFI_STATUS                Status;\r
417   UINTN                     TransLen;\r
418   UINT8                     Result;\r
419 \r
420   *CmdStatus  = USB_MASS_CMD_FAIL;\r
421   UsbBot      = (USB_BOT_PROTOCOL *) Context;\r
422 \r
423   //\r
424   // Send the command to the device. Return immediately if device\r
425   // rejects the command.\r
426   //\r
427   Status = UsbBotSendCommand (UsbBot, Cmd, CmdLen, DataDir, DataLen);\r
428   if (EFI_ERROR (Status)) {\r
429     DEBUG ((mUsbBotError, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status));\r
430     return Status;\r
431   }\r
432 \r
433   //\r
434   // Transfer the data. Don't return immediately even data transfer\r
435   // failed. The host should attempt to receive the CSW no matter\r
436   // whether it succeeds or failed.\r
437   //\r
438   TransLen = (UINTN) DataLen;\r
439   UsbBotDataTransfer (UsbBot, DataDir, Data, &TransLen, Timeout);\r
440 \r
441   //\r
442   // Get the status, if that succeeds, interpret the result\r
443   //\r
444   Status = UsbBotGetStatus (UsbBot, DataLen, &Result);\r
445   if (EFI_ERROR (Status)) {\r
446     DEBUG ((mUsbBotError, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status));\r
447     return Status;\r
448   }\r
449 \r
450   if (Result == 0) {\r
451     *CmdStatus = USB_MASS_CMD_SUCCESS;\r
452   }\r
453 \r
454   return EFI_SUCCESS;\r
455 }\r
456 \r
457 \r
458 /**\r
459   Reset the mass storage device by BOT protocol\r
460 \r
461   @param  Context               The context of the BOT protocol, that is,\r
462                                 USB_BOT_PROTOCOL\r
463 \r
464   @retval EFI_SUCCESS           The device is reset\r
465   @retval Others                Failed to reset the device.\r
466 \r
467 **/\r
468 STATIC\r
469 EFI_STATUS\r
470 UsbBotResetDevice (\r
471   IN  VOID                    *Context,\r
472   IN  BOOLEAN                  ExtendedVerification\r
473   )\r
474 {\r
475   USB_BOT_PROTOCOL        *UsbBot;\r
476   EFI_USB_DEVICE_REQUEST  Request;\r
477   EFI_STATUS              Status;\r
478   UINT32                  Result;\r
479   UINT32                  Timeout;\r
480 \r
481   UsbBot = (USB_BOT_PROTOCOL *) Context;\r
482 \r
483   if (ExtendedVerification) {\r
484     //\r
485     // If we need to do strictly reset, reset its parent hub port\r
486     //\r
487     Status = UsbBot->UsbIo->UsbPortReset (UsbBot->UsbIo);\r
488     if (EFI_ERROR (Status)) {\r
489       return Status;\r
490     }\r
491   }\r
492 \r
493   //\r
494   // Issue a class specific "Bulk-Only Mass Storage Reset reqest.\r
495   // See the spec section 3.1\r
496   //\r
497   Request.RequestType = 0x21;\r
498   Request.Request     = USB_BOT_RESET_REQUEST;\r
499   Request.Value       = 0;\r
500   Request.Index       = UsbBot->Interface.InterfaceNumber;\r
501   Request.Length      = 0;\r
502   Timeout             = USB_BOT_RESET_TIMEOUT / USB_MASS_STALL_1_MS;\r
503 \r
504   Status = UsbBot->UsbIo->UsbControlTransfer (\r
505                             UsbBot->UsbIo,\r
506                             &Request,\r
507                             EfiUsbNoData,\r
508                             Timeout,\r
509                             NULL,\r
510                             0,\r
511                             &Result\r
512                             );\r
513 \r
514   if (EFI_ERROR (Status)) {\r
515     DEBUG ((mUsbBotError, "UsbBotResetDevice: (%r)\n", Status));\r
516     return Status;\r
517   }\r
518 \r
519   //\r
520   // The device shall NAK the host's request until the reset is\r
521   // complete. We can use this to sync the device and host. For\r
522   // now just stall 100ms to wait the device.\r
523   //\r
524   gBS->Stall (USB_BOT_RESET_STALL);\r
525 \r
526   //\r
527   // Clear the Bulk-In and Bulk-Out stall condition.\r
528   //\r
529   UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkInEndpoint->EndpointAddress);\r
530   UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkOutEndpoint->EndpointAddress);\r
531   return Status;\r
532 }\r
533 \r
534 \r
535 /**\r
536   Clean up the resource used by this BOT protocol\r
537 \r
538   @param  Context               The context of the BOT protocol, that is,\r
539                                 USB_BOT_PROTOCOL\r
540 \r
541   @retval EFI_SUCCESS           The resource is cleaned up.\r
542 \r
543 **/\r
544 STATIC\r
545 EFI_STATUS\r
546 UsbBotFini (\r
547   IN  VOID                    *Context\r
548   )\r
549 {\r
550   gBS->FreePool (Context);\r
551   return EFI_SUCCESS;\r
552 }\r
553 \r
554 USB_MASS_TRANSPORT\r
555 mUsbBotTransport = {\r
556   USB_MASS_STORE_BOT,\r
557   UsbBotInit,\r
558   UsbBotExecCommand,\r
559   UsbBotResetDevice,\r
560   UsbBotFini\r
561 };\r