Home | History | Annotate | Download | only in UsbMassStorageDxe
      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
     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
    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
    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
    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
    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
    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
    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
    592 UsbBotCleanUp (
    593   IN  VOID                    *Context
    594   )
    595 {
    596   FreePool (Context);
    597   return EFI_SUCCESS;
    598 }
    599 
    600