/** @file BOT Transportation implementation. Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
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; }