/*++

Copyright (c) 2006, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

Module Name:

  BOT.c

Abstract:

--*/

#include "bot.h"

GLOBAL_REMOVE_IF_UNREFERENCED UINT32     gBOTDebugLevel  = EFI_D_INFO;
GLOBAL_REMOVE_IF_UNREFERENCED UINT32     gBOTErrorLevel  = EFI_D_INFO;

EFI_DRIVER_BINDING_PROTOCOL   gUsbBotDriverBinding = {
  BotDriverBindingSupported,
  BotDriverBindingStart,
  BotDriverBindingStop,
  0xa,
  NULL,
  NULL
};

//
// Bot Protocol
//
STATIC
EFI_STATUS
BotCommandPhase (
  IN  USB_BOT_DEVICE            *UsbBotDev,
  IN  VOID                      *Command,
  IN  UINT8                     CommandSize,
  IN  UINT32                    DataTransferLength,
  IN  EFI_USB_DATA_DIRECTION    Direction,
  IN  UINT16                    Timeout
  );

STATIC
EFI_STATUS
BotDataPhase (
  IN     USB_BOT_DEVICE            *UsbBotDev,
  IN     UINTN                     *DataSize,
  IN OUT VOID                      *DataBuffer,
  IN     EFI_USB_DATA_DIRECTION    Direction,
  IN     UINT16                    Timeout
  );

STATIC
EFI_STATUS
BotStatusPhase (
  IN  USB_BOT_DEVICE          *UsbBotDev,
  OUT UINT32                  *DataResidue, 
  IN  UINT16                  Timeout
  );
//
// USB Atapi protocol prototype
//
STATIC
EFI_STATUS
EFIAPI
BotAtapiCommand (
  IN  EFI_USB_ATAPI_PROTOCOL    *This,
  IN  VOID                      *Command,
  IN  UINT8                     CommandSize,
  IN  VOID                      *DataBuffer,
  IN  UINT32                    BufferLength,
  IN  EFI_USB_DATA_DIRECTION    Direction,
  IN  UINT16                    TimeOutInMilliSeconds
  );

STATIC
EFI_STATUS
EFIAPI
BotMassStorageReset (
  IN  EFI_USB_ATAPI_PROTOCOL    *This,
  IN  BOOLEAN                   ExtendedVerification
  );

STATIC
VOID
BotReportStatusCode (
  IN EFI_DEVICE_PATH_PROTOCOL  *DevicePath,
  IN EFI_STATUS_CODE_TYPE      CodeType,
  IN EFI_STATUS_CODE_VALUE     Value
  );

STATIC EFI_USB_ATAPI_PROTOCOL BotAtapiProtocol = {
  BotAtapiCommand,
  BotMassStorageReset,
  0
};

EFI_STATUS
EFIAPI
BotDriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
  IN EFI_HANDLE                     ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
  )
/*++

  Routine Description:
    Test to see if this driver supports ControllerHandle. Any ControllerHandle
    than contains a BlockIo and DiskIo protocol can be supported.

  Arguments:
    This                - Protocol instance pointer.
    ControllerHandle    - Handle of device to test
    RemainingDevicePath - Not used

  Returns:
    EFI_SUCCESS         - This driver supports this device
    EFI_ALREADY_STARTED - This driver is already running on this device
    other               - This driver does not support this device

--*/
{
  EFI_STATUS                    Status;
  EFI_USB_IO_PROTOCOL           *UsbIo;
  EFI_USB_INTERFACE_DESCRIPTOR  InterfaceDescriptor;

  //
  // Check if the Controller supports USB IO protocol
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiUsbIoProtocolGuid,
                  (VOID **) &UsbIo,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Get the Default interface descriptor, now we only
  // suppose interface 1
  //
  Status = UsbIo->UsbGetInterfaceDescriptor (
                    UsbIo,
                    &InterfaceDescriptor
                    );
  if (EFI_ERROR (Status)) {
    goto Exit;
  }
  //
  // Check if it is a BOT type Mass Storage Device
  //
  if ((InterfaceDescriptor.InterfaceClass    != MASS_STORAGE_CLASS) ||
      (InterfaceDescriptor.InterfaceProtocol != BOT)) {
    Status = EFI_UNSUPPORTED;
    goto Exit;
  }

Exit:
  gBS->CloseProtocol (
        ControllerHandle,
        &gEfiUsbIoProtocolGuid,
        This->DriverBindingHandle,
        ControllerHandle
        );
  return Status;
}

EFI_STATUS
EFIAPI
BotDriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL    *This,
  IN EFI_HANDLE                     ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath
  )
/*++

  Routine Description:
    Start this driver on ControllerHandle by opening a Block IO and Disk IO
    protocol, reading Device Path, and creating a child handle with a
    Disk IO and device path protocol.

  Arguments:
    This                - Protocol instance pointer.
    ControllerHandle    - Handle of device to bind driver to
    RemainingDevicePath - Not used

  Returns:
    EFI_SUCCESS         - This driver is added to DeviceHandle
    EFI_ALREADY_STARTED - This driver is already running on DeviceHandle
    EFI_OUT_OF_RESOURCES- Can't allocate the memory resource
    other               - This driver does not support this device

--*/
{
  USB_BOT_DEVICE                *UsbBotDev;
  UINT8                         Index;
  EFI_USB_ENDPOINT_DESCRIPTOR   *EndpointDescriptor;
  EFI_USB_INTERFACE_DESCRIPTOR  *InterfaceDescriptor;
  EFI_STATUS                    Status;
  EFI_USB_IO_PROTOCOL           *UsbIo;

  //
  // Check if the Controller supports USB IO protocol
  //
  UsbBotDev = NULL;

  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiUsbIoProtocolGuid,
                  (VOID **) &UsbIo,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  InterfaceDescriptor = AllocateZeroPool (sizeof (EFI_USB_INTERFACE_DESCRIPTOR));
  if (InterfaceDescriptor == NULL) {
    gBS->CloseProtocol (
          ControllerHandle,
          &gEfiUsbIoProtocolGuid,
          This->DriverBindingHandle,
          ControllerHandle
          );
    return EFI_OUT_OF_RESOURCES;
  }
  //
  // Get the controller interface descriptor,
  //
  Status = UsbIo->UsbGetInterfaceDescriptor (
                    UsbIo,
                    InterfaceDescriptor
                    );
  if (EFI_ERROR (Status)) {
    gBS->FreePool (InterfaceDescriptor);
    goto ErrorExit;
  }

  BotAtapiProtocol.CommandProtocol  = InterfaceDescriptor->InterfaceSubClass;

  UsbBotDev                         = AllocateZeroPool (sizeof (USB_BOT_DEVICE));
  if (UsbBotDev == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    gBS->FreePool (InterfaceDescriptor);
    goto ErrorExit;
  }

  UsbBotDev->Signature            = USB_BOT_DEVICE_SIGNATURE;
  UsbBotDev->UsbIo                = UsbIo;
  UsbBotDev->InterfaceDescriptor  = InterfaceDescriptor;
  CopyMem (&UsbBotDev->UsbAtapiProtocol, &BotAtapiProtocol, sizeof (BotAtapiProtocol));

  //
  // Get the Device Path Protocol on Controller's handle
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiDevicePathProtocolGuid,
                  (VOID **) &UsbBotDev->DevicePath,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );

  if (EFI_ERROR (Status)) {
    goto ErrorExit;
  }

  for (Index = 0; Index < InterfaceDescriptor->NumEndpoints; Index++) {
    EndpointDescriptor = AllocatePool (sizeof (EFI_USB_INTERFACE_DESCRIPTOR));
    if (EndpointDescriptor == NULL) {
      Status = EFI_OUT_OF_RESOURCES;
      goto ErrorExit;
    }

    UsbIo->UsbGetEndpointDescriptor (
            UsbIo,
            Index,
            EndpointDescriptor
            );

    //
    // We parse bulk endpoint
    //
    if ((EndpointDescriptor->Attributes & 0x03) == 0x02) {
      if ((EndpointDescriptor->EndpointAddress & 0x80) != 0) {
        UsbBotDev->BulkInEndpointDescriptor = EndpointDescriptor;
      } else {
        UsbBotDev->BulkOutEndpointDescriptor = EndpointDescriptor;
      }

      continue;
    }

    gBS->FreePool (EndpointDescriptor);
  }
  //
  // Double check we have these endpoint descriptors
  //
  if (!(UsbBotDev->BulkInEndpointDescriptor &&
        UsbBotDev->BulkOutEndpointDescriptor)) {
    Status = EFI_DEVICE_ERROR;
    goto ErrorExit;
  }
  //
  // After installing Usb-Atapi protocol onto this handle
  // it will be called by upper layer drivers such as Fat
  //
  BotReportStatusCode (
    UsbBotDev->DevicePath,
    EFI_PROGRESS_CODE,
    (EFI_PERIPHERAL_REMOVABLE_MEDIA | EFI_P_PC_ENABLE)
    );

  //
  // Install Usb-Atapi Protocol onto the handle
  //
  Status = gBS->InstallProtocolInterface (
                  &ControllerHandle,
                  &gEfiUsbAtapiProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &UsbBotDev->UsbAtapiProtocol
                  );

  if (EFI_ERROR (Status)) {
    goto ErrorExit;
  }

  UsbBotDev->ControllerNameTable = NULL;
  AddUnicodeString (
    "eng",
    gUsbBotComponentName.SupportedLanguages,
    &UsbBotDev->ControllerNameTable,
    (CHAR16 *) L"Usb Bot Mass Storage"
    );

  return EFI_SUCCESS;

ErrorExit:
  gBS->CloseProtocol (
        ControllerHandle,
        &gEfiUsbIoProtocolGuid,
        This->DriverBindingHandle,
        ControllerHandle
        );

  if (UsbBotDev != NULL) {
    if (UsbBotDev->InterfaceDescriptor != NULL) {
      gBS->FreePool (UsbBotDev->InterfaceDescriptor);
    }

    if (UsbBotDev->BulkInEndpointDescriptor != NULL) {
      gBS->FreePool (UsbBotDev->BulkInEndpointDescriptor);
    }

    if (UsbBotDev->BulkOutEndpointDescriptor != NULL) {
      gBS->FreePool (UsbBotDev->BulkOutEndpointDescriptor);
    }

    gBS->FreePool (UsbBotDev);
  }

  return Status;
}

EFI_STATUS
EFIAPI
BotDriverBindingStop (
  IN  EFI_DRIVER_BINDING_PROTOCOL    *This,
  IN  EFI_HANDLE                     ControllerHandle,
  IN  UINTN                          NumberOfChildren,
  IN  EFI_HANDLE                     *ChildHandleBuffer
  )
/*++

  Routine Description:
    Stop this driver on ControllerHandle. Support stoping any child handles
    created by this driver.

  Arguments:
    This              - Protocol instance pointer.
    ControllerHandle  - Handle of device to stop driver on
    NumberOfChildren  - Number of Children in the ChildHandleBuffer
    ChildHandleBuffer - List of handles for the children we need to stop.

  Returns:
    EFI_SUCCESS       - This driver is removed DeviceHandle
    EFI_UNSUPPORTED   - Can't open the gEfiUsbAtapiProtocolGuid protocl
    other             - This driver was not removed from this device

--*/
{
  EFI_STATUS              Status;
  EFI_USB_ATAPI_PROTOCOL  *BotAtapiProtocol;
  USB_BOT_DEVICE          *UsbBotDev;

  //
  // Get our context back.
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiUsbAtapiProtocolGuid,
                  (VOID **) &BotAtapiProtocol,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    return EFI_UNSUPPORTED;
  }

  UsbBotDev = USB_BOT_DEVICE_FROM_THIS (BotAtapiProtocol);

  //
  // After installing Usb-Atapi protocol onto this handle
  // it will be called by upper layer drivers such as Fat
  //

  BotReportStatusCode (
    UsbBotDev->DevicePath,
    EFI_PROGRESS_CODE,
    (EFI_PERIPHERAL_REMOVABLE_MEDIA | EFI_P_PC_DISABLE)
    );

  //
  // Uninstall protocol
  //
  Status = gBS->UninstallProtocolInterface (
                  ControllerHandle,
                  &gEfiUsbAtapiProtocolGuid,
                  &UsbBotDev->UsbAtapiProtocol
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = gBS->CloseProtocol (
                  ControllerHandle,
                  &gEfiUsbIoProtocolGuid,
                  This->DriverBindingHandle,
                  ControllerHandle
                  );
  //
  // Free all allocated resources
  //
  if (UsbBotDev->InterfaceDescriptor != NULL) {
    gBS->FreePool (UsbBotDev->InterfaceDescriptor);
  }

  if (UsbBotDev->BulkInEndpointDescriptor != NULL) {
    gBS->FreePool (UsbBotDev->BulkInEndpointDescriptor);
  }

  if (UsbBotDev->BulkOutEndpointDescriptor != NULL) {
    gBS->FreePool (UsbBotDev->BulkOutEndpointDescriptor);
  }

  if (UsbBotDev->ControllerNameTable) {
    FreeUnicodeStringTable (UsbBotDev->ControllerNameTable);
  }

  gBS->FreePool (UsbBotDev);

  return Status;
}



STATIC
EFI_STATUS
ClearBulkInPipe (
  IN  USB_BOT_DEVICE          *UsbBotDev
  )
{
 UINT32   Result;

 return UsbClearEndpointHalt (
          UsbBotDev->UsbIo,
          UsbBotDev->BulkInEndpointDescriptor->EndpointAddress,
          &Result
          );
}


STATIC
EFI_STATUS
ClearBulkOutPipe  (
  IN  USB_BOT_DEVICE          *UsbBotDev
  )
{
 UINT32   Result;
 return UsbClearEndpointHalt (
          UsbBotDev->UsbIo,
          UsbBotDev->BulkOutEndpointDescriptor->EndpointAddress,
          &Result
          );
}

STATIC
EFI_STATUS
BotRecoveryReset (
  IN  USB_BOT_DEVICE          *UsbBotDev
  )
/*++

Routine Description:

  Bot reset routine

Arguments:

  UsbBotDev - USB_BOT_DEVICE pointer

Returns:
  EFI_SUCCESS - Success the operation

--*/
{
  EFI_STATUS              Status;
  EFI_USB_DEVICE_REQUEST  Request;
  UINT32                  Result;
  BotReportStatusCode (
    UsbBotDev->DevicePath,
    EFI_PROGRESS_CODE,
    (EFI_PERIPHERAL_REMOVABLE_MEDIA | EFI_P_PC_RESET)
    );

  ZeroMem (&Request, sizeof (EFI_USB_DEVICE_REQUEST));

  //
  // See BOT specification
  //
  Request.RequestType = 0x21;
  Request.Request     = 0xFF;

  Status = UsbBotDev->UsbIo->UsbControlTransfer (
                               UsbBotDev->UsbIo,
                               &Request,
                               EfiUsbNoData,
                               TIMEOUT_VALUE,
                               NULL,
                               0,
                               &Result
                               );

  gBS->Stall (100 * 1000);

  ClearBulkInPipe (UsbBotDev);
  ClearBulkOutPipe (UsbBotDev);

  return Status;
}

//
// Bot Protocol Implementation
//
STATIC
EFI_STATUS
BotCommandPhase (
  IN  USB_BOT_DEVICE          *UsbBotDev,
  IN  VOID                    *Command,
  IN  UINT8                   CommandSize,
  IN  UINT32                  DataTransferLength,
  IN  EFI_USB_DATA_DIRECTION  Direction,
  IN  UINT16                  Timeout
  )
/*++

  Routine Description:
    Send ATAPI command through BOT interface.

  Parameters:
    UsbBotDev           -   USB_BOT_DEVICE
    Command             -   command packet
    CommandSize         -   Command size
    DataTransferLength  -   Data Transfer Length
    Direction           -   Data IN/OUT/NODATA
    Timeout             -   Time out value in milliseconds
  Return Values:
    EFI_SUCCESS
    Others

--*/
{
  CBW                 cbw;
  EFI_STATUS          Status;
  UINT32              Result;
  EFI_USB_IO_PROTOCOL *UsbIo;
  UINTN               DataSize;

  UsbIo = UsbBotDev->UsbIo;

  ZeroMem (&cbw, sizeof (CBW));

  //
  // Fill the command block, detailed see BOT spec
  //
  cbw.dCBWSignature           = CBWSIG;
  cbw.dCBWTag                 = 0x01;
  cbw.dCBWDataTransferLength  = DataTransferLength;
  cbw.bmCBWFlags              = (UINT8) ((Direction == EfiUsbDataIn) ? 0x80 : 0);
  cbw.bCBWCBLength            = CommandSize;

  CopyMem (cbw.CBWCB, Command, CommandSize);

  DataSize = sizeof (CBW);

  Status = UsbIo->UsbBulkTransfer (
                    UsbIo,
                    UsbBotDev->BulkOutEndpointDescriptor->EndpointAddress,
                    &cbw,
                    &DataSize,
                    Timeout,
                    &Result
                    );
  return Status;
}

STATIC
EFI_STATUS
BotDataPhase (
  IN      USB_BOT_DEVICE            *UsbBotDev,
  IN      UINTN                     *DataSize,
  IN  OUT VOID                      *DataBuffer,
  IN      EFI_USB_DATA_DIRECTION    Direction,
  IN      UINT16                    Timeout
  )
/*++

  Routine Description:
    Get/Send Data through BOT interface

  Parameters:
    UsbBotDev    -  USB_BOT_DEVICE pointer
    DataSize     -  Data size
    DataBuffer   -  Data buffer pointer
    Direction    -  IN/OUT/NODATA
    Timeout      -  Time out value in milliseconds
  Return Value:
    EFI_SUCCESS
    Others

--*/
{
  EFI_STATUS          Status;
  UINT32              Result;
  EFI_USB_IO_PROTOCOL *UsbIo;
  UINT8               EndpointAddr;
  UINT8               *BufferPtr;

  UsbIo           = UsbBotDev->UsbIo;
  BufferPtr       = (UINT8 *) DataBuffer;

  //
  // retrieve the the max packet length of the given endpoint
  //
  if (Direction == EfiUsbDataIn) {
    EndpointAddr  = UsbBotDev->BulkInEndpointDescriptor->EndpointAddress;
  } else {
    EndpointAddr  = UsbBotDev->BulkOutEndpointDescriptor->EndpointAddress;
  }

    Status = UsbIo->UsbBulkTransfer (
                      UsbIo,
                      EndpointAddr,
                      BufferPtr,
                      DataSize,
                      (UINT16)(Timeout),
                      &Result
                      );

    if (EFI_ERROR (Status)) {
      if ((Result & EFI_USB_ERR_STALL) == EFI_USB_ERR_STALL) {
        if (Direction == EfiUsbDataIn) {
          DEBUG((gBOTErrorLevel, "BOT: Data IN Stall, ClearBulkInPipe\n"));
          ClearBulkInPipe (UsbBotDev);
        } else {
          DEBUG((gBOTErrorLevel, "BOT: Data OUT Stall, ClearBulkInPipe\n"));
          ClearBulkOutPipe (UsbBotDev);
        }
      }
     // BotRecoveryReset (UsbBotDev);
   }


  return Status;
}

STATIC
EFI_STATUS
BotStatusPhase (
  IN  USB_BOT_DEVICE          *UsbBotDev,
  OUT UINT32                  *DataResidue,
  IN  UINT16                  Timeout
  )
/*++

  Routine Description:
    Get transfer status through BOT interface

  Parameters:
    UsbBotDev       -  USB_BOT_DEVICE pointer
    Timeout         -  Time out value in milliseconds
  Return Value:
    EFI_SUCCESS
    Others

--*/
{
  CSW                 csw;
  EFI_STATUS          Status;
  EFI_USB_IO_PROTOCOL *UsbIo;
  UINT8               EndpointAddr;
  UINTN               DataSize;
  UINT32              Result;
  UINT8               Index;

  UsbIo = UsbBotDev->UsbIo;
  EndpointAddr  = UsbBotDev->BulkInEndpointDescriptor->EndpointAddress;


  for (Index = 0; Index < 3; Index ++) {
    ZeroMem (&csw, sizeof (CSW));
    DataSize    = sizeof (CSW);
    Result      = 0;

    Status = UsbIo->UsbBulkTransfer (
                      UsbIo,
                      EndpointAddr,
                      &csw,
                      &DataSize,
                      Timeout,
                      &Result
                      );
    if (EFI_ERROR (Status)) {
      if ((Result & EFI_USB_ERR_STALL) == EFI_USB_ERR_STALL) {
        DEBUG((gBOTDebugLevel, "BOT: CSW Stall, ClearBulkInPipe\n"));
        ClearBulkInPipe (UsbBotDev);
        continue;
      }
    }

    if (csw.dCSWSignature == CSWSIG) {
      if (csw.bCSWStatus == 0 || csw.bCSWStatus  == 0x01) {
        if (DataResidue != NULL) {
          *DataResidue = csw.dCSWDataResidue;
        }
        if (csw.bCSWStatus  == 0x01) {
          return EFI_DEVICE_ERROR;
        }
        break;
      } else if (csw.bCSWStatus == 0x02) {
        DEBUG((gBOTErrorLevel, "BOT: Bot Phase error\n"));
        BotRecoveryReset (UsbBotDev);
      }

    }
  }

  if (Index == 3) {
    return EFI_DEVICE_ERROR;
  }

  return EFI_SUCCESS;
}

//
// Usb Atapi Protocol implementation
//
EFI_STATUS
EFIAPI
BotAtapiCommand (
  IN  EFI_USB_ATAPI_PROTOCOL      *This,
  IN  VOID                        *Command,
  IN  UINT8                       CommandSize,
  IN  VOID                        *DataBuffer,
  IN  UINT32                      BufferLength,
  IN  EFI_USB_DATA_DIRECTION      Direction,
  IN  UINT16                      TimeOutInMilliSeconds
  )
/*++

  Routine Description:
    Send ATAPI command using BOT protocol.

  Arguments:
    This                  - Protocol instance pointer.
    Command               - Command buffer
    CommandSize           - Size of Command Buffer
    DataBuffer            - Data buffer
    BufferLength          - Length of Data buffer
    Direction             - Data direction of this command
    TimeoutInMilliSeconds - Timeout value in ms

  Returns:
    EFI_SUCCESS         - Command succeeded.
    EFI_DEVICE_ERROR    - Command failed.

--*/
{
  EFI_STATUS      Status;
  EFI_STATUS      BotDataStatus;
  USB_BOT_DEVICE  *UsbBotDev;
  UINTN           BufferSize;
  UINT8           Index;
  UINT32          DataResidue;

  //
  // Get the context
  //
  UsbBotDev     = USB_BOT_DEVICE_FROM_THIS (This);
  BotDataStatus = EFI_SUCCESS;
  BufferSize    = 0;

  for (Index = 0; Index < 3; Index ++) {
    //
    // First send ATAPI command through Bot
    //
    Status = BotCommandPhase (
              UsbBotDev,
              Command,
              CommandSize,
              BufferLength,
              Direction,
              10 * 1000
              );

    if (EFI_ERROR (Status)) {
       DEBUG((gBOTErrorLevel, "BotCommandPhase Fail\n"));
       return Status;
    }
    //
    // Send/Get Data if there is a Data Stage
    //
    switch (Direction) {

    case EfiUsbDataIn:
    case EfiUsbDataOut:
      BufferSize = BufferLength;

      BotDataStatus = BotDataPhase (
                        UsbBotDev,
                        &BufferSize,
                        DataBuffer,
                        Direction,
                        (UINT16) (TimeOutInMilliSeconds)
                        );


      if (EFI_ERROR (BotDataStatus)) {
        DEBUG((gBOTErrorLevel, "BotDataPhase Fail\n"));
      }
      break;

    case EfiUsbNoData:
      break;
   }

    DataResidue = 0;
    //
    // Status Phase
    //
    Status = BotStatusPhase (
              UsbBotDev,
              &DataResidue,
              10 * 1000
              );

    if (EFI_ERROR (Status)) {
      DEBUG((gBOTErrorLevel, "BotStatusPhase Fail\n"));
      return Status;
    }

    if (!EFI_ERROR (BotDataStatus)) {
      break;
    }

  }
  return BotDataStatus;
}

EFI_STATUS
EFIAPI
BotMassStorageReset (
  IN  EFI_USB_ATAPI_PROTOCOL      *This,
  IN  BOOLEAN                     ExtendedVerification
  )
/*++

  Routine Description:
    Reset Bot Devices

  Arguments:
    This                    - Protocol instance pointer.
    ExtendedVerification    - TRUE if we need to do strictly reset.

  Returns:
    EFI_SUCCESS         - Command succeeded.
    EFI_DEVICE_ERROR    - Command failed.

--*/
{
  EFI_STATUS          Status;
  USB_BOT_DEVICE      *UsbBotDev;
  EFI_USB_IO_PROTOCOL *UsbIo;

  UsbBotDev = USB_BOT_DEVICE_FROM_THIS (This);
  UsbIo     = UsbBotDev->UsbIo;

  if (ExtendedVerification) {
    //
    // If we need to do strictly reset, reset its parent hub port
    //
    Status = UsbIo->UsbPortReset (UsbIo);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  Status = BotRecoveryReset (UsbBotDev);

  return Status;
}

STATIC
VOID
BotReportStatusCode (
  IN EFI_DEVICE_PATH_PROTOCOL  *DevicePath,
  IN EFI_STATUS_CODE_TYPE      CodeType,
  IN EFI_STATUS_CODE_VALUE     Value
  )
/*++

  Routine Description:
    Report Status Code in Usb Bot Driver

  Arguments:
    DevicePath - Use this to get Device Path
    CodeType   - Status Code Type
    CodeValue  - Status Code Value

  Returns:
    None

--*/
{
  REPORT_STATUS_CODE_WITH_DEVICE_PATH (
    CodeType,
    Value,
    DevicePath
    );
}
