git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@7072 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			2191 lines
		
	
	
		
			58 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2191 lines
		
	
	
		
			58 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   SCSI disk driver that layers on every SCSI IO protocol in the system.
 | |
| 
 | |
| Copyright (c) 2006 - 2008, Intel Corporation. <BR>
 | |
| All rights reserved. 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
 | |
| };
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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;
 | |
| 
 | |
|   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.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;
 | |
|     break;
 | |
| 
 | |
|   case EFI_SCSI_TYPE_CDROM:
 | |
|     ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;
 | |
|     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, TRUE, &Temp);
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     Status = gBS->InstallMultipleProtocolInterfaces (
 | |
|                     &Controller,
 | |
|                     &gEfiBlockIoProtocolGuid,
 | |
|                     &ScsiDiskDevice->BlkIo,
 | |
|                     NULL
 | |
|                     );
 | |
|   }
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (ScsiDiskDevice->SenseData);
 | |
|     gBS->CloseProtocol (
 | |
|            Controller,
 | |
|            &gEfiScsiIoProtocolGuid,
 | |
|            This->DriverBindingHandle,
 | |
|            Controller
 | |
|            );
 | |
|     FreePool (ScsiDiskDevice);
 | |
|     return 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;
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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->UninstallProtocolInterface (
 | |
|                   Controller,
 | |
|                   &gEfiBlockIoProtocolGuid,
 | |
|                   &ScsiDiskDevice->BlkIo
 | |
|                   );
 | |
|   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 retured 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 (!ExtendedVerification) {
 | |
|     goto Done;
 | |
|   }
 | |
| 
 | |
|   Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
 | |
| 
 | |
| 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;
 | |
|   if (Buffer == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (BufferSize == 0) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   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
 | |
|             );
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // 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 % 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;
 | |
|   if (Buffer == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (BufferSize == 0) {
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   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
 | |
|             );
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // 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 % 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;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Dectect 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_STATUS          ReadCapacityStatus;
 | |
|   EFI_SCSI_SENSE_DATA *SenseData;
 | |
|   UINTN               NumberOfSenseKeys;
 | |
|   BOOLEAN             NeedRetry;
 | |
|   BOOLEAN             NeedReadCapacity;
 | |
|   UINT8               Index;
 | |
|   UINT8               MaxRetry;
 | |
|   EFI_BLOCK_IO_MEDIA  OldMedia;
 | |
|   UINTN               Action;
 | |
| 
 | |
|   Status              = EFI_SUCCESS;
 | |
|   ReadCapacityStatus  = EFI_SUCCESS;
 | |
|   SenseData           = NULL;
 | |
|   NumberOfSenseKeys   = 0;
 | |
|   NeedReadCapacity    = FALSE;
 | |
|   CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia));
 | |
|   *MediaChange        = FALSE;
 | |
|   MaxRetry            = 3;
 | |
| 
 | |
|   for (Index = 0; Index < MaxRetry; Index++) {
 | |
|     Status = ScsiDiskTestUnitReady (
 | |
|               ScsiDiskDevice,
 | |
|               &NeedRetry,
 | |
|               &SenseData,
 | |
|               &NumberOfSenseKeys
 | |
|               );
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if (!NeedRetry) {
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if ((Index == MaxRetry) && EFI_ERROR (Status)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   Status = DetectMediaParsingSenseKeys (
 | |
|             ScsiDiskDevice,
 | |
|             SenseData,
 | |
|             NumberOfSenseKeys,
 | |
|             &Action
 | |
|             );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     return Status;
 | |
|   }
 | |
|   //
 | |
|   // ACTION_NO_ACTION: need not read capacity
 | |
|   // other action code: need read capacity
 | |
|   //
 | |
|   if (Action == ACTION_NO_ACTION) {
 | |
|     NeedReadCapacity = FALSE;
 | |
|   } else {
 | |
|     NeedReadCapacity = TRUE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,
 | |
|   // retrieve capacity via Read Capacity command
 | |
|   //
 | |
|   if (NeedReadCapacity || MustReadCapacity) {
 | |
|     //
 | |
|     // retrieve media information
 | |
|     //
 | |
|     MaxRetry = 3;
 | |
|     for (Index = 0; Index < MaxRetry; Index++) {
 | |
| 
 | |
|       ReadCapacityStatus = ScsiDiskReadCapacity (
 | |
|                             ScsiDiskDevice,
 | |
|                             &NeedRetry,
 | |
|                             &SenseData,
 | |
|                             &NumberOfSenseKeys
 | |
|                             );
 | |
|       if (EFI_ERROR (ReadCapacityStatus) && !NeedRetry) {
 | |
|         return EFI_DEVICE_ERROR;
 | |
|       }
 | |
|       //
 | |
|       // analyze sense key to action
 | |
|       //
 | |
|       Status = DetectMediaParsingSenseKeys (
 | |
|                 ScsiDiskDevice,
 | |
|                 SenseData,
 | |
|                 NumberOfSenseKeys,
 | |
|                 &Action
 | |
|                 );
 | |
|       //
 | |
|       // if Status is error, it may indicate crisis error,
 | |
|       // so return without retry.
 | |
|       //
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         return Status;
 | |
|       }
 | |
| 
 | |
|       switch (Action) {
 | |
|       case ACTION_NO_ACTION:
 | |
|         //
 | |
|         // no retry
 | |
|         //
 | |
|         Index = MaxRetry;
 | |
|         break;
 | |
| 
 | |
|       case ACTION_RETRY_COMMAND_LATER:
 | |
|         //
 | |
|         // retry the ReadCapacity later and continuously, until the condition
 | |
|         // no longer emerges.
 | |
|         // stall time is 100000us, or say 0.1 second.
 | |
|         //
 | |
|         gBS->Stall (100000);
 | |
|         Index = 0;
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         //
 | |
|         // other cases, just retry the command
 | |
|         //
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if ((Index == MaxRetry) && EFI_ERROR (ReadCapacityStatus)) {
 | |
|       return EFI_DEVICE_ERROR;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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;
 | |
| 
 | |
|   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);
 | |
|      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 SubmitInquiryCommand() failed.
 | |
|   // if ScsiDiskRequestSenseKeys() succeeds at last,
 | |
|   // better retry SubmitInquiryCommand(). (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 deivice.
 | |
| 
 | |
|   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) {
 | |
|     *Action = ACTION_NO_ACTION;
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
| 
 | |
|   if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {
 | |
|     //
 | |
|     // No Sense Key returned from last submitted command
 | |
|     //
 | |
|     *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 (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {
 | |
|     ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;
 | |
|     ScsiDiskDevice->BlkIo.Media->LastBlock    = 0;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {
 | |
|     if (RetryLater) {
 | |
|       *Action = ACTION_RETRY_COMMAND_LATER;
 | |
|       return EFI_SUCCESS;
 | |
|     }
 | |
| 
 | |
|     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
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ScsiDiskReadCapacity (
 | |
|   IN  OUT  SCSI_DISK_DEV           *ScsiDiskDevice,
 | |
|       OUT  BOOLEAN                 *NeedRetry,
 | |
|       OUT  EFI_SCSI_SENSE_DATA     **SenseDataArray,
 | |
|       OUT  UINTN                   *NumberOfSenseKeys
 | |
|   )
 | |
| {
 | |
|   EFI_SCSI_DISK_CAPACITY_DATA CapacityData;
 | |
|   UINT32                      DataLength;
 | |
|   UINT8                       HostAdapterStatus;
 | |
|   UINT8                       TargetStatus;
 | |
|   EFI_STATUS                  CommandStatus;
 | |
|   EFI_STATUS                  Status;
 | |
|   UINT8                       Index;
 | |
|   UINT8                       MaxRetry;
 | |
|   UINT8                       SenseDataLength;
 | |
| 
 | |
|   SenseDataLength = 0;
 | |
|   ZeroMem (&CapacityData, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));
 | |
|   DataLength          = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);
 | |
| 
 | |
|   *NumberOfSenseKeys  = 0;
 | |
|   *NeedRetry          = FALSE;
 | |
|   //
 | |
|   // submit Read Capacity Command. in this call,not request sense data
 | |
|   //
 | |
|   CommandStatus = ScsiReadCapacityCommand (
 | |
|                     ScsiDiskDevice->ScsiIo,
 | |
|                     EFI_TIMER_PERIOD_SECONDS (1),
 | |
|                     NULL,
 | |
|                     &SenseDataLength,
 | |
|                     &HostAdapterStatus,
 | |
|                     &TargetStatus,
 | |
|                     (VOID *) &CapacityData,
 | |
|                     &DataLength,
 | |
|                     FALSE
 | |
|                     );
 | |
|   //
 | |
|     // no need to check HostAdapterStatus and TargetStatus
 | |
|     //
 | |
|    if (CommandStatus == EFI_SUCCESS) {
 | |
|      GetMediaInfo (ScsiDiskDevice, &CapacityData);
 | |
|      return EFI_SUCCESS;
 | |
|  
 | |
|    } else 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 SubmitReadCapacityCommand() failed.
 | |
|   // if ScsiDiskRequestSenseKeys() succeeds at last,
 | |
|   // better retry SubmitReadCapacityCommand(). (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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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 encounterred, 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 = 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  Capacity        The pointer of EFI_SCSI_DISK_CAPACITY_DATA
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| GetMediaInfo (
 | |
|   IN  OUT  SCSI_DISK_DEV                 *ScsiDiskDevice,
 | |
|   IN       EFI_SCSI_DISK_CAPACITY_DATA   *Capacity
 | |
|   )
 | |
| {
 | |
|   ScsiDiskDevice->BlkIo.Media->LastBlock =  (Capacity->LastLba3 << 24) |
 | |
|                                             (Capacity->LastLba2 << 16) |
 | |
|                                             (Capacity->LastLba1 << 8)  |
 | |
|                                              Capacity->LastLba0;
 | |
| 
 | |
|   ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;
 | |
|   ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity->BlockSize3 << 24) |
 | |
|                                            (Capacity->BlockSize2 << 16) | 
 | |
|                                            (Capacity->BlockSize1 << 8)  |
 | |
|                                             Capacity->BlockSize0;
 | |
|   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 ? 0 : 1);
 | |
|   ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN) (!ScsiDiskDevice->FixedDevice);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Read sector from SCSI Disk.
 | |
| 
 | |
|   @param  ScsiDiskDevice  The poiniter 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;
 | |
|   UINT32              Lba32;
 | |
|   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) Command
 | |
|   //
 | |
|   MaxBlock  = 65536;
 | |
| 
 | |
|   PtrBuffer = Buffer;
 | |
|   Lba32     = (UINT32) Lba;
 | |
| 
 | |
|   while (BlocksRemaining > 0) {
 | |
| 
 | |
|     if (BlocksRemaining <= MaxBlock) {
 | |
| 
 | |
|       SectorCount = (UINT16) BlocksRemaining;
 | |
|     } else {
 | |
| 
 | |
|       SectorCount = MaxBlock;
 | |
|     }
 | |
| 
 | |
|     ByteCount = SectorCount * BlockSize;
 | |
|     Timeout   = EFI_TIMER_PERIOD_SECONDS (2);
 | |
| 
 | |
|     MaxRetry  = 2;
 | |
|     for (Index = 0; Index < MaxRetry; Index++) {
 | |
| 
 | |
|       Status = ScsiDiskRead10 (
 | |
|                 ScsiDiskDevice,
 | |
|                 &NeedRetry,
 | |
|                 &SenseData,
 | |
|                 &NumberOfSenseKeys,
 | |
|                 Timeout,
 | |
|                 PtrBuffer,
 | |
|                 &ByteCount,
 | |
|                 Lba32,
 | |
|                 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;
 | |
| 
 | |
|     Lba32 += SectorCount;
 | |
|     PtrBuffer = PtrBuffer + SectorCount * BlockSize;
 | |
|     BlocksRemaining -= SectorCount;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Write sector to SCSI Disk.
 | |
| 
 | |
|   @param  ScsiDiskDevice  The poiniter 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;
 | |
|   UINT32              Lba32;
 | |
|   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 Write(10) Command
 | |
|   //
 | |
|   MaxBlock  = 65536;
 | |
| 
 | |
|   PtrBuffer = Buffer;
 | |
|   Lba32     = (UINT32) Lba;
 | |
| 
 | |
|   while (BlocksRemaining > 0) {
 | |
| 
 | |
|     if (BlocksRemaining <= MaxBlock) {
 | |
| 
 | |
|       SectorCount = (UINT16) BlocksRemaining;
 | |
|     } else {
 | |
| 
 | |
|       SectorCount = MaxBlock;
 | |
|     }
 | |
| 
 | |
|     ByteCount = SectorCount * BlockSize;
 | |
|     Timeout   = EFI_TIMER_PERIOD_SECONDS (2);
 | |
|     MaxRetry  = 2;
 | |
|     for (Index = 0; Index < MaxRetry; Index++) {
 | |
|       Status = ScsiDiskWrite10 (
 | |
|                 ScsiDiskDevice,
 | |
|                 &NeedRetry,
 | |
|                 &SenseData,
 | |
|                 &NumberOfSenseKeys,
 | |
|                 Timeout,
 | |
|                 PtrBuffer,
 | |
|                 &ByteCount,
 | |
|                 Lba32,
 | |
|                 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;
 | |
| 
 | |
|     Lba32 += SectorCount;
 | |
|     PtrBuffer = PtrBuffer + SectorCount * BlockSize;
 | |
|     BlocksRemaining -= SectorCount;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Sumbmit Read 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 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;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   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  Medit 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;
 | |
| }
 |