Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Eric Dong <eric.dong@intel.com> Reviewed-by: Feng Tian <feng.tian@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15598 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			2277 lines
		
	
	
		
			66 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2277 lines
		
	
	
		
			66 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| PEIM to produce gEfiPeiVirtualBlockIoPpiGuid PPI for ATA controllers in the platform.
 | |
| This PPI canl be consumed by PEIM which produce gEfiPeiDeviceRecoveryModulePpiGuid
 | |
| for Atapi CD ROM device.
 | |
| 
 | |
| Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
 | |
| 
 | |
| This program and the accompanying materials
 | |
| are licensed and made available under the terms and conditions
 | |
| of the BSD License which accompanies this distribution.  The
 | |
| full text of the license may be found at
 | |
| http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "AtapiPeim.h"
 | |
| 
 | |
| /**
 | |
|   Initializes the Atapi Block Io PPI.  
 | |
|   
 | |
|   @param[in]  FileHandle           Handle of the file being invoked.
 | |
|   @param[in]  PeiServices          Describes the list of possible PEI Services.
 | |
| 
 | |
|   @retval     EFI_SUCCESS          Operation performed successfully.
 | |
|   @retval     EFI_OUT_OF_RESOURCES Not enough memory to allocate.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AtapiPeimEntry (
 | |
|   IN EFI_PEI_FILE_HANDLE        FileHandle,
 | |
|   IN CONST EFI_PEI_SERVICES     **PeiServices
 | |
|   )
 | |
| {
 | |
|   PEI_ATA_CONTROLLER_PPI  *AtaControllerPpi;
 | |
|   EFI_STATUS              Status;
 | |
|   ATAPI_BLK_IO_DEV        *AtapiBlkIoDev;
 | |
| 
 | |
|   Status = PeiServicesRegisterForShadow (FileHandle);
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = PeiServicesLocatePpi (
 | |
|               &gPeiAtaControllerPpiGuid,
 | |
|               0,
 | |
|               NULL,
 | |
|               (VOID **) &AtaControllerPpi
 | |
|               );
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|   AtapiBlkIoDev = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (*AtapiBlkIoDev)));
 | |
|   if (AtapiBlkIoDev == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   AtapiBlkIoDev->Signature        = ATAPI_BLK_IO_DEV_SIGNATURE;
 | |
|   AtapiBlkIoDev->AtaControllerPpi = AtaControllerPpi;
 | |
| 
 | |
|   //
 | |
|   // atapi device enumeration and build private data
 | |
|   //
 | |
|   AtapiEnumerateDevices (AtapiBlkIoDev);
 | |
| 
 | |
|   AtapiBlkIoDev->AtapiBlkIo.GetNumberOfBlockDevices = AtapiGetNumberOfBlockDevices;
 | |
|   AtapiBlkIoDev->AtapiBlkIo.GetBlockDeviceMediaInfo = AtapiGetBlockDeviceMediaInfo;
 | |
|   AtapiBlkIoDev->AtapiBlkIo.ReadBlocks              = AtapiReadBlocks;
 | |
| 
 | |
|   AtapiBlkIoDev->PpiDescriptor.Flags                = (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST);
 | |
|   AtapiBlkIoDev->PpiDescriptor.Guid                 = &gEfiPeiVirtualBlockIoPpiGuid;
 | |
|   AtapiBlkIoDev->PpiDescriptor.Ppi                  = &AtapiBlkIoDev->AtapiBlkIo;
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "Atatpi Device Count is %d\n", AtapiBlkIoDev->DeviceCount));
 | |
|   if (AtapiBlkIoDev->DeviceCount != 0) {
 | |
|     Status = PeiServicesInstallPpi (&AtapiBlkIoDev->PpiDescriptor);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return EFI_OUT_OF_RESOURCES;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Gets the count of block I/O devices that one specific block driver detects.
 | |
| 
 | |
|   This function is used for getting the count of block I/O devices that one 
 | |
|   specific block driver detects.  To the PEI ATAPI driver, it returns the number
 | |
|   of all the detected ATAPI devices it detects during the enumeration process. 
 | |
|   To the PEI legacy floppy driver, it returns the number of all the legacy 
 | |
|   devices it finds during its enumeration process. If no device is detected, 
 | |
|   then the function will return zero.  
 | |
|   
 | |
|   @param[in]  PeiServices          General-purpose services that are available 
 | |
|                                    to every PEIM.
 | |
|   @param[in]  This                 Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI 
 | |
|                                    instance.
 | |
|   @param[out] NumberBlockDevices   The number of block I/O devices discovered.
 | |
| 
 | |
|   @retval     EFI_SUCCESS          Operation performed successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AtapiGetNumberOfBlockDevices (
 | |
|   IN   EFI_PEI_SERVICES                  **PeiServices,
 | |
|   IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI   *This,
 | |
|   OUT  UINTN                             *NumberBlockDevices
 | |
|   )
 | |
| {
 | |
|   ATAPI_BLK_IO_DEV  *AtapiBlkIoDev;
 | |
| 
 | |
|   AtapiBlkIoDev = NULL;
 | |
| 
 | |
|   AtapiBlkIoDev       = PEI_RECOVERY_ATAPI_FROM_BLKIO_THIS (This);
 | |
| 
 | |
|   *NumberBlockDevices = AtapiBlkIoDev->DeviceCount;
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Gets a block device's media information.
 | |
| 
 | |
|   This function will provide the caller with the specified block device's media 
 | |
|   information. If the media changes, calling this function will update the media 
 | |
|   information accordingly.
 | |
| 
 | |
|   @param[in]  PeiServices   General-purpose services that are available to every
 | |
|                             PEIM
 | |
|   @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
 | |
|   @param[in]  DeviceIndex   Specifies the block device to which the function wants 
 | |
|                             to talk. Because the driver that implements Block I/O 
 | |
|                             PPIs will manage multiple block devices, the PPIs that 
 | |
|                             want to talk to a single device must specify the 
 | |
|                             device index that was assigned during the enumeration
 | |
|                             process. This index is a number from one to 
 | |
|                             NumberBlockDevices.
 | |
|   @param[out] MediaInfo     The media information of the specified block media.  
 | |
|                             The caller is responsible for the ownership of this 
 | |
|                             data structure.
 | |
|   
 | |
|   @retval EFI_SUCCESS           Media information about the specified block device 
 | |
|                                 was obtained successfully.
 | |
|   @retval EFI_DEVICE_ERROR      Cannot get the media information due to a hardware 
 | |
|                                 error.
 | |
|   @retval Others                Other failure occurs.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AtapiGetBlockDeviceMediaInfo (
 | |
|   IN   EFI_PEI_SERVICES                     **PeiServices,
 | |
|   IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI        *This,
 | |
|   IN   UINTN                                DeviceIndex,
 | |
|   OUT  EFI_PEI_BLOCK_IO_MEDIA               *MediaInfo
 | |
|   )
 | |
| {
 | |
|   UINTN             DeviceCount;
 | |
|   ATAPI_BLK_IO_DEV  *AtapiBlkIoDev;
 | |
|   EFI_STATUS        Status;
 | |
|   UINTN             Index;
 | |
| 
 | |
|   AtapiBlkIoDev = NULL;
 | |
| 
 | |
|   if (This == NULL || MediaInfo == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   AtapiBlkIoDev = PEI_RECOVERY_ATAPI_FROM_BLKIO_THIS (This);
 | |
| 
 | |
|   DeviceCount   = AtapiBlkIoDev->DeviceCount;
 | |
| 
 | |
|   //
 | |
|   // DeviceIndex is a value from 1 to NumberBlockDevices.
 | |
|   //
 | |
|   if ((DeviceIndex < 1) || (DeviceIndex > DeviceCount) || (DeviceIndex > MAX_IDE_DEVICES)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Index = DeviceIndex - 1;
 | |
| 
 | |
|   //
 | |
|   // probe media and retrieve latest media information
 | |
|   //
 | |
|   DEBUG ((EFI_D_INFO, "Atatpi GetInfo DevicePosition is %d\n", AtapiBlkIoDev->DeviceInfo[Index].DevicePosition));  
 | |
|   DEBUG ((EFI_D_INFO, "Atatpi GetInfo DeviceType is   %d\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.DeviceType));
 | |
|   DEBUG ((EFI_D_INFO, "Atatpi GetInfo MediaPresent is %d\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.MediaPresent));
 | |
|   DEBUG ((EFI_D_INFO, "Atatpi GetInfo BlockSize is  0x%x\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.BlockSize));
 | |
|   DEBUG ((EFI_D_INFO, "Atatpi GetInfo LastBlock is  0x%x\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.LastBlock));
 | |
| 
 | |
|   Status = DetectMedia (
 | |
|              AtapiBlkIoDev,
 | |
|              AtapiBlkIoDev->DeviceInfo[Index].DevicePosition,
 | |
|              &AtapiBlkIoDev->DeviceInfo[Index].MediaInfo
 | |
|              );
 | |
|   if (Status != EFI_SUCCESS) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "Atatpi GetInfo DevicePosition is %d\n", AtapiBlkIoDev->DeviceInfo[Index].DevicePosition));
 | |
|   DEBUG ((EFI_D_INFO, "Atatpi GetInfo DeviceType is   %d\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.DeviceType));
 | |
|   DEBUG ((EFI_D_INFO, "Atatpi GetInfo MediaPresent is %d\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.MediaPresent));
 | |
|   DEBUG ((EFI_D_INFO, "Atatpi GetInfo BlockSize is  0x%x\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.BlockSize));
 | |
|   DEBUG ((EFI_D_INFO, "Atatpi GetInfo LastBlock is  0x%x\n", AtapiBlkIoDev->DeviceInfo[Index].MediaInfo.LastBlock));
 | |
|   
 | |
|   //
 | |
|   // Get media info from AtapiBlkIoDev
 | |
|   //
 | |
|   CopyMem (MediaInfo, &AtapiBlkIoDev->DeviceInfo[Index].MediaInfo, sizeof(EFI_PEI_BLOCK_IO_MEDIA));
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Reads the requested number of blocks from the specified block device.
 | |
| 
 | |
|   The function reads the requested number of blocks from the device. All the 
 | |
|   blocks are read, or an error is returned. If there is no media in the device,
 | |
|   the function returns EFI_NO_MEDIA.
 | |
| 
 | |
|   @param[in]  PeiServices   General-purpose services that are available to 
 | |
|                             every PEIM.
 | |
|   @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
 | |
|   @param[in]  DeviceIndex   Specifies the block device to which the function wants 
 | |
|                             to talk. Because the driver that implements Block I/O 
 | |
|                             PPIs will manage multiple block devices, the PPIs that 
 | |
|                             want to talk to a single device must specify the device 
 | |
|                             index that was assigned during the enumeration process. 
 | |
|                             This index is a number from one to NumberBlockDevices.
 | |
|   @param[in]  StartLBA      The starting logical block address (LBA) to read from
 | |
|                             on the device
 | |
|   @param[in]  BufferSize    The size of the Buffer in bytes. This number must be
 | |
|                             a multiple of the intrinsic block size of the device.
 | |
|   @param[out] Buffer        A pointer to the destination buffer for the data.
 | |
|                             The caller is responsible for the ownership of the 
 | |
|                             buffer.
 | |
|                          
 | |
|   @retval EFI_SUCCESS             The data was read correctly from the device.
 | |
|   @retval EFI_DEVICE_ERROR        The device reported an error while attempting 
 | |
|                                   to perform the read operation.
 | |
|   @retval EFI_INVALID_PARAMETER   The read request contains LBAs that are not 
 | |
|                                   valid, or the buffer is not properly aligned.
 | |
|   @retval EFI_NO_MEDIA            There is no media in the device.
 | |
|   @retval EFI_BAD_BUFFER_SIZE     The BufferSize parameter is not a multiple of
 | |
|                                   the intrinsic block size of the device.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| AtapiReadBlocks (
 | |
|   IN   EFI_PEI_SERVICES                  **PeiServices,
 | |
|   IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI     *This,
 | |
|   IN   UINTN                             DeviceIndex,
 | |
|   IN   EFI_PEI_LBA                       StartLBA,
 | |
|   IN   UINTN                             BufferSize,
 | |
|   OUT  VOID                              *Buffer
 | |
|   )
 | |
| {
 | |
| 
 | |
|   EFI_PEI_BLOCK_IO_MEDIA  MediaInfo;
 | |
|   EFI_STATUS          Status;
 | |
|   UINTN               NumberOfBlocks;
 | |
|   UINTN               BlockSize;
 | |
|   ATAPI_BLK_IO_DEV    *AtapiBlkIoDev;
 | |
| 
 | |
|   AtapiBlkIoDev = NULL;
 | |
| 
 | |
|   if (This == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   AtapiBlkIoDev = PEI_RECOVERY_ATAPI_FROM_BLKIO_THIS (This);
 | |
| 
 | |
|   if (Buffer == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (BufferSize == 0) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   Status = AtapiGetBlockDeviceMediaInfo (
 | |
|             PeiServices,
 | |
|             This,
 | |
|             DeviceIndex,
 | |
|             &MediaInfo
 | |
|             );
 | |
|   if (Status != EFI_SUCCESS) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (!MediaInfo.MediaPresent) {
 | |
|     return EFI_NO_MEDIA;
 | |
|   }
 | |
| 
 | |
|   BlockSize = MediaInfo.BlockSize;
 | |
| 
 | |
|   if (BufferSize % BlockSize != 0) {
 | |
|     return EFI_BAD_BUFFER_SIZE;
 | |
|   }
 | |
| 
 | |
|   NumberOfBlocks = BufferSize / BlockSize;
 | |
| 
 | |
|   if ((StartLBA + NumberOfBlocks - 1) > AtapiBlkIoDev->DeviceInfo[DeviceIndex - 1].MediaInfo.LastBlock) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   Status = ReadSectors (
 | |
|             AtapiBlkIoDev,
 | |
|             AtapiBlkIoDev->DeviceInfo[DeviceIndex - 1].DevicePosition,
 | |
|             Buffer,
 | |
|             StartLBA,
 | |
|             NumberOfBlocks,
 | |
|             BlockSize
 | |
|             );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enumerate Atapi devices.
 | |
| 
 | |
|   This function is used to enumerate Atatpi device in Ide channel.
 | |
| 
 | |
|   @param[in]  AtapiBlkIoDev  A pointer to atapi block IO device
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| AtapiEnumerateDevices (
 | |
|   IN  ATAPI_BLK_IO_DEV  *AtapiBlkIoDev
 | |
|   )
 | |
| {
 | |
|   UINT8               Index1;
 | |
|   UINT8               Index2;
 | |
|   UINTN               DevicePosition;
 | |
|   EFI_PEI_BLOCK_IO_MEDIA  MediaInfo;
 | |
|   EFI_STATUS          Status;
 | |
|   UINTN               DeviceCount;
 | |
|   UINT16              CommandBlockBaseAddr;
 | |
|   UINT16              ControlBlockBaseAddr;
 | |
|   UINT32              IdeEnabledNumber;
 | |
|   IDE_REGS_BASE_ADDR  IdeRegsBaseAddr[MAX_IDE_CHANNELS];
 | |
| 
 | |
|   DeviceCount = 0;
 | |
|   DevicePosition = 0;
 | |
| 
 | |
|   //
 | |
|   // Scan IDE bus for ATAPI devices
 | |
|   //
 | |
| 
 | |
|   //
 | |
|   // Enable Sata and IDE controller.
 | |
|   //
 | |
|   AtapiBlkIoDev->AtaControllerPpi->EnableAtaChannel (
 | |
|                                   (EFI_PEI_SERVICES **) GetPeiServicesTablePointer(),
 | |
|                                   AtapiBlkIoDev->AtaControllerPpi,
 | |
|                                   PEI_ICH_IDE_PRIMARY | PEI_ICH_IDE_SECONDARY
 | |
|                                   );
 | |
| 
 | |
|   //
 | |
|   // Allow SATA Devices to spin-up. This is needed if 
 | |
|   // SEC and PEI phase is too short, for example Release Build.
 | |
|   //
 | |
|   DEBUG ((EFI_D_INFO, "Delay for %d seconds for SATA devices to spin-up\n", PcdGet16 (PcdSataSpinUpDelayInSecForRecoveryPath)));
 | |
|   MicroSecondDelay (PcdGet16 (PcdSataSpinUpDelayInSecForRecoveryPath) * 1000 * 1000); //
 | |
| 
 | |
|   //
 | |
|   // Get four channels (primary or secondary Pata, Sata Channel) Command and Control Regs Base address.
 | |
|   //
 | |
|   IdeEnabledNumber = AtapiBlkIoDev->AtaControllerPpi->GetIdeRegsBaseAddr (
 | |
|                                                       (EFI_PEI_SERVICES **) GetPeiServicesTablePointer(),
 | |
|                                                       AtapiBlkIoDev->AtaControllerPpi,
 | |
|                                                       IdeRegsBaseAddr
 | |
|                                                       );
 | |
| 
 | |
|   //
 | |
|   // Using Command and Control Regs Base Address to fill other registers.
 | |
|   //
 | |
|   for (Index1 = 0; Index1 < IdeEnabledNumber; Index1 ++) { 
 | |
|     CommandBlockBaseAddr               = IdeRegsBaseAddr[Index1].CommandBlockBaseAddr;
 | |
|     AtapiBlkIoDev->IdeIoPortReg[Index1].Data         = CommandBlockBaseAddr;
 | |
|     AtapiBlkIoDev->IdeIoPortReg[Index1].Reg1.Feature = (UINT16) (CommandBlockBaseAddr + 0x1);
 | |
|     AtapiBlkIoDev->IdeIoPortReg[Index1].SectorCount  = (UINT16) (CommandBlockBaseAddr + 0x2);
 | |
|     AtapiBlkIoDev->IdeIoPortReg[Index1].SectorNumber = (UINT16) (CommandBlockBaseAddr + 0x3);
 | |
|     AtapiBlkIoDev->IdeIoPortReg[Index1].CylinderLsb  = (UINT16) (CommandBlockBaseAddr + 0x4);
 | |
|     AtapiBlkIoDev->IdeIoPortReg[Index1].CylinderMsb  = (UINT16) (CommandBlockBaseAddr + 0x5);
 | |
|     AtapiBlkIoDev->IdeIoPortReg[Index1].Head         = (UINT16) (CommandBlockBaseAddr + 0x6);
 | |
|     AtapiBlkIoDev->IdeIoPortReg[Index1].Reg.Command  = (UINT16) (CommandBlockBaseAddr + 0x7);
 | |
| 
 | |
|     ControlBlockBaseAddr                = IdeRegsBaseAddr[Index1].ControlBlockBaseAddr;
 | |
|     AtapiBlkIoDev->IdeIoPortReg[Index1].Alt.DeviceControl = ControlBlockBaseAddr;
 | |
|     AtapiBlkIoDev->IdeIoPortReg[Index1].DriveAddress      = (UINT16) (ControlBlockBaseAddr + 0x1);
 | |
|     
 | |
|     //
 | |
|     // Scan IDE bus for ATAPI devices IDE or Sata device
 | |
|     //
 | |
|     for (Index2 = IdeMaster; Index2 < IdeMaxDevice; Index2++) {
 | |
|       //
 | |
|       // Pata & Sata, Primary & Secondary channel, Master & Slave device
 | |
|       //
 | |
|       DevicePosition = (UINTN) (Index1 * 2 + Index2);
 | |
| 
 | |
|       if (DiscoverAtapiDevice (AtapiBlkIoDev, DevicePosition, &MediaInfo)) {
 | |
|         //
 | |
|         // ATAPI Device at DevicePosition is found.
 | |
|         //
 | |
|         AtapiBlkIoDev->DeviceInfo[DeviceCount].DevicePosition = DevicePosition;
 | |
|         //
 | |
|         // Retrieve Media Info
 | |
|         //
 | |
|         Status  = DetectMedia (AtapiBlkIoDev, DevicePosition, &MediaInfo);
 | |
|         CopyMem (&(AtapiBlkIoDev->DeviceInfo[DeviceCount].MediaInfo), &MediaInfo, sizeof (MediaInfo));
 | |
|        
 | |
|         DEBUG ((EFI_D_INFO, "Atatpi Device Position is %d\n", DevicePosition));
 | |
|         DEBUG ((EFI_D_INFO, "Atatpi DeviceType is   %d\n", MediaInfo.DeviceType));
 | |
|         DEBUG ((EFI_D_INFO, "Atatpi MediaPresent is %d\n", MediaInfo.MediaPresent));
 | |
|         DEBUG ((EFI_D_INFO, "Atatpi BlockSize is  0x%x\n", MediaInfo.BlockSize));
 | |
| 
 | |
|         if (EFI_ERROR (Status)) {
 | |
|           AtapiBlkIoDev->DeviceInfo[DeviceCount].MediaInfo.MediaPresent = FALSE;
 | |
|           AtapiBlkIoDev->DeviceInfo[DeviceCount].MediaInfo.LastBlock    = 0;
 | |
|         }
 | |
|         DeviceCount += 1;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   AtapiBlkIoDev->DeviceCount = DeviceCount;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Detect Atapi devices.
 | |
|   
 | |
|   @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.
 | |
|   @param[in]  DevicePosition  An integer to signify device position.
 | |
|   @param[out] MediaInfo       The media information of the specified block media.
 | |
| 
 | |
|   @retval TRUE                Atapi device exists in specified position.
 | |
|   @retval FALSE               Atapi device does not exist in specified position.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| DiscoverAtapiDevice (
 | |
|   IN  ATAPI_BLK_IO_DEV              *AtapiBlkIoDev,
 | |
|   IN  UINTN                         DevicePosition,
 | |
|   OUT EFI_PEI_BLOCK_IO_MEDIA        *MediaInfo
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   if (!DetectIDEController (AtapiBlkIoDev, DevicePosition)) {
 | |
|     return FALSE;
 | |
|   }
 | |
|   //
 | |
|   // test if it is an ATAPI device (only supported device)
 | |
|   //
 | |
|   if (ATAPIIdentify (AtapiBlkIoDev, DevicePosition) == EFI_SUCCESS) {
 | |
| 
 | |
|     Status = Inquiry (AtapiBlkIoDev, DevicePosition, MediaInfo);
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       return TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check power mode of Atapi devices.
 | |
|   
 | |
|   @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.
 | |
|   @param[in]  DevicePosition  An integer to signify device position.
 | |
|   @param[in]  AtaCommand      The Ata Command passed in.
 | |
| 
 | |
|   @retval EFI_SUCCESS         The Atapi device support power mode.
 | |
|   @retval EFI_NOT_FOUND       The Atapi device not found.
 | |
|   @retval EFI_TIMEOUT         Atapi command transaction is time out.
 | |
|   @retval EFI_ABORTED         Atapi command abort.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| CheckPowerMode (
 | |
|   IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,
 | |
|   IN  UINTN               DevicePosition,
 | |
|   IN  UINT8               AtaCommand
 | |
|   )
 | |
| {
 | |
|   UINT8       Channel;
 | |
|   UINT8       Device;
 | |
|   UINT16      StatusRegister;
 | |
|   UINT16      HeadRegister;
 | |
|   UINT16      CommandRegister;
 | |
|   UINT16      ErrorRegister;
 | |
|   UINT16      SectorCountRegister;
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       StatusValue;
 | |
|   UINT8       ErrorValue;
 | |
|   UINT8       SectorCountValue;
 | |
| 
 | |
|   Channel             = (UINT8) (DevicePosition / 2);
 | |
|   Device              = (UINT8) (DevicePosition % 2);
 | |
| 
 | |
|   ASSERT (Channel < MAX_IDE_CHANNELS);
 | |
| 
 | |
|   StatusRegister      = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Status;
 | |
|   HeadRegister        = AtapiBlkIoDev->IdeIoPortReg[Channel].Head;
 | |
|   CommandRegister     = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Command;
 | |
|   ErrorRegister       = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg1.Error;
 | |
|   SectorCountRegister = AtapiBlkIoDev->IdeIoPortReg[Channel].SectorCount;
 | |
| 
 | |
|   //
 | |
|   // select device
 | |
|   //
 | |
|   IoWrite8 (HeadRegister, (UINT8) ((Device << 4) | 0xe0));
 | |
| 
 | |
|   //
 | |
|   // refresh the SectorCount register
 | |
|   //
 | |
|   SectorCountValue = 0x55;
 | |
|   IoWrite8 (SectorCountRegister, SectorCountValue);
 | |
| 
 | |
|   //
 | |
|   // select device
 | |
|   //
 | |
|   IoWrite8 (HeadRegister, (UINT8) ((Device << 4) | 0xe0));
 | |
| 
 | |
|   Status = DRDYReady (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 100);
 | |
| 
 | |
|   //
 | |
|   // select device
 | |
|   //
 | |
|   IoWrite8 (HeadRegister, (UINT8) ((Device << 4) | 0xe0));
 | |
|   //
 | |
|   // send 'check power' commandd via Command Register
 | |
|   //
 | |
|   IoWrite8 (CommandRegister, AtaCommand);
 | |
| 
 | |
|   Status = WaitForBSYClear (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 3000);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   StatusValue = IoRead8 (StatusRegister);
 | |
| 
 | |
|   //
 | |
|   // command returned status is DRDY, indicating device supports the command,
 | |
|   // so device is present.
 | |
|   //
 | |
|   if ((StatusValue & ATA_STSREG_DRDY) == ATA_STSREG_DRDY) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   SectorCountValue = IoRead8 (SectorCountRegister);
 | |
| 
 | |
|   //
 | |
|   // command returned status is ERR & ABRT_ERR, indicating device does not support
 | |
|   // the command, so device is present.
 | |
|   //
 | |
|   if ((StatusValue & ATA_STSREG_ERR) == ATA_STSREG_ERR) {
 | |
|     ErrorValue = IoRead8 (ErrorRegister);
 | |
|     if ((ErrorValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
 | |
|       return EFI_ABORTED;
 | |
|     } else {
 | |
|       //
 | |
|       // According to spec, no other error code is valid
 | |
|       //
 | |
|       return EFI_NOT_FOUND;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if ((SectorCountValue == 0x00) || (SectorCountValue == 0x80) || (SectorCountValue == 0xff)) {
 | |
|     //
 | |
|     // Write SectorCount 0x55 but return valid state value. Maybe no device
 | |
|     // exists or some slow kind of ATAPI device exists.
 | |
|     //
 | |
|     IoWrite8 (HeadRegister, (UINT8) ((Device << 4) | 0xe0));
 | |
| 
 | |
|     //
 | |
|     // write 0x55 and 0xaa to SectorCounter register,
 | |
|     // if the data could be written into the register,
 | |
|     // indicating the device is present, otherwise the device is not present.
 | |
|     //
 | |
|     SectorCountValue = 0x55;
 | |
|     IoWrite8 (SectorCountRegister, SectorCountValue);
 | |
|     MicroSecondDelay (10000);
 | |
| 
 | |
|     SectorCountValue = IoRead8 (SectorCountRegister);
 | |
|     if (SectorCountValue != 0x55) {
 | |
|       return EFI_NOT_FOUND;
 | |
|     }
 | |
|     //
 | |
|     // Send a "ATAPI TEST UNIT READY" command ... slow but accurate
 | |
|     //
 | |
|     Status = TestUnitReady (AtapiBlkIoDev, DevicePosition);
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   return EFI_NOT_FOUND;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Detect if an IDE controller exists in specified position.
 | |
|   
 | |
|   @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.
 | |
|   @param[in]  DevicePosition  An integer to signify device position.
 | |
| 
 | |
|   @retval TRUE         The Atapi device exists.
 | |
|   @retval FALSE        The Atapi device does not present.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| DetectIDEController (
 | |
|   IN  ATAPI_BLK_IO_DEV   *AtapiBlkIoDev,
 | |
|   IN  UINTN              DevicePosition
 | |
|   )
 | |
| {
 | |
|   UINT8       Channel;
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       AtaCommand;
 | |
| 
 | |
|   Channel           = (UINT8) (DevicePosition / 2);
 | |
| 
 | |
|   ASSERT (Channel < MAX_IDE_CHANNELS);
 | |
|   //
 | |
|   //  Wait 31 seconds for BSY clear
 | |
|   //
 | |
|   Status = WaitForBSYClear (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 31000);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return FALSE;
 | |
|   }
 | |
|   //
 | |
|   // Send 'check power' command for IDE device
 | |
|   //
 | |
|   AtaCommand  = 0xE5;
 | |
|   Status      = CheckPowerMode (AtapiBlkIoDev, DevicePosition, AtaCommand);
 | |
|   if ((Status == EFI_ABORTED) || (Status == EFI_SUCCESS)) {
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wait specified time interval to poll for BSY bit clear in the Status Register.
 | |
|   
 | |
|   @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.
 | |
|   @param[in]  IdeIoRegisters         A pointer to IDE IO registers.
 | |
|   @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.
 | |
| 
 | |
|   @retval EFI_SUCCESS        BSY bit is cleared in the specified time interval.
 | |
|   @retval EFI_TIMEOUT        BSY bit is not cleared in the specified time interval.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| WaitForBSYClear (
 | |
|   IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,
 | |
|   IN  IDE_BASE_REGISTERS  *IdeIoRegisters,
 | |
|   IN  UINTN               TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINTN   Delay;
 | |
|   UINT16  StatusRegister;
 | |
|   UINT8   StatusValue;
 | |
| 
 | |
|   StatusValue     = 0;
 | |
| 
 | |
|   StatusRegister  = IdeIoRegisters->Reg.Status;
 | |
| 
 | |
|   Delay           = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;
 | |
|   do {
 | |
|     StatusValue = IoRead8 (StatusRegister);
 | |
|     if ((StatusValue & ATA_STSREG_BSY) == 0x00) {
 | |
|       break;
 | |
|     }
 | |
|     MicroSecondDelay (250);
 | |
| 
 | |
|     Delay--;
 | |
| 
 | |
|   } while (Delay != 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wait specified time interval to poll for DRDY bit set in the Status register.
 | |
|   
 | |
|   @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.
 | |
|   @param[in]  IdeIoRegisters         A pointer to IDE IO registers.
 | |
|   @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.
 | |
| 
 | |
|   @retval EFI_SUCCESS        DRDY bit is set in the specified time interval.
 | |
|   @retval EFI_TIMEOUT        DRDY bit is not set in the specified time interval.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DRDYReady (
 | |
|   IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,
 | |
|   IN  IDE_BASE_REGISTERS  *IdeIoRegisters,
 | |
|   IN  UINTN               TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINTN   Delay;
 | |
|   UINT16  StatusRegister;
 | |
|   UINT8   StatusValue;
 | |
|   UINT8   ErrValue;
 | |
| 
 | |
|   StatusValue     = 0;
 | |
| 
 | |
|   StatusRegister  = IdeIoRegisters->Reg.Status;
 | |
| 
 | |
|   Delay           = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;
 | |
|   do {
 | |
|     StatusValue = IoRead8 (StatusRegister);
 | |
|     //
 | |
|     //  BSY == 0 , DRDY == 1
 | |
|     //
 | |
|     if ((StatusValue & (ATA_STSREG_DRDY | ATA_STSREG_BSY)) == ATA_STSREG_DRDY) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   if ((StatusValue & (ATA_STSREG_ERR | ATA_STSREG_BSY)) == ATA_STSREG_ERR) {
 | |
|     ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);
 | |
|     if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
 | |
|     return EFI_ABORTED;
 | |
|     }
 | |
|   }
 | |
|   
 | |
|     MicroSecondDelay (250);
 | |
| 
 | |
|     Delay--;
 | |
| 
 | |
|   } while (Delay != 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wait specified time interval to poll for DRQ bit clear in the Status Register.
 | |
|   
 | |
|   @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.
 | |
|   @param[in]  IdeIoRegisters         A pointer to IDE IO registers.
 | |
|   @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.
 | |
| 
 | |
|   @retval EFI_SUCCESS        DRQ bit is cleared in the specified time interval.
 | |
|   @retval EFI_TIMEOUT        DRQ bit is not cleared in the specified time interval.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DRQClear (
 | |
|   IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,
 | |
|   IN  IDE_BASE_REGISTERS  *IdeIoRegisters,
 | |
|   IN  UINTN               TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINTN   Delay;
 | |
|   UINT16  StatusRegister;
 | |
|   UINT8   StatusValue;
 | |
|   UINT8   ErrValue;
 | |
| 
 | |
|   StatusValue     = 0;
 | |
| 
 | |
|   StatusRegister  = IdeIoRegisters->Reg.Status;
 | |
| 
 | |
|   Delay           = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;
 | |
|   do {
 | |
| 
 | |
|     StatusValue = IoRead8 (StatusRegister);
 | |
| 
 | |
|     //
 | |
|     // wait for BSY == 0 and DRQ == 0
 | |
|     //
 | |
|     if ((StatusValue & (ATA_STSREG_DRQ | ATA_STSREG_BSY)) == 0) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   if ((StatusValue & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
 | |
|     ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);
 | |
|     if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
 | |
|     return EFI_ABORTED;
 | |
|     }
 | |
|   }
 | |
|   
 | |
|     MicroSecondDelay (250);
 | |
| 
 | |
|     Delay--;
 | |
|   } while (Delay != 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wait specified time interval to poll for DRQ bit clear in the Alternate Status Register.
 | |
|   
 | |
|   @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.
 | |
|   @param[in]  IdeIoRegisters         A pointer to IDE IO registers.
 | |
|   @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.
 | |
| 
 | |
|   @retval EFI_SUCCESS        DRQ bit is cleared in the specified time interval.
 | |
|   @retval EFI_TIMEOUT        DRQ bit is not cleared in the specified time interval.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DRQClear2 (
 | |
|   IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,
 | |
|   IN  IDE_BASE_REGISTERS  *IdeIoRegisters,
 | |
|   IN  UINTN               TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINTN   Delay;
 | |
|   UINT16  AltStatusRegister;
 | |
|   UINT8   AltStatusValue;
 | |
|   UINT8   ErrValue;
 | |
| 
 | |
|   AltStatusValue    = 0;
 | |
| 
 | |
|   AltStatusRegister = IdeIoRegisters->Alt.AltStatus;
 | |
| 
 | |
|   Delay             = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;
 | |
|   do {
 | |
| 
 | |
|     AltStatusValue = IoRead8 (AltStatusRegister);
 | |
| 
 | |
|     //
 | |
|     // wait for BSY == 0 and DRQ == 0
 | |
|     //
 | |
|     if ((AltStatusValue & (ATA_STSREG_DRQ | ATA_STSREG_BSY)) == 0) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   if ((AltStatusValue & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
 | |
|     ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);
 | |
|     if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
 | |
|     return EFI_ABORTED;
 | |
|     }
 | |
|   }
 | |
|   
 | |
|     MicroSecondDelay (250);
 | |
| 
 | |
|     Delay--;
 | |
|   } while (Delay != 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wait specified time interval to poll for DRQ bit set in the Status Register.
 | |
| 
 | |
|   @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.
 | |
|   @param[in]  IdeIoRegisters         A pointer to IDE IO registers.
 | |
|   @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.
 | |
| 
 | |
|   @retval EFI_SUCCESS        DRQ bit is set in the specified time interval.
 | |
|   @retval EFI_TIMEOUT        DRQ bit is not set in the specified time interval.
 | |
|   @retval EFI_ABORTED        Operation Aborted.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DRQReady (
 | |
|   IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,
 | |
|   IN  IDE_BASE_REGISTERS  *IdeIoRegisters,
 | |
|   IN  UINTN               TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINTN   Delay;
 | |
|   UINT16  StatusRegister;
 | |
|   UINT8   StatusValue;
 | |
|   UINT8   ErrValue;
 | |
| 
 | |
|   StatusValue     = 0;
 | |
|   ErrValue        = 0;
 | |
| 
 | |
|   StatusRegister  = IdeIoRegisters->Reg.Status;
 | |
| 
 | |
|   Delay           = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;
 | |
|   do {
 | |
|     //
 | |
|     //  read Status Register will clear interrupt
 | |
|     //
 | |
|     StatusValue = IoRead8 (StatusRegister);
 | |
| 
 | |
|     //
 | |
|     //  BSY==0,DRQ==1
 | |
|     //
 | |
|     if ((StatusValue & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if ((StatusValue & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
 | |
| 
 | |
|       ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);
 | |
|       if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
 | |
|         return EFI_ABORTED;
 | |
|       }
 | |
|     }
 | |
|     MicroSecondDelay (250);
 | |
| 
 | |
|     Delay--;
 | |
|   } while (Delay != 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wait specified time interval to poll for DRQ bit set in the Alternate Status Register.
 | |
| 
 | |
|   @param[in]  AtapiBlkIoDev          A pointer to atapi block IO device.
 | |
|   @param[in]  IdeIoRegisters         A pointer to IDE IO registers.
 | |
|   @param[in]  TimeoutInMilliSeconds  Time specified in milliseconds.
 | |
| 
 | |
|   @retval EFI_SUCCESS        DRQ bit is set in the specified time interval.
 | |
|   @retval EFI_TIMEOUT        DRQ bit is not set in the specified time interval.
 | |
|   @retval EFI_ABORTED        Operation Aborted.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DRQReady2 (
 | |
|   IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,
 | |
|   IN  IDE_BASE_REGISTERS  *IdeIoRegisters,
 | |
|   IN  UINTN               TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINTN   Delay;
 | |
|   UINT16  AltStatusRegister;
 | |
|   UINT8   AltStatusValue;
 | |
|   UINT8   ErrValue;
 | |
| 
 | |
|   AltStatusValue    = 0;
 | |
| 
 | |
|   AltStatusRegister = IdeIoRegisters->Alt.AltStatus;
 | |
| 
 | |
|   Delay             = ((TimeoutInMilliSeconds * STALL_1_MILLI_SECOND) / 250) + 1;
 | |
|   do {
 | |
| 
 | |
|     AltStatusValue = IoRead8 (AltStatusRegister);
 | |
| 
 | |
|     //
 | |
|     //  BSY==0,DRQ==1
 | |
|     //
 | |
|     if ((AltStatusValue & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if ((AltStatusValue & (ATA_STSREG_BSY | ATA_STSREG_ERR)) == ATA_STSREG_ERR) {
 | |
| 
 | |
|       ErrValue = IoRead8 (IdeIoRegisters->Reg1.Error);
 | |
|       if ((ErrValue & ATA_ERRREG_ABRT) == ATA_ERRREG_ABRT) {
 | |
|         return EFI_ABORTED;
 | |
|       }
 | |
|     }
 | |
|     MicroSecondDelay (250);
 | |
| 
 | |
|     Delay--;
 | |
|   } while (Delay != 0);
 | |
| 
 | |
|   if (Delay == 0) {
 | |
|     return EFI_TIMEOUT;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check if there is an error in Status Register.
 | |
| 
 | |
|   @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.
 | |
|   @param[in]  StatusReg         The address to IDE IO registers.
 | |
| 
 | |
|   @retval EFI_SUCCESS        Operation success.
 | |
|   @retval EFI_DEVICE_ERROR   Device error.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| CheckErrorStatus (
 | |
|   IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,
 | |
|   IN  UINT16              StatusReg
 | |
|   )
 | |
| {
 | |
|   UINT8 StatusValue;
 | |
| 
 | |
|   StatusValue = IoRead8 (StatusReg);
 | |
| 
 | |
|   if ((StatusValue & (ATA_STSREG_ERR | ATA_STSREG_DWF | ATA_STSREG_CORR)) == 0) {
 | |
| 
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   return EFI_DEVICE_ERROR;
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Idendify Atapi devices.
 | |
| 
 | |
|   @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.
 | |
|   @param[in]  DevicePosition    An integer to signify device position.
 | |
| 
 | |
|   @retval EFI_SUCCESS        Identify successfully.
 | |
|   @retval EFI_DEVICE_ERROR   Device cannot be identified successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ATAPIIdentify (
 | |
|   IN  ATAPI_BLK_IO_DEV        *AtapiBlkIoDev,
 | |
|   IN  UINTN                   DevicePosition
 | |
|   )
 | |
| {
 | |
|   ATAPI_IDENTIFY_DATA  AtapiIdentifyData;
 | |
|   UINT8       Channel;
 | |
|   UINT8       Device;
 | |
|   UINT16      StatusReg;
 | |
|   UINT16      HeadReg;
 | |
|   UINT16      CommandReg;
 | |
|   UINT16      DataReg;
 | |
|   UINT16      SectorCountReg;
 | |
|   UINT16      SectorNumberReg;
 | |
|   UINT16      CylinderLsbReg;
 | |
|   UINT16      CylinderMsbReg;
 | |
| 
 | |
|   UINT32      WordCount;
 | |
|   UINT32      Increment;
 | |
|   UINT32      Index;
 | |
|   UINT32      ByteCount;
 | |
|   UINT16      *Buffer16;
 | |
| 
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   ByteCount       = sizeof (AtapiIdentifyData);
 | |
|   Buffer16        = (UINT16 *) &AtapiIdentifyData;
 | |
| 
 | |
|   Channel         = (UINT8) (DevicePosition / 2);
 | |
|   Device          = (UINT8) (DevicePosition % 2);
 | |
| 
 | |
|   ASSERT (Channel < MAX_IDE_CHANNELS);
 | |
| 
 | |
|   StatusReg       = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Status;
 | |
|   HeadReg         = AtapiBlkIoDev->IdeIoPortReg[Channel].Head;
 | |
|   CommandReg      = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Command;
 | |
|   DataReg         = AtapiBlkIoDev->IdeIoPortReg[Channel].Data;
 | |
|   SectorCountReg  = AtapiBlkIoDev->IdeIoPortReg[Channel].SectorCount;
 | |
|   SectorNumberReg = AtapiBlkIoDev->IdeIoPortReg[Channel].SectorNumber;
 | |
|   CylinderLsbReg  = AtapiBlkIoDev->IdeIoPortReg[Channel].CylinderLsb;
 | |
|   CylinderMsbReg  = AtapiBlkIoDev->IdeIoPortReg[Channel].CylinderMsb;
 | |
| 
 | |
|   //
 | |
|   // Send ATAPI Identify Command to get IDENTIFY data.
 | |
|   //
 | |
|   if (WaitForBSYClear (
 | |
|         AtapiBlkIoDev,
 | |
|         &(AtapiBlkIoDev->IdeIoPortReg[Channel]),
 | |
|         ATATIMEOUT
 | |
|         ) != EFI_SUCCESS) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   //
 | |
|   // select device via Head/Device register.
 | |
|   // Before write Head/Device register, BSY and DRQ must be 0.
 | |
|   //
 | |
|   if (DRQClear2 (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), ATATIMEOUT) != EFI_SUCCESS) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   //
 | |
|   //  e0:1110,0000-- bit7 and bit5 are reserved bits.
 | |
|   //           bit6 set means LBA mode
 | |
|   //
 | |
|   IoWrite8 (HeadReg, (UINT8) ((Device << 4) | 0xe0));
 | |
| 
 | |
|   //
 | |
|   // set all the command parameters
 | |
|   // Before write to all the following registers, BSY and DRQ must be 0.
 | |
|   //
 | |
|   if (DRQClear2 (
 | |
|         AtapiBlkIoDev,
 | |
|         &(AtapiBlkIoDev->IdeIoPortReg[Channel]),
 | |
|         ATATIMEOUT
 | |
|         ) != EFI_SUCCESS) {
 | |
| 
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   IoWrite8 (SectorCountReg, 0);
 | |
|   IoWrite8 (SectorNumberReg, 0);
 | |
|   IoWrite8 (CylinderLsbReg, 0);
 | |
|   IoWrite8 (CylinderMsbReg, 0);
 | |
| 
 | |
|   //
 | |
|   // send command via Command Register
 | |
|   //
 | |
|   IoWrite8 (CommandReg, ATA_CMD_IDENTIFY_DEVICE);
 | |
| 
 | |
|   //
 | |
|   // According to PIO data in protocol, host can perform a series of reads to the
 | |
|   // data register after each time device set DRQ ready;
 | |
|   // The data size of "a series of read" is command specific.
 | |
|   // For most ATA command, data size received from device will not exceed 1 sector,
 | |
|   // hense the data size for "a series of read" can be the whole data size of one command request.
 | |
|   // For ATA command such as Read Sector command, whole data size of one ATA command request is often larger
 | |
|   // than 1 sector, according to the Read Sector command, the data size of "a series of read" is exactly
 | |
|   // 1 sector.
 | |
|   // Here for simplification reason, we specify the data size for "a series of read" to
 | |
|   // 1 sector (256 words) if whole data size of one ATA commmand request is larger than 256 words.
 | |
|   //
 | |
|   Increment = 256;
 | |
|   //
 | |
|   // 256 words
 | |
|   //
 | |
|   WordCount = 0;
 | |
|   //
 | |
|   // WordCount is used to record bytes of currently transfered data
 | |
|   //
 | |
|   while (WordCount < ByteCount / 2) {
 | |
|     //
 | |
|     // Poll DRQ bit set, data transfer can be performed only when DRQ is ready.
 | |
|     //
 | |
|     Status = DRQReady2 (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), ATATIMEOUT);
 | |
|     if (Status != EFI_SUCCESS) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     if (CheckErrorStatus (AtapiBlkIoDev, StatusReg) != EFI_SUCCESS) {
 | |
| 
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
|     //
 | |
|     // Get the byte count for one series of read
 | |
|     //
 | |
|     if ((WordCount + Increment) > ByteCount / 2) {
 | |
|       Increment = ByteCount / 2 - WordCount;
 | |
|     }
 | |
|     //
 | |
|     // perform a series of read without check DRQ ready
 | |
|     //
 | |
|     for (Index = 0; Index < Increment; Index++) {
 | |
|       *Buffer16++ = IoRead16 (DataReg);
 | |
|     }
 | |
| 
 | |
|     WordCount += Increment;
 | |
| 
 | |
|   }
 | |
|   //
 | |
|   // while
 | |
|   //
 | |
|   if (DRQClear (
 | |
|         AtapiBlkIoDev,
 | |
|         &(AtapiBlkIoDev->IdeIoPortReg[Channel]),
 | |
|         ATATIMEOUT
 | |
|         ) != EFI_SUCCESS) {
 | |
|     return CheckErrorStatus (AtapiBlkIoDev, StatusReg);
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Sends out ATAPI Test Unit Ready Packet Command to the specified device
 | |
|   to find out whether device is accessible.
 | |
| 
 | |
|   @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.
 | |
|   @param[in]  DevicePosition    An integer to signify device position.
 | |
| 
 | |
|   @retval EFI_SUCCESS        TestUnit command executed successfully.
 | |
|   @retval EFI_DEVICE_ERROR   Device cannot be executed TestUnit command successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| TestUnitReady (
 | |
|   IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,
 | |
|   IN  UINTN               DevicePosition
 | |
|   )
 | |
| {
 | |
|   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 = AtapiPacketCommandIn (AtapiBlkIoDev, DevicePosition, &Packet, NULL, 0, ATAPITIMEOUT);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Send out ATAPI commands conforms to the Packet Command with PIO Data In Protocol.
 | |
|   
 | |
|   @param[in]  AtapiBlkIoDev         A pointer to atapi block IO device.
 | |
|   @param[in]  DevicePosition        An integer to signify device position.
 | |
|   @param[in]  Packet                A pointer to ATAPI command packet.
 | |
|   @param[in]  Buffer                Buffer to contain requested transfer data from device.
 | |
|   @param[in]  ByteCount             Requested transfer data length.
 | |
|   @param[in]  TimeoutInMilliSeconds Time out value, in unit of milliseconds.
 | |
| 
 | |
|   @retval EFI_SUCCESS        Command executed successfully.
 | |
|   @retval EFI_DEVICE_ERROR   Device cannot be executed command successfully.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiPacketCommandIn (
 | |
|   IN  ATAPI_BLK_IO_DEV      *AtapiBlkIoDev,
 | |
|   IN  UINTN                 DevicePosition,
 | |
|   IN  ATAPI_PACKET_COMMAND  *Packet,
 | |
|   IN  UINT16                *Buffer,
 | |
|   IN  UINT32                ByteCount,
 | |
|   IN  UINTN                 TimeoutInMilliSeconds
 | |
|   )
 | |
| {
 | |
|   UINT8       Channel;
 | |
|   UINT8       Device;
 | |
|   UINT16      StatusReg;
 | |
|   UINT16      HeadReg;
 | |
|   UINT16      CommandReg;
 | |
|   UINT16      FeatureReg;
 | |
|   UINT16      CylinderLsbReg;
 | |
|   UINT16      CylinderMsbReg;
 | |
|   UINT16      DeviceControlReg;
 | |
|   UINT16      DataReg;
 | |
|   EFI_STATUS  Status;
 | |
|   UINT32      Count;
 | |
|   UINT16      *CommandIndex;
 | |
|   UINT16      *PtrBuffer;
 | |
|   UINT32      Index;
 | |
|   UINT8       StatusValue;
 | |
|   UINT32      WordCount;
 | |
| 
 | |
|   //
 | |
|   // required transfer data in word unit.
 | |
|   //
 | |
|   UINT32      RequiredWordCount;
 | |
| 
 | |
|   //
 | |
|   // actual transfer data in word unit.
 | |
|   //
 | |
|   UINT32      ActualWordCount;
 | |
| 
 | |
|   Channel           = (UINT8) (DevicePosition / 2);
 | |
|   Device            = (UINT8) (DevicePosition % 2);
 | |
| 
 | |
|   ASSERT (Channel < MAX_IDE_CHANNELS);
 | |
| 
 | |
|   StatusReg         = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Status;
 | |
|   HeadReg           = AtapiBlkIoDev->IdeIoPortReg[Channel].Head;
 | |
|   CommandReg        = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Command;
 | |
|   FeatureReg        = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg1.Feature;
 | |
|   CylinderLsbReg    = AtapiBlkIoDev->IdeIoPortReg[Channel].CylinderLsb;
 | |
|   CylinderMsbReg    = AtapiBlkIoDev->IdeIoPortReg[Channel].CylinderMsb;
 | |
|   DeviceControlReg  = AtapiBlkIoDev->IdeIoPortReg[Channel].Alt.DeviceControl;
 | |
|   DataReg           = AtapiBlkIoDev->IdeIoPortReg[Channel].Data;
 | |
| 
 | |
|   //
 | |
|   // Set all the command parameters by fill related registers.
 | |
|   // Before write to all the following registers, BSY and DRQ must be 0.
 | |
|   //
 | |
|   if (DRQClear2 (
 | |
|         AtapiBlkIoDev,
 | |
|         &(AtapiBlkIoDev->IdeIoPortReg[Channel]),
 | |
|         ATATIMEOUT
 | |
|         ) != EFI_SUCCESS) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   //
 | |
|   // Select device via Device/Head Register.
 | |
|   // DEFAULT_CMD: 0xa0 (1010,0000)
 | |
|   //
 | |
|   IoWrite8 (HeadReg, (UINT8) ((Device << 4) | ATA_DEFAULT_CMD));
 | |
| 
 | |
|   //
 | |
|   // No OVL; No DMA
 | |
|   //
 | |
|   IoWrite8 (FeatureReg, 0x00);
 | |
| 
 | |
|   //
 | |
|   // set the transfersize to MAX_ATAPI_BYTE_COUNT to let the device
 | |
|   // determine how many data should be transfered.
 | |
|   //
 | |
|   IoWrite8 (CylinderLsbReg, (UINT8) (ATAPI_MAX_BYTE_COUNT & 0x00ff));
 | |
|   IoWrite8 (CylinderMsbReg, (UINT8) (ATAPI_MAX_BYTE_COUNT >> 8));
 | |
| 
 | |
|   //
 | |
|   //  DEFAULT_CTL:0x0a (0000,1010)
 | |
|   //  Disable interrupt
 | |
|   //
 | |
|   IoWrite8 (DeviceControlReg, ATA_DEFAULT_CTL);
 | |
| 
 | |
|   //
 | |
|   // Send Packet command to inform device
 | |
|   // that the following data bytes are command packet.
 | |
|   //
 | |
|   IoWrite8 (CommandReg, ATA_CMD_PACKET);
 | |
| 
 | |
|   Status = DRQReady (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), TimeoutInMilliSeconds);
 | |
|   if (Status != EFI_SUCCESS) {
 | |
|     return Status;
 | |
|   }
 | |
|   //
 | |
|   // Send out command packet
 | |
|   //
 | |
|   CommandIndex = Packet->Data16;
 | |
|   for (Count = 0; Count < 6; Count++, CommandIndex++) {
 | |
|     IoWrite16 (DataReg, *CommandIndex);
 | |
|     MicroSecondDelay (10);
 | |
|   }
 | |
| 
 | |
|   StatusValue = IoRead8 (StatusReg);
 | |
|   if ((StatusValue & ATA_STSREG_ERR) == ATA_STSREG_ERR) {
 | |
|     //
 | |
|     // Trouble! Something's wrong here... Wait some time and return. 3 second is
 | |
|     // supposed to be long enough for a device reset latency or error recovery
 | |
|     //
 | |
|     MicroSecondDelay (3000000);
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (Buffer == NULL || ByteCount == 0) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
|   //
 | |
|   // call PioReadWriteData() function to get
 | |
|   // requested transfer data form device.
 | |
|   //
 | |
|   PtrBuffer         = Buffer;
 | |
|   RequiredWordCount = ByteCount / 2;
 | |
|   //
 | |
|   // ActuralWordCount means the word count of data really transfered.
 | |
|   //
 | |
|   ActualWordCount = 0;
 | |
| 
 | |
|   Status          = EFI_SUCCESS;
 | |
|   while ((Status == EFI_SUCCESS) && (ActualWordCount < RequiredWordCount)) {
 | |
|     //
 | |
|     // before each data transfer stream, the host should poll DRQ bit ready,
 | |
|     // which informs device is ready to transfer data.
 | |
|     //
 | |
|     if (DRQReady2 (
 | |
|           AtapiBlkIoDev,
 | |
|           &(AtapiBlkIoDev->IdeIoPortReg[Channel]),
 | |
|           TimeoutInMilliSeconds
 | |
|           ) != EFI_SUCCESS) {
 | |
|       return CheckErrorStatus (AtapiBlkIoDev, StatusReg);
 | |
|     }
 | |
|     //
 | |
|     // read Status Register will clear interrupt
 | |
|     //
 | |
|     StatusValue = IoRead8 (StatusReg);
 | |
| 
 | |
|     //
 | |
|     // get current data transfer size from Cylinder Registers.
 | |
|     //
 | |
|     WordCount = IoRead8 (CylinderMsbReg) << 8;
 | |
|     WordCount = WordCount | IoRead8 (CylinderLsbReg);
 | |
|     WordCount = WordCount & 0xffff;
 | |
|     WordCount /= 2;
 | |
| 
 | |
|     //
 | |
|     // perform a series data In/Out.
 | |
|     //
 | |
|     for (Index = 0; (Index < WordCount) && (ActualWordCount < RequiredWordCount); Index++, ActualWordCount++) {
 | |
| 
 | |
|       *PtrBuffer = IoRead16 (DataReg);
 | |
| 
 | |
|       PtrBuffer++;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     if (((ATAPI_REQUEST_SENSE_CMD *) Packet)->opcode == ATA_CMD_REQUEST_SENSE && ActualWordCount >= 4) {
 | |
|       RequiredWordCount = MIN (
 | |
|                             RequiredWordCount,
 | |
|                             (UINT32) (4 + (((ATAPI_REQUEST_SENSE_DATA *) Buffer)->addnl_sense_length / 2))
 | |
|                             );
 | |
|     }
 | |
| 
 | |
|   }
 | |
|   //
 | |
|   // After data transfer is completed, normally, DRQ bit should clear.
 | |
|   //
 | |
|   Status = DRQClear2 (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), TimeoutInMilliSeconds);
 | |
|   if (Status != EFI_SUCCESS) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   //
 | |
|   // read status register to check whether error happens.
 | |
|   //
 | |
|   Status = CheckErrorStatus (AtapiBlkIoDev, StatusReg);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Sends out ATAPI Inquiry Packet Command to the specified device.
 | |
|   This command will return INQUIRY data of the device.
 | |
| 
 | |
|   @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.
 | |
|   @param[in]  DevicePosition  An integer to signify device position.
 | |
|   @param[out] MediaInfo       The media information of the specified block media.
 | |
| 
 | |
|   @retval EFI_SUCCESS        Command executed successfully.
 | |
|   @retval EFI_DEVICE_ERROR   Device cannot be executed command successfully.
 | |
|   @retval EFI_UNSUPPORTED    Unsupported device type.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Inquiry (
 | |
|   IN  ATAPI_BLK_IO_DEV              *AtapiBlkIoDev,
 | |
|   IN  UINTN                         DevicePosition,
 | |
|   OUT EFI_PEI_BLOCK_IO_MEDIA        *MediaInfo
 | |
|   )
 | |
| {
 | |
|   ATAPI_PACKET_COMMAND  Packet;
 | |
|   EFI_STATUS            Status;
 | |
|   ATAPI_INQUIRY_DATA          Idata;
 | |
| 
 | |
|   //
 | |
|   // prepare command packet for the ATAPI Inquiry Packet Command.
 | |
|   //
 | |
|   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  = (UINT8) sizeof (ATAPI_INQUIRY_DATA);
 | |
| 
 | |
|   //
 | |
|   // Send command packet and get requested Inquiry data.
 | |
|   //
 | |
|   Status = AtapiPacketCommandIn (
 | |
|             AtapiBlkIoDev,
 | |
|             DevicePosition,
 | |
|             &Packet,
 | |
|             (UINT16 *) (&Idata),
 | |
|             sizeof (ATAPI_INQUIRY_DATA),
 | |
|             ATAPITIMEOUT
 | |
|             //50
 | |
|             );
 | |
| 
 | |
|   if (Status != EFI_SUCCESS) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   //
 | |
|   // Identify device type via INQUIRY data.
 | |
|   //
 | |
|   switch (Idata.peripheral_type & 0x1f) {
 | |
|   case 0x00:
 | |
|     //
 | |
|     // Magnetic Disk
 | |
|     //
 | |
|     MediaInfo->DeviceType   = IdeLS120;
 | |
|     MediaInfo->MediaPresent = FALSE;
 | |
|     MediaInfo->LastBlock    = 0;
 | |
|     MediaInfo->BlockSize    = 0x200;
 | |
|     break;
 | |
| 
 | |
|   case 0x05:
 | |
|     //
 | |
|     // CD-ROM
 | |
|     //
 | |
|     MediaInfo->DeviceType   = IdeCDROM;
 | |
|     MediaInfo->MediaPresent = FALSE;
 | |
|     MediaInfo->LastBlock    = 0;
 | |
|     MediaInfo->BlockSize    = 0x800;
 | |
|     break;
 | |
| 
 | |
|   default:
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**  
 | |
|   Used before read/write blocks from/to ATAPI device media. 
 | |
|   Since ATAPI device media is removable, it is necessary to detect
 | |
|   whether media is present and get current present media's information.
 | |
| 
 | |
|   @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.
 | |
|   @param[in]  DevicePosition    An integer to signify device position.
 | |
|   @param[in, out] MediaInfo         The media information of the specified block media.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Command executed successfully.
 | |
|   @retval EFI_DEVICE_ERROR      Some device errors happen.
 | |
|   @retval EFI_OUT_OF_RESOURCES  Can not allocate required resources.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DetectMedia (
 | |
|   IN  ATAPI_BLK_IO_DEV              *AtapiBlkIoDev,
 | |
|   IN  UINTN                         DevicePosition,
 | |
|   IN OUT EFI_PEI_BLOCK_IO_MEDIA     *MediaInfo
 | |
|   )
 | |
| {
 | |
| 
 | |
|   UINTN                   Index;
 | |
|   UINTN                   RetryNum;
 | |
|   UINTN                   MaxRetryNum;
 | |
|   ATAPI_REQUEST_SENSE_DATA      *SenseBuffers;
 | |
|   BOOLEAN                 NeedReadCapacity;
 | |
|   BOOLEAN                 NeedRetry;
 | |
|   EFI_STATUS              Status;
 | |
|   UINT8                   SenseCounts;
 | |
| 
 | |
|   SenseBuffers = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (*SenseBuffers)));
 | |
|   if (SenseBuffers == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // Test Unit Ready command is used to detect whether device is accessible,
 | |
|   // the device will produce corresponding Sense data.
 | |
|   //
 | |
|   for (Index = 0; Index < 2; Index++) {
 | |
| 
 | |
|     Status = TestUnitReady (AtapiBlkIoDev, DevicePosition);
 | |
|     if (Status != EFI_SUCCESS) {
 | |
|       Status = ResetDevice (AtapiBlkIoDev, DevicePosition, FALSE);
 | |
| 
 | |
|       if (Status != EFI_SUCCESS) {
 | |
|         ResetDevice (AtapiBlkIoDev, DevicePosition, TRUE);
 | |
|       }
 | |
| 
 | |
|     } else {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   SenseCounts       = MAX_SENSE_KEY_COUNT;
 | |
|   Status            = EFI_SUCCESS;
 | |
|   NeedReadCapacity  = TRUE;
 | |
| 
 | |
|   for (Index = 0; Index < 5; Index++) {
 | |
|     SenseCounts = MAX_SENSE_KEY_COUNT;
 | |
|     Status = RequestSense (
 | |
|               AtapiBlkIoDev,
 | |
|               DevicePosition,
 | |
|               SenseBuffers,
 | |
|               &SenseCounts
 | |
|               );
 | |
|     DEBUG ((EFI_D_INFO, "Atapi Request Sense Count is %d\n", SenseCounts));
 | |
|     if (IsDeviceStateUnclear (SenseBuffers, SenseCounts) || IsNoMedia (SenseBuffers, SenseCounts)) {
 | |
|       //
 | |
|       // We are not sure whether the media is present or not, try again
 | |
|       //
 | |
|       TestUnitReady (AtapiBlkIoDev, DevicePosition);
 | |
|     } else {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (Status == EFI_SUCCESS) {
 | |
| 
 | |
|     if (IsNoMedia (SenseBuffers, SenseCounts)) {
 | |
| 
 | |
|       NeedReadCapacity        = FALSE;
 | |
|       MediaInfo->MediaPresent = FALSE;
 | |
|       MediaInfo->LastBlock    = 0;
 | |
|     }
 | |
| 
 | |
|     if (IsMediaError (SenseBuffers, SenseCounts)) {
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (NeedReadCapacity) {
 | |
|     //
 | |
|     // at most retry 5 times
 | |
|     //
 | |
|     MaxRetryNum = 5;
 | |
|     RetryNum    = 1;
 | |
|     //
 | |
|     // initial retry once
 | |
|     //
 | |
|     for (Index = 0; (Index < RetryNum) && (Index < MaxRetryNum); Index++) {
 | |
| 
 | |
|       Status = ReadCapacity (AtapiBlkIoDev, DevicePosition, MediaInfo);
 | |
|       MicroSecondDelay (200000);
 | |
|       SenseCounts = MAX_SENSE_KEY_COUNT;
 | |
| 
 | |
|       if (Status != EFI_SUCCESS) {
 | |
| 
 | |
|         Status = RequestSense (AtapiBlkIoDev, DevicePosition, SenseBuffers, &SenseCounts);
 | |
|         //
 | |
|         // If Request Sense data failed, reset the device and retry.
 | |
|         //
 | |
|         if (Status != EFI_SUCCESS) {
 | |
| 
 | |
|           Status = ResetDevice (AtapiBlkIoDev, DevicePosition, FALSE);
 | |
|           //
 | |
|           // if ATAPI soft reset fail,
 | |
|           // use stronger reset mechanism -- ATA soft reset.
 | |
|           //
 | |
|           if (Status != EFI_SUCCESS) {
 | |
|             ResetDevice (AtapiBlkIoDev, DevicePosition, TRUE);
 | |
|           }
 | |
| 
 | |
|           RetryNum++;
 | |
|           //
 | |
|           // retry once more
 | |
|           //
 | |
|           continue;
 | |
|         }
 | |
|         //
 | |
|         // No Media
 | |
|         //
 | |
|         if (IsNoMedia (SenseBuffers, SenseCounts)) {
 | |
| 
 | |
|           MediaInfo->MediaPresent = FALSE;
 | |
|           MediaInfo->LastBlock    = 0;
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|         if (IsMediaError (SenseBuffers, SenseCounts)) {
 | |
|           return EFI_DEVICE_ERROR;
 | |
|         }
 | |
| 
 | |
|         if (!IsDriveReady (SenseBuffers, SenseCounts, &NeedRetry)) {
 | |
|           //
 | |
|           // Drive not ready: if NeedRetry, then retry once more;
 | |
|           // else return error
 | |
|           //
 | |
|           if (NeedRetry) {
 | |
|             RetryNum++;
 | |
|             continue;
 | |
|           } else {
 | |
|             return EFI_DEVICE_ERROR;
 | |
|           }
 | |
|         }
 | |
|         //
 | |
|         // if read capacity fail not for above reasons, retry once more
 | |
|         //
 | |
|         RetryNum++;
 | |
| 
 | |
|       }
 | |
| 
 | |
|     }
 | |
| 
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**  
 | |
|   Reset specified Atapi device.
 | |
| 
 | |
|   @param[in]  AtapiBlkIoDev     A pointer to atapi block IO device.
 | |
|   @param[in]  DevicePosition    An integer to signify device position.
 | |
|   @param[in]  Extensive         If TRUE, use ATA soft reset, otherwise use Atapi soft reset.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Command executed successfully.
 | |
|   @retval EFI_DEVICE_ERROR      Some device errors happen.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ResetDevice (
 | |
|   IN  ATAPI_BLK_IO_DEV  *AtapiBlkIoDev,
 | |
|   IN  UINTN             DevicePosition,
 | |
|   IN  BOOLEAN           Extensive
 | |
|   )
 | |
| {
 | |
|   UINT8   DevControl;
 | |
|   UINT8   Command;
 | |
|   UINT8   DeviceSelect;
 | |
|   UINT16  DeviceControlReg;
 | |
|   UINT16  CommandReg;
 | |
|   UINT16  HeadReg;
 | |
|   UINT8   Channel;
 | |
|   UINT8   Device;
 | |
| 
 | |
|   Channel           = (UINT8) (DevicePosition / 2);
 | |
|   Device            = (UINT8) (DevicePosition % 2);
 | |
| 
 | |
|   ASSERT (Channel < MAX_IDE_CHANNELS);
 | |
| 
 | |
|   DeviceControlReg  = AtapiBlkIoDev->IdeIoPortReg[Channel].Alt.DeviceControl;
 | |
|   CommandReg        = AtapiBlkIoDev->IdeIoPortReg[Channel].Reg.Command;
 | |
|   HeadReg           = AtapiBlkIoDev->IdeIoPortReg[Channel].Head;
 | |
| 
 | |
|   if (Extensive) {
 | |
| 
 | |
|     DevControl = 0;
 | |
|     DevControl |= ATA_CTLREG_SRST;
 | |
|     //
 | |
|     // set SRST bit to initiate soft reset
 | |
|     //
 | |
|     DevControl |= BIT1;
 | |
|     //
 | |
|     // disable Interrupt
 | |
|     //
 | |
|     IoWrite8 (DeviceControlReg, DevControl);
 | |
| 
 | |
|     //
 | |
|     // Wait 10us
 | |
|     //
 | |
|     MicroSecondDelay (10);
 | |
| 
 | |
|     //
 | |
|     // Clear SRST bit
 | |
|     //
 | |
|     DevControl &= 0xfb;
 | |
|     //
 | |
|     // 0xfb:1111,1011
 | |
|     //
 | |
|     IoWrite8 (DeviceControlReg, DevControl);
 | |
| 
 | |
|     //
 | |
|     // slave device needs at most 31s to clear BSY
 | |
|     //
 | |
|     if (WaitForBSYClear (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 31000) == EFI_TIMEOUT) {
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
| 
 | |
|   } else {
 | |
|     //
 | |
|     // for ATAPI device, no need to wait DRDY ready after device selecting.
 | |
|     // bit7 and bit5 are both set to 1 for backward compatibility
 | |
|     //
 | |
|     DeviceSelect = (UINT8) (((BIT7 | BIT5) | (Device << 4)));
 | |
|     IoWrite8 (HeadReg, DeviceSelect);
 | |
| 
 | |
|     Command = ATA_CMD_SOFT_RESET;
 | |
|     IoWrite8 (CommandReg, Command);
 | |
| 
 | |
|     //
 | |
|     // BSY cleared is the only status return to the host by the device when reset is completed
 | |
|     // slave device needs at most 31s to clear BSY
 | |
|     //
 | |
|     if (WaitForBSYClear (AtapiBlkIoDev, &(AtapiBlkIoDev->IdeIoPortReg[Channel]), 31000) != EFI_SUCCESS) {
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
|     //
 | |
|     // stall 5 seconds to make the device status stable
 | |
|     //
 | |
|     MicroSecondDelay (STALL_1_SECONDS * 5);
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| 
 | |
| }
 | |
| 
 | |
| /**  
 | |
|   Sends out ATAPI Request Sense Packet Command to the specified device.
 | |
| 
 | |
|   @param[in]      AtapiBlkIoDev   A pointer to atapi block IO device.
 | |
|   @param[in]      DevicePosition  An integer to signify device position.
 | |
|   @param[in]      SenseBuffers    Pointer to sense buffer.
 | |
|   @param[in, out] SenseCounts     Length of sense buffer.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Command executed successfully.
 | |
|   @retval EFI_DEVICE_ERROR      Some device errors happen.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| RequestSense (
 | |
|   IN  ATAPI_BLK_IO_DEV           *AtapiBlkIoDev,
 | |
|   IN  UINTN                      DevicePosition,
 | |
|   IN  ATAPI_REQUEST_SENSE_DATA   *SenseBuffers,
 | |
|   IN  OUT  UINT8                 *SenseCounts
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS            Status;
 | |
|   ATAPI_REQUEST_SENSE_DATA    *Sense;
 | |
|   UINT16                *Ptr;
 | |
|   BOOLEAN               SenseReq;
 | |
|   ATAPI_PACKET_COMMAND  Packet;
 | |
| 
 | |
|   ZeroMem (SenseBuffers, sizeof (ATAPI_REQUEST_SENSE_DATA) * (*SenseCounts));
 | |
|   //
 | |
|   // 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 = (UINT16 *) SenseBuffers;
 | |
|   //
 | |
|   // initialize pointer
 | |
|   //
 | |
|   *SenseCounts = 0;
 | |
|   //
 | |
|   //  request sense data from device continiously until no sense data exists in the device.
 | |
|   //
 | |
|   for (SenseReq = TRUE; SenseReq;) {
 | |
| 
 | |
|     Sense = (ATAPI_REQUEST_SENSE_DATA *) Ptr;
 | |
| 
 | |
|     //
 | |
|     // send out Request Sense Packet Command and get one Sense data form device
 | |
|     //
 | |
|     Status = AtapiPacketCommandIn (
 | |
|               AtapiBlkIoDev,
 | |
|               DevicePosition,
 | |
|               &Packet,
 | |
|               Ptr,
 | |
|               sizeof (ATAPI_REQUEST_SENSE_DATA),
 | |
|               ATAPITIMEOUT
 | |
|               );
 | |
|     //
 | |
|     // failed to get Sense data
 | |
|     //
 | |
|     if (Status != EFI_SUCCESS) {
 | |
|       if (*SenseCounts == 0) {
 | |
|         return EFI_DEVICE_ERROR;
 | |
|       } else {
 | |
|         return EFI_SUCCESS;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     (*SenseCounts)++;
 | |
| 
 | |
|     if (*SenseCounts > MAX_SENSE_KEY_COUNT) {
 | |
|       return EFI_SUCCESS;
 | |
|     }
 | |
|     //
 | |
|     // We limit MAX sense data count to 20 in order to avoid dead loop. Some
 | |
|     // incompatible ATAPI devices don't retrive NO_SENSE when there is no media.
 | |
|     // In this case, dead loop occurs if we don't have a gatekeeper. 20 is
 | |
|     // supposed to be large enough for any ATAPI device.
 | |
|     //
 | |
|     if ((Sense->sense_key != ATA_SK_NO_SENSE) && ((*SenseCounts) < 20)) {
 | |
| 
 | |
|       Ptr += sizeof (ATAPI_REQUEST_SENSE_DATA) / 2;
 | |
|       //
 | |
|       // Ptr is word based pointer
 | |
|       //
 | |
|     } 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[in]  AtapiBlkIoDev   A pointer to atapi block IO device.
 | |
|   @param[in]  DevicePosition  An integer to signify device position.
 | |
|   @param[in, out] MediaInfo       The media information of the specified block media.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Command executed successfully.
 | |
|   @retval EFI_DEVICE_ERROR      Some device errors happen.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ReadCapacity (
 | |
|   IN  ATAPI_BLK_IO_DEV              *AtapiBlkIoDev,
 | |
|   IN  UINTN                         DevicePosition,
 | |
|   IN OUT EFI_PEI_BLOCK_IO_MEDIA     *MediaInfo
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                Status;
 | |
|   ATAPI_PACKET_COMMAND      Packet;
 | |
| 
 | |
|   //
 | |
|   // used for capacity data returned from ATAPI device
 | |
|   //
 | |
|   ATAPI_READ_CAPACITY_DATA        Data;
 | |
|   ATAPI_READ_FORMAT_CAPACITY_DATA FormatData;
 | |
| 
 | |
|   ZeroMem (&Data, sizeof (Data));
 | |
|   ZeroMem (&FormatData, sizeof (FormatData));
 | |
| 
 | |
|   if (MediaInfo->DeviceType == IdeCDROM) {
 | |
| 
 | |
|     ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
 | |
|     Packet.Inquiry.opcode = ATA_CMD_READ_CAPACITY;
 | |
|     Status = AtapiPacketCommandIn (
 | |
|               AtapiBlkIoDev,
 | |
|               DevicePosition,
 | |
|               &Packet,
 | |
|               (UINT16 *) (&Data),
 | |
|               sizeof (ATAPI_READ_CAPACITY_DATA),
 | |
|               ATAPITIMEOUT
 | |
|               );
 | |
| 
 | |
|   } else {
 | |
|     //
 | |
|     // DeviceType == IdeLS120
 | |
|     //
 | |
|     ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
 | |
|     Packet.ReadFormatCapacity.opcode                = ATA_CMD_READ_FORMAT_CAPACITY;
 | |
|     Packet.ReadFormatCapacity.allocation_length_lo  = 12;
 | |
|     Status = AtapiPacketCommandIn (
 | |
|               AtapiBlkIoDev,
 | |
|               DevicePosition,
 | |
|               &Packet,
 | |
|               (UINT16 *) (&FormatData),
 | |
|               sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA),
 | |
|               ATAPITIMEOUT*10
 | |
|               );
 | |
|   }
 | |
| 
 | |
|   if (Status == EFI_SUCCESS) {
 | |
| 
 | |
|     if (MediaInfo->DeviceType == IdeCDROM) {
 | |
| 
 | |
|       MediaInfo->LastBlock    = (Data.LastLba3 << 24) | (Data.LastLba2 << 16) | (Data.LastLba1 << 8) | Data.LastLba0;
 | |
|       MediaInfo->MediaPresent = TRUE;
 | |
|       //
 | |
|       // Because the user data portion in the sector of the Data CD supported
 | |
|       // is always 800h
 | |
|       //
 | |
|       MediaInfo->BlockSize = 0x800;
 | |
|     }
 | |
| 
 | |
|     if (MediaInfo->DeviceType == IdeLS120) {
 | |
| 
 | |
|       if (FormatData.DesCode == 3) {
 | |
|         MediaInfo->MediaPresent = FALSE;
 | |
|         MediaInfo->LastBlock    = 0;
 | |
|       } else {
 | |
|         MediaInfo->LastBlock = (FormatData.LastLba3 << 24) |
 | |
|           (FormatData.LastLba2 << 16) |
 | |
|           (FormatData.LastLba1 << 8) |
 | |
|           FormatData.LastLba0;
 | |
|         MediaInfo->LastBlock--;
 | |
| 
 | |
|         MediaInfo->MediaPresent = TRUE;
 | |
| 
 | |
|         MediaInfo->BlockSize    = 0x200;
 | |
| 
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return EFI_SUCCESS;
 | |
| 
 | |
|   } else {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**  
 | |
|   Perform read from disk in block unit.
 | |
| 
 | |
|   @param[in]  AtapiBlkIoDev   A pointer to atapi block IO device.
 | |
|   @param[in]  DevicePosition  An integer to signify device position.
 | |
|   @param[in]  Buffer          Buffer to contain read data.
 | |
|   @param[in]  StartLba        Starting LBA address.
 | |
|   @param[in]  NumberOfBlocks  Number of blocks to read.
 | |
|   @param[in]  BlockSize       Size of each block.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Command executed successfully.
 | |
|   @retval EFI_DEVICE_ERROR      Some device errors happen.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ReadSectors (
 | |
|   IN  ATAPI_BLK_IO_DEV    *AtapiBlkIoDev,
 | |
|   IN  UINTN               DevicePosition,
 | |
|   IN  VOID                *Buffer,
 | |
|   IN  EFI_PEI_LBA         StartLba,
 | |
|   IN  UINTN               NumberOfBlocks,
 | |
|   IN  UINTN               BlockSize
 | |
|   )
 | |
| {
 | |
| 
 | |
|   ATAPI_PACKET_COMMAND  Packet;
 | |
|   ATAPI_READ10_CMD      *Read10Packet;
 | |
|   EFI_STATUS            Status;
 | |
|   UINTN                 BlocksRemaining;
 | |
|   UINT32                Lba32;
 | |
|   UINT32                ByteCount;
 | |
|   UINT16                SectorCount;
 | |
|   VOID                  *PtrBuffer;
 | |
|   UINT16                MaxBlock;
 | |
| 
 | |
|   //
 | |
|   // fill command packet for Read(10) command
 | |
|   //
 | |
|   ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
 | |
|   Read10Packet  = &Packet.Read10;
 | |
|   Lba32         = (UINT32) StartLba;
 | |
|   PtrBuffer     = Buffer;
 | |
| 
 | |
|   //
 | |
|   // limit the data bytes that can be transfered by one Read(10) Command
 | |
|   //
 | |
|   MaxBlock = (UINT16) (0x10000 / BlockSize);
 | |
|   //
 | |
|   // (64k bytes)
 | |
|   //
 | |
|   BlocksRemaining = NumberOfBlocks;
 | |
| 
 | |
|   Status          = EFI_SUCCESS;
 | |
|   while (BlocksRemaining > 0) {
 | |
| 
 | |
|     if (BlocksRemaining <= MaxBlock) {
 | |
|       SectorCount = (UINT16) BlocksRemaining;
 | |
|     } else {
 | |
|       SectorCount = MaxBlock;
 | |
|     }
 | |
|     //
 | |
|     // fill the Packet data sturcture
 | |
|     //
 | |
|     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               = (UINT32) (SectorCount * BlockSize);
 | |
| 
 | |
|     Status = AtapiPacketCommandIn (
 | |
|               AtapiBlkIoDev,
 | |
|               DevicePosition,
 | |
|               &Packet,
 | |
|               (UINT16 *) PtrBuffer,
 | |
|               ByteCount,
 | |
|               ATAPILONGTIMEOUT
 | |
|               );
 | |
|     if (Status != EFI_SUCCESS) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     Lba32 += SectorCount;
 | |
|     PtrBuffer = (UINT8 *) PtrBuffer + SectorCount * BlockSize;
 | |
|     BlocksRemaining -= SectorCount;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**  
 | |
|   Check if there is media according to sense data.
 | |
| 
 | |
|   @param[in]  SenseData   Pointer to sense data.
 | |
|   @param[in]  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             IsNoMedia;
 | |
| 
 | |
|   IsNoMedia = FALSE;
 | |
| 
 | |
|   SensePtr  = SenseData;
 | |
| 
 | |
|   for (Index = 0; Index < SenseCounts; Index++) {
 | |
| 
 | |
|     if ((SensePtr->sense_key == ATA_SK_NOT_READY) && (SensePtr->addnl_sense_code == ATA_ASC_NO_MEDIA)) {
 | |
|       IsNoMedia = TRUE;
 | |
|     }
 | |
| 
 | |
|     SensePtr++;
 | |
|   }
 | |
| 
 | |
|   return IsNoMedia;
 | |
| }
 | |
| 
 | |
| /**  
 | |
|   Check if device state is unclear according to sense data.
 | |
| 
 | |
|   @param[in]  SenseData   Pointer to sense data.
 | |
|   @param[in]  SenseCounts Count of sense data.
 | |
| 
 | |
|   @retval TRUE    Device state is unclear
 | |
|   @retval FALSE   Device state is clear  
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| IsDeviceStateUnclear (
 | |
|   IN  ATAPI_REQUEST_SENSE_DATA    *SenseData,
 | |
|   IN  UINTN                       SenseCounts
 | |
|   )
 | |
| {
 | |
|   ATAPI_REQUEST_SENSE_DATA  *SensePtr;
 | |
|   UINTN               Index;
 | |
|   BOOLEAN             Unclear;
 | |
| 
 | |
|   Unclear  = FALSE;
 | |
| 
 | |
|   SensePtr  = SenseData;
 | |
| 
 | |
|   for (Index = 0; Index < SenseCounts; Index++) {
 | |
| 
 | |
|     if (SensePtr->sense_key == 0x06) {
 | |
|       //
 | |
|       // Sense key is 0x06 means the device is just be reset or media just
 | |
|       // changed. The current state of the device is unclear.
 | |
|       //
 | |
|       Unclear = TRUE;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     SensePtr++;
 | |
|   }
 | |
| 
 | |
|   return Unclear;
 | |
| }
 | |
| 
 | |
| /**  
 | |
|   Check if there is media error according to sense data.
 | |
| 
 | |
|   @param[in]  SenseData   Pointer to sense data.
 | |
|   @param[in]  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             IsError;
 | |
| 
 | |
|   IsError   = FALSE;
 | |
| 
 | |
|   SensePtr  = SenseData;
 | |
| 
 | |
|   for (Index = 0; Index < SenseCounts; Index++) {
 | |
| 
 | |
|     switch (SensePtr->sense_key) {
 | |
| 
 | |
|     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:
 | |
|         IsError = TRUE;
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case ATA_SK_NOT_READY:
 | |
|       switch (SensePtr->addnl_sense_code) {
 | |
|       case ATA_ASC_MEDIA_UPSIDE_DOWN:
 | |
|         IsError = TRUE;
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         break;
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     SensePtr++;
 | |
|   }
 | |
| 
 | |
|   return IsError;
 | |
| }
 | |
| 
 | |
| /**  
 | |
|   Check if drive is ready according to sense data.
 | |
| 
 | |
|   @param[in]  SenseData   Pointer to sense data.
 | |
|   @param[in]  SenseCounts Count of sense data.
 | |
|   @param[out] NeedRetry   Indicate if retry is needed.
 | |
| 
 | |
|   @retval TRUE    Drive ready
 | |
|   @retval FALSE   Drive not ready
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| IsDriveReady (
 | |
|   IN  ATAPI_REQUEST_SENSE_DATA    *SenseData,
 | |
|   IN  UINTN                       SenseCounts,
 | |
|   OUT BOOLEAN                     *NeedRetry
 | |
|   )
 | |
| {
 | |
|   ATAPI_REQUEST_SENSE_DATA  *SensePtr;
 | |
|   UINTN               Index;
 | |
|   BOOLEAN             IsReady;
 | |
| 
 | |
|   IsReady     = TRUE;
 | |
|   *NeedRetry  = FALSE;
 | |
| 
 | |
|   SensePtr    = SenseData;
 | |
| 
 | |
|   for (Index = 0; Index < SenseCounts; Index++) {
 | |
| 
 | |
|     switch (SensePtr->sense_key) {
 | |
| 
 | |
|     case ATA_SK_NOT_READY:
 | |
|       switch (SensePtr->addnl_sense_code) {
 | |
|       case ATA_ASC_NOT_READY:
 | |
|         switch (SensePtr->addnl_sense_code_qualifier) {
 | |
|         case ATA_ASCQ_IN_PROGRESS:
 | |
|           IsReady     = FALSE;
 | |
|           *NeedRetry  = TRUE;
 | |
|           break;
 | |
| 
 | |
|         default:
 | |
|           IsReady     = FALSE;
 | |
|           *NeedRetry  = FALSE;
 | |
|           break;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         break;
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     SensePtr++;
 | |
|   }
 | |
| 
 | |
|   return IsReady;
 | |
| }
 |