In function AtapiReadCapacity(), the following expression: IdeDev->BlkIo.Media->LastBlock = (Data.LastLba3 << 24) | (Data.LastLba2 << 16) | (Data.LastLba1 << 8) | Data.LastLba0; (There is also a similar case in this function.) will involve undefined behavior in signed left shift operations. Since Data.LastLbaX is of type UINT8, and IdeDev->BlkIo.Media->LastBlock is of type UINT64. Therefore, Data.LastLbaX will be promoted to int (32 bits, signed) first, and then perform the left shift operation. According to the C11 spec, Section 6.5.7: 4 The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 * 2^E2 , reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 * 2^E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined. So if bit 7 of Data.LastLba3 is 1, (Data.LastLba3 << 24) will be out of the range within int type. The undefined behavior of the signed left shift will lead to a potential of setting the high 32 bits of IdeDev->BlkIo.Media->LastBlock to 1 during the cast from type int to type UINT64. This commit will add an explicit UINT32 type cast for Data.LastLba3 to resolve this issue. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Feng Tian <feng.tian@intel.com>
		
			
				
	
	
		
			1953 lines
		
	
	
		
			55 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1953 lines
		
	
	
		
			55 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|    This file contains all helper functions on the ATAPI command 
 | |
|   
 | |
|   Copyright (c) 2006 - 2017, 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 "IdeBus.h"
 | |
| 
 | |
| /**
 | |
|   This function is used to get the current status of the media residing
 | |
|   in the LS-120 drive or ZIP drive. The media status is returned in the 
 | |
|   Error Status.
 | |
| 
 | |
|   @param IdeDev   pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                   to record all the information of the IDE device.
 | |
| 
 | |
|   @retval EFI_SUCCESS         The media status is achieved successfully and the media
 | |
|                               can be read/written.
 | |
|   @retval EFI_DEVICE_ERROR    Get Media Status Command is failed.
 | |
|   @retval EFI_NO_MEDIA        There is no media in the drive.
 | |
|   @retval EFI_WRITE_PROTECTED The media is writing protected.
 | |
| 
 | |
|   @note  This function must be called after the LS120EnableMediaStatus() 
 | |
|          with second parameter set to TRUE 
 | |
|          (means enable media status notification) is called.
 | |
| **/
 | |
| EFI_STATUS
 | |
| LS120GetMediaStatus (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev
 | |
|   )
 | |
| {
 | |
|   UINT8       DeviceSelect;
 | |
|   UINT8       StatusValue;
 | |
|   EFI_STATUS  EfiStatus;
 | |
|   //
 | |
|   // Poll Alternate Register for BSY clear within timeout.
 | |
|   //
 | |
|   EfiStatus = WaitForBSYClear2 (IdeDev, ATATIMEOUT);
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Select device via Device/Head Register.
 | |
|   //
 | |
|   DeviceSelect = (UINT8) ((IdeDev->Device) << 4 | 0xe0);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, DeviceSelect);
 | |
| 
 | |
|   //
 | |
|   // Poll Alternate Register for DRDY set within timeout.
 | |
|   // After device is selected, DRDY set indicates the device is ready to
 | |
|   // accept command.
 | |
|   //
 | |
|   EfiStatus = DRDYReady2 (IdeDev, ATATIMEOUT);
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get Media Status Command is sent
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, 0xDA);
 | |
| 
 | |
|   //
 | |
|   // BSY bit will clear after command is complete.
 | |
|   //
 | |
|   EfiStatus = WaitForBSYClear2 (IdeDev, ATATIMEOUT);
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // the media status is returned by the command in the ERROR register
 | |
|   //
 | |
|   StatusValue = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
 | |
| 
 | |
|   if ((StatusValue & BIT1) != 0) {
 | |
|     return EFI_NO_MEDIA;
 | |
|   }
 | |
| 
 | |
|   if ((StatusValue & BIT6) != 0) {
 | |
|     return EFI_WRITE_PROTECTED;
 | |
|   } else {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| }
 | |
| /**
 | |
|   This function is used to send Enable Media Status Notification Command
 | |
|   or Disable Media Status Notification Command.
 | |
| 
 | |
|   @param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                 to record all the information of the IDE device.
 | |
| 
 | |
|   @param Enable a flag that indicates whether enable or disable media
 | |
|                 status notification.
 | |
|   @retval EFI_SUCCESS      If command completes successfully.
 | |
|   @retval EFI_DEVICE_ERROR If command failed.
 | |
| **/
 | |
| EFI_STATUS
 | |
| LS120EnableMediaStatus (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  BOOLEAN         Enable
 | |
|   )
 | |
| {
 | |
|   UINT8       DeviceSelect;
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   //
 | |
|   // Poll Alternate Register for BSY clear within timeout.
 | |
|   //
 | |
|   Status = WaitForBSYClear2 (IdeDev, ATATIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Select device via Device/Head Register.
 | |
|   //
 | |
|   DeviceSelect = (UINT8) ((IdeDev->Device) << 4 | 0xe0);
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, DeviceSelect);
 | |
| 
 | |
|   //
 | |
|   // Poll Alternate Register for DRDY set within timeout.
 | |
|   // After device is selected, DRDY set indicates the device is ready to
 | |
|   // accept command.
 | |
|   //
 | |
|   Status = DRDYReady2 (IdeDev, ATATIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (Enable) {
 | |
|     //
 | |
|     // 0x95: Enable media status notification
 | |
|     //
 | |
|     IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x95);
 | |
|   } else {
 | |
|     //
 | |
|     // 0x31: Disable media status notification
 | |
|     //
 | |
|     IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x31);
 | |
|   }
 | |
|   //
 | |
|   // Set Feature Command is sent
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, 0xEF);
 | |
| 
 | |
|   //
 | |
|   // BSY bit will clear after command is complete.
 | |
|   //
 | |
|   Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| /**
 | |
|   This function reads the pending data in the device.
 | |
| 
 | |
|   @param IdeDev   Indicates the calling context.
 | |
| 
 | |
|   @retval EFI_SUCCESS   Successfully read.
 | |
|   @retval EFI_NOT_READY The BSY is set avoiding reading.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiReadPendingData (
 | |
|   IN IDE_BLK_IO_DEV     *IdeDev
 | |
|   )
 | |
| {
 | |
|   UINT8     AltRegister;
 | |
|   UINT16    TempWordBuffer;
 | |
| 
 | |
|   AltRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus);
 | |
|   if ((AltRegister & ATA_STSREG_BSY) == ATA_STSREG_BSY) {
 | |
|     return EFI_NOT_READY;
 | |
|   }
 | |
|   if ((AltRegister & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {
 | |
|     TempWordBuffer = IDEReadPortB (IdeDev->PciIo,IdeDev->IoPort->Alt.AltStatus);
 | |
|     while ((TempWordBuffer & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {
 | |
|       IDEReadPortWMultiple (
 | |
|         IdeDev->PciIo,
 | |
|         IdeDev->IoPort->Data, 
 | |
|         1, 
 | |
|         &TempWordBuffer
 | |
|         );
 | |
|       TempWordBuffer = IDEReadPortB (IdeDev->PciIo,IdeDev->IoPort->Alt.AltStatus);
 | |
|     }
 | |
|   }
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function is called by either AtapiPacketCommandIn() or AtapiPacketCommandOut(). 
 | |
|   It is used to transfer data between host and device. The data direction is specified
 | |
|   by the fourth parameter.
 | |
| 
 | |
|   @param IdeDev     pointer pointing to IDE_BLK_IO_DEV data structure, used to record
 | |
|                     all the information of the IDE device.
 | |
|   @param Buffer     buffer contained data transferred between host and device.
 | |
|   @param ByteCount  data size in byte unit of the buffer.
 | |
|   @param Read       flag used to determine the data transfer direction.
 | |
|                     Read equals 1, means data transferred from device to host;
 | |
|                     Read equals 0, means data transferred from host to device.
 | |
|   @param TimeOut    timeout value for wait DRQ ready before each data stream's transfer.
 | |
| 
 | |
|   @retval EFI_SUCCESS      data is transferred successfully.
 | |
|   @retval EFI_DEVICE_ERROR the device failed to transfer data.
 | |
| **/
 | |
| EFI_STATUS
 | |
| PioReadWriteData (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  UINT16          *Buffer,
 | |
|   IN  UINT32          ByteCount,
 | |
|   IN  BOOLEAN         Read,
 | |
|   IN  UINTN           TimeOut
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // required transfer data in word unit.
 | |
|   //
 | |
|   UINT32      RequiredWordCount;
 | |
| 
 | |
|   //
 | |
|   // actual transfer data in word unit.
 | |
|   //
 | |
|   UINT32      ActualWordCount;
 | |
|   UINT32      WordCount;
 | |
|   EFI_STATUS  Status;
 | |
|   UINT16      *PtrBuffer;
 | |
| 
 | |
|   //
 | |
|   // No data transfer is premitted.
 | |
|   //
 | |
|   if (ByteCount == 0) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
|   //
 | |
|   // for performance, we assert the ByteCount is an even number
 | |
|   // which is actually a resonable assumption  
 | |
|   ASSERT((ByteCount%2) == 0);
 | |
|   
 | |
|   PtrBuffer         = Buffer;
 | |
|   RequiredWordCount = ByteCount / 2;
 | |
|   //
 | |
|   // ActuralWordCount means the word count of data really transferred.
 | |
|   //
 | |
|   ActualWordCount = 0;
 | |
| 
 | |
|   while (ActualWordCount < RequiredWordCount) {
 | |
|     
 | |
|     //
 | |
|     // before each data transfer stream, the host should poll DRQ bit ready,
 | |
|     // to see whether indicates device is ready to transfer data.
 | |
|     //
 | |
|     Status = DRQReady2 (IdeDev, TimeOut);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return CheckErrorStatus (IdeDev);
 | |
|     }
 | |
|     
 | |
|     //
 | |
|     // read Status Register will clear interrupt
 | |
|     //
 | |
|     IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
 | |
| 
 | |
|     //
 | |
|     // get current data transfer size from Cylinder Registers.
 | |
|     //
 | |
|     WordCount = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb) << 8;
 | |
|     WordCount = WordCount | IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb);
 | |
|     WordCount = WordCount & 0xffff;
 | |
|     WordCount /= 2;
 | |
| 
 | |
|     WordCount = MIN (WordCount, (RequiredWordCount - ActualWordCount));
 | |
| 
 | |
|     if (Read) {
 | |
|       IDEReadPortWMultiple (
 | |
|         IdeDev->PciIo,
 | |
|         IdeDev->IoPort->Data,
 | |
|         WordCount,
 | |
|         PtrBuffer
 | |
|         );
 | |
|     } else {
 | |
|       IDEWritePortWMultiple (
 | |
|         IdeDev->PciIo,
 | |
|         IdeDev->IoPort->Data,
 | |
|         WordCount,
 | |
|         PtrBuffer
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     PtrBuffer += WordCount;
 | |
|     ActualWordCount += WordCount;
 | |
|   }
 | |
|   
 | |
|   if (Read) {
 | |
|     //
 | |
|     // In the case where the drive wants to send more data than we need to read,
 | |
|     // the DRQ bit will be set and cause delays from DRQClear2().
 | |
|     // We need to read data from the drive until it clears DRQ so we can move on.
 | |
|     //
 | |
|     AtapiReadPendingData (IdeDev);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // After data transfer is completed, normally, DRQ bit should clear.
 | |
|   //
 | |
|   Status = DRQClear2 (IdeDev, ATAPITIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // read status register to check whether error happens.
 | |
|   //
 | |
|   return CheckErrorStatus (IdeDev);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function is used to send out ATAPI commands conforms to the Packet Command 
 | |
|   with PIO Data In Protocol.
 | |
| 
 | |
|   @param IdeDev    pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                    to record all the information of the IDE device.
 | |
|   @param Packet    pointer pointing to ATAPI_PACKET_COMMAND data structure
 | |
|                    which contains the contents of the command.     
 | |
|   @param Buffer    buffer contained data transferred from device to host.
 | |
|   @param ByteCount data size in byte unit of the buffer.
 | |
|   @param TimeOut   this parameter is used to specify the timeout value for the 
 | |
|                    PioReadWriteData() function. 
 | |
| 
 | |
|   @retval EFI_SUCCESS       send out the ATAPI packet command successfully
 | |
|                             and device sends data successfully.
 | |
|   @retval EFI_DEVICE_ERROR  the device failed to send data.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiPacketCommandIn (
 | |
|   IN  IDE_BLK_IO_DEV        *IdeDev,
 | |
|   IN  ATAPI_PACKET_COMMAND  *Packet,
 | |
|   IN  UINT16                *Buffer,
 | |
|   IN  UINT32                ByteCount,
 | |
|   IN  UINTN                 TimeOut
 | |
|   )
 | |
| {
 | |
|   UINT16      *CommandIndex;
 | |
|   EFI_STATUS  Status;
 | |
|   UINT32      Count;
 | |
| 
 | |
|   //
 | |
|   // Set all the command parameters by fill related registers.
 | |
|   // Before write to all the following registers, BSY and DRQ must be 0.
 | |
|   //
 | |
|   Status = DRQClear2 (IdeDev, ATAPITIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Select device via Device/Head Register.
 | |
|   //
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->Head,
 | |
|     (UINT8) ((IdeDev->Device << 4) | ATA_DEFAULT_CMD)  // DEFAULT_CMD: 0xa0 (1010,0000)
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   // No OVL; No DMA
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x00);
 | |
| 
 | |
|   //
 | |
|   // set the transfersize to ATAPI_MAX_BYTE_COUNT to let the device
 | |
|   // determine how many data should be transferred.
 | |
|   //
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->CylinderLsb,
 | |
|     (UINT8) (ATAPI_MAX_BYTE_COUNT & 0x00ff)
 | |
|     );
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->CylinderMsb,
 | |
|     (UINT8) (ATAPI_MAX_BYTE_COUNT >> 8)
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   //  ATA_DEFAULT_CTL:0x0a (0000,1010)
 | |
|   //  Disable interrupt
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, ATA_DEFAULT_CTL);
 | |
| 
 | |
|   //
 | |
|   // Send Packet command to inform device
 | |
|   // that the following data bytes are command packet.
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, ATA_CMD_PACKET);
 | |
| 
 | |
|   Status = DRQReady (IdeDev, ATAPITIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Send out command packet
 | |
|   //
 | |
|   CommandIndex = Packet->Data16;
 | |
|   for (Count = 0; Count < 6; Count++, CommandIndex++) {
 | |
| 
 | |
|     IDEWritePortW (IdeDev->PciIo, IdeDev->IoPort->Data, *CommandIndex);
 | |
|     gBS->Stall (10);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // call PioReadWriteData() function to get
 | |
|   // requested transfer data form device.
 | |
|   //
 | |
|   return PioReadWriteData (IdeDev, Buffer, ByteCount, 1, TimeOut);
 | |
| }
 | |
| /**
 | |
|   This function is used to send out ATAPI commands conforms to the Packet Command
 | |
|   with PIO Data Out Protocol.
 | |
| 
 | |
|   @param IdeDev      pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                      to record all the information of the IDE device.
 | |
|   @param Packet      pointer pointing to ATAPI_PACKET_COMMAND data structure
 | |
|                      which contains the contents of the command.
 | |
|   @param Buffer      buffer contained data transferred from host to device.
 | |
|   @param ByteCount   data size in byte unit of the buffer.
 | |
|   @param TimeOut     this parameter is used to specify the timeout value 
 | |
|                      for the PioReadWriteData() function. 
 | |
|   @retval EFI_SUCCESS      send out the ATAPI packet command successfully
 | |
|                            and device received data successfully.  
 | |
|   @retval EFI_DEVICE_ERROR the device failed to send data.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiPacketCommandOut (
 | |
|   IN  IDE_BLK_IO_DEV        *IdeDev,
 | |
|   IN  ATAPI_PACKET_COMMAND  *Packet,
 | |
|   IN  UINT16                *Buffer,
 | |
|   IN  UINT32                ByteCount,
 | |
|   IN  UINTN                 TimeOut
 | |
|   )
 | |
| {
 | |
|   UINT16      *CommandIndex;
 | |
|   EFI_STATUS  Status;
 | |
|   UINT32      Count;
 | |
| 
 | |
|   //
 | |
|   // set all the command parameters
 | |
|   // Before write to all the following registers, BSY and DRQ must be 0.
 | |
|   //
 | |
|   Status = DRQClear2 (IdeDev, ATAPITIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // Select device via Device/Head Register.
 | |
|   //
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->Head,
 | |
|     (UINT8) ((IdeDev->Device << 4) | ATA_DEFAULT_CMD)   // ATA_DEFAULT_CMD: 0xa0 (1010,0000)
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   // No OVL; No DMA
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x00);
 | |
| 
 | |
|   //
 | |
|   // set the transfersize to ATAPI_MAX_BYTE_COUNT to
 | |
|   // let the device determine how many data should be transferred.
 | |
|   //
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->CylinderLsb,
 | |
|     (UINT8) (ATAPI_MAX_BYTE_COUNT & 0x00ff)
 | |
|     );
 | |
|   IDEWritePortB (
 | |
|     IdeDev->PciIo,
 | |
|     IdeDev->IoPort->CylinderMsb,
 | |
|     (UINT8) (ATAPI_MAX_BYTE_COUNT >> 8)
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   //  DEFAULT_CTL:0x0a (0000,1010)
 | |
|   //  Disable interrupt
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, ATA_DEFAULT_CTL);
 | |
| 
 | |
|   //
 | |
|   // Send Packet command to inform device
 | |
|   // that the following data bytes are command packet.
 | |
|   //
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, ATA_CMD_PACKET);
 | |
| 
 | |
|   Status = DRQReady2 (IdeDev, ATAPITIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Send out command packet
 | |
|   //
 | |
|   CommandIndex = Packet->Data16;
 | |
|   for (Count = 0; Count < 6; Count++, CommandIndex++) {
 | |
|     IDEWritePortW (IdeDev->PciIo, IdeDev->IoPort->Data, *CommandIndex);
 | |
|     gBS->Stall (10);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // call PioReadWriteData() function to send requested transfer data to device.
 | |
|   //
 | |
|   return PioReadWriteData (IdeDev, Buffer, ByteCount, 0, TimeOut);
 | |
| }
 | |
| /**
 | |
|   Sends out ATAPI Inquiry Packet Command to the specified device. This command will
 | |
|   return INQUIRY data of the device.
 | |
| 
 | |
|   @param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                 to record all the information of the IDE device.
 | |
| 
 | |
|   @retval EFI_SUCCESS       Inquiry command completes successfully.
 | |
|   @retval EFI_DEVICE_ERROR  Inquiry command failed.
 | |
| 
 | |
|   @note  Parameter "IdeDev" will be updated in this function.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiInquiry (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev
 | |
|   )
 | |
| {
 | |
|   ATAPI_PACKET_COMMAND  Packet;
 | |
|   EFI_STATUS            Status;
 | |
|   ATAPI_INQUIRY_DATA          *InquiryData;
 | |
| 
 | |
|   //
 | |
|   // prepare command packet for the ATAPI Inquiry Packet Command.
 | |
|   //
 | |
|   ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
 | |
|   Packet.Inquiry.opcode             = ATA_CMD_INQUIRY;
 | |
|   Packet.Inquiry.page_code          = 0;
 | |
|   Packet.Inquiry.allocation_length  = (UINT8) sizeof (ATAPI_INQUIRY_DATA);
 | |
| 
 | |
|   InquiryData                       = AllocatePool (sizeof (ATAPI_INQUIRY_DATA));
 | |
|   if (InquiryData == NULL) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Send command packet and get requested Inquiry data.
 | |
|   //
 | |
|   Status = AtapiPacketCommandIn (
 | |
|             IdeDev,
 | |
|             &Packet,
 | |
|             (UINT16 *) InquiryData,
 | |
|             sizeof (ATAPI_INQUIRY_DATA),
 | |
|             ATAPITIMEOUT
 | |
|             );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     gBS->FreePool (InquiryData);
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   IdeDev->InquiryData = InquiryData;
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| /**
 | |
|   This function is called by DiscoverIdeDevice() during its device
 | |
|   identification.
 | |
|   Its main purpose is to get enough information for the device media
 | |
|   to fill in the Media data structure of the Block I/O Protocol interface.
 | |
| 
 | |
|   There are 5 steps to reach such objective:
 | |
|   1. Sends out the ATAPI Identify Command to the specified device. 
 | |
|   Only ATAPI device responses to this command. If the command succeeds,
 | |
|   it returns the Identify data structure which filled with information 
 | |
|   about the device. Since the ATAPI device contains removable media, 
 | |
|   the only meaningful information is the device module name.
 | |
|   2. Sends out ATAPI Inquiry Packet Command to the specified device.
 | |
|   This command will return inquiry data of the device, which contains
 | |
|   the device type information.
 | |
|   3. Allocate sense data space for future use. We don't detect the media
 | |
|   presence here to improvement boot performance, especially when CD 
 | |
|   media is present. The media detection will be performed just before
 | |
|   each BLK_IO read/write
 | |
|   
 | |
|   @param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                  to record all the information of the IDE device.
 | |
| 
 | |
|   @retval EFI_SUCCESS       Identify ATAPI device successfully.
 | |
|   @retval EFI_DEVICE_ERROR  ATAPI Identify Device Command failed or device type
 | |
|                             is not supported by this IDE driver.
 | |
|   @retval EFI_OUT_OF_RESOURCES Allocate memory for sense data failed 
 | |
| 
 | |
|   @note   Parameter "IdeDev" will be updated in this function.
 | |
| **/
 | |
| EFI_STATUS
 | |
| ATAPIIdentify (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev
 | |
|   )
 | |
| {
 | |
|   EFI_IDENTIFY_DATA *AtapiIdentifyPointer;
 | |
|   UINT8             DeviceSelect;
 | |
|   EFI_STATUS        Status;
 | |
| 
 | |
|   //
 | |
|   // device select bit
 | |
|   //
 | |
|   DeviceSelect          = (UINT8) ((IdeDev->Device) << 4);
 | |
| 
 | |
|   AtapiIdentifyPointer  = AllocatePool (sizeof (EFI_IDENTIFY_DATA));
 | |
|   if (AtapiIdentifyPointer == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
|   //
 | |
|   // Send ATAPI Identify Command to get IDENTIFY data.
 | |
|   //
 | |
|   Status = AtaPioDataIn (
 | |
|             IdeDev,
 | |
|             (VOID *) AtapiIdentifyPointer,
 | |
|             sizeof (EFI_IDENTIFY_DATA),
 | |
|             ATA_CMD_IDENTIFY_DEVICE,
 | |
|             DeviceSelect,
 | |
|             0,
 | |
|             0,
 | |
|             0,
 | |
|             0
 | |
|             );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     gBS->FreePool (AtapiIdentifyPointer);
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   IdeDev->IdData = AtapiIdentifyPointer;
 | |
|   PrintAtaModuleName (IdeDev);
 | |
| 
 | |
|   //
 | |
|   // Send ATAPI Inquiry Packet Command to get INQUIRY data.
 | |
|   //
 | |
|   Status = AtapiInquiry (IdeDev);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     gBS->FreePool (IdeDev->IdData);
 | |
|     //
 | |
|     // Make sure the pIdData will not be freed again.
 | |
|     //
 | |
|     IdeDev->IdData = NULL;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   //
 | |
|   // Get media removable info from INQUIRY data.
 | |
|   //
 | |
|   IdeDev->BlkIo.Media->RemovableMedia = (UINT8) ((IdeDev->InquiryData->RMB & 0x80) == 0x80);
 | |
| 
 | |
|   //
 | |
|   // Identify device type via INQUIRY data.
 | |
|   //
 | |
|   switch (IdeDev->InquiryData->peripheral_type & 0x1f) {
 | |
| 
 | |
|   //
 | |
|   // Magnetic Disk
 | |
|   //
 | |
|   case 0x00:
 | |
| 
 | |
|     //
 | |
|     // device is LS120 or ZIP drive.
 | |
|     //
 | |
|     IdeDev->Type = IdeMagnetic;
 | |
| 
 | |
|     IdeDev->BlkIo.Media->MediaId      = 0;
 | |
|     //
 | |
|     // Give initial value
 | |
|     //
 | |
|     IdeDev->BlkIo.Media->MediaPresent = FALSE;
 | |
| 
 | |
|     IdeDev->BlkIo.Media->LastBlock  = 0;
 | |
|     IdeDev->BlkIo.Media->BlockSize  = 0x200;
 | |
|     break;
 | |
| 
 | |
|   //
 | |
|   // CD-ROM
 | |
|   //
 | |
|   case 0x05:
 | |
| 
 | |
|     IdeDev->Type                      = IdeCdRom;
 | |
|     IdeDev->BlkIo.Media->MediaId      = 0;
 | |
|     //
 | |
|     // Give initial value
 | |
|     //
 | |
|     IdeDev->BlkIo.Media->MediaPresent = FALSE;
 | |
| 
 | |
|     IdeDev->BlkIo.Media->LastBlock  = 0;
 | |
|     IdeDev->BlkIo.Media->BlockSize  = 0x800;
 | |
|     IdeDev->BlkIo.Media->ReadOnly   = TRUE;
 | |
|     break;
 | |
| 
 | |
|   //
 | |
|   // Tape
 | |
|   //
 | |
|   case 0x01:
 | |
| 
 | |
|   //
 | |
|   // WORM
 | |
|   //
 | |
|   case 0x04:
 | |
|   
 | |
|   //
 | |
|   // Optical
 | |
|   //
 | |
|   case 0x07:
 | |
| 
 | |
|   default:
 | |
|     IdeDev->Type = IdeUnknown;
 | |
|     gBS->FreePool (IdeDev->IdData);
 | |
|     gBS->FreePool (IdeDev->InquiryData);
 | |
|     //
 | |
|     // Make sure the pIdData and pInquiryData will not be freed again.
 | |
|     //
 | |
|     IdeDev->IdData       = NULL;
 | |
|     IdeDev->InquiryData  = NULL;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // original sense data numbers
 | |
|   //
 | |
|   IdeDev->SenseDataNumber = 20;
 | |
| 
 | |
|   IdeDev->SenseData = AllocatePool (IdeDev->SenseDataNumber * sizeof (ATAPI_REQUEST_SENSE_DATA));
 | |
|   if (IdeDev->SenseData == NULL) {
 | |
|     gBS->FreePool (IdeDev->IdData);
 | |
|     gBS->FreePool (IdeDev->InquiryData);
 | |
|     //
 | |
|     // Make sure the pIdData and pInquiryData will not be freed again.
 | |
|     //
 | |
|     IdeDev->IdData       = NULL;
 | |
|     IdeDev->InquiryData  = NULL;
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| /**
 | |
|   Sends out ATAPI Request Sense Packet Command to the specified device. This command
 | |
|   will return all the current Sense data in the device.  This function will pack 
 | |
|   all the Sense data in one single buffer.
 | |
| 
 | |
|   @param IdeDev       pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                       to record all the information of the IDE device.
 | |
|   @param SenseCounts  allocated in this function, and freed by the calling function.
 | |
|                       This buffer is used to accommodate all the sense data returned 
 | |
|                       by the device.
 | |
| 
 | |
|   @retval EFI_SUCCESS      Request Sense command completes successfully.
 | |
|   @retval EFI_DEVICE_ERROR Request Sense command failed.
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiRequestSense (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   OUT UINTN           *SenseCounts
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS            Status;
 | |
|   ATAPI_REQUEST_SENSE_DATA    *Sense;
 | |
|   UINT16                *Ptr;
 | |
|   BOOLEAN               FetchSenseData;
 | |
|   ATAPI_PACKET_COMMAND  Packet;
 | |
| 
 | |
|   *SenseCounts = 0;
 | |
| 
 | |
|   ZeroMem (IdeDev->SenseData, sizeof (ATAPI_REQUEST_SENSE_DATA) * (IdeDev->SenseDataNumber));
 | |
|   //
 | |
|   // 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);
 | |
| 
 | |
|   //
 | |
|   // initialize pointer
 | |
|   //
 | |
|   Ptr = (UINT16 *) IdeDev->SenseData;
 | |
|   //
 | |
|   //  request sense data from device continuously until no sense data
 | |
|   //  exists in the device.
 | |
|   //
 | |
|   for (FetchSenseData = TRUE; FetchSenseData;) {
 | |
| 
 | |
|     Sense = (ATAPI_REQUEST_SENSE_DATA *) Ptr;
 | |
| 
 | |
|     //
 | |
|     // send out Request Sense Packet Command and get one Sense data form device
 | |
|     //
 | |
|     Status = AtapiPacketCommandIn (
 | |
|               IdeDev,
 | |
|               &Packet,
 | |
|               Ptr,
 | |
|               sizeof (ATAPI_REQUEST_SENSE_DATA),
 | |
|               ATAPITIMEOUT
 | |
|               );
 | |
|     //
 | |
|     // failed to get Sense data
 | |
|     //
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       if (*SenseCounts == 0) {
 | |
|         return EFI_DEVICE_ERROR;
 | |
|       } else {
 | |
|         return EFI_SUCCESS;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     (*SenseCounts)++;
 | |
|     //
 | |
|     // 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 is word-based pointer
 | |
|       //
 | |
|       Ptr += (sizeof (ATAPI_REQUEST_SENSE_DATA) + 1) >> 1;
 | |
| 
 | |
|     } else {
 | |
|       //
 | |
|       // when no sense key, skip out the loop
 | |
|       //
 | |
|       FetchSenseData = FALSE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| /**
 | |
|   This function is used to parse sense data. Only the first sense data is honoured
 | |
|   
 | |
|   @param IdeDev     Indicates the calling context.
 | |
|   @param SenseCount Count of sense data.
 | |
|   @param Result    The parsed result.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Successfully parsed.
 | |
|   @retval EFI_INVALID_PARAMETER Count of sense data is zero.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ParseSenseData (
 | |
|   IN IDE_BLK_IO_DEV     *IdeDev,
 | |
|   IN UINTN              SenseCount,
 | |
|   OUT SENSE_RESULT      *Result
 | |
|   )
 | |
| {
 | |
|   ATAPI_REQUEST_SENSE_DATA      *SenseData;
 | |
| 
 | |
|   if (SenseCount == 0) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Only use the first sense data
 | |
|   //
 | |
|   SenseData = IdeDev->SenseData;
 | |
|   *Result   = SenseOtherSense;
 | |
| 
 | |
|   switch (SenseData->sense_key) {
 | |
|   case ATA_SK_NO_SENSE:
 | |
|     *Result = SenseNoSenseKey;
 | |
|     break;
 | |
|   case ATA_SK_NOT_READY:
 | |
|     switch (SenseData->addnl_sense_code) {
 | |
|     case ATA_ASC_NO_MEDIA:
 | |
|       *Result = SenseNoMedia;
 | |
|       break;
 | |
|     case ATA_ASC_MEDIA_UPSIDE_DOWN:
 | |
|       *Result = SenseMediaError;
 | |
|       break;
 | |
|     case ATA_ASC_NOT_READY:
 | |
|       if (SenseData->addnl_sense_code_qualifier == ATA_ASCQ_IN_PROGRESS) {
 | |
|         *Result = SenseDeviceNotReadyNeedRetry;
 | |
|       } else {
 | |
|         *Result = SenseDeviceNotReadyNoRetry;
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     break;
 | |
|   case ATA_SK_UNIT_ATTENTION:
 | |
|     if (SenseData->addnl_sense_code == ATA_ASC_MEDIA_CHANGE) {
 | |
|       *Result = SenseMediaChange;
 | |
|     }
 | |
|     break;
 | |
|   case ATA_SK_MEDIUM_ERROR:
 | |
|     switch (SenseData->addnl_sense_code) {
 | |
|     case ATA_ASC_MEDIA_ERR1:
 | |
|     case ATA_ASC_MEDIA_ERR2:
 | |
|     case ATA_ASC_MEDIA_ERR3:
 | |
|     case ATA_ASC_MEDIA_ERR4:
 | |
|       *Result = SenseMediaError;
 | |
|       break;
 | |
|     }
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Sends out ATAPI Test Unit Ready Packet Command to the specified device
 | |
|   to find out whether device is accessible.
 | |
| 
 | |
|   @param IdeDev    Pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                    to record all the information of the IDE device.
 | |
|   @param SResult   Sense result for this packet command.
 | |
| 
 | |
|   @retval EFI_SUCCESS      Device is accessible.
 | |
|   @retval EFI_DEVICE_ERROR Device is not accessible.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiTestUnitReady (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   OUT SENSE_RESULT    *SResult  
 | |
|   )
 | |
| {
 | |
|   ATAPI_PACKET_COMMAND  Packet;
 | |
|   EFI_STATUS            Status;
 | |
|   UINTN 				SenseCount;
 | |
| 
 | |
|   //
 | |
|   // fill command packet
 | |
|   //
 | |
|   ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
 | |
|   Packet.TestUnitReady.opcode = ATA_CMD_TEST_UNIT_READY;
 | |
| 
 | |
|   //
 | |
|   // send command packet
 | |
|   //
 | |
|   Status = AtapiPacketCommandIn (IdeDev, &Packet, NULL, 0, ATAPITIMEOUT);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = AtapiRequestSense (IdeDev, &SenseCount);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   ParseSenseData (IdeDev, SenseCount, SResult);
 | |
|   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.
 | |
| 
 | |
|   Current device status will impact device's response to the Read Capacity
 | |
|   Command. For example, if the device once reset, the Read Capacity
 | |
|   Command will fail. The Sense data record the current device status, so 
 | |
|   if the Read Capacity Command failed, the Sense data must be requested
 | |
|   and be analyzed to determine if the Read Capacity Command should retry.
 | |
| 
 | |
|   @param IdeDev    Pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                    to record all the information of the IDE device.
 | |
|   @param SResult   Sense result for this packet command
 | |
| 
 | |
|   @retval EFI_SUCCESS      Read Capacity Command finally completes successfully.
 | |
|   @retval EFI_DEVICE_ERROR Read Capacity Command failed because of device error.
 | |
|   @retval EFI_NOT_READY    Operation succeeds but returned capacity is 0
 | |
| 
 | |
|   @note Parameter "IdeDev" will be updated in this function.
 | |
| 
 | |
|   
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiReadCapacity (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   OUT SENSE_RESULT	  *SResult  
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // status returned by Read Capacity Packet Command
 | |
|   //
 | |
|   EFI_STATUS                Status;
 | |
|   EFI_STATUS                SenseStatus;
 | |
|   ATAPI_PACKET_COMMAND      Packet;
 | |
|   UINTN 					SenseCount;
 | |
| 
 | |
|   //
 | |
|   // 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 (IdeDev->Type == IdeCdRom) {
 | |
| 
 | |
|     ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
 | |
|     Packet.Inquiry.opcode = ATA_CMD_READ_CAPACITY;
 | |
|     Status = AtapiPacketCommandIn (
 | |
|                IdeDev,
 | |
|                &Packet,
 | |
|                (UINT16 *) &Data,
 | |
|                sizeof (ATAPI_READ_CAPACITY_DATA),
 | |
|                ATAPITIMEOUT
 | |
|                );
 | |
| 
 | |
|   } else {
 | |
|     //
 | |
|     // Type == IdeMagnetic
 | |
|     //
 | |
|     ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
 | |
|     Packet.ReadFormatCapacity.opcode                = ATA_CMD_READ_FORMAT_CAPACITY;
 | |
|     Packet.ReadFormatCapacity.allocation_length_lo  = 12;
 | |
|     Status = AtapiPacketCommandIn (
 | |
|                IdeDev,
 | |
|                &Packet,
 | |
|                (UINT16 *) &FormatData,
 | |
|                sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA),
 | |
|                ATAPITIMEOUT
 | |
|                );
 | |
|   }
 | |
| 
 | |
|   if (Status == EFI_TIMEOUT) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   SenseStatus = AtapiRequestSense (IdeDev, &SenseCount);
 | |
| 
 | |
|   if (!EFI_ERROR (SenseStatus)) {
 | |
| 	ParseSenseData (IdeDev, SenseCount, SResult);			 
 | |
| 															 
 | |
| 	if (!EFI_ERROR (Status) && *SResult == SenseNoSenseKey) {
 | |
|       if (IdeDev->Type == IdeCdRom) {
 | |
| 
 | |
|         IdeDev->BlkIo.Media->LastBlock = ((UINT32) Data.LastLba3 << 24) |
 | |
|           (Data.LastLba2 << 16) |
 | |
|           (Data.LastLba1 << 8) |
 | |
|           Data.LastLba0;
 | |
| 
 | |
| 	      IdeDev->BlkIo.Media->MediaPresent = TRUE;
 | |
| 
 | |
|         IdeDev->BlkIo.Media->ReadOnly = TRUE;
 | |
| 
 | |
|         //
 | |
|         // Because the user data portion in the sector of the Data CD supported
 | |
|         // is always 0x800
 | |
|         //
 | |
|         IdeDev->BlkIo.Media->BlockSize = 0x800;
 | |
|       }
 | |
| 
 | |
|       if (IdeDev->Type == IdeMagnetic) {
 | |
| 
 | |
|         if (FormatData.DesCode == 3) {
 | |
|           IdeDev->BlkIo.Media->MediaPresent = FALSE;
 | |
|           IdeDev->BlkIo.Media->LastBlock    = 0;
 | |
|         } else {
 | |
| 
 | |
|           IdeDev->BlkIo.Media->LastBlock = ((UINT32) FormatData.LastLba3 << 24) |
 | |
|             (FormatData.LastLba2 << 16) | 
 | |
|             (FormatData.LastLba1 << 8)  |
 | |
|             FormatData.LastLba0;
 | |
|           if (IdeDev->BlkIo.Media->LastBlock != 0) {
 | |
|             IdeDev->BlkIo.Media->LastBlock--;
 | |
| 
 | |
|             IdeDev->BlkIo.Media->BlockSize = (FormatData.BlockSize2 << 16) |
 | |
|               (FormatData.BlockSize1 << 8) |
 | |
|               FormatData.BlockSize0;
 | |
| 
 | |
|             IdeDev->BlkIo.Media->MediaPresent = TRUE;
 | |
|           } else {
 | |
|             IdeDev->BlkIo.Media->MediaPresent = FALSE;
 | |
|             //
 | |
|             // Return EFI_NOT_READY operation succeeds but returned capacity is 0
 | |
|             //
 | |
|             return EFI_NOT_READY;
 | |
|           }
 | |
| 
 | |
|           IdeDev->BlkIo.Media->BlockSize = 0x200;
 | |
| 
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return EFI_SUCCESS;
 | |
| 
 | |
|   } else {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| }
 | |
| /**
 | |
|   This function is used to test the current media write-protected or not residing
 | |
|   in the LS-120 drive or ZIP drive. 
 | |
|   @param IdeDev          pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                          to record all the information of the IDE device.
 | |
|   @param WriteProtected  if True, current media is write protected.
 | |
|                          if FALSE, current media is writable
 | |
| 
 | |
|   @retval EFI_SUCCESS         The media write-protected status is achieved successfully
 | |
|   @retval EFI_DEVICE_ERROR    Get Media Status Command is failed.
 | |
| **/
 | |
| EFI_STATUS
 | |
| IsLS120orZipWriteProtected (
 | |
|   IN  IDE_BLK_IO_DEV    *IdeDev,
 | |
|   OUT BOOLEAN           *WriteProtected
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   *WriteProtected = FALSE;
 | |
| 
 | |
|   Status          = LS120EnableMediaStatus (IdeDev, TRUE);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // the Get Media Status Command is only valid
 | |
|   // if a Set Features/Enable Media Status Command has been priviously issued.
 | |
|   //
 | |
|   if (LS120GetMediaStatus (IdeDev) == EFI_WRITE_PROTECTED) {
 | |
| 
 | |
|     *WriteProtected = TRUE;
 | |
|   } else {
 | |
| 
 | |
|     *WriteProtected = FALSE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // After Get Media Status Command completes,
 | |
|   // Set Features/Disable Media Command should be sent.
 | |
|   //
 | |
|   Status = LS120EnableMediaStatus (IdeDev, FALSE);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   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, and if media has been changed, Block
 | |
|   I/O Protocol need to be reinstalled.
 | |
| 
 | |
|   @param IdeDev       pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                       to record all the information of the IDE device.
 | |
|   @param MediaChange  return value that indicates if the media of the device has been
 | |
|                       changed.
 | |
| 
 | |
|   @retval EFI_SUCCESS       media found successfully.
 | |
|   @retval EFI_DEVICE_ERROR  any error encounters during media detection.
 | |
|   @retval EFI_NO_MEDIA      media not found.
 | |
| 
 | |
|   @note
 | |
|   parameter IdeDev may be updated in this function.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiDetectMedia (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   OUT BOOLEAN         *MediaChange
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                    Status;
 | |
|   EFI_STATUS                    CleanStateStatus;
 | |
|   EFI_BLOCK_IO_MEDIA            OldMediaInfo;
 | |
|   UINTN                         RetryTimes;
 | |
|   UINTN                         RetryNotReady;
 | |
|   SENSE_RESULT                  SResult;
 | |
|   BOOLEAN                       WriteProtected;
 | |
| 
 | |
|   CopyMem (&OldMediaInfo, IdeDev->BlkIo.Media, sizeof (EFI_BLOCK_IO_MEDIA));
 | |
|   *MediaChange  = FALSE;
 | |
|   //
 | |
|   // Retry for SenseDeviceNotReadyNeedRetry.
 | |
|   // Each retry takes 1s and we limit the upper boundary to
 | |
|   // 120 times about 2 min.
 | |
|   //
 | |
|   RetryNotReady = 120;
 | |
| 
 | |
|   //
 | |
|   // Do Test Unit Ready
 | |
|   //
 | |
|  DoTUR:
 | |
|   //
 | |
|   // Retry 5 times
 | |
|   //
 | |
|   RetryTimes = 5;
 | |
|   while (RetryTimes != 0) {
 | |
| 
 | |
|     Status = AtapiTestUnitReady (IdeDev, &SResult);
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       //
 | |
|       // Test Unit Ready error without sense data.
 | |
|       // For some devices, this means there's extra data
 | |
|       // that has not been read, so we read these extra
 | |
|       // data out before going on.
 | |
|       //
 | |
|       CleanStateStatus = AtapiReadPendingData (IdeDev);
 | |
|       if (EFI_ERROR (CleanStateStatus)) {
 | |
|         //
 | |
|         // Busy wait failed, try again
 | |
|         //
 | |
|         RetryTimes--;
 | |
|       }
 | |
|       //
 | |
|       // Try again without counting down RetryTimes
 | |
|       //
 | |
|       continue;
 | |
|     } else {
 | |
|       switch (SResult) {
 | |
|       case SenseNoSenseKey:
 | |
|         if (IdeDev->BlkIo.Media->MediaPresent) {
 | |
|           goto Done;
 | |
|         } else {
 | |
|           //
 | |
|           // Media present but the internal structure need refreshed.
 | |
|           // Try Read Capacity
 | |
|           //
 | |
|           goto DoRC;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       case SenseDeviceNotReadyNeedRetry:
 | |
|         if (--RetryNotReady == 0) {
 | |
|           return EFI_DEVICE_ERROR;
 | |
|         }
 | |
|         gBS->Stall (1000 * STALL_1_MILLI_SECOND);
 | |
|         continue;
 | |
|         break;
 | |
| 
 | |
|       case SenseNoMedia:
 | |
|         IdeDev->BlkIo.Media->MediaPresent = FALSE;
 | |
|         IdeDev->BlkIo.Media->LastBlock    = 0;
 | |
|         goto Done;
 | |
|         break;
 | |
| 
 | |
|       case SenseDeviceNotReadyNoRetry:
 | |
|       case SenseMediaError:
 | |
|         return EFI_DEVICE_ERROR;
 | |
| 
 | |
|       case SenseMediaChange:
 | |
|         IdeDev->BlkIo.Media->MediaId++;
 | |
|         goto DoRC;
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         RetryTimes--;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_DEVICE_ERROR;
 | |
| 
 | |
|   //
 | |
|   // Do Read Capacity
 | |
|   //
 | |
|  DoRC:
 | |
|     RetryTimes = 5;
 | |
| 
 | |
|     while (RetryTimes != 0) {
 | |
| 
 | |
|       Status = AtapiReadCapacity (IdeDev, &SResult);
 | |
| 
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         RetryTimes--;
 | |
|         continue;
 | |
|       } else {
 | |
|         switch (SResult) {
 | |
|         case SenseNoSenseKey:
 | |
|           goto Done;
 | |
|           break;
 | |
| 
 | |
|         case SenseDeviceNotReadyNeedRetry:
 | |
|           //
 | |
|           // We use Test Unit Ready to retry which
 | |
|           // is faster.
 | |
|           //
 | |
|           goto DoTUR;
 | |
|           break;
 | |
| 
 | |
|         case SenseNoMedia:
 | |
|           IdeDev->BlkIo.Media->MediaPresent = FALSE;
 | |
|           IdeDev->BlkIo.Media->LastBlock    = 0;
 | |
|           goto Done;
 | |
|           break;
 | |
| 
 | |
|         case SenseDeviceNotReadyNoRetry:
 | |
|         case SenseMediaError:
 | |
|           return EFI_DEVICE_ERROR;
 | |
| 
 | |
|         case SenseMediaChange:
 | |
|           IdeDev->BlkIo.Media->MediaId++;
 | |
|           continue;
 | |
|           break;
 | |
| 
 | |
|         default:
 | |
|           RetryTimes--;
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|   return EFI_DEVICE_ERROR;
 | |
| 
 | |
|  Done:
 | |
|   //
 | |
|   // the following code is to check the write-protected for LS120 media
 | |
|   //
 | |
|   if ((IdeDev->BlkIo.Media->MediaPresent) && (IdeDev->Type == IdeMagnetic)) {
 | |
| 
 | |
|     Status = IsLS120orZipWriteProtected (IdeDev, &WriteProtected);
 | |
|     if (!EFI_ERROR (Status)) {
 | |
| 
 | |
|       if (WriteProtected) {
 | |
| 
 | |
|         IdeDev->BlkIo.Media->ReadOnly = TRUE;
 | |
|       } else {
 | |
| 
 | |
|         IdeDev->BlkIo.Media->ReadOnly = FALSE;
 | |
|       }
 | |
| 
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (IdeDev->BlkIo.Media->MediaId != OldMediaInfo.MediaId) {
 | |
|     //
 | |
|     // Media change information got from the device
 | |
|     //
 | |
|     *MediaChange = TRUE;
 | |
|   }
 | |
| 
 | |
|   if (IdeDev->BlkIo.Media->ReadOnly != OldMediaInfo.ReadOnly) {
 | |
|     *MediaChange = TRUE;
 | |
|     IdeDev->BlkIo.Media->MediaId += 1;
 | |
|   }
 | |
| 
 | |
|   if (IdeDev->BlkIo.Media->BlockSize != OldMediaInfo.BlockSize) {
 | |
|     *MediaChange = TRUE;
 | |
|     IdeDev->BlkIo.Media->MediaId += 1;
 | |
|   }
 | |
| 
 | |
|   if (IdeDev->BlkIo.Media->LastBlock != OldMediaInfo.LastBlock) {
 | |
|     *MediaChange = TRUE;
 | |
|     IdeDev->BlkIo.Media->MediaId += 1;
 | |
|   }
 | |
| 
 | |
|   if (IdeDev->BlkIo.Media->MediaPresent != OldMediaInfo.MediaPresent) {
 | |
|     if (IdeDev->BlkIo.Media->MediaPresent) {
 | |
|       //
 | |
|       // when change from no media to media present, reset the MediaId to 1.
 | |
|       //
 | |
|       IdeDev->BlkIo.Media->MediaId = 1;
 | |
|     } else {
 | |
|       //
 | |
|       // when no media, reset the MediaId to zero.
 | |
|       //
 | |
|       IdeDev->BlkIo.Media->MediaId = 0;
 | |
|     }
 | |
| 
 | |
|     *MediaChange = TRUE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // if any change on current existing media,
 | |
|   // the Block I/O protocol need to be reinstalled.
 | |
|   //
 | |
|   if (*MediaChange) {
 | |
|     gBS->ReinstallProtocolInterface (
 | |
|           IdeDev->Handle,
 | |
|           &gEfiBlockIoProtocolGuid,
 | |
|           &IdeDev->BlkIo,
 | |
|           &IdeDev->BlkIo
 | |
|           );
 | |
|   }
 | |
| 
 | |
|   if (IdeDev->BlkIo.Media->MediaPresent) {
 | |
|     return EFI_SUCCESS;
 | |
|   } else {
 | |
|     return EFI_NO_MEDIA;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function is called by the AtapiBlkIoReadBlocks() to perform
 | |
|   read from media in block unit.
 | |
| 
 | |
|   The main command used to access media here is READ(10) Command. 
 | |
|   READ(10) Command requests that the ATAPI device media transfer 
 | |
|   specified data to the host. Data is transferred in block(sector) 
 | |
|   unit. The maximum number of blocks that can be transferred once is
 | |
|   65536. This is the main difference between READ(10) and READ(12) 
 | |
|   Command. The maximum number of blocks in READ(12) is 2 power 32.
 | |
| 
 | |
|   @param IdeDev           pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                           to record all the information of the IDE device.
 | |
|   @param Buffer           A pointer to the destination buffer for the data. 
 | |
|   @param Lba              The starting logical block address to read from on the 
 | |
|                           device media.
 | |
|   @param NumberOfBlocks   The number of transfer data blocks.
 | |
| 
 | |
|   @return status is fully dependent on the return status of AtapiPacketCommandIn() function.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiReadSectors (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  VOID            *Buffer,
 | |
|   IN  EFI_LBA         Lba,
 | |
|   IN  UINTN           NumberOfBlocks
 | |
|   )
 | |
| {
 | |
| 
 | |
|   ATAPI_PACKET_COMMAND  Packet;
 | |
|   ATAPI_READ10_CMD            *Read10Packet;
 | |
|   EFI_STATUS            Status;
 | |
|   UINTN                 BlocksRemaining;
 | |
|   UINT32                Lba32;
 | |
|   UINT32                BlockSize;
 | |
|   UINT32                ByteCount;
 | |
|   UINT16                SectorCount;
 | |
|   VOID                  *PtrBuffer;
 | |
|   UINT16                MaxBlock;
 | |
|   UINTN                 TimeOut;
 | |
| 
 | |
|   //
 | |
|   // fill command packet for Read(10) command
 | |
|   //
 | |
|   ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
 | |
|   Read10Packet  = &Packet.Read10;
 | |
|   Lba32         = (UINT32) Lba;
 | |
|   PtrBuffer     = Buffer;
 | |
| 
 | |
|   BlockSize     = IdeDev->BlkIo.Media->BlockSize;
 | |
| 
 | |
|   //
 | |
|   // limit the data bytes that can be transferred by one Read(10) Command
 | |
|   //
 | |
|   MaxBlock        = 65535;
 | |
| 
 | |
|   BlocksRemaining = NumberOfBlocks;
 | |
| 
 | |
|   Status          = EFI_SUCCESS;
 | |
|   while (BlocksRemaining > 0) {
 | |
| 
 | |
|     if (BlocksRemaining <= MaxBlock) {
 | |
| 
 | |
|       SectorCount = (UINT16) 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;
 | |
| 
 | |
|     if (IdeDev->Type == IdeCdRom) {
 | |
|       TimeOut = CDROMLONGTIMEOUT;
 | |
|     } else {
 | |
|       TimeOut = ATAPILONGTIMEOUT;
 | |
|     }
 | |
| 
 | |
|     Status = AtapiPacketCommandIn (
 | |
|               IdeDev,
 | |
|               &Packet,
 | |
|               (UINT16 *) PtrBuffer,
 | |
|               ByteCount,
 | |
|               TimeOut
 | |
|               );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     Lba32 += SectorCount;
 | |
|     PtrBuffer = (UINT8 *) PtrBuffer + SectorCount * BlockSize;
 | |
|     BlocksRemaining -= SectorCount;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function is called by the AtapiBlkIoWriteBlocks() to perform
 | |
|   write onto media in block unit.
 | |
|   The main command used to access media here is Write(10) Command. 
 | |
|   Write(10) Command requests that the ATAPI device media transfer 
 | |
|   specified data to the host. Data is transferred in block (sector) 
 | |
|   unit. The maximum number of blocks that can be transferred once is
 | |
|   65536. 
 | |
| 
 | |
|   @param IdeDev          pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                          to record all the information of the IDE device.
 | |
|   @param Buffer          A pointer to the source buffer for the data. 
 | |
|   @param Lba             The starting logical block address to write onto 
 | |
|                          the device media.
 | |
|   @param NumberOfBlocks  The number of transfer data blocks.
 | |
|   
 | |
|   @return status is fully dependent on the return status of AtapiPacketCommandOut() function.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiWriteSectors (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev,
 | |
|   IN  VOID            *Buffer,
 | |
|   IN  EFI_LBA         Lba,
 | |
|   IN  UINTN           NumberOfBlocks
 | |
|   )
 | |
| {
 | |
| 
 | |
|   ATAPI_PACKET_COMMAND  Packet;
 | |
|   ATAPI_READ10_CMD            *Read10Packet;
 | |
| 
 | |
|   EFI_STATUS            Status;
 | |
|   UINTN                 BlocksRemaining;
 | |
|   UINT32                Lba32;
 | |
|   UINT32                BlockSize;
 | |
|   UINT32                ByteCount;
 | |
|   UINT16                SectorCount;
 | |
|   VOID                  *PtrBuffer;
 | |
|   UINT16                MaxBlock;
 | |
| 
 | |
|   //
 | |
|   // fill command packet for Write(10) command
 | |
|   // Write(10) command packet has the same data structure as
 | |
|   // Read(10) command packet,
 | |
|   // so here use the Read10Packet data structure
 | |
|   // for the Write(10) command packet.
 | |
|   //
 | |
|   ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
 | |
|   Read10Packet  = &Packet.Read10;
 | |
| 
 | |
|   Lba32         = (UINT32) Lba;
 | |
|   PtrBuffer     = Buffer;
 | |
| 
 | |
|   BlockSize     = IdeDev->BlkIo.Media->BlockSize;
 | |
| 
 | |
|   //
 | |
|   // limit the data bytes that can be transferred by one Read(10) Command
 | |
|   //
 | |
|   MaxBlock        = (UINT16) (65536 / BlockSize);
 | |
| 
 | |
|   BlocksRemaining = NumberOfBlocks;
 | |
| 
 | |
|   Status          = EFI_SUCCESS;
 | |
|   while (BlocksRemaining > 0) {
 | |
| 
 | |
|     if (BlocksRemaining >= MaxBlock) {
 | |
|       SectorCount = MaxBlock;
 | |
|     } else {
 | |
|       SectorCount = (UINT16) BlocksRemaining;
 | |
|     }
 | |
|   
 | |
|     //
 | |
|     // Command code is WRITE_10.
 | |
|     //
 | |
|     Read10Packet->opcode = ATA_CMD_WRITE_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;
 | |
| 
 | |
|     Status = AtapiPacketCommandOut (
 | |
|               IdeDev,
 | |
|               &Packet,
 | |
|               (UINT16 *) PtrBuffer,
 | |
|               ByteCount,
 | |
|               ATAPILONGTIMEOUT
 | |
|               );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
| 
 | |
|     Lba32 += SectorCount;
 | |
|     PtrBuffer = ((UINT8 *) PtrBuffer + SectorCount * BlockSize);
 | |
|     BlocksRemaining -= SectorCount;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| /**
 | |
|   This function is used to implement the Soft Reset on the specified
 | |
|   ATAPI device. Different from the AtaSoftReset(), here reset is a ATA
 | |
|   Soft Reset Command special for ATAPI device, and it only take effects
 | |
|   on the specified ATAPI device, not on the whole IDE bus.
 | |
|   Since the ATAPI soft reset is needed when device is in exceptional
 | |
|   condition (such as BSY bit is always set ), I think the Soft Reset
 | |
|   command should be sent without waiting for the BSY clear and DRDY
 | |
|   set.
 | |
|   This function is called by IdeBlkIoReset(), 
 | |
|   a interface function of Block I/O protocol.
 | |
| 
 | |
|   @param IdeDev    pointer pointing to IDE_BLK_IO_DEV data structure, used
 | |
|                    to record all the information of the IDE device.
 | |
| 
 | |
|   @retval EFI_SUCCESS      Soft reset completes successfully.
 | |
|   @retval EFI_DEVICE_ERROR Any step during the reset process is failed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiSoftReset (
 | |
|   IN  IDE_BLK_IO_DEV  *IdeDev
 | |
|   )
 | |
| {
 | |
|   UINT8       Command;
 | |
|   UINT8       DeviceSelect;
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   //
 | |
|   // 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) | (IdeDev->Device << 4)));
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, DeviceSelect);
 | |
| 
 | |
|   Command = ATA_CMD_SOFT_RESET;
 | |
|   IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, 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
 | |
|   //
 | |
|   Status = WaitForBSYClear (IdeDev, 31000);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // stall 5 seconds to make the device status stable
 | |
|   //
 | |
|   gBS->Stall (5000000);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function is the ATAPI implementation for ReadBlocks in the
 | |
|   Block I/O Protocol interface.
 | |
| 
 | |
|   @param IdeBlkIoDevice Indicates the calling context.
 | |
|   @param MediaId        The media id that the read request is for.
 | |
|   @param Lba            The starting logical block address to read from on the device.
 | |
|   @param BufferSize     The size of the Buffer in bytes. This must be a multiple
 | |
|                         of the intrinsic block size of the device.
 | |
|   @param Buffer         A pointer to the destination buffer for the data. The caller
 | |
|                         is responsible for either having implicit or explicit 
 | |
|                         ownership of the memory that data is read into.
 | |
|   
 | |
|   @retval EFI_SUCCESS           Read Blocks successfully.
 | |
|   @retval EFI_DEVICE_ERROR      Read Blocks failed.
 | |
|   @retval EFI_NO_MEDIA          There is no media in the device.
 | |
|   @retval EFI_MEDIA_CHANGED     The MediaId is not for the current media.
 | |
|   @retval EFI_BAD_BUFFER_SIZE   The BufferSize parameter is not a multiple of the
 | |
|                                 intrinsic block size of the device.
 | |
|   @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
 | |
|                                 or the data buffer is not valid.
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiBlkIoReadBlocks (
 | |
|   IN IDE_BLK_IO_DEV   *IdeBlkIoDevice,
 | |
|   IN UINT32           MediaId,
 | |
|   IN EFI_LBA          Lba,
 | |
|   IN UINTN            BufferSize,
 | |
|   OUT VOID            *Buffer
 | |
|   )
 | |
| {
 | |
|   EFI_BLOCK_IO_MEDIA  *Media;
 | |
|   UINTN               BlockSize;
 | |
|   UINTN               NumberOfBlocks;
 | |
|   EFI_STATUS          Status;
 | |
| 
 | |
|   BOOLEAN             MediaChange;
 | |
| 
 | |
|   if (Buffer == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (BufferSize == 0) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // ATAPI device media is removable, so it is a must
 | |
|   // to detect media first before read operation
 | |
|   //
 | |
|   MediaChange = FALSE;
 | |
|   Status      = AtapiDetectMedia (IdeBlkIoDevice, &MediaChange);
 | |
|   if (EFI_ERROR (Status)) {
 | |
| 
 | |
|     if (IdeBlkIoDevice->Cache != NULL) {
 | |
|       gBS->FreePool (IdeBlkIoDevice->Cache);
 | |
|       IdeBlkIoDevice->Cache = NULL;
 | |
|     }
 | |
| 
 | |
|     return Status;
 | |
|   }
 | |
|   //
 | |
|   // Get the intrinsic block size
 | |
|   //
 | |
|   Media           = IdeBlkIoDevice->BlkIo.Media;
 | |
|   BlockSize       = Media->BlockSize;
 | |
| 
 | |
|   NumberOfBlocks  = BufferSize / BlockSize;
 | |
| 
 | |
|   if (!(Media->MediaPresent)) {
 | |
| 
 | |
|     if (IdeBlkIoDevice->Cache != NULL) {
 | |
|       gBS->FreePool (IdeBlkIoDevice->Cache);
 | |
|       IdeBlkIoDevice->Cache = NULL;
 | |
|     }
 | |
|     return EFI_NO_MEDIA;
 | |
| 
 | |
|   }
 | |
| 
 | |
|   if ((MediaId != Media->MediaId) || MediaChange) {
 | |
| 
 | |
|     if (IdeBlkIoDevice->Cache != NULL) {
 | |
|       gBS->FreePool (IdeBlkIoDevice->Cache);
 | |
|       IdeBlkIoDevice->Cache = NULL;
 | |
|     }
 | |
|     return EFI_MEDIA_CHANGED;
 | |
|   }
 | |
| 
 | |
|   if (BufferSize % BlockSize != 0) {
 | |
|     return EFI_BAD_BUFFER_SIZE;
 | |
|   }
 | |
| 
 | |
|   if (Lba > Media->LastBlock) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // if all the parameters are valid, then perform read sectors command
 | |
|   // to transfer data from device to host.
 | |
|   //
 | |
|   Status = AtapiReadSectors (IdeBlkIoDevice, Buffer, Lba, NumberOfBlocks);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // Read blocks succeeded
 | |
|   //
 | |
|   
 | |
|   //
 | |
|   // save the first block to the cache for performance
 | |
|   //
 | |
|   if (Lba == 0 && (IdeBlkIoDevice->Cache == NULL)) {
 | |
|     IdeBlkIoDevice->Cache = AllocatePool (BlockSize);
 | |
|     if (IdeBlkIoDevice->Cache!= NULL) {
 | |
|       CopyMem ((UINT8 *) IdeBlkIoDevice->Cache, (UINT8 *) Buffer, BlockSize);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| 
 | |
| }
 | |
| /**
 | |
|   This function is the ATAPI implementation for WriteBlocks in the
 | |
|   Block I/O Protocol interface.
 | |
| 
 | |
|   @param IdeBlkIoDevice  Indicates the calling context.
 | |
|   @param MediaId         The media id that the write request is for.
 | |
|   @param Lba             The starting logical block address to write onto the device.
 | |
|   @param BufferSize      The size of the Buffer in bytes. This must be a multiple
 | |
|                          of the intrinsic block size of the device.
 | |
|   @param Buffer          A pointer to the source buffer for the data. The caller
 | |
|                          is responsible for either having implicit or explicit ownership
 | |
|                          of the memory that data is written from.
 | |
| 
 | |
|   @retval EFI_SUCCESS            Write Blocks successfully.
 | |
|   @retval EFI_DEVICE_ERROR       Write Blocks failed.
 | |
|   @retval EFI_NO_MEDIA           There is no media in the device.
 | |
|   @retval EFI_MEDIA_CHANGE       The MediaId is not for the current media.
 | |
|   @retval EFI_BAD_BUFFER_SIZE    The BufferSize parameter is not a multiple of the
 | |
|                                  intrinsic block size of the device.  
 | |
|   @retval EFI_INVALID_PARAMETER  The write request contains LBAs that are not valid, 
 | |
|                                  or the data buffer is not valid.
 | |
| 
 | |
|   @retval EFI_WRITE_PROTECTED    The write protected is enabled or the media does not support write
 | |
| **/
 | |
| EFI_STATUS
 | |
| AtapiBlkIoWriteBlocks (
 | |
|   IN IDE_BLK_IO_DEV   *IdeBlkIoDevice,
 | |
|   IN UINT32           MediaId,
 | |
|   IN EFI_LBA          Lba,
 | |
|   IN UINTN            BufferSize,
 | |
|   OUT VOID            *Buffer
 | |
|   )
 | |
| {
 | |
| 
 | |
|   EFI_BLOCK_IO_MEDIA  *Media;
 | |
|   UINTN               BlockSize;
 | |
|   UINTN               NumberOfBlocks;
 | |
|   EFI_STATUS          Status;
 | |
|   BOOLEAN             MediaChange;
 | |
| 
 | |
|   if (Lba == 0 && IdeBlkIoDevice->Cache != NULL) {
 | |
|     gBS->FreePool (IdeBlkIoDevice->Cache);
 | |
|     IdeBlkIoDevice->Cache = NULL;
 | |
|   }
 | |
| 
 | |
|   if (Buffer == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (BufferSize == 0) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // ATAPI device media is removable,
 | |
|   // so it is a must to detect media first before write operation
 | |
|   //
 | |
|   MediaChange = FALSE;
 | |
|   Status      = AtapiDetectMedia (IdeBlkIoDevice, &MediaChange);
 | |
|   if (EFI_ERROR (Status)) {
 | |
| 
 | |
|     if (Lba == 0 && IdeBlkIoDevice->Cache != NULL) {
 | |
|       gBS->FreePool (IdeBlkIoDevice->Cache);
 | |
|       IdeBlkIoDevice->Cache = NULL;
 | |
|     }
 | |
|     return Status;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // Get the intrinsic block size
 | |
|   //
 | |
|   Media           = IdeBlkIoDevice->BlkIo.Media;
 | |
|   BlockSize       = Media->BlockSize;
 | |
|   NumberOfBlocks  = BufferSize / BlockSize;
 | |
| 
 | |
|   if (!(Media->MediaPresent)) {
 | |
| 
 | |
|     if (Lba == 0 && IdeBlkIoDevice->Cache != NULL) {
 | |
|       gBS->FreePool (IdeBlkIoDevice->Cache);
 | |
|       IdeBlkIoDevice->Cache = NULL;
 | |
|     }
 | |
|     return EFI_NO_MEDIA;
 | |
|   }
 | |
| 
 | |
|   if ((MediaId != Media->MediaId) || MediaChange) {
 | |
| 
 | |
|     if (Lba == 0 && IdeBlkIoDevice->Cache != NULL) {
 | |
|       gBS->FreePool (IdeBlkIoDevice->Cache);
 | |
|       IdeBlkIoDevice->Cache = NULL;
 | |
|     }
 | |
|     return EFI_MEDIA_CHANGED;
 | |
|   }
 | |
| 
 | |
|   if (Media->ReadOnly) {
 | |
|     return EFI_WRITE_PROTECTED;
 | |
|   }
 | |
| 
 | |
|   if (BufferSize % BlockSize != 0) {
 | |
|     return EFI_BAD_BUFFER_SIZE;
 | |
|   }
 | |
| 
 | |
|   if (Lba > Media->LastBlock) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // if all the parameters are valid,
 | |
|   // then perform write sectors command to transfer data from host to device.
 | |
|   //
 | |
|   Status = AtapiWriteSectors (IdeBlkIoDevice, Buffer, Lba, NumberOfBlocks);
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 |