alder_lake_bios/Insyde/InsydeModulePkg/Bus/Usb/UsbMassStoragePei/UsbBot.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;
}