scsidisk.c
来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 2,420 行 · 第 1/4 页
C
2,420 行
}
//
// Get the intrinsic block size
//
Media = ScsiDiskDevice->BlkIo.Media;
BlockSize = Media->BlockSize;
NumberOfBlocks = BufferSize / BlockSize;
if (!(Media->MediaPresent)) {
return EFI_NO_MEDIA;
}
if (MediaId != Media->MediaId) {
return EFI_MEDIA_CHANGED;
}
if (BufferSize % BlockSize != 0) {
return EFI_BAD_BUFFER_SIZE;
}
if (LBA > Media->LastBlock) {
return EFI_INVALID_PARAMETER;
}
if ((LBA + NumberOfBlocks - 1) > Media->LastBlock) {
return EFI_INVALID_PARAMETER;
}
if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
return EFI_INVALID_PARAMETER;
}
//
// if all the parameters are valid, then perform read sectors command
// to transfer data from device to host.
//
Status = ScsiDiskWriteSectors (ScsiDiskDevice, Buffer, LBA, NumberOfBlocks);
return Status;
}
EFI_STATUS
EFIAPI
ScsiDiskFlushBlocks (
IN EFI_BLOCK_IO_PROTOCOL *This
)
/*++
Routine Description:
Flush Block to Disk
Arguments:
This - The pointer of EFI_BLOCK_IO_PROTOCOL
Returns:
EFI_SUCCESS
--*/
{
//
// return directly
//
return EFI_SUCCESS;
}
EFI_STATUS
ScsiDiskDetectMedia (
SCSI_DISK_DEV *ScsiDiskDevice,
BOOLEAN MustReadCapacity,
BOOLEAN *MediaChange
)
/*++
Routine Description:
Dectect Device and read out capacity ,if error occurs, parse the sense key.
Arguments:
ScsiDiskDevice - The pointer of SCSI_DISK_DEV
MustReadCapacity - The flag about reading device capacity
MediaChange - The pointer of flag indicates if media has changed
Returns:
EFI_DEVICE_ERROR - Indicates that error occurs
EFI_SUCCESS - Successfully to detect media
--*/
{
EFI_STATUS Status;
EFI_STATUS ReadCapacityStatus;
EFI_SCSI_SENSE_DATA *SenseData;
UINTN NumberOfSenseKeys;
BOOLEAN NeedRetry;
BOOLEAN NeedReadCapacity;
UINT8 Index;
UINT8 MaxRetry;
EFI_BLOCK_IO_MEDIA OldMedia;
UINTN Action;
Status = EFI_SUCCESS;
ReadCapacityStatus = EFI_SUCCESS;
SenseData = NULL;
NumberOfSenseKeys = 0;
NeedReadCapacity = FALSE;
OldMedia = *(ScsiDiskDevice->BlkIo.Media);
*MediaChange = FALSE;
MaxRetry = 3;
for (Index = 0; Index < MaxRetry; Index++) {
Status = ScsiDiskTestUnitReady (
ScsiDiskDevice,
&NeedRetry,
&SenseData,
&NumberOfSenseKeys
);
if (!EFI_ERROR (Status)) {
break;
}
if (!NeedRetry) {
return Status;
}
}
if ((Index == MaxRetry) && EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
Status = DetectMediaParsingSenseKeys (
ScsiDiskDevice,
SenseData,
NumberOfSenseKeys,
&Action
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// ACTION_NO_ACTION: need not read capacity
// other action code: need read capacity
//
if (Action == ACTION_NO_ACTION) {
NeedReadCapacity = FALSE;
} else {
NeedReadCapacity = TRUE;
}
//
// either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,
// retrieve capacity via Read Capacity command
//
if (NeedReadCapacity || MustReadCapacity) {
//
// retrieve media information
//
MaxRetry = 3;
for (Index = 0; Index < MaxRetry; Index++) {
ReadCapacityStatus = ScsiDiskReadCapacity (
ScsiDiskDevice,
&NeedRetry,
&SenseData,
&NumberOfSenseKeys
);
if (EFI_ERROR (ReadCapacityStatus) && !NeedRetry) {
return EFI_DEVICE_ERROR;
}
//
// analyze sense key to action
//
Status = DetectMediaParsingSenseKeys (
ScsiDiskDevice,
SenseData,
NumberOfSenseKeys,
&Action
);
//
// if Status is error, it may indicate crisis error,
// so return without retry.
//
if (EFI_ERROR (Status)) {
return Status;
}
switch (Action) {
case ACTION_NO_ACTION:
//
// no retry
//
Index = MaxRetry;
break;
case ACTION_RETRY_COMMAND_LATER:
//
// retry the ReadCapacity later and continuously, until the condition
// no longer emerges.
// stall time is 100000us, or say 0.1 second.
//
gBS->Stall (100000);
Index = 0;
break;
default:
//
// other cases, just retry the command
//
break;
}
}
if ((Index == MaxRetry) && EFI_ERROR (ReadCapacityStatus)) {
return EFI_DEVICE_ERROR;
}
}
if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {
//
// Media change information got from the device
//
*MediaChange = TRUE;
}
if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {
*MediaChange = TRUE;
ScsiDiskDevice->BlkIo.Media->MediaId += 1;
}
if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {
*MediaChange = TRUE;
ScsiDiskDevice->BlkIo.Media->MediaId += 1;
}
if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) {
*MediaChange = TRUE;
ScsiDiskDevice->BlkIo.Media->MediaId += 1;
}
if (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) {
if (ScsiDiskDevice->BlkIo.Media->MediaPresent) {
//
// when change from no media to media present, reset the MediaId to 1.
//
ScsiDiskDevice->BlkIo.Media->MediaId = 1;
} else {
//
// when no media, reset the MediaId to zero.
//
ScsiDiskDevice->BlkIo.Media->MediaId = 0;
}
*MediaChange = TRUE;
}
return EFI_SUCCESS;
}
EFI_STATUS
ScsiDiskInquiryDevice (
SCSI_DISK_DEV *ScsiDiskDevice,
BOOLEAN *NeedRetry
)
/*++
Routine Description:
Send out Inquiry command to Device
Arguments:
ScsiDiskDevice - The pointer of SCSI_DISK_DEV
NeedRetry - Indicates if needs try again when error happens
Returns:
EFI_DEVICE_ERROR - Indicates that error occurs
EFI_SUCCESS - Successfully to detect media
--*/
{
UINT32 InquiryDataLength;
UINT8 SenseDataLength;
UINT8 HostAdapterStatus;
UINT8 TargetStatus;
EFI_SCSI_SENSE_DATA *SenseDataArray;
UINTN NumberOfSenseKeys;
EFI_STATUS Status;
UINT8 MaxRetry;
UINT8 Index;
InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA);
SenseDataLength = 0;
Status = SubmitInquiryCommand (
ScsiDiskDevice->ScsiIo,
EfiScsiStallSeconds (1),
NULL,
&SenseDataLength,
&HostAdapterStatus,
&TargetStatus,
(VOID *) &(ScsiDiskDevice->InquiryData),
&InquiryDataLength,
FALSE
);
switch (Status) {
//
// no need to check HostAdapterStatus and TargetStatus
//
case EFI_SUCCESS:
case EFI_WARN_BUFFER_TOO_SMALL:
ParseInquiryData (ScsiDiskDevice);
return EFI_SUCCESS;
case EFI_NOT_READY:
*NeedRetry = TRUE;
return EFI_DEVICE_ERROR;
case EFI_INVALID_PARAMETER:
case EFI_UNSUPPORTED:
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
//
// go ahead to check HostAdapterStatus and TargetStatus
// (EFI_TIMEOUT, EFI_DEVICE_ERROR)
//
default:
break;
}
Status = CheckHostAdapterStatus (HostAdapterStatus);
switch (Status) {
case EFI_SUCCESS:
break;
case EFI_TIMEOUT:
case EFI_NOT_READY:
*NeedRetry = TRUE;
return EFI_DEVICE_ERROR;
case EFI_DEVICE_ERROR:
//
// reset the scsi channel
//
ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
}
Status = CheckTargetStatus (TargetStatus);
switch (Status) {
case EFI_SUCCESS:
break;
case EFI_NOT_READY:
//
// reset the scsi device
//
ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
*NeedRetry = TRUE;
return EFI_DEVICE_ERROR;
case EFI_DEVICE_ERROR:
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
}
//
// if goes here, meant SubmitInquiryCommand() failed.
// if ScsiDiskRequestSenseKeys() succeeds at last,
// better retry SubmitInquiryCommand(). (by setting *NeedRetry = TRUE)
//
MaxRetry = 3;
for (Index = 0; Index < MaxRetry; Index++) {
Status = ScsiDiskRequestSenseKeys (
ScsiDiskDevice,
NeedRetry,
&SenseDataArray,
&NumberOfSenseKeys,
TRUE
);
if (!EFI_ERROR (Status)) {
*NeedRetry = TRUE;
return EFI_DEVICE_ERROR;
}
if (!*NeedRetry) {
return EFI_DEVICE_ERROR;
}
}
//
// ScsiDiskRequestSenseKeys() failed after several rounds of retry.
// set *NeedRetry = FALSE to avoid the outside caller try again.
//
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
}
EFI_STATUS
ScsiDiskTestUnitReady (
SCSI_DISK_DEV *ScsiDiskDevice,
BOOLEAN *NeedRetry,
EFI_SCSI_SENSE_DATA **SenseDataArray,
UINTN *NumberOfSenseKeys
)
/*++
Routine Description:
When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense;
When Test Unit Ready command encounters any error caused by host adapter or
target, return error without retrieving Sense Keys.
Arguments:
ScsiDiskDevice - The pointer of SCSI_DISK_DEV
NeedRetry - The pointer of flag indicates try again
SenseDataArray - The pointer of an array of sense data
NumberOfSenseKeys - The pointer of the number of sense data array
Returns:
EFI_DEVICE_ERROR - Indicates that error occurs
EFI_SUCCESS - Successfully to test unit
--*/
{
EFI_STATUS Status;
UINT8 SenseDataLength;
UINT8 HostAdapterStatus;
UINT8 TargetStatus;
UINT8 Index;
UINT8 MaxRetry;
SenseDataLength = 0;
*NumberOfSenseKeys = 0;
//
// Parameter 3 and 4: do not require sense data, retrieve it when needed.
//
Status = SubmitTestUnitReadyCommand (
ScsiDiskDevice->ScsiIo,
EfiScsiStallSeconds (1),
NULL,
&SenseDataLength,
&HostAdapterStatus,
&TargetStatus
);
switch (Status) {
//
// no need to check HostAdapterStatus and TargetStatus
//
case EFI_NOT_READY:
*NeedRetry = TRUE;
return EFI_DEVICE_ERROR;
case EFI_INVALID_PARAMETER:
case EFI_UNSUPPORTED:
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
//
// go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)
//
default:
break;
}
Status = CheckHostAdapterStatus (HostAdapterStatus);
switch (Status) {
case EFI_SUCCESS:
break;
case EFI_TIMEOUT:
case EFI_NOT_READY:
*NeedRetry = TRUE;
return EFI_DEVICE_ERROR;
case EFI_DEVICE_ERROR:
//
// reset the scsi channel
//
ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
}
Status = CheckTargetStatus (TargetStatus);
switch (Status) {
case EFI_SUCCESS:
break;
case EFI_NOT_READY:
//
// reset the scsi device
//
ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);
*NeedRetry = TRUE;
return EFI_DEVICE_ERROR;
case EFI_DEVICE_ERROR:
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
}
MaxRetry = 3;
for (Index = 0; Index < MaxRetry; Index++) {
Status = ScsiDiskRequestSenseKeys (
ScsiDiskDevice,
NeedRetry,
SenseDataArray,
NumberOfSenseKeys,
FALSE
);
if (!EFI_ERROR (Status)) {
return EFI_SUCCESS;
}
if (!*NeedRetry) {
return EFI_DEVICE_ERROR;
}
}
//
// ScsiDiskRequestSenseKeys() failed after several rounds of retry.
// set *NeedRetry = FALSE to avoid the outside caller try again.
//
*NeedRetry = FALSE;
return EFI_DEVICE_ERROR;
}
EFI_STATUS
DetectMediaParsingSenseKeys (
SCSI_DISK_DEV *ScsiDiskDevice,
EFI_SCSI_SENSE_DATA *SenseData,
UINTN NumberOfSenseKeys,
UINTN *Action
)
/*++
Routine Description:
Parsing Sense Keys which got from request sense command.
Arguments:
ScsiDiskDevice - The pointer of SCSI_DISK_DEV
SenseData - The pointer of EFI_SCSI_SENSE_DATA
NumberOfSenseKeys - The number of sense key
Action - The pointer of action which indicates what is need to do next
Returns:
EFI_DEVICE_ERROR - Indicates that error occurs
EFI_SUCCESS - Successfully to complete the parsing
--*/
{
BOOLEAN RetryLater;
//
// Default is to read capacity, unless..
//
*Action = ACTION_READ_CAPACITY;
if (NumberOfSenseKeys == 0) {
*Action = ACTION_NO_ACTION;
return EFI_SUCCESS;
}
if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {
//
// No Sense Key returned from last submitted command
//
*Action = ACTION_NO_ACTION;
return EFI_SUCCESS;
}
if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) {
ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;
ScsiDiskDevice->BlkIo.Media->LastBlock = 0;
*Action = ACTION_NO_ACTION;
return EFI_SUCCESS;
}
if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {
ScsiDiskDevice->BlkIo.Media->MediaId++;
return EFI_SUCCESS;
}
if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {
ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;
ScsiDiskDevice->BlkIo.Media->LastBlock = 0;
return EFI_DEVICE_ERROR;
}
if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {
return EFI_DEVICE_ERROR;
}
if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {
if (RetryLater) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?