/** @file | |
BOT Transportation implementation. | |
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "UsbBotPeim.h" | |
#include "BotPeim.h" | |
#include "PeiUsbLib.h" | |
/** | |
Reset the given usb device. | |
@param PeiServices The pointer of EFI_PEI_SERVICES. | |
@param PeiBotDev The instance to PEI_BOT_DEVICE. | |
@retval EFI_INVALID_PARAMETER Can not get usb io ppi. | |
@retval EFI_SUCCESS Failed to reset the given usb device. | |
**/ | |
EFI_STATUS | |
BotRecoveryReset ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_BOT_DEVICE *PeiBotDev | |
) | |
{ | |
EFI_USB_DEVICE_REQUEST DevReq; | |
UINT32 Timeout; | |
PEI_USB_IO_PPI *UsbIoPpi; | |
UINT8 EndpointAddr; | |
EFI_STATUS Status; | |
UsbIoPpi = PeiBotDev->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 = (PeiBotDev->BulkInEndpoint)->EndpointAddress; | |
PeiUsbClearEndpointHalt (PeiServices, UsbIoPpi, EndpointAddr); | |
// | |
// clear bulk out endpoint stall feature | |
// | |
EndpointAddr = (PeiBotDev->BulkOutEndpoint)->EndpointAddress; | |
PeiUsbClearEndpointHalt (PeiServices, UsbIoPpi, EndpointAddr); | |
return Status; | |
} | |
/** | |
Send the command to the device using Bulk-Out endpoint. | |
This function sends the command to the device using Bulk-Out endpoint. | |
BOT transfer is composed of three phases: Command, Data, and Status. | |
This is the Command phase. | |
@param PeiServices The pointer of EFI_PEI_SERVICES. | |
@param PeiBotDev The instance to PEI_BOT_DEVICE. | |
@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_DEVICE_ERROR Successful to send the command to device. | |
@retval EFI_SUCCESS Failed to send the command to device. | |
**/ | |
EFI_STATUS | |
BotCommandPhase ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_BOT_DEVICE *PeiBotDev, | |
IN VOID *Command, | |
IN UINT8 CommandSize, | |
IN UINT32 DataTransferLength, | |
IN EFI_USB_DATA_DIRECTION Direction, | |
IN UINT16 Timeout | |
) | |
{ | |
CBW Cbw; | |
EFI_STATUS Status; | |
PEI_USB_IO_PPI *UsbIoPpi; | |
UINTN DataSize; | |
UsbIoPpi = PeiBotDev->UsbIoPpi; | |
ZeroMem (&Cbw, sizeof (CBW)); | |
// | |
// Fill the command block, detailed see BOT spec | |
// | |
Cbw.Signature = CBWSIG; | |
Cbw.Tag = 0x01; | |
Cbw.DataTransferLength = DataTransferLength; | |
Cbw.Flags = (UINT8)((Direction == EfiUsbDataIn) ? 0x80 : 0); | |
Cbw.Lun = 0; | |
Cbw.CmdLen = CommandSize; | |
CopyMem (Cbw.CmdBlock, Command, CommandSize); | |
DataSize = sizeof (CBW); | |
Status = UsbIoPpi->UsbBulkTransfer ( | |
PeiServices, | |
UsbIoPpi, | |
(PeiBotDev->BulkOutEndpoint)->EndpointAddress, | |
(UINT8 *)&Cbw, | |
&DataSize, | |
Timeout | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// Command phase fail, we need to recovery reset this device | |
// | |
BotRecoveryReset (PeiServices, PeiBotDev); | |
return EFI_DEVICE_ERROR; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Transfer the data between the device and host. | |
This function transfers the data between the device and host. | |
BOT transfer is composed of three phases: Command, Data, and Status. | |
This is the Data phase. | |
@param PeiServices The pointer of EFI_PEI_SERVICES. | |
@param PeiBotDev The instance to PEI_BOT_DEVICE. | |
@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_DEVICE_ERROR Successful to send the data to device. | |
@retval EFI_SUCCESS Failed to send the data to device. | |
**/ | |
EFI_STATUS | |
BotDataPhase ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_BOT_DEVICE *PeiBotDev, | |
IN UINT32 *DataSize, | |
IN OUT VOID *DataBuffer, | |
IN EFI_USB_DATA_DIRECTION Direction, | |
IN UINT16 Timeout | |
) | |
{ | |
EFI_STATUS Status; | |
PEI_USB_IO_PPI *UsbIoPpi; | |
UINT8 EndpointAddr; | |
UINTN Remain; | |
UINTN Increment; | |
UINT32 MaxPacketLen; | |
UINT8 *BufferPtr; | |
UINTN TransferredSize; | |
UsbIoPpi = PeiBotDev->UsbIoPpi; | |
Remain = *DataSize; | |
BufferPtr = (UINT8 *)DataBuffer; | |
TransferredSize = 0; | |
// | |
// retrieve the max packet length of the given endpoint | |
// | |
if (Direction == EfiUsbDataIn) { | |
MaxPacketLen = (PeiBotDev->BulkInEndpoint)->MaxPacketSize; | |
EndpointAddr = (PeiBotDev->BulkInEndpoint)->EndpointAddress; | |
} else { | |
MaxPacketLen = (PeiBotDev->BulkOutEndpoint)->MaxPacketSize; | |
EndpointAddr = (PeiBotDev->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 (EFI_ERROR (Status)) { | |
PeiUsbClearEndpointHalt (PeiServices, UsbIoPpi, EndpointAddr); | |
return Status; | |
} | |
BufferPtr += Increment; | |
Remain -= Increment; | |
} | |
*DataSize = (UINT32)TransferredSize; | |
return EFI_SUCCESS; | |
} | |
/** | |
Get the command execution status from device. | |
This function gets the command execution status from device. | |
BOT transfer is composed of three phases: Command, Data, and Status. | |
This is the Status phase. | |
@param PeiServices The pointer of EFI_PEI_SERVICES. | |
@param PeiBotDev The instance to PEI_BOT_DEVICE. | |
@param TransferStatus The status of the transaction. | |
@param Timeout Indicates the maximum time, in millisecond, which the | |
transfer is allowed to complete. | |
@retval EFI_DEVICE_ERROR Successful to get the status of device. | |
@retval EFI_SUCCESS Failed to get the status of device. | |
**/ | |
EFI_STATUS | |
BotStatusPhase ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_BOT_DEVICE *PeiBotDev, | |
OUT UINT8 *TransferStatus, | |
IN UINT16 Timeout | |
) | |
{ | |
CSW Csw; | |
EFI_STATUS Status; | |
PEI_USB_IO_PPI *UsbIoPpi; | |
UINT8 EndpointAddr; | |
UINTN DataSize; | |
UsbIoPpi = PeiBotDev->UsbIoPpi; | |
ZeroMem (&Csw, sizeof (CSW)); | |
EndpointAddr = (PeiBotDev->BulkInEndpoint)->EndpointAddress; | |
DataSize = sizeof (CSW); | |
// | |
// Get the status field from bulk transfer | |
// | |
Status = UsbIoPpi->UsbBulkTransfer ( | |
PeiServices, | |
UsbIoPpi, | |
EndpointAddr, | |
&Csw, | |
&DataSize, | |
Timeout | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (Csw.Signature == CSWSIG) { | |
*TransferStatus = Csw.Status; | |
} else { | |
return EFI_DEVICE_ERROR; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Send ATAPI command using BOT protocol. | |
@param PeiServices The pointer of EFI_PEI_SERVICES. | |
@param PeiBotDev The instance to PEI_BOT_DEVICE. | |
@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_DEVICE_ERROR Successful to get the status of device. | |
@retval EFI_SUCCESS Failed to get the status of device. | |
**/ | |
EFI_STATUS | |
PeiAtapiCommand ( | |
IN EFI_PEI_SERVICES **PeiServices, | |
IN PEI_BOT_DEVICE *PeiBotDev, | |
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; | |
// | |
// First send ATAPI command through Bot | |
// | |
Status = BotCommandPhase ( | |
PeiServices, | |
PeiBotDev, | |
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: | |
BufferSize = BufferLength; | |
BotDataStatus = BotDataPhase ( | |
PeiServices, | |
PeiBotDev, | |
&BufferSize, | |
DataBuffer, | |
Direction, | |
TimeOutInMilliSeconds | |
); | |
break; | |
case EfiUsbNoData: | |
break; | |
} | |
// | |
// Status Phase | |
// | |
Status = BotStatusPhase ( | |
PeiServices, | |
PeiBotDev, | |
&TransferStatus, | |
TimeOutInMilliSeconds | |
); | |
if (EFI_ERROR (Status)) { | |
BotRecoveryReset (PeiServices, PeiBotDev); | |
return EFI_DEVICE_ERROR; | |
} | |
if (TransferStatus == 0x01) { | |
return EFI_DEVICE_ERROR; | |
} | |
return BotDataStatus; | |
} |