/** @file Pei USB ATATPI command implementations. Copyright (c) 1999 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "UsbBotPeim.h" #include "BotPeim.h" #define MAXSENSEKEY 5 /** Sends out ATAPI Inquiry Packet Command to the specified device. This command will return INQUIRY data of the device. @param PeiServices The pointer of EFI_PEI_SERVICES. @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. @retval EFI_SUCCESS Inquiry command completes successfully. @retval EFI_DEVICE_ERROR Inquiry command failed. **/ EFI_STATUS PeiUsbInquiry ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_BOT_DEVICE *PeiBotDevice ) { ATAPI_PACKET_COMMAND Packet; EFI_STATUS Status; ATAPI_INQUIRY_DATA Idata; // // fill command packet // ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND)); ZeroMem (&Idata, sizeof (ATAPI_INQUIRY_DATA)); Packet.Inquiry.opcode = ATA_CMD_INQUIRY; Packet.Inquiry.page_code = 0; Packet.Inquiry.allocation_length = 36; // // Send scsi INQUIRY command packet. // According to SCSI Primary Commands-2 spec, host only needs to // retrieve the first 36 bytes for standard INQUIRY data. // Status = PeiAtapiCommand ( PeiServices, PeiBotDevice, &Packet, (UINT8) sizeof (ATAPI_PACKET_COMMAND), &Idata, 36, EfiUsbDataIn, 2000 ); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } if ((Idata.peripheral_type & 0x1f) == 0x05) { PeiBotDevice->DeviceType = USBCDROM; PeiBotDevice->Media.BlockSize = 0x800; PeiBotDevice->Media2.ReadOnly = TRUE; PeiBotDevice->Media2.RemovableMedia = TRUE; PeiBotDevice->Media2.BlockSize = 0x800; } else { PeiBotDevice->DeviceType = USBFLOPPY; PeiBotDevice->Media.BlockSize = 0x200; PeiBotDevice->Media2.ReadOnly = FALSE; PeiBotDevice->Media2.RemovableMedia = TRUE; PeiBotDevice->Media2.BlockSize = 0x200; } return EFI_SUCCESS; } /** Sends out ATAPI Test Unit Ready Packet Command to the specified device to find out whether device is accessible. @param PeiServices The pointer of EFI_PEI_SERVICES. @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. @retval EFI_SUCCESS TestUnit command executed successfully. @retval EFI_DEVICE_ERROR Device cannot be executed TestUnit command successfully. **/ EFI_STATUS PeiUsbTestUnitReady ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_BOT_DEVICE *PeiBotDevice ) { ATAPI_PACKET_COMMAND Packet; EFI_STATUS Status; // // fill command packet // ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND)); Packet.TestUnitReady.opcode = ATA_CMD_TEST_UNIT_READY; // // send command packet // Status = PeiAtapiCommand ( PeiServices, PeiBotDevice, &Packet, (UINT8) sizeof (ATAPI_PACKET_COMMAND), NULL, 0, EfiUsbNoData, 2000 ); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } return EFI_SUCCESS; } /** Sends out ATAPI Request Sense Packet Command to the specified device. @param PeiServices The pointer of EFI_PEI_SERVICES. @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. @param SenseCounts Length of sense buffer. @param SenseKeyBuffer Pointer to sense buffer. @retval EFI_SUCCESS Command executed successfully. @retval EFI_DEVICE_ERROR Some device errors happen. **/ EFI_STATUS PeiUsbRequestSense ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_BOT_DEVICE *PeiBotDevice, OUT UINTN *SenseCounts, IN UINT8 *SenseKeyBuffer ) { EFI_STATUS Status; ATAPI_PACKET_COMMAND Packet; UINT8 *Ptr; BOOLEAN SenseReq; ATAPI_REQUEST_SENSE_DATA *Sense; *SenseCounts = 0; // // fill command packet for Request Sense Packet Command // ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND)); Packet.RequestSence.opcode = ATA_CMD_REQUEST_SENSE; Packet.RequestSence.allocation_length = (UINT8) sizeof (ATAPI_REQUEST_SENSE_DATA); Ptr = SenseKeyBuffer; SenseReq = TRUE; // // request sense data from device continuously // until no sense data exists in the device. // while (SenseReq) { Sense = (ATAPI_REQUEST_SENSE_DATA *) Ptr; // // send out Request Sense Packet Command and get one Sense // data form device. // Status = PeiAtapiCommand ( PeiServices, PeiBotDevice, &Packet, (UINT8) sizeof (ATAPI_PACKET_COMMAND), (VOID *) Ptr, sizeof (ATAPI_REQUEST_SENSE_DATA), EfiUsbDataIn, 2000 ); // // failed to get Sense data // if (EFI_ERROR (Status)) { if (*SenseCounts == 0) { return EFI_DEVICE_ERROR; } else { return EFI_SUCCESS; } } if (Sense->sense_key != ATA_SK_NO_SENSE) { Ptr += sizeof (ATAPI_REQUEST_SENSE_DATA); // // Ptr is byte based pointer // (*SenseCounts)++; if (*SenseCounts == MAXSENSEKEY) { break; } } else { // // when no sense key, skip out the loop // SenseReq = FALSE; } } return EFI_SUCCESS; } /** Sends out ATAPI Read Capacity Packet Command to the specified device. This command will return the information regarding the capacity of the media in the device. @param PeiServices The pointer of EFI_PEI_SERVICES. @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. @retval EFI_SUCCESS Command executed successfully. @retval EFI_DEVICE_ERROR Some device errors happen. **/ EFI_STATUS PeiUsbReadCapacity ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_BOT_DEVICE *PeiBotDevice ) { EFI_STATUS Status; ATAPI_PACKET_COMMAND Packet; ATAPI_READ_CAPACITY_DATA Data; UINT32 LastBlock; ZeroMem (&Data, sizeof (ATAPI_READ_CAPACITY_DATA)); ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND)); Packet.Inquiry.opcode = ATA_CMD_READ_CAPACITY; // // send command packet // Status = PeiAtapiCommand ( PeiServices, PeiBotDevice, &Packet, (UINT8) sizeof (ATAPI_PACKET_COMMAND), (VOID *) &Data, sizeof (ATAPI_READ_CAPACITY_DATA), EfiUsbDataIn, 2000 ); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } LastBlock = ((UINT32) Data.LastLba3 << 24) | (Data.LastLba2 << 16) | (Data.LastLba1 << 8) | Data.LastLba0; if (LastBlock == 0xFFFFFFFF) { DEBUG ((EFI_D_INFO, "The usb device LBA count is larger than 0xFFFFFFFF!\n")); } PeiBotDevice->Media.LastBlock = LastBlock; PeiBotDevice->Media.MediaPresent = TRUE; PeiBotDevice->Media2.LastBlock = LastBlock; PeiBotDevice->Media2.MediaPresent = TRUE; return EFI_SUCCESS; } /** Sends out ATAPI Read Format Capacity Data Command to the specified device. This command will return the information regarding the capacity of the media in the device. @param PeiServices The pointer of EFI_PEI_SERVICES. @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. @retval EFI_SUCCESS Command executed successfully. @retval EFI_DEVICE_ERROR Some device errors happen. **/ EFI_STATUS PeiUsbReadFormattedCapacity ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_BOT_DEVICE *PeiBotDevice ) { EFI_STATUS Status; ATAPI_PACKET_COMMAND Packet; ATAPI_READ_FORMAT_CAPACITY_DATA FormatData; UINT32 LastBlock; ZeroMem (&FormatData, sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA)); ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND)); Packet.ReadFormatCapacity.opcode = ATA_CMD_READ_FORMAT_CAPACITY; Packet.ReadFormatCapacity.allocation_length_lo = 12; // // send command packet // Status = PeiAtapiCommand ( PeiServices, PeiBotDevice, &Packet, (UINT8) sizeof (ATAPI_PACKET_COMMAND), (VOID *) &FormatData, sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA), EfiUsbDataIn, 2000 ); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } if (FormatData.DesCode == 3) { // // Media is not present // PeiBotDevice->Media.MediaPresent = FALSE; PeiBotDevice->Media.LastBlock = 0; PeiBotDevice->Media2.MediaPresent = FALSE; PeiBotDevice->Media2.LastBlock = 0; } else { LastBlock = ((UINT32) FormatData.LastLba3 << 24) | (FormatData.LastLba2 << 16) | (FormatData.LastLba1 << 8) | FormatData.LastLba0; if (LastBlock == 0xFFFFFFFF) { DEBUG ((EFI_D_INFO, "The usb device LBA count is larger than 0xFFFFFFFF!\n")); } PeiBotDevice->Media.LastBlock = LastBlock; PeiBotDevice->Media.LastBlock--; PeiBotDevice->Media.MediaPresent = TRUE; PeiBotDevice->Media2.MediaPresent = TRUE; PeiBotDevice->Media2.LastBlock = PeiBotDevice->Media.LastBlock; } return EFI_SUCCESS; } /** Execute Read(10) ATAPI command on a specific SCSI target. Executes the ATAPI Read(10) command on the ATAPI target specified by PeiBotDevice. @param PeiServices The pointer of EFI_PEI_SERVICES. @param PeiBotDevice The pointer to PEI_BOT_DEVICE instance. @param Buffer The pointer to data buffer. @param Lba The start logic block address of reading. @param NumberOfBlocks The block number of reading. @retval EFI_SUCCESS Command executed successfully. @retval EFI_DEVICE_ERROR Some device errors happen. **/ EFI_STATUS PeiUsbRead10 ( IN EFI_PEI_SERVICES **PeiServices, IN PEI_BOT_DEVICE *PeiBotDevice, IN VOID *Buffer, IN EFI_PEI_LBA Lba, IN UINTN NumberOfBlocks ) { ATAPI_PACKET_COMMAND Packet; ATAPI_READ10_CMD *Read10Packet; UINT16 MaxBlock; UINT16 BlocksRemaining; UINT16 SectorCount; UINT32 Lba32; UINT32 BlockSize; UINT32 ByteCount; VOID *PtrBuffer; EFI_STATUS Status; UINT16 TimeOut; // // prepare command packet for the Inquiry Packet Command. // ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND)); Read10Packet = &Packet.Read10; Lba32 = (UINT32) Lba; PtrBuffer = Buffer; BlockSize = (UINT32) PeiBotDevice->Media.BlockSize; MaxBlock = (UINT16) (65535 / BlockSize); BlocksRemaining = (UINT16) NumberOfBlocks; Status = EFI_SUCCESS; while (BlocksRemaining > 0) { if (BlocksRemaining <= MaxBlock) { SectorCount = BlocksRemaining; } else { SectorCount = MaxBlock; } // // fill the Packet data structure // Read10Packet->opcode = ATA_CMD_READ_10; // // Lba0 ~ Lba3 specify the start logical block address of the data transfer. // Lba0 is MSB, Lba3 is LSB // Read10Packet->Lba3 = (UINT8) (Lba32 & 0xff); Read10Packet->Lba2 = (UINT8) (Lba32 >> 8); Read10Packet->Lba1 = (UINT8) (Lba32 >> 16); Read10Packet->Lba0 = (UINT8) (Lba32 >> 24); // // TranLen0 ~ TranLen1 specify the transfer length in block unit. // TranLen0 is MSB, TranLen is LSB // Read10Packet->TranLen1 = (UINT8) (SectorCount & 0xff); Read10Packet->TranLen0 = (UINT8) (SectorCount >> 8); ByteCount = SectorCount * BlockSize; TimeOut = (UINT16) (SectorCount * 2000); // // send command packet // Status = PeiAtapiCommand ( PeiServices, PeiBotDevice, &Packet, (UINT8) sizeof (ATAPI_PACKET_COMMAND), (VOID *) PtrBuffer, ByteCount, EfiUsbDataIn, TimeOut ); if (Status != EFI_SUCCESS) { return Status; } Lba32 += SectorCount; PtrBuffer = (UINT8 *) PtrBuffer + SectorCount * BlockSize; BlocksRemaining = (UINT16) (BlocksRemaining - SectorCount); } return Status; } /** Check if there is media according to sense data. @param SenseData Pointer to sense data. @param SenseCounts Count of sense data. @retval TRUE No media @retval FALSE Media exists **/ BOOLEAN IsNoMedia ( IN ATAPI_REQUEST_SENSE_DATA *SenseData, IN UINTN SenseCounts ) { ATAPI_REQUEST_SENSE_DATA *SensePtr; UINTN Index; BOOLEAN NoMedia; NoMedia = FALSE; SensePtr = SenseData; for (Index = 0; Index < SenseCounts; Index++) { switch (SensePtr->sense_key) { case ATA_SK_NOT_READY: switch (SensePtr->addnl_sense_code) { // // if no media, fill IdeDev parameter with specific info. // case ATA_ASC_NO_MEDIA: NoMedia = TRUE; break; default: break; } break; default: break; } SensePtr++; } return NoMedia; } /** Check if there is media error according to sense data. @param SenseData Pointer to sense data. @param SenseCounts Count of sense data. @retval TRUE Media error @retval FALSE No media error **/ BOOLEAN IsMediaError ( IN ATAPI_REQUEST_SENSE_DATA *SenseData, IN UINTN SenseCounts ) { ATAPI_REQUEST_SENSE_DATA *SensePtr; UINTN Index; BOOLEAN Error; SensePtr = SenseData; Error = FALSE; for (Index = 0; Index < SenseCounts; Index++) { switch (SensePtr->sense_key) { // // Medium error case // case ATA_SK_MEDIUM_ERROR: switch (SensePtr->addnl_sense_code) { case ATA_ASC_MEDIA_ERR1: // // fall through // case ATA_ASC_MEDIA_ERR2: // // fall through // case ATA_ASC_MEDIA_ERR3: // // fall through // case ATA_ASC_MEDIA_ERR4: Error = TRUE; break; default: break; } break; // // Medium upside-down case // case ATA_SK_NOT_READY: switch (SensePtr->addnl_sense_code) { case ATA_ASC_MEDIA_UPSIDE_DOWN: Error = TRUE; break; default: break; } break; default: break; } SensePtr++; } return Error; } /** Check if media is changed according to sense data. @param SenseData Pointer to sense data. @param SenseCounts Count of sense data. @retval TRUE There is media change event. @retval FALSE media is NOT changed. **/ BOOLEAN IsMediaChange ( IN ATAPI_REQUEST_SENSE_DATA *SenseData, IN UINTN SenseCounts ) { ATAPI_REQUEST_SENSE_DATA *SensePtr; UINTN Index; BOOLEAN MediaChange; MediaChange = FALSE; SensePtr = SenseData; for (Index = 0; Index < SenseCounts; Index++) { // // catch media change sense key and addition sense data // switch (SensePtr->sense_key) { case ATA_SK_UNIT_ATTENTION: switch (SensePtr->addnl_sense_code) { case ATA_ASC_MEDIA_CHANGE: MediaChange = TRUE; break; default: break; } break; default: break; } SensePtr++; } return MediaChange; }