• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Implementation of the USB mass storage Bulk-Only Transport protocol,
3   according to USB Mass Storage Class Bulk-Only Transport, Revision 1.0.
4 
5 Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution.  The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10 
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "UsbMass.h"
17 
18 //
19 // Definition of USB BOT Transport Protocol
20 //
21 USB_MASS_TRANSPORT mUsbBotTransport = {
22   USB_MASS_STORE_BOT,
23   UsbBotInit,
24   UsbBotExecCommand,
25   UsbBotResetDevice,
26   UsbBotGetMaxLun,
27   UsbBotCleanUp
28 };
29 
30 /**
31   Initializes USB BOT protocol.
32 
33   This function initializes the USB mass storage class BOT protocol.
34   It will save its context which is a USB_BOT_PROTOCOL structure
35   in the Context if Context isn't NULL.
36 
37   @param  UsbIo                 The USB I/O Protocol instance
38   @param  Context               The buffer to save the context to
39 
40   @retval EFI_SUCCESS           The device is successfully initialized.
41   @retval EFI_UNSUPPORTED       The transport protocol doesn't support the device.
42   @retval Other                 The USB BOT initialization fails.
43 
44 **/
45 EFI_STATUS
UsbBotInit(IN EFI_USB_IO_PROTOCOL * UsbIo,OUT VOID ** Context OPTIONAL)46 UsbBotInit (
47   IN  EFI_USB_IO_PROTOCOL       *UsbIo,
48   OUT VOID                      **Context OPTIONAL
49   )
50 {
51   USB_BOT_PROTOCOL              *UsbBot;
52   EFI_USB_INTERFACE_DESCRIPTOR  *Interface;
53   EFI_USB_ENDPOINT_DESCRIPTOR   EndPoint;
54   EFI_STATUS                    Status;
55   UINT8                         Index;
56 
57   //
58   // Allocate the BOT context for USB_BOT_PROTOCOL and two endpoint descriptors.
59   //
60   UsbBot = AllocateZeroPool (sizeof (USB_BOT_PROTOCOL) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR));
61   ASSERT (UsbBot != NULL);
62 
63   UsbBot->UsbIo = UsbIo;
64 
65   //
66   // Get the interface descriptor and validate that it
67   // is a USB Mass Storage BOT interface.
68   //
69   Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbBot->Interface);
70 
71   if (EFI_ERROR (Status)) {
72     goto ON_ERROR;
73   }
74 
75   Interface = &UsbBot->Interface;
76 
77   if (Interface->InterfaceProtocol != USB_MASS_STORE_BOT) {
78     Status = EFI_UNSUPPORTED;
79     goto ON_ERROR;
80   }
81 
82   //
83   // Locate and save the first bulk-in and bulk-out endpoint
84   //
85   for (Index = 0; Index < Interface->NumEndpoints; Index++) {
86     Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);
87 
88     if (EFI_ERROR (Status) || !USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {
89       continue;
90     }
91 
92     if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&
93        (UsbBot->BulkInEndpoint == NULL)) {
94 
95       UsbBot->BulkInEndpoint  = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1);
96       CopyMem(UsbBot->BulkInEndpoint, &EndPoint, sizeof (EndPoint));
97     }
98 
99     if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&
100        (UsbBot->BulkOutEndpoint == NULL)) {
101 
102       UsbBot->BulkOutEndpoint   = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1) + 1;
103       CopyMem (UsbBot->BulkOutEndpoint, &EndPoint, sizeof(EndPoint));
104     }
105   }
106 
107   //
108   // If bulk-in or bulk-out endpoint is not found, report error.
109   //
110   if ((UsbBot->BulkInEndpoint == NULL) || (UsbBot->BulkOutEndpoint == NULL)) {
111     Status = EFI_UNSUPPORTED;
112     goto ON_ERROR;
113   }
114 
115   //
116   // The USB BOT protocol uses CBWTag to match the CBW and CSW.
117   //
118   UsbBot->CbwTag = 0x01;
119 
120   if (Context != NULL) {
121     *Context = UsbBot;
122   } else {
123     FreePool (UsbBot);
124   }
125 
126   return EFI_SUCCESS;
127 
128 ON_ERROR:
129   FreePool (UsbBot);
130   return Status;
131 }
132 
133 /**
134   Send the command to the device using Bulk-Out endpoint.
135 
136   This function sends the command to the device using Bulk-Out endpoint.
137   BOT transfer is composed of three phases: Command, Data, and Status.
138   This is the Command phase.
139 
140   @param  UsbBot                The USB BOT device
141   @param  Cmd                   The command to transfer to device
142   @param  CmdLen                The length of the command
143   @param  DataDir               The direction of the data
144   @param  TransLen              The expected length of the data
145   @param  Lun                   The number of logic unit
146 
147   @retval EFI_SUCCESS           The command is sent to the device.
148   @retval EFI_NOT_READY         The device return NAK to the transfer
149   @retval Others                Failed to send the command to device
150 
151 **/
152 EFI_STATUS
UsbBotSendCommand(IN USB_BOT_PROTOCOL * UsbBot,IN UINT8 * Cmd,IN UINT8 CmdLen,IN EFI_USB_DATA_DIRECTION DataDir,IN UINT32 TransLen,IN UINT8 Lun)153 UsbBotSendCommand (
154   IN USB_BOT_PROTOCOL         *UsbBot,
155   IN UINT8                    *Cmd,
156   IN UINT8                    CmdLen,
157   IN EFI_USB_DATA_DIRECTION   DataDir,
158   IN UINT32                   TransLen,
159   IN UINT8                    Lun
160   )
161 {
162   USB_BOT_CBW               Cbw;
163   EFI_STATUS                Status;
164   UINT32                    Result;
165   UINTN                     DataLen;
166   UINTN                     Timeout;
167 
168   ASSERT ((CmdLen > 0) && (CmdLen <= USB_BOT_MAX_CMDLEN));
169 
170   //
171   // Fill in the Command Block Wrapper.
172   //
173   Cbw.Signature = USB_BOT_CBW_SIGNATURE;
174   Cbw.Tag       = UsbBot->CbwTag;
175   Cbw.DataLen   = TransLen;
176   Cbw.Flag      = (UINT8) ((DataDir == EfiUsbDataIn) ? BIT7 : 0);
177   Cbw.Lun       = Lun;
178   Cbw.CmdLen    = CmdLen;
179 
180   ZeroMem (Cbw.CmdBlock, USB_BOT_MAX_CMDLEN);
181   CopyMem (Cbw.CmdBlock, Cmd, CmdLen);
182 
183   Result  = 0;
184   DataLen = sizeof (USB_BOT_CBW);
185   Timeout = USB_BOT_SEND_CBW_TIMEOUT / USB_MASS_1_MILLISECOND;
186 
187   //
188   // Use USB I/O Protocol to send the Command Block Wrapper to the device.
189   //
190   Status = UsbBot->UsbIo->UsbBulkTransfer (
191                             UsbBot->UsbIo,
192                             UsbBot->BulkOutEndpoint->EndpointAddress,
193                             &Cbw,
194                             &DataLen,
195                             Timeout,
196                             &Result
197                             );
198   if (EFI_ERROR (Status)) {
199     if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL) && DataDir == EfiUsbDataOut) {
200       //
201       // Respond to Bulk-Out endpoint stall with a Reset Recovery,
202       // according to section 5.3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
203       //
204       UsbBotResetDevice (UsbBot, FALSE);
205     } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {
206       Status = EFI_NOT_READY;
207     }
208   }
209 
210   return Status;
211 }
212 
213 
214 /**
215   Transfer the data between the device and host.
216 
217   This function transfers the data between the device and host.
218   BOT transfer is composed of three phases: Command, Data, and Status.
219   This is the Data phase.
220 
221   @param  UsbBot                The USB BOT device
222   @param  DataDir               The direction of the data
223   @param  Data                  The buffer to hold data
224   @param  TransLen              The expected length of the data
225   @param  Timeout               The time to wait the command to complete
226 
227   @retval EFI_SUCCESS           The data is transferred
228   @retval EFI_SUCCESS           No data to transfer
229   @retval EFI_NOT_READY         The device return NAK to the transfer
230   @retval Others                Failed to transfer data
231 
232 **/
233 EFI_STATUS
UsbBotDataTransfer(IN USB_BOT_PROTOCOL * UsbBot,IN EFI_USB_DATA_DIRECTION DataDir,IN OUT UINT8 * Data,IN OUT UINTN * TransLen,IN UINT32 Timeout)234 UsbBotDataTransfer (
235   IN USB_BOT_PROTOCOL         *UsbBot,
236   IN EFI_USB_DATA_DIRECTION   DataDir,
237   IN OUT UINT8                *Data,
238   IN OUT UINTN                *TransLen,
239   IN UINT32                   Timeout
240   )
241 {
242   EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;
243   EFI_STATUS                  Status;
244   UINT32                      Result;
245 
246   //
247   // If no data to transfer, just return EFI_SUCCESS.
248   //
249   if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
250     return EFI_SUCCESS;
251   }
252 
253   //
254   // Select the endpoint then issue the transfer
255   //
256   if (DataDir == EfiUsbDataIn) {
257     Endpoint = UsbBot->BulkInEndpoint;
258   } else {
259     Endpoint = UsbBot->BulkOutEndpoint;
260   }
261 
262   Result  = 0;
263   Timeout = Timeout / USB_MASS_1_MILLISECOND;
264 
265   Status = UsbBot->UsbIo->UsbBulkTransfer (
266                             UsbBot->UsbIo,
267                             Endpoint->EndpointAddress,
268                             Data,
269                             TransLen,
270                             Timeout,
271                             &Result
272                             );
273   if (EFI_ERROR (Status)) {
274     if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {
275       DEBUG ((EFI_D_INFO, "UsbBotDataTransfer: (%r)\n", Status));
276       DEBUG ((EFI_D_INFO, "UsbBotDataTransfer: DataIn Stall\n"));
277       UsbClearEndpointStall (UsbBot->UsbIo, Endpoint->EndpointAddress);
278     } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {
279       Status = EFI_NOT_READY;
280     } else {
281       DEBUG ((EFI_D_ERROR, "UsbBotDataTransfer: (%r)\n", Status));
282     }
283     if(Status == EFI_TIMEOUT){
284       UsbBotResetDevice(UsbBot, FALSE);
285     }
286   }
287 
288   return Status;
289 }
290 
291 
292 /**
293   Get the command execution status from device.
294 
295   This function gets the command execution status from device.
296   BOT transfer is composed of three phases: Command, Data, and Status.
297   This is the Status phase.
298 
299   This function returns the transfer status of the BOT's CSW status,
300   and returns the high level command execution result in Result. So
301   even if EFI_SUCCESS is returned, the command may still have failed.
302 
303   @param  UsbBot         The USB BOT device.
304   @param  TransLen       The expected length of the data.
305   @param  CmdStatus      The result of the command execution.
306 
307   @retval EFI_SUCCESS    Command execute result is retrieved and in the Result.
308   @retval Other          Error occurred when trying to get status.
309 
310 **/
311 EFI_STATUS
UsbBotGetStatus(IN USB_BOT_PROTOCOL * UsbBot,IN UINT32 TransLen,OUT UINT8 * CmdStatus)312 UsbBotGetStatus (
313   IN  USB_BOT_PROTOCOL      *UsbBot,
314   IN  UINT32                TransLen,
315   OUT UINT8                 *CmdStatus
316   )
317 {
318   USB_BOT_CSW               Csw;
319   UINTN                     Len;
320   UINT8                     Endpoint;
321   EFI_STATUS                Status;
322   UINT32                    Result;
323   EFI_USB_IO_PROTOCOL       *UsbIo;
324   UINT32                    Index;
325   UINTN                     Timeout;
326 
327   *CmdStatus = USB_BOT_COMMAND_ERROR;
328   Status     = EFI_DEVICE_ERROR;
329   Endpoint   = UsbBot->BulkInEndpoint->EndpointAddress;
330   UsbIo      = UsbBot->UsbIo;
331   Timeout    = USB_BOT_RECV_CSW_TIMEOUT / USB_MASS_1_MILLISECOND;
332 
333   for (Index = 0; Index < USB_BOT_RECV_CSW_RETRY; Index++) {
334     //
335     // Attemp to the read Command Status Wrapper from bulk in endpoint
336     //
337     ZeroMem (&Csw, sizeof (USB_BOT_CSW));
338     Result = 0;
339     Len    = sizeof (USB_BOT_CSW);
340     Status = UsbIo->UsbBulkTransfer (
341                       UsbIo,
342                       Endpoint,
343                       &Csw,
344                       &Len,
345                       Timeout,
346                       &Result
347                       );
348     if (EFI_ERROR(Status)) {
349       if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {
350         UsbClearEndpointStall (UsbIo, Endpoint);
351       }
352       continue;
353     }
354 
355     if (Csw.Signature != USB_BOT_CSW_SIGNATURE) {
356       //
357       // CSW is invalid, so perform reset recovery
358       //
359       Status = UsbBotResetDevice (UsbBot, FALSE);
360     } else if (Csw.CmdStatus == USB_BOT_COMMAND_ERROR) {
361       //
362       // Respond phase error also needs reset recovery
363       //
364       Status = UsbBotResetDevice (UsbBot, FALSE);
365     } else {
366       *CmdStatus = Csw.CmdStatus;
367       break;
368     }
369   }
370   //
371   //The tag is increased even if there is an error.
372   //
373   UsbBot->CbwTag++;
374 
375   return Status;
376 }
377 
378 
379 /**
380   Call the USB Mass Storage Class BOT protocol to issue
381   the command/data/status circle to execute the commands.
382 
383   @param  Context               The context of the BOT protocol, that is,
384                                 USB_BOT_PROTOCOL
385   @param  Cmd                   The high level command
386   @param  CmdLen                The command length
387   @param  DataDir               The direction of the data transfer
388   @param  Data                  The buffer to hold data
389   @param  DataLen               The length of the data
390   @param  Lun                   The number of logic unit
391   @param  Timeout               The time to wait command
392   @param  CmdStatus             The result of high level command execution
393 
394   @retval EFI_SUCCESS           The command is executed successfully.
395   @retval Other                 Failed to execute command
396 
397 **/
398 EFI_STATUS
UsbBotExecCommand(IN VOID * Context,IN VOID * Cmd,IN UINT8 CmdLen,IN EFI_USB_DATA_DIRECTION DataDir,IN VOID * Data,IN UINT32 DataLen,IN UINT8 Lun,IN UINT32 Timeout,OUT UINT32 * CmdStatus)399 UsbBotExecCommand (
400   IN  VOID                    *Context,
401   IN  VOID                    *Cmd,
402   IN  UINT8                   CmdLen,
403   IN  EFI_USB_DATA_DIRECTION  DataDir,
404   IN  VOID                    *Data,
405   IN  UINT32                  DataLen,
406   IN  UINT8                   Lun,
407   IN  UINT32                  Timeout,
408   OUT UINT32                  *CmdStatus
409   )
410 {
411   USB_BOT_PROTOCOL          *UsbBot;
412   EFI_STATUS                Status;
413   UINTN                     TransLen;
414   UINT8                     Result;
415 
416   *CmdStatus  = USB_MASS_CMD_FAIL;
417   UsbBot      = (USB_BOT_PROTOCOL *) Context;
418 
419   //
420   // Send the command to the device. Return immediately if device
421   // rejects the command.
422   //
423   Status = UsbBotSendCommand (UsbBot, Cmd, CmdLen, DataDir, DataLen, Lun);
424   if (EFI_ERROR (Status)) {
425     DEBUG ((EFI_D_ERROR, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status));
426     return Status;
427   }
428 
429   //
430   // Transfer the data. Don't return immediately even data transfer
431   // failed. The host should attempt to receive the CSW no matter
432   // whether it succeeds or fails.
433   //
434   TransLen = (UINTN) DataLen;
435   UsbBotDataTransfer (UsbBot, DataDir, Data, &TransLen, Timeout);
436 
437   //
438   // Get the status, if that succeeds, interpret the result
439   //
440   Status = UsbBotGetStatus (UsbBot, DataLen, &Result);
441   if (EFI_ERROR (Status)) {
442     DEBUG ((EFI_D_ERROR, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status));
443     return Status;
444   }
445 
446   if (Result == 0) {
447     *CmdStatus = USB_MASS_CMD_SUCCESS;
448   }
449 
450   return EFI_SUCCESS;
451 }
452 
453 
454 /**
455   Reset the USB mass storage device by BOT protocol.
456 
457   @param  Context               The context of the BOT protocol, that is,
458                                 USB_BOT_PROTOCOL.
459   @param  ExtendedVerification  If FALSE, just issue Bulk-Only Mass Storage Reset request.
460                                 If TRUE, additionally reset parent hub port.
461 
462   @retval EFI_SUCCESS           The device is reset.
463   @retval Others                Failed to reset the device..
464 
465 **/
466 EFI_STATUS
UsbBotResetDevice(IN VOID * Context,IN BOOLEAN ExtendedVerification)467 UsbBotResetDevice (
468   IN  VOID                    *Context,
469   IN  BOOLEAN                 ExtendedVerification
470   )
471 {
472   USB_BOT_PROTOCOL        *UsbBot;
473   EFI_USB_DEVICE_REQUEST  Request;
474   EFI_STATUS              Status;
475   UINT32                  Result;
476   UINT32                  Timeout;
477 
478   UsbBot = (USB_BOT_PROTOCOL *) Context;
479 
480   if (ExtendedVerification) {
481     //
482     // If we need to do strictly reset, reset its parent hub port
483     //
484     Status = UsbBot->UsbIo->UsbPortReset (UsbBot->UsbIo);
485     if (EFI_ERROR (Status)) {
486       return EFI_DEVICE_ERROR;
487     }
488   }
489 
490   //
491   // Issue a class specific Bulk-Only Mass Storage Reset request,
492   // according to section 3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
493   //
494   Request.RequestType = 0x21;
495   Request.Request     = USB_BOT_RESET_REQUEST;
496   Request.Value       = 0;
497   Request.Index       = UsbBot->Interface.InterfaceNumber;
498   Request.Length      = 0;
499   Timeout             = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;
500 
501   Status = UsbBot->UsbIo->UsbControlTransfer (
502                             UsbBot->UsbIo,
503                             &Request,
504                             EfiUsbNoData,
505                             Timeout,
506                             NULL,
507                             0,
508                             &Result
509                             );
510 
511   if (EFI_ERROR (Status)) {
512     return EFI_DEVICE_ERROR;
513   }
514 
515   //
516   // The device shall NAK the host's request until the reset is
517   // complete. We can use this to sync the device and host. For
518   // now just stall 100ms to wait for the device.
519   //
520   gBS->Stall (USB_BOT_RESET_DEVICE_STALL);
521 
522   //
523   // Clear the Bulk-In and Bulk-Out stall condition.
524   //
525   UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkInEndpoint->EndpointAddress);
526   UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkOutEndpoint->EndpointAddress);
527 
528   return Status;
529 }
530 
531 
532 /**
533   Get the max LUN (Logical Unit Number) of USB mass storage device.
534 
535   @param  Context          The context of the BOT protocol, that is, USB_BOT_PROTOCOL
536   @param  MaxLun           Return pointer to the max number of LUN. (e.g. MaxLun=1 means LUN0 and
537                            LUN1 in all.)
538 
539   @retval EFI_SUCCESS      Max LUN is got successfully.
540   @retval Others           Fail to execute this request.
541 
542 **/
543 EFI_STATUS
UsbBotGetMaxLun(IN VOID * Context,OUT UINT8 * MaxLun)544 UsbBotGetMaxLun (
545   IN  VOID                    *Context,
546   OUT UINT8                   *MaxLun
547   )
548 {
549   USB_BOT_PROTOCOL        *UsbBot;
550   EFI_USB_DEVICE_REQUEST  Request;
551   EFI_STATUS              Status;
552   UINT32                  Result;
553   UINT32                  Timeout;
554 
555   ASSERT (Context);
556 
557   UsbBot = (USB_BOT_PROTOCOL *) Context;
558 
559   //
560   // Issue a class specific Bulk-Only Mass Storage get max lun reqest.
561   // according to section 3.2 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
562   //
563   Request.RequestType = 0xA1;
564   Request.Request     = USB_BOT_GETLUN_REQUEST;
565   Request.Value       = 0;
566   Request.Index       = UsbBot->Interface.InterfaceNumber;
567   Request.Length      = 1;
568   Timeout             = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;
569 
570   Status = UsbBot->UsbIo->UsbControlTransfer (
571                             UsbBot->UsbIo,
572                             &Request,
573                             EfiUsbDataIn,
574                             Timeout,
575                             (VOID *) MaxLun,
576                             1,
577                             &Result
578                             );
579 
580   return Status;
581 }
582 
583 /**
584   Clean up the resource used by this BOT protocol.
585 
586   @param  Context         The context of the BOT protocol, that is, USB_BOT_PROTOCOL.
587 
588   @retval EFI_SUCCESS     The resource is cleaned up.
589 
590 **/
591 EFI_STATUS
UsbBotCleanUp(IN VOID * Context)592 UsbBotCleanUp (
593   IN  VOID                    *Context
594   )
595 {
596   FreePool (Context);
597   return EFI_SUCCESS;
598 }
599 
600