Home | History | Annotate | Download | only in UsbMassStorageDxe
      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
     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
    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
    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
    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
    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
    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
    606 UsbCbiCleanUp (
    607   IN  VOID                   *Context
    608   )
    609 {
    610   FreePool (Context);
    611   return EFI_SUCCESS;
    612 }
    613