/*++

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:

    ScsiLib.c
    
Abstract: 
    

Revision History
--*/


#include <IndustryStandard/scsi.h>

EFI_STATUS
SubmitTestUnitReadyCommand (
  IN  EFI_SCSI_IO_PROTOCOL  *ScsiIo,
  IN  UINT64                Timeout,
  OUT VOID                  *SenseData,
  OUT UINT8                 *SenseDataLength,
  OUT UINT8                 *HostAdapterStatus,
  OUT UINT8                 *TargetStatus
  )
/*++

Routine Description:
  Function tests the ready status of SCSI unit.

Arguments:
  ScsiIo               - A pointer to SCSI IO protocol.
  Timeout              - The length of timeout period.
  SenseData            - A pointer to output sense data.
  SenseDataLength      - The length of output sense data.
  HostAdapterStatus    - The status of Host Adapter.
  TargetStatus         - The status of the target.

Returns:

  Returns:
    EFI_SUCCESS            - The status of the unit is tested successfully.
    EFI_WARN_BUFFER_TOO_SMALL  - The SCSI Request Packet was executed, 
                            but the entire DataBuffer could not be transferred.
                            The actual number of bytes transferred is returned
                            in InTransferLength.
    EFI_NOT_READY          - The SCSI Request Packet could not be sent because 
                            there are too many SCSI Command Packets already 
                            queued.
    EFI_DEVICE_ERROR      - A device error occurred while attempting to send 
                            the SCSI Request Packet.
    EFI_INVALID_PARAMETER  - The contents of CommandPacket are invalid.  
    EFI_UNSUPPORTED        - The command described by the SCSI Request Packet
                            is not supported by the SCSI initiator(i.e., SCSI 
                            Host Controller).
    EFI_TIMEOUT            - A timeout occurred while waiting for the SCSI 
                            Request Packet to execute.

--*/
{
  EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;
  UINT64                          Lun;
  UINT32                          Target;
  EFI_STATUS                      Status;
  UINT8                           Cdb[6];

  ZeroMem (&CommandPacket, sizeof (EFI_SCSI_IO_SCSI_REQUEST_PACKET));
  ZeroMem (Cdb, 6);

  CommandPacket.Timeout         = Timeout;
  CommandPacket.DataBuffer      = NULL;
  CommandPacket.SenseData       = SenseData;
  CommandPacket.TransferLength  = 0;
  CommandPacket.Cdb             = Cdb;

  //
  // Fill Cdb for Test Unit Ready Command
  //
  ScsiIo->GetDeviceLocation (ScsiIo, &Target, &Lun);

  Cdb[0]                        = EFI_SCSI_OP_TEST_UNIT_READY;
  Cdb[1]                        = (UINT8) (Lun & 0xe0);
  CommandPacket.CdbLength       = (UINT8) 6;
  CommandPacket.SenseDataLength = *SenseDataLength;

  Status                        = ScsiIo->ExecuteSCSICommand (ScsiIo, &CommandPacket, NULL);

  *HostAdapterStatus            = CommandPacket.HostAdapterStatus;
  *TargetStatus                 = CommandPacket.TargetStatus;
  *SenseDataLength              = CommandPacket.SenseDataLength;

  return Status;
}

EFI_STATUS
SubmitInquiryCommand (
  IN  EFI_SCSI_IO_PROTOCOL  *ScsiIo,
  IN  UINT64                Timeout,
  IN  VOID                  *SenseData,
  IN OUT UINT8              *SenseDataLength,
  OUT UINT8                 *HostAdapterStatus,
  OUT UINT8                 *TargetStatus,
  IN OUT VOID               *InquiryDataBuffer,
  IN OUT UINT32             *InquiryDataLength,
  IN  BOOLEAN               EnableVitalProductData
  )
/*++

Routine Description:
  Function to submit SCSI inquiry command.

Arguments:
  ScsiIo               - A pointer to SCSI IO protocol.
  Timeout              - The length of timeout period.
  SenseData            - A pointer to output sense data.
  SenseDataLength      - The length of output sense data.
  HostAdapterStatus    - The status of Host Adapter.
  TargetStatus         - The status of the target.
  InquiryDataBuffer    - A pointer to inquiry data buffer.
  InquiryDataLength    - The length of inquiry data buffer.
  EnableVitalProductData - Boolean to enable Vital Product Data.

Returns:

  Returns:
    EFI_SUCCESS            - The status of the unit is tested successfully.
    EFI_WARN_BUFFER_TOO_SMALL  - The SCSI Request Packet was executed, 
                            but the entire DataBuffer could not be transferred.
                            The actual number of bytes transferred is returned
                            in TransferLength.
    EFI_NOT_READY          - The SCSI Request Packet could not be sent because 
                            there are too many SCSI Command Packets already 
                            queued.
    EFI_DEVICE_ERROR      - A device error occurred while attempting to send 
                            the SCSI Request Packet.
    EFI_INVALID_PARAMETER  - The contents of CommandPacket are invalid.  
    EFI_UNSUPPORTED        - The command described by the SCSI Request Packet
                            is not supported by the SCSI initiator(i.e., SCSI 
                            Host Controller).
    EFI_TIMEOUT            - A timeout occurred while waiting for the SCSI 
                            Request Packet to execute.

--*/
{
  EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;
  UINT64                          Lun;
  UINT32                          Target;
  EFI_STATUS                      Status;
  UINT8                           Cdb[6];

  ZeroMem (&CommandPacket, sizeof (EFI_SCSI_IO_SCSI_REQUEST_PACKET));
  ZeroMem (Cdb, 6);

  CommandPacket.Timeout         = Timeout;
  CommandPacket.DataBuffer      = InquiryDataBuffer;
  CommandPacket.TransferLength  = *InquiryDataLength;
  CommandPacket.SenseData       = SenseData;
  CommandPacket.SenseDataLength = *SenseDataLength;
  CommandPacket.Cdb             = Cdb;

  ScsiIo->GetDeviceLocation (ScsiIo, &Target, &Lun);

  Cdb[0]  = EFI_SCSI_OP_INQUIRY;
  Cdb[1]  = (UINT8) (Lun & 0xe0);
  if (EnableVitalProductData) {
    Cdb[1] |= 0x01;
  }

  if (*InquiryDataLength > 0xff) {
    *InquiryDataLength = 0xff;
  }

  Cdb[4]                      = (UINT8) (*InquiryDataLength);
  CommandPacket.CdbLength     = (UINT8) 6;
  CommandPacket.DataDirection = EFI_SCSI_DATA_IN;

  Status                      = ScsiIo->ExecuteSCSICommand (ScsiIo, &CommandPacket, NULL);

  *HostAdapterStatus          = CommandPacket.HostAdapterStatus;
  *TargetStatus               = CommandPacket.TargetStatus;
  *SenseDataLength            = CommandPacket.SenseDataLength;
  *InquiryDataLength          = CommandPacket.TransferLength;

  return Status;
}

EFI_STATUS
SubmitModeSense10Command (
  IN  EFI_SCSI_IO_PROTOCOL    *ScsiIo,
  IN  UINT64                  Timeout,
  IN  VOID                    *SenseData,
  IN OUT UINT8                *SenseDataLength,
  OUT UINT8                   *HostAdapterStatus,
  OUT UINT8                   *TargetStatus,
  IN  VOID                    *DataBuffer,
  IN OUT UINT32               *DataLength,
  IN  UINT8                   DBDField, OPTIONAL
  IN  UINT8                   PageControl,
  IN  UINT8                   PageCode
  )
/*++

Routine Description:
  Function to submit SCSI mode sense 10 command.

Arguments:
  ScsiIo               - A pointer to SCSI IO protocol.
  Timeout              - The length of timeout period.
  SenseData            - A pointer to output sense data.
  SenseDataLength      - The length of output sense data.
  HostAdapterStatus    - The status of Host Adapter.
  TargetStatus         - The status of the target.
  DataBuffer           - A pointer to input data buffer.
  DataLength           - The length of input data buffer.
  DBDField             - The DBD Field (Optional).
  PageControl          - Page Control.
  PageCode             - Page code.

Returns:

  Returns:
    EFI_SUCCESS            - The status of the unit is tested successfully.
    EFI_WARN_BUFFER_TOO_SMALL  - The SCSI Request Packet was executed, 
                            but the entire DataBuffer could not be transferred.
                            The actual number of bytes transferred is returned
                            in TransferLength.
    EFI_NOT_READY          - The SCSI Request Packet could not be sent because 
                            there are too many SCSI Command Packets already 
                            queued.
    EFI_DEVICE_ERROR      - A device error occurred while attempting to send 
                            the SCSI Request Packet.
    EFI_INVALID_PARAMETER  - The contents of CommandPacket are invalid.  
    EFI_UNSUPPORTED        - The command described by the SCSI Request Packet
                            is not supported by the SCSI initiator(i.e., SCSI 
                            Host Controller).
    EFI_TIMEOUT            - A timeout occurred while waiting for the SCSI 
                            Request Packet to execute.

--*/
{
  EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;
  UINT64                          Lun;
  UINT32                          Target;
  EFI_STATUS                      Status;
  UINT8                           Cdb[10];

  ZeroMem (&CommandPacket, sizeof (EFI_SCSI_IO_SCSI_REQUEST_PACKET));
  ZeroMem (Cdb, 10);

  CommandPacket.Timeout         = Timeout;
  CommandPacket.DataBuffer      = DataBuffer;
  CommandPacket.SenseData       = SenseData;
  CommandPacket.TransferLength  = *DataLength;
  CommandPacket.Cdb             = Cdb;
  //
  // Fill Cdb for Mode Sense (10) Command
  //
  ScsiIo->GetDeviceLocation (ScsiIo, &Target, &Lun);

  Cdb[0]                        = EFI_SCSI_OP_MODE_SEN10;
  Cdb[1]                        = (UINT8) ((Lun & 0xe0) + ((DBDField << 3) & 0x08));
  Cdb[2]                        = (UINT8) ((PageControl & 0xc0) | (PageCode & 0x3f));
  Cdb[7]                        = (UINT8) (*DataLength >> 8);
  Cdb[8]                        = (UINT8) (*DataLength);

  CommandPacket.CdbLength       = 10;
  CommandPacket.DataDirection   = EFI_SCSI_DATA_IN;
  CommandPacket.SenseDataLength = *SenseDataLength;

  Status                        = ScsiIo->ExecuteSCSICommand (ScsiIo, &CommandPacket, NULL);

  *HostAdapterStatus            = CommandPacket.HostAdapterStatus;
  *TargetStatus                 = CommandPacket.TargetStatus;
  *SenseDataLength              = CommandPacket.SenseDataLength;
  *DataLength                   = CommandPacket.TransferLength;

  return Status;
}

EFI_STATUS
SubmitRequestSenseCommand (
  IN  EFI_SCSI_IO_PROTOCOL  *ScsiIo,
  IN  UINT64                Timeout,
  IN  VOID                  *SenseData,
  IN OUT UINT8              *SenseDataLength,
  OUT UINT8                 *HostAdapterStatus,
  OUT UINT8                 *TargetStatus
  )
/*++

Routine Description:
  Function to submit SCSI request sense command.

Arguments:
  ScsiIo               - A pointer to SCSI IO protocol.
  Timeout              - The length of timeout period.
  SenseData            - A pointer to output sense data.
  SenseDataLength      - The length of output sense data.
  HostAdapterStatus    - The status of Host Adapter.
  TargetStatus         - The status of the target.

Returns:

  Returns:
    EFI_SUCCESS            - The status of the unit is tested successfully.
    EFI_WARN_BUFFER_TOO_SMALL  - The SCSI Request Packet was executed, 
                            but the entire DataBuffer could not be transferred.
                            The actual number of bytes transferred is returned
                            in TransferLength.
    EFI_NOT_READY          - The SCSI Request Packet could not be sent because 
                            there are too many SCSI Command Packets already 
                            queued.
    EFI_DEVICE_ERROR      - A device error occurred while attempting to send 
                            the SCSI Request Packet.
    EFI_INVALID_PARAMETER  - The contents of CommandPacket are invalid.  
    EFI_UNSUPPORTED        - The command described by the SCSI Request Packet
                            is not supported by the SCSI initiator(i.e., SCSI 
                            Host Controller).
    EFI_TIMEOUT            - A timeout occurred while waiting for the SCSI 
                            Request Packet to execute.

--*/
{
  EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;
  UINT64                          Lun;
  UINT32                          Target;
  EFI_STATUS                      Status;
  UINT8                           Cdb[6];

  ZeroMem (&CommandPacket, sizeof (EFI_SCSI_IO_SCSI_REQUEST_PACKET));
  ZeroMem (Cdb, 6);

  CommandPacket.Timeout         = Timeout;
  CommandPacket.DataBuffer      = SenseData;
  CommandPacket.SenseData       = NULL;
  CommandPacket.TransferLength  = *SenseDataLength;
  CommandPacket.Cdb             = Cdb;
  //
  // Fill Cdb for Request Sense Command
  //
  ScsiIo->GetDeviceLocation (ScsiIo, &Target, &Lun);

  Cdb[0]                        = EFI_SCSI_OP_REQUEST_SENSE;
  Cdb[1]                        = (UINT8) (Lun & 0xe0);
  Cdb[4]                        = (UINT8) (*SenseDataLength);

  CommandPacket.CdbLength       = (UINT8) 6;
  CommandPacket.DataDirection   = EFI_SCSI_DATA_IN;
  CommandPacket.SenseDataLength = 0;

  Status                        = ScsiIo->ExecuteSCSICommand (ScsiIo, &CommandPacket, NULL);

  *HostAdapterStatus            = CommandPacket.HostAdapterStatus;
  *TargetStatus                 = CommandPacket.TargetStatus;
  *SenseDataLength              = (UINT8) CommandPacket.TransferLength;

  return Status;
}

EFI_STATUS
SubmitReadCapacityCommand (
  IN  EFI_SCSI_IO_PROTOCOL  *ScsiIo,
  IN  UINT64                Timeout,
  IN  VOID                  *SenseData,
  IN OUT UINT8              *SenseDataLength,
  OUT UINT8                 *HostAdapterStatus,
  OUT UINT8                 *TargetStatus,
  OUT VOID                  *DataBuffer,
  IN OUT UINT32             *DataLength,
  IN  BOOLEAN               PMI
  )
/*++

Routine Description:
  Function to submit read capacity command.

Arguments:
  ScsiIo               - A pointer to SCSI IO protocol.
  Timeout              - The length of timeout period.
  SenseData            - A pointer to output sense data.
  SenseDataLength      - The length of output sense data.
  HostAdapterStatus    - The status of Host Adapter.
  TargetStatus         - The status of the target.
  DataBuffer           - A pointer to a data buffer.
  DataLength           - The length of data buffer.
  PMI                  - Partial medium indicator.

Returns:

  Returns:
    EFI_SUCCESS            - The status of the unit is tested successfully.
    EFI_WARN_BUFFER_TOO_SMALL  - The SCSI Request Packet was executed, 
                            but the entire DataBuffer could not be transferred.
                            The actual number of bytes transferred is returned
                            in TransferLength.
    EFI_NOT_READY          - The SCSI Request Packet could not be sent because 
                            there are too many SCSI Command Packets already 
                            queued.
    EFI_DEVICE_ERROR      - A device error occurred while attempting to send 
                            the SCSI Request Packet.
    EFI_INVALID_PARAMETER  - The contents of CommandPacket are invalid.  
    EFI_UNSUPPORTED        - The command described by the SCSI Request Packet
                            is not supported by the SCSI initiator(i.e., SCSI 
                            Host Controller).
    EFI_TIMEOUT            - A timeout occurred while waiting for the SCSI 
                            Request Packet to execute.

--*/
{
  EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;
  UINT64                          Lun;
  UINT32                          Target;
  EFI_STATUS                      Status;
  UINT8                           Cdb[10];

  ZeroMem (&CommandPacket, sizeof (EFI_SCSI_IO_SCSI_REQUEST_PACKET));
  ZeroMem (Cdb, 10);

  CommandPacket.Timeout         = Timeout;
  CommandPacket.DataBuffer      = DataBuffer;
  CommandPacket.SenseData       = SenseData;
  CommandPacket.TransferLength  = *DataLength;
  CommandPacket.Cdb             = Cdb;
  //
  // Fill Cdb for Read Capacity Command
  //
  ScsiIo->GetDeviceLocation (ScsiIo, &Target, &Lun);

  Cdb[0]  = EFI_SCSI_OP_READ_CAPACITY;
  Cdb[1]  = (UINT8) (Lun & 0xe0);
  if (!PMI) {
    //
    // Partial medium indicator,if PMI is FALSE, the Cdb.2 ~ Cdb.5 MUST BE ZERO.
    //
    ZeroMem ((Cdb + 2), 4);
  } else {
    Cdb[8] |= 0x01;
  }

  CommandPacket.CdbLength       = 10;
  CommandPacket.DataDirection   = EFI_SCSI_DATA_IN;
  CommandPacket.SenseDataLength = *SenseDataLength;

  Status                        = ScsiIo->ExecuteSCSICommand (ScsiIo, &CommandPacket, NULL);

  *HostAdapterStatus            = CommandPacket.HostAdapterStatus;
  *TargetStatus                 = CommandPacket.TargetStatus;
  *SenseDataLength              = CommandPacket.SenseDataLength;
  *DataLength                   = CommandPacket.TransferLength;

  return Status;
}

EFI_STATUS
SubmitRead10Command (
  IN  EFI_SCSI_IO_PROTOCOL  *ScsiIo,
  IN  UINT64                Timeout,
  IN  VOID                  *SenseData,
  IN OUT UINT8              *SenseDataLength,
  OUT UINT8                 *HostAdapterStatus,
  OUT UINT8                 *TargetStatus,
  OUT VOID                  *DataBuffer,
  IN OUT UINT32             *DataLength,
  IN  UINT32                StartLba,
  IN  UINT32                SectorSize
  )
/*++

Routine Description:
  Function to submit read 10 command.

Arguments:
  ScsiIo               - A pointer to SCSI IO protocol.
  Timeout              - The length of timeout period.
  SenseData            - A pointer to output sense data.
  SenseDataLength      - The length of output sense data.
  HostAdapterStatus    - The status of Host Adapter.
  TargetStatus         - The status of the target.
  DataBuffer           - A pointer to a data buffer.
  DataLength           - The length of data buffer.
  StartLba             - The start address of LBA.
  SectorSize           - The sector size.

Returns:

  Returns:
    EFI_SUCCESS            - The status of the unit is tested successfully.
    EFI_WARN_BUFFER_TOO_SMALL  - The SCSI Request Packet was executed, 
                            but the entire DataBuffer could not be transferred.
                            The actual number of bytes transferred is returned
                            in TransferLength.
    EFI_NOT_READY          - The SCSI Request Packet could not be sent because 
                            there are too many SCSI Command Packets already 
                            queued.
    EFI_DEVICE_ERROR      - A device error occurred while attempting to send 
                            the SCSI Request Packet.
    EFI_INVALID_PARAMETER  - The contents of CommandPacket are invalid.  
    EFI_UNSUPPORTED        - The command described by the SCSI Request Packet
                            is not supported by the SCSI initiator(i.e., SCSI 
                            Host Controller).
    EFI_TIMEOUT            - A timeout occurred while waiting for the SCSI 
                            Request Packet to execute.

--*/
{
  EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;
  UINT64                          Lun;
  UINT32                          Target;
  EFI_STATUS                      Status;
  UINT8                           Cdb[10];

  ZeroMem (&CommandPacket, sizeof (EFI_SCSI_IO_SCSI_REQUEST_PACKET));
  ZeroMem (Cdb, 10);

  CommandPacket.Timeout         = Timeout;
  CommandPacket.DataBuffer      = DataBuffer;
  CommandPacket.SenseData       = SenseData;
  CommandPacket.TransferLength  = *DataLength;
  CommandPacket.Cdb             = Cdb;
  //
  // Fill Cdb for Read (10) Command
  //
  ScsiIo->GetDeviceLocation (ScsiIo, &Target, &Lun);

  Cdb[0]                        = EFI_SCSI_OP_READ10;
  Cdb[1]                        = (UINT8) (Lun & 0xe0);
  Cdb[2]                        = (UINT8) (StartLba >> 24);
  Cdb[3]                        = (UINT8) (StartLba >> 16);
  Cdb[4]                        = (UINT8) (StartLba >> 8);
  Cdb[5]                        = (UINT8) StartLba;
  Cdb[7]                        = (UINT8) (SectorSize >> 8);
  Cdb[8]                        = (UINT8) SectorSize;

  CommandPacket.CdbLength       = 10;
  CommandPacket.DataDirection   = EFI_SCSI_DATA_IN;
  CommandPacket.SenseDataLength = *SenseDataLength;

  Status                        = ScsiIo->ExecuteSCSICommand (ScsiIo, &CommandPacket, NULL);

  *HostAdapterStatus            = CommandPacket.HostAdapterStatus;
  *TargetStatus                 = CommandPacket.TargetStatus;
  *SenseDataLength              = CommandPacket.SenseDataLength;
  *DataLength                   = CommandPacket.TransferLength;

  return Status;
}

EFI_STATUS
SubmitWrite10Command (
  IN  EFI_SCSI_IO_PROTOCOL  *ScsiIo,
  IN  UINT64                Timeout,
  IN  VOID                  *SenseData,
  IN OUT UINT8              *SenseDataLength,
  OUT UINT8                 *HostAdapterStatus,
  OUT UINT8                 *TargetStatus,
  OUT VOID                  *DataBuffer,
  IN OUT UINT32             *DataLength,
  IN  UINT32                StartLba,
  IN  UINT32                SectorSize
  )
/*++

Routine Description:
  Function to submit SCSI write 10 command.

Arguments:
  ScsiIo               - A pointer to SCSI IO protocol.
  Timeout              - The length of timeout period.
  SenseData            - A pointer to output sense data.
  SenseDataLength      - The length of output sense data.
  HostAdapterStatus    - The status of Host Adapter.
  TargetStatus         - The status of the target.
  DataBuffer           - A pointer to a data buffer.
  DataLength           - The length of data buffer.
  StartLba             - The start address of LBA.
  SectorSize           - The sector size.

Returns:

  Returns:
    EFI_SUCCESS            - The status of the unit is tested successfully.
    EFI_WARN_BUFFER_TOO_SMALL  - The SCSI Request Packet was executed, 
                            but the entire DataBuffer could not be transferred.
                            The actual number of bytes transferred is returned
                            in InTransferLength.
    EFI_NOT_READY          - The SCSI Request Packet could not be sent because 
                            there are too many SCSI Command Packets already 
                            queued.
    EFI_DEVICE_ERROR      - A device error occurred while attempting to send 
                            the SCSI Request Packet.
    EFI_INVALID_PARAMETER  - The contents of CommandPacket are invalid.  
    EFI_UNSUPPORTED        - The command described by the SCSI Request Packet
                            is not supported by the SCSI initiator(i.e., SCSI 
                            Host Controller).
    EFI_TIMEOUT            - A timeout occurred while waiting for the SCSI 
                            Request Packet to execute.

--*/
{
  EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;
  UINT64                          Lun;
  UINT32                          Target;
  EFI_STATUS                      Status;
  UINT8                           Cdb[10];

  ZeroMem (&CommandPacket, sizeof (EFI_SCSI_IO_SCSI_REQUEST_PACKET));
  ZeroMem (Cdb, 10);

  CommandPacket.Timeout         = Timeout;
  CommandPacket.DataBuffer      = DataBuffer;
  CommandPacket.SenseData       = SenseData;
  CommandPacket.TransferLength  = *DataLength;
  CommandPacket.Cdb             = Cdb;
  //
  // Fill Cdb for Write (10) Command
  //
  ScsiIo->GetDeviceLocation (ScsiIo, &Target, &Lun);

  Cdb[0]                        = EFI_SCSI_OP_WRITE10;
  Cdb[1]                        = (UINT8) (Lun & 0xe0);
  Cdb[2]                        = (UINT8) (StartLba >> 24);
  Cdb[3]                        = (UINT8) (StartLba >> 16);
  Cdb[4]                        = (UINT8) (StartLba >> 8);
  Cdb[5]                        = (UINT8) StartLba;
  Cdb[7]                        = (UINT8) (SectorSize >> 8);
  Cdb[8]                        = (UINT8) SectorSize;

  CommandPacket.CdbLength       = 10;
  CommandPacket.DataDirection   = EFI_SCSI_DATA_OUT;
  CommandPacket.SenseDataLength = *SenseDataLength;

  Status                        = ScsiIo->ExecuteSCSICommand (ScsiIo, &CommandPacket, NULL);

  *HostAdapterStatus            = CommandPacket.HostAdapterStatus;
  *TargetStatus                 = CommandPacket.TargetStatus;
  *SenseDataLength              = CommandPacket.SenseDataLength;
  *DataLength                   = CommandPacket.TransferLength;

  return Status;
}
