/** @file\r
SCSI disk driver that layers on every SCSI IO protocol in the system.\r
\r
-Copyright (c) 2006 - 2008, Intel Corporation. <BR>\r
-All rights reserved. This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution. The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 1985 - 2022, American Megatrends International LLC.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
-#include <Uefi.h>\r
-\r
-\r
-#include <Protocol/ScsiIo.h>\r
-#include <Protocol/ComponentName.h>\r
-#include <Protocol/BlockIo.h>\r
-#include <Protocol/DriverBinding.h>\r
-#include <Protocol/ScsiPassThruExt.h>\r
-\r
-#include <Library/DebugLib.h>\r
-#include <Library/UefiDriverEntryPoint.h>\r
-#include <Library/UefiLib.h>\r
-#include <Library/BaseMemoryLib.h>\r
-#include <Library/ScsiLib.h>\r
-#include <Library/UefiBootServicesTableLib.h>\r
-\r
#include "ScsiDisk.h"\r
\r
-EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {\r
+EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {\r
ScsiDiskDriverBindingSupported,\r
ScsiDiskDriverBindingStart,\r
ScsiDiskDriverBindingStop,\r
NULL\r
};\r
\r
+EFI_DISK_INFO_PROTOCOL gScsiDiskInfoProtocolTemplate = {\r
+ EFI_DISK_INFO_SCSI_INTERFACE_GUID,\r
+ ScsiDiskInfoInquiry,\r
+ ScsiDiskInfoIdentify,\r
+ ScsiDiskInfoSenseData,\r
+ ScsiDiskInfoWhichIde\r
+};\r
+\r
+/**\r
+ Allocates an aligned buffer for SCSI disk.\r
+\r
+ This function allocates an aligned buffer for the SCSI disk to perform\r
+ SCSI IO operations. The alignment requirement is from SCSI IO interface.\r
+\r
+ @param ScsiDiskDevice The SCSI disk involved for the operation.\r
+ @param BufferSize The request buffer size.\r
+\r
+ @return A pointer to the aligned buffer or NULL if the allocation fails.\r
+\r
+**/\r
+VOID *\r
+AllocateAlignedBuffer (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN UINTN BufferSize\r
+ )\r
+{\r
+ return AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), ScsiDiskDevice->ScsiIo->IoAlign);\r
+}\r
+\r
+/**\r
+ Frees an aligned buffer for SCSI disk.\r
+\r
+ This function frees an aligned buffer for the SCSI disk to perform\r
+ SCSI IO operations.\r
+\r
+ @param Buffer The aligned buffer to be freed.\r
+ @param BufferSize The request buffer size.\r
+\r
+**/\r
+VOID\r
+FreeAlignedBuffer (\r
+ IN VOID *Buffer,\r
+ IN UINTN BufferSize\r
+ )\r
+{\r
+ if (Buffer != NULL) {\r
+ FreeAlignedPages (Buffer, EFI_SIZE_TO_PAGES (BufferSize));\r
+ }\r
+}\r
+\r
+/**\r
+ Remove trailing spaces from the string.\r
+\r
+ @param String The ASCII string to remove the trailing spaces.\r
+\r
+ @retval the new length of the string.\r
+**/\r
+UINTN\r
+RemoveTrailingSpaces (\r
+ IN OUT CHAR8 *String\r
+ )\r
+{\r
+ UINTN Length;\r
+\r
+ Length = AsciiStrLen (String);\r
+ if (Length == 0) {\r
+ return 0;\r
+ }\r
+\r
+ while ((Length > 0) && (String[Length-1] == ' ')) {\r
+ Length--;\r
+ }\r
+\r
+ String[Length] = '\0';\r
+ return Length;\r
+}\r
+\r
/**\r
- The user Entry Point for module ScsiDisk. The user code starts with this function.\r
+ The user Entry Point for module ScsiDisk.\r
+\r
+ The user code starts with this function.\r
+\r
+ @param ImageHandle The firmware allocated handle for the EFI image.\r
+ @param SystemTable A pointer to the EFI System Table.\r
\r
- @param[in] ImageHandle The firmware allocated handle for the EFI image. \r
- @param[in] SystemTable A pointer to the EFI System Table.\r
- \r
@retval EFI_SUCCESS The entry point is executed successfully.\r
@retval other Some error occurs when executing this entry point.\r
\r
**/\r
EFI_STATUS\r
EFIAPI\r
-InitializeScsiDisk(\r
- IN EFI_HANDLE ImageHandle,\r
- IN EFI_SYSTEM_TABLE *SystemTable\r
+InitializeScsiDisk (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
)\r
{\r
- EFI_STATUS Status;\r
+ EFI_STATUS Status;\r
\r
//\r
// Install driver model protocol(s).\r
);\r
ASSERT_EFI_ERROR (Status);\r
\r
-\r
return Status;\r
}\r
\r
+/**\r
+ Test to see if this driver supports ControllerHandle.\r
+\r
+ This service is called by the EFI boot service ConnectController(). In order\r
+ to make drivers as small as possible, there are a few calling restrictions for\r
+ this service. ConnectController() must follow these calling restrictions.\r
+ If any other agent wishes to call Supported() it must also follow these\r
+ calling restrictions.\r
+\r
+ @param This Protocol instance pointer.\r
+ @param ControllerHandle Handle of device to test\r
+ @param RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCESS This driver supports this device\r
+ @retval EFI_ALREADY_STARTED This driver is already running on this device\r
+ @retval other This driver does not support this device\r
+\r
+**/\r
EFI_STATUS\r
EFIAPI\r
ScsiDiskDriverBindingSupported (\r
IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
IN EFI_HANDLE Controller,\r
- IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Test to see if this driver supports ControllerHandle. Any ControllerHandle\r
- that has ScsiIoProtocol installed will be supported.\r
-\r
-Arguments:\r
-\r
- This - Protocol instance pointer.\r
- Controller - Handle of device to test\r
- RemainingDevicePath - Not used\r
-\r
-Returns:\r
-\r
- EFI_SUCCESS - This driver supports this device.\r
- EFI_UNSUPPORTED - This driver does not support this device.\r
- \r
- \r
---*/\r
{\r
EFI_STATUS Status;\r
EFI_SCSI_IO_PROTOCOL *ScsiIo;\r
Status = gBS->OpenProtocol (\r
Controller,\r
&gEfiScsiIoProtocolGuid,\r
- (VOID **) &ScsiIo,\r
+ (VOID **)&ScsiIo,\r
This->DriverBindingHandle,\r
Controller,\r
EFI_OPEN_PROTOCOL_BY_DRIVER\r
\r
Status = ScsiIo->GetDeviceType (ScsiIo, &DeviceType);\r
if (!EFI_ERROR (Status)) {\r
- if ((DeviceType == EFI_SCSI_TYPE_DISK) || (DeviceType == EFI_SCSI_TYPE_CDROM)) {\r
+ if ((DeviceType == EFI_SCSI_TYPE_DISK) ||\r
+ (DeviceType == EFI_SCSI_TYPE_CDROM) ||\r
+ (DeviceType == EFI_SCSI_TYPE_WLUN))\r
+ {\r
Status = EFI_SUCCESS;\r
} else {\r
Status = EFI_UNSUPPORTED;\r
return Status;\r
}\r
\r
+/**\r
+ Start this driver on ControllerHandle.\r
+\r
+ This service is called by the EFI boot service ConnectController(). In order\r
+ to make drivers as small as possible, there are a few calling restrictions for\r
+ this service. ConnectController() must follow these calling restrictions. If\r
+ any other agent wishes to call Start() it must also follow these calling\r
+ restrictions.\r
+\r
+ @param This Protocol instance pointer.\r
+ @param ControllerHandle Handle of device to bind driver to\r
+ @param RemainingDevicePath Optional parameter use to pick a specific child\r
+ device to start.\r
+\r
+ @retval EFI_SUCCESS This driver is added to ControllerHandle\r
+ @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle\r
+ @retval other This driver does not support this device\r
+\r
+**/\r
EFI_STATUS\r
EFIAPI\r
ScsiDiskDriverBindingStart (\r
IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
IN EFI_HANDLE Controller,\r
- IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Start SCSI Disk Driver, and attach BlockIoProtocol to it.\r
- \r
-Arguments:\r
-\r
- This - Protocol instance pointer.\r
- Controller - Handle of device to test\r
- RemainingDevicePath - Not used\r
-\r
-Returns:\r
-\r
- EFI_SUCCESS - This driver supports this device.\r
- EFI_UNSUPPORTED - This driver does not support this device.\r
- \r
- \r
---*/\r
{\r
EFI_STATUS Status;\r
EFI_SCSI_IO_PROTOCOL *ScsiIo;\r
UINT8 Index;\r
UINT8 MaxRetry;\r
BOOLEAN NeedRetry;\r
+ BOOLEAN MustReadCapacity;\r
+ CHAR8 VendorStr[VENDOR_IDENTIFICATION_LENGTH + 1];\r
+ CHAR8 ProductStr[PRODUCT_IDENTIFICATION_LENGTH + 1];\r
+ CHAR16 DeviceStr[VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2];\r
\r
- Status = gBS->AllocatePool (\r
- EfiBootServicesData,\r
- sizeof (SCSI_DISK_DEV),\r
- (VOID **) &ScsiDiskDevice\r
- );\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
+ MustReadCapacity = TRUE;\r
\r
- ZeroMem (ScsiDiskDevice, sizeof (SCSI_DISK_DEV));\r
+ ScsiDiskDevice = (SCSI_DISK_DEV *)AllocateZeroPool (sizeof (SCSI_DISK_DEV));\r
+ if (ScsiDiskDevice == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
\r
Status = gBS->OpenProtocol (\r
Controller,\r
&gEfiScsiIoProtocolGuid,\r
- (VOID **) &ScsiIo,\r
+ (VOID **)&ScsiIo,\r
This->DriverBindingHandle,\r
Controller,\r
EFI_OPEN_PROTOCOL_BY_DRIVER\r
);\r
if (EFI_ERROR (Status)) {\r
- gBS->FreePool (ScsiDiskDevice);\r
+ FreePool (ScsiDiskDevice);\r
return Status;\r
}\r
\r
- ScsiDiskDevice->Signature = SCSI_DISK_DEV_SIGNATURE;\r
- ScsiDiskDevice->ScsiIo = ScsiIo;\r
- ScsiDiskDevice->BlkIo.Media = &ScsiDiskDevice->BlkIoMedia;\r
- ScsiDiskDevice->BlkIo.Reset = ScsiDiskReset;\r
- ScsiDiskDevice->BlkIo.ReadBlocks = ScsiDiskReadBlocks;\r
- ScsiDiskDevice->BlkIo.WriteBlocks = ScsiDiskWriteBlocks;\r
- ScsiDiskDevice->BlkIo.FlushBlocks = ScsiDiskFlushBlocks;\r
- ScsiDiskDevice->Handle = Controller;\r
+ ScsiDiskDevice->Signature = SCSI_DISK_DEV_SIGNATURE;\r
+ ScsiDiskDevice->ScsiIo = ScsiIo;\r
+ ScsiDiskDevice->BlkIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3;\r
+ ScsiDiskDevice->BlkIo.Media = &ScsiDiskDevice->BlkIoMedia;\r
+ ScsiDiskDevice->BlkIo.Media->IoAlign = ScsiIo->IoAlign;\r
+ ScsiDiskDevice->BlkIo.Reset = ScsiDiskReset;\r
+ ScsiDiskDevice->BlkIo.ReadBlocks = ScsiDiskReadBlocks;\r
+ ScsiDiskDevice->BlkIo.WriteBlocks = ScsiDiskWriteBlocks;\r
+ ScsiDiskDevice->BlkIo.FlushBlocks = ScsiDiskFlushBlocks;\r
+ ScsiDiskDevice->BlkIo2.Media = &ScsiDiskDevice->BlkIoMedia;\r
+ ScsiDiskDevice->BlkIo2.Reset = ScsiDiskResetEx;\r
+ ScsiDiskDevice->BlkIo2.ReadBlocksEx = ScsiDiskReadBlocksEx;\r
+ ScsiDiskDevice->BlkIo2.WriteBlocksEx = ScsiDiskWriteBlocksEx;\r
+ ScsiDiskDevice->BlkIo2.FlushBlocksEx = ScsiDiskFlushBlocksEx;\r
+ ScsiDiskDevice->StorageSecurity.ReceiveData = ScsiDiskReceiveData;\r
+ ScsiDiskDevice->StorageSecurity.SendData = ScsiDiskSendData;\r
+ ScsiDiskDevice->EraseBlock.Revision = EFI_ERASE_BLOCK_PROTOCOL_REVISION;\r
+ ScsiDiskDevice->EraseBlock.EraseLengthGranularity = 1;\r
+ ScsiDiskDevice->EraseBlock.EraseBlocks = ScsiDiskEraseBlocks;\r
+ ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt = 1;\r
+ ScsiDiskDevice->BlockLimitsVpdSupported = FALSE;\r
+ ScsiDiskDevice->Handle = Controller;\r
+ InitializeListHead (&ScsiDiskDevice->AsyncTaskQueue);\r
\r
ScsiIo->GetDeviceType (ScsiIo, &(ScsiDiskDevice->DeviceType));\r
switch (ScsiDiskDevice->DeviceType) {\r
- case EFI_SCSI_TYPE_DISK:\r
- ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;\r
- break;\r
+ case EFI_SCSI_TYPE_DISK:\r
+ ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;\r
+ MustReadCapacity = TRUE;\r
+ break;\r
+\r
+ case EFI_SCSI_TYPE_CDROM:\r
+ ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;\r
+ ScsiDiskDevice->BlkIo.Media->ReadOnly = TRUE;\r
+ MustReadCapacity = FALSE;\r
+ break;\r
\r
- case EFI_SCSI_TYPE_CDROM:\r
- ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;\r
- break;\r
+ case EFI_SCSI_TYPE_WLUN:\r
+ MustReadCapacity = FALSE;\r
+ break;\r
}\r
+\r
//\r
// The Sense Data Array's initial size is 6\r
//\r
ScsiDiskDevice->SenseDataNumber = 6;\r
- Status = gBS->AllocatePool (\r
- EfiBootServicesData,\r
- sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber,\r
- (VOID **) &(ScsiDiskDevice->SenseData)\r
- );\r
- if (EFI_ERROR (Status)) {\r
+ ScsiDiskDevice->SenseData = (EFI_SCSI_SENSE_DATA *)AllocateZeroPool (\r
+ sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber\r
+ );\r
+ if (ScsiDiskDevice->SenseData == NULL) {\r
gBS->CloseProtocol (\r
- Controller,\r
- &gEfiScsiIoProtocolGuid,\r
- This->DriverBindingHandle,\r
- Controller\r
- );\r
- gBS->FreePool (ScsiDiskDevice);\r
- return Status;\r
+ Controller,\r
+ &gEfiScsiIoProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Controller\r
+ );\r
+ FreePool (ScsiDiskDevice);\r
+ return EFI_OUT_OF_RESOURCES;\r
}\r
\r
- ZeroMem (\r
- ScsiDiskDevice->SenseData,\r
- sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber\r
- );\r
-\r
//\r
- // Retrive device information\r
+ // Retrieve device information\r
//\r
MaxRetry = 2;\r
for (Index = 0; Index < MaxRetry; Index++) {\r
}\r
\r
if (!NeedRetry) {\r
- gBS->FreePool (ScsiDiskDevice->SenseData);\r
+ FreePool (ScsiDiskDevice->SenseData);\r
gBS->CloseProtocol (\r
Controller,\r
&gEfiScsiIoProtocolGuid,\r
This->DriverBindingHandle,\r
Controller\r
);\r
- gBS->FreePool (ScsiDiskDevice);\r
+ FreePool (ScsiDiskDevice);\r
return EFI_DEVICE_ERROR;\r
}\r
}\r
+\r
//\r
// The second parameter "TRUE" means must\r
// retrieve media capacity\r
//\r
- Status = ScsiDiskDetectMedia (ScsiDiskDevice, TRUE, &Temp);\r
+ Status = ScsiDiskDetectMedia (ScsiDiskDevice, MustReadCapacity, &Temp);\r
if (!EFI_ERROR (Status)) {\r
- Status = gBS->InstallMultipleProtocolInterfaces (\r
- &Controller,\r
- &gEfiBlockIoProtocolGuid,\r
- &ScsiDiskDevice->BlkIo,\r
- NULL\r
- );\r
- }\r
+ //\r
+ // Determine if Block IO & Block IO2 should be produced on this controller\r
+ // handle\r
+ //\r
+ if (DetermineInstallBlockIo (Controller)) {\r
+ InitializeInstallDiskInfo (ScsiDiskDevice, Controller);\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Controller,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &gEfiDiskInfoProtocolGuid,\r
+ &ScsiDiskDevice->DiskInfo,\r
+ NULL\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ if (DetermineInstallEraseBlock (ScsiDiskDevice, Controller)) {\r
+ Status = gBS->InstallProtocolInterface (\r
+ &Controller,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "ScsiDisk: Failed to install the Erase Block Protocol! Status = %r\n", Status));\r
+ }\r
+ }\r
\r
- if (EFI_ERROR (Status)) {\r
- gBS->FreePool (ScsiDiskDevice->SenseData);\r
- gBS->CloseProtocol (\r
- Controller,\r
- &gEfiScsiIoProtocolGuid,\r
- This->DriverBindingHandle,\r
- Controller\r
- );\r
- gBS->FreePool (ScsiDiskDevice);\r
- return Status;\r
+ if (DetermineInstallStorageSecurity (ScsiDiskDevice, Controller)) {\r
+ Status = gBS->InstallProtocolInterface (\r
+ &Controller,\r
+ &gEfiStorageSecurityCommandProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ &ScsiDiskDevice->StorageSecurity\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "ScsiDisk: Failed to install the Storage Security Command Protocol! Status = %r\n", Status));\r
+ }\r
+ }\r
+\r
+ CopyMem (\r
+ VendorStr,\r
+ &ScsiDiskDevice->InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET],\r
+ VENDOR_IDENTIFICATION_LENGTH\r
+ );\r
+ VendorStr[VENDOR_IDENTIFICATION_LENGTH] = 0;\r
+ RemoveTrailingSpaces (VendorStr);\r
+\r
+ CopyMem (\r
+ ProductStr,\r
+ &ScsiDiskDevice->InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET],\r
+ PRODUCT_IDENTIFICATION_LENGTH\r
+ );\r
+ ProductStr[PRODUCT_IDENTIFICATION_LENGTH] = 0;\r
+ RemoveTrailingSpaces (ProductStr);\r
+\r
+ UnicodeSPrint (DeviceStr, sizeof (DeviceStr), L"%a %a", VendorStr, ProductStr);\r
+\r
+ ScsiDiskDevice->ControllerNameTable = NULL;\r
+ AddUnicodeString2 (\r
+ "eng",\r
+ gScsiDiskComponentName.SupportedLanguages,\r
+ &ScsiDiskDevice->ControllerNameTable,\r
+ DeviceStr,\r
+ TRUE\r
+ );\r
+ AddUnicodeString2 (\r
+ "en",\r
+ gScsiDiskComponentName2.SupportedLanguages,\r
+ &ScsiDiskDevice->ControllerNameTable,\r
+ DeviceStr,\r
+ FALSE\r
+ );\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
}\r
\r
- ScsiDiskDevice->ControllerNameTable = NULL;\r
- AddUnicodeString2 (\r
- "eng",\r
- gScsiDiskComponentName.SupportedLanguages,\r
- &ScsiDiskDevice->ControllerNameTable,\r
- L"SCSI Disk Device",\r
- TRUE\r
- );\r
- AddUnicodeString2 (\r
- "en",\r
- gScsiDiskComponentName2.SupportedLanguages,\r
- &ScsiDiskDevice->ControllerNameTable,\r
- L"SCSI Disk Device",\r
- FALSE\r
- );\r
+ gBS->FreePool (ScsiDiskDevice->SenseData);\r
+ gBS->FreePool (ScsiDiskDevice);\r
+ gBS->CloseProtocol (\r
+ Controller,\r
+ &gEfiScsiIoProtocolGuid,\r
+ This->DriverBindingHandle,\r
+ Controller\r
+ );\r
+ return Status;\r
+}\r
\r
+/**\r
+ Stop this driver on ControllerHandle.\r
\r
- return EFI_SUCCESS;\r
+ This service is called by the EFI boot service DisconnectController().\r
+ In order to make drivers as small as possible, there are a few calling\r
+ restrictions for this service. DisconnectController() must follow these\r
+ calling restrictions. If any other agent wishes to call Stop() it must\r
+ also follow these calling restrictions.\r
\r
-}\r
+ @param This Protocol instance pointer.\r
+ @param ControllerHandle Handle of device to stop driver on\r
+ @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of\r
+ children is zero stop the entire bus driver.\r
+ @param ChildHandleBuffer List of Child Handles to Stop.\r
+\r
+ @retval EFI_SUCCESS This driver is removed ControllerHandle\r
+ @retval other This driver was not removed from this device\r
\r
+**/\r
EFI_STATUS\r
EFIAPI\r
ScsiDiskDriverBindingStop (\r
- IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
- IN EFI_HANDLE Controller,\r
- IN UINTN NumberOfChildren,\r
- IN EFI_HANDLE *ChildHandleBuffer\r
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
+ IN EFI_HANDLE Controller,\r
+ IN UINTN NumberOfChildren,\r
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Stop this driver on ControllerHandle. Support stoping any child handles\r
- created by this driver.\r
-\r
-Arguments:\r
-\r
- This - Protocol instance pointer.\r
- Controller - Handle of device to stop driver on\r
- NumberOfChildren - Number of Children in the ChildHandleBuffer\r
- ChildHandleBuffer - List of handles for the children we need to stop.\r
-\r
-Returns:\r
-\r
- EFI_SUCCESS\r
- EFI_DEVICE_ERROR\r
- others\r
- \r
---*/\r
{\r
- EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
- SCSI_DISK_DEV *ScsiDiskDevice;\r
- EFI_STATUS Status;\r
+ EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
+ EFI_ERASE_BLOCK_PROTOCOL *EraseBlock;\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_STATUS Status;\r
\r
Status = gBS->OpenProtocol (\r
Controller,\r
&gEfiBlockIoProtocolGuid,\r
- (VOID **) &BlkIo,\r
+ (VOID **)&BlkIo,\r
This->DriverBindingHandle,\r
Controller,\r
EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
return Status;\r
}\r
\r
- ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (BlkIo);\r
- Status = gBS->UninstallProtocolInterface (\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (BlkIo);\r
+\r
+ //\r
+ // Wait for the BlockIo2 requests queue to become empty\r
+ //\r
+ while (!IsListEmpty (&ScsiDiskDevice->AsyncTaskQueue)) {\r
+ }\r
+\r
+ //\r
+ // If Erase Block Protocol is installed, then uninstall this protocol.\r
+ //\r
+ Status = gBS->OpenProtocol (\r
+ Controller,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ (VOID **)&EraseBlock,\r
+ This->DriverBindingHandle,\r
+ Controller,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = gBS->UninstallProtocolInterface (\r
+ Controller,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ Status = gBS->UninstallMultipleProtocolInterfaces (\r
Controller,\r
&gEfiBlockIoProtocolGuid,\r
- &ScsiDiskDevice->BlkIo\r
+ &ScsiDiskDevice->BlkIo,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &gEfiDiskInfoProtocolGuid,\r
+ &ScsiDiskDevice->DiskInfo,\r
+ NULL\r
);\r
if (!EFI_ERROR (Status)) {\r
gBS->CloseProtocol (\r
\r
return EFI_SUCCESS;\r
}\r
+\r
//\r
// errors met\r
//\r
return Status;\r
}\r
\r
+/**\r
+ Reset SCSI Disk.\r
+\r
+\r
+ @param This The pointer of EFI_BLOCK_IO_PROTOCOL\r
+ @param ExtendedVerification The flag about if extend verificate\r
\r
+ @retval EFI_SUCCESS The device was reset.\r
+ @retval EFI_DEVICE_ERROR The device is not functioning properly and could\r
+ not be reset.\r
+ @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice().\r
+\r
+**/\r
EFI_STATUS\r
EFIAPI\r
ScsiDiskReset (\r
- IN EFI_BLOCK_IO_PROTOCOL *This,\r
- IN BOOLEAN ExtendedVerification\r
+ IN EFI_BLOCK_IO_PROTOCOL *This,\r
+ IN BOOLEAN ExtendedVerification\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Reset SCSI Disk \r
-\r
-Arguments:\r
-\r
- This - The pointer of EFI_BLOCK_IO_PROTOCOL\r
- ExtendedVerification - The flag about if extend verificate\r
-\r
-Returns:\r
-\r
- EFI_STATUS\r
-\r
---*/\r
{\r
- EFI_TPL OldTpl;\r
- SCSI_DISK_DEV *ScsiDiskDevice;\r
- EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_STATUS Status;\r
\r
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
\r
- ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);\r
\r
- Status = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+ Status = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_UNSUPPORTED) {\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+ }\r
\r
if (!ExtendedVerification) {\r
goto Done;\r
\r
Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
Done:\r
gBS->RestoreTPL (OldTpl);\r
return Status;\r
}\r
\r
+/**\r
+ The function is to Read Block from SCSI Disk.\r
+\r
+ @param This The pointer of EFI_BLOCK_IO_PROTOCOL.\r
+ @param MediaId The Id of Media detected\r
+ @param Lba The logic block address\r
+ @param BufferSize The size of Buffer\r
+ @param Buffer The buffer to fill the read out data\r
+\r
+ @retval EFI_SUCCESS Successfully to read out block.\r
+ @retval EFI_DEVICE_ERROR Fail to detect media.\r
+ @retval EFI_NO_MEDIA Media is not present.\r
+ @retval EFI_MEDIA_CHANGED Media has changed.\r
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter passed in.\r
+\r
+**/\r
EFI_STATUS\r
EFIAPI\r
ScsiDiskReadBlocks (\r
- IN EFI_BLOCK_IO_PROTOCOL *This,\r
- IN UINT32 MediaId,\r
- IN EFI_LBA LBA,\r
- IN UINTN BufferSize,\r
- OUT VOID *Buffer\r
+ IN EFI_BLOCK_IO_PROTOCOL *This,\r
+ IN UINT32 MediaId,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN BufferSize,\r
+ OUT VOID *Buffer\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- The function is to Read Block from SCSI Disk\r
-\r
-Arguments:\r
-\r
- This - The pointer of EFI_BLOCK_IO_PROTOCOL\r
- MediaId - The Id of Media detected\r
- LBA - The logic block address\r
- BufferSize - The size of Buffer\r
- Buffer - The buffer to fill the read out data\r
-\r
-Returns:\r
-\r
- EFI_INVALID_PARAMETER - Invalid parameter passed in.\r
- EFI_SUCCESS - Successfully to read out block.\r
- EFI_DEVICE_ERROR - Fail to detect media.\r
- EFI_NO_MEDIA - Media is not present.\r
- EFI_MEDIA_CHANGED - Media has changed.\r
- EFI_BAD_BUFFER_SIZE - The buffer size is not multiple of BlockSize.\r
-\r
---*/\r
{\r
SCSI_DISK_DEV *ScsiDiskDevice;\r
EFI_BLOCK_IO_MEDIA *Media;\r
BOOLEAN MediaChange;\r
EFI_TPL OldTpl;\r
\r
- MediaChange = FALSE;\r
- if (!Buffer) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- if (BufferSize == 0) {\r
- return EFI_SUCCESS;\r
- }\r
-\r
- OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
-\r
- ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
-\r
- if (!IsDeviceFixed (ScsiDiskDevice)) {\r
+ MediaChange = FALSE;\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);\r
+ Media = ScsiDiskDevice->BlkIo.Media;\r
\r
+ if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
if (EFI_ERROR (Status)) {\r
Status = EFI_DEVICE_ERROR;\r
\r
if (MediaChange) {\r
gBS->ReinstallProtocolInterface (\r
- ScsiDiskDevice->Handle,\r
- &gEfiBlockIoProtocolGuid,\r
- &ScsiDiskDevice->BlkIo,\r
- &ScsiDiskDevice->BlkIo\r
- );\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &ScsiDiskDevice->BlkIo\r
+ );\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &ScsiDiskDevice->BlkIo2\r
+ );\r
+ if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ }\r
+\r
+ if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiStorageSecurityCommandProtocolGuid,\r
+ &ScsiDiskDevice->StorageSecurity,\r
+ &ScsiDiskDevice->StorageSecurity\r
+ );\r
+ }\r
+\r
+ if (Media->MediaPresent) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ } else {\r
+ Status = EFI_NO_MEDIA;\r
+ }\r
+\r
+ goto Done;\r
}\r
}\r
+\r
//\r
// Get the intrinsic block size\r
//\r
- Media = ScsiDiskDevice->BlkIo.Media;\r
- BlockSize = Media->BlockSize;\r
+ BlockSize = Media->BlockSize;\r
+\r
+ if (BlockSize == 0) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
\r
- NumberOfBlocks = BufferSize / BlockSize;\r
+ NumberOfBlocks = BufferSize / BlockSize;\r
\r
if (!(Media->MediaPresent)) {\r
Status = EFI_NO_MEDIA;\r
goto Done;\r
}\r
\r
+ if (Buffer == NULL) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if (BufferSize == 0) {\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ }\r
+\r
if (BufferSize % BlockSize != 0) {\r
Status = EFI_BAD_BUFFER_SIZE;\r
goto Done;\r
}\r
\r
- if (LBA > Media->LastBlock) {\r
+ if (Lba > Media->LastBlock) {\r
Status = EFI_INVALID_PARAMETER;\r
goto Done;\r
}\r
\r
- if ((LBA + NumberOfBlocks - 1) > Media->LastBlock) {\r
+ if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
Status = EFI_INVALID_PARAMETER;\r
goto Done;\r
}\r
\r
- if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
+ if ((Media->IoAlign > 1) && (((UINTN)Buffer & (Media->IoAlign - 1)) != 0)) {\r
Status = EFI_INVALID_PARAMETER;\r
goto Done;\r
}\r
// If all the parameters are valid, then perform read sectors command\r
// to transfer data from device to host.\r
//\r
- Status = ScsiDiskReadSectors (ScsiDiskDevice, Buffer, LBA, NumberOfBlocks);\r
+ Status = ScsiDiskReadSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);\r
\r
Done:\r
gBS->RestoreTPL (OldTpl);\r
return Status;\r
}\r
\r
+/**\r
+ The function is to Write Block to SCSI Disk.\r
+\r
+ @param This The pointer of EFI_BLOCK_IO_PROTOCOL\r
+ @param MediaId The Id of Media detected\r
+ @param Lba The logic block address\r
+ @param BufferSize The size of Buffer\r
+ @param Buffer The buffer to fill the read out data\r
+\r
+ @retval EFI_SUCCESS Successfully to read out block.\r
+ @retval EFI_WRITE_PROTECTED The device can not be written to.\r
+ @retval EFI_DEVICE_ERROR Fail to detect media.\r
+ @retval EFI_NO_MEDIA Media is not present.\r
+ @retval EFI_MEDIA_CHANGED Media has changed.\r
+ @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter passed in.\r
+\r
+**/\r
EFI_STATUS\r
EFIAPI\r
ScsiDiskWriteBlocks (\r
- IN EFI_BLOCK_IO_PROTOCOL *This,\r
- IN UINT32 MediaId,\r
- IN EFI_LBA LBA,\r
- IN UINTN BufferSize,\r
- IN VOID *Buffer\r
+ IN EFI_BLOCK_IO_PROTOCOL *This,\r
+ IN UINT32 MediaId,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN BufferSize,\r
+ IN VOID *Buffer\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- The function is to Write Block to SCSI Disk\r
-\r
-Arguments:\r
-\r
- This - The pointer of EFI_BLOCK_IO_PROTOCOL\r
- MediaId - The Id of Media detected\r
- LBA - The logic block address\r
- BufferSize - The size of Buffer\r
- Buffer - The buffer to fill the read out data\r
-\r
-Returns:\r
-\r
- EFI_INVALID_PARAMETER - Invalid parameter passed in.\r
- EFI_SUCCESS - Successfully to read out block.\r
- EFI_DEVICE_ERROR - Fail to detect media.\r
- EFI_NO_MEDIA - Media is not present.\r
- EFI_MEDIA_CHANGED - Media has changed.\r
- EFI_BAD_BUFFER_SIZE - The buffer size is not multiple of BlockSize.\r
-\r
---*/\r
{\r
SCSI_DISK_DEV *ScsiDiskDevice;\r
EFI_BLOCK_IO_MEDIA *Media;\r
BOOLEAN MediaChange;\r
EFI_TPL OldTpl;\r
\r
- MediaChange = FALSE;\r
- if (!Buffer) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- if (BufferSize == 0) {\r
- return EFI_SUCCESS;\r
- }\r
-\r
- OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
-\r
- ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
-\r
- if (!IsDeviceFixed (ScsiDiskDevice)) {\r
+ MediaChange = FALSE;\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);\r
+ Media = ScsiDiskDevice->BlkIo.Media;\r
\r
+ if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
if (EFI_ERROR (Status)) {\r
Status = EFI_DEVICE_ERROR;\r
\r
if (MediaChange) {\r
gBS->ReinstallProtocolInterface (\r
- ScsiDiskDevice->Handle,\r
- &gEfiBlockIoProtocolGuid,\r
- &ScsiDiskDevice->BlkIo,\r
- &ScsiDiskDevice->BlkIo\r
- );\r
- }\r
- }\r
- //\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &ScsiDiskDevice->BlkIo\r
+ );\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &ScsiDiskDevice->BlkIo2\r
+ );\r
+ if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ }\r
+\r
+ if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiStorageSecurityCommandProtocolGuid,\r
+ &ScsiDiskDevice->StorageSecurity,\r
+ &ScsiDiskDevice->StorageSecurity\r
+ );\r
+ }\r
+\r
+ if (Media->MediaPresent) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ } else {\r
+ Status = EFI_NO_MEDIA;\r
+ }\r
+\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ //\r
// Get the intrinsic block size\r
//\r
- Media = ScsiDiskDevice->BlkIo.Media;\r
- BlockSize = Media->BlockSize;\r
+ BlockSize = Media->BlockSize;\r
+\r
+ if (BlockSize == 0) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
\r
- NumberOfBlocks = BufferSize / BlockSize;\r
+ NumberOfBlocks = BufferSize / BlockSize;\r
\r
if (!(Media->MediaPresent)) {\r
Status = EFI_NO_MEDIA;\r
goto Done;\r
}\r
\r
+ if (Media->ReadOnly) {\r
+ Status = EFI_WRITE_PROTECTED;\r
+ goto Done;\r
+ }\r
+\r
+ if (BufferSize == 0) {\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ }\r
+\r
+ if (Buffer == NULL) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
if (BufferSize % BlockSize != 0) {\r
Status = EFI_BAD_BUFFER_SIZE;\r
goto Done;\r
}\r
\r
- if (LBA > Media->LastBlock) {\r
+ if (Lba > Media->LastBlock) {\r
Status = EFI_INVALID_PARAMETER;\r
goto Done;\r
}\r
\r
- if ((LBA + NumberOfBlocks - 1) > Media->LastBlock) {\r
+ if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
Status = EFI_INVALID_PARAMETER;\r
goto Done;\r
}\r
\r
- if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
+ if ((Media->IoAlign > 1) && (((UINTN)Buffer & (Media->IoAlign - 1)) != 0)) {\r
Status = EFI_INVALID_PARAMETER;\r
goto Done;\r
}\r
+\r
//\r
// if all the parameters are valid, then perform read sectors command\r
// to transfer data from device to host.\r
//\r
- Status = ScsiDiskWriteSectors (ScsiDiskDevice, Buffer, LBA, NumberOfBlocks);\r
+ Status = ScsiDiskWriteSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);\r
\r
Done:\r
gBS->RestoreTPL (OldTpl);\r
return Status;\r
}\r
\r
-EFI_STATUS\r
-EFIAPI\r
-ScsiDiskFlushBlocks (\r
- IN EFI_BLOCK_IO_PROTOCOL *This\r
- )\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Flush Block to Disk\r
-\r
-Arguments:\r
+/**\r
+ Flush Block to Disk.\r
\r
- This - The pointer of EFI_BLOCK_IO_PROTOCOL\r
+ EFI_SUCCESS is returned directly.\r
\r
-Returns:\r
+ @param This The pointer of EFI_BLOCK_IO_PROTOCOL\r
\r
- EFI_SUCCESS \r
+ @retval EFI_SUCCESS All outstanding data was written to the device\r
\r
---*/\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskFlushBlocks (\r
+ IN EFI_BLOCK_IO_PROTOCOL *This\r
+ )\r
{\r
//\r
// return directly\r
return EFI_SUCCESS;\r
}\r
\r
+/**\r
+ Reset SCSI Disk.\r
+\r
+ @param This The pointer of EFI_BLOCK_IO2_PROTOCOL.\r
+ @param ExtendedVerification The flag about if extend verificate.\r
+\r
+ @retval EFI_SUCCESS The device was reset.\r
+ @retval EFI_DEVICE_ERROR The device is not functioning properly and could\r
+ not be reset.\r
+ @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice().\r
+\r
+**/\r
EFI_STATUS\r
-ScsiDiskDetectMedia (\r
- SCSI_DISK_DEV *ScsiDiskDevice,\r
- BOOLEAN MustReadCapacity,\r
- BOOLEAN *MediaChange\r
+EFIAPI\r
+ScsiDiskResetEx (\r
+ IN EFI_BLOCK_IO2_PROTOCOL *This,\r
+ IN BOOLEAN ExtendedVerification\r
)\r
-/*++\r
+{\r
+ EFI_TPL OldTpl;\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_STATUS Status;\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
+\r
+ Status = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
\r
-Routine Description:\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_UNSUPPORTED) {\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+ }\r
\r
- Dectect Device and read out capacity ,if error occurs, parse the sense key.\r
+ if (!ExtendedVerification) {\r
+ goto Done;\r
+ }\r
\r
-Arguments:\r
+ Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
\r
- ScsiDiskDevice - The pointer of SCSI_DISK_DEV\r
- MustReadCapacity - The flag about reading device capacity\r
- MediaChange - The pointer of flag indicates if media has changed \r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
\r
-Returns:\r
+Done:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
\r
- EFI_DEVICE_ERROR - Indicates that error occurs\r
- EFI_SUCCESS - Successfully to detect media\r
+/**\r
+ The function is to Read Block from SCSI Disk.\r
+\r
+ @param This The pointer of EFI_BLOCK_IO_PROTOCOL.\r
+ @param MediaId The Id of Media detected.\r
+ @param Lba The logic block address.\r
+ @param Token A pointer to the token associated with the transaction.\r
+ @param BufferSize The size of Buffer.\r
+ @param Buffer The buffer to fill the read out data.\r
+\r
+ @retval EFI_SUCCESS The read request was queued if Token-> Event is\r
+ not NULL. The data was read correctly from the\r
+ device if theToken-> Event is NULL.\r
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting\r
+ to perform the read operation.\r
+ @retval EFI_NO_MEDIA There is no media in the device.\r
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of\r
+ the intrinsic block size of the device.\r
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not\r
+ valid, or the buffer is not on proper\r
+ alignment.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
+ lack of resources.\r
\r
---*/\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskReadBlocksEx (\r
+ IN EFI_BLOCK_IO2_PROTOCOL *This,\r
+ IN UINT32 MediaId,\r
+ IN EFI_LBA Lba,\r
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token,\r
+ IN UINTN BufferSize,\r
+ OUT VOID *Buffer\r
+ )\r
{\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
EFI_STATUS Status;\r
- EFI_STATUS ReadCapacityStatus;\r
- EFI_SCSI_SENSE_DATA *SenseData;\r
- UINTN NumberOfSenseKeys;\r
- BOOLEAN NeedRetry;\r
- BOOLEAN NeedReadCapacity;\r
- UINT8 Index;\r
- UINT8 MaxRetry;\r
- EFI_BLOCK_IO_MEDIA OldMedia;\r
- UINTN Action;\r
-\r
- Status = EFI_SUCCESS;\r
- ReadCapacityStatus = EFI_SUCCESS;\r
- SenseData = NULL;\r
- NumberOfSenseKeys = 0;\r
- NeedReadCapacity = FALSE;\r
- CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia));\r
- *MediaChange = FALSE;\r
- MaxRetry = 3;\r
+ UINTN BlockSize;\r
+ UINTN NumberOfBlocks;\r
+ BOOLEAN MediaChange;\r
+ EFI_TPL OldTpl;\r
\r
- for (Index = 0; Index < MaxRetry; Index++) {\r
- Status = ScsiDiskTestUnitReady (\r
- ScsiDiskDevice,\r
- &NeedRetry,\r
- &SenseData,\r
- &NumberOfSenseKeys\r
- );\r
- if (!EFI_ERROR (Status)) {\r
- break;\r
- }\r
+ MediaChange = FALSE;\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
+ Media = ScsiDiskDevice->BlkIo.Media;\r
\r
- if (!NeedRetry) {\r
- return Status;\r
+ if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
+ Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
}\r
- }\r
\r
- if ((Index == MaxRetry) && EFI_ERROR (Status)) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
+ if (MediaChange) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &ScsiDiskDevice->BlkIo\r
+ );\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &ScsiDiskDevice->BlkIo2\r
+ );\r
+ if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ }\r
\r
- Status = DetectMediaParsingSenseKeys (\r
- ScsiDiskDevice,\r
- SenseData,\r
- NumberOfSenseKeys,\r
- &Action\r
- );\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
+ if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiStorageSecurityCommandProtocolGuid,\r
+ &ScsiDiskDevice->StorageSecurity,\r
+ &ScsiDiskDevice->StorageSecurity\r
+ );\r
+ }\r
+\r
+ if (Media->MediaPresent) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ } else {\r
+ Status = EFI_NO_MEDIA;\r
+ }\r
+\r
+ goto Done;\r
+ }\r
}\r
+\r
//\r
- // ACTION_NO_ACTION: need not read capacity\r
- // other action code: need read capacity\r
+ // Get the intrinsic block size\r
//\r
- if (Action == ACTION_NO_ACTION) {\r
- NeedReadCapacity = FALSE;\r
- } else {\r
- NeedReadCapacity = TRUE;\r
+ BlockSize = Media->BlockSize;\r
+\r
+ if (BlockSize == 0) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
}\r
\r
- //\r
- // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,\r
- // retrieve capacity via Read Capacity command\r
- //\r
- if (NeedReadCapacity || MustReadCapacity) {\r
- //\r
- // retrieve media information\r
- //\r
- MaxRetry = 3;\r
- for (Index = 0; Index < MaxRetry; Index++) {\r
+ NumberOfBlocks = BufferSize / BlockSize;\r
\r
- ReadCapacityStatus = ScsiDiskReadCapacity (\r
- ScsiDiskDevice,\r
- &NeedRetry,\r
- &SenseData,\r
- &NumberOfSenseKeys\r
- );\r
- if (EFI_ERROR (ReadCapacityStatus) && !NeedRetry) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- //\r
- // analyze sense key to action\r
- //\r
- Status = DetectMediaParsingSenseKeys (\r
- ScsiDiskDevice,\r
- SenseData,\r
- NumberOfSenseKeys,\r
- &Action\r
- );\r
- //\r
- // if Status is error, it may indicate crisis error,\r
- // so return without retry.\r
- //\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
+ if (!(Media->MediaPresent)) {\r
+ Status = EFI_NO_MEDIA;\r
+ goto Done;\r
+ }\r
\r
- switch (Action) {\r
- case ACTION_NO_ACTION:\r
- //\r
- // no retry\r
- //\r
- Index = MaxRetry;\r
- break;\r
+ if (MediaId != Media->MediaId) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ goto Done;\r
+ }\r
\r
- case ACTION_RETRY_COMMAND_LATER:\r
- //\r
- // retry the ReadCapacity later and continuously, until the condition\r
- // no longer emerges.\r
- // stall time is 100000us, or say 0.1 second.\r
- //\r
- gBS->Stall (100000);\r
- Index = 0;\r
- break;\r
+ if (Buffer == NULL) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
\r
- default:\r
- //\r
- // other cases, just retry the command\r
- //\r
- break;\r
- }\r
+ if (BufferSize == 0) {\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+ gBS->SignalEvent (Token->Event);\r
}\r
\r
- if ((Index == MaxRetry) && EFI_ERROR (ReadCapacityStatus)) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
}\r
\r
- if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {\r
- //\r
- // Media change information got from the device\r
- //\r
- *MediaChange = TRUE;\r
+ if (BufferSize % BlockSize != 0) {\r
+ Status = EFI_BAD_BUFFER_SIZE;\r
+ goto Done;\r
}\r
\r
- if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {\r
- *MediaChange = TRUE;\r
- ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+ if (Lba > Media->LastBlock) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
}\r
\r
- if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {\r
- *MediaChange = TRUE;\r
- ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+ if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
}\r
\r
- if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) {\r
- *MediaChange = TRUE;\r
- ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+ if ((Media->IoAlign > 1) && (((UINTN)Buffer & (Media->IoAlign - 1)) != 0)) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
}\r
\r
- if (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) {\r
- if (ScsiDiskDevice->BlkIo.Media->MediaPresent) {\r
- //\r
- // when change from no media to media present, reset the MediaId to 1.\r
- //\r
- ScsiDiskDevice->BlkIo.Media->MediaId = 1;\r
- } else {\r
- //\r
- // when no media, reset the MediaId to zero.\r
- //\r
- ScsiDiskDevice->BlkIo.Media->MediaId = 0;\r
- }\r
-\r
- *MediaChange = TRUE;\r
+ //\r
+ // If all the parameters are valid, then perform read sectors command\r
+ // to transfer data from device to host.\r
+ //\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+ Status = ScsiDiskAsyncReadSectors (\r
+ ScsiDiskDevice,\r
+ Buffer,\r
+ Lba,\r
+ NumberOfBlocks,\r
+ Token\r
+ );\r
+ } else {\r
+ Status = ScsiDiskReadSectors (\r
+ ScsiDiskDevice,\r
+ Buffer,\r
+ Lba,\r
+ NumberOfBlocks\r
+ );\r
}\r
\r
- return EFI_SUCCESS;\r
+Done:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
}\r
\r
+/**\r
+ The function is to Write Block to SCSI Disk.\r
+\r
+ @param This The pointer of EFI_BLOCK_IO_PROTOCOL.\r
+ @param MediaId The Id of Media detected.\r
+ @param Lba The logic block address.\r
+ @param Token A pointer to the token associated with the transaction.\r
+ @param BufferSize The size of Buffer.\r
+ @param Buffer The buffer to fill the read out data.\r
+\r
+ @retval EFI_SUCCESS The data were written correctly to the device.\r
+ @retval EFI_WRITE_PROTECTED The device cannot be written to.\r
+ @retval EFI_NO_MEDIA There is no media in the device.\r
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting\r
+ to perform the write operation.\r
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of\r
+ the intrinsic block size of the device.\r
+ @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not\r
+ valid, or the buffer is not on proper\r
+ alignment.\r
+\r
+**/\r
EFI_STATUS\r
-ScsiDiskInquiryDevice (\r
- SCSI_DISK_DEV *ScsiDiskDevice,\r
- BOOLEAN *NeedRetry\r
+EFIAPI\r
+ScsiDiskWriteBlocksEx (\r
+ IN EFI_BLOCK_IO2_PROTOCOL *This,\r
+ IN UINT32 MediaId,\r
+ IN EFI_LBA Lba,\r
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token,\r
+ IN UINTN BufferSize,\r
+ IN VOID *Buffer\r
)\r
-/*++\r
+{\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
+ EFI_STATUS Status;\r
+ UINTN BlockSize;\r
+ UINTN NumberOfBlocks;\r
+ BOOLEAN MediaChange;\r
+ EFI_TPL OldTpl;\r
\r
-Routine Description:\r
+ MediaChange = FALSE;\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
+ Media = ScsiDiskDevice->BlkIo.Media;\r
\r
- Send out Inquiry command to Device\r
+ if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
+ Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
\r
-Arguments:\r
+ if (MediaChange) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &ScsiDiskDevice->BlkIo\r
+ );\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &ScsiDiskDevice->BlkIo2\r
+ );\r
+ if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ }\r
\r
- ScsiDiskDevice - The pointer of SCSI_DISK_DEV\r
- NeedRetry - Indicates if needs try again when error happens\r
+ if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiStorageSecurityCommandProtocolGuid,\r
+ &ScsiDiskDevice->StorageSecurity,\r
+ &ScsiDiskDevice->StorageSecurity\r
+ );\r
+ }\r
\r
-Returns:\r
+ if (Media->MediaPresent) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ } else {\r
+ Status = EFI_NO_MEDIA;\r
+ }\r
\r
- EFI_DEVICE_ERROR - Indicates that error occurs\r
- EFI_SUCCESS - Successfully to detect media\r
+ goto Done;\r
+ }\r
+ }\r
\r
---*/\r
-{\r
- UINT32 InquiryDataLength;\r
- UINT8 SenseDataLength;\r
- UINT8 HostAdapterStatus;\r
- UINT8 TargetStatus;\r
- EFI_SCSI_SENSE_DATA *SenseDataArray;\r
- UINTN NumberOfSenseKeys;\r
- EFI_STATUS Status;\r
- UINT8 MaxRetry;\r
- UINT8 Index;\r
+ //\r
+ // Get the intrinsic block size\r
+ //\r
+ BlockSize = Media->BlockSize;\r
\r
- InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA);\r
- SenseDataLength = 0;\r
+ if (BlockSize == 0) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ NumberOfBlocks = BufferSize / BlockSize;\r
+\r
+ if (!(Media->MediaPresent)) {\r
+ Status = EFI_NO_MEDIA;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaId != Media->MediaId) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ goto Done;\r
+ }\r
+\r
+ if (Media->ReadOnly) {\r
+ Status = EFI_WRITE_PROTECTED;\r
+ goto Done;\r
+ }\r
+\r
+ if (BufferSize == 0) {\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ }\r
+\r
+ if (Buffer == NULL) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if (BufferSize % BlockSize != 0) {\r
+ Status = EFI_BAD_BUFFER_SIZE;\r
+ goto Done;\r
+ }\r
+\r
+ if (Lba > Media->LastBlock) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if ((Media->IoAlign > 1) && (((UINTN)Buffer & (Media->IoAlign - 1)) != 0)) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // if all the parameters are valid, then perform write sectors command\r
+ // to transfer data from device to host.\r
+ //\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+ Status = ScsiDiskAsyncWriteSectors (\r
+ ScsiDiskDevice,\r
+ Buffer,\r
+ Lba,\r
+ NumberOfBlocks,\r
+ Token\r
+ );\r
+ } else {\r
+ Status = ScsiDiskWriteSectors (\r
+ ScsiDiskDevice,\r
+ Buffer,\r
+ Lba,\r
+ NumberOfBlocks\r
+ );\r
+ }\r
+\r
+Done:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Flush the Block Device.\r
+\r
+ @param This Indicates a pointer to the calling context.\r
+ @param Token A pointer to the token associated with the transaction.\r
+\r
+ @retval EFI_SUCCESS All outstanding data was written to the device.\r
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting to\r
+ write data.\r
+ @retval EFI_WRITE_PROTECTED The device cannot be written to.\r
+ @retval EFI_NO_MEDIA There is no media in the device.\r
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskFlushBlocksEx (\r
+ IN EFI_BLOCK_IO2_PROTOCOL *This,\r
+ IN OUT EFI_BLOCK_IO2_TOKEN *Token\r
+ )\r
+{\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
+ EFI_STATUS Status;\r
+ BOOLEAN MediaChange;\r
+ EFI_TPL OldTpl;\r
+\r
+ MediaChange = FALSE;\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
+ Media = ScsiDiskDevice->BlkIo.Media;\r
+\r
+ if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
+ Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaChange) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &ScsiDiskDevice->BlkIo\r
+ );\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &ScsiDiskDevice->BlkIo2\r
+ );\r
+ if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ }\r
+\r
+ if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiStorageSecurityCommandProtocolGuid,\r
+ &ScsiDiskDevice->StorageSecurity,\r
+ &ScsiDiskDevice->StorageSecurity\r
+ );\r
+ }\r
+\r
+ if (Media->MediaPresent) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ } else {\r
+ Status = EFI_NO_MEDIA;\r
+ }\r
+\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ if (!(Media->MediaPresent)) {\r
+ Status = EFI_NO_MEDIA;\r
+ goto Done;\r
+ }\r
+\r
+ if (Media->ReadOnly) {\r
+ Status = EFI_WRITE_PROTECTED;\r
+ goto Done;\r
+ }\r
+\r
+ //\r
+ // Wait for the BlockIo2 requests queue to become empty\r
+ //\r
+ while (!IsListEmpty (&ScsiDiskDevice->AsyncTaskQueue)) {\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Signal caller event\r
+ //\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
+\r
+Done:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Internal helper notify function which process the result of an asynchronous\r
+ SCSI UNMAP Command and signal the event passed from EraseBlocks.\r
+\r
+ @param Event The instance of EFI_EVENT.\r
+ @param Context The parameter passed in.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+ScsiDiskAsyncUnmapNotify (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ SCSI_ERASEBLK_REQUEST *EraseBlkReq;\r
+ EFI_SCSI_IO_SCSI_REQUEST_PACKET *CommandPacket;\r
+ EFI_ERASE_BLOCK_TOKEN *Token;\r
+ EFI_STATUS Status;\r
+\r
+ gBS->CloseEvent (Event);\r
+\r
+ EraseBlkReq = (SCSI_ERASEBLK_REQUEST *)Context;\r
+ CommandPacket = &EraseBlkReq->CommandPacket;\r
+ Token = EraseBlkReq->Token;\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+\r
+ Status = CheckHostAdapterStatus (CommandPacket->HostAdapterStatus);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ScsiDiskAsyncUnmapNotify: Host adapter indicating error status 0x%x.\n",\r
+ CommandPacket->HostAdapterStatus\r
+ ));\r
+\r
+ Token->TransactionStatus = Status;\r
+ goto Done;\r
+ }\r
+\r
+ Status = CheckTargetStatus (CommandPacket->TargetStatus);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ScsiDiskAsyncUnmapNotify: Target indicating error status 0x%x.\n",\r
+ CommandPacket->HostAdapterStatus\r
+ ));\r
+\r
+ Token->TransactionStatus = Status;\r
+ goto Done;\r
+ }\r
+\r
+Done:\r
+ RemoveEntryList (&EraseBlkReq->Link);\r
+ FreePool (CommandPacket->OutDataBuffer);\r
+ FreePool (EraseBlkReq->CommandPacket.Cdb);\r
+ FreePool (EraseBlkReq);\r
+\r
+ gBS->SignalEvent (Token->Event);\r
+}\r
+\r
+/**\r
+ Require the device server to cause one or more LBAs to be unmapped.\r
+\r
+ @param ScsiDiskDevice The pointer of ScsiDiskDevice.\r
+ @param Lba The start block number.\r
+ @param Blocks Total block number to be unmapped.\r
+ @param Token The pointer to the token associated with the\r
+ non-blocking erase block request.\r
+\r
+ @retval EFI_SUCCESS Target blocks have been successfully unmapped.\r
+ @retval EFI_DEVICE_ERROR Fail to unmap the target blocks.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskUnmap (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN UINT64 Lba,\r
+ IN UINTN Blocks,\r
+ IN EFI_ERASE_BLOCK_TOKEN *Token OPTIONAL\r
+ )\r
+{\r
+ EFI_SCSI_IO_PROTOCOL *ScsiIo;\r
+ SCSI_ERASEBLK_REQUEST *EraseBlkReq;\r
+ EFI_SCSI_IO_SCSI_REQUEST_PACKET *CommandPacket;\r
+ EFI_SCSI_DISK_UNMAP_BLOCK_DESP *BlkDespPtr;\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
+ UINT8 *Cdb;\r
+ UINT32 MaxLbaCnt;\r
+ UINT32 MaxBlkDespCnt;\r
+ UINT32 BlkDespCnt;\r
+ UINT16 UnmapParamListLen;\r
+ VOID *UnmapParamList;\r
+ EFI_EVENT AsyncUnmapEvent;\r
+ EFI_TPL OldTpl;\r
+\r
+ ScsiIo = ScsiDiskDevice->ScsiIo;\r
+ MaxLbaCnt = ScsiDiskDevice->UnmapInfo.MaxLbaCnt;\r
+ MaxBlkDespCnt = ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt;\r
+ EraseBlkReq = NULL;\r
+ UnmapParamList = NULL;\r
+ AsyncUnmapEvent = NULL;\r
+ ReturnStatus = EFI_SUCCESS;\r
+\r
+ if (Blocks / (UINTN)MaxLbaCnt > MaxBlkDespCnt) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ EraseBlkReq = AllocateZeroPool (sizeof (SCSI_ERASEBLK_REQUEST));\r
+ if (EraseBlkReq == NULL) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ EraseBlkReq->CommandPacket.Cdb = AllocateZeroPool (0xA);\r
+ if (EraseBlkReq->CommandPacket.Cdb == NULL) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ BlkDespCnt = (UINT32)((Blocks - 1) / MaxLbaCnt + 1);\r
+ UnmapParamListLen = (UINT16)(sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER)\r
+ + BlkDespCnt * sizeof (EFI_SCSI_DISK_UNMAP_BLOCK_DESP));\r
+ UnmapParamList = AllocateZeroPool (UnmapParamListLen);\r
+ if (UnmapParamList == NULL) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ *((UINT16 *)UnmapParamList) = SwapBytes16 (UnmapParamListLen - 2);\r
+ *((UINT16 *)UnmapParamList + 1) = SwapBytes16 (UnmapParamListLen - sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER));\r
+\r
+ BlkDespPtr = (EFI_SCSI_DISK_UNMAP_BLOCK_DESP *)((UINT8 *)UnmapParamList + sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER));\r
+ while (Blocks > 0) {\r
+ if (Blocks > MaxLbaCnt) {\r
+ *(UINT64 *)(&BlkDespPtr->Lba) = SwapBytes64 (Lba);\r
+ *(UINT32 *)(&BlkDespPtr->BlockNum) = SwapBytes32 (MaxLbaCnt);\r
+ Blocks -= MaxLbaCnt;\r
+ Lba += MaxLbaCnt;\r
+ } else {\r
+ *(UINT64 *)(&BlkDespPtr->Lba) = SwapBytes64 (Lba);\r
+ *(UINT32 *)(&BlkDespPtr->BlockNum) = SwapBytes32 ((UINT32)Blocks);\r
+ Blocks = 0;\r
+ }\r
+\r
+ BlkDespPtr++;\r
+ }\r
+\r
+ CommandPacket = &EraseBlkReq->CommandPacket;\r
+ CommandPacket->Timeout = SCSI_DISK_TIMEOUT;\r
+ CommandPacket->OutDataBuffer = UnmapParamList;\r
+ CommandPacket->OutTransferLength = UnmapParamListLen;\r
+ CommandPacket->CdbLength = 0xA;\r
+ CommandPacket->DataDirection = EFI_SCSI_DATA_OUT;\r
+ //\r
+ // Fill Cdb for UNMAP Command\r
+ //\r
+ Cdb = CommandPacket->Cdb;\r
+ Cdb[0] = EFI_SCSI_OP_UNMAP;\r
+ WriteUnaligned16 ((UINT16 *)&Cdb[7], SwapBytes16 (UnmapParamListLen));\r
+\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ //\r
+ // Non-blocking UNMAP request\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ ScsiDiskAsyncUnmapNotify,\r
+ EraseBlkReq,\r
+ &AsyncUnmapEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &EraseBlkReq->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ EraseBlkReq->Token = Token;\r
+\r
+ Status = ScsiIo->ExecuteScsiCommand (\r
+ ScsiIo,\r
+ CommandPacket,\r
+ AsyncUnmapEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ RemoveEntryList (&EraseBlkReq->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ goto Done;\r
+ } else {\r
+ //\r
+ // Directly return if the non-blocking UNMAP request is queued.\r
+ //\r
+ return EFI_SUCCESS;\r
+ }\r
+ } else {\r
+ //\r
+ // Blocking UNMAP request\r
+ //\r
+ Status = ScsiIo->ExecuteScsiCommand (\r
+ ScsiIo,\r
+ CommandPacket,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Only blocking UNMAP request will reach here.\r
+ //\r
+ Status = CheckHostAdapterStatus (CommandPacket->HostAdapterStatus);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ScsiDiskUnmap: Host adapter indicating error status 0x%x.\n",\r
+ CommandPacket->HostAdapterStatus\r
+ ));\r
+\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ Status = CheckTargetStatus (CommandPacket->TargetStatus);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "ScsiDiskUnmap: Target indicating error status 0x%x.\n",\r
+ CommandPacket->HostAdapterStatus\r
+ ));\r
+\r
+ ReturnStatus = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+Done:\r
+ if (EraseBlkReq != NULL) {\r
+ if (EraseBlkReq->CommandPacket.Cdb != NULL) {\r
+ FreePool (EraseBlkReq->CommandPacket.Cdb);\r
+ }\r
+\r
+ FreePool (EraseBlkReq);\r
+ }\r
+\r
+ if (UnmapParamList != NULL) {\r
+ FreePool (UnmapParamList);\r
+ }\r
+\r
+ if (AsyncUnmapEvent != NULL) {\r
+ gBS->CloseEvent (AsyncUnmapEvent);\r
+ }\r
+\r
+ return ReturnStatus;\r
+}\r
+\r
+/**\r
+ Erase a specified number of device blocks.\r
+\r
+ @param[in] This Indicates a pointer to the calling context.\r
+ @param[in] MediaId The media ID that the erase request is for.\r
+ @param[in] Lba The starting logical block address to be\r
+ erased. The caller is responsible for erasing\r
+ only legitimate locations.\r
+ @param[in, out] Token A pointer to the token associated with the\r
+ transaction.\r
+ @param[in] Size The size in bytes to be erased. This must be\r
+ a multiple of the physical block size of the\r
+ device.\r
+\r
+ @retval EFI_SUCCESS The erase request was queued if Event is not\r
+ NULL. The data was erased correctly to the\r
+ device if the Event is NULL.to the device.\r
+ @retval EFI_WRITE_PROTECTED The device cannot be erased due to write\r
+ protection.\r
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting\r
+ to perform the erase operation.\r
+ @retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not\r
+ valid.\r
+ @retval EFI_NO_MEDIA There is no media in the device.\r
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskEraseBlocks (\r
+ IN EFI_ERASE_BLOCK_PROTOCOL *This,\r
+ IN UINT32 MediaId,\r
+ IN EFI_LBA Lba,\r
+ IN OUT EFI_ERASE_BLOCK_TOKEN *Token,\r
+ IN UINTN Size\r
+ )\r
+{\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
+ EFI_STATUS Status;\r
+ UINTN BlockSize;\r
+ UINTN NumberOfBlocks;\r
+ BOOLEAN MediaChange;\r
+ EFI_TPL OldTpl;\r
+\r
+ MediaChange = FALSE;\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_ERASEBLK (This);\r
+\r
+ if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
+ Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaChange) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &ScsiDiskDevice->BlkIo\r
+ );\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &ScsiDiskDevice->BlkIo2\r
+ );\r
+ if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ }\r
+\r
+ if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiStorageSecurityCommandProtocolGuid,\r
+ &ScsiDiskDevice->StorageSecurity,\r
+ &ScsiDiskDevice->StorageSecurity\r
+ );\r
+ }\r
+\r
+ Status = EFI_MEDIA_CHANGED;\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Get the intrinsic block size\r
+ //\r
+ Media = ScsiDiskDevice->BlkIo.Media;\r
+\r
+ if (!(Media->MediaPresent)) {\r
+ Status = EFI_NO_MEDIA;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaId != Media->MediaId) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ goto Done;\r
+ }\r
+\r
+ if (Media->ReadOnly) {\r
+ Status = EFI_WRITE_PROTECTED;\r
+ goto Done;\r
+ }\r
+\r
+ if (Size == 0) {\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Token->TransactionStatus = EFI_SUCCESS;\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ }\r
+\r
+ BlockSize = Media->BlockSize;\r
+ if ((Size % BlockSize) != 0) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ NumberOfBlocks = Size / BlockSize;\r
+ if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if ((Token != NULL) && (Token->Event != NULL)) {\r
+ Status = ScsiDiskUnmap (ScsiDiskDevice, Lba, NumberOfBlocks, Token);\r
+ } else {\r
+ Status = ScsiDiskUnmap (ScsiDiskDevice, Lba, NumberOfBlocks, NULL);\r
+ }\r
+\r
+Done:\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Send a security protocol command to a device that receives data and/or the result\r
+ of one or more commands sent by SendData.\r
+\r
+ The ReceiveData function sends a security protocol command to the given MediaId.\r
+ The security protocol command sent is defined by SecurityProtocolId and contains\r
+ the security protocol specific data SecurityProtocolSpecificData. The function\r
+ returns the data from the security protocol command in PayloadBuffer.\r
+\r
+ For devices supporting the SCSI command set, the security protocol command is sent\r
+ using the SECURITY PROTOCOL IN command defined in SPC-4.\r
+\r
+ If PayloadBufferSize is too small to store the available data from the security\r
+ protocol command, the function shall copy PayloadBufferSize bytes into the\r
+ PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL.\r
+\r
+ If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero,\r
+ the function shall return EFI_INVALID_PARAMETER.\r
+\r
+ If the given MediaId does not support security protocol commands, the function shall\r
+ return EFI_UNSUPPORTED. If there is no media in the device, the function returns\r
+ EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device,\r
+ the function returns EFI_MEDIA_CHANGED.\r
+\r
+ If the security protocol fails to complete within the Timeout period, the function\r
+ shall return EFI_TIMEOUT.\r
+\r
+ If the security protocol command completes without an error, the function shall\r
+ return EFI_SUCCESS. If the security protocol command completes with an error, the\r
+ function shall return EFI_DEVICE_ERROR.\r
+\r
+ @param This Indicates a pointer to the calling context.\r
+ @param MediaId ID of the medium to receive data from.\r
+ @param Timeout The timeout, in 100ns units, to use for the execution\r
+ of the security protocol command. A Timeout value of 0\r
+ means that this function will wait indefinitely for the\r
+ security protocol command to execute. If Timeout is greater\r
+ than zero, then this function will return EFI_TIMEOUT if the\r
+ time required to execute the receive data command is greater than Timeout.\r
+ @param SecurityProtocolId The value of the "Security Protocol" parameter of\r
+ the security protocol command to be sent.\r
+ @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter\r
+ of the security protocol command to be sent.\r
+ @param PayloadBufferSize Size in bytes of the payload data buffer.\r
+ @param PayloadBuffer A pointer to a destination buffer to store the security\r
+ protocol command specific payload data for the security\r
+ protocol command. The caller is responsible for having\r
+ either implicit or explicit ownership of the buffer.\r
+ @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the\r
+ data written to the payload data buffer.\r
+\r
+ @retval EFI_SUCCESS The security protocol command completed successfully.\r
+ @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available\r
+ data from the device. The PayloadBuffer contains the truncated data.\r
+ @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands.\r
+ @retval EFI_DEVICE_ERROR The security protocol command completed with an error.\r
+ @retval EFI_NO_MEDIA There is no media in the device.\r
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and\r
+ PayloadBufferSize is non-zero.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security\r
+ protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskReceiveData (\r
+ IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This,\r
+ IN UINT32 MediaId OPTIONAL,\r
+ IN UINT64 Timeout,\r
+ IN UINT8 SecurityProtocolId,\r
+ IN UINT16 SecurityProtocolSpecificData,\r
+ IN UINTN PayloadBufferSize,\r
+ OUT VOID *PayloadBuffer,\r
+ OUT UINTN *PayloadTransferSize\r
+ )\r
+{\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
+ EFI_STATUS Status;\r
+ BOOLEAN MediaChange;\r
+ EFI_TPL OldTpl;\r
+ UINT8 SenseDataLength;\r
+ UINT8 HostAdapterStatus;\r
+ UINT8 TargetStatus;\r
+ VOID *AlignedBuffer;\r
+ BOOLEAN AlignedBufferAllocated;\r
+\r
+ AlignedBuffer = NULL;\r
+ MediaChange = FALSE;\r
+ AlignedBufferAllocated = FALSE;\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_STORSEC (This);\r
+ Media = ScsiDiskDevice->BlkIo.Media;\r
+\r
+ SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+\r
+ if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
+ Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaChange) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &ScsiDiskDevice->BlkIo\r
+ );\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &ScsiDiskDevice->BlkIo2\r
+ );\r
+ if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ }\r
+\r
+ if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiStorageSecurityCommandProtocolGuid,\r
+ &ScsiDiskDevice->StorageSecurity,\r
+ &ScsiDiskDevice->StorageSecurity\r
+ );\r
+ }\r
+\r
+ if (Media->MediaPresent) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ } else {\r
+ Status = EFI_NO_MEDIA;\r
+ }\r
+\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Validate Media\r
+ //\r
+ if (!(Media->MediaPresent)) {\r
+ Status = EFI_NO_MEDIA;\r
+ goto Done;\r
+ }\r
+\r
+ if ((MediaId != 0) && (MediaId != Media->MediaId)) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ goto Done;\r
+ }\r
+\r
+ if (PayloadBufferSize != 0) {\r
+ if ((PayloadBuffer == NULL) || (PayloadTransferSize == NULL)) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if ((ScsiDiskDevice->ScsiIo->IoAlign > 1) && !IS_ALIGNED (PayloadBuffer, ScsiDiskDevice->ScsiIo->IoAlign)) {\r
+ AlignedBuffer = AllocateAlignedBuffer (ScsiDiskDevice, PayloadBufferSize);\r
+ if (AlignedBuffer == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+\r
+ ZeroMem (AlignedBuffer, PayloadBufferSize);\r
+ AlignedBufferAllocated = TRUE;\r
+ } else {\r
+ AlignedBuffer = PayloadBuffer;\r
+ }\r
+ }\r
+\r
+ Status = ScsiSecurityProtocolInCommand (\r
+ ScsiDiskDevice->ScsiIo,\r
+ Timeout,\r
+ ScsiDiskDevice->SenseData,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus,\r
+ SecurityProtocolId,\r
+ SecurityProtocolSpecificData,\r
+ FALSE,\r
+ PayloadBufferSize,\r
+ AlignedBuffer,\r
+ PayloadTransferSize\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ if (AlignedBufferAllocated) {\r
+ CopyMem (PayloadBuffer, AlignedBuffer, PayloadBufferSize);\r
+ }\r
+\r
+ if (PayloadBufferSize < *PayloadTransferSize) {\r
+ Status = EFI_WARN_BUFFER_TOO_SMALL;\r
+ goto Done;\r
+ }\r
+\r
+ Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ Status = CheckTargetStatus (TargetStatus);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+Done:\r
+ if (AlignedBufferAllocated) {\r
+ ZeroMem (AlignedBuffer, PayloadBufferSize);\r
+ FreeAlignedBuffer (AlignedBuffer, PayloadBufferSize);\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Send a security protocol command to a device.\r
+\r
+ The SendData function sends a security protocol command containing the payload\r
+ PayloadBuffer to the given MediaId. The security protocol command sent is\r
+ defined by SecurityProtocolId and contains the security protocol specific data\r
+ SecurityProtocolSpecificData. If the underlying protocol command requires a\r
+ specific padding for the command payload, the SendData function shall add padding\r
+ bytes to the command payload to satisfy the padding requirements.\r
+\r
+ For devices supporting the SCSI command set, the security protocol command is sent\r
+ using the SECURITY PROTOCOL OUT command defined in SPC-4.\r
+\r
+ If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall\r
+ return EFI_INVALID_PARAMETER.\r
+\r
+ If the given MediaId does not support security protocol commands, the function\r
+ shall return EFI_UNSUPPORTED. If there is no media in the device, the function\r
+ returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the\r
+ device, the function returns EFI_MEDIA_CHANGED.\r
+\r
+ If the security protocol fails to complete within the Timeout period, the function\r
+ shall return EFI_TIMEOUT.\r
+\r
+ If the security protocol command completes without an error, the function shall return\r
+ EFI_SUCCESS. If the security protocol command completes with an error, the function\r
+ shall return EFI_DEVICE_ERROR.\r
+\r
+ @param This Indicates a pointer to the calling context.\r
+ @param MediaId ID of the medium to receive data from.\r
+ @param Timeout The timeout, in 100ns units, to use for the execution\r
+ of the security protocol command. A Timeout value of 0\r
+ means that this function will wait indefinitely for the\r
+ security protocol command to execute. If Timeout is greater\r
+ than zero, then this function will return EFI_TIMEOUT if the\r
+ time required to execute the receive data command is greater than Timeout.\r
+ @param SecurityProtocolId The value of the "Security Protocol" parameter of\r
+ the security protocol command to be sent.\r
+ @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter\r
+ of the security protocol command to be sent.\r
+ @param PayloadBufferSize Size in bytes of the payload data buffer.\r
+ @param PayloadBuffer A pointer to a destination buffer to store the security\r
+ protocol command specific payload data for the security\r
+ protocol command.\r
+\r
+ @retval EFI_SUCCESS The security protocol command completed successfully.\r
+ @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands.\r
+ @retval EFI_DEVICE_ERROR The security protocol command completed with an error.\r
+ @retval EFI_NO_MEDIA There is no media in the device.\r
+ @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
+ @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero.\r
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the security\r
+ protocol command to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskSendData (\r
+ IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This,\r
+ IN UINT32 MediaId OPTIONAL,\r
+ IN UINT64 Timeout,\r
+ IN UINT8 SecurityProtocolId,\r
+ IN UINT16 SecurityProtocolSpecificData,\r
+ IN UINTN PayloadBufferSize,\r
+ OUT VOID *PayloadBuffer\r
+ )\r
+{\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_BLOCK_IO_MEDIA *Media;\r
+ EFI_STATUS Status;\r
+ BOOLEAN MediaChange;\r
+ EFI_TPL OldTpl;\r
+ UINT8 SenseDataLength;\r
+ UINT8 HostAdapterStatus;\r
+ UINT8 TargetStatus;\r
+ VOID *AlignedBuffer;\r
+ BOOLEAN AlignedBufferAllocated;\r
+\r
+ AlignedBuffer = NULL;\r
+ MediaChange = FALSE;\r
+ AlignedBufferAllocated = FALSE;\r
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_STORSEC (This);\r
+ Media = ScsiDiskDevice->BlkIo.Media;\r
+\r
+ SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+\r
+ if (!IS_DEVICE_FIXED (ScsiDiskDevice)) {\r
+ Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ }\r
+\r
+ if (MediaChange) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIoProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo,\r
+ &ScsiDiskDevice->BlkIo\r
+ );\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiBlockIo2ProtocolGuid,\r
+ &ScsiDiskDevice->BlkIo2,\r
+ &ScsiDiskDevice->BlkIo2\r
+ );\r
+ if (DetermineInstallEraseBlock (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiEraseBlockProtocolGuid,\r
+ &ScsiDiskDevice->EraseBlock,\r
+ &ScsiDiskDevice->EraseBlock\r
+ );\r
+ }\r
+\r
+ if (DetermineInstallStorageSecurity (ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
+ gBS->ReinstallProtocolInterface (\r
+ ScsiDiskDevice->Handle,\r
+ &gEfiStorageSecurityCommandProtocolGuid,\r
+ &ScsiDiskDevice->StorageSecurity,\r
+ &ScsiDiskDevice->StorageSecurity\r
+ );\r
+ }\r
+\r
+ if (Media->MediaPresent) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ } else {\r
+ Status = EFI_NO_MEDIA;\r
+ }\r
+\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Validate Media\r
+ //\r
+ if (!(Media->MediaPresent)) {\r
+ Status = EFI_NO_MEDIA;\r
+ goto Done;\r
+ }\r
+\r
+ if ((MediaId != 0) && (MediaId != Media->MediaId)) {\r
+ Status = EFI_MEDIA_CHANGED;\r
+ goto Done;\r
+ }\r
+\r
+ if (Media->ReadOnly) {\r
+ Status = EFI_WRITE_PROTECTED;\r
+ goto Done;\r
+ }\r
+\r
+ if (PayloadBufferSize != 0) {\r
+ if (PayloadBuffer == NULL) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto Done;\r
+ }\r
+\r
+ if ((ScsiDiskDevice->ScsiIo->IoAlign > 1) && !IS_ALIGNED (PayloadBuffer, ScsiDiskDevice->ScsiIo->IoAlign)) {\r
+ AlignedBuffer = AllocateAlignedBuffer (ScsiDiskDevice, PayloadBufferSize);\r
+ if (AlignedBuffer == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Done;\r
+ }\r
+\r
+ CopyMem (AlignedBuffer, PayloadBuffer, PayloadBufferSize);\r
+ AlignedBufferAllocated = TRUE;\r
+ } else {\r
+ AlignedBuffer = PayloadBuffer;\r
+ }\r
+ }\r
+\r
+ Status = ScsiSecurityProtocolOutCommand (\r
+ ScsiDiskDevice->ScsiIo,\r
+ Timeout,\r
+ ScsiDiskDevice->SenseData,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus,\r
+ SecurityProtocolId,\r
+ SecurityProtocolSpecificData,\r
+ FALSE,\r
+ PayloadBufferSize,\r
+ AlignedBuffer\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+ Status = CheckTargetStatus (TargetStatus);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Done;\r
+ }\r
+\r
+Done:\r
+ if (AlignedBufferAllocated) {\r
+ ZeroMem (AlignedBuffer, PayloadBufferSize);\r
+ FreeAlignedBuffer (AlignedBuffer, PayloadBufferSize);\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Detect Device and read out capacity ,if error occurs, parse the sense key.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
+ @param MustReadCapacity The flag about reading device capacity\r
+ @param MediaChange The pointer of flag indicates if media has changed\r
+\r
+ @retval EFI_DEVICE_ERROR Indicates that error occurs\r
+ @retval EFI_SUCCESS Successfully to detect media\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskDetectMedia (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN BOOLEAN MustReadCapacity,\r
+ OUT BOOLEAN *MediaChange\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_SCSI_SENSE_DATA *SenseData;\r
+ UINTN NumberOfSenseKeys;\r
+ BOOLEAN NeedRetry;\r
+ BOOLEAN NeedReadCapacity;\r
+ UINT8 Retry;\r
+ UINT8 MaxRetry;\r
+ EFI_BLOCK_IO_MEDIA OldMedia;\r
+ UINTN Action;\r
+ EFI_EVENT TimeoutEvt;\r
+\r
+ Status = EFI_SUCCESS;\r
+ SenseData = NULL;\r
+ NumberOfSenseKeys = 0;\r
+ Retry = 0;\r
+ MaxRetry = 3;\r
+ Action = ACTION_NO_ACTION;\r
+ NeedReadCapacity = FALSE;\r
+ *MediaChange = FALSE;\r
+ TimeoutEvt = NULL;\r
+\r
+ CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia));\r
+\r
+ Status = gBS->CreateEvent (\r
+ EVT_TIMER,\r
+ TPL_CALLBACK,\r
+ NULL,\r
+ NULL,\r
+ &TimeoutEvt\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gBS->SetTimer (TimeoutEvt, TimerRelative, EFI_TIMER_PERIOD_SECONDS (120));\r
+ if (EFI_ERROR (Status)) {\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Sending Test_Unit cmd to poll device status.\r
+ // If the sense data shows the drive is not ready or reset before, we need poll the device status again.\r
+ // We limit the upper boundary to 120 seconds.\r
+ //\r
+ while (EFI_ERROR (gBS->CheckEvent (TimeoutEvt))) {\r
+ Status = ScsiDiskTestUnitReady (\r
+ ScsiDiskDevice,\r
+ &NeedRetry,\r
+ &SenseData,\r
+ &NumberOfSenseKeys\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = DetectMediaParsingSenseKeys (\r
+ ScsiDiskDevice,\r
+ SenseData,\r
+ NumberOfSenseKeys,\r
+ &Action\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto EXIT;\r
+ } else if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+ continue;\r
+ } else {\r
+ break;\r
+ }\r
+ } else {\r
+ Retry++;\r
+ if (!NeedRetry || (Retry >= MaxRetry)) {\r
+ goto EXIT;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // ACTION_NO_ACTION: need not read capacity\r
+ // other action code: need read capacity\r
+ //\r
+ if (Action == ACTION_READ_CAPACITY) {\r
+ NeedReadCapacity = TRUE;\r
+ }\r
+\r
+ //\r
+ // READ_CAPACITY command is not supported by any of the UFS WLUNs.\r
+ //\r
+ if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_WLUN) {\r
+ NeedReadCapacity = FALSE;\r
+ MustReadCapacity = FALSE;\r
+ ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;\r
+ }\r
+\r
+ //\r
+ // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,\r
+ // retrieve capacity via Read Capacity command\r
+ //\r
+ if (NeedReadCapacity || MustReadCapacity) {\r
+ //\r
+ // retrieve media information\r
+ //\r
+ for (Retry = 0; Retry < MaxRetry; Retry++) {\r
+ Status = ScsiDiskReadCapacity (\r
+ ScsiDiskDevice,\r
+ &NeedRetry,\r
+ &SenseData,\r
+ &NumberOfSenseKeys\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // analyze sense key to action\r
+ //\r
+ Status = DetectMediaParsingSenseKeys (\r
+ ScsiDiskDevice,\r
+ SenseData,\r
+ NumberOfSenseKeys,\r
+ &Action\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // if Status is error, it may indicate crisis error,\r
+ // so return without retry.\r
+ //\r
+ goto EXIT;\r
+ } else if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+ Retry = 0;\r
+ continue;\r
+ } else {\r
+ break;\r
+ }\r
+ } else {\r
+ Retry++;\r
+ if (!NeedRetry || (Retry >= MaxRetry)) {\r
+ goto EXIT;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto EXIT;\r
+ }\r
+ }\r
+\r
+ if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {\r
+ //\r
+ // Media change information got from the device\r
+ //\r
+ *MediaChange = TRUE;\r
+ }\r
+\r
+ if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {\r
+ *MediaChange = TRUE;\r
+ ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+ }\r
+\r
+ if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {\r
+ *MediaChange = TRUE;\r
+ ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+ }\r
+\r
+ if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) {\r
+ *MediaChange = TRUE;\r
+ ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
+ }\r
+\r
+ if (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) {\r
+ if (ScsiDiskDevice->BlkIo.Media->MediaPresent) {\r
+ //\r
+ // when change from no media to media present, reset the MediaId to 1.\r
+ //\r
+ ScsiDiskDevice->BlkIo.Media->MediaId = 1;\r
+ } else {\r
+ //\r
+ // when no media, reset the MediaId to zero.\r
+ //\r
+ ScsiDiskDevice->BlkIo.Media->MediaId = 0;\r
+ }\r
+\r
+ *MediaChange = TRUE;\r
+ }\r
+\r
+EXIT:\r
+ if (TimeoutEvt != NULL) {\r
+ gBS->CloseEvent (TimeoutEvt);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Send out Inquiry command to Device.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
+ @param NeedRetry Indicates if needs try again when error happens\r
+\r
+ @retval EFI_DEVICE_ERROR Indicates that error occurs\r
+ @retval EFI_SUCCESS Successfully to detect media\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskInquiryDevice (\r
+ IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
+ OUT BOOLEAN *NeedRetry\r
+ )\r
+{\r
+ UINT32 InquiryDataLength;\r
+ UINT8 SenseDataLength;\r
+ UINT8 HostAdapterStatus;\r
+ UINT8 TargetStatus;\r
+ EFI_SCSI_SENSE_DATA *SenseDataArray;\r
+ UINTN NumberOfSenseKeys;\r
+ EFI_STATUS Status;\r
+ UINT8 MaxRetry;\r
+ UINT8 Index;\r
+ EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE *SupportedVpdPages;\r
+ EFI_SCSI_BLOCK_LIMITS_VPD_PAGE *BlockLimits;\r
+ UINTN PageLength;\r
+\r
+ InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA);\r
+ SenseDataLength = 0;\r
\r
Status = ScsiInquiryCommand (\r
- ScsiDiskDevice->ScsiIo,\r
- EfiScsiStallSeconds (1),\r
- NULL,\r
- &SenseDataLength,\r
- &HostAdapterStatus,\r
- &TargetStatus,\r
- (VOID *) &(ScsiDiskDevice->InquiryData),\r
- &InquiryDataLength,\r
- FALSE\r
- );\r
- //\r
- // no need to check HostAdapterStatus and TargetStatus\r
+ ScsiDiskDevice->ScsiIo,\r
+ SCSI_DISK_TIMEOUT,\r
+ NULL,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus,\r
+ (VOID *)&(ScsiDiskDevice->InquiryData),\r
+ &InquiryDataLength,\r
+ FALSE\r
+ );\r
+ //\r
+ // no need to check HostAdapterStatus and TargetStatus\r
+ //\r
+ if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
+ ParseInquiryData (ScsiDiskDevice);\r
+\r
+ if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {\r
+ //\r
+ // Check whether the device supports Block Limits VPD page (0xB0)\r
+ //\r
+ SupportedVpdPages = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
+ if (SupportedVpdPages == NULL) {\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ ZeroMem (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
+ InquiryDataLength = sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE);\r
+ SenseDataLength = 0;\r
+ Status = ScsiInquiryCommandEx (\r
+ ScsiDiskDevice->ScsiIo,\r
+ SCSI_DISK_TIMEOUT,\r
+ NULL,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus,\r
+ (VOID *)SupportedVpdPages,\r
+ &InquiryDataLength,\r
+ TRUE,\r
+ EFI_SCSI_PAGE_CODE_SUPPORTED_VPD\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ PageLength = (SupportedVpdPages->PageLength2 << 8)\r
+ | SupportedVpdPages->PageLength1;\r
+\r
+ //\r
+ // Sanity checks for coping with broken devices\r
+ //\r
+ if (PageLength > sizeof SupportedVpdPages->SupportedVpdPageList) {\r
+ DEBUG ((\r
+ DEBUG_WARN,\r
+ "%a: invalid PageLength (%u) in Supported VPD Pages page\n",\r
+ __FUNCTION__,\r
+ (UINT32)PageLength\r
+ ));\r
+ PageLength = 0;\r
+ }\r
+\r
+ if ((PageLength > 0) &&\r
+ (SupportedVpdPages->SupportedVpdPageList[0] !=\r
+ EFI_SCSI_PAGE_CODE_SUPPORTED_VPD))\r
+ {\r
+ DEBUG ((\r
+ DEBUG_WARN,\r
+ "%a: Supported VPD Pages page doesn't start with code 0x%02x\n",\r
+ __FUNCTION__,\r
+ EFI_SCSI_PAGE_CODE_SUPPORTED_VPD\r
+ ));\r
+ PageLength = 0;\r
+ }\r
+\r
+ //\r
+ // Locate the code for the Block Limits VPD page\r
+ //\r
+ for (Index = 0; Index < PageLength; Index++) {\r
+ //\r
+ // Sanity check\r
+ //\r
+ if ((Index > 0) &&\r
+ (SupportedVpdPages->SupportedVpdPageList[Index] <=\r
+ SupportedVpdPages->SupportedVpdPageList[Index - 1]))\r
+ {\r
+ DEBUG ((\r
+ DEBUG_WARN,\r
+ "%a: non-ascending code in Supported VPD Pages page @ %u\n",\r
+ __FUNCTION__,\r
+ Index\r
+ ));\r
+ Index = 0;\r
+ PageLength = 0;\r
+ break;\r
+ }\r
+\r
+ if (SupportedVpdPages->SupportedVpdPageList[Index] == EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Query the Block Limits VPD page\r
+ //\r
+ if (Index < PageLength) {\r
+ BlockLimits = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));\r
+ if (BlockLimits == NULL) {\r
+ FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ ZeroMem (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));\r
+ InquiryDataLength = sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE);\r
+ SenseDataLength = 0;\r
+ Status = ScsiInquiryCommandEx (\r
+ ScsiDiskDevice->ScsiIo,\r
+ SCSI_DISK_TIMEOUT,\r
+ NULL,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus,\r
+ (VOID *)BlockLimits,\r
+ &InquiryDataLength,\r
+ TRUE,\r
+ EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ ScsiDiskDevice->BlkIo.Media->OptimalTransferLengthGranularity =\r
+ (BlockLimits->OptimalTransferLengthGranularity2 << 8) |\r
+ BlockLimits->OptimalTransferLengthGranularity1;\r
+\r
+ ScsiDiskDevice->UnmapInfo.MaxLbaCnt =\r
+ (BlockLimits->MaximumUnmapLbaCount4 << 24) |\r
+ (BlockLimits->MaximumUnmapLbaCount3 << 16) |\r
+ (BlockLimits->MaximumUnmapLbaCount2 << 8) |\r
+ BlockLimits->MaximumUnmapLbaCount1;\r
+ ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt =\r
+ (BlockLimits->MaximumUnmapBlockDescriptorCount4 << 24) |\r
+ (BlockLimits->MaximumUnmapBlockDescriptorCount3 << 16) |\r
+ (BlockLimits->MaximumUnmapBlockDescriptorCount2 << 8) |\r
+ BlockLimits->MaximumUnmapBlockDescriptorCount1;\r
+ ScsiDiskDevice->EraseBlock.EraseLengthGranularity =\r
+ (BlockLimits->OptimalUnmapGranularity4 << 24) |\r
+ (BlockLimits->OptimalUnmapGranularity3 << 16) |\r
+ (BlockLimits->OptimalUnmapGranularity2 << 8) |\r
+ BlockLimits->OptimalUnmapGranularity1;\r
+ if (BlockLimits->UnmapGranularityAlignmentValid != 0) {\r
+ ScsiDiskDevice->UnmapInfo.GranularityAlignment =\r
+ (BlockLimits->UnmapGranularityAlignment4 << 24) |\r
+ (BlockLimits->UnmapGranularityAlignment3 << 16) |\r
+ (BlockLimits->UnmapGranularityAlignment2 << 8) |\r
+ BlockLimits->UnmapGranularityAlignment1;\r
+ }\r
+\r
+ if (ScsiDiskDevice->EraseBlock.EraseLengthGranularity == 0) {\r
+ //\r
+ // A value of 0 indicates that the optimal unmap granularity is\r
+ // not reported.\r
+ //\r
+ ScsiDiskDevice->EraseBlock.EraseLengthGranularity = 1;\r
+ }\r
+\r
+ ScsiDiskDevice->BlockLimitsVpdSupported = TRUE;\r
+ }\r
+\r
+ FreeAlignedBuffer (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));\r
+ }\r
+ }\r
+\r
+ FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
+ }\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ return EFI_SUCCESS;\r
+ } else if (Status == EFI_NOT_READY) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // go ahead to check HostAdapterStatus and TargetStatus\r
+ // (EFI_TIMEOUT, EFI_DEVICE_ERROR)\r
+ //\r
+\r
+ Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+ if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
+ //\r
+ // reset the scsi channel\r
+ //\r
+ ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Status = CheckTargetStatus (TargetStatus);\r
+ if (Status == EFI_NOT_READY) {\r
+ //\r
+ // reset the scsi device\r
+ //\r
+ ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // if goes here, meant ScsiInquiryCommand() failed.\r
+ // if ScsiDiskRequestSenseKeys() succeeds at last,\r
+ // better retry ScsiInquiryCommand(). (by setting *NeedRetry = TRUE)\r
+ //\r
+ MaxRetry = 3;\r
+ for (Index = 0; Index < MaxRetry; Index++) {\r
+ Status = ScsiDiskRequestSenseKeys (\r
+ ScsiDiskDevice,\r
+ NeedRetry,\r
+ &SenseDataArray,\r
+ &NumberOfSenseKeys,\r
+ TRUE\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (!*NeedRetry) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
+ // set *NeedRetry = FALSE to avoid the outside caller try again.\r
+ //\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+}\r
+\r
+/**\r
+ To test device.\r
+\r
+ When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense;\r
+ When Test Unit Ready command encounters any error caused by host adapter or\r
+ target, return error without retrieving Sense Keys.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
+ @param NeedRetry The pointer of flag indicates try again\r
+ @param SenseDataArray The pointer of an array of sense data\r
+ @param NumberOfSenseKeys The pointer of the number of sense data array\r
+\r
+ @retval EFI_DEVICE_ERROR Indicates that error occurs\r
+ @retval EFI_SUCCESS Successfully to test unit\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskTestUnitReady (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ OUT BOOLEAN *NeedRetry,\r
+ OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
+ OUT UINTN *NumberOfSenseKeys\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 SenseDataLength;\r
+ UINT8 HostAdapterStatus;\r
+ UINT8 TargetStatus;\r
+ UINT8 Index;\r
+ UINT8 MaxRetry;\r
+\r
+ SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+ *NumberOfSenseKeys = 0;\r
+\r
+ //\r
+ // Parameter 3 and 4: do not require sense data, retrieve it when needed.\r
+ //\r
+ Status = ScsiTestUnitReadyCommand (\r
+ ScsiDiskDevice->ScsiIo,\r
+ SCSI_DISK_TIMEOUT,\r
+ ScsiDiskDevice->SenseData,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus\r
+ );\r
+ //\r
+ // no need to check HostAdapterStatus and TargetStatus\r
+ //\r
+ if (Status == EFI_NOT_READY) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)\r
+ //\r
+\r
+ Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+ if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
+ //\r
+ // reset the scsi channel\r
+ //\r
+ ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Status = CheckTargetStatus (TargetStatus);\r
+ if (Status == EFI_NOT_READY) {\r
+ //\r
+ // reset the scsi device\r
+ //\r
+ ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (SenseDataLength != 0) {\r
+ *NumberOfSenseKeys = SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA);\r
+ *SenseDataArray = ScsiDiskDevice->SenseData;\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ MaxRetry = 3;\r
+ for (Index = 0; Index < MaxRetry; Index++) {\r
+ Status = ScsiDiskRequestSenseKeys (\r
+ ScsiDiskDevice,\r
+ NeedRetry,\r
+ SenseDataArray,\r
+ NumberOfSenseKeys,\r
+ FALSE\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (!*NeedRetry) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
+ // set *NeedRetry = FALSE to avoid the outside caller try again.\r
+ //\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+}\r
+\r
+/**\r
+ Parsing Sense Keys which got from request sense command.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
+ @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
+ @param NumberOfSenseKeys The number of sense key\r
+ @param Action The pointer of action which indicates what is need to do next\r
+\r
+ @retval EFI_DEVICE_ERROR Indicates that error occurs\r
+ @retval EFI_SUCCESS Successfully to complete the parsing\r
+\r
+**/\r
+EFI_STATUS\r
+DetectMediaParsingSenseKeys (\r
+ OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN EFI_SCSI_SENSE_DATA *SenseData,\r
+ IN UINTN NumberOfSenseKeys,\r
+ OUT UINTN *Action\r
+ )\r
+{\r
+ BOOLEAN RetryLater;\r
+\r
+ //\r
+ // Default is to read capacity, unless..\r
+ //\r
+ *Action = ACTION_READ_CAPACITY;\r
+\r
+ if (NumberOfSenseKeys == 0) {\r
+ if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) {\r
+ *Action = ACTION_NO_ACTION;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {\r
+ //\r
+ // No Sense Key returned from last submitted command\r
+ //\r
+ if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) {\r
+ *Action = ACTION_NO_ACTION;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) {\r
+ ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;\r
+ ScsiDiskDevice->BlkIo.Media->LastBlock = 0;\r
+ *Action = ACTION_NO_ACTION;\r
+ DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsNoMedia\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {\r
+ ScsiDiskDevice->BlkIo.Media->MediaId++;\r
+ DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsMediaChange!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (ScsiDiskIsResetBefore (SenseData, NumberOfSenseKeys)) {\r
+ *Action = ACTION_RETRY_COMMAND_LATER;\r
+ DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsResetBefore!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {\r
+ DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsMediaError\n"));\r
+ *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {\r
+ DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskIsHardwareError\n"));\r
+ *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {\r
+ if (RetryLater) {\r
+ *Action = ACTION_RETRY_COMMAND_LATER;\r
+ DEBUG ((DEBUG_VERBOSE, "ScsiDisk: ScsiDiskDriveNotReady!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ *Action = ACTION_NO_ACTION;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;\r
+ DEBUG ((DEBUG_VERBOSE, "ScsiDisk: Sense Key = 0x%x ASC = 0x%x!\n", SenseData->Sense_Key, SenseData->Addnl_Sense_Code));\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Send read capacity command to device and get the device parameter.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
+ @param NeedRetry The pointer of flag indicates if need a retry\r
+ @param SenseDataArray The pointer of an array of sense data\r
+ @param NumberOfSenseKeys The number of sense key\r
+\r
+ @retval EFI_DEVICE_ERROR Indicates that error occurs\r
+ @retval EFI_SUCCESS Successfully to read capacity or sense data is received.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskReadCapacity (\r
+ IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
+ OUT BOOLEAN *NeedRetry,\r
+ OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
+ OUT UINTN *NumberOfSenseKeys\r
+ )\r
+{\r
+ UINT8 HostAdapterStatus;\r
+ UINT8 TargetStatus;\r
+ EFI_STATUS CommandStatus;\r
+ EFI_STATUS Status;\r
+ UINT8 Index;\r
+ UINT8 MaxRetry;\r
+ UINT8 SenseDataLength;\r
+ UINT32 DataLength10;\r
+ UINT32 DataLength16;\r
+ EFI_SCSI_DISK_CAPACITY_DATA *CapacityData10;\r
+ EFI_SCSI_DISK_CAPACITY_DATA16 *CapacityData16;\r
+\r
+ CapacityData10 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
+ if (CapacityData10 == NULL) {\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ CapacityData16 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
+ if (CapacityData16 == NULL) {\r
+ FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ SenseDataLength = 0;\r
+ DataLength10 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);\r
+ DataLength16 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);\r
+ ZeroMem (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
+ ZeroMem (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
+\r
+ *NumberOfSenseKeys = 0;\r
+ *NeedRetry = FALSE;\r
+\r
+ //\r
+ // submit Read Capacity(10) Command. If it returns capacity of FFFFFFFFh,\r
+ // 16 byte command should be used to access large hard disk >2TB\r
+ //\r
+ CommandStatus = ScsiReadCapacityCommand (\r
+ ScsiDiskDevice->ScsiIo,\r
+ SCSI_DISK_TIMEOUT,\r
+ NULL,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus,\r
+ (VOID *)CapacityData10,\r
+ &DataLength10,\r
+ FALSE\r
+ );\r
+\r
+ ScsiDiskDevice->Cdb16Byte = FALSE;\r
+ if ((!EFI_ERROR (CommandStatus)) && (CapacityData10->LastLba3 == 0xff) && (CapacityData10->LastLba2 == 0xff) &&\r
+ (CapacityData10->LastLba1 == 0xff) && (CapacityData10->LastLba0 == 0xff))\r
+ {\r
+ //\r
+ // use Read Capacity (16), Read (16) and Write (16) next when hard disk size > 2TB\r
+ //\r
+ ScsiDiskDevice->Cdb16Byte = TRUE;\r
+ //\r
+ // submit Read Capacity(16) Command to get parameter LogicalBlocksPerPhysicalBlock\r
+ // and LowestAlignedLba\r
+ //\r
+ CommandStatus = ScsiReadCapacity16Command (\r
+ ScsiDiskDevice->ScsiIo,\r
+ SCSI_DISK_TIMEOUT,\r
+ NULL,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus,\r
+ (VOID *)CapacityData16,\r
+ &DataLength16,\r
+ FALSE\r
+ );\r
+ }\r
+\r
+ //\r
+ // no need to check HostAdapterStatus and TargetStatus\r
+ //\r
+ if (CommandStatus == EFI_SUCCESS) {\r
+ GetMediaInfo (ScsiDiskDevice, CapacityData10, CapacityData16);\r
+ FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
+ FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
+ FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
+\r
+ if (CommandStatus == EFI_NOT_READY) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if ((CommandStatus == EFI_INVALID_PARAMETER) || (CommandStatus == EFI_UNSUPPORTED)) {\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // go ahead to check HostAdapterStatus and TargetStatus\r
+ // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
+ //\r
+\r
+ Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+ if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
+ //\r
+ // reset the scsi channel\r
+ //\r
+ ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ Status = CheckTargetStatus (TargetStatus);\r
+ if (Status == EFI_NOT_READY) {\r
+ //\r
+ // reset the scsi device\r
+ //\r
+ ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // if goes here, meant ScsiReadCapacityCommand() failed.\r
+ // if ScsiDiskRequestSenseKeys() succeeds at last,\r
+ // better retry ScsiReadCapacityCommand(). (by setting *NeedRetry = TRUE)\r
+ //\r
+ MaxRetry = 3;\r
+ for (Index = 0; Index < MaxRetry; Index++) {\r
+ Status = ScsiDiskRequestSenseKeys (\r
+ ScsiDiskDevice,\r
+ NeedRetry,\r
+ SenseDataArray,\r
+ NumberOfSenseKeys,\r
+ TRUE\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (!*NeedRetry) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
+\r
+ //\r
+ // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
+ // set *NeedRetry = FALSE to avoid the outside caller try again.\r
+ //\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+}\r
+\r
+/**\r
+ Check the HostAdapter status and re-interpret it in EFI_STATUS.\r
+\r
+ @param HostAdapterStatus Host Adapter status\r
+\r
+ @retval EFI_SUCCESS Host adapter is OK.\r
+ @retval EFI_TIMEOUT Timeout.\r
+ @retval EFI_NOT_READY Adapter NOT ready.\r
+ @retval EFI_DEVICE_ERROR Adapter device error.\r
+\r
+**/\r
+EFI_STATUS\r
+CheckHostAdapterStatus (\r
+ IN UINT8 HostAdapterStatus\r
+ )\r
+{\r
+ switch (HostAdapterStatus) {\r
+ case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK:\r
+ return EFI_SUCCESS;\r
+\r
+ case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT:\r
+ case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT:\r
+ case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND:\r
+ return EFI_TIMEOUT;\r
+\r
+ case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT:\r
+ case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR:\r
+ case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED:\r
+ case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN:\r
+ case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET:\r
+ return EFI_NOT_READY;\r
+\r
+ case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE:\r
+ case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR:\r
+ return EFI_DEVICE_ERROR;\r
+\r
+ default:\r
+ return EFI_SUCCESS;\r
+ }\r
+}\r
+\r
+/**\r
+ Check the target status and re-interpret it in EFI_STATUS.\r
+\r
+ @param TargetStatus Target status\r
+\r
+ @retval EFI_NOT_READY Device is NOT ready.\r
+ @retval EFI_DEVICE_ERROR\r
+ @retval EFI_SUCCESS\r
+\r
+**/\r
+EFI_STATUS\r
+CheckTargetStatus (\r
+ IN UINT8 TargetStatus\r
+ )\r
+{\r
+ switch (TargetStatus) {\r
+ case EFI_EXT_SCSI_STATUS_TARGET_GOOD:\r
+ case EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION:\r
+ case EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET:\r
+ return EFI_SUCCESS;\r
+\r
+ case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE:\r
+ case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET:\r
+ case EFI_EXT_SCSI_STATUS_TARGET_BUSY:\r
+ case EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL:\r
+ return EFI_NOT_READY;\r
+\r
+ case EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT:\r
+ return EFI_DEVICE_ERROR;\r
+\r
+ default:\r
+ return EFI_SUCCESS;\r
+ }\r
+}\r
+\r
+/**\r
+ Retrieve all sense keys from the device.\r
+\r
+ When encountering error during the process, if retrieve sense keys before\r
+ error encountered, it returns the sense keys with return status set to EFI_SUCCESS,\r
+ and NeedRetry set to FALSE; otherwise, return the proper return status.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
+ @param NeedRetry The pointer of flag indicates if need a retry\r
+ @param SenseDataArray The pointer of an array of sense data\r
+ @param NumberOfSenseKeys The number of sense key\r
+ @param AskResetIfError The flag indicates if need reset when error occurs\r
+\r
+ @retval EFI_DEVICE_ERROR Indicates that error occurs\r
+ @retval EFI_SUCCESS Successfully to request sense key\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskRequestSenseKeys (\r
+ IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
+ OUT BOOLEAN *NeedRetry,\r
+ OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
+ OUT UINTN *NumberOfSenseKeys,\r
+ IN BOOLEAN AskResetIfError\r
+ )\r
+{\r
+ EFI_SCSI_SENSE_DATA *PtrSenseData;\r
+ UINT8 SenseDataLength;\r
+ BOOLEAN SenseReq;\r
+ EFI_STATUS Status;\r
+ EFI_STATUS FallStatus;\r
+ UINT8 HostAdapterStatus;\r
+ UINT8 TargetStatus;\r
+\r
+ FallStatus = EFI_SUCCESS;\r
+ SenseDataLength = (UINT8)sizeof (EFI_SCSI_SENSE_DATA);\r
+\r
+ ZeroMem (\r
+ ScsiDiskDevice->SenseData,\r
+ sizeof (EFI_SCSI_SENSE_DATA) * (ScsiDiskDevice->SenseDataNumber)\r
+ );\r
+\r
+ *NumberOfSenseKeys = 0;\r
+ *SenseDataArray = ScsiDiskDevice->SenseData;\r
+ Status = EFI_SUCCESS;\r
+ PtrSenseData = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SENSE_DATA));\r
+ if (PtrSenseData == NULL) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ for (SenseReq = TRUE; SenseReq;) {\r
+ ZeroMem (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA));\r
+ Status = ScsiRequestSenseCommand (\r
+ ScsiDiskDevice->ScsiIo,\r
+ SCSI_DISK_TIMEOUT,\r
+ PtrSenseData,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus\r
+ );\r
+ if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
+ FallStatus = EFI_SUCCESS;\r
+ } else if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+ *NeedRetry = TRUE;\r
+ FallStatus = EFI_DEVICE_ERROR;\r
+ } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
+ *NeedRetry = FALSE;\r
+ FallStatus = EFI_DEVICE_ERROR;\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
+ if (AskResetIfError) {\r
+ ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+ }\r
+\r
+ FallStatus = EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ if (EFI_ERROR (FallStatus)) {\r
+ if (*NumberOfSenseKeys != 0) {\r
+ *NeedRetry = FALSE;\r
+ Status = EFI_SUCCESS;\r
+ goto EXIT;\r
+ } else {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto EXIT;\r
+ }\r
+ }\r
+\r
+ CopyMem (ScsiDiskDevice->SenseData + *NumberOfSenseKeys, PtrSenseData, SenseDataLength);\r
+ (*NumberOfSenseKeys) += 1;\r
+\r
+ //\r
+ // no more sense key or number of sense keys exceeds predefined,\r
+ // skip the loop.\r
+ //\r
+ if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) ||\r
+ (*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber))\r
+ {\r
+ SenseReq = FALSE;\r
+ }\r
+ }\r
+\r
+EXIT:\r
+ FreeAlignedBuffer (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Get information from media read capacity command.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
+ @param Capacity10 The pointer of EFI_SCSI_DISK_CAPACITY_DATA\r
+ @param Capacity16 The pointer of EFI_SCSI_DISK_CAPACITY_DATA16\r
+\r
+**/\r
+VOID\r
+GetMediaInfo (\r
+ IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN EFI_SCSI_DISK_CAPACITY_DATA *Capacity10,\r
+ IN EFI_SCSI_DISK_CAPACITY_DATA16 *Capacity16\r
+ )\r
+{\r
+ UINT8 *Ptr;\r
+\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ ScsiDiskDevice->BlkIo.Media->LastBlock = ((UINT32)Capacity10->LastLba3 << 24) |\r
+ (Capacity10->LastLba2 << 16) |\r
+ (Capacity10->LastLba1 << 8) |\r
+ Capacity10->LastLba0;\r
+\r
+ ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity10->BlockSize3 << 24) |\r
+ (Capacity10->BlockSize2 << 16) |\r
+ (Capacity10->BlockSize1 << 8) |\r
+ Capacity10->BlockSize0;\r
+ ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = 0;\r
+ ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = 0;\r
+ if (!ScsiDiskDevice->BlockLimitsVpdSupported) {\r
+ ScsiDiskDevice->UnmapInfo.MaxLbaCnt = (UINT32)ScsiDiskDevice->BlkIo.Media->LastBlock;\r
+ }\r
+ } else {\r
+ Ptr = (UINT8 *)&ScsiDiskDevice->BlkIo.Media->LastBlock;\r
+ *Ptr++ = Capacity16->LastLba0;\r
+ *Ptr++ = Capacity16->LastLba1;\r
+ *Ptr++ = Capacity16->LastLba2;\r
+ *Ptr++ = Capacity16->LastLba3;\r
+ *Ptr++ = Capacity16->LastLba4;\r
+ *Ptr++ = Capacity16->LastLba5;\r
+ *Ptr++ = Capacity16->LastLba6;\r
+ *Ptr = Capacity16->LastLba7;\r
+\r
+ ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity16->BlockSize3 << 24) |\r
+ (Capacity16->BlockSize2 << 16) |\r
+ (Capacity16->BlockSize1 << 8) |\r
+ Capacity16->BlockSize0;\r
+\r
+ ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = (Capacity16->LowestAlignLogic2 << 8) |\r
+ Capacity16->LowestAlignLogic1;\r
+ ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = (1 << Capacity16->LogicPerPhysical);\r
+ if (!ScsiDiskDevice->BlockLimitsVpdSupported) {\r
+ if (ScsiDiskDevice->BlkIo.Media->LastBlock > (UINT32)-1) {\r
+ ScsiDiskDevice->UnmapInfo.MaxLbaCnt = (UINT32)-1;\r
+ } else {\r
+ ScsiDiskDevice->UnmapInfo.MaxLbaCnt = (UINT32)ScsiDiskDevice->BlkIo.Media->LastBlock;\r
+ }\r
+ }\r
+ }\r
+\r
+ ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;\r
+}\r
+\r
+/**\r
+ Parse Inquiry data.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
+\r
+**/\r
+VOID\r
+ParseInquiryData (\r
+ IN OUT SCSI_DISK_DEV *ScsiDiskDevice\r
+ )\r
+{\r
+ ScsiDiskDevice->FixedDevice = (BOOLEAN)((ScsiDiskDevice->InquiryData.Rmb == 1) ? 0 : 1);\r
+ ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN)(!ScsiDiskDevice->FixedDevice);\r
+}\r
+\r
+/**\r
+ Read sector from SCSI Disk.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
+ @param Buffer The buffer to fill in the read out data\r
+ @param Lba Logic block address\r
+ @param NumberOfBlocks The number of blocks to read\r
+\r
+ @retval EFI_DEVICE_ERROR Indicates a device error.\r
+ @retval EFI_SUCCESS Operation is successful.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskReadSectors (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ OUT VOID *Buffer,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN NumberOfBlocks\r
+ )\r
+{\r
+ UINTN BlocksRemaining;\r
+ UINT8 *PtrBuffer;\r
+ UINT32 BlockSize;\r
+ UINT32 ByteCount;\r
+ UINT32 MaxBlock;\r
+ UINT32 SectorCount;\r
+ UINT32 NextSectorCount;\r
+ UINT64 Timeout;\r
+ EFI_STATUS Status;\r
+ UINT8 Index;\r
+ UINT8 MaxRetry;\r
+ BOOLEAN NeedRetry;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ BlocksRemaining = NumberOfBlocks;\r
+ BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+\r
+ //\r
+ // limit the data bytes that can be transferred by one Read(10) or Read(16) Command\r
+ //\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ MaxBlock = 0xFFFF;\r
+ } else {\r
+ MaxBlock = 0xFFFFFFFF;\r
+ }\r
+\r
+ PtrBuffer = Buffer;\r
+\r
+ while (BlocksRemaining > 0) {\r
+ if (BlocksRemaining <= MaxBlock) {\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ SectorCount = (UINT16)BlocksRemaining;\r
+ } else {\r
+ SectorCount = (UINT32)BlocksRemaining;\r
+ }\r
+ } else {\r
+ SectorCount = MaxBlock;\r
+ }\r
+\r
+ ByteCount = SectorCount * BlockSize;\r
+ //\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | ATA Transfer Mode | Transfer Rate | SCSI Interface | Transfer Rate |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 0 | 3.3Mbytes/sec | SCSI-1 | 5Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 1 | 5.2Mbytes/sec | Fast SCSI | 10Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 2 | 8.3Mbytes/sec | Fast-Wide SCSI | 20Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 3 | 11.1Mbytes/sec | Ultra SCSI | 20Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 4 | 16.6Mbytes/sec | Ultra Wide SCSI | 40Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Single-word DMA Mode 0 | 2.1Mbytes/sec | Ultra2 SCSI | 40Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Single-word DMA Mode 1 | 4.2Mbytes/sec | Ultra2 Wide SCSI | 80Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Single-word DMA Mode 2 | 8.4Mbytes/sec | Ultra3 SCSI | 160Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Multi-word DMA Mode 0 | 4.2Mbytes/sec | Ultra-320 SCSI | 320Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Multi-word DMA Mode 1 | 13.3Mbytes/sec | Ultra-640 SCSI | 640Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ //\r
+ // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, we have to use\r
+ // the lowest transfer rate to calculate the possible maximum timeout value for each operation.\r
+ // From the above table, we could know 2.1Mbytes per second is lowest one.\r
+ // The timeout value is rounded up to nearest integer and here an additional 30s is added\r
+ // to follow ATA spec in which it mentioned that the device may take up to 30s to respond\r
+ // commands in the Standby/Idle mode.\r
+ //\r
+ Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);\r
+\r
+ MaxRetry = 2;\r
+ for (Index = 0; Index < MaxRetry; Index++) {\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ Status = ScsiDiskRead10 (\r
+ ScsiDiskDevice,\r
+ &NeedRetry,\r
+ Timeout,\r
+ PtrBuffer,\r
+ &ByteCount,\r
+ (UINT32)Lba,\r
+ SectorCount\r
+ );\r
+ } else {\r
+ Status = ScsiDiskRead16 (\r
+ ScsiDiskDevice,\r
+ &NeedRetry,\r
+ Timeout,\r
+ PtrBuffer,\r
+ &ByteCount,\r
+ Lba,\r
+ SectorCount\r
+ );\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ if (!NeedRetry) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // We need to retry. However, if ScsiDiskRead10() or ScsiDiskRead16() has\r
+ // lowered ByteCount on output, we must make sure that we lower\r
+ // SectorCount accordingly. SectorCount will be encoded in the CDB, and\r
+ // it is invalid to request more sectors in the CDB than the entire\r
+ // transfer (ie. ByteCount) can carry.\r
+ //\r
+ // In addition, ByteCount is only expected to go down, or stay unchanged.\r
+ // Therefore we don't need to update Timeout: the original timeout should\r
+ // accommodate shorter transfers too.\r
+ //\r
+ NextSectorCount = ByteCount / BlockSize;\r
+ if (NextSectorCount < SectorCount) {\r
+ SectorCount = NextSectorCount;\r
+ //\r
+ // Account for any rounding down.\r
+ //\r
+ ByteCount = SectorCount * BlockSize;\r
+ }\r
+ }\r
+\r
+ if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // actual transferred sectors\r
+ //\r
+ SectorCount = ByteCount / BlockSize;\r
+\r
+ Lba += SectorCount;\r
+ PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
+ BlocksRemaining -= SectorCount;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Write sector to SCSI Disk.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
+ @param Buffer The buffer of data to be written into SCSI Disk\r
+ @param Lba Logic block address\r
+ @param NumberOfBlocks The number of blocks to read\r
+\r
+ @retval EFI_DEVICE_ERROR Indicates a device error.\r
+ @retval EFI_SUCCESS Operation is successful.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskWriteSectors (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN VOID *Buffer,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN NumberOfBlocks\r
+ )\r
+{\r
+ UINTN BlocksRemaining;\r
+ UINT8 *PtrBuffer;\r
+ UINT32 BlockSize;\r
+ UINT32 ByteCount;\r
+ UINT32 MaxBlock;\r
+ UINT32 SectorCount;\r
+ UINT32 NextSectorCount;\r
+ UINT64 Timeout;\r
+ EFI_STATUS Status;\r
+ UINT8 Index;\r
+ UINT8 MaxRetry;\r
+ BOOLEAN NeedRetry;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ BlocksRemaining = NumberOfBlocks;\r
+ BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+\r
+ //\r
+ // limit the data bytes that can be transferred by one Read(10) or Read(16) Command\r
+ //\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ MaxBlock = 0xFFFF;\r
+ } else {\r
+ MaxBlock = 0xFFFFFFFF;\r
+ }\r
+\r
+ PtrBuffer = Buffer;\r
+\r
+ while (BlocksRemaining > 0) {\r
+ if (BlocksRemaining <= MaxBlock) {\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ SectorCount = (UINT16)BlocksRemaining;\r
+ } else {\r
+ SectorCount = (UINT32)BlocksRemaining;\r
+ }\r
+ } else {\r
+ SectorCount = MaxBlock;\r
+ }\r
+\r
+ ByteCount = SectorCount * BlockSize;\r
+ //\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | ATA Transfer Mode | Transfer Rate | SCSI Interface | Transfer Rate |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 0 | 3.3Mbytes/sec | SCSI-1 | 5Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 1 | 5.2Mbytes/sec | Fast SCSI | 10Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 2 | 8.3Mbytes/sec | Fast-Wide SCSI | 20Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 3 | 11.1Mbytes/sec | Ultra SCSI | 20Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 4 | 16.6Mbytes/sec | Ultra Wide SCSI | 40Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Single-word DMA Mode 0 | 2.1Mbytes/sec | Ultra2 SCSI | 40Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Single-word DMA Mode 1 | 4.2Mbytes/sec | Ultra2 Wide SCSI | 80Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Single-word DMA Mode 2 | 8.4Mbytes/sec | Ultra3 SCSI | 160Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Multi-word DMA Mode 0 | 4.2Mbytes/sec | Ultra-320 SCSI | 320Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Multi-word DMA Mode 1 | 13.3Mbytes/sec | Ultra-640 SCSI | 640Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ //\r
+ // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, we have to use\r
+ // the lowest transfer rate to calculate the possible maximum timeout value for each operation.\r
+ // From the above table, we could know 2.1Mbytes per second is lowest one.\r
+ // The timeout value is rounded up to nearest integer and here an additional 30s is added\r
+ // to follow ATA spec in which it mentioned that the device may take up to 30s to respond\r
+ // commands in the Standby/Idle mode.\r
+ //\r
+ Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);\r
+ MaxRetry = 2;\r
+ for (Index = 0; Index < MaxRetry; Index++) {\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ Status = ScsiDiskWrite10 (\r
+ ScsiDiskDevice,\r
+ &NeedRetry,\r
+ Timeout,\r
+ PtrBuffer,\r
+ &ByteCount,\r
+ (UINT32)Lba,\r
+ SectorCount\r
+ );\r
+ } else {\r
+ Status = ScsiDiskWrite16 (\r
+ ScsiDiskDevice,\r
+ &NeedRetry,\r
+ Timeout,\r
+ PtrBuffer,\r
+ &ByteCount,\r
+ Lba,\r
+ SectorCount\r
+ );\r
+ }\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ break;\r
+ }\r
+\r
+ if (!NeedRetry) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // We need to retry. However, if ScsiDiskWrite10() or ScsiDiskWrite16()\r
+ // has lowered ByteCount on output, we must make sure that we lower\r
+ // SectorCount accordingly. SectorCount will be encoded in the CDB, and\r
+ // it is invalid to request more sectors in the CDB than the entire\r
+ // transfer (ie. ByteCount) can carry.\r
+ //\r
+ // In addition, ByteCount is only expected to go down, or stay unchanged.\r
+ // Therefore we don't need to update Timeout: the original timeout should\r
+ // accommodate shorter transfers too.\r
+ //\r
+ NextSectorCount = ByteCount / BlockSize;\r
+ if (NextSectorCount < SectorCount) {\r
+ SectorCount = NextSectorCount;\r
+ //\r
+ // Account for any rounding down.\r
+ //\r
+ ByteCount = SectorCount * BlockSize;\r
+ }\r
+ }\r
+\r
+ if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ //\r
+ // actual transferred sectors\r
+ //\r
+ SectorCount = ByteCount / BlockSize;\r
+\r
+ Lba += SectorCount;\r
+ PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
+ BlocksRemaining -= SectorCount;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Asynchronously read sector from SCSI Disk.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.\r
+ @param Buffer The buffer to fill in the read out data.\r
+ @param Lba Logic block address.\r
+ @param NumberOfBlocks The number of blocks to read.\r
+ @param Token A pointer to the token associated with the\r
+ non-blocking read request.\r
+\r
+ @retval EFI_INVALID_PARAMETER Token is NULL or Token->Event is NULL.\r
+ @retval EFI_DEVICE_ERROR Indicates a device error.\r
+ @retval EFI_SUCCESS Operation is successful.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskAsyncReadSectors (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ OUT VOID *Buffer,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN NumberOfBlocks,\r
+ IN EFI_BLOCK_IO2_TOKEN *Token\r
+ )\r
+{\r
+ UINTN BlocksRemaining;\r
+ UINT8 *PtrBuffer;\r
+ UINT32 BlockSize;\r
+ UINT32 ByteCount;\r
+ UINT32 MaxBlock;\r
+ UINT32 SectorCount;\r
+ UINT64 Timeout;\r
+ SCSI_BLKIO2_REQUEST *BlkIo2Req;\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+\r
+ if ((Token == NULL) || (Token->Event == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ BlkIo2Req = AllocateZeroPool (sizeof (SCSI_BLKIO2_REQUEST));\r
+ if (BlkIo2Req == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ BlkIo2Req->Token = Token;\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &BlkIo2Req->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ InitializeListHead (&BlkIo2Req->ScsiRWQueue);\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ BlocksRemaining = NumberOfBlocks;\r
+ BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+\r
+ //\r
+ // Limit the data bytes that can be transferred by one Read(10) or Read(16)\r
+ // Command\r
+ //\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ MaxBlock = 0xFFFF;\r
+ } else {\r
+ MaxBlock = 0xFFFFFFFF;\r
+ }\r
+\r
+ PtrBuffer = Buffer;\r
+\r
+ while (BlocksRemaining > 0) {\r
+ if (BlocksRemaining <= MaxBlock) {\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ SectorCount = (UINT16)BlocksRemaining;\r
+ } else {\r
+ SectorCount = (UINT32)BlocksRemaining;\r
+ }\r
+ } else {\r
+ SectorCount = MaxBlock;\r
+ }\r
+\r
+ ByteCount = SectorCount * BlockSize;\r
+ //\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | ATA Transfer Mode | Transfer Rate | SCSI Interface | Transfer Rate |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 0 | 3.3Mbytes/sec | SCSI-1 | 5Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 1 | 5.2Mbytes/sec | Fast SCSI | 10Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 2 | 8.3Mbytes/sec | Fast-Wide SCSI | 20Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 3 | 11.1Mbytes/sec | Ultra SCSI | 20Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 4 | 16.6Mbytes/sec | Ultra Wide SCSI | 40Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Single-word DMA Mode 0 | 2.1Mbytes/sec | Ultra2 SCSI | 40Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Single-word DMA Mode 1 | 4.2Mbytes/sec | Ultra2 Wide SCSI | 80Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Single-word DMA Mode 2 | 8.4Mbytes/sec | Ultra3 SCSI | 160Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Multi-word DMA Mode 0 | 4.2Mbytes/sec | Ultra-320 SCSI | 320Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Multi-word DMA Mode 1 | 13.3Mbytes/sec | Ultra-640 SCSI | 640Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ //\r
+ // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices,\r
+ // we have to use the lowest transfer rate to calculate the possible\r
+ // maximum timeout value for each operation.\r
+ // From the above table, we could know 2.1Mbytes per second is lowest one.\r
+ // The timeout value is rounded up to nearest integer and here an additional\r
+ // 30s is added to follow ATA spec in which it mentioned that the device\r
+ // may take up to 30s to respond commands in the Standby/Idle mode.\r
+ //\r
+ Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);\r
+\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ Status = ScsiDiskAsyncRead10 (\r
+ ScsiDiskDevice,\r
+ Timeout,\r
+ 0,\r
+ PtrBuffer,\r
+ ByteCount,\r
+ (UINT32)Lba,\r
+ SectorCount,\r
+ BlkIo2Req,\r
+ Token\r
+ );\r
+ } else {\r
+ Status = ScsiDiskAsyncRead16 (\r
+ ScsiDiskDevice,\r
+ Timeout,\r
+ 0,\r
+ PtrBuffer,\r
+ ByteCount,\r
+ Lba,\r
+ SectorCount,\r
+ BlkIo2Req,\r
+ Token\r
+ );\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Some devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data\r
+ // length of a SCSI I/O command is too large.\r
+ // In this case, we retry sending the SCSI command with a data length\r
+ // half of its previous value.\r
+ //\r
+ if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_TIMEOUT)) {\r
+ if ((MaxBlock > 1) && (SectorCount > 1)) {\r
+ MaxBlock = MIN (MaxBlock, SectorCount) >> 1;\r
+ continue;\r
+ }\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
+ //\r
+ // Free the SCSI_BLKIO2_REQUEST structure only when there is no other\r
+ // SCSI sub-task running. Otherwise, it will be freed in the callback\r
+ // function ScsiDiskNotify().\r
+ //\r
+ RemoveEntryList (&BlkIo2Req->Link);\r
+ FreePool (BlkIo2Req);\r
+ BlkIo2Req = NULL;\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ //\r
+ // It is safe to return error status to the caller, since there is no\r
+ // previous SCSI sub-task executing.\r
+ //\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ } else {\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ //\r
+ // There are previous SCSI commands still running, EFI_SUCCESS should\r
+ // be returned to make sure that the caller does not free resources\r
+ // still using by these SCSI commands.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Sectors submitted for transfer\r
+ //\r
+ SectorCount = ByteCount / BlockSize;\r
+\r
+ Lba += SectorCount;\r
+ PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
+ BlocksRemaining -= SectorCount;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+Done:\r
+ if (BlkIo2Req != NULL) {\r
+ BlkIo2Req->LastScsiRW = TRUE;\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
+ RemoveEntryList (&BlkIo2Req->Link);\r
+ FreePool (BlkIo2Req);\r
+ BlkIo2Req = NULL;\r
+\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Asynchronously write sector to SCSI Disk.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.\r
+ @param Buffer The buffer of data to be written into SCSI Disk.\r
+ @param Lba Logic block address.\r
+ @param NumberOfBlocks The number of blocks to read.\r
+ @param Token A pointer to the token associated with the\r
+ non-blocking read request.\r
+\r
+ @retval EFI_INVALID_PARAMETER Token is NULL or Token->Event is NULL\r
+ @retval EFI_DEVICE_ERROR Indicates a device error.\r
+ @retval EFI_SUCCESS Operation is successful.\r
+\r
+**/\r
+EFI_STATUS\r
+ScsiDiskAsyncWriteSectors (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN VOID *Buffer,\r
+ IN EFI_LBA Lba,\r
+ IN UINTN NumberOfBlocks,\r
+ IN EFI_BLOCK_IO2_TOKEN *Token\r
+ )\r
+{\r
+ UINTN BlocksRemaining;\r
+ UINT8 *PtrBuffer;\r
+ UINT32 BlockSize;\r
+ UINT32 ByteCount;\r
+ UINT32 MaxBlock;\r
+ UINT32 SectorCount;\r
+ UINT64 Timeout;\r
+ SCSI_BLKIO2_REQUEST *BlkIo2Req;\r
+ EFI_STATUS Status;\r
+ EFI_TPL OldTpl;\r
+\r
+ if ((Token == NULL) || (Token->Event == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ BlkIo2Req = AllocateZeroPool (sizeof (SCSI_BLKIO2_REQUEST));\r
+ if (BlkIo2Req == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ BlkIo2Req->Token = Token;\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &BlkIo2Req->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ InitializeListHead (&BlkIo2Req->ScsiRWQueue);\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ BlocksRemaining = NumberOfBlocks;\r
+ BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+\r
+ //\r
+ // Limit the data bytes that can be transferred by one Read(10) or Read(16)\r
+ // Command\r
+ //\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ MaxBlock = 0xFFFF;\r
+ } else {\r
+ MaxBlock = 0xFFFFFFFF;\r
+ }\r
+\r
+ PtrBuffer = Buffer;\r
+\r
+ while (BlocksRemaining > 0) {\r
+ if (BlocksRemaining <= MaxBlock) {\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ SectorCount = (UINT16)BlocksRemaining;\r
+ } else {\r
+ SectorCount = (UINT32)BlocksRemaining;\r
+ }\r
+ } else {\r
+ SectorCount = MaxBlock;\r
+ }\r
+\r
+ ByteCount = SectorCount * BlockSize;\r
+ //\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | ATA Transfer Mode | Transfer Rate | SCSI Interface | Transfer Rate |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 0 | 3.3Mbytes/sec | SCSI-1 | 5Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 1 | 5.2Mbytes/sec | Fast SCSI | 10Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 2 | 8.3Mbytes/sec | Fast-Wide SCSI | 20Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 3 | 11.1Mbytes/sec | Ultra SCSI | 20Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | PIO Mode 4 | 16.6Mbytes/sec | Ultra Wide SCSI | 40Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Single-word DMA Mode 0 | 2.1Mbytes/sec | Ultra2 SCSI | 40Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Single-word DMA Mode 1 | 4.2Mbytes/sec | Ultra2 Wide SCSI | 80Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Single-word DMA Mode 2 | 8.4Mbytes/sec | Ultra3 SCSI | 160Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Multi-word DMA Mode 0 | 4.2Mbytes/sec | Ultra-320 SCSI | 320Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ // | Multi-word DMA Mode 1 | 13.3Mbytes/sec | Ultra-640 SCSI | 640Mbytes/sec |\r
+ // |------------------------|-----------------|------------------|-----------------|\r
+ //\r
+ // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices,\r
+ // we have to use the lowest transfer rate to calculate the possible\r
+ // maximum timeout value for each operation.\r
+ // From the above table, we could know 2.1Mbytes per second is lowest one.\r
+ // The timeout value is rounded up to nearest integer and here an additional\r
+ // 30s is added to follow ATA spec in which it mentioned that the device\r
+ // may take up to 30s to respond commands in the Standby/Idle mode.\r
+ //\r
+ Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);\r
+\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ Status = ScsiDiskAsyncWrite10 (\r
+ ScsiDiskDevice,\r
+ Timeout,\r
+ 0,\r
+ PtrBuffer,\r
+ ByteCount,\r
+ (UINT32)Lba,\r
+ SectorCount,\r
+ BlkIo2Req,\r
+ Token\r
+ );\r
+ } else {\r
+ Status = ScsiDiskAsyncWrite16 (\r
+ ScsiDiskDevice,\r
+ Timeout,\r
+ 0,\r
+ PtrBuffer,\r
+ ByteCount,\r
+ Lba,\r
+ SectorCount,\r
+ BlkIo2Req,\r
+ Token\r
+ );\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Some devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data\r
+ // length of a SCSI I/O command is too large.\r
+ // In this case, we retry sending the SCSI command with a data length\r
+ // half of its previous value.\r
+ //\r
+ if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_TIMEOUT)) {\r
+ if ((MaxBlock > 1) && (SectorCount > 1)) {\r
+ MaxBlock = MIN (MaxBlock, SectorCount) >> 1;\r
+ continue;\r
+ }\r
+ }\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
+ //\r
+ // Free the SCSI_BLKIO2_REQUEST structure only when there is no other\r
+ // SCSI sub-task running. Otherwise, it will be freed in the callback\r
+ // function ScsiDiskNotify().\r
+ //\r
+ RemoveEntryList (&BlkIo2Req->Link);\r
+ FreePool (BlkIo2Req);\r
+ BlkIo2Req = NULL;\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ //\r
+ // It is safe to return error status to the caller, since there is no\r
+ // previous SCSI sub-task executing.\r
+ //\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto Done;\r
+ } else {\r
+ gBS->RestoreTPL (OldTpl);\r
+\r
+ //\r
+ // There are previous SCSI commands still running, EFI_SUCCESS should\r
+ // be returned to make sure that the caller does not free resources\r
+ // still using by these SCSI commands.\r
+ //\r
+ Status = EFI_SUCCESS;\r
+ goto Done;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Sectors submitted for transfer\r
+ //\r
+ SectorCount = ByteCount / BlockSize;\r
+\r
+ Lba += SectorCount;\r
+ PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
+ BlocksRemaining -= SectorCount;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+Done:\r
+ if (BlkIo2Req != NULL) {\r
+ BlkIo2Req->LastScsiRW = TRUE;\r
+\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
+ RemoveEntryList (&BlkIo2Req->Link);\r
+ FreePool (BlkIo2Req);\r
+ BlkIo2Req = NULL;\r
+\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
+\r
+ gBS->RestoreTPL (OldTpl);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Submit Read(10) command.\r
+\r
+ @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
+ @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
+ @param Timeout The time to complete the command\r
+ @param DataBuffer The buffer to fill with the read out data\r
+ @param DataLength The length of buffer\r
+ @param StartLba The start logic block address\r
+ @param SectorCount The number of blocks to read\r
+\r
+ @return EFI_STATUS is returned by calling ScsiRead10Command().\r
+**/\r
+EFI_STATUS\r
+ScsiDiskRead10 (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ OUT BOOLEAN *NeedRetry,\r
+ IN UINT64 Timeout,\r
+ OUT UINT8 *DataBuffer,\r
+ IN OUT UINT32 *DataLength,\r
+ IN UINT32 StartLba,\r
+ IN UINT32 SectorCount\r
+ )\r
+{\r
+ UINT8 SenseDataLength;\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
+ UINT8 HostAdapterStatus;\r
+ UINT8 TargetStatus;\r
+ UINTN Action;\r
+\r
+ //\r
+ // Implement a backoff algorithm to resolve some compatibility issues that\r
+ // some SCSI targets or ATAPI devices couldn't correctly response reading/writing\r
+ // big data in a single operation.\r
+ // This algorithm will at first try to execute original request. If the request fails\r
+ // with media error sense data or else, it will reduce the transfer length to half and\r
+ // try again till the operation succeeds or fails with one sector transfer length.\r
+ //\r
+BackOff:\r
+ *NeedRetry = FALSE;\r
+ Action = ACTION_NO_ACTION;\r
+ SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+ ReturnStatus = ScsiRead10Command (\r
+ ScsiDiskDevice->ScsiIo,\r
+ Timeout,\r
+ ScsiDiskDevice->SenseData,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus,\r
+ DataBuffer,\r
+ DataLength,\r
+ StartLba,\r
+ SectorCount\r
+ );\r
+\r
+ if ((ReturnStatus == EFI_NOT_READY) || (ReturnStatus == EFI_BAD_BUFFER_SIZE)) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {\r
+ *NeedRetry = FALSE;\r
+ return ReturnStatus;\r
+ }\r
+\r
+ //\r
+ // go ahead to check HostAdapterStatus and TargetStatus\r
+ // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
+ //\r
+ Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+ if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
+ //\r
+ // reset the scsi channel\r
//\r
- if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
- ParseInquiryData (ScsiDiskDevice);\r
- return EFI_SUCCESS;\r
- \r
- } else if (Status == EFI_NOT_READY) {\r
- *NeedRetry = TRUE;\r
- return EFI_DEVICE_ERROR;\r
- \r
- } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
- *NeedRetry = FALSE;\r
- return EFI_DEVICE_ERROR;\r
- }\r
- //\r
- // go ahead to check HostAdapterStatus and TargetStatus\r
- // (EFI_TIMEOUT, EFI_DEVICE_ERROR)\r
- //\r
- \r
- Status = CheckHostAdapterStatus (HostAdapterStatus);\r
- if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
- *NeedRetry = TRUE;\r
- return EFI_DEVICE_ERROR;\r
- } else if (Status == EFI_DEVICE_ERROR) {\r
- //\r
- // reset the scsi channel\r
- //\r
ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
*NeedRetry = FALSE;\r
return EFI_DEVICE_ERROR;\r
ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
*NeedRetry = TRUE;\r
return EFI_DEVICE_ERROR;\r
-\r
} else if (Status == EFI_DEVICE_ERROR) {\r
*NeedRetry = FALSE;\r
return EFI_DEVICE_ERROR;\r
}\r
- \r
- //\r
- // if goes here, meant SubmitInquiryCommand() failed.\r
- // if ScsiDiskRequestSenseKeys() succeeds at last,\r
- // better retry SubmitInquiryCommand(). (by setting *NeedRetry = TRUE)\r
- //\r
- MaxRetry = 3;\r
- for (Index = 0; Index < MaxRetry; Index++) {\r
- Status = ScsiDiskRequestSenseKeys (\r
- ScsiDiskDevice,\r
- NeedRetry,\r
- &SenseDataArray,\r
- &NumberOfSenseKeys,\r
- TRUE\r
- );\r
- if (!EFI_ERROR (Status)) {\r
+\r
+ if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {\r
+ DEBUG ((DEBUG_ERROR, "ScsiDiskRead10: Check Condition happened!\n"));\r
+ DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);\r
+ if (Action == ACTION_RETRY_COMMAND_LATER) {\r
*NeedRetry = TRUE;\r
return EFI_DEVICE_ERROR;\r
- }\r
+ } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
+ if (SectorCount <= 1) {\r
+ //\r
+ // Jump out if the operation still fails with one sector transfer length.\r
+ //\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
\r
- if (!*NeedRetry) {\r
+ //\r
+ // Try again with half length if the sense data shows we need to retry.\r
+ //\r
+ SectorCount >>= 1;\r
+ *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+ goto BackOff;\r
+ } else {\r
+ *NeedRetry = FALSE;\r
return EFI_DEVICE_ERROR;\r
}\r
}\r
- //\r
- // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
- // set *NeedRetry = FALSE to avoid the outside caller try again.\r
- //\r
- *NeedRetry = FALSE;\r
- return EFI_DEVICE_ERROR;\r
-}\r
-\r
-EFI_STATUS\r
-ScsiDiskTestUnitReady (\r
- SCSI_DISK_DEV *ScsiDiskDevice,\r
- BOOLEAN *NeedRetry,\r
- EFI_SCSI_SENSE_DATA **SenseDataArray,\r
- UINTN *NumberOfSenseKeys\r
- )\r
- /*++\r
\r
-Routine Description:\r
+ return ReturnStatus;\r
+}\r
\r
- When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense;\r
- When Test Unit Ready command encounters any error caused by host adapter or\r
- target, return error without retrieving Sense Keys.\r
- \r
-Arguments:\r
+/**\r
+ Submit Write(10) Command.\r
\r
- ScsiDiskDevice - The pointer of SCSI_DISK_DEV\r
- NeedRetry - The pointer of flag indicates try again\r
- SenseDataArray - The pointer of an array of sense data\r
- NumberOfSenseKeys - The pointer of the number of sense data array\r
- \r
-Returns:\r
+ @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
+ @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
+ @param Timeout The time to complete the command\r
+ @param DataBuffer The buffer to fill with the read out data\r
+ @param DataLength The length of buffer\r
+ @param StartLba The start logic block address\r
+ @param SectorCount The number of blocks to write\r
\r
- EFI_DEVICE_ERROR - Indicates that error occurs\r
- EFI_SUCCESS - Successfully to test unit\r
+ @return EFI_STATUS is returned by calling ScsiWrite10Command().\r
\r
---*/\r
+**/\r
+EFI_STATUS\r
+ScsiDiskWrite10 (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ OUT BOOLEAN *NeedRetry,\r
+ IN UINT64 Timeout,\r
+ IN UINT8 *DataBuffer,\r
+ IN OUT UINT32 *DataLength,\r
+ IN UINT32 StartLba,\r
+ IN UINT32 SectorCount\r
+ )\r
{\r
EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
UINT8 SenseDataLength;\r
UINT8 HostAdapterStatus;\r
UINT8 TargetStatus;\r
- UINT8 Index;\r
- UINT8 MaxRetry;\r
-\r
- SenseDataLength = 0;\r
- *NumberOfSenseKeys = 0;\r
+ UINTN Action;\r
\r
//\r
- // Parameter 3 and 4: do not require sense data, retrieve it when needed.\r
- //\r
- Status = ScsiTestUnitReadyCommand (\r
- ScsiDiskDevice->ScsiIo,\r
- EfiScsiStallSeconds (1),\r
- NULL,\r
- &SenseDataLength,\r
- &HostAdapterStatus,\r
- &TargetStatus\r
- );\r
- //\r
- // no need to check HostAdapterStatus and TargetStatus\r
+ // Implement a backoff algorithm to resolve some compatibility issues that\r
+ // some SCSI targets or ATAPI devices couldn't correctly response reading/writing\r
+ // big data in a single operation.\r
+ // This algorithm will at first try to execute original request. If the request fails\r
+ // with media error sense data or else, it will reduce the transfer length to half and\r
+ // try again till the operation succeeds or fails with one sector transfer length.\r
//\r
- if (Status == EFI_NOT_READY) {\r
+BackOff:\r
+ *NeedRetry = FALSE;\r
+ Action = ACTION_NO_ACTION;\r
+ SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+ ReturnStatus = ScsiWrite10Command (\r
+ ScsiDiskDevice->ScsiIo,\r
+ Timeout,\r
+ ScsiDiskDevice->SenseData,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus,\r
+ DataBuffer,\r
+ DataLength,\r
+ StartLba,\r
+ SectorCount\r
+ );\r
+ if ((ReturnStatus == EFI_NOT_READY) || (ReturnStatus == EFI_BAD_BUFFER_SIZE)) {\r
*NeedRetry = TRUE;\r
return EFI_DEVICE_ERROR;\r
-\r
- } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
+ } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {\r
*NeedRetry = FALSE;\r
- return EFI_DEVICE_ERROR;\r
+ return ReturnStatus;\r
}\r
+\r
//\r
- // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)\r
+ // go ahead to check HostAdapterStatus and TargetStatus\r
+ // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
//\r
-\r
Status = CheckHostAdapterStatus (HostAdapterStatus);\r
if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
*NeedRetry = TRUE;\r
return EFI_DEVICE_ERROR;\r
-\r
} else if (Status == EFI_DEVICE_ERROR) {\r
//\r
// reset the scsi channel\r
ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
*NeedRetry = TRUE;\r
return EFI_DEVICE_ERROR;\r
-\r
} else if (Status == EFI_DEVICE_ERROR) {\r
*NeedRetry = FALSE;\r
return EFI_DEVICE_ERROR;\r
}\r
\r
- MaxRetry = 3;\r
- for (Index = 0; Index < MaxRetry; Index++) {\r
- Status = ScsiDiskRequestSenseKeys (\r
- ScsiDiskDevice,\r
- NeedRetry,\r
- SenseDataArray,\r
- NumberOfSenseKeys,\r
- FALSE\r
- );\r
- if (!EFI_ERROR (Status)) {\r
- return EFI_SUCCESS;\r
- }\r
+ if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {\r
+ DEBUG ((DEBUG_ERROR, "ScsiDiskWrite10: Check Condition happened!\n"));\r
+ DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);\r
+ if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
+ if (SectorCount <= 1) {\r
+ //\r
+ // Jump out if the operation still fails with one sector transfer length.\r
+ //\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
\r
- if (!*NeedRetry) {\r
+ //\r
+ // Try again with half length if the sense data shows we need to retry.\r
+ //\r
+ SectorCount >>= 1;\r
+ *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+ goto BackOff;\r
+ } else {\r
+ *NeedRetry = FALSE;\r
return EFI_DEVICE_ERROR;\r
}\r
}\r
- //\r
- // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
- // set *NeedRetry = FALSE to avoid the outside caller try again.\r
- //\r
- *NeedRetry = FALSE;\r
- return EFI_DEVICE_ERROR;\r
-}\r
\r
-EFI_STATUS\r
-DetectMediaParsingSenseKeys (\r
- SCSI_DISK_DEV *ScsiDiskDevice,\r
- EFI_SCSI_SENSE_DATA *SenseData,\r
- UINTN NumberOfSenseKeys,\r
- UINTN *Action\r
- )\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Parsing Sense Keys which got from request sense command.\r
- \r
-Arguments:\r
-\r
- ScsiDiskDevice - The pointer of SCSI_DISK_DEV\r
- SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
- NumberOfSenseKeys - The number of sense key \r
- Action - The pointer of action which indicates what is need to do next\r
+ return ReturnStatus;\r
+}\r
\r
-Returns:\r
+/**\r
+ Submit Read(16) command.\r
\r
- EFI_DEVICE_ERROR - Indicates that error occurs\r
- EFI_SUCCESS - Successfully to complete the parsing\r
+ @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
+ @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
+ @param Timeout The time to complete the command\r
+ @param DataBuffer The buffer to fill with the read out data\r
+ @param DataLength The length of buffer\r
+ @param StartLba The start logic block address\r
+ @param SectorCount The number of blocks to read\r
\r
---*/\r
+ @return EFI_STATUS is returned by calling ScsiRead16Command().\r
+**/\r
+EFI_STATUS\r
+ScsiDiskRead16 (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ OUT BOOLEAN *NeedRetry,\r
+ IN UINT64 Timeout,\r
+ OUT UINT8 *DataBuffer,\r
+ IN OUT UINT32 *DataLength,\r
+ IN UINT64 StartLba,\r
+ IN UINT32 SectorCount\r
+ )\r
{\r
- BOOLEAN RetryLater;\r
+ UINT8 SenseDataLength;\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
+ UINT8 HostAdapterStatus;\r
+ UINT8 TargetStatus;\r
+ UINTN Action;\r
\r
//\r
- // Default is to read capacity, unless..\r
+ // Implement a backoff algorithm to resolve some compatibility issues that\r
+ // some SCSI targets or ATAPI devices couldn't correctly response reading/writing\r
+ // big data in a single operation.\r
+ // This algorithm will at first try to execute original request. If the request fails\r
+ // with media error sense data or else, it will reduce the transfer length to half and\r
+ // try again till the operation succeeds or fails with one sector transfer length.\r
//\r
- *Action = ACTION_READ_CAPACITY;\r
-\r
- if (NumberOfSenseKeys == 0) {\r
- *Action = ACTION_NO_ACTION;\r
- return EFI_SUCCESS;\r
+BackOff:\r
+ *NeedRetry = FALSE;\r
+ Action = ACTION_NO_ACTION;\r
+ SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+ ReturnStatus = ScsiRead16Command (\r
+ ScsiDiskDevice->ScsiIo,\r
+ Timeout,\r
+ ScsiDiskDevice->SenseData,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus,\r
+ DataBuffer,\r
+ DataLength,\r
+ StartLba,\r
+ SectorCount\r
+ );\r
+ if ((ReturnStatus == EFI_NOT_READY) || (ReturnStatus == EFI_BAD_BUFFER_SIZE)) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {\r
+ *NeedRetry = FALSE;\r
+ return ReturnStatus;\r
}\r
\r
- if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {\r
+ //\r
+ // go ahead to check HostAdapterStatus and TargetStatus\r
+ // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
+ //\r
+ Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+ if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
//\r
- // No Sense Key returned from last submitted command\r
+ // reset the scsi channel\r
//\r
- *Action = ACTION_NO_ACTION;\r
- return EFI_SUCCESS;\r
- }\r
-\r
- if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) {\r
- ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;\r
- ScsiDiskDevice->BlkIo.Media->LastBlock = 0;\r
- *Action = ACTION_NO_ACTION;\r
- return EFI_SUCCESS;\r
- }\r
-\r
- if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {\r
- ScsiDiskDevice->BlkIo.Media->MediaId++;\r
- return EFI_SUCCESS;\r
- }\r
-\r
- if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {\r
- ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;\r
- ScsiDiskDevice->BlkIo.Media->LastBlock = 0;\r
+ ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
+ *NeedRetry = FALSE;\r
return EFI_DEVICE_ERROR;\r
}\r
\r
- if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {\r
+ Status = CheckTargetStatus (TargetStatus);\r
+ if (Status == EFI_NOT_READY) {\r
+ //\r
+ // reset the scsi device\r
+ //\r
+ ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
+ *NeedRetry = FALSE;\r
return EFI_DEVICE_ERROR;\r
}\r
\r
- if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {\r
- if (RetryLater) {\r
- *Action = ACTION_RETRY_COMMAND_LATER;\r
- return EFI_SUCCESS;\r
- }\r
+ if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {\r
+ DEBUG ((DEBUG_ERROR, "ScsiDiskRead16: Check Condition happened!\n"));\r
+ DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);\r
+ if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
+ if (SectorCount <= 1) {\r
+ //\r
+ // Jump out if the operation still fails with one sector transfer length.\r
+ //\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
\r
- return EFI_DEVICE_ERROR;\r
+ //\r
+ // Try again with half length if the sense data shows we need to retry.\r
+ //\r
+ SectorCount >>= 1;\r
+ *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+ goto BackOff;\r
+ } else {\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
}\r
\r
- return EFI_SUCCESS;\r
+ return ReturnStatus;\r
}\r
\r
+/**\r
+ Submit Write(16) Command.\r
+\r
+ @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
+ @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
+ @param Timeout The time to complete the command\r
+ @param DataBuffer The buffer to fill with the read out data\r
+ @param DataLength The length of buffer\r
+ @param StartLba The start logic block address\r
+ @param SectorCount The number of blocks to write\r
+\r
+ @return EFI_STATUS is returned by calling ScsiWrite16Command().\r
+\r
+**/\r
EFI_STATUS\r
-ScsiDiskReadCapacity (\r
- SCSI_DISK_DEV *ScsiDiskDevice,\r
- BOOLEAN *NeedRetry,\r
- EFI_SCSI_SENSE_DATA **SenseDataArray,\r
- UINTN *NumberOfSenseKeys\r
+ScsiDiskWrite16 (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ OUT BOOLEAN *NeedRetry,\r
+ IN UINT64 Timeout,\r
+ IN UINT8 *DataBuffer,\r
+ IN OUT UINT32 *DataLength,\r
+ IN UINT64 StartLba,\r
+ IN UINT32 SectorCount\r
)\r
-/*++\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_STATUS ReturnStatus;\r
+ UINT8 SenseDataLength;\r
+ UINT8 HostAdapterStatus;\r
+ UINT8 TargetStatus;\r
+ UINTN Action;\r
+\r
+ //\r
+ // Implement a backoff algorithm to resolve some compatibility issues that\r
+ // some SCSI targets or ATAPI devices couldn't correctly response reading/writing\r
+ // big data in a single operation.\r
+ // This algorithm will at first try to execute original request. If the request fails\r
+ // with media error sense data or else, it will reduce the transfer length to half and\r
+ // try again till the operation succeeds or fails with one sector transfer length.\r
+ //\r
+BackOff:\r
+ *NeedRetry = FALSE;\r
+ Action = ACTION_NO_ACTION;\r
+ SenseDataLength = (UINT8)(ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
+ ReturnStatus = ScsiWrite16Command (\r
+ ScsiDiskDevice->ScsiIo,\r
+ Timeout,\r
+ ScsiDiskDevice->SenseData,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus,\r
+ DataBuffer,\r
+ DataLength,\r
+ StartLba,\r
+ SectorCount\r
+ );\r
+ if ((ReturnStatus == EFI_NOT_READY) || (ReturnStatus == EFI_BAD_BUFFER_SIZE)) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {\r
+ *NeedRetry = FALSE;\r
+ return ReturnStatus;\r
+ }\r
+\r
+ //\r
+ // go ahead to check HostAdapterStatus and TargetStatus\r
+ // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
+ //\r
+ Status = CheckHostAdapterStatus (HostAdapterStatus);\r
+ if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
+ //\r
+ // reset the scsi channel\r
+ //\r
+ ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
\r
-Routine Description:\r
+ Status = CheckTargetStatus (TargetStatus);\r
+ if (Status == EFI_NOT_READY) {\r
+ //\r
+ // reset the scsi device\r
+ //\r
+ ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
\r
- Send read capacity command to device and get the device parameter\r
+ if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {\r
+ DEBUG ((DEBUG_ERROR, "ScsiDiskWrite16: Check Condition happened!\n"));\r
+ DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);\r
+ if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+ *NeedRetry = TRUE;\r
+ return EFI_DEVICE_ERROR;\r
+ } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
+ if (SectorCount <= 1) {\r
+ //\r
+ // Jump out if the operation still fails with one sector transfer length.\r
+ //\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
\r
-Arguments:\r
+ //\r
+ // Try again with half length if the sense data shows we need to retry.\r
+ //\r
+ SectorCount >>= 1;\r
+ *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+ goto BackOff;\r
+ } else {\r
+ *NeedRetry = FALSE;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+ }\r
\r
- ScsiDiskDevice - The pointer of SCSI_DISK_DEV\r
- NeedRetry - The pointer of flag indicates if need a retry\r
- SenseDataArray - The pointer of an array of sense data\r
- NumberOfSenseKeys - The number of sense key\r
+ return ReturnStatus;\r
+}\r
\r
-Returns:\r
+/**\r
+ Internal helper notify function in which determine whether retry of a SCSI\r
+ Read/Write command is needed and signal the event passed from Block I/O(2) if\r
+ the SCSI I/O operation completes.\r
\r
- EFI_DEVICE_ERROR - Indicates that error occurs\r
- EFI_SUCCESS - Successfully to read capacity\r
+ @param Event The instance of EFI_EVENT.\r
+ @param Context The parameter passed in.\r
\r
---*/\r
+**/\r
+VOID\r
+EFIAPI\r
+ScsiDiskNotify (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
{\r
- EFI_SCSI_DISK_CAPACITY_DATA CapacityData;\r
- UINT32 DataLength;\r
- UINT8 HostAdapterStatus;\r
- UINT8 TargetStatus;\r
- EFI_STATUS CommandStatus;\r
- EFI_STATUS Status;\r
- UINT8 Index;\r
- UINT8 MaxRetry;\r
- UINT8 SenseDataLength;\r
-\r
- SenseDataLength = 0;\r
- ZeroMem (&CapacityData, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
- DataLength = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);\r
+ EFI_STATUS Status;\r
+ SCSI_ASYNC_RW_REQUEST *Request;\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
+ EFI_BLOCK_IO2_TOKEN *Token;\r
+ UINTN Action;\r
+ UINT32 OldDataLength;\r
+ UINT32 OldSectorCount;\r
+ UINT8 MaxRetry;\r
+\r
+ gBS->CloseEvent (Event);\r
+\r
+ Request = (SCSI_ASYNC_RW_REQUEST *)Context;\r
+ ScsiDiskDevice = Request->ScsiDiskDevice;\r
+ Token = Request->BlkIo2Req->Token;\r
+ OldDataLength = Request->DataLength;\r
+ OldSectorCount = Request->SectorCount;\r
+ MaxRetry = 2;\r
\r
- *NumberOfSenseKeys = 0;\r
- *NeedRetry = FALSE;\r
//\r
- // submit Read Capacity Command. in this call,not request sense data\r
+ // If previous sub-tasks already fails, no need to process this sub-task.\r
//\r
- CommandStatus = ScsiReadCapacityCommand (\r
- ScsiDiskDevice->ScsiIo,\r
- EfiScsiStallSeconds (1),\r
- NULL,\r
- &SenseDataLength,\r
- &HostAdapterStatus,\r
- &TargetStatus,\r
- (VOID *) &CapacityData,\r
- &DataLength,\r
- FALSE\r
- );\r
+ if (Token->TransactionStatus != EFI_SUCCESS) {\r
+ goto Exit;\r
+ }\r
+\r
//\r
- // no need to check HostAdapterStatus and TargetStatus\r
- //\r
- if (CommandStatus == EFI_SUCCESS) {\r
- GetMediaInfo (ScsiDiskDevice, &CapacityData);\r
- return EFI_SUCCESS;\r
- \r
- } else if (CommandStatus == EFI_NOT_READY) {\r
- *NeedRetry = TRUE;\r
- return EFI_DEVICE_ERROR;\r
- \r
- } else if ((CommandStatus == EFI_INVALID_PARAMETER) || (CommandStatus == EFI_UNSUPPORTED)) {\r
- *NeedRetry = FALSE;\r
- return EFI_DEVICE_ERROR;\r
- }\r
- //\r
- // go ahead to check HostAdapterStatus and TargetStatus\r
- // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
- //\r
- \r
- Status = CheckHostAdapterStatus (HostAdapterStatus);\r
- if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
- *NeedRetry = TRUE;\r
- return EFI_DEVICE_ERROR;\r
- \r
- } else if (Status == EFI_DEVICE_ERROR) {\r
+ // Check HostAdapterStatus and TargetStatus\r
+ // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
+ //\r
+ Status = CheckHostAdapterStatus (Request->HostAdapterStatus);\r
+ if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
+ if (++Request->TimesRetry > MaxRetry) {\r
+ Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
+ } else {\r
+ goto Retry;\r
+ }\r
+ } else if (Status == EFI_DEVICE_ERROR) {\r
//\r
// reset the scsi channel\r
//\r
ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
- *NeedRetry = FALSE;\r
- return EFI_DEVICE_ERROR;\r
+ Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
}\r
\r
- Status = CheckTargetStatus (TargetStatus);\r
+ Status = CheckTargetStatus (Request->TargetStatus);\r
if (Status == EFI_NOT_READY) {\r
//\r
// reset the scsi device\r
//\r
ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
- *NeedRetry = TRUE;\r
- return EFI_DEVICE_ERROR;\r
-\r
+ if (++Request->TimesRetry > MaxRetry) {\r
+ Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
+ } else {\r
+ goto Retry;\r
+ }\r
} else if (Status == EFI_DEVICE_ERROR) {\r
- *NeedRetry = FALSE;\r
- return EFI_DEVICE_ERROR;\r
+ Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
}\r
- \r
- //\r
- // if goes here, meant SubmitReadCapacityCommand() failed.\r
- // if ScsiDiskRequestSenseKeys() succeeds at last,\r
- // better retry SubmitReadCapacityCommand(). (by setting *NeedRetry = TRUE)\r
- //\r
- MaxRetry = 3;\r
- for (Index = 0; Index < MaxRetry; Index++) {\r
\r
- Status = ScsiDiskRequestSenseKeys (\r
- ScsiDiskDevice,\r
- NeedRetry,\r
- SenseDataArray,\r
- NumberOfSenseKeys,\r
- TRUE\r
- );\r
- if (!EFI_ERROR (Status)) {\r
- *NeedRetry = TRUE;\r
- return EFI_DEVICE_ERROR;\r
- }\r
+ if (Request->TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) {\r
+ DEBUG ((DEBUG_ERROR, "ScsiDiskNotify: Check Condition happened!\n"));\r
+\r
+ DetectMediaParsingSenseKeys (\r
+ ScsiDiskDevice,\r
+ Request->SenseData,\r
+ Request->SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA),\r
+ &Action\r
+ );\r
+ if (Action == ACTION_RETRY_COMMAND_LATER) {\r
+ if (++Request->TimesRetry > MaxRetry) {\r
+ Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
+ } else {\r
+ goto Retry;\r
+ }\r
+ } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
+ if (Request->SectorCount <= 1) {\r
+ //\r
+ // Jump out if the operation still fails with one sector transfer\r
+ // length.\r
+ //\r
+ Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
+ }\r
\r
- if (!*NeedRetry) {\r
- return EFI_DEVICE_ERROR;\r
+ //\r
+ // Try again with two half length request if the sense data shows we need\r
+ // to retry.\r
+ //\r
+ Request->SectorCount >>= 1;\r
+ Request->DataLength = Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
+ Request->TimesRetry = 0;\r
+\r
+ goto Retry;\r
+ } else {\r
+ Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
}\r
}\r
+\r
//\r
- // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
- // set *NeedRetry = FALSE to avoid the outside caller try again.\r
+ // This sub-task succeeds, no need to retry.\r
//\r
- *NeedRetry = FALSE;\r
- return EFI_DEVICE_ERROR;\r
-}\r
+ goto Exit;\r
\r
-EFI_STATUS\r
-CheckHostAdapterStatus (\r
- UINT8 HostAdapterStatus\r
- )\r
-/*++\r
+Retry:\r
+ if (Request->InBuffer != NULL) {\r
+ //\r
+ // SCSI read command\r
+ //\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ Status = ScsiDiskAsyncRead10 (\r
+ ScsiDiskDevice,\r
+ Request->Timeout,\r
+ Request->TimesRetry,\r
+ Request->InBuffer,\r
+ Request->DataLength,\r
+ (UINT32)Request->StartLba,\r
+ Request->SectorCount,\r
+ Request->BlkIo2Req,\r
+ Token\r
+ );\r
+ } else {\r
+ Status = ScsiDiskAsyncRead16 (\r
+ ScsiDiskDevice,\r
+ Request->Timeout,\r
+ Request->TimesRetry,\r
+ Request->InBuffer,\r
+ Request->DataLength,\r
+ Request->StartLba,\r
+ Request->SectorCount,\r
+ Request->BlkIo2Req,\r
+ Token\r
+ );\r
+ }\r
\r
-Routine Description:\r
+ if (EFI_ERROR (Status)) {\r
+ Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
+ } else if (OldSectorCount != Request->SectorCount) {\r
+ //\r
+ // Original sub-task will be split into two new sub-tasks with smaller\r
+ // DataLength\r
+ //\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ Status = ScsiDiskAsyncRead10 (\r
+ ScsiDiskDevice,\r
+ Request->Timeout,\r
+ 0,\r
+ Request->InBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,\r
+ OldDataLength - Request->DataLength,\r
+ (UINT32)Request->StartLba + Request->SectorCount,\r
+ OldSectorCount - Request->SectorCount,\r
+ Request->BlkIo2Req,\r
+ Token\r
+ );\r
+ } else {\r
+ Status = ScsiDiskAsyncRead16 (\r
+ ScsiDiskDevice,\r
+ Request->Timeout,\r
+ 0,\r
+ Request->InBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,\r
+ OldDataLength - Request->DataLength,\r
+ Request->StartLba + Request->SectorCount,\r
+ OldSectorCount - Request->SectorCount,\r
+ Request->BlkIo2Req,\r
+ Token\r
+ );\r
+ }\r
\r
- Check the HostAdapter status\r
- \r
-Arguments:\r
+ if (EFI_ERROR (Status)) {\r
+ Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
+ }\r
+ }\r
+ } else {\r
+ //\r
+ // SCSI write command\r
+ //\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ Status = ScsiDiskAsyncWrite10 (\r
+ ScsiDiskDevice,\r
+ Request->Timeout,\r
+ Request->TimesRetry,\r
+ Request->OutBuffer,\r
+ Request->DataLength,\r
+ (UINT32)Request->StartLba,\r
+ Request->SectorCount,\r
+ Request->BlkIo2Req,\r
+ Token\r
+ );\r
+ } else {\r
+ Status = ScsiDiskAsyncWrite16 (\r
+ ScsiDiskDevice,\r
+ Request->Timeout,\r
+ Request->TimesRetry,\r
+ Request->OutBuffer,\r
+ Request->DataLength,\r
+ Request->StartLba,\r
+ Request->SectorCount,\r
+ Request->BlkIo2Req,\r
+ Token\r
+ );\r
+ }\r
\r
- HostAdapterStatus - Host Adapter status\r
+ if (EFI_ERROR (Status)) {\r
+ Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
+ } else if (OldSectorCount != Request->SectorCount) {\r
+ //\r
+ // Original sub-task will be split into two new sub-tasks with smaller\r
+ // DataLength\r
+ //\r
+ if (!ScsiDiskDevice->Cdb16Byte) {\r
+ Status = ScsiDiskAsyncWrite10 (\r
+ ScsiDiskDevice,\r
+ Request->Timeout,\r
+ 0,\r
+ Request->OutBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,\r
+ OldDataLength - Request->DataLength,\r
+ (UINT32)Request->StartLba + Request->SectorCount,\r
+ OldSectorCount - Request->SectorCount,\r
+ Request->BlkIo2Req,\r
+ Token\r
+ );\r
+ } else {\r
+ Status = ScsiDiskAsyncWrite16 (\r
+ ScsiDiskDevice,\r
+ Request->Timeout,\r
+ 0,\r
+ Request->OutBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,\r
+ OldDataLength - Request->DataLength,\r
+ Request->StartLba + Request->SectorCount,\r
+ OldSectorCount - Request->SectorCount,\r
+ Request->BlkIo2Req,\r
+ Token\r
+ );\r
+ }\r
\r
-Returns:\r
+ if (EFI_ERROR (Status)) {\r
+ Token->TransactionStatus = EFI_DEVICE_ERROR;\r
+ goto Exit;\r
+ }\r
+ }\r
+ }\r
\r
- EFI_SUCCESS \r
- EFI_TIMEOUT \r
- EFI_NOT_READY \r
- EFI_DEVICE_ERROR \r
+Exit:\r
+ RemoveEntryList (&Request->Link);\r
+ if ((IsListEmpty (&Request->BlkIo2Req->ScsiRWQueue)) &&\r
+ (Request->BlkIo2Req->LastScsiRW))\r
+ {\r
+ //\r
+ // The last SCSI R/W command of a BlockIo2 request completes\r
+ //\r
+ RemoveEntryList (&Request->BlkIo2Req->Link);\r
+ FreePool (Request->BlkIo2Req); // Should be freed only once\r
+ gBS->SignalEvent (Token->Event);\r
+ }\r
\r
---*/\r
-{\r
- switch (HostAdapterStatus) {\r
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK:\r
- return EFI_SUCCESS;\r
+ FreePool (Request->SenseData);\r
+ FreePool (Request);\r
+}\r
\r
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT:\r
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT:\r
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND:\r
- return EFI_TIMEOUT;\r
+/**\r
+ Submit Async Read(10) command.\r
+\r
+ @param ScsiDiskDevice The pointer of ScsiDiskDevice.\r
+ @param Timeout The time to complete the command.\r
+ @param TimesRetry The number of times the command has been retried.\r
+ @param DataBuffer The buffer to fill with the read out data.\r
+ @param DataLength The length of buffer.\r
+ @param StartLba The start logic block address.\r
+ @param SectorCount The number of blocks to read.\r
+ @param BlkIo2Req The upstream BlockIo2 request.\r
+ @param Token The pointer to the token associated with the\r
+ non-blocking read request.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
+ lack of resources.\r
+ @return others Status returned by calling\r
+ ScsiRead10CommandEx().\r
\r
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT:\r
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR:\r
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED:\r
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN:\r
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET:\r
- return EFI_NOT_READY;\r
+**/\r
+EFI_STATUS\r
+ScsiDiskAsyncRead10 (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN UINT64 Timeout,\r
+ IN UINT8 TimesRetry,\r
+ OUT UINT8 *DataBuffer,\r
+ IN UINT32 DataLength,\r
+ IN UINT32 StartLba,\r
+ IN UINT32 SectorCount,\r
+ IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req,\r
+ IN EFI_BLOCK_IO2_TOKEN *Token\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ SCSI_ASYNC_RW_REQUEST *Request;\r
+ EFI_EVENT AsyncIoEvent;\r
+ EFI_TPL OldTpl;\r
\r
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE:\r
- case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR:\r
- return EFI_DEVICE_ERROR;\r
+ AsyncIoEvent = NULL;\r
\r
- default:\r
- return EFI_SUCCESS;\r
+ Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
+ if (Request == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
}\r
-}\r
\r
-EFI_STATUS\r
-CheckTargetStatus (\r
- UINT8 TargetStatus\r
- )\r
-/*++\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
\r
-Routine Description:\r
+ Request->SenseDataLength = (UINT8)(6 * sizeof (EFI_SCSI_SENSE_DATA));\r
+ Request->SenseData = AllocateZeroPool (Request->SenseDataLength);\r
+ if (Request->SenseData == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ErrorExit;\r
+ }\r
\r
- Check the target status\r
- \r
-Arguments:\r
+ Request->ScsiDiskDevice = ScsiDiskDevice;\r
+ Request->Timeout = Timeout;\r
+ Request->TimesRetry = TimesRetry;\r
+ Request->InBuffer = DataBuffer;\r
+ Request->DataLength = DataLength;\r
+ Request->StartLba = StartLba;\r
+ Request->SectorCount = SectorCount;\r
+ Request->BlkIo2Req = BlkIo2Req;\r
\r
- TargetStatus - Target status\r
+ //\r
+ // Create Event\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ ScsiDiskNotify,\r
+ Request,\r
+ &AsyncIoEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
+ }\r
\r
-Returns:\r
+ Status = ScsiRead10CommandEx (\r
+ ScsiDiskDevice->ScsiIo,\r
+ Request->Timeout,\r
+ Request->SenseData,\r
+ &Request->SenseDataLength,\r
+ &Request->HostAdapterStatus,\r
+ &Request->TargetStatus,\r
+ Request->InBuffer,\r
+ &Request->DataLength,\r
+ (UINT32)Request->StartLba,\r
+ Request->SectorCount,\r
+ AsyncIoEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
+ }\r
\r
- EFI_NOT_READY \r
- EFI_DEVICE_ERROR \r
- EFI_SUCCESS\r
+ return EFI_SUCCESS;\r
\r
---*/\r
-{\r
- switch (TargetStatus) {\r
- case EFI_EXT_SCSI_STATUS_TARGET_GOOD:\r
- case EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION:\r
- case EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET:\r
- return EFI_SUCCESS;\r
+ErrorExit:\r
+ if (AsyncIoEvent != NULL) {\r
+ gBS->CloseEvent (AsyncIoEvent);\r
+ }\r
\r
- case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE:\r
- case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET:\r
- case EFI_EXT_SCSI_STATUS_TARGET_BUSY:\r
- case EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL:\r
- return EFI_NOT_READY;\r
+ if (Request != NULL) {\r
+ if (Request->SenseData != NULL) {\r
+ FreePool (Request->SenseData);\r
+ }\r
\r
- case EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT:\r
- return EFI_DEVICE_ERROR;\r
- break;\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ RemoveEntryList (&Request->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
\r
- default:\r
- return EFI_SUCCESS;\r
+ FreePool (Request);\r
}\r
+\r
+ return Status;\r
}\r
\r
+/**\r
+ Submit Async Write(10) command.\r
+\r
+ @param ScsiDiskDevice The pointer of ScsiDiskDevice.\r
+ @param Timeout The time to complete the command.\r
+ @param TimesRetry The number of times the command has been retried.\r
+ @param DataBuffer The buffer contains the data to write.\r
+ @param DataLength The length of buffer.\r
+ @param StartLba The start logic block address.\r
+ @param SectorCount The number of blocks to write.\r
+ @param BlkIo2Req The upstream BlockIo2 request.\r
+ @param Token The pointer to the token associated with the\r
+ non-blocking read request.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
+ lack of resources.\r
+ @return others Status returned by calling\r
+ ScsiWrite10CommandEx().\r
+\r
+**/\r
EFI_STATUS\r
-ScsiDiskRequestSenseKeys (\r
- SCSI_DISK_DEV *ScsiDiskDevice,\r
- BOOLEAN *NeedRetry,\r
- EFI_SCSI_SENSE_DATA **SenseDataArray,\r
- UINTN *NumberOfSenseKeys,\r
- BOOLEAN AskResetIfError\r
+ScsiDiskAsyncWrite10 (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN UINT64 Timeout,\r
+ IN UINT8 TimesRetry,\r
+ IN UINT8 *DataBuffer,\r
+ IN UINT32 DataLength,\r
+ IN UINT32 StartLba,\r
+ IN UINT32 SectorCount,\r
+ IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req,\r
+ IN EFI_BLOCK_IO2_TOKEN *Token\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Retrieve all sense keys from the device.\r
- When encountering error during the process,\r
- if retrieve sense keys before error encounterred,\r
- return the sense keys with return status set to EFI_SUCCESS,\r
- and NeedRetry set to FALSE; otherwize, return the proper return\r
- status.\r
-\r
-Arguments:\r
-\r
- ScsiDiskDevice - The pointer of SCSI_DISK_DEV\r
- NeedRetry - The pointer of flag indicates if need a retry\r
- SenseDataArray - The pointer of an array of sense data\r
- NumberOfSenseKeys - The number of sense key\r
- AskResetIfError - The flag indicates if need reset when error occurs\r
- \r
-Returns:\r
-\r
- EFI_DEVICE_ERROR - Indicates that error occurs\r
- EFI_SUCCESS - Successfully to request sense key\r
-\r
---*/\r
{\r
- EFI_SCSI_SENSE_DATA *PtrSenseData;\r
- UINT8 SenseDataLength;\r
- BOOLEAN SenseReq;\r
- EFI_STATUS Status;\r
- EFI_STATUS FallStatus;\r
- UINT8 HostAdapterStatus;\r
- UINT8 TargetStatus;\r
+ EFI_STATUS Status;\r
+ SCSI_ASYNC_RW_REQUEST *Request;\r
+ EFI_EVENT AsyncIoEvent;\r
+ EFI_TPL OldTpl;\r
\r
- FallStatus = EFI_SUCCESS;\r
- SenseDataLength = sizeof (EFI_SCSI_SENSE_DATA);\r
+ AsyncIoEvent = NULL;\r
\r
- ZeroMem (\r
- ScsiDiskDevice->SenseData,\r
- sizeof (EFI_SCSI_SENSE_DATA) * (ScsiDiskDevice->SenseDataNumber)\r
- );\r
+ Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
+ if (Request == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
\r
- *NumberOfSenseKeys = 0;\r
- *SenseDataArray = ScsiDiskDevice->SenseData;\r
- PtrSenseData = ScsiDiskDevice->SenseData;\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
\r
- for (SenseReq = TRUE; SenseReq;) {\r
- Status = ScsiRequestSenseCommand (\r
- ScsiDiskDevice->ScsiIo,\r
- EfiScsiStallSeconds (2),\r
- PtrSenseData,\r
- &SenseDataLength,\r
- &HostAdapterStatus,\r
- &TargetStatus\r
- );\r
- if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
- FallStatus = EFI_SUCCESS;\r
- \r
- } else if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
- *NeedRetry = TRUE;\r
- FallStatus = EFI_DEVICE_ERROR;\r
- \r
- } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
- *NeedRetry = FALSE;\r
- FallStatus = EFI_DEVICE_ERROR;\r
- \r
- } else if (Status == EFI_DEVICE_ERROR) {\r
- if (AskResetIfError) {\r
- ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
- }\r
- \r
- FallStatus = EFI_DEVICE_ERROR;\r
- }\r
+ Request->SenseDataLength = (UINT8)(6 * sizeof (EFI_SCSI_SENSE_DATA));\r
+ Request->SenseData = AllocateZeroPool (Request->SenseDataLength);\r
+ if (Request->SenseData == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ErrorExit;\r
+ }\r
\r
- if (EFI_ERROR (FallStatus)) {\r
- if (*NumberOfSenseKeys != 0) {\r
- *NeedRetry = FALSE;\r
- return EFI_SUCCESS;\r
- } else {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- }\r
+ Request->ScsiDiskDevice = ScsiDiskDevice;\r
+ Request->Timeout = Timeout;\r
+ Request->TimesRetry = TimesRetry;\r
+ Request->OutBuffer = DataBuffer;\r
+ Request->DataLength = DataLength;\r
+ Request->StartLba = StartLba;\r
+ Request->SectorCount = SectorCount;\r
+ Request->BlkIo2Req = BlkIo2Req;\r
\r
- (*NumberOfSenseKeys) += 1;\r
+ //\r
+ // Create Event\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ ScsiDiskNotify,\r
+ Request,\r
+ &AsyncIoEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
+ }\r
\r
- //\r
- // no more sense key or number of sense keys exceeds predefined,\r
- // skip the loop.\r
- //\r
- if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) || \r
- (*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber)) {\r
- SenseReq = FALSE;\r
- }\r
- PtrSenseData += 1;\r
+ Status = ScsiWrite10CommandEx (\r
+ ScsiDiskDevice->ScsiIo,\r
+ Request->Timeout,\r
+ Request->SenseData,\r
+ &Request->SenseDataLength,\r
+ &Request->HostAdapterStatus,\r
+ &Request->TargetStatus,\r
+ Request->OutBuffer,\r
+ &Request->DataLength,\r
+ (UINT32)Request->StartLba,\r
+ Request->SectorCount,\r
+ AsyncIoEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
}\r
- return EFI_SUCCESS;\r
-}\r
\r
-VOID\r
-GetMediaInfo (\r
- SCSI_DISK_DEV *ScsiDiskDevice,\r
- EFI_SCSI_DISK_CAPACITY_DATA *Capacity\r
- )\r
-/*++\r
+ return EFI_SUCCESS;\r
\r
-Routine Description:\r
+ErrorExit:\r
+ if (AsyncIoEvent != NULL) {\r
+ gBS->CloseEvent (AsyncIoEvent);\r
+ }\r
\r
- Get information from media read capacity command\r
+ if (Request != NULL) {\r
+ if (Request->SenseData != NULL) {\r
+ FreePool (Request->SenseData);\r
+ }\r
\r
-Arguments:\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ RemoveEntryList (&Request->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
\r
- ScsiDiskDevice - The pointer of SCSI_DISK_DEV\r
- Capacity - The pointer of EFI_SCSI_DISK_CAPACITY_DATA\r
+ FreePool (Request);\r
+ }\r
\r
-Returns:\r
+ return Status;\r
+}\r
\r
- NONE\r
+/**\r
+ Submit Async Read(16) command.\r
+\r
+ @param ScsiDiskDevice The pointer of ScsiDiskDevice.\r
+ @param Timeout The time to complete the command.\r
+ @param TimesRetry The number of times the command has been retried.\r
+ @param DataBuffer The buffer to fill with the read out data.\r
+ @param DataLength The length of buffer.\r
+ @param StartLba The start logic block address.\r
+ @param SectorCount The number of blocks to read.\r
+ @param BlkIo2Req The upstream BlockIo2 request.\r
+ @param Token The pointer to the token associated with the\r
+ non-blocking read request.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
+ lack of resources.\r
+ @return others Status returned by calling\r
+ ScsiRead16CommandEx().\r
\r
---*/\r
+**/\r
+EFI_STATUS\r
+ScsiDiskAsyncRead16 (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN UINT64 Timeout,\r
+ IN UINT8 TimesRetry,\r
+ OUT UINT8 *DataBuffer,\r
+ IN UINT32 DataLength,\r
+ IN UINT64 StartLba,\r
+ IN UINT32 SectorCount,\r
+ IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req,\r
+ IN EFI_BLOCK_IO2_TOKEN *Token\r
+ )\r
{\r
- ScsiDiskDevice->BlkIo.Media->LastBlock = (Capacity->LastLba3 << 24) |\r
- (Capacity->LastLba2 << 16) |\r
- (Capacity->LastLba1 << 8) |\r
- Capacity->LastLba0;\r
+ EFI_STATUS Status;\r
+ SCSI_ASYNC_RW_REQUEST *Request;\r
+ EFI_EVENT AsyncIoEvent;\r
+ EFI_TPL OldTpl;\r
\r
- ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;\r
- ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity->BlockSize3 << 24) |\r
- (Capacity->BlockSize2 << 16) | \r
- (Capacity->BlockSize1 << 8) |\r
- Capacity->BlockSize0;\r
- if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {\r
- ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;\r
- }\r
+ AsyncIoEvent = NULL;\r
\r
- if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_CDROM) {\r
- ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;\r
+ Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
+ if (Request == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
}\r
-}\r
\r
-VOID\r
-ParseInquiryData (\r
- SCSI_DISK_DEV *ScsiDiskDevice\r
- )\r
-/*++\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
\r
-Routine Description:\r
+ Request->SenseDataLength = (UINT8)(6 * sizeof (EFI_SCSI_SENSE_DATA));\r
+ Request->SenseData = AllocateZeroPool (Request->SenseDataLength);\r
+ if (Request->SenseData == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ErrorExit;\r
+ }\r
\r
- Parse Inquiry data\r
+ Request->ScsiDiskDevice = ScsiDiskDevice;\r
+ Request->Timeout = Timeout;\r
+ Request->TimesRetry = TimesRetry;\r
+ Request->InBuffer = DataBuffer;\r
+ Request->DataLength = DataLength;\r
+ Request->StartLba = StartLba;\r
+ Request->SectorCount = SectorCount;\r
+ Request->BlkIo2Req = BlkIo2Req;\r
\r
-Arguments:\r
+ //\r
+ // Create Event\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ ScsiDiskNotify,\r
+ Request,\r
+ &AsyncIoEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
+ }\r
\r
- ScsiDiskDevice - The pointer of SCSI_DISK_DEV\r
+ Status = ScsiRead16CommandEx (\r
+ ScsiDiskDevice->ScsiIo,\r
+ Request->Timeout,\r
+ Request->SenseData,\r
+ &Request->SenseDataLength,\r
+ &Request->HostAdapterStatus,\r
+ &Request->TargetStatus,\r
+ Request->InBuffer,\r
+ &Request->DataLength,\r
+ Request->StartLba,\r
+ Request->SectorCount,\r
+ AsyncIoEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
+ }\r
\r
-Returns:\r
+ return EFI_SUCCESS;\r
\r
- NONE\r
+ErrorExit:\r
+ if (AsyncIoEvent != NULL) {\r
+ gBS->CloseEvent (AsyncIoEvent);\r
+ }\r
\r
---*/\r
-{\r
- ScsiDiskDevice->FixedDevice = (BOOLEAN) (ScsiDiskDevice->InquiryData.RMB ? 0 : 1);\r
- ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN) (!ScsiDiskDevice->FixedDevice);\r
-}\r
+ if (Request != NULL) {\r
+ if (Request->SenseData != NULL) {\r
+ FreePool (Request->SenseData);\r
+ }\r
\r
-EFI_STATUS\r
-EFIAPI\r
-ScsiDiskReadSectors (\r
- SCSI_DISK_DEV *ScsiDiskDevice,\r
- VOID *Buffer,\r
- EFI_LBA Lba,\r
- UINTN NumberOfBlocks\r
- )\r
-/*++\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ RemoveEntryList (&Request->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
\r
-Routine Description:\r
+ FreePool (Request);\r
+ }\r
\r
- Read sector from SCSI Disk\r
+ return Status;\r
+}\r
\r
-Arguments:\r
+/**\r
+ Submit Async Write(16) command.\r
+\r
+ @param ScsiDiskDevice The pointer of ScsiDiskDevice.\r
+ @param Timeout The time to complete the command.\r
+ @param TimesRetry The number of times the command has been retried.\r
+ @param DataBuffer The buffer contains the data to write.\r
+ @param DataLength The length of buffer.\r
+ @param StartLba The start logic block address.\r
+ @param SectorCount The number of blocks to write.\r
+ @param BlkIo2Req The upstream BlockIo2 request.\r
+ @param Token The pointer to the token associated with the\r
+ non-blocking read request.\r
+\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
+ lack of resources.\r
+ @return others Status returned by calling\r
+ ScsiWrite16CommandEx().\r
\r
- ScsiDiskDevice - The poiniter of SCSI_DISK_DEV\r
- Buffer - The buffer to fill in the read out data\r
- Lba - Logic block address\r
- NumberOfBlocks - The number of blocks to read\r
+**/\r
+EFI_STATUS\r
+ScsiDiskAsyncWrite16 (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN UINT64 Timeout,\r
+ IN UINT8 TimesRetry,\r
+ IN UINT8 *DataBuffer,\r
+ IN UINT32 DataLength,\r
+ IN UINT64 StartLba,\r
+ IN UINT32 SectorCount,\r
+ IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req,\r
+ IN EFI_BLOCK_IO2_TOKEN *Token\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ SCSI_ASYNC_RW_REQUEST *Request;\r
+ EFI_EVENT AsyncIoEvent;\r
+ EFI_TPL OldTpl;\r
\r
-Returns:\r
+ AsyncIoEvent = NULL;\r
\r
- EFI_DEVICE_ERROR\r
- EFI_SUCCESS\r
+ Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
+ if (Request == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
\r
---*/\r
-{\r
- UINTN BlocksRemaining;\r
- UINT32 Lba32;\r
- UINT8 *PtrBuffer;\r
- UINT32 BlockSize;\r
- UINT32 ByteCount;\r
- UINT32 MaxBlock;\r
- UINT32 SectorCount;\r
- UINT64 Timeout;\r
- EFI_STATUS Status;\r
- UINT8 Index;\r
- UINT8 MaxRetry;\r
- BOOLEAN NeedRetry;\r
- EFI_SCSI_SENSE_DATA *SenseData;\r
- UINTN NumberOfSenseKeys;\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
\r
- SenseData = NULL;\r
- NumberOfSenseKeys = 0;\r
+ Request->SenseDataLength = (UINT8)(6 * sizeof (EFI_SCSI_SENSE_DATA));\r
+ Request->SenseData = AllocateZeroPool (Request->SenseDataLength);\r
+ if (Request->SenseData == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ErrorExit;\r
+ }\r
\r
- Status = EFI_SUCCESS;\r
+ Request->ScsiDiskDevice = ScsiDiskDevice;\r
+ Request->Timeout = Timeout;\r
+ Request->TimesRetry = TimesRetry;\r
+ Request->OutBuffer = DataBuffer;\r
+ Request->DataLength = DataLength;\r
+ Request->StartLba = StartLba;\r
+ Request->SectorCount = SectorCount;\r
+ Request->BlkIo2Req = BlkIo2Req;\r
\r
- BlocksRemaining = NumberOfBlocks;\r
- BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
//\r
- // limit the data bytes that can be transferred by one Read(10) Command\r
+ // Create Event\r
//\r
- MaxBlock = 65536;\r
-\r
- PtrBuffer = Buffer;\r
- Lba32 = (UINT32) Lba;\r
-\r
- while (BlocksRemaining > 0) {\r
-\r
- if (BlocksRemaining <= MaxBlock) {\r
-\r
- SectorCount = (UINT16) BlocksRemaining;\r
- } else {\r
-\r
- SectorCount = MaxBlock;\r
- }\r
-\r
- ByteCount = SectorCount * BlockSize;\r
- Timeout = EfiScsiStallSeconds (2);\r
-\r
- MaxRetry = 2;\r
- for (Index = 0; Index < MaxRetry; Index++) {\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_NOTIFY,\r
+ ScsiDiskNotify,\r
+ Request,\r
+ &AsyncIoEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
+ }\r
\r
- Status = ScsiDiskRead10 (\r
- ScsiDiskDevice,\r
- &NeedRetry,\r
- &SenseData,\r
- &NumberOfSenseKeys,\r
- Timeout,\r
- PtrBuffer,\r
- &ByteCount,\r
- Lba32,\r
- SectorCount\r
- );\r
- if (!EFI_ERROR (Status)) {\r
- break;\r
- }\r
+ Status = ScsiWrite16CommandEx (\r
+ ScsiDiskDevice->ScsiIo,\r
+ Request->Timeout,\r
+ Request->SenseData,\r
+ &Request->SenseDataLength,\r
+ &Request->HostAdapterStatus,\r
+ &Request->TargetStatus,\r
+ Request->OutBuffer,\r
+ &Request->DataLength,\r
+ Request->StartLba,\r
+ Request->SectorCount,\r
+ AsyncIoEvent\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
+ }\r
\r
- if (!NeedRetry) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
+ return EFI_SUCCESS;\r
\r
- }\r
+ErrorExit:\r
+ if (AsyncIoEvent != NULL) {\r
+ gBS->CloseEvent (AsyncIoEvent);\r
+ }\r
\r
- if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
- return EFI_DEVICE_ERROR;\r
+ if (Request != NULL) {\r
+ if (Request->SenseData != NULL) {\r
+ FreePool (Request->SenseData);\r
}\r
\r
- //\r
- // actual transferred sectors\r
- //\r
- SectorCount = ByteCount / BlockSize;\r
+ OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+ RemoveEntryList (&Request->Link);\r
+ gBS->RestoreTPL (OldTpl);\r
\r
- Lba32 += SectorCount;\r
- PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
- BlocksRemaining -= SectorCount;\r
+ FreePool (Request);\r
}\r
\r
- return EFI_SUCCESS;\r
+ return Status;\r
}\r
\r
-EFI_STATUS\r
-ScsiDiskWriteSectors (\r
- SCSI_DISK_DEV *ScsiDiskDevice,\r
- VOID *Buffer,\r
- EFI_LBA Lba,\r
- UINTN NumberOfBlocks\r
+/**\r
+ Check sense key to find if media presents.\r
+\r
+ @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
+ @param SenseCounts The number of sense key\r
+\r
+ @retval TRUE NOT any media\r
+ @retval FALSE Media presents\r
+**/\r
+BOOLEAN\r
+ScsiDiskIsNoMedia (\r
+ IN EFI_SCSI_SENSE_DATA *SenseData,\r
+ IN UINTN SenseCounts\r
)\r
-/*++\r
+{\r
+ EFI_SCSI_SENSE_DATA *SensePtr;\r
+ UINTN Index;\r
+ BOOLEAN IsNoMedia;\r
\r
-Routine Description:\r
+ IsNoMedia = FALSE;\r
+ SensePtr = SenseData;\r
+\r
+ for (Index = 0; Index < SenseCounts; Index++) {\r
+ //\r
+ // Sense Key is EFI_SCSI_SK_NOT_READY (0x2),\r
+ // Additional Sense Code is ASC_NO_MEDIA (0x3A)\r
+ //\r
+ if ((SensePtr->Sense_Key == EFI_SCSI_SK_NOT_READY) &&\r
+ (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA))\r
+ {\r
+ IsNoMedia = TRUE;\r
+ }\r
\r
- Write SCSI Disk sectors\r
+ SensePtr++;\r
+ }\r
\r
-Arguments:\r
+ return IsNoMedia;\r
+}\r
\r
- ScsiDiskDevice - The pointer of SCSI_DISK_DEV\r
- Buffer - The data buffer to write sector\r
- Lba - Logic block address\r
- NumberOfBlocks - The number of blocks to write\r
+/**\r
+ Parse sense key.\r
\r
-Returns:\r
+ @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
+ @param SenseCounts The number of sense key\r
\r
- EFI_DEVICE_ERROR \r
- EFI_SUCCESS\r
+ @retval TRUE Error\r
+ @retval FALSE NOT error\r
\r
---*/\r
+**/\r
+BOOLEAN\r
+ScsiDiskIsMediaError (\r
+ IN EFI_SCSI_SENSE_DATA *SenseData,\r
+ IN UINTN SenseCounts\r
+ )\r
{\r
- UINTN BlocksRemaining;\r
- UINT32 Lba32;\r
- UINT8 *PtrBuffer;\r
- UINT32 BlockSize;\r
- UINT32 ByteCount;\r
- UINT32 MaxBlock;\r
- UINT32 SectorCount;\r
- UINT64 Timeout;\r
- EFI_STATUS Status;\r
- UINT8 Index;\r
- UINT8 MaxRetry;\r
- BOOLEAN NeedRetry;\r
- EFI_SCSI_SENSE_DATA *SenseData;\r
- UINTN NumberOfSenseKeys;\r
+ EFI_SCSI_SENSE_DATA *SensePtr;\r
+ UINTN Index;\r
+ BOOLEAN IsError;\r
\r
- SenseData = NULL;\r
- NumberOfSenseKeys = 0;\r
+ IsError = FALSE;\r
+ SensePtr = SenseData;\r
\r
- Status = EFI_SUCCESS;\r
+ for (Index = 0; Index < SenseCounts; Index++) {\r
+ switch (SensePtr->Sense_Key) {\r
+ case EFI_SCSI_SK_MEDIUM_ERROR:\r
+ //\r
+ // Sense Key is EFI_SCSI_SK_MEDIUM_ERROR (0x3)\r
+ //\r
+ switch (SensePtr->Addnl_Sense_Code) {\r
+ //\r
+ // fall through\r
+ //\r
+ case EFI_SCSI_ASC_MEDIA_ERR1:\r
\r
- BlocksRemaining = NumberOfBlocks;\r
- BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
- //\r
- // limit the data bytes that can be transferred by one Write(10) Command\r
- //\r
- MaxBlock = 65536;\r
+ //\r
+ // fall through\r
+ //\r
+ case EFI_SCSI_ASC_MEDIA_ERR2:\r
\r
- PtrBuffer = Buffer;\r
- Lba32 = (UINT32) Lba;\r
+ //\r
+ // fall through\r
+ //\r
+ case EFI_SCSI_ASC_MEDIA_ERR3:\r
+ case EFI_SCSI_ASC_MEDIA_ERR4:\r
+ IsError = TRUE;\r
+ break;\r
\r
- while (BlocksRemaining > 0) {\r
+ default:\r
+ break;\r
+ }\r
\r
- if (BlocksRemaining <= MaxBlock) {\r
+ break;\r
\r
- SectorCount = (UINT16) BlocksRemaining;\r
- } else {\r
+ case EFI_SCSI_SK_NOT_READY:\r
+ //\r
+ // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
+ //\r
+ switch (SensePtr->Addnl_Sense_Code) {\r
+ //\r
+ // Additional Sense Code is ASC_MEDIA_UPSIDE_DOWN (0x6)\r
+ //\r
+ case EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN:\r
+ IsError = TRUE;\r
+ break;\r
\r
- SectorCount = MaxBlock;\r
- }\r
+ default:\r
+ break;\r
+ }\r
\r
- ByteCount = SectorCount * BlockSize;\r
- Timeout = EfiScsiStallSeconds (2);\r
- MaxRetry = 2;\r
- for (Index = 0; Index < MaxRetry; Index++) {\r
- Status = ScsiDiskWrite10 (\r
- ScsiDiskDevice,\r
- &NeedRetry,\r
- &SenseData,\r
- &NumberOfSenseKeys,\r
- Timeout,\r
- PtrBuffer,\r
- &ByteCount,\r
- Lba32,\r
- SectorCount\r
- );\r
- if (!EFI_ERROR (Status)) {\r
break;\r
- }\r
-\r
- if (!NeedRetry) {\r
- return EFI_DEVICE_ERROR;\r
- }\r
- }\r
\r
- if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
- return EFI_DEVICE_ERROR;\r
+ default:\r
+ break;\r
}\r
- //\r
- // actual transferred sectors\r
- //\r
- SectorCount = ByteCount / BlockSize;\r
\r
- Lba32 += SectorCount;\r
- PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
- BlocksRemaining -= SectorCount;\r
+ SensePtr++;\r
}\r
\r
- return EFI_SUCCESS;\r
+ return IsError;\r
}\r
\r
-EFI_STATUS\r
-ScsiDiskRead10 (\r
- SCSI_DISK_DEV *ScsiDiskDevice,\r
- BOOLEAN *NeedRetry,\r
- EFI_SCSI_SENSE_DATA **SenseDataArray,\r
- UINTN *NumberOfSenseKeys,\r
- UINT64 Timeout,\r
- UINT8 *DataBuffer,\r
- UINT32 *DataLength,\r
- UINT32 StartLba,\r
- UINT32 SectorSize\r
- )\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Sumbmit Read command \r
-\r
-Arguments:\r
-\r
- ScsiDiskDevice - The pointer of ScsiDiskDevice\r
- NeedRetry - The pointer of flag indicates if needs retry if error happens\r
- SenseDataArray - The pointer of an array of sense data\r
- NumberOfSenseKeys - The number of sense key\r
- Timeout - The time to complete the command\r
- DataBuffer - The buffer to fill with the read out data\r
- DataLength - The length of buffer\r
- StartLba - The start logic block address\r
- SectorSize - The size of sector\r
-\r
-Returns:\r
+/**\r
+ Check sense key to find if hardware error happens.\r
\r
- EFI_STATUS\r
- \r
---*/\r
-{\r
- UINT8 SenseDataLength;\r
- EFI_STATUS Status;\r
- UINT8 HostAdapterStatus;\r
- UINT8 TargetStatus;\r
+ @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
+ @param SenseCounts The number of sense key\r
\r
- *NeedRetry = FALSE;\r
- *NumberOfSenseKeys = 0;\r
- SenseDataLength = 0;\r
- Status = ScsiRead10Command (\r
- ScsiDiskDevice->ScsiIo,\r
- Timeout,\r
- NULL,\r
- &SenseDataLength,\r
- &HostAdapterStatus,\r
- &TargetStatus,\r
- DataBuffer,\r
- DataLength,\r
- StartLba,\r
- SectorSize\r
- );\r
- return Status;\r
-}\r
+ @retval TRUE Hardware error exits.\r
+ @retval FALSE NO error.\r
\r
-EFI_STATUS\r
-ScsiDiskWrite10 (\r
- SCSI_DISK_DEV *ScsiDiskDevice,\r
- BOOLEAN *NeedRetry,\r
- EFI_SCSI_SENSE_DATA **SenseDataArray,\r
- UINTN *NumberOfSenseKeys,\r
- UINT64 Timeout,\r
- UINT8 *DataBuffer,\r
- UINT32 *DataLength,\r
- UINT32 StartLba,\r
- UINT32 SectorSize\r
+**/\r
+BOOLEAN\r
+ScsiDiskIsHardwareError (\r
+ IN EFI_SCSI_SENSE_DATA *SenseData,\r
+ IN UINTN SenseCounts\r
)\r
-/*++\r
-\r
-Routine Description:\r
-\r
- Submit Write Command\r
+{\r
+ EFI_SCSI_SENSE_DATA *SensePtr;\r
+ UINTN Index;\r
+ BOOLEAN IsError;\r
\r
-Arguments:\r
+ IsError = FALSE;\r
+ SensePtr = SenseData;\r
\r
- ScsiDiskDevice - The pointer of ScsiDiskDevice\r
- NeedRetry - The pointer of flag indicates if needs retry if error happens\r
- SenseDataArray - The pointer of an array of sense data\r
- NumberOfSenseKeys - The number of sense key\r
- Timeout - The time to complete the command\r
- DataBuffer - The buffer to fill with the read out data\r
- DataLength - The length of buffer\r
- StartLba - The start logic block address\r
- SectorSize - The size of sector\r
+ for (Index = 0; Index < SenseCounts; Index++) {\r
+ //\r
+ // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4)\r
+ //\r
+ if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {\r
+ IsError = TRUE;\r
+ }\r
\r
-Returns:\r
+ SensePtr++;\r
+ }\r
\r
- EFI_STATUS\r
+ return IsError;\r
+}\r
\r
---*/\r
-{\r
- EFI_STATUS Status;\r
- UINT8 SenseDataLength;\r
- UINT8 HostAdapterStatus;\r
- UINT8 TargetStatus;\r
+/**\r
+ Check sense key to find if media has changed.\r
\r
- *NeedRetry = FALSE;\r
- *NumberOfSenseKeys = 0;\r
- SenseDataLength = 0;\r
- Status = ScsiWrite10Command (\r
- ScsiDiskDevice->ScsiIo,\r
- Timeout,\r
- NULL,\r
- &SenseDataLength,\r
- &HostAdapterStatus,\r
- &TargetStatus,\r
- DataBuffer,\r
- DataLength,\r
- StartLba,\r
- SectorSize\r
- );\r
- return Status;\r
-}\r
+ @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
+ @param SenseCounts The number of sense key\r
\r
+ @retval TRUE Media is changed.\r
+ @retval FALSE Media is NOT changed.\r
+**/\r
BOOLEAN\r
-ScsiDiskIsNoMedia (\r
- IN EFI_SCSI_SENSE_DATA *SenseData,\r
- IN UINTN SenseCounts\r
+ScsiDiskIsMediaChange (\r
+ IN EFI_SCSI_SENSE_DATA *SenseData,\r
+ IN UINTN SenseCounts\r
)\r
-/*++\r
+{\r
+ EFI_SCSI_SENSE_DATA *SensePtr;\r
+ UINTN Index;\r
+ BOOLEAN IsMediaChanged;\r
+\r
+ IsMediaChanged = FALSE;\r
+ SensePtr = SenseData;\r
\r
-Routine Description:\r
+ for (Index = 0; Index < SenseCounts; Index++) {\r
+ //\r
+ // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6),\r
+ // Additional sense code is EFI_SCSI_ASC_MEDIA_CHANGE (0x28)\r
+ //\r
+ if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
+ (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE))\r
+ {\r
+ IsMediaChanged = TRUE;\r
+ }\r
\r
- Check sense key to find if media presents\r
+ SensePtr++;\r
+ }\r
\r
-Arguments:\r
+ return IsMediaChanged;\r
+}\r
\r
- SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
- SenseCounts - The number of sense key\r
+/**\r
+ Check sense key to find if reset happens.\r
\r
-Returns:\r
+ @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
+ @param SenseCounts The number of sense key\r
\r
- BOOLEAN\r
+ @retval TRUE It is reset before.\r
+ @retval FALSE It is NOT reset before.\r
\r
---*/\r
+**/\r
+BOOLEAN\r
+ScsiDiskIsResetBefore (\r
+ IN EFI_SCSI_SENSE_DATA *SenseData,\r
+ IN UINTN SenseCounts\r
+ )\r
{\r
- EFI_SCSI_SENSE_DATA *SensePtr;\r
- UINTN Index;\r
- BOOLEAN IsNoMedia;\r
+ EFI_SCSI_SENSE_DATA *SensePtr;\r
+ UINTN Index;\r
+ BOOLEAN IsResetBefore;\r
\r
- IsNoMedia = FALSE;\r
- SensePtr = SenseData;\r
+ IsResetBefore = FALSE;\r
+ SensePtr = SenseData;\r
\r
for (Index = 0; Index < SenseCounts; Index++) {\r
//\r
- // Sense Key is EFI_SCSI_SK_NOT_READY (0x2),\r
- // Additional Sense Code is ASC_NO_MEDIA (0x3A)\r
+ // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6)\r
+ // Additional Sense Code is EFI_SCSI_ASC_RESET (0x29)\r
//\r
- if ((SensePtr->Sense_Key == EFI_SCSI_SK_NOT_READY) &&\r
- (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) {\r
- IsNoMedia = TRUE;\r
+ if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
+ (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_RESET))\r
+ {\r
+ IsResetBefore = TRUE;\r
}\r
+\r
SensePtr++;\r
}\r
\r
- return IsNoMedia;\r
+ return IsResetBefore;\r
}\r
\r
+/**\r
+ Check sense key to find if the drive is ready.\r
+\r
+ @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
+ @param SenseCounts The number of sense key\r
+ @param RetryLater The flag means if need a retry\r
+\r
+ @retval TRUE Drive is ready.\r
+ @retval FALSE Drive is NOT ready.\r
+\r
+**/\r
BOOLEAN\r
-ScsiDiskIsMediaError (\r
- IN EFI_SCSI_SENSE_DATA *SenseData,\r
- IN UINTN SenseCounts\r
+ScsiDiskIsDriveReady (\r
+ IN EFI_SCSI_SENSE_DATA *SenseData,\r
+ IN UINTN SenseCounts,\r
+ OUT BOOLEAN *RetryLater\r
)\r
-/*++\r
+{\r
+ EFI_SCSI_SENSE_DATA *SensePtr;\r
+ UINTN Index;\r
+ BOOLEAN IsReady;\r
\r
-Routine Description:\r
+ IsReady = TRUE;\r
+ *RetryLater = FALSE;\r
+ SensePtr = SenseData;\r
\r
- Parse sense key\r
+ for (Index = 0; Index < SenseCounts; Index++) {\r
+ switch (SensePtr->Sense_Key) {\r
+ case EFI_SCSI_SK_NOT_READY:\r
+ //\r
+ // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
+ //\r
+ switch (SensePtr->Addnl_Sense_Code) {\r
+ case EFI_SCSI_ASC_NOT_READY:\r
+ //\r
+ // Additional Sense Code is EFI_SCSI_ASC_NOT_READY (0x4)\r
+ //\r
+ switch (SensePtr->Addnl_Sense_Code_Qualifier) {\r
+ case EFI_SCSI_ASCQ_IN_PROGRESS:\r
+ //\r
+ // Additional Sense Code Qualifier is\r
+ // EFI_SCSI_ASCQ_IN_PROGRESS (0x1)\r
+ //\r
+ IsReady = FALSE;\r
+ *RetryLater = TRUE;\r
+ break;\r
+\r
+ default:\r
+ IsReady = FALSE;\r
+ *RetryLater = FALSE;\r
+ break;\r
+ }\r
+\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
\r
-Arguments:\r
+ break;\r
\r
- SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
- SenseCounts - The number of sense key\r
+ default:\r
+ break;\r
+ }\r
\r
-Returns:\r
+ SensePtr++;\r
+ }\r
\r
- BOOLEAN\r
+ return IsReady;\r
+}\r
\r
---*/\r
-{\r
- EFI_SCSI_SENSE_DATA *SensePtr;\r
- UINTN Index;\r
- BOOLEAN IsError;\r
+/**\r
+ Check sense key to find if it has sense key.\r
\r
- IsError = FALSE;\r
- SensePtr = SenseData;\r
+ @param SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
+ @param SenseCounts - The number of sense key\r
\r
- for (Index = 0; Index < SenseCounts; Index++) {\r
+ @retval TRUE It has sense key.\r
+ @retval FALSE It has NOT any sense key.\r
\r
- switch (SensePtr->Sense_Key) {\r
+**/\r
+BOOLEAN\r
+ScsiDiskHaveSenseKey (\r
+ IN EFI_SCSI_SENSE_DATA *SenseData,\r
+ IN UINTN SenseCounts\r
+ )\r
+{\r
+ EFI_SCSI_SENSE_DATA *SensePtr;\r
+ UINTN Index;\r
+ BOOLEAN HaveSenseKey;\r
\r
- case EFI_SCSI_SK_MEDIUM_ERROR:\r
- //\r
- // Sense Key is EFI_SCSI_SK_MEDIUM_ERROR (0x3)\r
- //\r
- switch (SensePtr->Addnl_Sense_Code) {\r
+ if (SenseCounts == 0) {\r
+ HaveSenseKey = FALSE;\r
+ } else {\r
+ HaveSenseKey = TRUE;\r
+ }\r
\r
- //\r
- // fall through\r
- //\r
- case EFI_SCSI_ASC_MEDIA_ERR1:\r
+ SensePtr = SenseData;\r
\r
- //\r
- // fall through\r
- //\r
- case EFI_SCSI_ASC_MEDIA_ERR2:\r
+ for (Index = 0; Index < SenseCounts; Index++) {\r
+ //\r
+ // Sense Key is SK_NO_SENSE (0x0)\r
+ //\r
+ if ((SensePtr->Sense_Key == EFI_SCSI_SK_NO_SENSE) &&\r
+ (Index == 0))\r
+ {\r
+ HaveSenseKey = FALSE;\r
+ }\r
\r
- //\r
- // fall through\r
- //\r
- case EFI_SCSI_ASC_MEDIA_ERR3:\r
- case EFI_SCSI_ASC_MEDIA_ERR4:\r
- IsError = TRUE;\r
- break;\r
+ SensePtr++;\r
+ }\r
\r
- default:\r
- break;\r
- }\r
+ return HaveSenseKey;\r
+}\r
\r
- break;\r
+/**\r
+ Release resource about disk device.\r
\r
- case EFI_SCSI_SK_NOT_READY:\r
- //\r
- // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
- //\r
- switch (SensePtr->Addnl_Sense_Code) {\r
- //\r
- // Additional Sense Code is ASC_MEDIA_UPSIDE_DOWN (0x6)\r
- //\r
- case EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN:\r
- IsError = TRUE;\r
- break;\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
\r
- default:\r
- break;\r
- }\r
- break;\r
+**/\r
+VOID\r
+ReleaseScsiDiskDeviceResources (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice\r
+ )\r
+{\r
+ if (ScsiDiskDevice == NULL) {\r
+ return;\r
+ }\r
\r
- default:\r
- break;\r
- }\r
+ if (ScsiDiskDevice->SenseData != NULL) {\r
+ FreePool (ScsiDiskDevice->SenseData);\r
+ ScsiDiskDevice->SenseData = NULL;\r
+ }\r
\r
- SensePtr++;\r
+ if (ScsiDiskDevice->ControllerNameTable != NULL) {\r
+ FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable);\r
+ ScsiDiskDevice->ControllerNameTable = NULL;\r
}\r
\r
- return IsError;\r
+ FreePool (ScsiDiskDevice);\r
+\r
+ ScsiDiskDevice = NULL;\r
}\r
\r
+/**\r
+ Determine if Block Io & Block Io2 should be produced.\r
+\r
+\r
+ @param ChildHandle Child Handle to retrieve Parent information.\r
+\r
+ @retval TRUE Should produce Block Io & Block Io2.\r
+ @retval FALSE Should not produce Block Io & Block Io2.\r
+\r
+**/\r
BOOLEAN\r
-ScsiDiskIsHardwareError (\r
- IN EFI_SCSI_SENSE_DATA *SenseData,\r
- IN UINTN SenseCounts\r
+DetermineInstallBlockIo (\r
+ IN EFI_HANDLE ChildHandle\r
)\r
-/*++\r
+{\r
+ EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru;\r
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru;\r
\r
-Routine Description:\r
+ //\r
+ // Firstly, check if ExtScsiPassThru Protocol parent handle exists. If existence,\r
+ // check its attribute, logic or physical.\r
+ //\r
+ ExtScsiPassThru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiExtScsiPassThruProtocolGuid, ChildHandle);\r
+ if (ExtScsiPassThru != NULL) {\r
+ if ((ExtScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {\r
+ return TRUE;\r
+ }\r
+ }\r
\r
- Check sense key to find if hardware error happens\r
+ //\r
+ // Secondly, check if ScsiPassThru Protocol parent handle exists. If existence,\r
+ // check its attribute, logic or physical.\r
+ //\r
+ ScsiPassThru = (EFI_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiScsiPassThruProtocolGuid, ChildHandle);\r
+ if (ScsiPassThru != NULL) {\r
+ if ((ScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {\r
+ return TRUE;\r
+ }\r
+ }\r
\r
-Arguments:\r
+ return FALSE;\r
+}\r
\r
- SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
- SenseCounts - The number of sense key\r
+/**\r
+ Search protocol database and check to see if the protocol\r
+ specified by ProtocolGuid is present on a ControllerHandle and opened by\r
+ ChildHandle with an attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.\r
+ If the ControllerHandle is found, then the protocol specified by ProtocolGuid\r
+ will be opened on it.\r
\r
-Returns:\r
\r
- BOOLEAN\r
+ @param ProtocolGuid ProtocolGuid pointer.\r
+ @param ChildHandle Child Handle to retrieve Parent information.\r
\r
---*/\r
+**/\r
+VOID *\r
+EFIAPI\r
+GetParentProtocol (\r
+ IN EFI_GUID *ProtocolGuid,\r
+ IN EFI_HANDLE ChildHandle\r
+ )\r
{\r
- EFI_SCSI_SENSE_DATA *SensePtr;\r
- UINTN Index;\r
- BOOLEAN IsError;\r
+ UINTN Index;\r
+ UINTN HandleCount;\r
+ VOID *Interface;\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE *HandleBuffer;\r
\r
- IsError = FALSE;\r
- SensePtr = SenseData;\r
+ //\r
+ // Retrieve the list of all handles from the handle database\r
+ //\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ ProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &HandleBuffer\r
+ );\r
\r
- for (Index = 0; Index < SenseCounts; Index++) {\r
- \r
- //\r
- // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4)\r
- //\r
- if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {\r
- IsError = TRUE;\r
- }\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
\r
- SensePtr++;\r
+ //\r
+ // Iterate to find who is parent handle that is opened with ProtocolGuid by ChildHandle\r
+ //\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ Status = EfiTestChildHandle (HandleBuffer[Index], ChildHandle, ProtocolGuid);\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = gBS->HandleProtocol (HandleBuffer[Index], ProtocolGuid, (VOID **)&Interface);\r
+ if (!EFI_ERROR (Status)) {\r
+ gBS->FreePool (HandleBuffer);\r
+ return Interface;\r
+ }\r
+ }\r
}\r
\r
- return IsError;\r
+ gBS->FreePool (HandleBuffer);\r
+ return NULL;\r
}\r
\r
+/**\r
+ Determine if EFI Erase Block Protocol should be produced.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.\r
+ @param ChildHandle Handle of device.\r
+\r
+ @retval TRUE Should produce EFI Erase Block Protocol.\r
+ @retval FALSE Should not produce EFI Erase Block Protocol.\r
+\r
+**/\r
BOOLEAN\r
-ScsiDiskIsMediaChange (\r
- IN EFI_SCSI_SENSE_DATA *SenseData,\r
- IN UINTN SenseCounts\r
+DetermineInstallEraseBlock (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN EFI_HANDLE ChildHandle\r
)\r
-/*++\r
+{\r
+ UINT8 HostAdapterStatus;\r
+ UINT8 TargetStatus;\r
+ EFI_STATUS CommandStatus;\r
+ EFI_STATUS Status;\r
+ BOOLEAN UfsDevice;\r
+ BOOLEAN RetVal;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;\r
+ UINT8 SenseDataLength;\r
+ UINT32 DataLength16;\r
+ EFI_SCSI_DISK_CAPACITY_DATA16 *CapacityData16;\r
+\r
+ UfsDevice = FALSE;\r
+ RetVal = TRUE;\r
+ CapacityData16 = NULL;\r
\r
-Routine Description:\r
+ //\r
+ // UNMAP command is not supported by any of the UFS WLUNs.\r
+ //\r
+ if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_WLUN) {\r
+ RetVal = FALSE;\r
+ goto Done;\r
+ }\r
\r
- Check sense key to find if media has changed\r
+ Status = gBS->HandleProtocol (\r
+ ChildHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ (VOID **)&DevicePathNode\r
+ );\r
+ //\r
+ // Device Path protocol must be installed on the device handle.\r
+ //\r
+ ASSERT_EFI_ERROR (Status);\r
\r
-Arguments:\r
+ while (!IsDevicePathEndType (DevicePathNode)) {\r
+ //\r
+ // For now, only support Erase Block Protocol on UFS devices.\r
+ //\r
+ if ((DevicePathNode->Type == MESSAGING_DEVICE_PATH) &&\r
+ (DevicePathNode->SubType == MSG_UFS_DP))\r
+ {\r
+ UfsDevice = TRUE;\r
+ break;\r
+ }\r
\r
- SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
- SenseCounts - The number of sense key\r
+ DevicePathNode = NextDevicePathNode (DevicePathNode);\r
+ }\r
\r
-Returns:\r
+ if (!UfsDevice) {\r
+ RetVal = FALSE;\r
+ goto Done;\r
+ }\r
\r
- BOOLEAN\r
+ //\r
+ // Check whether the erase functionality is enabled on the UFS device.\r
+ //\r
+ CapacityData16 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
+ if (CapacityData16 == NULL) {\r
+ RetVal = FALSE;\r
+ goto Done;\r
+ }\r
\r
---*/\r
-{\r
- EFI_SCSI_SENSE_DATA *SensePtr;\r
- UINTN Index;\r
- BOOLEAN IsMediaChanged;\r
+ SenseDataLength = 0;\r
+ DataLength16 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);\r
+ ZeroMem (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
\r
- IsMediaChanged = FALSE;\r
- SensePtr = SenseData;\r
+ CommandStatus = ScsiReadCapacity16Command (\r
+ ScsiDiskDevice->ScsiIo,\r
+ SCSI_DISK_TIMEOUT,\r
+ NULL,\r
+ &SenseDataLength,\r
+ &HostAdapterStatus,\r
+ &TargetStatus,\r
+ (VOID *)CapacityData16,\r
+ &DataLength16,\r
+ FALSE\r
+ );\r
\r
- for (Index = 0; Index < SenseCounts; Index++) {\r
+ if (CommandStatus == EFI_SUCCESS) {\r
//\r
- // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6),\r
- // Additional sense code is EFI_SCSI_ASC_MEDIA_CHANGE (0x28)\r
+ // Universal Flash Storage (UFS) Version 2.0\r
+ // Section 11.3.9.2\r
+ // Bits TPE and TPRZ should both be set to enable the erase feature on UFS.\r
//\r
- if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
- (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) {\r
- IsMediaChanged = TRUE;\r
+ if (((CapacityData16->LowestAlignLogic2 & BIT7) == 0) ||\r
+ ((CapacityData16->LowestAlignLogic2 & BIT6) == 0))\r
+ {\r
+ DEBUG ((\r
+ DEBUG_VERBOSE,\r
+ "ScsiDisk EraseBlock: Either TPE or TPRZ is not set: 0x%x.\n",\r
+ CapacityData16->LowestAlignLogic2\r
+ ));\r
+\r
+ RetVal = FALSE;\r
+ goto Done;\r
}\r
+ } else {\r
+ DEBUG ((\r
+ DEBUG_VERBOSE,\r
+ "ScsiDisk EraseBlock: ReadCapacity16 failed with status %r.\n",\r
+ CommandStatus\r
+ ));\r
\r
- SensePtr++;\r
+ RetVal = FALSE;\r
+ goto Done;\r
}\r
\r
- return IsMediaChanged;\r
-}\r
-\r
-BOOLEAN\r
-ScsiDiskIsResetBefore (\r
- IN EFI_SCSI_SENSE_DATA *SenseData,\r
- IN UINTN SenseCounts\r
- )\r
-/*++\r
-\r
-Routine Description:\r
+ //\r
+ // Check whether the UFS device server implements the UNMAP command.\r
+ //\r
+ if ((ScsiDiskDevice->UnmapInfo.MaxLbaCnt == 0) ||\r
+ (ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt == 0))\r
+ {\r
+ DEBUG ((\r
+ DEBUG_VERBOSE,\r
+ "ScsiDisk EraseBlock: The device server does not implement the UNMAP command.\n"\r
+ ));\r
+\r
+ RetVal = FALSE;\r
+ goto Done;\r
+ }\r
\r
- Check sense key to find if reset happens\r
+Done:\r
+ if (CapacityData16 != NULL) {\r
+ FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
+ }\r
\r
-Arguments:\r
+ return RetVal;\r
+}\r
\r
- SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
- SenseCounts - The number of sense key\r
+/**\r
+ Determine if EFI Storage Security Command Protocol should be produced.\r
\r
-Returns:\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.\r
+ @param ChildHandle Handle of device.\r
\r
- BOOLEAN\r
+ @retval TRUE Should produce EFI Storage Security Command Protocol.\r
+ @retval FALSE Should not produce EFI Storage Security Command Protocol.\r
\r
---*/\r
+**/\r
+BOOLEAN\r
+DetermineInstallStorageSecurity (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN EFI_HANDLE ChildHandle\r
+ )\r
{\r
- EFI_SCSI_SENSE_DATA *SensePtr;\r
- UINTN Index;\r
- BOOLEAN IsResetBefore;\r
-\r
- IsResetBefore = FALSE;\r
- SensePtr = SenseData;\r
+ EFI_STATUS Status;\r
+ UFS_DEVICE_PATH *UfsDevice;\r
+ BOOLEAN RetVal;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;\r
+\r
+ UfsDevice = NULL;\r
+ RetVal = TRUE;\r
+\r
+ Status = gBS->HandleProtocol (\r
+ ChildHandle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ (VOID **)&DevicePathNode\r
+ );\r
+ //\r
+ // Device Path protocol must be installed on the device handle.\r
+ //\r
+ ASSERT_EFI_ERROR (Status);\r
\r
- for (Index = 0; Index < SenseCounts; Index++) {\r
- \r
+ while (!IsDevicePathEndType (DevicePathNode)) {\r
//\r
- // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6)\r
- // Additional Sense Code is EFI_SCSI_ASC_RESET (0x29)\r
+ // For now, only support Storage Security Command Protocol on UFS devices.\r
//\r
- if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
- (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) {\r
- IsResetBefore = TRUE;\r
+ if ((DevicePathNode->Type == MESSAGING_DEVICE_PATH) &&\r
+ (DevicePathNode->SubType == MSG_UFS_DP))\r
+ {\r
+ UfsDevice = (UFS_DEVICE_PATH *)DevicePathNode;\r
+ break;\r
}\r
\r
- SensePtr++;\r
+ DevicePathNode = NextDevicePathNode (DevicePathNode);\r
}\r
\r
- return IsResetBefore;\r
+ if (UfsDevice == NULL) {\r
+ RetVal = FALSE;\r
+ goto Done;\r
+ }\r
+\r
+ if (UfsDevice->Lun != UFS_WLUN_RPMB) {\r
+ RetVal = FALSE;\r
+ }\r
+\r
+Done:\r
+ return RetVal;\r
}\r
\r
-BOOLEAN\r
-ScsiDiskIsDriveReady (\r
- IN EFI_SCSI_SENSE_DATA *SenseData,\r
- IN UINTN SenseCounts,\r
- OUT BOOLEAN *RetryLater\r
- )\r
-/*++\r
+/**\r
+ Provides inquiry information for the controller type.\r
\r
-Routine Description:\r
+ This function is used by the IDE bus driver to get inquiry data. Data format\r
+ of Identify data is defined by the Interface GUID.\r
\r
- Check sense key to find if the drive is ready\r
+ @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.\r
+ @param[in, out] InquiryData Pointer to a buffer for the inquiry data.\r
+ @param[in, out] InquiryDataSize Pointer to the value for the inquiry data size.\r
\r
-Arguments:\r
+ @retval EFI_SUCCESS The command was accepted without any errors.\r
+ @retval EFI_NOT_FOUND Device does not support this data class\r
+ @retval EFI_DEVICE_ERROR Error reading InquiryData from device\r
+ @retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough\r
\r
- SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
- SenseCounts - The number of sense key\r
- RetryLater - The flag means if need a retry \r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskInfoInquiry (\r
+ IN EFI_DISK_INFO_PROTOCOL *This,\r
+ IN OUT VOID *InquiryData,\r
+ IN OUT UINT32 *InquiryDataSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
\r
-Returns:\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
\r
- BOOLEAN\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ if (*InquiryDataSize >= sizeof (ScsiDiskDevice->InquiryData)) {\r
+ Status = EFI_SUCCESS;\r
+ CopyMem (InquiryData, &ScsiDiskDevice->InquiryData, sizeof (ScsiDiskDevice->InquiryData));\r
+ }\r
\r
---*/\r
-{\r
- EFI_SCSI_SENSE_DATA *SensePtr;\r
- UINTN Index;\r
- BOOLEAN IsReady;\r
+ *InquiryDataSize = sizeof (ScsiDiskDevice->InquiryData);\r
+ return Status;\r
+}\r
\r
- IsReady = TRUE;\r
- *RetryLater = FALSE;\r
- SensePtr = SenseData;\r
+/**\r
+ Provides identify information for the controller type.\r
\r
- for (Index = 0; Index < SenseCounts; Index++) {\r
+ This function is used by the IDE bus driver to get identify data. Data format\r
+ of Identify data is defined by the Interface GUID.\r
\r
- switch (SensePtr->Sense_Key) {\r
+ @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL\r
+ instance.\r
+ @param[in, out] IdentifyData Pointer to a buffer for the identify data.\r
+ @param[in, out] IdentifyDataSize Pointer to the value for the identify data\r
+ size.\r
\r
- case EFI_SCSI_SK_NOT_READY:\r
- //\r
- // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
- //\r
- switch (SensePtr->Addnl_Sense_Code) {\r
- case EFI_SCSI_ASC_NOT_READY:\r
- //\r
- // Additional Sense Code is EFI_SCSI_ASC_NOT_READY (0x4)\r
- //\r
- switch (SensePtr->Addnl_Sense_Code_Qualifier) {\r
- case EFI_SCSI_ASCQ_IN_PROGRESS:\r
- //\r
- // Additional Sense Code Qualifier is\r
- // EFI_SCSI_ASCQ_IN_PROGRESS (0x1)\r
- //\r
- IsReady = FALSE;\r
- *RetryLater = TRUE;\r
- break;\r
+ @retval EFI_SUCCESS The command was accepted without any errors.\r
+ @retval EFI_NOT_FOUND Device does not support this data class\r
+ @retval EFI_DEVICE_ERROR Error reading IdentifyData from device\r
+ @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough\r
\r
- default:\r
- IsReady = FALSE;\r
- *RetryLater = FALSE;\r
- break;\r
- }\r
- break;\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskInfoIdentify (\r
+ IN EFI_DISK_INFO_PROTOCOL *This,\r
+ IN OUT VOID *IdentifyData,\r
+ IN OUT UINT32 *IdentifyDataSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
\r
- default:\r
- break;\r
- }\r
- break;\r
+ if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid) || CompareGuid (&This->Interface, &gEfiDiskInfoUfsInterfaceGuid)) {\r
+ //\r
+ // Physical SCSI bus does not support this data class.\r
+ //\r
+ return EFI_NOT_FOUND;\r
+ }\r
\r
- default:\r
- break;\r
- }\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
\r
- SensePtr++;\r
+ Status = EFI_BUFFER_TOO_SMALL;\r
+ if (*IdentifyDataSize >= sizeof (ScsiDiskDevice->IdentifyData)) {\r
+ Status = EFI_SUCCESS;\r
+ CopyMem (IdentifyData, &ScsiDiskDevice->IdentifyData, sizeof (ScsiDiskDevice->IdentifyData));\r
}\r
\r
- return IsReady;\r
+ *IdentifyDataSize = sizeof (ScsiDiskDevice->IdentifyData);\r
+ return Status;\r
}\r
\r
-BOOLEAN\r
-ScsiDiskHaveSenseKey (\r
- IN EFI_SCSI_SENSE_DATA *SenseData,\r
- IN UINTN SenseCounts\r
- )\r
-/*++\r
-\r
-Routine Description:\r
+/**\r
+ Provides sense data information for the controller type.\r
\r
- Check sense key to find if it has sense key\r
+ This function is used by the IDE bus driver to get sense data.\r
+ Data format of Sense data is defined by the Interface GUID.\r
\r
-Arguments:\r
+ @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.\r
+ @param[in, out] SenseData Pointer to the SenseData.\r
+ @param[in, out] SenseDataSize Size of SenseData in bytes.\r
+ @param[out] SenseDataNumber Pointer to the value for the sense data size.\r
\r
- SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
- SenseCounts - The number of sense key\r
+ @retval EFI_SUCCESS The command was accepted without any errors.\r
+ @retval EFI_NOT_FOUND Device does not support this data class.\r
+ @retval EFI_DEVICE_ERROR Error reading SenseData from device.\r
+ @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough.\r
\r
-Returns:\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskInfoSenseData (\r
+ IN EFI_DISK_INFO_PROTOCOL *This,\r
+ IN OUT VOID *SenseData,\r
+ IN OUT UINT32 *SenseDataSize,\r
+ OUT UINT8 *SenseDataNumber\r
+ )\r
+{\r
+ return EFI_NOT_FOUND;\r
+}\r
\r
- BOOLEAN\r
+/**\r
+ This function is used by the IDE bus driver to get controller information.\r
\r
---*/\r
-{\r
- EFI_SCSI_SENSE_DATA *SensePtr;\r
- UINTN Index;\r
- BOOLEAN HaveSenseKey;\r
+ @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.\r
+ @param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary.\r
+ @param[out] IdeDevice Pointer to the Ide Device number. Master or slave.\r
\r
- if (SenseCounts == 0) {\r
- HaveSenseKey = FALSE;\r
- } else {\r
- HaveSenseKey = TRUE;\r
- }\r
+ @retval EFI_SUCCESS IdeChannel and IdeDevice are valid.\r
+ @retval EFI_UNSUPPORTED This is not an IDE device.\r
\r
- SensePtr = SenseData;\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ScsiDiskInfoWhichIde (\r
+ IN EFI_DISK_INFO_PROTOCOL *This,\r
+ OUT UINT32 *IdeChannel,\r
+ OUT UINT32 *IdeDevice\r
+ )\r
+{\r
+ SCSI_DISK_DEV *ScsiDiskDevice;\r
\r
- for (Index = 0; Index < SenseCounts; Index++) {\r
- \r
+ if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid) || CompareGuid (&This->Interface, &gEfiDiskInfoUfsInterfaceGuid)) {\r
//\r
- // Sense Key is SK_NO_SENSE (0x0)\r
+ // This is not an IDE physical device.\r
//\r
- if ((SensePtr->Sense_Key == EFI_SCSI_SK_NO_SENSE) &&\r
- (Index == 0)) {\r
- HaveSenseKey = FALSE;\r
- }\r
-\r
- SensePtr++;\r
+ return EFI_UNSUPPORTED;\r
}\r
\r
- return HaveSenseKey;\r
+ ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
+ *IdeChannel = ScsiDiskDevice->Channel;\r
+ *IdeDevice = ScsiDiskDevice->Device;\r
+\r
+ return EFI_SUCCESS;\r
}\r
\r
-VOID\r
-ReleaseScsiDiskDeviceResources (\r
- IN SCSI_DISK_DEV *ScsiDiskDevice\r
+/**\r
+ Issues ATA IDENTIFY DEVICE command to identify ATAPI device.\r
+\r
+ This function tries to fill 512-byte ATAPI_IDENTIFY_DATA for ATAPI device to\r
+ implement Identify() interface for DiskInfo protocol. The ATA command is sent\r
+ via SCSI Request Packet.\r
+\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
+\r
+ @retval EFI_SUCCESS The ATAPI device identify data were retrieved successfully.\r
+ @retval others Some error occurred during the identification that ATAPI device.\r
+\r
+**/\r
+EFI_STATUS\r
+AtapiIdentifyDevice (\r
+ IN OUT SCSI_DISK_DEV *ScsiDiskDevice\r
)\r
-/*++\r
+{\r
+ EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;\r
+ UINT8 Cdb[6];\r
\r
-Routine Description:\r
+ //\r
+ // Initialize SCSI REQUEST_PACKET and 6-byte Cdb\r
+ //\r
+ ZeroMem (&CommandPacket, sizeof (CommandPacket));\r
+ ZeroMem (Cdb, sizeof (Cdb));\r
\r
- Release resource about disk device\r
+ Cdb[0] = ATA_CMD_IDENTIFY_DEVICE;\r
+ CommandPacket.Timeout = SCSI_DISK_TIMEOUT;\r
+ CommandPacket.Cdb = Cdb;\r
+ CommandPacket.CdbLength = (UINT8)sizeof (Cdb);\r
+ CommandPacket.InDataBuffer = &ScsiDiskDevice->IdentifyData;\r
+ CommandPacket.InTransferLength = sizeof (ScsiDiskDevice->IdentifyData);\r
\r
-Arguments:\r
+ return ScsiDiskDevice->ScsiIo->ExecuteScsiCommand (ScsiDiskDevice->ScsiIo, &CommandPacket, NULL);\r
+}\r
\r
- ScsiDiskDevice - The pointer of SCSI_DISK_DEV\r
+/**\r
+ Initialize the installation of DiskInfo protocol.\r
\r
-Returns:\r
+ This function prepares for the installation of DiskInfo protocol on the child handle.\r
+ By default, it installs DiskInfo protocol with SCSI interface GUID. If it further\r
+ detects that the physical device is an ATAPI/AHCI device, it then updates interface GUID\r
+ to be IDE/AHCI interface GUID.\r
\r
- NONE\r
+ @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.\r
+ @param ChildHandle Child handle to install DiskInfo protocol.\r
\r
---*/\r
+**/\r
+VOID\r
+InitializeInstallDiskInfo (\r
+ IN SCSI_DISK_DEV *ScsiDiskDevice,\r
+ IN EFI_HANDLE ChildHandle\r
+ )\r
{\r
- if (ScsiDiskDevice == NULL) {\r
- return ;\r
- }\r
-\r
- if (ScsiDiskDevice->SenseData != NULL) {\r
- gBS->FreePool (ScsiDiskDevice->SenseData);\r
- ScsiDiskDevice->SenseData = NULL;\r
- }\r
+ EFI_STATUS Status;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;\r
+ EFI_DEVICE_PATH_PROTOCOL *ChildDevicePathNode;\r
+ ATAPI_DEVICE_PATH *AtapiDevicePath;\r
+ SATA_DEVICE_PATH *SataDevicePath;\r
+ UINTN IdentifyRetry;\r
+\r
+ Status = gBS->HandleProtocol (ChildHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathNode);\r
+ //\r
+ // Device Path protocol must be installed on the device handle.\r
+ //\r
+ ASSERT_EFI_ERROR (Status);\r
+ //\r
+ // Copy the DiskInfo protocol template.\r
+ //\r
+ CopyMem (&ScsiDiskDevice->DiskInfo, &gScsiDiskInfoProtocolTemplate, sizeof (gScsiDiskInfoProtocolTemplate));\r
+\r
+ while (!IsDevicePathEnd (DevicePathNode)) {\r
+ ChildDevicePathNode = NextDevicePathNode (DevicePathNode);\r
+ if ((DevicePathType (DevicePathNode) == HARDWARE_DEVICE_PATH) &&\r
+ (DevicePathSubType (DevicePathNode) == HW_PCI_DP) &&\r
+ (DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) &&\r
+ ((DevicePathSubType (ChildDevicePathNode) == MSG_ATAPI_DP) ||\r
+ (DevicePathSubType (ChildDevicePathNode) == MSG_SATA_DP)))\r
+ {\r
+ IdentifyRetry = 3;\r
+ do {\r
+ //\r
+ // Issue ATA Identify Device Command via SCSI command, which is required to publish DiskInfo protocol\r
+ // with IDE/AHCI interface GUID.\r
+ //\r
+ Status = AtapiIdentifyDevice (ScsiDiskDevice);\r
+ if (!EFI_ERROR (Status)) {\r
+ if (DevicePathSubType (ChildDevicePathNode) == MSG_ATAPI_DP) {\r
+ //\r
+ // We find the valid ATAPI device path\r
+ //\r
+ AtapiDevicePath = (ATAPI_DEVICE_PATH *)ChildDevicePathNode;\r
+ ScsiDiskDevice->Channel = AtapiDevicePath->PrimarySecondary;\r
+ ScsiDiskDevice->Device = AtapiDevicePath->SlaveMaster;\r
+ //\r
+ // Update the DiskInfo.Interface to IDE interface GUID for the physical ATAPI device.\r
+ //\r
+ CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoIdeInterfaceGuid);\r
+ } else {\r
+ //\r
+ // We find the valid SATA device path\r
+ //\r
+ SataDevicePath = (SATA_DEVICE_PATH *)ChildDevicePathNode;\r
+ ScsiDiskDevice->Channel = SataDevicePath->HBAPortNumber;\r
+ ScsiDiskDevice->Device = SataDevicePath->PortMultiplierPortNumber;\r
+ //\r
+ // Update the DiskInfo.Interface to AHCI interface GUID for the physical AHCI device.\r
+ //\r
+ CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoAhciInterfaceGuid);\r
+ }\r
+\r
+ return;\r
+ }\r
+ } while (--IdentifyRetry > 0);\r
+ } else if ((DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) &&\r
+ (DevicePathSubType (ChildDevicePathNode) == MSG_UFS_DP))\r
+ {\r
+ CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoUfsInterfaceGuid);\r
+ break;\r
+ }\r
\r
- if (ScsiDiskDevice->ControllerNameTable != NULL) {\r
- FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable);\r
- ScsiDiskDevice->ControllerNameTable = NULL;\r
+ DevicePathNode = ChildDevicePathNode;\r
}\r
\r
- gBS->FreePool (ScsiDiskDevice);\r
-\r
- ScsiDiskDevice = NULL;\r
+ return;\r
}\r