Signed-off-by: erictian Reviewed-by: rsun3 git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13082 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			2963 lines
		
	
	
		
			85 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2963 lines
		
	
	
		
			85 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   SCSI disk driver that layers on every SCSI IO protocol in the system.
 | |
| 
 | |
| Copyright (c) 2006 - 2012, 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 "ScsiDisk.h"
 | |
| 
 | |
| EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {
 | |
|   ScsiDiskDriverBindingSupported,
 | |
|   ScsiDiskDriverBindingStart,
 | |
|   ScsiDiskDriverBindingStop,
 | |
|   0xa,
 | |
|   NULL,
 | |
|   NULL
 | |
| };
 | |
| 
 | |
| EFI_DISK_INFO_PROTOCOL gScsiDiskInfoProtocolTemplate = {
 | |
|   EFI_DISK_INFO_SCSI_INTERFACE_GUID,
 | |
|   ScsiDiskInfoInquiry,
 | |
|   ScsiDiskInfoIdentify,
 | |
|   ScsiDiskInfoSenseData,
 | |
|   ScsiDiskInfoWhichIde
 | |
| };
 | |
| 
 | |
| /**
 | |
|   Allocates an aligned buffer for SCSI disk.
 | |
| 
 | |
|   This function allocates an aligned buffer for the SCSI disk to perform
 | |
|   SCSI IO operations. The alignment requirement is from SCSI IO interface.
 | |
| 
 | |
|   @param  ScsiDiskDevice    The SCSI disk involved for the operation.
 | |
|   @param  BufferSize        The request buffer size.
 | |
| 
 | |
|   @return A pointer to the aligned buffer or NULL if the allocation fails.
 | |
| 
 | |
| **/
 | |
| VOID *
 | |
| AllocateAlignedBuffer (
 | |
|   IN SCSI_DISK_DEV            *ScsiDiskDevice,
 | |
|   IN UINTN                    BufferSize
 | |
|   )
 | |
| {
 | |
|   return AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), ScsiDiskDevice->ScsiIo->IoAlign);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Frees an aligned buffer for SCSI disk.
 | |
| 
 | |
|   This function frees an aligned buffer for the SCSI disk to perform
 | |
|   SCSI IO operations.
 | |
| 
 | |
|   @param  Buffer            The aligned buffer to be freed.
 | |
|   @param  BufferSize        The request buffer size.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| FreeAlignedBuffer (
 | |
|   IN VOID                     *Buffer,
 | |
|   IN UINTN                    BufferSize
 | |
|   )
 | |
| {
 | |
|   if (Buffer != NULL) {
 | |
|     FreeAlignedPages (Buffer, EFI_SIZE_TO_PAGES (BufferSize));
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The user Entry Point for module ScsiDisk.
 | |
| 
 | |
|   The user code starts with this function.
 | |
| 
 | |
|   @param  ImageHandle    The firmware allocated handle for the EFI image.  
 | |
|   @param  SystemTable    A pointer to the EFI System Table.
 | |
|   
 | |
|   @retval EFI_SUCCESS       The entry point is executed successfully.
 | |
|   @retval other             Some error occurs when executing this entry point.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| InitializeScsiDisk(
 | |
|   IN EFI_HANDLE           ImageHandle,
 | |
|   IN EFI_SYSTEM_TABLE     *SystemTable
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS              Status;
 | |
| 
 | |
|   //
 | |
|   // Install driver model protocol(s).
 | |
|   //
 | |
|   Status = EfiLibInstallDriverBindingComponentName2 (
 | |
|              ImageHandle,
 | |
|              SystemTable,
 | |
|              &gScsiDiskDriverBinding,
 | |
|              ImageHandle,
 | |
|              &gScsiDiskComponentName,
 | |
|              &gScsiDiskComponentName2
 | |
|              );
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Test to see if this driver supports ControllerHandle.
 | |
| 
 | |
|   This service is called by the EFI boot service ConnectController(). In order
 | |
|   to make drivers as small as possible, there are a few calling restrictions for
 | |
|   this service. ConnectController() must follow these calling restrictions.
 | |
|   If any other agent wishes to call Supported() it must also follow these
 | |
|   calling restrictions.
 | |
| 
 | |
|   @param  This                Protocol instance pointer.
 | |
|   @param  ControllerHandle    Handle of device to test
 | |
|   @param  RemainingDevicePath Optional parameter use to pick a specific child
 | |
|                               device to start.
 | |
| 
 | |
|   @retval EFI_SUCCESS         This driver supports this device
 | |
|   @retval EFI_ALREADY_STARTED This driver is already running on this device
 | |
|   @retval other               This driver does not support this device
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| ScsiDiskDriverBindingSupported (
 | |
|   IN EFI_DRIVER_BINDING_PROTOCOL  *This,
 | |
|   IN EFI_HANDLE                   Controller,
 | |
|   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath   OPTIONAL
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS            Status;
 | |
|   EFI_SCSI_IO_PROTOCOL  *ScsiIo;
 | |
|   UINT8                 DeviceType;
 | |
| 
 | |
|   Status = gBS->OpenProtocol (
 | |
|                   Controller,
 | |
|                   &gEfiScsiIoProtocolGuid,
 | |
|                   (VOID **) &ScsiIo,
 | |
|                   This->DriverBindingHandle,
 | |
|                   Controller,
 | |
|                   EFI_OPEN_PROTOCOL_BY_DRIVER
 | |
|                   );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = ScsiIo->GetDeviceType (ScsiIo, &DeviceType);
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     if ((DeviceType == EFI_SCSI_TYPE_DISK) || (DeviceType == EFI_SCSI_TYPE_CDROM)) {
 | |
|       Status = EFI_SUCCESS;
 | |
|     } else {
 | |
|       Status = EFI_UNSUPPORTED;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   gBS->CloseProtocol (
 | |
|          Controller,
 | |
|          &gEfiScsiIoProtocolGuid,
 | |
|          This->DriverBindingHandle,
 | |
|          Controller
 | |
|          );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Start this driver on ControllerHandle.
 | |
| 
 | |
|   This service is called by the EFI boot service ConnectController(). In order
 | |
|   to make drivers as small as possible, there are a few calling restrictions for
 | |
|   this service. ConnectController() must follow these calling restrictions. If
 | |
|   any other agent wishes to call Start() it must also follow these calling
 | |
|   restrictions.
 | |
| 
 | |
|   @param  This                 Protocol instance pointer.
 | |
|   @param  ControllerHandle     Handle of device to bind driver to
 | |
|   @param  RemainingDevicePath  Optional parameter use to pick a specific child
 | |
|                                device to start.
 | |
| 
 | |
|   @retval EFI_SUCCESS          This driver is added to ControllerHandle
 | |
|   @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle
 | |
|   @retval other                This driver does not support this device
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| ScsiDiskDriverBindingStart (
 | |
|   IN EFI_DRIVER_BINDING_PROTOCOL  *This,
 | |
|   IN EFI_HANDLE                   Controller,
 | |
|   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath   OPTIONAL
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS            Status;
 | |
|   EFI_SCSI_IO_PROTOCOL  *ScsiIo;
 | |
|   SCSI_DISK_DEV         *ScsiDiskDevice;
 | |
|   BOOLEAN               Temp;
 | |
|   UINT8                 Index;
 | |
|   UINT8                 MaxRetry;
 | |
|   BOOLEAN               NeedRetry;
 | |
|   BOOLEAN               MustReadCapacity;
 | |
| 
 | |
|   MustReadCapacity = TRUE;
 | |
| 
 | |
|   ScsiDiskDevice = (SCSI_DISK_DEV *) AllocateZeroPool (sizeof (SCSI_DISK_DEV));
 | |
|   if (ScsiDiskDevice == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   Status = gBS->OpenProtocol (
 | |
|                   Controller,
 | |
|                   &gEfiScsiIoProtocolGuid,
 | |
|                   (VOID **) &ScsiIo,
 | |
|                   This->DriverBindingHandle,
 | |
|                   Controller,
 | |
|                   EFI_OPEN_PROTOCOL_BY_DRIVER
 | |
|                   );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (ScsiDiskDevice);
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   ScsiDiskDevice->Signature         = SCSI_DISK_DEV_SIGNATURE;
 | |
|   ScsiDiskDevice->ScsiIo            = ScsiIo;
 | |
|   ScsiDiskDevice->BlkIo.Revision    = EFI_BLOCK_IO_PROTOCOL_REVISION3;
 | |
|   ScsiDiskDevice->BlkIo.Media       = &ScsiDiskDevice->BlkIoMedia;
 | |
|   ScsiDiskDevice->BlkIo.Reset       = ScsiDiskReset;
 | |
|   ScsiDiskDevice->BlkIo.ReadBlocks  = ScsiDiskReadBlocks;
 | |
|   ScsiDiskDevice->BlkIo.WriteBlocks = ScsiDiskWriteBlocks;
 | |
|   ScsiDiskDevice->BlkIo.FlushBlocks = ScsiDiskFlushBlocks;
 | |
|   ScsiDiskDevice->Handle            = Controller;
 | |
| 
 | |
|   ScsiIo->GetDeviceType (ScsiIo, &(ScsiDiskDevice->DeviceType));
 | |
|   switch (ScsiDiskDevice->DeviceType) {
 | |
|   case EFI_SCSI_TYPE_DISK:
 | |
|     ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;
 | |
|     MustReadCapacity = TRUE;
 | |
|     break;
 | |
| 
 | |
|   case EFI_SCSI_TYPE_CDROM:
 | |
|     ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;
 | |
|     MustReadCapacity = FALSE;
 | |
|     break;
 | |
|   }
 | |
|   //
 | |
|   // The Sense Data Array's initial size is 6
 | |
|   //
 | |
|   ScsiDiskDevice->SenseDataNumber = 6;
 | |
|   ScsiDiskDevice->SenseData = (EFI_SCSI_SENSE_DATA *) AllocateZeroPool (
 | |
|                                  sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber
 | |
|                                  );
 | |
|   if (ScsiDiskDevice->SenseData == NULL) {
 | |
|     gBS->CloseProtocol (
 | |
|           Controller,
 | |
|           &gEfiScsiIoProtocolGuid,
 | |
|           This->DriverBindingHandle,
 | |
|           Controller
 | |
|           );
 | |
|     FreePool (ScsiDiskDevice);
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Retrieve device information
 | |
|   //
 | |
|   MaxRetry = 2;
 | |
|   for (Index = 0; Index < MaxRetry; Index++) {
 | |
|     Status = ScsiDiskInquiryDevice (ScsiDiskDevice, &NeedRetry);
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if (!NeedRetry) {
 | |
|       FreePool (ScsiDiskDevice->SenseData);
 | |
|       gBS->CloseProtocol (
 | |
|              Controller,
 | |
|              &gEfiScsiIoProtocolGuid,
 | |
|              This->DriverBindingHandle,
 | |
|              Controller
 | |
|              );
 | |
|       FreePool (ScsiDiskDevice);
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // The second parameter "TRUE" means must
 | |
|   // retrieve media capacity
 | |
|   //
 | |
|   Status = ScsiDiskDetectMedia (ScsiDiskDevice, MustReadCapacity, &Temp);
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     //
 | |
|     // Determine if Block IO should be produced on this controller handle
 | |
|     //
 | |
|     if (DetermineInstallBlockIo(Controller)) {
 | |
|       InitializeInstallDiskInfo(ScsiDiskDevice, Controller);
 | |
|       Status = gBS->InstallMultipleProtocolInterfaces (
 | |
|                       &Controller,
 | |
|                       &gEfiBlockIoProtocolGuid,
 | |
|                       &ScsiDiskDevice->BlkIo,
 | |
|                       &gEfiDiskInfoProtocolGuid,
 | |
|                       &ScsiDiskDevice->DiskInfo,
 | |
|                       NULL
 | |
|                       );
 | |
|       if (!EFI_ERROR(Status)) {
 | |
|         ScsiDiskDevice->ControllerNameTable = NULL;
 | |
|         AddUnicodeString2 (
 | |
|           "eng",
 | |
|           gScsiDiskComponentName.SupportedLanguages,
 | |
|           &ScsiDiskDevice->ControllerNameTable,
 | |
|           L"SCSI Disk Device",
 | |
|           TRUE
 | |
|           );
 | |
|         AddUnicodeString2 (
 | |
|           "en",
 | |
|           gScsiDiskComponentName2.SupportedLanguages,
 | |
|           &ScsiDiskDevice->ControllerNameTable,
 | |
|           L"SCSI Disk Device",
 | |
|           FALSE
 | |
|           );
 | |
|         return EFI_SUCCESS;
 | |
|       }
 | |
|     } 
 | |
|   }
 | |
| 
 | |
|   gBS->FreePool (ScsiDiskDevice->SenseData);
 | |
|   gBS->FreePool (ScsiDiskDevice);
 | |
|   gBS->CloseProtocol (
 | |
|          Controller,
 | |
|          &gEfiScsiIoProtocolGuid,
 | |
|          This->DriverBindingHandle,
 | |
|          Controller
 | |
|          );
 | |
|   return Status;
 | |
|   
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Stop this driver on ControllerHandle.
 | |
| 
 | |
|   This service is called by the EFI boot service DisconnectController().
 | |
|   In order to make drivers as small as possible, there are a few calling
 | |
|   restrictions for this service. DisconnectController() must follow these
 | |
|   calling restrictions. If any other agent wishes to call Stop() it must
 | |
|   also follow these calling restrictions.
 | |
|   
 | |
|   @param  This              Protocol instance pointer.
 | |
|   @param  ControllerHandle  Handle of device to stop driver on
 | |
|   @param  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
 | |
|                             children is zero stop the entire bus driver.
 | |
|   @param  ChildHandleBuffer List of Child Handles to Stop.
 | |
| 
 | |
|   @retval EFI_SUCCESS       This driver is removed ControllerHandle
 | |
|   @retval other             This driver was not removed from this device
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| ScsiDiskDriverBindingStop (
 | |
|   IN  EFI_DRIVER_BINDING_PROTOCOL     *This,
 | |
|   IN  EFI_HANDLE                      Controller,
 | |
|   IN  UINTN                           NumberOfChildren,
 | |
|   IN  EFI_HANDLE                      *ChildHandleBuffer   OPTIONAL
 | |
|   )
 | |
| {
 | |
|   EFI_BLOCK_IO_PROTOCOL *BlkIo;
 | |
|   SCSI_DISK_DEV         *ScsiDiskDevice;
 | |
|   EFI_STATUS            Status;
 | |
| 
 | |
|   Status = gBS->OpenProtocol (
 | |
|                   Controller,
 | |
|                   &gEfiBlockIoProtocolGuid,
 | |
|                   (VOID **) &BlkIo,
 | |
|                   This->DriverBindingHandle,
 | |
|                   Controller,
 | |
|                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
 | |
|                   );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (BlkIo);
 | |
|   Status = gBS->UninstallMultipleProtocolInterfaces (
 | |
|                   Controller,
 | |
|                   &gEfiBlockIoProtocolGuid,
 | |
|                   &ScsiDiskDevice->BlkIo,
 | |
|                   &gEfiDiskInfoProtocolGuid,
 | |
|                   &ScsiDiskDevice->DiskInfo,
 | |
|                   NULL
 | |
|                   );
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     gBS->CloseProtocol (
 | |
|            Controller,
 | |
|            &gEfiScsiIoProtocolGuid,
 | |
|            This->DriverBindingHandle,
 | |
|            Controller
 | |
|            );
 | |
| 
 | |
|     ReleaseScsiDiskDeviceResources (ScsiDiskDevice);
 | |
| 
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
|   //
 | |
|   // errors met
 | |
|   //
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Reset SCSI Disk.
 | |
| 
 | |
| 
 | |
|   @param  This                 The pointer of EFI_BLOCK_IO_PROTOCOL
 | |
|   @param  ExtendedVerification The flag about if extend verificate
 | |
| 
 | |
|   @retval EFI_SUCCESS          The device was reset.
 | |
|   @retval EFI_DEVICE_ERROR     The device is not functioning properly and could
 | |
|                                not be reset.
 | |
|   @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice().
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| ScsiDiskReset (
 | |
|   IN  EFI_BLOCK_IO_PROTOCOL   *This,
 | |
|   IN  BOOLEAN                 ExtendedVerification
 | |
|   )
 | |
| {
 | |
|   EFI_TPL       OldTpl;
 | |
|   SCSI_DISK_DEV *ScsiDiskDevice;
 | |
|   EFI_STATUS    Status;
 | |
| 
 | |
|   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
 | |
| 
 | |
|   ScsiDiskDevice  = SCSI_DISK_DEV_FROM_THIS (This);
 | |
| 
 | |
|   Status          = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     Status = EFI_DEVICE_ERROR;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if (!ExtendedVerification) {
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     Status = EFI_DEVICE_ERROR;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
| Done:
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The function is to Read Block from SCSI Disk.
 | |
| 
 | |
|   @param  This       The pointer of EFI_BLOCK_IO_PROTOCOL.
 | |
|   @param  MediaId    The Id of Media detected
 | |
|   @param  Lba        The logic block address
 | |
|   @param  BufferSize The size of Buffer
 | |
|   @param  Buffer     The buffer to fill the read out data
 | |
| 
 | |
|   @retval EFI_SUCCESS           Successfully to read out block.
 | |
|   @retval EFI_DEVICE_ERROR      Fail to detect media.
 | |
|   @retval EFI_NO_MEDIA          Media is not present.
 | |
|   @retval EFI_MEDIA_CHANGED     Media has changed.
 | |
|   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.
 | |
|   @retval EFI_INVALID_PARAMETER Invalid parameter passed in.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| ScsiDiskReadBlocks (
 | |
|   IN  EFI_BLOCK_IO_PROTOCOL   *This,
 | |
|   IN  UINT32                  MediaId,
 | |
|   IN  EFI_LBA                 Lba,
 | |
|   IN  UINTN                   BufferSize,
 | |
|   OUT VOID                    *Buffer
 | |
|   )
 | |
| {
 | |
|   SCSI_DISK_DEV       *ScsiDiskDevice;
 | |
|   EFI_BLOCK_IO_MEDIA  *Media;
 | |
|   EFI_STATUS          Status;
 | |
|   UINTN               BlockSize;
 | |
|   UINTN               NumberOfBlocks;
 | |
|   BOOLEAN             MediaChange;
 | |
|   EFI_TPL             OldTpl;
 | |
| 
 | |
|   MediaChange    = FALSE;
 | |
|   OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);
 | |
|   ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);
 | |
| 
 | |
|   if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {
 | |
| 
 | |
|     Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       Status = EFI_DEVICE_ERROR;
 | |
|       goto Done;
 | |
|     }
 | |
| 
 | |
|     if (MediaChange) {
 | |
|       gBS->ReinstallProtocolInterface (
 | |
|             ScsiDiskDevice->Handle,
 | |
|             &gEfiBlockIoProtocolGuid,
 | |
|             &ScsiDiskDevice->BlkIo,
 | |
|             &ScsiDiskDevice->BlkIo
 | |
|             );
 | |
|       Status = EFI_MEDIA_CHANGED;
 | |
|       goto Done;
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Get the intrinsic block size
 | |
|   //
 | |
|   Media           = ScsiDiskDevice->BlkIo.Media;
 | |
|   BlockSize       = Media->BlockSize;
 | |
| 
 | |
|   NumberOfBlocks  = BufferSize / BlockSize;
 | |
| 
 | |
|   if (!(Media->MediaPresent)) {
 | |
|     Status = EFI_NO_MEDIA;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if (MediaId != Media->MediaId) {
 | |
|     Status = EFI_MEDIA_CHANGED;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if (Buffer == NULL) {
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if (BufferSize == 0) {
 | |
|     Status = EFI_SUCCESS;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if (BufferSize % BlockSize != 0) {
 | |
|     Status = EFI_BAD_BUFFER_SIZE;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if (Lba > Media->LastBlock) {
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If all the parameters are valid, then perform read sectors command
 | |
|   // to transfer data from device to host.
 | |
|   //
 | |
|   Status = ScsiDiskReadSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);
 | |
| 
 | |
| Done:
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The function is to Write Block to SCSI Disk.
 | |
| 
 | |
|   @param  This       The pointer of EFI_BLOCK_IO_PROTOCOL
 | |
|   @param  MediaId    The Id of Media detected
 | |
|   @param  Lba        The logic block address
 | |
|   @param  BufferSize The size of Buffer
 | |
|   @param  Buffer     The buffer to fill the read out data
 | |
| 
 | |
|   @retval EFI_SUCCESS           Successfully to read out block.
 | |
|   @retval EFI_WRITE_PROTECTED   The device can not be written to.
 | |
|   @retval EFI_DEVICE_ERROR      Fail to detect media.
 | |
|   @retval EFI_NO_MEDIA          Media is not present.
 | |
|   @retval EFI_MEDIA_CHNAGED     Media has changed.
 | |
|   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.
 | |
|   @retval EFI_INVALID_PARAMETER Invalid parameter passed in.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| ScsiDiskWriteBlocks (
 | |
|   IN  EFI_BLOCK_IO_PROTOCOL   *This,
 | |
|   IN  UINT32                  MediaId,
 | |
|   IN  EFI_LBA                 Lba,
 | |
|   IN  UINTN                   BufferSize,
 | |
|   IN  VOID                    *Buffer
 | |
|   )
 | |
| {
 | |
|   SCSI_DISK_DEV       *ScsiDiskDevice;
 | |
|   EFI_BLOCK_IO_MEDIA  *Media;
 | |
|   EFI_STATUS          Status;
 | |
|   UINTN               BlockSize;
 | |
|   UINTN               NumberOfBlocks;
 | |
|   BOOLEAN             MediaChange;
 | |
|   EFI_TPL             OldTpl;
 | |
| 
 | |
|   MediaChange    = FALSE;
 | |
|   OldTpl         = gBS->RaiseTPL (TPL_CALLBACK);
 | |
|   ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);
 | |
| 
 | |
|   if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {
 | |
| 
 | |
|     Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       Status = EFI_DEVICE_ERROR;
 | |
|       goto Done;
 | |
|     }
 | |
| 
 | |
|     if (MediaChange) {
 | |
|       gBS->ReinstallProtocolInterface (
 | |
|             ScsiDiskDevice->Handle,
 | |
|             &gEfiBlockIoProtocolGuid,
 | |
|             &ScsiDiskDevice->BlkIo,
 | |
|             &ScsiDiskDevice->BlkIo
 | |
|             );
 | |
|       Status = EFI_MEDIA_CHANGED;
 | |
|       goto Done;
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Get the intrinsic block size
 | |
|   //
 | |
|   Media           = ScsiDiskDevice->BlkIo.Media;
 | |
|   BlockSize       = Media->BlockSize;
 | |
| 
 | |
|   NumberOfBlocks  = BufferSize / BlockSize;
 | |
| 
 | |
|   if (!(Media->MediaPresent)) {
 | |
|     Status = EFI_NO_MEDIA;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if (MediaId != Media->MediaId) {
 | |
|     Status = EFI_MEDIA_CHANGED;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if (BufferSize == 0) {
 | |
|     Status = EFI_SUCCESS;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if (Buffer == NULL) {
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if (BufferSize % BlockSize != 0) {
 | |
|     Status = EFI_BAD_BUFFER_SIZE;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if (Lba > Media->LastBlock) {
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
 | |
|     Status = EFI_INVALID_PARAMETER;
 | |
|     goto Done;
 | |
|   }
 | |
|   //
 | |
|   // if all the parameters are valid, then perform read sectors command
 | |
|   // to transfer data from device to host.
 | |
|   //
 | |
|   Status = ScsiDiskWriteSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);
 | |
| 
 | |
| Done:
 | |
|   gBS->RestoreTPL (OldTpl);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Flush Block to Disk.
 | |
| 
 | |
|   EFI_SUCCESS is returned directly.
 | |
| 
 | |
|   @param  This              The pointer of EFI_BLOCK_IO_PROTOCOL
 | |
| 
 | |
|   @retval EFI_SUCCESS       All outstanding data was written to the device
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| ScsiDiskFlushBlocks (
 | |
|   IN  EFI_BLOCK_IO_PROTOCOL   *This
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // return directly
 | |
|   //
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Detect Device and read out capacity ,if error occurs, parse the sense key.
 | |
| 
 | |
|   @param  ScsiDiskDevice    The pointer of SCSI_DISK_DEV
 | |
|   @param  MustReadCapacity  The flag about reading device capacity
 | |
|   @param  MediaChange       The pointer of flag indicates if media has changed 
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR  Indicates that error occurs
 | |
|   @retval EFI_SUCCESS       Successfully to detect media
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ScsiDiskDetectMedia (
 | |
|   IN   SCSI_DISK_DEV   *ScsiDiskDevice,
 | |
|   IN   BOOLEAN         MustReadCapacity,
 | |
|   OUT  BOOLEAN         *MediaChange
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS          Status;
 | |
|   EFI_SCSI_SENSE_DATA *SenseData;
 | |
|   UINTN               NumberOfSenseKeys;
 | |
|   BOOLEAN             NeedRetry;
 | |
|   BOOLEAN             NeedReadCapacity;
 | |
|   UINT8               Retry;
 | |
|   UINT8               MaxRetry;
 | |
|   EFI_BLOCK_IO_MEDIA  OldMedia;
 | |
|   UINTN               Action;
 | |
|   EFI_EVENT           TimeoutEvt;
 | |
| 
 | |
|   Status              = EFI_SUCCESS;
 | |
|   SenseData           = NULL;
 | |
|   NumberOfSenseKeys   = 0;
 | |
|   Retry               = 0;
 | |
|   MaxRetry            = 3;
 | |
|   Action              = ACTION_NO_ACTION;
 | |
|   NeedReadCapacity    = FALSE;
 | |
|   *MediaChange        = FALSE;
 | |
|   TimeoutEvt          = NULL;
 | |
| 
 | |
|   CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia));
 | |
| 
 | |
|   Status = gBS->CreateEvent (
 | |
|                   EVT_TIMER,
 | |
|                   TPL_CALLBACK,
 | |
|                   NULL,
 | |
|                   NULL,
 | |
|                   &TimeoutEvt
 | |
|                   );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   Status = gBS->SetTimer (TimeoutEvt, TimerRelative, EFI_TIMER_PERIOD_SECONDS(120));
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto EXIT;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Sending Test_Unit cmd to poll device status.
 | |
|   // If the sense data shows the drive is not ready or reset before, we need poll the device status again.
 | |
|   // We limit the upper boundary to 120 seconds.
 | |
|   //
 | |
|   while (EFI_ERROR (gBS->CheckEvent (TimeoutEvt))) {
 | |
|     Status = ScsiDiskTestUnitReady (
 | |
|               ScsiDiskDevice,
 | |
|               &NeedRetry,
 | |
|               &SenseData,
 | |
|               &NumberOfSenseKeys
 | |
|               );
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       Status = DetectMediaParsingSenseKeys (
 | |
|                  ScsiDiskDevice,
 | |
|                  SenseData,
 | |
|                  NumberOfSenseKeys,
 | |
|                  &Action
 | |
|                  );
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         goto EXIT;
 | |
|       } else if (Action == ACTION_RETRY_COMMAND_LATER) {
 | |
|         continue;
 | |
|       } else {
 | |
|         break;
 | |
|       }
 | |
|     } else {
 | |
|       Retry++;
 | |
|       if (!NeedRetry || (Retry >= MaxRetry)) {
 | |
|         goto EXIT;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto EXIT;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // ACTION_NO_ACTION: need not read capacity
 | |
|   // other action code: need read capacity
 | |
|   //
 | |
|   if (Action == ACTION_READ_CAPACITY) {
 | |
|     NeedReadCapacity = TRUE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,
 | |
|   // retrieve capacity via Read Capacity command
 | |
|   //
 | |
|   if (NeedReadCapacity || MustReadCapacity) {
 | |
|     //
 | |
|     // retrieve media information
 | |
|     //
 | |
|     for (Retry = 0; Retry < MaxRetry; Retry++) {
 | |
|       Status = ScsiDiskReadCapacity (
 | |
|                  ScsiDiskDevice,
 | |
|                  &NeedRetry,
 | |
|                  &SenseData,
 | |
|                  &NumberOfSenseKeys
 | |
|                  );
 | |
|       if (!EFI_ERROR (Status)) {
 | |
|         //
 | |
|         // analyze sense key to action
 | |
|         //
 | |
|         Status = DetectMediaParsingSenseKeys (
 | |
|                    ScsiDiskDevice,
 | |
|                    SenseData,
 | |
|                    NumberOfSenseKeys,
 | |
|                    &Action
 | |
|                    );
 | |
|         if (EFI_ERROR (Status)) {
 | |
|           //
 | |
|           // if Status is error, it may indicate crisis error,
 | |
|           // so return without retry.
 | |
|           //
 | |
|           goto EXIT;
 | |
|         } else if (Action == ACTION_RETRY_COMMAND_LATER) {
 | |
|           Retry = 0;
 | |
|           continue;
 | |
|         } else {
 | |
|           break;
 | |
|         }
 | |
|       } else {   
 | |
|         Retry++;
 | |
|         if (!NeedRetry || (Retry >= MaxRetry)) {
 | |
|           goto EXIT;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto EXIT;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {
 | |
|     //
 | |
|     // Media change information got from the device
 | |
|     //
 | |
|     *MediaChange = TRUE;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {
 | |
|     *MediaChange = TRUE;
 | |
|     ScsiDiskDevice->BlkIo.Media->MediaId += 1;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {
 | |
|     *MediaChange = TRUE;
 | |
|     ScsiDiskDevice->BlkIo.Media->MediaId += 1;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) {
 | |
|     *MediaChange = TRUE;
 | |
|     ScsiDiskDevice->BlkIo.Media->MediaId += 1;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) {
 | |
|     if (ScsiDiskDevice->BlkIo.Media->MediaPresent) {
 | |
|       //
 | |
|       // when change from no media to media present, reset the MediaId to 1.
 | |
|       //
 | |
|       ScsiDiskDevice->BlkIo.Media->MediaId = 1;
 | |
|     } else {
 | |
|       //
 | |
|       // when no media, reset the MediaId to zero.
 | |
|       //
 | |
|       ScsiDiskDevice->BlkIo.Media->MediaId = 0;
 | |
|     }
 | |
| 
 | |
|     *MediaChange = TRUE;
 | |
|   }
 | |
| 
 | |
| EXIT:
 | |
|   if (TimeoutEvt != NULL) {
 | |
|     gBS->CloseEvent (TimeoutEvt);
 | |
|   }
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Send out Inquiry command to Device.
 | |
| 
 | |
|   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
 | |
|   @param  NeedRetry       Indicates if needs try again when error happens
 | |
| 
 | |
|   @retval  EFI_DEVICE_ERROR  Indicates that error occurs
 | |
|   @retval  EFI_SUCCESS       Successfully to detect media
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ScsiDiskInquiryDevice (
 | |
|   IN OUT  SCSI_DISK_DEV   *ScsiDiskDevice,
 | |
|      OUT  BOOLEAN         *NeedRetry
 | |
|   )
 | |
| {
 | |
|   UINT32                                InquiryDataLength;
 | |
|   UINT8                                 SenseDataLength;
 | |
|   UINT8                                 HostAdapterStatus;
 | |
|   UINT8                                 TargetStatus;
 | |
|   EFI_SCSI_SENSE_DATA                   *SenseDataArray;
 | |
|   UINTN                                 NumberOfSenseKeys;
 | |
|   EFI_STATUS                            Status;
 | |
|   UINT8                                 MaxRetry;
 | |
|   UINT8                                 Index;
 | |
|   EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE *SupportedVpdPages;
 | |
|   EFI_SCSI_BLOCK_LIMITS_VPD_PAGE        *BlockLimits;
 | |
|   UINTN                                 PageLength;
 | |
| 
 | |
|   InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA);
 | |
|   SenseDataLength   = 0;
 | |
| 
 | |
|   Status = ScsiInquiryCommand (
 | |
|             ScsiDiskDevice->ScsiIo,
 | |
|             EFI_TIMER_PERIOD_SECONDS (1),
 | |
|             NULL,
 | |
|             &SenseDataLength,
 | |
|             &HostAdapterStatus,
 | |
|             &TargetStatus,
 | |
|             (VOID *) &(ScsiDiskDevice->InquiryData),
 | |
|             &InquiryDataLength,
 | |
|             FALSE
 | |
|             );
 | |
|     //
 | |
|     // no need to check HostAdapterStatus and TargetStatus
 | |
|     //
 | |
|   if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {
 | |
|     ParseInquiryData (ScsiDiskDevice);
 | |
| 
 | |
|     if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {
 | |
|       //
 | |
|       // Check whether the device supports Block Limits VPD page (0xB0)
 | |
|       //
 | |
|       SupportedVpdPages = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));
 | |
|       if (SupportedVpdPages == NULL) {
 | |
|         *NeedRetry = FALSE;
 | |
|         return EFI_DEVICE_ERROR;
 | |
|       }
 | |
|       ZeroMem (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));
 | |
|       InquiryDataLength = sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE);
 | |
|       SenseDataLength   = 0;
 | |
|       Status = ScsiInquiryCommandEx (
 | |
|                  ScsiDiskDevice->ScsiIo,
 | |
|                  EFI_TIMER_PERIOD_SECONDS (1),
 | |
|                  NULL,
 | |
|                  &SenseDataLength,
 | |
|                  &HostAdapterStatus,
 | |
|                  &TargetStatus,
 | |
|                  (VOID *) SupportedVpdPages,
 | |
|                  &InquiryDataLength,
 | |
|                  TRUE,
 | |
|                  EFI_SCSI_PAGE_CODE_SUPPORTED_VPD
 | |
|                  );
 | |
|       if (!EFI_ERROR (Status)) {
 | |
|         PageLength = (SupportedVpdPages->PageLength2 << 8)
 | |
|                    |  SupportedVpdPages->PageLength1;
 | |
|         for (Index = 0; Index < PageLength; Index++) {
 | |
|           if (SupportedVpdPages->SupportedVpdPageList[Index] == EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD) {
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         //
 | |
|         // Query the Block Limits VPD page
 | |
|         //
 | |
|         if (Index < PageLength) {
 | |
|           BlockLimits = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));
 | |
|           if (BlockLimits == NULL) {
 | |
|             FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));
 | |
|             *NeedRetry = FALSE;
 | |
|             return EFI_DEVICE_ERROR;
 | |
|           }
 | |
|           ZeroMem (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));
 | |
|           InquiryDataLength = sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE);
 | |
|           SenseDataLength   = 0;
 | |
|           Status = ScsiInquiryCommandEx (
 | |
|                      ScsiDiskDevice->ScsiIo,
 | |
|                      EFI_TIMER_PERIOD_SECONDS (1),
 | |
|                      NULL,
 | |
|                      &SenseDataLength,
 | |
|                      &HostAdapterStatus,
 | |
|                      &TargetStatus,
 | |
|                      (VOID *) BlockLimits,
 | |
|                      &InquiryDataLength,
 | |
|                      TRUE,
 | |
|                      EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD
 | |
|                      );
 | |
|           if (!EFI_ERROR (Status)) {
 | |
|             ScsiDiskDevice->BlkIo.Media->OptimalTransferLengthGranularity = 
 | |
|               (BlockLimits->OptimalTransferLengthGranularity2 << 8) |
 | |
|                BlockLimits->OptimalTransferLengthGranularity1;
 | |
|           }
 | |
| 
 | |
|           FreeAlignedBuffer (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     return EFI_SUCCESS;
 | |
| 
 | |
|   } else if (Status == EFI_NOT_READY) {
 | |
|     *NeedRetry = TRUE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|  
 | |
|   } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {
 | |
|     *NeedRetry = FALSE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   //
 | |
|   // go ahead to check HostAdapterStatus and TargetStatus
 | |
|   // (EFI_TIMEOUT, EFI_DEVICE_ERROR)
 | |
|   //
 | |
| 
 | |
|   Status = CheckHostAdapterStatus (HostAdapterStatus);
 | |
|   if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
 | |
|     *NeedRetry = TRUE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   } else if (Status == EFI_DEVICE_ERROR) {
 | |
|       //
 | |
|       // reset the scsi channel
 | |
|       //
 | |
|     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
 | |
|     *NeedRetry = FALSE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   Status = CheckTargetStatus (TargetStatus);
 | |
|   if (Status == EFI_NOT_READY) {
 | |
|     //
 | |
|     // reset the scsi device
 | |
|     //
 | |
|     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
 | |
|     *NeedRetry = TRUE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
| 
 | |
|   } else if (Status == EFI_DEVICE_ERROR) {
 | |
|     *NeedRetry = FALSE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // if goes here, meant ScsiInquiryCommand() failed.
 | |
|   // if ScsiDiskRequestSenseKeys() succeeds at last,
 | |
|   // better retry ScsiInquiryCommand(). (by setting *NeedRetry = TRUE)
 | |
|   //
 | |
|   MaxRetry = 3;
 | |
|   for (Index = 0; Index < MaxRetry; Index++) {
 | |
|     Status = ScsiDiskRequestSenseKeys (
 | |
|               ScsiDiskDevice,
 | |
|               NeedRetry,
 | |
|               &SenseDataArray,
 | |
|               &NumberOfSenseKeys,
 | |
|               TRUE
 | |
|               );
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       *NeedRetry = TRUE;
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
| 
 | |
|     if (!*NeedRetry) {
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // ScsiDiskRequestSenseKeys() failed after several rounds of retry.
 | |
|   // set *NeedRetry = FALSE to avoid the outside caller try again.
 | |
|   //
 | |
|   *NeedRetry = FALSE;
 | |
|   return EFI_DEVICE_ERROR;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   To test device.
 | |
| 
 | |
|   When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense;
 | |
|   When Test Unit Ready command encounters any error caused by host adapter or
 | |
|   target, return error without retrieving Sense Keys.
 | |
| 
 | |
|   @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV
 | |
|   @param  NeedRetry          The pointer of flag indicates try again
 | |
|   @param  SenseDataArray     The pointer of an array of sense data
 | |
|   @param  NumberOfSenseKeys  The pointer of the number of sense data array
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR   Indicates that error occurs
 | |
|   @retval EFI_SUCCESS        Successfully to test unit
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ScsiDiskTestUnitReady (
 | |
|   IN  SCSI_DISK_DEV         *ScsiDiskDevice,
 | |
|   OUT BOOLEAN               *NeedRetry,
 | |
|   OUT EFI_SCSI_SENSE_DATA   **SenseDataArray,
 | |
|   OUT UINTN                 *NumberOfSenseKeys
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       SenseDataLength;
 | |
|   UINT8       HostAdapterStatus;
 | |
|   UINT8       TargetStatus;
 | |
|   UINT8       Index;
 | |
|   UINT8       MaxRetry;
 | |
| 
 | |
|   SenseDataLength     = 0;
 | |
|   *NumberOfSenseKeys  = 0;
 | |
| 
 | |
|   //
 | |
|   // Parameter 3 and 4: do not require sense data, retrieve it when needed.
 | |
|   //
 | |
|   Status = ScsiTestUnitReadyCommand (
 | |
|             ScsiDiskDevice->ScsiIo,
 | |
|             EFI_TIMER_PERIOD_SECONDS (1),
 | |
|             NULL,
 | |
|             &SenseDataLength,
 | |
|             &HostAdapterStatus,
 | |
|             &TargetStatus
 | |
|             );
 | |
|   //
 | |
|   // no need to check HostAdapterStatus and TargetStatus
 | |
|   //
 | |
|   if (Status == EFI_NOT_READY) {
 | |
|     *NeedRetry = TRUE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
| 
 | |
|   } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {
 | |
|     *NeedRetry = FALSE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   //
 | |
|   // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)
 | |
|   //
 | |
| 
 | |
|   Status = CheckHostAdapterStatus (HostAdapterStatus);
 | |
|   if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
 | |
|     *NeedRetry = TRUE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
| 
 | |
|   } else if (Status == EFI_DEVICE_ERROR) {
 | |
|     //
 | |
|     // reset the scsi channel
 | |
|     //
 | |
|     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
 | |
|     *NeedRetry = FALSE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   Status = CheckTargetStatus (TargetStatus);
 | |
|   if (Status == EFI_NOT_READY) {
 | |
|     //
 | |
|     // reset the scsi device
 | |
|     //
 | |
|     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
 | |
|     *NeedRetry = TRUE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
| 
 | |
|   } else if (Status == EFI_DEVICE_ERROR) {
 | |
|     *NeedRetry = FALSE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   MaxRetry = 3;
 | |
|   for (Index = 0; Index < MaxRetry; Index++) {
 | |
|     Status = ScsiDiskRequestSenseKeys (
 | |
|               ScsiDiskDevice,
 | |
|               NeedRetry,
 | |
|               SenseDataArray,
 | |
|               NumberOfSenseKeys,
 | |
|               FALSE
 | |
|               );
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       return EFI_SUCCESS;
 | |
|     }
 | |
| 
 | |
|     if (!*NeedRetry) {
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // ScsiDiskRequestSenseKeys() failed after several rounds of retry.
 | |
|   // set *NeedRetry = FALSE to avoid the outside caller try again.
 | |
|   //
 | |
|   *NeedRetry = FALSE;
 | |
|   return EFI_DEVICE_ERROR;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Parsing Sense Keys which got from request sense command.
 | |
| 
 | |
|   @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV
 | |
|   @param  SenseData          The pointer of EFI_SCSI_SENSE_DATA
 | |
|   @param  NumberOfSenseKeys  The number of sense key  
 | |
|   @param  Action             The pointer of action which indicates what is need to do next
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR   Indicates that error occurs
 | |
|   @retval EFI_SUCCESS        Successfully to complete the parsing
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| DetectMediaParsingSenseKeys (
 | |
|   OUT  SCSI_DISK_DEV           *ScsiDiskDevice,
 | |
|   IN   EFI_SCSI_SENSE_DATA     *SenseData,
 | |
|   IN   UINTN                   NumberOfSenseKeys,
 | |
|   OUT  UINTN                   *Action
 | |
|   )
 | |
| {
 | |
|   BOOLEAN RetryLater;
 | |
| 
 | |
|   //
 | |
|   // Default is to read capacity, unless..
 | |
|   //
 | |
|   *Action = ACTION_READ_CAPACITY;
 | |
| 
 | |
|   if (NumberOfSenseKeys == 0) {
 | |
|     if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) {
 | |
|       *Action = ACTION_NO_ACTION;
 | |
|     }
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {
 | |
|     //
 | |
|     // No Sense Key returned from last submitted command
 | |
|     //
 | |
|     if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) {
 | |
|       *Action = ACTION_NO_ACTION;
 | |
|     }
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) {
 | |
|     ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;
 | |
|     ScsiDiskDevice->BlkIo.Media->LastBlock    = 0;
 | |
|     *Action = ACTION_NO_ACTION;
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {
 | |
|     ScsiDiskDevice->BlkIo.Media->MediaId++;
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskIsResetBefore (SenseData, NumberOfSenseKeys)) {
 | |
|     *Action = ACTION_RETRY_COMMAND_LATER;
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {
 | |
|     ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;
 | |
|     ScsiDiskDevice->BlkIo.Media->LastBlock    = 0;
 | |
|     *Action = ACTION_NO_ACTION;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {
 | |
|     *Action = ACTION_NO_ACTION;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {
 | |
|     if (RetryLater) {
 | |
|       *Action = ACTION_RETRY_COMMAND_LATER;
 | |
|       return EFI_SUCCESS;
 | |
|     }
 | |
|     *Action = ACTION_NO_ACTION;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Send read capacity command to device and get the device parameter.
 | |
| 
 | |
|   @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV
 | |
|   @param  NeedRetry          The pointer of flag indicates if need a retry
 | |
|   @param  SenseDataArray     The pointer of an array of sense data
 | |
|   @param  NumberOfSenseKeys  The number of sense key
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR   Indicates that error occurs
 | |
|   @retval EFI_SUCCESS        Successfully to read capacity or sense data is received.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ScsiDiskReadCapacity (
 | |
|   IN  OUT  SCSI_DISK_DEV           *ScsiDiskDevice,
 | |
|       OUT  BOOLEAN                 *NeedRetry,
 | |
|       OUT  EFI_SCSI_SENSE_DATA     **SenseDataArray,
 | |
|       OUT  UINTN                   *NumberOfSenseKeys
 | |
|   )
 | |
| {
 | |
|   UINT8                         HostAdapterStatus;
 | |
|   UINT8                         TargetStatus;
 | |
|   EFI_STATUS                    CommandStatus;
 | |
|   EFI_STATUS                    Status;
 | |
|   UINT8                         Index;
 | |
|   UINT8                         MaxRetry;
 | |
|   UINT8                         SenseDataLength;
 | |
|   UINT32                        DataLength10;
 | |
|   UINT32                        DataLength16;
 | |
|   EFI_SCSI_DISK_CAPACITY_DATA   *CapacityData10;
 | |
|   EFI_SCSI_DISK_CAPACITY_DATA16 *CapacityData16;
 | |
| 
 | |
|   CapacityData10 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));
 | |
|   if (CapacityData10 == NULL) {
 | |
|     *NeedRetry = FALSE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   CapacityData16 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));
 | |
|   if (CapacityData16 == NULL) {
 | |
|     FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));
 | |
|     *NeedRetry = FALSE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   SenseDataLength       = 0;
 | |
|   DataLength10          = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);
 | |
|   DataLength16          = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);
 | |
|   ZeroMem (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));
 | |
|   ZeroMem (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));
 | |
| 
 | |
|   *NumberOfSenseKeys  = 0;
 | |
|   *NeedRetry          = FALSE;
 | |
| 
 | |
|   //
 | |
|   // submit Read Capacity(10) Command. If it returns capacity of FFFFFFFFh, 
 | |
|   // 16 byte command should be used to access large hard disk >2TB
 | |
|   //
 | |
|   CommandStatus = ScsiReadCapacityCommand (
 | |
|                     ScsiDiskDevice->ScsiIo,
 | |
|                     EFI_TIMER_PERIOD_SECONDS(1),
 | |
|                     NULL,
 | |
|                     &SenseDataLength,
 | |
|                     &HostAdapterStatus,
 | |
|                     &TargetStatus,
 | |
|                     (VOID *) CapacityData10,
 | |
|                     &DataLength10,
 | |
|                     FALSE
 | |
|                     );
 | |
| 
 | |
|   ScsiDiskDevice->Cdb16Byte = FALSE;
 | |
|   if ((!EFI_ERROR (CommandStatus)) && (CapacityData10->LastLba3 == 0xff) && (CapacityData10->LastLba2 == 0xff) &&
 | |
|       (CapacityData10->LastLba1 == 0xff) && (CapacityData10->LastLba0 == 0xff)) {
 | |
|     //
 | |
|     // use Read Capacity (16), Read (16) and Write (16) next when hard disk size > 2TB
 | |
|     //
 | |
|     ScsiDiskDevice->Cdb16Byte = TRUE;
 | |
|     //
 | |
|     // submit Read Capacity(16) Command to get parameter LogicalBlocksPerPhysicalBlock
 | |
|     // and LowestAlignedLba
 | |
|     //
 | |
|     CommandStatus = ScsiReadCapacity16Command (
 | |
|                       ScsiDiskDevice->ScsiIo,
 | |
|                       EFI_TIMER_PERIOD_SECONDS (1),
 | |
|                       NULL,
 | |
|                       &SenseDataLength,
 | |
|                       &HostAdapterStatus,
 | |
|                       &TargetStatus,
 | |
|                       (VOID *) CapacityData16,
 | |
|                       &DataLength16,
 | |
|                       FALSE
 | |
|                       );
 | |
|   }
 | |
| 
 | |
|     //
 | |
|     // no need to check HostAdapterStatus and TargetStatus
 | |
|     //
 | |
|    if (CommandStatus == EFI_SUCCESS) {
 | |
|      GetMediaInfo (ScsiDiskDevice, CapacityData10, CapacityData16);
 | |
|      FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));
 | |
|      FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));
 | |
|      return EFI_SUCCESS;
 | |
|    }
 | |
| 
 | |
|    FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));
 | |
|    FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));
 | |
| 
 | |
|    if (CommandStatus == EFI_NOT_READY) {
 | |
|      *NeedRetry = TRUE;
 | |
|      return EFI_DEVICE_ERROR;
 | |
|    } else if ((CommandStatus == EFI_INVALID_PARAMETER) || (CommandStatus == EFI_UNSUPPORTED)) {
 | |
|      *NeedRetry = FALSE;
 | |
|      return EFI_DEVICE_ERROR;
 | |
|    }
 | |
| 
 | |
|    //
 | |
|    // go ahead to check HostAdapterStatus and TargetStatus
 | |
|    // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)
 | |
|    //
 | |
|  
 | |
|    Status = CheckHostAdapterStatus (HostAdapterStatus);
 | |
|    if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
 | |
|      *NeedRetry = TRUE;
 | |
|      return EFI_DEVICE_ERROR;
 | |
|  
 | |
|    } else if (Status == EFI_DEVICE_ERROR) {
 | |
|     //
 | |
|     // reset the scsi channel
 | |
|     //
 | |
|     ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
 | |
|     *NeedRetry = FALSE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   Status = CheckTargetStatus (TargetStatus);
 | |
|   if (Status == EFI_NOT_READY) {
 | |
|     //
 | |
|     // reset the scsi device
 | |
|     //
 | |
|     ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
 | |
|     *NeedRetry = TRUE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
| 
 | |
|   } else if (Status == EFI_DEVICE_ERROR) {
 | |
|     *NeedRetry = FALSE;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // if goes here, meant ScsiReadCapacityCommand() failed.
 | |
|   // if ScsiDiskRequestSenseKeys() succeeds at last,
 | |
|   // better retry ScsiReadCapacityCommand(). (by setting *NeedRetry = TRUE)
 | |
|   //
 | |
|   MaxRetry = 3;
 | |
|   for (Index = 0; Index < MaxRetry; Index++) {
 | |
| 
 | |
|     Status = ScsiDiskRequestSenseKeys (
 | |
|               ScsiDiskDevice,
 | |
|               NeedRetry,
 | |
|               SenseDataArray,
 | |
|               NumberOfSenseKeys,
 | |
|               TRUE
 | |
|               );
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       return EFI_SUCCESS;
 | |
|     }
 | |
| 
 | |
|     if (!*NeedRetry) {
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // ScsiDiskRequestSenseKeys() failed after several rounds of retry.
 | |
|   // set *NeedRetry = FALSE to avoid the outside caller try again.
 | |
|   //
 | |
|   *NeedRetry = FALSE;
 | |
|   return EFI_DEVICE_ERROR;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check the HostAdapter status and re-interpret it in EFI_STATUS.
 | |
| 
 | |
|   @param  HostAdapterStatus  Host Adapter status
 | |
| 
 | |
|   @retval  EFI_SUCCESS       Host adapter is OK.
 | |
|   @retval  EFI_TIMEOUT       Timeout.
 | |
|   @retval  EFI_NOT_READY     Adapter NOT ready.
 | |
|   @retval  EFI_DEVICE_ERROR  Adapter device error.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| CheckHostAdapterStatus (
 | |
|   IN UINT8   HostAdapterStatus
 | |
|   )
 | |
| {
 | |
|   switch (HostAdapterStatus) {
 | |
|   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK:
 | |
|     return EFI_SUCCESS;
 | |
| 
 | |
|   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT:
 | |
|   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT:
 | |
|   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND:
 | |
|     return EFI_TIMEOUT;
 | |
| 
 | |
|   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT:
 | |
|   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR:
 | |
|   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED:
 | |
|   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN:
 | |
|   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET:
 | |
|     return EFI_NOT_READY;
 | |
| 
 | |
|   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE:
 | |
|   case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR:
 | |
|     return EFI_DEVICE_ERROR;
 | |
| 
 | |
|   default:
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Check the target status and re-interpret it in EFI_STATUS.
 | |
| 
 | |
|   @param  TargetStatus  Target status
 | |
| 
 | |
|   @retval EFI_NOT_READY       Device is NOT ready.
 | |
|   @retval EFI_DEVICE_ERROR 
 | |
|   @retval EFI_SUCCESS
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| CheckTargetStatus (
 | |
|   IN  UINT8   TargetStatus
 | |
|   )
 | |
| {
 | |
|   switch (TargetStatus) {
 | |
|   case EFI_EXT_SCSI_STATUS_TARGET_GOOD:
 | |
|   case EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION:
 | |
|   case EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET:
 | |
|     return EFI_SUCCESS;
 | |
| 
 | |
|   case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE:
 | |
|   case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET:
 | |
|   case EFI_EXT_SCSI_STATUS_TARGET_BUSY:
 | |
|   case EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL:
 | |
|     return EFI_NOT_READY;
 | |
| 
 | |
|   case EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT:
 | |
|     return EFI_DEVICE_ERROR;
 | |
|     break;
 | |
| 
 | |
|   default:
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Retrieve all sense keys from the device.
 | |
| 
 | |
|   When encountering error during the process, if retrieve sense keys before
 | |
|   error encountered, it returns the sense keys with return status set to EFI_SUCCESS,
 | |
|   and NeedRetry set to FALSE; otherwize, return the proper return status.
 | |
| 
 | |
|   @param  ScsiDiskDevice     The pointer of SCSI_DISK_DEV
 | |
|   @param  NeedRetry          The pointer of flag indicates if need a retry
 | |
|   @param  SenseDataArray     The pointer of an array of sense data
 | |
|   @param  NumberOfSenseKeys  The number of sense key
 | |
|   @param  AskResetIfError    The flag indicates if need reset when error occurs
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR   Indicates that error occurs
 | |
|   @retval EFI_SUCCESS        Successfully to request sense key
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ScsiDiskRequestSenseKeys (
 | |
|   IN  OUT  SCSI_DISK_DEV           *ScsiDiskDevice,
 | |
|       OUT  BOOLEAN                 *NeedRetry,
 | |
|       OUT  EFI_SCSI_SENSE_DATA     **SenseDataArray,
 | |
|       OUT  UINTN                   *NumberOfSenseKeys,
 | |
|   IN       BOOLEAN                 AskResetIfError
 | |
|   )
 | |
| {
 | |
|   EFI_SCSI_SENSE_DATA *PtrSenseData;
 | |
|   UINT8               SenseDataLength;
 | |
|   BOOLEAN             SenseReq;
 | |
|   EFI_STATUS          Status;
 | |
|   EFI_STATUS          FallStatus;
 | |
|   UINT8               HostAdapterStatus;
 | |
|   UINT8               TargetStatus;
 | |
| 
 | |
|   FallStatus      = EFI_SUCCESS;
 | |
|   SenseDataLength = (UINT8) sizeof (EFI_SCSI_SENSE_DATA);
 | |
| 
 | |
|   ZeroMem (
 | |
|     ScsiDiskDevice->SenseData,
 | |
|     sizeof (EFI_SCSI_SENSE_DATA) * (ScsiDiskDevice->SenseDataNumber)
 | |
|     );
 | |
| 
 | |
|   *NumberOfSenseKeys  = 0;
 | |
|   *SenseDataArray     = ScsiDiskDevice->SenseData;
 | |
|   PtrSenseData        = ScsiDiskDevice->SenseData;
 | |
| 
 | |
|   for (SenseReq = TRUE; SenseReq;) {
 | |
|     Status = ScsiRequestSenseCommand (
 | |
|               ScsiDiskDevice->ScsiIo,
 | |
|               EFI_TIMER_PERIOD_SECONDS (2),
 | |
|               PtrSenseData,
 | |
|               &SenseDataLength,
 | |
|               &HostAdapterStatus,
 | |
|               &TargetStatus
 | |
|               );
 | |
|      if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {
 | |
|         FallStatus = EFI_SUCCESS;
 | |
|   
 | |
|      } else if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {
 | |
|        *NeedRetry  = TRUE;
 | |
|        FallStatus  = EFI_DEVICE_ERROR;
 | |
|  
 | |
|      } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {
 | |
|        *NeedRetry  = FALSE;
 | |
|        FallStatus  = EFI_DEVICE_ERROR;
 | |
|  
 | |
|      } else if (Status == EFI_DEVICE_ERROR) {
 | |
|         if (AskResetIfError) {
 | |
|           ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
 | |
|         }
 | |
|   
 | |
|         FallStatus = EFI_DEVICE_ERROR;
 | |
|     }
 | |
| 
 | |
|     if (EFI_ERROR (FallStatus)) {
 | |
|       if (*NumberOfSenseKeys != 0) {
 | |
|         *NeedRetry = FALSE;
 | |
|         return EFI_SUCCESS;
 | |
|       } else {
 | |
|         return EFI_DEVICE_ERROR;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     (*NumberOfSenseKeys) += 1;
 | |
| 
 | |
|     //
 | |
|     // no more sense key or number of sense keys exceeds predefined,
 | |
|     // skip the loop.
 | |
|     //
 | |
|     if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) || 
 | |
|         (*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber)) {
 | |
|       SenseReq = FALSE;
 | |
|     }
 | |
|     PtrSenseData += 1;
 | |
|   }
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Get information from media read capacity command.
 | |
| 
 | |
|   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
 | |
|   @param  Capacity10      The pointer of EFI_SCSI_DISK_CAPACITY_DATA
 | |
|   @param  Capacity16      The pointer of EFI_SCSI_DISK_CAPACITY_DATA16
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| GetMediaInfo (
 | |
|   IN OUT SCSI_DISK_DEV                  *ScsiDiskDevice,
 | |
|   IN     EFI_SCSI_DISK_CAPACITY_DATA    *Capacity10,
 | |
|   IN     EFI_SCSI_DISK_CAPACITY_DATA16  *Capacity16
 | |
|   )
 | |
| {
 | |
|   UINT8       *Ptr;
 | |
| 
 | |
|   if (!ScsiDiskDevice->Cdb16Byte) {
 | |
|     ScsiDiskDevice->BlkIo.Media->LastBlock =  (Capacity10->LastLba3 << 24) |
 | |
|                                               (Capacity10->LastLba2 << 16) |
 | |
|                                               (Capacity10->LastLba1 << 8)  |
 | |
|                                                Capacity10->LastLba0;
 | |
|   
 | |
|     ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity10->BlockSize3 << 24) |
 | |
|                                              (Capacity10->BlockSize2 << 16) | 
 | |
|                                              (Capacity10->BlockSize1 << 8)  |
 | |
|                                               Capacity10->BlockSize0;
 | |
|     ScsiDiskDevice->BlkIo.Media->LowestAlignedLba               = 0;
 | |
|     ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock  = 0;
 | |
|   } else {
 | |
|     Ptr = (UINT8*)&ScsiDiskDevice->BlkIo.Media->LastBlock;
 | |
|     *Ptr++ = Capacity16->LastLba0;
 | |
|     *Ptr++ = Capacity16->LastLba1;
 | |
|     *Ptr++ = Capacity16->LastLba2;
 | |
|     *Ptr++ = Capacity16->LastLba3;
 | |
|     *Ptr++ = Capacity16->LastLba4;
 | |
|     *Ptr++ = Capacity16->LastLba5;
 | |
|     *Ptr++ = Capacity16->LastLba6;
 | |
|     *Ptr   = Capacity16->LastLba7;
 | |
| 
 | |
|     ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity16->BlockSize3 << 24) |
 | |
|                                              (Capacity16->BlockSize2 << 16) | 
 | |
|                                              (Capacity16->BlockSize1 << 8)  |
 | |
|                                               Capacity16->BlockSize0;
 | |
| 
 | |
|     ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = (Capacity16->LowestAlignLogic2 << 8) |
 | |
|                                                      Capacity16->LowestAlignLogic1;
 | |
|     ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock  = (1 << Capacity16->LogicPerPhysical);
 | |
|   }
 | |
| 
 | |
|   ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;
 | |
|   
 | |
|   if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {
 | |
|     ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_CDROM) {
 | |
|     ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Parse Inquiry data.
 | |
| 
 | |
|   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| ParseInquiryData (
 | |
|   IN OUT SCSI_DISK_DEV   *ScsiDiskDevice
 | |
|   )
 | |
| {
 | |
|   ScsiDiskDevice->FixedDevice               = (BOOLEAN) ((ScsiDiskDevice->InquiryData.Rmb == 1) ? 0 : 1);
 | |
|   ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN) (!ScsiDiskDevice->FixedDevice);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Read sector from SCSI Disk.
 | |
| 
 | |
|   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
 | |
|   @param  Buffer          The buffer to fill in the read out data
 | |
|   @param  Lba             Logic block address
 | |
|   @param  NumberOfBlocks  The number of blocks to read
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR  Indicates a device error.
 | |
|   @retval EFI_SUCCESS       Operation is successful.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ScsiDiskReadSectors (
 | |
|   IN   SCSI_DISK_DEV     *ScsiDiskDevice,
 | |
|   OUT  VOID              *Buffer,
 | |
|   IN   EFI_LBA           Lba,
 | |
|   IN   UINTN             NumberOfBlocks
 | |
|   )
 | |
| {
 | |
|   UINTN               BlocksRemaining;
 | |
|   UINT8               *PtrBuffer;
 | |
|   UINT32              BlockSize;
 | |
|   UINT32              ByteCount;
 | |
|   UINT32              MaxBlock;
 | |
|   UINT32              SectorCount;
 | |
|   UINT64              Timeout;
 | |
|   EFI_STATUS          Status;
 | |
|   UINT8               Index;
 | |
|   UINT8               MaxRetry;
 | |
|   BOOLEAN             NeedRetry;
 | |
|   EFI_SCSI_SENSE_DATA *SenseData;
 | |
|   UINTN               NumberOfSenseKeys;
 | |
| 
 | |
|   SenseData         = NULL;
 | |
|   NumberOfSenseKeys = 0;
 | |
| 
 | |
|   Status            = EFI_SUCCESS;
 | |
| 
 | |
|   BlocksRemaining   = NumberOfBlocks;
 | |
|   BlockSize         = ScsiDiskDevice->BlkIo.Media->BlockSize;
 | |
|   
 | |
|   //
 | |
|   // limit the data bytes that can be transferred by one Read(10) or Read(16) Command
 | |
|   //
 | |
|   if (!ScsiDiskDevice->Cdb16Byte) {
 | |
|     MaxBlock         = 0xFFFF;
 | |
|   } else {
 | |
|     MaxBlock         = 0xFFFFFFFF;
 | |
|   }
 | |
| 
 | |
|   PtrBuffer = Buffer;
 | |
| 
 | |
|   while (BlocksRemaining > 0) {
 | |
| 
 | |
|     if (BlocksRemaining <= MaxBlock) {
 | |
|       if (!ScsiDiskDevice->Cdb16Byte) {
 | |
|         SectorCount = (UINT16) BlocksRemaining;
 | |
|       } else {
 | |
|         SectorCount = (UINT32) BlocksRemaining;
 | |
|       }
 | |
|     } else {
 | |
|       SectorCount = MaxBlock;
 | |
|     }
 | |
| 
 | |
|     ByteCount = SectorCount * BlockSize;
 | |
|     Timeout   = EFI_TIMER_PERIOD_SECONDS (2);
 | |
| 
 | |
|     MaxRetry  = 2;
 | |
|     for (Index = 0; Index < MaxRetry; Index++) {
 | |
|       if (!ScsiDiskDevice->Cdb16Byte) {
 | |
|         Status = ScsiDiskRead10 (
 | |
|                   ScsiDiskDevice,
 | |
|                   &NeedRetry,
 | |
|                   &SenseData,
 | |
|                   &NumberOfSenseKeys,
 | |
|                   Timeout,
 | |
|                   PtrBuffer,
 | |
|                   &ByteCount,
 | |
|                   (UINT32) Lba,
 | |
|                   SectorCount
 | |
|                   );
 | |
|       } else {
 | |
|         Status = ScsiDiskRead16 (
 | |
|                   ScsiDiskDevice,
 | |
|                   &NeedRetry,
 | |
|                   &SenseData,
 | |
|                   &NumberOfSenseKeys,
 | |
|                   Timeout,
 | |
|                   PtrBuffer,
 | |
|                   &ByteCount,
 | |
|                   Lba,
 | |
|                   SectorCount
 | |
|                   );
 | |
|       }
 | |
|       if (!EFI_ERROR (Status)) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       if (!NeedRetry) {
 | |
|         return EFI_DEVICE_ERROR;
 | |
|       }
 | |
| 
 | |
|     }
 | |
| 
 | |
|     if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // actual transferred sectors
 | |
|     //
 | |
|     SectorCount = ByteCount / BlockSize;
 | |
| 
 | |
|     Lba += SectorCount;
 | |
|     PtrBuffer = PtrBuffer + SectorCount * BlockSize;
 | |
|     BlocksRemaining -= SectorCount;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Write sector to SCSI Disk.
 | |
| 
 | |
|   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
 | |
|   @param  Buffer          The buffer of data to be written into SCSI Disk
 | |
|   @param  Lba             Logic block address
 | |
|   @param  NumberOfBlocks  The number of blocks to read
 | |
| 
 | |
|   @retval EFI_DEVICE_ERROR  Indicates a device error.
 | |
|   @retval EFI_SUCCESS       Operation is successful.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ScsiDiskWriteSectors (
 | |
|   IN  SCSI_DISK_DEV     *ScsiDiskDevice,
 | |
|   IN  VOID              *Buffer,
 | |
|   IN  EFI_LBA           Lba,
 | |
|   IN  UINTN             NumberOfBlocks
 | |
|   )
 | |
| {
 | |
|   UINTN               BlocksRemaining;
 | |
|   UINT8               *PtrBuffer;
 | |
|   UINT32              BlockSize;
 | |
|   UINT32              ByteCount;
 | |
|   UINT32              MaxBlock;
 | |
|   UINT32              SectorCount;
 | |
|   UINT64              Timeout;
 | |
|   EFI_STATUS          Status;
 | |
|   UINT8               Index;
 | |
|   UINT8               MaxRetry;
 | |
|   BOOLEAN             NeedRetry;
 | |
|   EFI_SCSI_SENSE_DATA *SenseData;
 | |
|   UINTN               NumberOfSenseKeys;
 | |
| 
 | |
|   SenseData         = NULL;
 | |
|   NumberOfSenseKeys = 0;
 | |
| 
 | |
|   Status            = EFI_SUCCESS;
 | |
| 
 | |
|   BlocksRemaining   = NumberOfBlocks;
 | |
|   BlockSize         = ScsiDiskDevice->BlkIo.Media->BlockSize;
 | |
| 
 | |
|   //
 | |
|   // limit the data bytes that can be transferred by one Read(10) or Read(16) Command
 | |
|   //
 | |
|   if (!ScsiDiskDevice->Cdb16Byte) {
 | |
|     MaxBlock         = 0xFFFF;
 | |
|   } else {
 | |
|     MaxBlock         = 0xFFFFFFFF;
 | |
|   }
 | |
| 
 | |
|   PtrBuffer = Buffer;
 | |
| 
 | |
|   while (BlocksRemaining > 0) {
 | |
| 
 | |
|     if (BlocksRemaining <= MaxBlock) {
 | |
|       if (!ScsiDiskDevice->Cdb16Byte) {
 | |
|         SectorCount = (UINT16) BlocksRemaining;
 | |
|       } else {
 | |
|         SectorCount = (UINT32) BlocksRemaining;
 | |
|       }
 | |
|     } else {
 | |
|       SectorCount = MaxBlock;
 | |
|     }
 | |
| 
 | |
|     ByteCount = SectorCount * BlockSize;
 | |
|     Timeout   = EFI_TIMER_PERIOD_SECONDS (2);
 | |
|     MaxRetry  = 2;
 | |
|     for (Index = 0; Index < MaxRetry; Index++) {
 | |
|       if (!ScsiDiskDevice->Cdb16Byte) {
 | |
|         Status = ScsiDiskWrite10 (
 | |
|                   ScsiDiskDevice,
 | |
|                   &NeedRetry,
 | |
|                   &SenseData,
 | |
|                   &NumberOfSenseKeys,
 | |
|                   Timeout,
 | |
|                   PtrBuffer,
 | |
|                   &ByteCount,
 | |
|                   (UINT32) Lba,
 | |
|                   SectorCount
 | |
|                   );
 | |
|       } else {
 | |
|         Status = ScsiDiskWrite16 (
 | |
|                   ScsiDiskDevice,
 | |
|                   &NeedRetry,
 | |
|                   &SenseData,
 | |
|                   &NumberOfSenseKeys,
 | |
|                   Timeout,
 | |
|                   PtrBuffer,
 | |
|                   &ByteCount,
 | |
|                   Lba,
 | |
|                   SectorCount
 | |
|                   );         
 | |
|         }
 | |
|       if (!EFI_ERROR (Status)) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       if (!NeedRetry) {
 | |
|         return EFI_DEVICE_ERROR;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
|     //
 | |
|     // actual transferred sectors
 | |
|     //
 | |
|     SectorCount = ByteCount / BlockSize;
 | |
| 
 | |
|     Lba += SectorCount;
 | |
|     PtrBuffer = PtrBuffer + SectorCount * BlockSize;
 | |
|     BlocksRemaining -= SectorCount;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Submit Read(10) command.
 | |
| 
 | |
|   @param  ScsiDiskDevice     The pointer of ScsiDiskDevice
 | |
|   @param  NeedRetry          The pointer of flag indicates if needs retry if error happens
 | |
|   @param  SenseDataArray     NOT used yet in this function
 | |
|   @param  NumberOfSenseKeys  The number of sense key
 | |
|   @param  Timeout            The time to complete the command
 | |
|   @param  DataBuffer         The buffer to fill with the read out data
 | |
|   @param  DataLength         The length of buffer
 | |
|   @param  StartLba           The start logic block address
 | |
|   @param  SectorSize         The size of sector
 | |
| 
 | |
|   @return  EFI_STATUS is returned by calling ScsiRead10Command().
 | |
| **/
 | |
| EFI_STATUS
 | |
| ScsiDiskRead10 (
 | |
|   IN     SCSI_DISK_DEV         *ScsiDiskDevice,
 | |
|      OUT BOOLEAN               *NeedRetry,
 | |
|      OUT EFI_SCSI_SENSE_DATA   **SenseDataArray,   OPTIONAL
 | |
|      OUT UINTN                 *NumberOfSenseKeys,
 | |
|   IN     UINT64                Timeout,
 | |
|      OUT UINT8                 *DataBuffer,
 | |
|   IN OUT UINT32                *DataLength,
 | |
|   IN     UINT32                StartLba,
 | |
|   IN     UINT32                SectorSize
 | |
|   )
 | |
| {
 | |
|   UINT8       SenseDataLength;
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       HostAdapterStatus;
 | |
|   UINT8       TargetStatus;
 | |
| 
 | |
|   *NeedRetry          = FALSE;
 | |
|   *NumberOfSenseKeys  = 0;
 | |
|   SenseDataLength     = 0;
 | |
|   Status = ScsiRead10Command (
 | |
|             ScsiDiskDevice->ScsiIo,
 | |
|             Timeout,
 | |
|             NULL,
 | |
|             &SenseDataLength,
 | |
|             &HostAdapterStatus,
 | |
|             &TargetStatus,
 | |
|             DataBuffer,
 | |
|             DataLength,
 | |
|             StartLba,
 | |
|             SectorSize
 | |
|             );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Submit Write(10) Command.
 | |
| 
 | |
|   @param  ScsiDiskDevice     The pointer of ScsiDiskDevice
 | |
|   @param  NeedRetry          The pointer of flag indicates if needs retry if error happens
 | |
|   @param  SenseDataArray     NOT used yet in this function
 | |
|   @param  NumberOfSenseKeys  The number of sense key
 | |
|   @param  Timeout            The time to complete the command
 | |
|   @param  DataBuffer         The buffer to fill with the read out data
 | |
|   @param  DataLength         The length of buffer
 | |
|   @param  StartLba           The start logic block address
 | |
|   @param  SectorSize         The size of sector
 | |
| 
 | |
|   @return  EFI_STATUS is returned by calling ScsiWrite10Command().
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ScsiDiskWrite10 (
 | |
|   IN     SCSI_DISK_DEV         *ScsiDiskDevice,
 | |
|      OUT BOOLEAN               *NeedRetry,
 | |
|      OUT EFI_SCSI_SENSE_DATA   **SenseDataArray,   OPTIONAL
 | |
|      OUT UINTN                 *NumberOfSenseKeys,
 | |
|   IN     UINT64                Timeout,
 | |
|   IN     UINT8                 *DataBuffer,
 | |
|   IN OUT UINT32                *DataLength,
 | |
|   IN     UINT32                StartLba,
 | |
|   IN     UINT32                SectorSize
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       SenseDataLength;
 | |
|   UINT8       HostAdapterStatus;
 | |
|   UINT8       TargetStatus;
 | |
| 
 | |
|   *NeedRetry          = FALSE;
 | |
|   *NumberOfSenseKeys  = 0;
 | |
|   SenseDataLength     = 0;
 | |
|   Status = ScsiWrite10Command (
 | |
|             ScsiDiskDevice->ScsiIo,
 | |
|             Timeout,
 | |
|             NULL,
 | |
|             &SenseDataLength,
 | |
|             &HostAdapterStatus,
 | |
|             &TargetStatus,
 | |
|             DataBuffer,
 | |
|             DataLength,
 | |
|             StartLba,
 | |
|             SectorSize
 | |
|             );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Submit Read(16) command.
 | |
| 
 | |
|   @param  ScsiDiskDevice     The pointer of ScsiDiskDevice
 | |
|   @param  NeedRetry          The pointer of flag indicates if needs retry if error happens
 | |
|   @param  SenseDataArray     NOT used yet in this function
 | |
|   @param  NumberOfSenseKeys  The number of sense key
 | |
|   @param  Timeout            The time to complete the command
 | |
|   @param  DataBuffer         The buffer to fill with the read out data
 | |
|   @param  DataLength         The length of buffer
 | |
|   @param  StartLba           The start logic block address
 | |
|   @param  SectorSize         The size of sector
 | |
| 
 | |
|   @return  EFI_STATUS is returned by calling ScsiRead10Command().
 | |
| **/
 | |
| EFI_STATUS
 | |
| ScsiDiskRead16 (
 | |
|   IN     SCSI_DISK_DEV         *ScsiDiskDevice,
 | |
|      OUT BOOLEAN               *NeedRetry,
 | |
|      OUT EFI_SCSI_SENSE_DATA   **SenseDataArray,   OPTIONAL
 | |
|      OUT UINTN                 *NumberOfSenseKeys,
 | |
|   IN     UINT64                Timeout,
 | |
|      OUT UINT8                 *DataBuffer,
 | |
|   IN OUT UINT32                *DataLength,
 | |
|   IN     UINT64                StartLba,
 | |
|   IN     UINT32                SectorSize
 | |
|   )
 | |
| {
 | |
|   UINT8       SenseDataLength;
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       HostAdapterStatus;
 | |
|   UINT8       TargetStatus;
 | |
| 
 | |
|   *NeedRetry          = FALSE;
 | |
|   *NumberOfSenseKeys  = 0;
 | |
|   SenseDataLength     = 0;
 | |
|   Status = ScsiRead16Command (
 | |
|             ScsiDiskDevice->ScsiIo,
 | |
|             Timeout,
 | |
|             NULL,
 | |
|             &SenseDataLength,
 | |
|             &HostAdapterStatus,
 | |
|             &TargetStatus,
 | |
|             DataBuffer,
 | |
|             DataLength,
 | |
|             StartLba,
 | |
|             SectorSize
 | |
|             );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Submit Write(16) Command.
 | |
| 
 | |
|   @param  ScsiDiskDevice     The pointer of ScsiDiskDevice
 | |
|   @param  NeedRetry          The pointer of flag indicates if needs retry if error happens
 | |
|   @param  SenseDataArray     NOT used yet in this function
 | |
|   @param  NumberOfSenseKeys  The number of sense key
 | |
|   @param  Timeout            The time to complete the command
 | |
|   @param  DataBuffer         The buffer to fill with the read out data
 | |
|   @param  DataLength         The length of buffer
 | |
|   @param  StartLba           The start logic block address
 | |
|   @param  SectorSize         The size of sector
 | |
| 
 | |
|   @return  EFI_STATUS is returned by calling ScsiWrite10Command().
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ScsiDiskWrite16 (
 | |
|   IN     SCSI_DISK_DEV         *ScsiDiskDevice,
 | |
|      OUT BOOLEAN               *NeedRetry,
 | |
|      OUT EFI_SCSI_SENSE_DATA   **SenseDataArray,   OPTIONAL
 | |
|      OUT UINTN                 *NumberOfSenseKeys,
 | |
|   IN     UINT64                Timeout,
 | |
|   IN     UINT8                 *DataBuffer,
 | |
|   IN OUT UINT32                *DataLength,
 | |
|   IN     UINT64                StartLba,
 | |
|   IN     UINT32                SectorSize
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   UINT8       SenseDataLength;
 | |
|   UINT8       HostAdapterStatus;
 | |
|   UINT8       TargetStatus;
 | |
| 
 | |
|   *NeedRetry          = FALSE;
 | |
|   *NumberOfSenseKeys  = 0;
 | |
|   SenseDataLength     = 0;
 | |
|   Status = ScsiWrite16Command (
 | |
|             ScsiDiskDevice->ScsiIo,
 | |
|             Timeout,
 | |
|             NULL,
 | |
|             &SenseDataLength,
 | |
|             &HostAdapterStatus,
 | |
|             &TargetStatus,
 | |
|             DataBuffer,
 | |
|             DataLength,
 | |
|             StartLba,
 | |
|             SectorSize
 | |
|             );
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Check sense key to find if media presents.
 | |
| 
 | |
|   @param  SenseData   The pointer of EFI_SCSI_SENSE_DATA
 | |
|   @param  SenseCounts The number of sense key
 | |
| 
 | |
|   @retval TRUE    NOT any media
 | |
|   @retval FALSE   Media presents
 | |
| **/
 | |
| BOOLEAN
 | |
| ScsiDiskIsNoMedia (
 | |
|   IN  EFI_SCSI_SENSE_DATA   *SenseData,
 | |
|   IN  UINTN                 SenseCounts
 | |
|   )
 | |
| {
 | |
|   EFI_SCSI_SENSE_DATA *SensePtr;
 | |
|   UINTN               Index;
 | |
|   BOOLEAN             IsNoMedia;
 | |
| 
 | |
|   IsNoMedia = FALSE;
 | |
|   SensePtr  = SenseData;
 | |
| 
 | |
|   for (Index = 0; Index < SenseCounts; Index++) {
 | |
|     //
 | |
|     // Sense Key is EFI_SCSI_SK_NOT_READY (0x2),
 | |
|     // Additional Sense Code is ASC_NO_MEDIA (0x3A)
 | |
|     //
 | |
|     if ((SensePtr->Sense_Key == EFI_SCSI_SK_NOT_READY) &&
 | |
|         (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) {
 | |
|       IsNoMedia = TRUE;
 | |
|     }
 | |
|     SensePtr++;
 | |
|   }
 | |
| 
 | |
|   return IsNoMedia;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Parse sense key.
 | |
| 
 | |
|   @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA
 | |
|   @param  SenseCounts  The number of sense key
 | |
| 
 | |
|   @retval TRUE   Error
 | |
|   @retval FALSE  NOT error
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| ScsiDiskIsMediaError (
 | |
|   IN  EFI_SCSI_SENSE_DATA   *SenseData,
 | |
|   IN  UINTN                 SenseCounts
 | |
|   )
 | |
| {
 | |
|   EFI_SCSI_SENSE_DATA *SensePtr;
 | |
|   UINTN               Index;
 | |
|   BOOLEAN             IsError;
 | |
| 
 | |
|   IsError   = FALSE;
 | |
|   SensePtr  = SenseData;
 | |
| 
 | |
|   for (Index = 0; Index < SenseCounts; Index++) {
 | |
| 
 | |
|     switch (SensePtr->Sense_Key) {
 | |
| 
 | |
|     case EFI_SCSI_SK_MEDIUM_ERROR:
 | |
|       //
 | |
|       // Sense Key is EFI_SCSI_SK_MEDIUM_ERROR (0x3)
 | |
|       //
 | |
|       switch (SensePtr->Addnl_Sense_Code) {
 | |
| 
 | |
|       //
 | |
|       // fall through
 | |
|       //
 | |
|       case EFI_SCSI_ASC_MEDIA_ERR1:
 | |
| 
 | |
|       //
 | |
|       // fall through
 | |
|       //
 | |
|       case EFI_SCSI_ASC_MEDIA_ERR2:
 | |
| 
 | |
|       //
 | |
|       // fall through
 | |
|       //
 | |
|       case EFI_SCSI_ASC_MEDIA_ERR3:
 | |
|       case EFI_SCSI_ASC_MEDIA_ERR4:
 | |
|         IsError = TRUE;
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case EFI_SCSI_SK_NOT_READY:
 | |
|       //
 | |
|       // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)
 | |
|       //
 | |
|       switch (SensePtr->Addnl_Sense_Code) {
 | |
|       //
 | |
|       // Additional Sense Code is ASC_MEDIA_UPSIDE_DOWN (0x6)
 | |
|       //
 | |
|       case EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN:
 | |
|         IsError = TRUE;
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         break;
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     SensePtr++;
 | |
|   }
 | |
| 
 | |
|   return IsError;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Check sense key to find if hardware error happens.
 | |
| 
 | |
|   @param  SenseData     The pointer of EFI_SCSI_SENSE_DATA
 | |
|   @param  SenseCounts   The number of sense key
 | |
| 
 | |
|   @retval TRUE  Hardware error exits.
 | |
|   @retval FALSE NO error.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| ScsiDiskIsHardwareError (
 | |
|   IN  EFI_SCSI_SENSE_DATA   *SenseData,
 | |
|   IN  UINTN                 SenseCounts
 | |
|   )
 | |
| {
 | |
|   EFI_SCSI_SENSE_DATA *SensePtr;
 | |
|   UINTN               Index;
 | |
|   BOOLEAN             IsError;
 | |
| 
 | |
|   IsError   = FALSE;
 | |
|   SensePtr  = SenseData;
 | |
| 
 | |
|   for (Index = 0; Index < SenseCounts; Index++) {
 | |
|     
 | |
|     //
 | |
|     // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4)
 | |
|     //
 | |
|     if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {
 | |
|       IsError = TRUE;
 | |
|     }
 | |
| 
 | |
|     SensePtr++;
 | |
|   }
 | |
| 
 | |
|   return IsError;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Check sense key to find if media has changed.
 | |
| 
 | |
|   @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA
 | |
|   @param  SenseCounts  The number of sense key
 | |
| 
 | |
|   @retval TRUE   Media is changed.
 | |
|   @retval FALSE  Media is NOT changed.
 | |
| **/
 | |
| BOOLEAN
 | |
| ScsiDiskIsMediaChange (
 | |
|   IN  EFI_SCSI_SENSE_DATA   *SenseData,
 | |
|   IN  UINTN                 SenseCounts
 | |
|   )
 | |
| {
 | |
|   EFI_SCSI_SENSE_DATA *SensePtr;
 | |
|   UINTN               Index;
 | |
|   BOOLEAN             IsMediaChanged;
 | |
| 
 | |
|   IsMediaChanged  = FALSE;
 | |
|   SensePtr        = SenseData;
 | |
| 
 | |
|   for (Index = 0; Index < SenseCounts; Index++) {
 | |
|     //
 | |
|     // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6),
 | |
|     // Additional sense code is EFI_SCSI_ASC_MEDIA_CHANGE (0x28)
 | |
|     //
 | |
|     if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&
 | |
|         (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) {
 | |
|       IsMediaChanged = TRUE;
 | |
|     }
 | |
| 
 | |
|     SensePtr++;
 | |
|   }
 | |
| 
 | |
|   return IsMediaChanged;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check sense key to find if reset happens.
 | |
| 
 | |
|   @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA
 | |
|   @param  SenseCounts  The number of sense key
 | |
| 
 | |
|   @retval TRUE  It is reset before.
 | |
|   @retval FALSE It is NOT reset before.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| ScsiDiskIsResetBefore (
 | |
|   IN  EFI_SCSI_SENSE_DATA   *SenseData,
 | |
|   IN  UINTN                 SenseCounts
 | |
|   )
 | |
| {
 | |
|   EFI_SCSI_SENSE_DATA *SensePtr;
 | |
|   UINTN               Index;
 | |
|   BOOLEAN             IsResetBefore;
 | |
| 
 | |
|   IsResetBefore = FALSE;
 | |
|   SensePtr      = SenseData;
 | |
| 
 | |
|   for (Index = 0; Index < SenseCounts; Index++) {
 | |
|     
 | |
|     //
 | |
|     // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6)
 | |
|     // Additional Sense Code is EFI_SCSI_ASC_RESET (0x29)
 | |
|     //
 | |
|     if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&
 | |
|         (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) {
 | |
|       IsResetBefore = TRUE;
 | |
|     }
 | |
| 
 | |
|     SensePtr++;
 | |
|   }
 | |
| 
 | |
|   return IsResetBefore;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check sense key to find if the drive is ready.
 | |
| 
 | |
|   @param  SenseData    The pointer of EFI_SCSI_SENSE_DATA
 | |
|   @param  SenseCounts  The number of sense key
 | |
|   @param  RetryLater   The flag means if need a retry 
 | |
| 
 | |
|   @retval TRUE  Drive is ready.
 | |
|   @retval FALSE Drive is NOT ready.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| ScsiDiskIsDriveReady (
 | |
|   IN  EFI_SCSI_SENSE_DATA   *SenseData,
 | |
|   IN  UINTN                 SenseCounts,
 | |
|   OUT BOOLEAN               *RetryLater
 | |
|   )
 | |
| {
 | |
|   EFI_SCSI_SENSE_DATA *SensePtr;
 | |
|   UINTN               Index;
 | |
|   BOOLEAN             IsReady;
 | |
| 
 | |
|   IsReady     = TRUE;
 | |
|   *RetryLater = FALSE;
 | |
|   SensePtr    = SenseData;
 | |
| 
 | |
|   for (Index = 0; Index < SenseCounts; Index++) {
 | |
| 
 | |
|     switch (SensePtr->Sense_Key) {
 | |
| 
 | |
|     case EFI_SCSI_SK_NOT_READY:
 | |
|       //
 | |
|       // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)
 | |
|       //
 | |
|       switch (SensePtr->Addnl_Sense_Code) {
 | |
|       case EFI_SCSI_ASC_NOT_READY:
 | |
|         //
 | |
|         // Additional Sense Code is EFI_SCSI_ASC_NOT_READY (0x4)
 | |
|         //
 | |
|         switch (SensePtr->Addnl_Sense_Code_Qualifier) {
 | |
|         case EFI_SCSI_ASCQ_IN_PROGRESS:
 | |
|           //
 | |
|           // Additional Sense Code Qualifier is
 | |
|           // EFI_SCSI_ASCQ_IN_PROGRESS (0x1)
 | |
|           //
 | |
|           IsReady     = FALSE;
 | |
|           *RetryLater = TRUE;
 | |
|           break;
 | |
| 
 | |
|         default:
 | |
|           IsReady     = FALSE;
 | |
|           *RetryLater = FALSE;
 | |
|           break;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         break;
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     SensePtr++;
 | |
|   }
 | |
| 
 | |
|   return IsReady;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check sense key to find if it has sense key.
 | |
| 
 | |
|   @param  SenseData   - The pointer of EFI_SCSI_SENSE_DATA
 | |
|   @param  SenseCounts - The number of sense key
 | |
| 
 | |
|   @retval TRUE  It has sense key.
 | |
|   @retval FALSE It has NOT any sense key.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| ScsiDiskHaveSenseKey (
 | |
|   IN  EFI_SCSI_SENSE_DATA   *SenseData,
 | |
|   IN  UINTN                 SenseCounts
 | |
|   )
 | |
| {
 | |
|   EFI_SCSI_SENSE_DATA *SensePtr;
 | |
|   UINTN               Index;
 | |
|   BOOLEAN             HaveSenseKey;
 | |
| 
 | |
|   if (SenseCounts == 0) {
 | |
|     HaveSenseKey = FALSE;
 | |
|   } else {
 | |
|     HaveSenseKey = TRUE;
 | |
|   }
 | |
| 
 | |
|   SensePtr = SenseData;
 | |
| 
 | |
|   for (Index = 0; Index < SenseCounts; Index++) {
 | |
|     
 | |
|     //
 | |
|     // Sense Key is SK_NO_SENSE (0x0)
 | |
|     //
 | |
|     if ((SensePtr->Sense_Key == EFI_SCSI_SK_NO_SENSE) &&
 | |
|         (Index == 0)) {
 | |
|       HaveSenseKey = FALSE;
 | |
|     }
 | |
| 
 | |
|     SensePtr++;
 | |
|   }
 | |
| 
 | |
|   return HaveSenseKey;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Release resource about disk device.
 | |
| 
 | |
|   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| ReleaseScsiDiskDeviceResources (
 | |
|   IN  SCSI_DISK_DEV   *ScsiDiskDevice
 | |
|   )
 | |
| {
 | |
|   if (ScsiDiskDevice == NULL) {
 | |
|     return ;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskDevice->SenseData != NULL) {
 | |
|     FreePool (ScsiDiskDevice->SenseData);
 | |
|     ScsiDiskDevice->SenseData = NULL;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskDevice->ControllerNameTable != NULL) {
 | |
|     FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable);
 | |
|     ScsiDiskDevice->ControllerNameTable = NULL;
 | |
|   }
 | |
| 
 | |
|   FreePool (ScsiDiskDevice);
 | |
| 
 | |
|   ScsiDiskDevice = NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Determine if Block Io should be produced.
 | |
|   
 | |
| 
 | |
|   @param  ChildHandle  Child Handle to retrieve Parent information.
 | |
|   
 | |
|   @retval  TRUE    Should produce Block Io.
 | |
|   @retval  FALSE   Should not produce Block Io.
 | |
| 
 | |
| **/  
 | |
| BOOLEAN
 | |
| DetermineInstallBlockIo (
 | |
|   IN  EFI_HANDLE      ChildHandle
 | |
|   )  
 | |
| {
 | |
|   EFI_SCSI_PASS_THRU_PROTOCOL           *ScsiPassThru;
 | |
|   EFI_EXT_SCSI_PASS_THRU_PROTOCOL       *ExtScsiPassThru;
 | |
| 
 | |
|   //
 | |
|   // Firstly, check if ExtScsiPassThru Protocol parent handle exists. If existence,
 | |
|   // check its attribute, logic or physical.
 | |
|   //
 | |
|   ExtScsiPassThru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiExtScsiPassThruProtocolGuid, ChildHandle);
 | |
|   if (ExtScsiPassThru != NULL) {
 | |
|     if ((ExtScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {
 | |
|       return TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Secondly, check if ScsiPassThru Protocol parent handle exists. If existence,
 | |
|   // check its attribute, logic or physical.
 | |
|   //
 | |
|   ScsiPassThru = (EFI_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiScsiPassThruProtocolGuid, ChildHandle);
 | |
|   if (ScsiPassThru != NULL) {
 | |
|     if ((ScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {
 | |
|       return TRUE;
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Search protocol database and check to see if the protocol
 | |
|   specified by ProtocolGuid is present on a ControllerHandle and opened by
 | |
|   ChildHandle with an attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
 | |
|   If the ControllerHandle is found, then the protocol specified by ProtocolGuid
 | |
|   will be opened on it.  
 | |
|   
 | |
| 
 | |
|   @param  ProtocolGuid   ProtocolGuid pointer.
 | |
|   @param  ChildHandle    Child Handle to retrieve Parent information.
 | |
|   
 | |
| **/ 
 | |
| VOID *
 | |
| EFIAPI
 | |
| GetParentProtocol (
 | |
|   IN  EFI_GUID                          *ProtocolGuid,
 | |
|   IN  EFI_HANDLE                        ChildHandle
 | |
|   ) 
 | |
| {
 | |
|   UINTN                                 Index;
 | |
|   UINTN                                 HandleCount;
 | |
|   VOID                                  *Interface;  
 | |
|   EFI_STATUS                            Status;
 | |
|   EFI_HANDLE                            *HandleBuffer;
 | |
| 
 | |
|   //
 | |
|   // Retrieve the list of all handles from the handle database
 | |
|   //
 | |
|   Status = gBS->LocateHandleBuffer (
 | |
|                   ByProtocol,
 | |
|                   ProtocolGuid,
 | |
|                   NULL,
 | |
|                   &HandleCount,
 | |
|                   &HandleBuffer
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Iterate to find who is parent handle that is opened with ProtocolGuid by ChildHandle 
 | |
|   //
 | |
|   for (Index = 0; Index < HandleCount; Index++) {
 | |
|     Status = EfiTestChildHandle (HandleBuffer[Index], ChildHandle, ProtocolGuid);
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       Status = gBS->HandleProtocol (HandleBuffer[Index], ProtocolGuid, (VOID **)&Interface);
 | |
|       if (!EFI_ERROR (Status)) {
 | |
|         gBS->FreePool (HandleBuffer);
 | |
|         return Interface;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   gBS->FreePool (HandleBuffer);
 | |
|   return NULL;
 | |
| } 
 | |
| 
 | |
| /**
 | |
|   Provides inquiry information for the controller type.
 | |
|   
 | |
|   This function is used by the IDE bus driver to get inquiry data.  Data format
 | |
|   of Identify data is defined by the Interface GUID.
 | |
| 
 | |
|   @param[in]      This              Pointer to the EFI_DISK_INFO_PROTOCOL instance.
 | |
|   @param[in, out] InquiryData       Pointer to a buffer for the inquiry data.
 | |
|   @param[in, out] InquiryDataSize   Pointer to the value for the inquiry data size.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The command was accepted without any errors.
 | |
|   @retval EFI_NOT_FOUND          Device does not support this data class 
 | |
|   @retval EFI_DEVICE_ERROR       Error reading InquiryData from device 
 | |
|   @retval EFI_BUFFER_TOO_SMALL   InquiryDataSize not big enough 
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| ScsiDiskInfoInquiry (
 | |
|   IN     EFI_DISK_INFO_PROTOCOL   *This,
 | |
|   IN OUT VOID                     *InquiryData,
 | |
|   IN OUT UINT32                   *InquiryDataSize
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS      Status;
 | |
|   SCSI_DISK_DEV   *ScsiDiskDevice;
 | |
| 
 | |
|   ScsiDiskDevice  = SCSI_DISK_DEV_FROM_DISKINFO (This);
 | |
| 
 | |
|   Status = EFI_BUFFER_TOO_SMALL;
 | |
|   if (*InquiryDataSize >= sizeof (ScsiDiskDevice->InquiryData)) {
 | |
|     Status = EFI_SUCCESS;
 | |
|     CopyMem (InquiryData, &ScsiDiskDevice->InquiryData, sizeof (ScsiDiskDevice->InquiryData));
 | |
|   }
 | |
|   *InquiryDataSize = sizeof (ScsiDiskDevice->InquiryData);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Provides identify information for the controller type.
 | |
| 
 | |
|   This function is used by the IDE bus driver to get identify data.  Data format
 | |
|   of Identify data is defined by the Interface GUID.
 | |
| 
 | |
|   @param[in]      This              Pointer to the EFI_DISK_INFO_PROTOCOL 
 | |
|                                     instance.
 | |
|   @param[in, out] IdentifyData      Pointer to a buffer for the identify data.
 | |
|   @param[in, out] IdentifyDataSize  Pointer to the value for the identify data
 | |
|                                     size.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The command was accepted without any errors.
 | |
|   @retval EFI_NOT_FOUND          Device does not support this data class 
 | |
|   @retval EFI_DEVICE_ERROR       Error reading IdentifyData from device 
 | |
|   @retval EFI_BUFFER_TOO_SMALL   IdentifyDataSize not big enough 
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| ScsiDiskInfoIdentify (
 | |
|   IN     EFI_DISK_INFO_PROTOCOL   *This,
 | |
|   IN OUT VOID                     *IdentifyData,
 | |
|   IN OUT UINT32                   *IdentifyDataSize
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS      Status;
 | |
|   SCSI_DISK_DEV   *ScsiDiskDevice;
 | |
| 
 | |
|   if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {
 | |
|     //
 | |
|     // Physical SCSI bus does not support this data class. 
 | |
|     //
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   ScsiDiskDevice  = SCSI_DISK_DEV_FROM_DISKINFO (This);
 | |
| 
 | |
|   Status = EFI_BUFFER_TOO_SMALL;
 | |
|   if (*IdentifyDataSize >= sizeof (ScsiDiskDevice->IdentifyData)) {
 | |
|     Status = EFI_SUCCESS;
 | |
|     CopyMem (IdentifyData, &ScsiDiskDevice->IdentifyData, sizeof (ScsiDiskDevice->IdentifyData));
 | |
|   }
 | |
|   *IdentifyDataSize = sizeof (ScsiDiskDevice->IdentifyData);
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Provides sense data information for the controller type.
 | |
|   
 | |
|   This function is used by the IDE bus driver to get sense data. 
 | |
|   Data format of Sense data is defined by the Interface GUID.
 | |
| 
 | |
|   @param[in]      This              Pointer to the EFI_DISK_INFO_PROTOCOL instance.
 | |
|   @param[in, out] SenseData         Pointer to the SenseData.
 | |
|   @param[in, out] SenseDataSize     Size of SenseData in bytes.
 | |
|   @param[out]     SenseDataNumber   Pointer to the value for the sense data size.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The command was accepted without any errors.
 | |
|   @retval EFI_NOT_FOUND          Device does not support this data class.
 | |
|   @retval EFI_DEVICE_ERROR       Error reading SenseData from device.
 | |
|   @retval EFI_BUFFER_TOO_SMALL   SenseDataSize not big enough.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| ScsiDiskInfoSenseData (
 | |
|   IN     EFI_DISK_INFO_PROTOCOL   *This,
 | |
|   IN OUT VOID                     *SenseData,
 | |
|   IN OUT UINT32                   *SenseDataSize,
 | |
|   OUT    UINT8                    *SenseDataNumber
 | |
|   )
 | |
| {
 | |
|   return EFI_NOT_FOUND;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This function is used by the IDE bus driver to get controller information.
 | |
| 
 | |
|   @param[in]  This         Pointer to the EFI_DISK_INFO_PROTOCOL instance. 
 | |
|   @param[out] IdeChannel   Pointer to the Ide Channel number.  Primary or secondary.
 | |
|   @param[out] IdeDevice    Pointer to the Ide Device number.  Master or slave.
 | |
| 
 | |
|   @retval EFI_SUCCESS       IdeChannel and IdeDevice are valid.
 | |
|   @retval EFI_UNSUPPORTED   This is not an IDE device.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| ScsiDiskInfoWhichIde (
 | |
|   IN  EFI_DISK_INFO_PROTOCOL   *This,
 | |
|   OUT UINT32                   *IdeChannel,
 | |
|   OUT UINT32                   *IdeDevice
 | |
|   )
 | |
| {
 | |
|   SCSI_DISK_DEV   *ScsiDiskDevice;
 | |
| 
 | |
|   if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {
 | |
|     //
 | |
|     // This is not an IDE physical device.
 | |
|     //
 | |
|     return EFI_UNSUPPORTED;
 | |
|   }
 | |
| 
 | |
|   ScsiDiskDevice  = SCSI_DISK_DEV_FROM_DISKINFO (This);
 | |
|   *IdeChannel     = ScsiDiskDevice->Channel;
 | |
|   *IdeDevice      = ScsiDiskDevice->Device;
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Issues ATA IDENTIFY DEVICE command to identify ATAPI device.
 | |
| 
 | |
|   This function tries to fill 512-byte ATAPI_IDENTIFY_DATA for ATAPI device to
 | |
|   implement Identify() interface for DiskInfo protocol. The ATA command is sent
 | |
|   via SCSI Request Packet.
 | |
| 
 | |
|   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV
 | |
|   
 | |
|   @retval EFI_SUCCESS     The ATAPI device identify data were retrieved successfully.
 | |
|   @retval others          Some error occurred during the identification that ATAPI device.
 | |
| 
 | |
| **/  
 | |
| EFI_STATUS
 | |
| AtapiIdentifyDevice (
 | |
|   IN OUT SCSI_DISK_DEV   *ScsiDiskDevice
 | |
|   )
 | |
| {
 | |
|   EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;
 | |
|   UINT8                           Cdb[6];
 | |
| 
 | |
|   //
 | |
|   // Initialize SCSI REQUEST_PACKET and 6-byte Cdb
 | |
|   //
 | |
|   ZeroMem (&CommandPacket, sizeof (CommandPacket));
 | |
|   ZeroMem (Cdb, sizeof (Cdb));
 | |
| 
 | |
|   Cdb[0] = ATA_CMD_IDENTIFY_DEVICE;
 | |
|   CommandPacket.Timeout = EFI_TIMER_PERIOD_SECONDS (1);
 | |
|   CommandPacket.Cdb = Cdb;
 | |
|   CommandPacket.CdbLength = (UINT8) sizeof (Cdb);
 | |
|   CommandPacket.InDataBuffer = &ScsiDiskDevice->IdentifyData;
 | |
|   CommandPacket.InTransferLength = sizeof (ScsiDiskDevice->IdentifyData);
 | |
| 
 | |
|   return ScsiDiskDevice->ScsiIo->ExecuteScsiCommand (ScsiDiskDevice->ScsiIo, &CommandPacket, NULL);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Initialize the installation of DiskInfo protocol.
 | |
| 
 | |
|   This function prepares for the installation of DiskInfo protocol on the child handle.
 | |
|   By default, it installs DiskInfo protocol with SCSI interface GUID. If it further
 | |
|   detects that the physical device is an ATAPI/AHCI device, it then updates interface GUID
 | |
|   to be IDE/AHCI interface GUID.
 | |
| 
 | |
|   @param  ScsiDiskDevice  The pointer of SCSI_DISK_DEV.
 | |
|   @param  ChildHandle     Child handle to install DiskInfo protocol.
 | |
|   
 | |
| **/  
 | |
| VOID
 | |
| InitializeInstallDiskInfo (
 | |
|   IN  SCSI_DISK_DEV   *ScsiDiskDevice,
 | |
|   IN  EFI_HANDLE      ChildHandle
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                Status;
 | |
|   EFI_DEVICE_PATH_PROTOCOL  *DevicePathNode;
 | |
|   EFI_DEVICE_PATH_PROTOCOL  *ChildDevicePathNode;
 | |
|   ATAPI_DEVICE_PATH         *AtapiDevicePath;
 | |
|   SATA_DEVICE_PATH          *SataDevicePath;
 | |
|   UINTN                     IdentifyRetry;
 | |
| 
 | |
|   Status = gBS->HandleProtocol (ChildHandle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePathNode);
 | |
|   //
 | |
|   // Device Path protocol must be installed on the device handle. 
 | |
|   //
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
|   //
 | |
|   // Copy the DiskInfo protocol template.
 | |
|   //
 | |
|   CopyMem (&ScsiDiskDevice->DiskInfo, &gScsiDiskInfoProtocolTemplate, sizeof (gScsiDiskInfoProtocolTemplate));
 | |
| 
 | |
|   while (!IsDevicePathEnd (DevicePathNode)) {
 | |
|     ChildDevicePathNode = NextDevicePathNode (DevicePathNode);
 | |
|     if ((DevicePathType (DevicePathNode) == HARDWARE_DEVICE_PATH) &&
 | |
|         (DevicePathSubType (DevicePathNode) == HW_PCI_DP) &&
 | |
|         (DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) &&
 | |
|        ((DevicePathSubType (ChildDevicePathNode) == MSG_ATAPI_DP) ||
 | |
|         (DevicePathSubType (ChildDevicePathNode) == MSG_SATA_DP))) {
 | |
| 
 | |
|       IdentifyRetry = 3;
 | |
|       do {
 | |
|         //
 | |
|         // Issue ATA Identify Device Command via SCSI command, which is required to publish DiskInfo protocol
 | |
|         // with IDE/AHCI interface GUID.
 | |
|         //
 | |
|         Status = AtapiIdentifyDevice (ScsiDiskDevice);
 | |
|         if (!EFI_ERROR (Status)) {
 | |
|           if (DevicePathSubType(ChildDevicePathNode) == MSG_ATAPI_DP) {
 | |
|             //
 | |
|             // We find the valid ATAPI device path
 | |
|             //
 | |
|             AtapiDevicePath = (ATAPI_DEVICE_PATH *) ChildDevicePathNode;
 | |
|             ScsiDiskDevice->Channel = AtapiDevicePath->PrimarySecondary;
 | |
|             ScsiDiskDevice->Device = AtapiDevicePath->SlaveMaster;
 | |
|             //
 | |
|             // Update the DiskInfo.Interface to IDE interface GUID for the physical ATAPI device. 
 | |
|             //
 | |
|             CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoIdeInterfaceGuid);
 | |
|           } else {
 | |
|             //
 | |
|             // We find the valid SATA device path
 | |
|             //
 | |
|             SataDevicePath = (SATA_DEVICE_PATH *) ChildDevicePathNode;
 | |
|             ScsiDiskDevice->Channel = SataDevicePath->HBAPortNumber;
 | |
|             ScsiDiskDevice->Device = SataDevicePath->PortMultiplierPortNumber;
 | |
|             //
 | |
|             // Update the DiskInfo.Interface to AHCI interface GUID for the physical AHCI device. 
 | |
|             //
 | |
|             CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoAhciInterfaceGuid);
 | |
|           }
 | |
|           return;
 | |
|         }
 | |
|       } while (--IdentifyRetry > 0);
 | |
|     }
 | |
|     DevicePathNode = ChildDevicePathNode;
 | |
|   }
 | |
| 
 | |
|   return;
 | |
| }
 |