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