+ 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
+ return Status;\r
+}\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
+ 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
+ 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 ((EFI_D_WARN,\r
+ "%a: invalid PageLength (%u) in Supported VPD Pages page\n",\r
+ __FUNCTION__, (UINT32)PageLength));\r
+ PageLength = 0;\r
+ }\r
+\r
+ if ((PageLength > 0) &&\r
+ (SupportedVpdPages->SupportedVpdPageList[0] !=\r
+ EFI_SCSI_PAGE_CODE_SUPPORTED_VPD)) {\r
+ DEBUG ((EFI_D_WARN,\r
+ "%a: Supported VPD Pages page doesn't start with code 0x%02x\n",\r
+ __FUNCTION__, EFI_SCSI_PAGE_CODE_SUPPORTED_VPD));\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
+ DEBUG ((EFI_D_WARN,\r
+ "%a: non-ascending code in Supported VPD Pages page @ %u\n",\r
+ __FUNCTION__, Index));\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
+ 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
+\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
+ }\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
+\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
+ // 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
+\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(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
+\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
+\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
+ // 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
+ 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
+ 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 ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsNoMedia\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {\r
+ ScsiDiskDevice->BlkIo.Media->MediaId++;\r
+ DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsMediaChange!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (ScsiDiskIsResetBefore (SenseData, NumberOfSenseKeys)) {\r
+ *Action = ACTION_RETRY_COMMAND_LATER;\r
+ DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsResetBefore!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {\r
+ DEBUG ((EFI_D_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 ((EFI_D_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 ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskDriveNotReady!\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+ *Action = ACTION_NO_ACTION;\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;\r
+ DEBUG ((EFI_D_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
+/**\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
+ 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
+ // 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
+\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
+\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
+\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
+ // 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
+/**\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
+/**\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; otherwize, 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
+\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
+\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
+ SenseReq = FALSE;\r
+ }\r
+ }\r
+\r
+EXIT:\r
+ FreeAlignedBuffer (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA));\r
+ return Status;\r
+}\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