476 lines
14 KiB
C
476 lines
14 KiB
C
/** @file
|
|
BOT Transportation implementation
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2012, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
#include "UsbMassStoragePei.h"
|
|
|
|
#pragma pack(1)
|
|
//
|
|
// Bulk Only device protocol
|
|
//
|
|
typedef struct {
|
|
UINT32 dCBWSignature;
|
|
UINT32 dCBWTag;
|
|
UINT32 dCBWDataTransferLength;
|
|
UINT8 bmCBWFlags;
|
|
UINT8 bCBWLUN;
|
|
UINT8 bCBWCBLength;
|
|
UINT8 CBWCB[16];
|
|
} CBW;
|
|
|
|
typedef struct {
|
|
UINT32 dCSWSignature;
|
|
UINT32 dCSWTag;
|
|
UINT32 dCSWDataResidue;
|
|
UINT8 bCSWStatus;
|
|
} CSW;
|
|
|
|
#pragma pack()
|
|
//
|
|
// Status code, see Usb Bot device spec
|
|
//
|
|
#define CSWSIG 0x53425355
|
|
#define CBWSIG 0x43425355
|
|
|
|
/**
|
|
|
|
Reset the given usb device
|
|
|
|
@param PeiServices General purpose services that are available to every PEIM.
|
|
@param PeiBlkIoDev Indicates the PEI_BLKIO_DEVICE instance.
|
|
|
|
@retval EFI_SUCCESS Failed to reset the given usb device.
|
|
@retval EFI_INVALID_PARAMETER Can not get usb io ppi.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
BotRecoveryReset (
|
|
IN EFI_PEI_SERVICES **PeiServices,
|
|
IN PEI_BLKIO_DEVICE *PeiBlkIoDev
|
|
)
|
|
{
|
|
EFI_USB_DEVICE_REQUEST DevReq;
|
|
UINT32 Timeout;
|
|
PEI_USB3_IO_PPI *UsbIoPpi;
|
|
UINT8 EndpointAddr;
|
|
EFI_STATUS Status;
|
|
|
|
UsbIoPpi = PeiBlkIoDev->UsbIoPpi;
|
|
|
|
if (UsbIoPpi == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
ZeroMem (&DevReq, sizeof (EFI_USB_DEVICE_REQUEST));
|
|
|
|
DevReq.RequestType = 0x21;
|
|
DevReq.Request = 0xFF;
|
|
DevReq.Value = 0;
|
|
DevReq.Index = 0;
|
|
DevReq.Length = 0;
|
|
|
|
Timeout = 3000;
|
|
|
|
Status = UsbIoPpi->UsbControlTransfer (
|
|
PeiServices,
|
|
UsbIoPpi,
|
|
&DevReq,
|
|
EfiUsbNoData,
|
|
Timeout,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
//
|
|
// clear bulk in endpoint stall feature
|
|
//
|
|
EndpointAddr = (PeiBlkIoDev->BulkInEndpoint)->EndpointAddress;
|
|
UsbIoPpi->UsbClearEndpointHalt (PeiServices, UsbIoPpi, EndpointAddr);
|
|
|
|
//
|
|
// clear bulk out endpoint stall feature
|
|
//
|
|
EndpointAddr = (PeiBlkIoDev->BulkOutEndpoint)->EndpointAddress;
|
|
UsbIoPpi->UsbClearEndpointHalt (PeiServices, UsbIoPpi, EndpointAddr);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
Send the command to the device using Bulk-Out endpoint.
|
|
|
|
@param PeiServices General purpose services that are available to every PEIM.
|
|
@param PeiBlkIoDev Indicates the PEI_BLKIO_DEVICE instance.
|
|
@param Command The command to transfer to device.
|
|
@param CommandSize The length of the command.
|
|
@param DataTransferLength The expected length of the data.
|
|
@param Direction The direction of the data.
|
|
@param Timeout Indicates the maximum time, in millisecond, which the transfer is allowed to complete.
|
|
|
|
@retval EFI_SUCCESS Successful to send the command to device.
|
|
@retval EFI_DEVICE_ERROR Failed to send the command to device.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
BotCommandPhase (
|
|
IN EFI_PEI_SERVICES **PeiServices,
|
|
IN PEI_BLKIO_DEVICE *PeiBlkIoDev,
|
|
IN VOID *Command,
|
|
IN UINT8 CommandSize,
|
|
IN UINT32 DataTransferLength,
|
|
IN EFI_USB_DATA_DIRECTION Direction,
|
|
IN UINT16 Timeout
|
|
)
|
|
{
|
|
CBW cbw;
|
|
EFI_STATUS Status;
|
|
PEI_USB3_IO_PPI *UsbIoPpi;
|
|
UINTN DataSize;
|
|
|
|
UsbIoPpi = PeiBlkIoDev->UsbIoPpi;
|
|
|
|
ZeroMem (&cbw, sizeof (CBW));
|
|
|
|
//
|
|
// Fill the command block, detailed see BOT spec
|
|
//
|
|
cbw.dCBWSignature = CBWSIG;
|
|
cbw.dCBWTag = PeiBlkIoDev->CbwTag;
|
|
cbw.dCBWDataTransferLength = DataTransferLength;
|
|
cbw.bmCBWFlags = (UINT8) ((Direction == EfiUsbDataIn) ? 0x80 : 0);
|
|
cbw.bCBWLUN = 0;
|
|
cbw.bCBWCBLength = CommandSize;
|
|
|
|
CopyMem (cbw.CBWCB, Command, CommandSize);
|
|
|
|
DataSize = sizeof (CBW);
|
|
|
|
Status = UsbIoPpi->UsbBulkTransfer (
|
|
PeiServices,
|
|
UsbIoPpi,
|
|
(PeiBlkIoDev->BulkOutEndpoint)->EndpointAddress,
|
|
(UINT8 *) &cbw,
|
|
&DataSize,
|
|
Timeout
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Command phase fail, we need to recovery reset this device
|
|
//
|
|
BotRecoveryReset (PeiServices, PeiBlkIoDev);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Transfer the data between the device and host.
|
|
|
|
@param PeiServices General purpose services that are available to every PEIM.
|
|
@param PeiBlkIoDev Indicates the PEI_BLKIO_DEVICE instance.
|
|
@param DataSize The length of the data.
|
|
@param DataBuffer The pointer to the data.
|
|
@param Direction The direction of the data.
|
|
@param Timeout Indicates the maximum time, in millisecond, which the transfer is allowed to complete.
|
|
|
|
@retval EFI_SUCCESS Successful to send the data to device.
|
|
@retval EFI_DEVICE_ERROR Failed to send the data to device.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
BotDataPhase (
|
|
IN EFI_PEI_SERVICES **PeiServices,
|
|
IN PEI_BLKIO_DEVICE *PeiBlkIoDev,
|
|
IN UINT32 *DataSize,
|
|
IN OUT VOID *DataBuffer,
|
|
IN EFI_USB_DATA_DIRECTION Direction,
|
|
IN UINT16 Timeout
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
PEI_USB3_IO_PPI *UsbIoPpi;
|
|
UINT8 EndpointAddr;
|
|
UINTN Remain;
|
|
UINTN Increment;
|
|
UINT32 MaxPacketLen;
|
|
UINT8 *BufferPtr;
|
|
UINTN TransferredSize;
|
|
|
|
UsbIoPpi = PeiBlkIoDev->UsbIoPpi;
|
|
|
|
Remain = *DataSize;
|
|
BufferPtr = (UINT8 *) DataBuffer;
|
|
TransferredSize = 0;
|
|
|
|
//
|
|
// retrieve the the max packet length of the given endpoint
|
|
//
|
|
if (Direction == EfiUsbDataIn) {
|
|
MaxPacketLen = (PeiBlkIoDev->BulkInEndpoint)->MaxPacketSize;
|
|
EndpointAddr = (PeiBlkIoDev->BulkInEndpoint)->EndpointAddress;
|
|
} else {
|
|
MaxPacketLen = (PeiBlkIoDev->BulkOutEndpoint)->MaxPacketSize;
|
|
EndpointAddr = (PeiBlkIoDev->BulkOutEndpoint)->EndpointAddress;
|
|
}
|
|
|
|
while (Remain > 0) {
|
|
//
|
|
// Using 15 packets to avoid Bitstuff error
|
|
//
|
|
if (Remain > 16 * MaxPacketLen) {
|
|
Increment = 16 * MaxPacketLen;
|
|
} else {
|
|
Increment = Remain;
|
|
}
|
|
|
|
Status = UsbIoPpi->UsbBulkTransfer (
|
|
PeiServices,
|
|
UsbIoPpi,
|
|
EndpointAddr,
|
|
BufferPtr,
|
|
&Increment,
|
|
Timeout
|
|
);
|
|
|
|
TransferredSize += Increment;
|
|
|
|
if (Status == EFI_DEVICE_ERROR || (Status == EFI_TIMEOUT && PeiBlkIoDev->BypassCswTimeout)) {
|
|
UsbIoPpi->UsbClearEndpointHalt (PeiServices, UsbIoPpi, EndpointAddr);
|
|
if (PeiBlkIoDev->BypassCswTimeout) {
|
|
if (PeiBlkIoDev->BypassCswTimeout == 1) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
continue;
|
|
} else return Status;
|
|
}
|
|
|
|
BufferPtr += Increment;
|
|
Remain -= Increment;
|
|
}
|
|
|
|
*DataSize = (UINT32) TransferredSize;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Get the command execution status from device.
|
|
|
|
@param PeiServices General purpose services that are available to every PEIM.
|
|
@param PeiBlkIoDev Indicates the PEI_BLKIO_DEVICE instance.
|
|
@param TransferStatus The status of the transaction.
|
|
@param Timeout Indicates the maximum time, in millisecond, which the transfer is allowed to complete.
|
|
|
|
@retval EFI_SUCCESS Successful to get the status of device.
|
|
@retval EFI_DEVICE_ERROR Failed to get the status of device.
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
BotStatusPhase (
|
|
IN EFI_PEI_SERVICES **PeiServices,
|
|
IN PEI_BLKIO_DEVICE *PeiBlkIoDev,
|
|
OUT UINT8 *TransferStatus,
|
|
IN UINT16 Timeout
|
|
)
|
|
{
|
|
CSW csw;
|
|
EFI_STATUS Status;
|
|
PEI_USB3_IO_PPI *UsbIoPpi;
|
|
UINT8 EndpointAddr;
|
|
UINTN DataSize;
|
|
|
|
UsbIoPpi = PeiBlkIoDev->UsbIoPpi;
|
|
|
|
ZeroMem (&csw, sizeof (CSW));
|
|
|
|
EndpointAddr = (PeiBlkIoDev->BulkInEndpoint)->EndpointAddress;
|
|
|
|
Retry:
|
|
DataSize = sizeof (CSW);
|
|
|
|
//
|
|
// Get the status field from bulk transfer
|
|
//
|
|
Status = UsbIoPpi->UsbBulkTransfer (
|
|
PeiServices,
|
|
UsbIoPpi,
|
|
EndpointAddr,
|
|
&csw,
|
|
&DataSize,
|
|
Timeout
|
|
);
|
|
if (Status == EFI_DEVICE_ERROR || (Status == EFI_TIMEOUT && PeiBlkIoDev->BypassCswTimeout)) {
|
|
UsbIoPpi->UsbClearEndpointHalt (PeiServices, UsbIoPpi, EndpointAddr);
|
|
if (PeiBlkIoDev->BypassCswTimeout) {
|
|
if (PeiBlkIoDev->BypassCswTimeout == 1) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
goto Retry;
|
|
} else return Status;
|
|
}
|
|
//
|
|
// Following situations will be treated as valid CSW
|
|
// 1. Signature not equal to "USBS" but the tag equal to CBW's tag
|
|
// 2. Both of signature and tag equal to zero
|
|
//
|
|
if (csw.dCSWTag != PeiBlkIoDev->CbwTag && !(csw.dCSWSignature == 0 && csw.dCSWTag == 0)) {
|
|
return EFI_DEVICE_ERROR;
|
|
} else {
|
|
*TransferStatus = csw.bCSWStatus;
|
|
}
|
|
//
|
|
//The tag is increased even there is an error.
|
|
//
|
|
PeiBlkIoDev->CbwTag++;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Send ATAPI command using BOT protocol.
|
|
|
|
@param PeiServices General purpose services that are available to every PEIM.
|
|
@param PeiBlkIoDev Indicates the PEI_BLKIO_DEVICE instance.
|
|
@param Command The command to be sent to ATAPI device.
|
|
@param CommandSize The length of the data to be sent.
|
|
@param DataBuffer The pointer to the data.
|
|
@param BufferLength The length of the data.
|
|
@param Direction The direction of the data.
|
|
@param TimeOutInMilliSeconds Indicates the maximum time, in millisecond, which the transfer is allowed to complete.
|
|
|
|
@retval EFI_SUCCESS Command executed successfully.
|
|
@retval EFI_DEVICE_ERROR Some device errors happen.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
PeiBotAtapiCommand (
|
|
IN EFI_PEI_SERVICES **PeiServices,
|
|
IN PEI_BLKIO_DEVICE *PeiBlkIoDev,
|
|
IN VOID *Command,
|
|
IN UINT8 CommandSize,
|
|
IN VOID *DataBuffer,
|
|
IN UINT32 BufferLength,
|
|
IN EFI_USB_DATA_DIRECTION Direction,
|
|
IN UINT16 TimeOutInMilliSeconds
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STATUS BotDataStatus;
|
|
UINT8 TransferStatus;
|
|
UINT32 BufferSize;
|
|
|
|
BotDataStatus = EFI_SUCCESS;
|
|
TransferStatus = 0;
|
|
//
|
|
// First send ATAPI command through Bot
|
|
//
|
|
Status = BotCommandPhase (
|
|
PeiServices,
|
|
PeiBlkIoDev,
|
|
Command,
|
|
CommandSize,
|
|
BufferLength,
|
|
Direction,
|
|
TimeOutInMilliSeconds
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
//
|
|
// Send/Get Data if there is a Data Stage
|
|
//
|
|
switch (Direction) {
|
|
case EfiUsbDataIn:
|
|
case EfiUsbDataOut:
|
|
//
|
|
// Some devices needs extremely long stall on first Read10 command when
|
|
// the SATA "Standby Immediately" been set to the device
|
|
//
|
|
if (!PeiBlkIoDev->FirstReadPassed && *(UINT8*)Command == 0x28) {
|
|
if (TimeOutInMilliSeconds < USB_BOT_DATA_TIMEOUT) TimeOutInMilliSeconds = USB_BOT_DATA_TIMEOUT;
|
|
PeiBlkIoDev->FirstReadPassed = TRUE;
|
|
}
|
|
//
|
|
// Some ODD devices needs extremely long stall on first Read Capacity command
|
|
//
|
|
if (!PeiBlkIoDev->FirstReadCapPassed && *(UINT8*)Command == 0x25) {
|
|
if (TimeOutInMilliSeconds < USB_BOT_DATA_TIMEOUT) TimeOutInMilliSeconds = USB_BOT_DATA_TIMEOUT;
|
|
PeiBlkIoDev->FirstReadCapPassed = TRUE;
|
|
}
|
|
//
|
|
// Some devices needs extremely long stall on first RequestSense command when
|
|
// last command got Command Failed result
|
|
//
|
|
if (!PeiBlkIoDev->FirstRequestSensePassed && *(UINT8*)Command == 0x03) {
|
|
if (TimeOutInMilliSeconds < USB_BOT_DATA_TIMEOUT) TimeOutInMilliSeconds = USB_BOT_DATA_TIMEOUT;
|
|
PeiBlkIoDev->FirstRequestSensePassed = TRUE;
|
|
}
|
|
//
|
|
// Some devices needs extremely long stall on first Inquiry command
|
|
//
|
|
if (!PeiBlkIoDev->FirstInquiryPassed && *(UINT8*)Command == 0x12 && !PeiBlkIoDev->BypassCswTimeout) {
|
|
if (TimeOutInMilliSeconds < USB_BOT_DATA_TIMEOUT) TimeOutInMilliSeconds = USB_BOT_DATA_TIMEOUT;
|
|
PeiBlkIoDev->FirstInquiryPassed = TRUE;
|
|
}
|
|
|
|
BufferSize = BufferLength;
|
|
|
|
BotDataStatus = BotDataPhase (
|
|
PeiServices,
|
|
PeiBlkIoDev,
|
|
&BufferSize,
|
|
DataBuffer,
|
|
Direction,
|
|
TimeOutInMilliSeconds
|
|
);
|
|
break;
|
|
|
|
case EfiUsbNoData:
|
|
break;
|
|
}
|
|
//
|
|
// Some devices needs extremely long stall on first BOT command to waiting
|
|
// for firmware startup during cold boot
|
|
//
|
|
TimeOutInMilliSeconds = USB_BOT_CSW_TIMEOUT;
|
|
if (PeiBlkIoDev->CbwTag == 1 && !PeiBlkIoDev->BypassCswTimeout) {
|
|
TimeOutInMilliSeconds = USB_BOT_CSW_TIMEOUT * 2;
|
|
}
|
|
//
|
|
// Status Phase
|
|
//
|
|
Status = BotStatusPhase (
|
|
PeiServices,
|
|
PeiBlkIoDev,
|
|
&TransferStatus,
|
|
TimeOutInMilliSeconds
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
BotRecoveryReset (PeiServices, PeiBlkIoDev);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
if (TransferStatus == 0x01) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
return BotDataStatus;
|
|
}
|