mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
2600 lines
93 KiB
C
2600 lines
93 KiB
C
/*--
|
|
|
|
Copyright (C) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
sense.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the methods needed to accurately
|
|
determine how to retry requests on CDROM device types.
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "stddef.h"
|
|
#include "string.h"
|
|
|
|
#include "ntddk.h"
|
|
#include "ntddstor.h"
|
|
#include "cdrom.h"
|
|
#include "ntstrsafe.h"
|
|
|
|
|
|
#ifdef DEBUG_USE_WPP
|
|
#include "sense.tmh"
|
|
#endif
|
|
|
|
// Forward declarations
|
|
VOID
|
|
SenseInfoInterpretRefineByIoControl(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ ULONG IoControlCode,
|
|
_In_ BOOLEAN OverrideVerifyVolume,
|
|
_Inout_ BOOLEAN* Retry,
|
|
_Inout_ NTSTATUS* Status
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(PAGE, SenseInfoInterpretRefineByIoControl)
|
|
|
|
#endif
|
|
|
|
|
|
//
|
|
// FROM CLASSPNP\CLASSP.H
|
|
// Lots of retries of synchronized SCSI commands that devices may not
|
|
// even support really slows down the system (especially while booting).
|
|
// (Even GetDriveCapacity may be failed on purpose if an external disk is powered off).
|
|
// If a disk cannot return a small initialization buffer at startup
|
|
// in two attempts (with delay interval) then we cannot expect it to return
|
|
// data consistently with four retries.
|
|
// So don't set the retry counts as high here as for data SRBs.
|
|
//
|
|
// If we find that these requests are failing consecutively,
|
|
// despite the retry interval, on otherwise reliable media,
|
|
// then we should either increase the retry interval for
|
|
// that failure or (by all means) increase these retry counts as appropriate.
|
|
//
|
|
|
|
#define TOTAL_COUNT_RETRY_DEFAULT 4
|
|
#define TOTAL_COUNT_RETRY_LOCK_MEDIA 1
|
|
#define TOTAL_COUNT_RETRY_MODESENSE 1
|
|
#define TOTAL_COUNT_RETRY_READ_CAPACITY 1
|
|
|
|
|
|
#define TOTAL_SECONDS_RETRY_TIME_WRITE 160
|
|
#define TOTAL_SECONDS_RETRY_TIME_MEDIUM_REMOVAL 120
|
|
|
|
typedef struct _ERROR_LOG_CONTEXT {
|
|
BOOLEAN LogError;
|
|
BOOLEAN ErrorUnhandled;
|
|
NTSTATUS ErrorCode;
|
|
ULONG UniqueErrorValue;
|
|
ULONG BadSector;
|
|
} ERROR_LOG_CONTEXT, *PERROR_LOG_CONTEXT;
|
|
|
|
NTSTATUS
|
|
DeviceErrorHandlerForMmc(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb,
|
|
_Inout_ PNTSTATUS Status,
|
|
_Inout_ PBOOLEAN Retry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
this routine will be used for error handler for all MMC devices.
|
|
it's invoked by DeviceErrorHandler()that invoked by SenseInfoInterpret() or GESN
|
|
|
|
This routine just checks for media change sense/asc/ascq and
|
|
also for other events, such as bus resets. this is used to
|
|
determine if the device behaviour has changed, to allow for
|
|
read and write operations to be allowed and/or disallowed.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device context
|
|
Srb - SRB structure for analyze
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
Status -
|
|
Retry -
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN mediaChange = FALSE;
|
|
PCDB cdb = (PCDB)Srb->Cdb;
|
|
|
|
if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID))
|
|
{
|
|
PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
|
|
|
|
// the following sense keys could indicate a change in capabilities.
|
|
|
|
// we used to expect this to be serialized, and only hit from our
|
|
// own routine. we now allow some requests to continue during our
|
|
// processing of the capabilities update in order to allow
|
|
// IoReadPartitionTable() to succeed.
|
|
switch (senseBuffer->SenseKey & 0xf)
|
|
{
|
|
|
|
case SCSI_SENSE_NOT_READY:
|
|
{
|
|
if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)
|
|
{
|
|
if (DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"DeviceErrorHandlerForMmc: media removed, writes will be "
|
|
"failed until new media detected\n"));
|
|
}
|
|
|
|
// NOTE - REF #0002
|
|
DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
|
|
}
|
|
else if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)
|
|
{
|
|
if (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_BECOMING_READY)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"DeviceErrorHandlerForMmc: media becoming ready, "
|
|
"SHOULD notify shell of change time by sending "
|
|
"GESN request immediately!\n"));
|
|
}
|
|
else if (((senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_OPERATION_IN_PROGRESS) ||
|
|
(senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS)
|
|
) &&
|
|
((Srb->Cdb[0] == SCSIOP_READ) ||
|
|
(Srb->Cdb[0] == SCSIOP_READ6) ||
|
|
(Srb->Cdb[0] == SCSIOP_READ_CAPACITY) ||
|
|
(Srb->Cdb[0] == SCSIOP_READ_CD) ||
|
|
(Srb->Cdb[0] == SCSIOP_READ_CD_MSF) ||
|
|
(Srb->Cdb[0] == SCSIOP_READ_TOC) ||
|
|
(Srb->Cdb[0] == SCSIOP_WRITE) ||
|
|
(Srb->Cdb[0] == SCSIOP_WRITE6) ||
|
|
(Srb->Cdb[0] == SCSIOP_READ_TRACK_INFORMATION) ||
|
|
(Srb->Cdb[0] == SCSIOP_READ_DISK_INFORMATION)
|
|
)
|
|
)
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"DeviceErrorHandlerForMmc: LONG_WRITE or "
|
|
"OP_IN_PROGRESS for limited subset of cmds -- "
|
|
"setting retry to TRUE\n"));
|
|
*Retry = TRUE;
|
|
*Status = STATUS_DEVICE_BUSY;
|
|
}
|
|
}
|
|
break;
|
|
} // end SCSI_SENSE_NOT_READY
|
|
|
|
case SCSI_SENSE_UNIT_ATTENTION:
|
|
{
|
|
switch (senseBuffer->AdditionalSenseCode)
|
|
{
|
|
case SCSI_ADSENSE_MEDIUM_CHANGED:
|
|
{
|
|
// always update if the medium may have changed
|
|
|
|
// NOTE - REF #0002
|
|
DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
|
|
DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"DeviceErrorHandlerForMmc: media change detected, need to "
|
|
"update drive capabilities\n"));
|
|
mediaChange = TRUE;
|
|
break;
|
|
|
|
} // end SCSI_ADSENSE_MEDIUM_CHANGED
|
|
|
|
case SCSI_ADSENSE_BUS_RESET:
|
|
{
|
|
// NOTE - REF #0002
|
|
DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
|
|
DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"DeviceErrorHandlerForMmc: bus reset detected, need to "
|
|
"update drive capabilities\n"));
|
|
break;
|
|
|
|
} // end SCSI_ADSENSE_BUS_RESET
|
|
|
|
case SCSI_ADSENSE_OPERATOR_REQUEST:
|
|
{
|
|
|
|
BOOLEAN b = FALSE;
|
|
|
|
switch (senseBuffer->AdditionalSenseCodeQualifier)
|
|
{
|
|
case SCSI_SENSEQ_MEDIUM_REMOVAL:
|
|
{
|
|
// eject notification currently handled by classpnp
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"DeviceErrorHandlerForMmc: Eject requested by user\n"));
|
|
*Retry = TRUE;
|
|
*Status = STATUS_DEVICE_BUSY;
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSEQ_WRITE_PROTECT_DISABLE:
|
|
b = TRUE;
|
|
case SCSI_SENSEQ_WRITE_PROTECT_ENABLE:
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"DeviceErrorHandlerForMmc: Write protect %s requested "
|
|
"by user\n",
|
|
(b ? "disable" : "enable")));
|
|
*Retry = TRUE;
|
|
*Status = STATUS_DEVICE_BUSY;
|
|
// NOTE - REF #0002
|
|
DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
|
|
DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
|
|
|
|
break;
|
|
}
|
|
|
|
} // end of AdditionalSenseCodeQualifier switch
|
|
|
|
|
|
break;
|
|
|
|
} // end SCSI_ADSENSE_OPERATOR_REQUEST
|
|
|
|
default:
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"DeviceErrorHandlerForMmc: Unit attention %02x/%02x\n",
|
|
senseBuffer->AdditionalSenseCode,
|
|
senseBuffer->AdditionalSenseCodeQualifier));
|
|
break;
|
|
}
|
|
|
|
} // end of AdditionSenseCode switch
|
|
break;
|
|
|
|
} // end SCSI_SENSE_UNIT_ATTENTION
|
|
|
|
case SCSI_SENSE_ILLEGAL_REQUEST:
|
|
{
|
|
if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_WRITE_PROTECT)
|
|
{
|
|
if (DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed)
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"DeviceErrorHandlerForMmc: media was writable, but "
|
|
"failed request with WRITE_PROTECT error...\n"));
|
|
}
|
|
// NOTE - REF #0002
|
|
// do not update all the capabilities just because
|
|
// we can't write to the disc.
|
|
DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
|
|
}
|
|
break;
|
|
} // end SCSI_SENSE_ILLEGAL_REQUEST
|
|
|
|
} // end of SenseKey switch
|
|
|
|
// Check if we failed to set the DVD region key and send appropriate error
|
|
if (cdb->CDB16.OperationCode == SCSIOP_SEND_KEY)
|
|
{
|
|
if (cdb->SEND_KEY.KeyFormat == DvdSetRpcKey)
|
|
{
|
|
if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)
|
|
{
|
|
// media of appropriate region required
|
|
*Status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
*Retry = FALSE;
|
|
}
|
|
else if ((senseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST) &&
|
|
(senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_COPY_PROTECTION_FAILURE) &&
|
|
(senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT))
|
|
{
|
|
// media of appropriate region required
|
|
*Status = STATUS_CSS_REGION_MISMATCH;
|
|
*Retry = FALSE;
|
|
}
|
|
else if ((senseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST) &&
|
|
(senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INVALID_MEDIA) &&
|
|
(senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_INCOMPATIBLE_FORMAT))
|
|
{
|
|
// media of appropriate region required
|
|
*Status = STATUS_CSS_REGION_MISMATCH;
|
|
*Retry = FALSE;
|
|
}
|
|
}
|
|
}
|
|
} // end of SRB_STATUS_AUTOSENSE_VALID
|
|
|
|
// On media change, if device speed should be reset to default then
|
|
// queue a workitem to send the commands to the device. Do this on
|
|
// media arrival as some device will fail this command if no media
|
|
// is present. Ignore the fake media change from classpnp driver.
|
|
if ((mediaChange == TRUE) && (*Status != STATUS_MEDIA_CHANGED))
|
|
{
|
|
if (DeviceExtension->DeviceAdditionalData.RestoreDefaults == TRUE)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
WDF_OBJECT_ATTRIBUTES attributes;
|
|
WDF_WORKITEM_CONFIG workitemConfig;
|
|
WDFWORKITEM workItem;
|
|
|
|
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
|
attributes.ParentObject = DeviceExtension->Device;
|
|
|
|
WDF_WORKITEM_CONFIG_INIT(&workitemConfig,
|
|
DeviceRestoreDefaultSpeed);
|
|
|
|
status = WdfWorkItemCreate(&workitemConfig,
|
|
&attributes,
|
|
&workItem);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
WdfWorkItemEnqueue(workItem);
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"DeviceErrorHandlerForMmc: Restore device default speed for %p\n",
|
|
DeviceExtension->DeviceObject));
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
DeviceErrorHandlerForHitachiGD2000(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb,
|
|
_Inout_ PNTSTATUS Status,
|
|
_Inout_ PBOOLEAN Retry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
error handler for HITACHI CDR-1750S, CDR-3650/1650S
|
|
|
|
This routine checks the type of error. If the error suggests that the
|
|
drive has spun down and cannot reinitialize itself, send a
|
|
START_UNIT or READ to the device. This will force the drive to spin
|
|
up. This drive also loses the AGIDs it has granted when it spins down,
|
|
which may result in playback failure the first time around.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - the device object.
|
|
|
|
Srb - Supplies a pointer to the failing Srb.
|
|
|
|
Status - return the final status for this command?
|
|
|
|
Retry - return if the command should be retried.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
|
|
|
|
if (!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID))
|
|
{
|
|
return STATUS_SUCCESS; //nobody cares about this return value yet.
|
|
}
|
|
|
|
if (((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_HARDWARE_ERROR) &&
|
|
(senseBuffer->AdditionalSenseCode == 0x44))
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"DeviceErrorHandlerForHitachiGD2000 (%p) => Internal Target "
|
|
"Failure Detected -- spinning up drive\n", DeviceExtension->Device));
|
|
|
|
// the request should be retried because the device isn't ready
|
|
*Retry = TRUE;
|
|
*Status = STATUS_DEVICE_NOT_READY;
|
|
|
|
// send a START_STOP unit to spin up the drive
|
|
// NOTE: this temporarily violates the StartIo serialization
|
|
// mechanism, but the completion routine on this will NOT
|
|
// call StartNextPacket(), so it's a temporary disruption
|
|
// of the serialization only.
|
|
DeviceSendStartUnit(DeviceExtension->Device);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
SenseInfoRequestGetInformation(
|
|
_In_ WDFREQUEST Request,
|
|
_Out_ UCHAR* MajorFunctionCode,
|
|
_Out_ ULONG* IoControlCode,
|
|
_Out_ BOOLEAN* OverrideVerifyVolume,
|
|
_Out_ ULONGLONG* Total100nsSinceFirstSend
|
|
)
|
|
{
|
|
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
|
|
|
|
*MajorFunctionCode = 0;
|
|
*IoControlCode = 0;
|
|
*OverrideVerifyVolume = FALSE;
|
|
*Total100nsSinceFirstSend = 0;
|
|
|
|
if (requestContext->OriginalRequest != NULL)
|
|
{
|
|
PIO_STACK_LOCATION originalIrpStack = NULL;
|
|
|
|
PIRP originalIrp = WdfRequestWdmGetIrp(requestContext->OriginalRequest);
|
|
|
|
if (originalIrp != NULL)
|
|
{
|
|
originalIrpStack = IoGetCurrentIrpStackLocation(originalIrp);
|
|
}
|
|
|
|
if (originalIrpStack != NULL)
|
|
{
|
|
*MajorFunctionCode = originalIrpStack->MajorFunction;
|
|
|
|
if (*MajorFunctionCode == IRP_MJ_DEVICE_CONTROL)
|
|
{
|
|
*IoControlCode = originalIrpStack->Parameters.DeviceIoControl.IoControlCode;
|
|
}
|
|
|
|
*OverrideVerifyVolume = TEST_FLAG(originalIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME);
|
|
}
|
|
}
|
|
|
|
// Calculate time past since the request was first time sent.
|
|
if (requestContext->TimeSentDownFirstTime.QuadPart > 0)
|
|
{
|
|
LARGE_INTEGER tmp;
|
|
KeQueryTickCount(&tmp);
|
|
tmp.QuadPart -= requestContext->TimeSentDownFirstTime.QuadPart;
|
|
tmp.QuadPart *= KeQueryTimeIncrement();
|
|
*Total100nsSinceFirstSend = tmp.QuadPart;
|
|
}
|
|
else
|
|
{
|
|
// set to -1 if field TimeSentDownFirstTime not set.
|
|
*Total100nsSinceFirstSend = (ULONGLONG) -1;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
SenseInfoInterpretByAdditionalSenseCode(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb,
|
|
_In_ UCHAR AdditionalSenseCode,
|
|
_In_ UCHAR AdditionalSenseCodeQual,
|
|
_Inout_ NTSTATUS* Status,
|
|
_Inout_ BOOLEAN* Retry,
|
|
_Out_ _Deref_out_range_(0,100) ULONG* RetryIntervalInSeconds,
|
|
_Inout_ PERROR_LOG_CONTEXT LogContext
|
|
)
|
|
/*
|
|
This function will interpret error based on ASC/ASCQ.
|
|
|
|
If the error code is not processed in this function, e.g. return value is TRUE,
|
|
caller needs to call SenseInfoInterpretBySenseKey() for further interpret.
|
|
*/
|
|
{
|
|
BOOLEAN needFurtherInterpret = TRUE;
|
|
PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
|
|
|
|
// set default values for retry fields.
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
*RetryIntervalInSeconds = 0;
|
|
|
|
switch (AdditionalSenseCode)
|
|
{
|
|
case SCSI_ADSENSE_LUN_NOT_READY:
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Lun not ready\n"));
|
|
|
|
//
|
|
// Many non-WHQL certified drives (mostly CD-RW) return
|
|
// 2/4/0 when they have no media instead of the obvious choice of:
|
|
//
|
|
// SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
|
|
//
|
|
// These drives should not pass WHQL certification due to this discrepency.
|
|
//
|
|
// However, we have to retry on 2/4/0 (Not ready, LUN not ready, no info)
|
|
// and also 3/2/0 (no seek complete).
|
|
//
|
|
// These conditions occur when the shell tries to examine an
|
|
// injected CD (e.g. for autoplay) before the CD is spun up.
|
|
//
|
|
// The drive should be returning an ASCQ of SCSI_SENSEQ_BECOMING_READY
|
|
// (0x01) in order to comply with WHQL standards.
|
|
//
|
|
// The default retry timeout of one second is acceptable to balance
|
|
// these discrepencies. don't modify the status, though....
|
|
//
|
|
|
|
switch (AdditionalSenseCodeQual)
|
|
{
|
|
case SCSI_SENSEQ_OPERATION_IN_PROGRESS:
|
|
{
|
|
DEVICE_EVENT_BECOMING_READY notReady = {0};
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Operation In Progress\n"));
|
|
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Retry = TRUE;
|
|
*RetryIntervalInSeconds = NOT_READY_RETRY_INTERVAL;
|
|
*Status = STATUS_DEVICE_NOT_READY;
|
|
|
|
notReady.Version = 1;
|
|
notReady.Reason = 2;
|
|
notReady.Estimated100msToReady = *RetryIntervalInSeconds * 10;
|
|
DeviceSendNotification(DeviceExtension,
|
|
&GUID_IO_DEVICE_BECOMING_READY,
|
|
sizeof(DEVICE_EVENT_BECOMING_READY),
|
|
¬Ready);
|
|
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSEQ_BECOMING_READY:
|
|
{
|
|
DEVICE_EVENT_BECOMING_READY notReady = {0};
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: In process of becoming ready\n"));
|
|
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Retry = TRUE;
|
|
*RetryIntervalInSeconds = NOT_READY_RETRY_INTERVAL;
|
|
*Status = STATUS_DEVICE_NOT_READY;
|
|
|
|
notReady.Version = 1;
|
|
notReady.Reason = 1;
|
|
notReady.Estimated100msToReady = *RetryIntervalInSeconds * 10;
|
|
DeviceSendNotification(DeviceExtension,
|
|
&GUID_IO_DEVICE_BECOMING_READY,
|
|
sizeof(DEVICE_EVENT_BECOMING_READY),
|
|
¬Ready);
|
|
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS:
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Long write in progress\n"));
|
|
|
|
needFurtherInterpret = FALSE;
|
|
|
|
// This has been seen as a transcient failure on some drives
|
|
*Status = STATUS_DEVICE_NOT_READY;
|
|
*Retry = TRUE;
|
|
// Set retry interval to be 0 as the drive can be ready at anytime.
|
|
*RetryIntervalInSeconds = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED:
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Manual intervention required\n"));
|
|
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
*Retry = FALSE;
|
|
*RetryIntervalInSeconds = NOT_READY_RETRY_INTERVAL;
|
|
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSEQ_FORMAT_IN_PROGRESS:
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Format in progress\n"));
|
|
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Status = STATUS_DEVICE_NOT_READY;
|
|
*Retry = FALSE;
|
|
*RetryIntervalInSeconds = NOT_READY_RETRY_INTERVAL;
|
|
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE:
|
|
case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
|
|
default:
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Initializing command required\n"));
|
|
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Status = STATUS_DEVICE_NOT_READY;
|
|
*Retry = TRUE;
|
|
*RetryIntervalInSeconds = 0;
|
|
|
|
// This sense code/additional sense code combination may indicate
|
|
// that the device needs to be started.
|
|
if (TEST_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT) &&
|
|
!TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY))
|
|
{
|
|
DeviceSendStartUnit(DeviceExtension->Device);
|
|
}
|
|
|
|
break;
|
|
}
|
|
} // end switch (AdditionalSenseCodeQual)
|
|
break;
|
|
|
|
} // end case (SCSI_ADSENSE_LUN_NOT_READY)
|
|
|
|
case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE:
|
|
{
|
|
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: No Media in device.\n"));
|
|
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
*Retry = FALSE;
|
|
|
|
if (AdditionalSenseCodeQual == 0xCC)
|
|
{
|
|
// The IMAPIv1 filter returns this ASCQ value while it is burning CD media, and we want
|
|
// to preserve this functionality for compatibility reasons.
|
|
// We want to indicate that the media is not present to most applications;
|
|
// but RSM has to know that the media is still in the drive (i.e. the drive is not free).
|
|
DeviceSetMediaChangeStateEx(DeviceExtension, MediaUnavailable, NULL);
|
|
}
|
|
else
|
|
{
|
|
DeviceSetMediaChangeStateEx(DeviceExtension, MediaNotPresent, NULL);
|
|
}
|
|
|
|
break;
|
|
} // end case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
|
|
|
|
case SCSI_ADSENSE_INVALID_MEDIA:
|
|
{
|
|
switch (AdditionalSenseCodeQual)
|
|
{
|
|
|
|
case SCSI_SENSEQ_UNKNOWN_FORMAT:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
|
|
// Log error only if this is a paging request
|
|
*Status = STATUS_UNRECOGNIZED_MEDIA;
|
|
*Retry = FALSE;
|
|
|
|
LogContext->LogError = TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING);
|
|
LogContext->UniqueErrorValue = 256;
|
|
LogContext->ErrorCode = IO_ERR_BAD_BLOCK;
|
|
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSEQ_INCOMPATIBLE_FORMAT:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Status = STATUS_UNRECOGNIZED_MEDIA;
|
|
*Retry = FALSE;
|
|
|
|
LogContext->LogError = FALSE;
|
|
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
|
|
*Retry = FALSE;
|
|
|
|
LogContext->LogError = FALSE;
|
|
LogContext->UniqueErrorValue = 256;
|
|
LogContext->ErrorCode = IO_ERR_BAD_BLOCK;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
needFurtherInterpret = TRUE;
|
|
break;
|
|
}
|
|
} // end case AdditionalSenseCodeQual
|
|
|
|
break;
|
|
} // end case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
|
|
|
|
case SCSI_ADSENSE_NO_SEEK_COMPLETE:
|
|
{
|
|
switch (AdditionalSenseCodeQual)
|
|
{
|
|
|
|
case 0x00:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Status = STATUS_DEVICE_DATA_ERROR;
|
|
*Retry = TRUE;
|
|
*RetryIntervalInSeconds = 0;
|
|
LogContext->LogError = TRUE;
|
|
LogContext->UniqueErrorValue = 256;
|
|
LogContext->ErrorCode = IO_ERR_BAD_BLOCK;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
needFurtherInterpret = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
} // end case SCSI_ADSENSE_NO_SEEK_COMPLETE
|
|
|
|
case SCSI_ADSENSE_LUN_COMMUNICATION:
|
|
{
|
|
switch (AdditionalSenseCodeQual)
|
|
{
|
|
|
|
case SCSI_SESNEQ_COMM_CRC_ERROR:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
*RetryIntervalInSeconds = 1;
|
|
LogContext->LogError = TRUE;
|
|
LogContext->UniqueErrorValue = 257;
|
|
LogContext->ErrorCode = IO_ERR_CONTROLLER_ERROR;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
needFurtherInterpret = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
} // end case SCSI_ADSENSE_LUN_COMMUNICATION
|
|
|
|
case SCSI_ADSENSE_ILLEGAL_BLOCK:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Illegal block address\n"));
|
|
*Status = STATUS_NONEXISTENT_SECTOR;
|
|
*Retry = FALSE;
|
|
break;
|
|
}
|
|
|
|
case SCSI_ADSENSE_INVALID_LUN:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Invalid LUN\n"));
|
|
*Status = STATUS_NO_SUCH_DEVICE;
|
|
*Retry = FALSE;
|
|
break;
|
|
}
|
|
|
|
case SCSI_ADSENSE_COPY_PROTECTION_FAILURE:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Retry = FALSE;
|
|
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Key - Copy protection failure\n"));
|
|
|
|
switch (AdditionalSenseCodeQual)
|
|
{
|
|
case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Authentication failure\n"));
|
|
*Status = STATUS_CSS_AUTHENTICATION_FAILURE;
|
|
break;
|
|
case SCSI_SENSEQ_KEY_NOT_PRESENT:
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Key not present\n"));
|
|
*Status = STATUS_CSS_KEY_NOT_PRESENT;
|
|
break;
|
|
case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Key not established\n"));
|
|
*Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
|
|
break;
|
|
case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Read of scrambled sector w/o authentication\n"));
|
|
*Status = STATUS_CSS_SCRAMBLED_SECTOR;
|
|
break;
|
|
case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Media region does not logical unit region\n"));
|
|
*Status = STATUS_CSS_REGION_MISMATCH;
|
|
break;
|
|
case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Region set error -- region may be permanent\n"));
|
|
*Status = STATUS_CSS_RESETS_EXHAUSTED;
|
|
break;
|
|
|
|
default:
|
|
*Status = STATUS_COPY_PROTECTION_FAILURE;
|
|
break;
|
|
} // end switch of ASCQ for COPY_PROTECTION_FAILURE
|
|
|
|
break;
|
|
}
|
|
|
|
case SCSI_ADSENSE_INVALID_CDB:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Key - Invalid CDB\n"));
|
|
|
|
*Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
*Retry = FALSE;
|
|
|
|
// Note: the retry interval is not typically used.
|
|
// it is set here only because a ClassErrorHandler
|
|
// cannot set the RetryIntervalInSeconds, and the error may
|
|
// require a few commands to be sent to clear whatever
|
|
// caused this condition (i.e. disk clears the write
|
|
// cache, requiring at least two commands)
|
|
//
|
|
// hopefully, this shortcoming can be changed for blackcomb.
|
|
*RetryIntervalInSeconds = 3;
|
|
|
|
break;
|
|
}
|
|
|
|
case SCSI_ADSENSE_MEDIUM_CHANGED:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
*RetryIntervalInSeconds = 0;
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Media changed\n"));
|
|
|
|
DeviceSetMediaChangeStateEx(DeviceExtension, MediaPresent, NULL);
|
|
|
|
// special process for Media Change
|
|
if (IsVolumeMounted(DeviceExtension->DeviceObject))
|
|
{
|
|
// Set bit to indicate that media may have changed and volume needs verification.
|
|
SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
|
|
|
|
*Status = STATUS_VERIFY_REQUIRED;
|
|
*Retry = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SCSI_ADSENSE_OPERATOR_REQUEST:
|
|
{
|
|
switch (AdditionalSenseCodeQual)
|
|
{
|
|
case SCSI_SENSEQ_MEDIUM_REMOVAL:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
*RetryIntervalInSeconds = 0;
|
|
|
|
InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Ejection request received!\n"));
|
|
//Send eject notification.
|
|
DeviceSendNotification(DeviceExtension,
|
|
&GUID_IO_MEDIA_EJECT_REQUEST,
|
|
0,
|
|
NULL);
|
|
// special process for Media Change
|
|
if (IsVolumeMounted(DeviceExtension->DeviceObject))
|
|
{
|
|
// Set bit to indicate that media may have changed and volume needs verification.
|
|
SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
|
|
|
|
*Status = STATUS_VERIFY_REQUIRED;
|
|
*Retry = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
needFurtherInterpret = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
*RetryIntervalInSeconds = 5;
|
|
|
|
InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
|
|
|
|
// Device information has changed, we need to rescan the
|
|
// bus for changed information such as the capacity.
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Device information changed. Invalidate the bus\n"));
|
|
|
|
IoInvalidateDeviceRelations(DeviceExtension->LowerPdo, BusRelations);
|
|
|
|
// special process for Media Change
|
|
if (IsVolumeMounted(DeviceExtension->DeviceObject))
|
|
{
|
|
// Set bit to indicate that media may have changed and volume needs verification.
|
|
SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
|
|
|
|
*Status = STATUS_VERIFY_REQUIRED;
|
|
*Retry = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
}
|
|
break;
|
|
} //end Case SCSI_ADSENSE_OPERATING_CONDITIONS_CHANGED
|
|
|
|
|
|
case SCSI_ADSENSE_REC_DATA_NOECC:
|
|
case SCSI_ADSENSE_REC_DATA_ECC:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Status = STATUS_SUCCESS;
|
|
*Retry = FALSE;
|
|
LogContext->LogError = TRUE;
|
|
LogContext->UniqueErrorValue = 258;
|
|
LogContext->ErrorCode = IO_RECOVERED_VIA_ECC;
|
|
|
|
if (senseBuffer->IncorrectLength)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Incorrect length detected.\n"));
|
|
*Status = STATUS_INVALID_BLOCK_LENGTH ;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED:
|
|
{
|
|
UCHAR wmiEventData[sizeof(ULONG)+sizeof(UCHAR)] = {0};
|
|
|
|
*((PULONG)wmiEventData) = sizeof(UCHAR);
|
|
wmiEventData[sizeof(ULONG)] = AdditionalSenseCodeQual;
|
|
|
|
needFurtherInterpret = FALSE;
|
|
|
|
// Don't log another eventlog if we have already logged once
|
|
// NOTE: this should have been interlocked, but the structure
|
|
// was publicly defined to use a BOOLEAN (char). Since
|
|
// media only reports these errors once per X minutes,
|
|
// the potential race condition is nearly non-existant.
|
|
// the worst case is duplicate log entries, so ignore.
|
|
|
|
*Status = STATUS_SUCCESS;
|
|
*Retry = FALSE;
|
|
LogContext->UniqueErrorValue = 258;
|
|
LogContext->LogError = TRUE;
|
|
LogContext->ErrorCode = IO_WRN_FAILURE_PREDICTED;
|
|
|
|
if (senseBuffer->IncorrectLength)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretByAdditionalSenseCode: Incorrect length detected.\n"));
|
|
*Status = STATUS_INVALID_BLOCK_LENGTH ;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case 0x57:
|
|
{
|
|
// UNABLE_TO_RECOVER_TABLE_OF_CONTENTS
|
|
// the Matshita CR-585 returns this for all read commands
|
|
// on blank CD-R and CD-RW media, and we need to handle
|
|
// this for READ_CD detection ability.
|
|
switch (AdditionalSenseCodeQual)
|
|
{
|
|
case 0x00:
|
|
{
|
|
needFurtherInterpret = FALSE;
|
|
|
|
*Status = STATUS_UNRECOGNIZED_MEDIA;
|
|
*Retry = FALSE;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
needFurtherInterpret = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
} //end case Matshita specific error 0x57
|
|
|
|
default:
|
|
{
|
|
needFurtherInterpret = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return needFurtherInterpret;
|
|
}
|
|
|
|
VOID
|
|
SenseInfoInterpretBySenseKey(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ PSENSE_DATA SenseData,
|
|
_In_ UCHAR SenseKey,
|
|
_Inout_ NTSTATUS* Status,
|
|
_Inout_ BOOLEAN* Retry,
|
|
_Out_ _Deref_out_range_(0,100) ULONG* RetryIntervalInSeconds,
|
|
_Inout_ PERROR_LOG_CONTEXT LogContext
|
|
)
|
|
{
|
|
// set default values for retry fields.
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
*RetryIntervalInSeconds = 0;
|
|
|
|
switch (SenseKey)
|
|
{
|
|
case SCSI_SENSE_NOT_READY:
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: Key - Not Ready (bad block)\n"));
|
|
|
|
*Status = STATUS_DEVICE_NOT_READY;
|
|
*Retry = TRUE;
|
|
|
|
// for unprocessed "not ready" codes, retry the command immediately.
|
|
*RetryIntervalInSeconds = 0;
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSE_DATA_PROTECT:
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: Key - Media write protected\n"));
|
|
*Status = STATUS_MEDIA_WRITE_PROTECTED;
|
|
*Retry = FALSE;
|
|
break;
|
|
}
|
|
|
|
case SCSI_SENSE_MEDIUM_ERROR:
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: Key - Medium Error (bad block)\n"));
|
|
|
|
*Status = STATUS_DEVICE_DATA_ERROR;
|
|
*Retry = FALSE;
|
|
LogContext->LogError = TRUE;
|
|
LogContext->UniqueErrorValue = 256;
|
|
LogContext->ErrorCode = IO_ERR_BAD_BLOCK;
|
|
|
|
break;
|
|
} // end SCSI_SENSE_MEDIUM_ERROR
|
|
|
|
case SCSI_SENSE_HARDWARE_ERROR:
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: Key - Hardware error\n"));
|
|
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
LogContext->LogError = TRUE;
|
|
LogContext->UniqueErrorValue = 257;
|
|
LogContext->ErrorCode = IO_ERR_CONTROLLER_ERROR;
|
|
|
|
break;
|
|
} // end SCSI_SENSE_HARDWARE_ERROR
|
|
|
|
case SCSI_SENSE_ILLEGAL_REQUEST:
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: Key - Illegal SCSI request\n"));
|
|
*Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
*Retry = FALSE;
|
|
|
|
break;
|
|
} // end SCSI_SENSE_ILLEGAL_REQUEST
|
|
|
|
case SCSI_SENSE_UNIT_ATTENTION:
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: Key - Unit Attention\n"));
|
|
|
|
// A media change may have occured so increment the change
|
|
// count for the physical device
|
|
InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
|
|
|
|
if (IsVolumeMounted(DeviceExtension->DeviceObject))
|
|
{
|
|
// Set bit to indicate that media may have changed
|
|
// and volume needs verification.
|
|
SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
|
|
|
|
*Status = STATUS_VERIFY_REQUIRED;
|
|
*Retry = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
}
|
|
|
|
break;
|
|
|
|
} // end SCSI_SENSE_UNIT_ATTENTION
|
|
|
|
case SCSI_SENSE_ABORTED_COMMAND:
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: Command aborted\n"));
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
*RetryIntervalInSeconds = 1;
|
|
break;
|
|
} // end SCSI_SENSE_ABORTED_COMMAND
|
|
|
|
case SCSI_SENSE_BLANK_CHECK:
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: Media blank check\n"));
|
|
*Retry = FALSE;
|
|
*Status = STATUS_NO_DATA_DETECTED;
|
|
break;
|
|
} // end SCSI_SENSE_BLANK_CHECK
|
|
|
|
case SCSI_SENSE_RECOVERED_ERROR:
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: Recovered error\n"));
|
|
*Status = STATUS_SUCCESS;
|
|
*Retry = FALSE;
|
|
LogContext->LogError = TRUE;
|
|
LogContext->UniqueErrorValue = 258;
|
|
LogContext->ErrorCode = IO_ERR_CONTROLLER_ERROR;
|
|
|
|
if (SenseData->IncorrectLength)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: Incorrect length detected.\n"));
|
|
*Status = STATUS_INVALID_BLOCK_LENGTH ;
|
|
}
|
|
|
|
break;
|
|
} // end SCSI_SENSE_RECOVERED_ERROR
|
|
|
|
case SCSI_SENSE_NO_SENSE:
|
|
{
|
|
// Check other indicators.
|
|
if (SenseData->IncorrectLength)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: Incorrect length detected.\n"));
|
|
*Status = STATUS_INVALID_BLOCK_LENGTH ;
|
|
*Retry = FALSE;
|
|
}
|
|
else
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: No specific sense key\n"));
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
}
|
|
|
|
break;
|
|
} // end SCSI_SENSE_NO_SENSE
|
|
|
|
default:
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretBySenseKey: Unrecognized sense code\n"));
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
*RetryIntervalInSeconds = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
} // end switch (SenseKey)
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
SenseInfoInterpretBySrbStatus(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb,
|
|
_In_ ULONG RetriedCount,
|
|
_Inout_ NTSTATUS* Status,
|
|
_Inout_ BOOLEAN* Retry,
|
|
_Out_ _Deref_out_range_(0,100) ULONG* RetryIntervalInSeconds,
|
|
_Inout_ PERROR_LOG_CONTEXT LogContext
|
|
)
|
|
{
|
|
BOOLEAN incrementErrorCount = FALSE;
|
|
|
|
// set default values for retry fields.
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
*RetryIntervalInSeconds = 0;
|
|
|
|
switch (SRB_STATUS(Srb->SrbStatus))
|
|
{
|
|
case SRB_STATUS_INVALID_LUN:
|
|
case SRB_STATUS_INVALID_TARGET_ID:
|
|
case SRB_STATUS_NO_DEVICE:
|
|
case SRB_STATUS_NO_HBA:
|
|
case SRB_STATUS_INVALID_PATH_ID:
|
|
{
|
|
*Status = STATUS_NO_SUCH_DEVICE;
|
|
*Retry = FALSE;
|
|
break;
|
|
}
|
|
|
|
case SRB_STATUS_COMMAND_TIMEOUT:
|
|
case SRB_STATUS_TIMEOUT:
|
|
{
|
|
// Update the error count for the device.
|
|
*Status = STATUS_IO_TIMEOUT;
|
|
*Retry = TRUE;
|
|
*RetryIntervalInSeconds = 0;
|
|
incrementErrorCount = TRUE;
|
|
break;
|
|
}
|
|
|
|
case SRB_STATUS_ABORTED:
|
|
{
|
|
// Update the error count for the device.
|
|
*Status = STATUS_IO_TIMEOUT;
|
|
*Retry = TRUE;
|
|
*RetryIntervalInSeconds = 1;
|
|
incrementErrorCount = TRUE;
|
|
break;
|
|
}
|
|
|
|
case SRB_STATUS_SELECTION_TIMEOUT:
|
|
{
|
|
*Status = STATUS_DEVICE_NOT_CONNECTED;
|
|
*Retry = FALSE;
|
|
*RetryIntervalInSeconds = 2;
|
|
LogContext->LogError = TRUE;
|
|
LogContext->ErrorCode = IO_ERR_NOT_READY;
|
|
LogContext->UniqueErrorValue = 260;
|
|
break;
|
|
}
|
|
|
|
case SRB_STATUS_DATA_OVERRUN:
|
|
{
|
|
*Status = STATUS_DATA_OVERRUN;
|
|
*Retry = FALSE;
|
|
break;
|
|
}
|
|
|
|
case SRB_STATUS_PHASE_SEQUENCE_FAILURE:
|
|
{
|
|
// Update the error count for the device.
|
|
incrementErrorCount = TRUE;
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
|
|
// If there was phase sequence error then limit the number of retries.
|
|
*Retry = (RetriedCount <= 1);
|
|
|
|
break;
|
|
}
|
|
|
|
case SRB_STATUS_REQUEST_FLUSHED:
|
|
{
|
|
// If the status needs verification bit is set. Then set
|
|
// the status to need verification and no retry; otherwise,
|
|
// just retry the request.
|
|
if (TEST_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME))
|
|
{
|
|
*Status = STATUS_VERIFY_REQUIRED;
|
|
*Retry = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SRB_STATUS_INVALID_REQUEST:
|
|
{
|
|
*Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
*Retry = FALSE;
|
|
break;
|
|
}
|
|
|
|
case SRB_STATUS_UNEXPECTED_BUS_FREE:
|
|
case SRB_STATUS_PARITY_ERROR:
|
|
// Update the error count for the device and fall through to below
|
|
incrementErrorCount = TRUE;
|
|
|
|
case SRB_STATUS_BUS_RESET:
|
|
{
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
break;
|
|
}
|
|
|
|
case SRB_STATUS_ERROR:
|
|
{
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
|
|
if (Srb->ScsiStatus == 0)
|
|
{
|
|
// This is some strange return code. Update the error
|
|
// count for the device.
|
|
incrementErrorCount = TRUE;
|
|
}
|
|
|
|
if (Srb->ScsiStatus == SCSISTAT_BUSY)
|
|
{
|
|
*Status = STATUS_DEVICE_NOT_READY;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
LogContext->LogError = TRUE;
|
|
LogContext->ErrorCode = IO_ERR_CONTROLLER_ERROR;
|
|
LogContext->UniqueErrorValue = 259;
|
|
LogContext->ErrorUnhandled = TRUE;
|
|
break;
|
|
}
|
|
|
|
} //end of (SRB_STATUS(Srb->SrbStatus))
|
|
|
|
if (incrementErrorCount)
|
|
{
|
|
// if any error count occurred, delay the retry of this io by
|
|
// at least one second, if caller supports it.
|
|
if (*RetryIntervalInSeconds == 0)
|
|
{
|
|
*RetryIntervalInSeconds = 1;
|
|
}
|
|
|
|
DevicePerfIncrementErrorCount(DeviceExtension);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
SenseInfoLogError(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb,
|
|
_In_ UCHAR MajorFunctionCode,
|
|
_In_ ULONG IoControlCode,
|
|
_In_ ULONG RetriedCount,
|
|
_In_ NTSTATUS* Status,
|
|
_In_ BOOLEAN* Retry,
|
|
_Inout_ PERROR_LOG_CONTEXT LogContext
|
|
)
|
|
{
|
|
// Always log the error in our internal log.
|
|
// If logError is set, also log the error in the system log.
|
|
PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
|
|
ULONG totalSize = 0;
|
|
ULONG senseBufferSize = 0;
|
|
IO_ERROR_LOG_PACKET staticErrLogEntry = {0};
|
|
CDROM_ERROR_LOG_DATA staticErrLogData = {0};
|
|
|
|
// Calculate the total size of the error log entry.
|
|
// add to totalSize in the order that they are used.
|
|
// the advantage to calculating all the sizes here is
|
|
// that we don't have to do a bunch of extraneous checks
|
|
// later on in this code path.
|
|
totalSize = sizeof(IO_ERROR_LOG_PACKET) // required
|
|
+ sizeof(CDROM_ERROR_LOG_DATA); // struct for ease
|
|
|
|
// also save any available extra sense data, up to the maximum errlog
|
|
// packet size . WMI should be used for real-time analysis.
|
|
// the event log should only be used for post-mortem debugging.
|
|
if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID))
|
|
{
|
|
ULONG validSenseBytes;
|
|
BOOLEAN validSense;
|
|
|
|
// make sure we can at least access the AdditionalSenseLength field
|
|
validSense = RTL_CONTAINS_FIELD(senseBuffer,
|
|
Srb->SenseInfoBufferLength,
|
|
AdditionalSenseLength);
|
|
if (validSense)
|
|
{
|
|
// if extra info exists, copy the maximum amount of available
|
|
// sense data that is safe into the the errlog.
|
|
validSenseBytes = senseBuffer->AdditionalSenseLength
|
|
+ offsetof(SENSE_DATA, AdditionalSenseLength);
|
|
|
|
// this is invalid because it causes overflow!
|
|
// whoever sent this type of request would cause
|
|
// a system crash.
|
|
NT_ASSERT(validSenseBytes < MAX_ADDITIONAL_SENSE_BYTES);
|
|
|
|
// set to save the most sense buffer possible
|
|
senseBufferSize = max(validSenseBytes, sizeof(SENSE_DATA));
|
|
senseBufferSize = min(senseBufferSize, Srb->SenseInfoBufferLength);
|
|
}
|
|
else
|
|
{
|
|
// it's smaller than required to read the total number of
|
|
// valid bytes, so just use the SenseInfoBufferLength field.
|
|
senseBufferSize = Srb->SenseInfoBufferLength;
|
|
}
|
|
|
|
// Bump totalSize by the number of extra senseBuffer bytes
|
|
// (beyond the default sense buffer within CDROM_ERROR_LOG_DATA).
|
|
// Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
|
|
if (senseBufferSize > sizeof(SENSE_DATA))
|
|
{
|
|
totalSize += senseBufferSize-sizeof(SENSE_DATA);
|
|
if (totalSize > ERROR_LOG_MAXIMUM_SIZE)
|
|
{
|
|
senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE;
|
|
totalSize = ERROR_LOG_MAXIMUM_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we've used up all of our retry attempts, set the final status to
|
|
// reflect the appropriate result.
|
|
//
|
|
// ISSUE: the test below should also check RetriedCount to determine if we will actually retry,
|
|
// but there is no easy test because we'd have to consider the original retry count
|
|
// for the op; besides, InterpretTransferPacketError sometimes ignores the retry
|
|
// decision returned by this function. So just ErrorRetried to be true in the majority case.
|
|
//
|
|
if (*Retry)
|
|
{
|
|
staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
|
|
staticErrLogData.ErrorRetried = TRUE;
|
|
}
|
|
else
|
|
{
|
|
staticErrLogEntry.FinalStatus = *Status;
|
|
}
|
|
|
|
// Don't log generic IO_WARNING_PAGING_FAILURE message if either the
|
|
// I/O is retried, or it completed successfully.
|
|
if ((LogContext->ErrorCode == IO_WARNING_PAGING_FAILURE) &&
|
|
(*Retry || NT_SUCCESS(*Status)) )
|
|
{
|
|
LogContext->LogError = FALSE;
|
|
}
|
|
|
|
if (TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING))
|
|
{
|
|
staticErrLogData.ErrorPaging = TRUE;
|
|
}
|
|
|
|
staticErrLogData.ErrorUnhandled = LogContext->ErrorUnhandled;
|
|
|
|
// Calculate the device offset if there is a geometry.
|
|
staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)LogContext->BadSector;
|
|
staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)DeviceExtension->DiskGeometry.BytesPerSector;
|
|
|
|
if (LogContext->ErrorCode == -1)
|
|
{
|
|
staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR;
|
|
}
|
|
else
|
|
{
|
|
staticErrLogEntry.ErrorCode = LogContext->ErrorCode;
|
|
}
|
|
|
|
// The dump data follows the IO_ERROR_LOG_PACKET
|
|
staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET);
|
|
|
|
staticErrLogEntry.SequenceNumber = 0;
|
|
staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
|
|
staticErrLogEntry.IoControlCode = IoControlCode;
|
|
staticErrLogEntry.RetryCount = (UCHAR)RetriedCount;
|
|
staticErrLogEntry.UniqueErrorValue = LogContext->UniqueErrorValue;
|
|
|
|
KeQueryTickCount(&staticErrLogData.TickCount);
|
|
staticErrLogData.PortNumber = (ULONG)-1;
|
|
|
|
// Save the entire contents of the SRB.
|
|
staticErrLogData.Srb = *Srb;
|
|
|
|
// For our private log, save just the default length of the SENSE_DATA.
|
|
if (senseBufferSize != 0)
|
|
{
|
|
RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(SENSE_DATA)));
|
|
}
|
|
|
|
// Save the error log in our context.
|
|
// We only save the default sense buffer length.
|
|
{
|
|
KIRQL oldIrql;
|
|
KeAcquireSpinLock(&DeviceExtension->PrivateFdoData->SpinLock, &oldIrql);
|
|
DeviceExtension->PrivateFdoData->ErrorLogs[DeviceExtension->PrivateFdoData->ErrorLogNextIndex] = staticErrLogData;
|
|
DeviceExtension->PrivateFdoData->ErrorLogNextIndex++;
|
|
DeviceExtension->PrivateFdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES;
|
|
KeReleaseSpinLock(&DeviceExtension->PrivateFdoData->SpinLock, oldIrql);
|
|
}
|
|
|
|
// If logError is set, also save this log in the system's error log.
|
|
// But make sure we don't log TUR failures over and over
|
|
// (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
|
|
if (LogContext->LogError)
|
|
{
|
|
// We do not want to log certain system events repetitively
|
|
switch (((PCDB)Srb->Cdb)->CDB10.OperationCode)
|
|
{
|
|
case SCSIOP_TEST_UNIT_READY:
|
|
{
|
|
if (DeviceExtension->PrivateFdoData->LoggedTURFailureSinceLastIO)
|
|
{
|
|
LogContext->LogError = FALSE;
|
|
}
|
|
else
|
|
{
|
|
DeviceExtension->PrivateFdoData->LoggedTURFailureSinceLastIO = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SCSIOP_SYNCHRONIZE_CACHE:
|
|
{
|
|
if (DeviceExtension->PrivateFdoData->LoggedSYNCFailure)
|
|
{
|
|
LogContext->LogError = FALSE;
|
|
}
|
|
else
|
|
{
|
|
DeviceExtension->PrivateFdoData->LoggedSYNCFailure = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Do not log 5/21/00 LOGICAL BLOCK ADDRESS OUT OF RANGE if the disc is blank,
|
|
// it is known to litter the Event Log with repetitive errors
|
|
// Do not log this error for READ, as it's known that File System mount process reads different sectors from media.
|
|
if (senseBufferSize > RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCodeQualifier) &&
|
|
senseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST &&
|
|
senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_ILLEGAL_BLOCK &&
|
|
senseBuffer->AdditionalSenseCodeQualifier == 0 &&
|
|
IS_SCSIOP_READ(((PCDB)Srb->Cdb)->CDB10.OperationCode))
|
|
{
|
|
LogContext->LogError = FALSE;
|
|
}
|
|
}
|
|
|
|
// Write the error log packet to the system error logging thread.
|
|
if (LogContext->LogError)
|
|
{
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
PCDROM_ERROR_LOG_DATA errlogData;
|
|
|
|
errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(DeviceExtension->DeviceObject, (UCHAR)totalSize);
|
|
if (errorLogEntry)
|
|
{
|
|
errlogData = (PCDROM_ERROR_LOG_DATA)errorLogEntry->DumpData;
|
|
|
|
*errorLogEntry = staticErrLogEntry;
|
|
*errlogData = staticErrLogData;
|
|
|
|
// For the system log, copy as much of the sense buffer as possible.
|
|
if (senseBufferSize != 0)
|
|
{
|
|
RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
|
|
}
|
|
|
|
// errorLogEntry - It will be freed by the kernel.
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
SenseInfoInterpretRefineByScsiCommand(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb,
|
|
_In_ ULONG RetriedCount,
|
|
_In_ LONGLONG Total100nsSinceFirstSend,
|
|
_In_ BOOLEAN OverrideVerifyVolume,
|
|
_Inout_ BOOLEAN* Retry,
|
|
_Inout_ NTSTATUS* Status,
|
|
_Inout_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
|
|
LONGLONG* RetryIntervalIn100ns
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Based on SCSI command, modify the interpretion result.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension.
|
|
Srb - Supplies the scsi request block which failed.
|
|
RetriedCount - retried count.
|
|
Total100nsUnitsSinceFirstSend - time spent after the request was sent down first time.
|
|
OverrideVerifyVolume - should override verify volume request.
|
|
|
|
Return Value:
|
|
|
|
Retry - the reques should be retried or not.
|
|
Status - Returns the status for the request.
|
|
RetryInterval - waiting time (in 100ns) before the request should be retried.
|
|
Zero indicates the request should be immediately retried.
|
|
|
|
--*/
|
|
{
|
|
UCHAR const opCode = Srb->Cdb[0];
|
|
CDB const* cdb = (CDB const*)(Srb->Cdb);
|
|
PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
|
|
|
|
if (opCode == SCSIOP_MEDIUM_REMOVAL)
|
|
{
|
|
if (( cdb->AsByte[1] == 0) &&
|
|
( cdb->AsByte[2] == 0) &&
|
|
( cdb->AsByte[3] == 0) &&
|
|
((cdb->AsByte[4] & 0xFC) == 0)
|
|
)
|
|
{
|
|
// byte[4] == 0x3 or byte[4] == 0x1 == UNLOCK OF MEDIA
|
|
if ((cdb->AsByte[4] & 0x01) == 0)
|
|
{
|
|
if (RetriedCount < TOTAL_COUNT_RETRY_DEFAULT)
|
|
{
|
|
// keep retrying unlock operation for several times
|
|
*Retry = TRUE;
|
|
}
|
|
}
|
|
else // LOCK REQUEST
|
|
{
|
|
// do not retry LOCK requests more than once (per CLASSPNP code)
|
|
if (RetriedCount > TOTAL_COUNT_RETRY_LOCK_MEDIA)
|
|
{
|
|
*Retry = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// want a minimum time to retry of 2 seconds
|
|
if ((*Status == STATUS_DEVICE_NOT_READY) &&
|
|
(senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY))
|
|
{
|
|
*RetryIntervalIn100ns = max(*RetryIntervalIn100ns, SECONDS_TO_100NS_UNITS(2));
|
|
}
|
|
else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)
|
|
{
|
|
*RetryIntervalIn100ns = max(*RetryIntervalIn100ns, SECONDS_TO_100NS_UNITS(2));
|
|
}
|
|
}
|
|
else if ((opCode == SCSIOP_MODE_SENSE) || (opCode == SCSIOP_MODE_SENSE10))
|
|
{
|
|
// want a minimum time to retry of 2 seconds
|
|
if ((*Status == STATUS_DEVICE_NOT_READY) &&
|
|
(senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY))
|
|
{
|
|
*RetryIntervalIn100ns = max(*RetryIntervalIn100ns, SECONDS_TO_100NS_UNITS(2));
|
|
}
|
|
else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)
|
|
{
|
|
*RetryIntervalIn100ns = max(*RetryIntervalIn100ns, SECONDS_TO_100NS_UNITS(2));
|
|
}
|
|
|
|
// Want to ignore a STATUS_VERIFY_REQUIRED error because it either
|
|
// doesn't make sense or is required to satisfy the VERIFY.
|
|
if (*Status == STATUS_VERIFY_REQUIRED)
|
|
{
|
|
*Retry = TRUE;
|
|
}
|
|
else if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN)
|
|
{
|
|
/*
|
|
* This is a HACK.
|
|
* Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
|
|
* underrun (i.e. success, and the buffer is longer than needed).
|
|
* So treat this as a success.
|
|
* When the caller of this function sees that the status was changed to success,
|
|
* it will add the transferred length to the original irp.
|
|
*/
|
|
*Status = STATUS_SUCCESS;
|
|
*Retry = FALSE;
|
|
}
|
|
|
|
// limit the count of retries
|
|
if (RetriedCount > TOTAL_COUNT_RETRY_MODESENSE)
|
|
{
|
|
*Retry = FALSE;
|
|
}
|
|
}
|
|
else if ((opCode == SCSIOP_READ_CAPACITY) || (opCode == SCSIOP_READ_CAPACITY16))
|
|
{
|
|
// Want to ignore a STATUS_VERIFY_REQUIRED error because it either
|
|
// doesn't make sense or is required to satisfy the VERIFY.
|
|
if (*Status == STATUS_VERIFY_REQUIRED)
|
|
{
|
|
*Retry = TRUE;
|
|
}
|
|
|
|
if (RetriedCount > TOTAL_COUNT_RETRY_READ_CAPACITY)
|
|
{
|
|
*Retry = FALSE;
|
|
}
|
|
}
|
|
else if ((opCode == SCSIOP_RESERVE_UNIT) || (opCode == SCSIOP_RELEASE_UNIT))
|
|
{
|
|
// The RESERVE(6) / RELEASE(6) commands are optional.
|
|
// So if they aren't supported, try the 10-byte equivalents
|
|
if (*Status == STATUS_INVALID_DEVICE_REQUEST)
|
|
{
|
|
PCDB tempCdb = (PCDB)Srb->Cdb;
|
|
|
|
Srb->CdbLength = 10;
|
|
tempCdb->CDB10.OperationCode = (tempCdb->CDB6GENERIC.OperationCode == SCSIOP_RESERVE_UNIT)
|
|
? SCSIOP_RESERVE_UNIT10
|
|
: SCSIOP_RELEASE_UNIT10;
|
|
|
|
SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_RESERVE6);
|
|
*Retry = TRUE;
|
|
}
|
|
}
|
|
else if (IS_SCSIOP_READWRITE(opCode))
|
|
{
|
|
// Retry if still verifying a (possibly) reloaded disk/cdrom.
|
|
if (OverrideVerifyVolume && (*Status == STATUS_VERIFY_REQUIRED))
|
|
{
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
}
|
|
|
|
// Special case for streaming READ/WRITE commands
|
|
if (((opCode == SCSIOP_READ12) && (cdb->READ12.Streaming == 1)) ||
|
|
((opCode == SCSIOP_WRITE12) && (cdb->WRITE12.Streaming == 1)))
|
|
{
|
|
// We've got a failure while performing a streaming operation and now need to guess if
|
|
// it's likely to be a permanent error because the drive does not support streaming at all
|
|
// (in which case we're going to fall back to normal reads/writes), or a transient error
|
|
// (in which case we quickly fail the request but contrinue to use streaming).
|
|
//
|
|
// We analyze the sense information to make that decision. Bus resets and device timeouts
|
|
// are treated as permanent errors, because some non-compliant devices may even hang when
|
|
// they get a command that they do not expect.
|
|
|
|
BOOLEAN disableStreaming = FALSE;
|
|
|
|
if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_TIMEOUT ||
|
|
SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_COMMAND_TIMEOUT ||
|
|
SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT ||
|
|
SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_BUS_RESET)
|
|
{
|
|
disableStreaming = TRUE;
|
|
}
|
|
else if ((senseBuffer->SenseKey &0xf) == SCSI_SENSE_UNIT_ATTENTION)
|
|
{
|
|
if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_BUS_RESET ||
|
|
senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INSUFFICIENT_TIME_FOR_OPERATION)
|
|
{
|
|
disableStreaming = TRUE;
|
|
}
|
|
}
|
|
else if ((senseBuffer->SenseKey &0xf) == SCSI_SENSE_ILLEGAL_REQUEST)
|
|
{
|
|
// LBA Out of Range is an exception, as it's more likely to be caused by
|
|
// upper layers attempting to read/write a wrong LBA.
|
|
if (senseBuffer->AdditionalSenseCode != SCSI_ADSENSE_ILLEGAL_BLOCK)
|
|
{
|
|
disableStreaming = TRUE;
|
|
}
|
|
}
|
|
|
|
if (disableStreaming)
|
|
{
|
|
// if the failure looks permanent, we disable streaming for all future reads/writes
|
|
// and retry the command immediately
|
|
SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_STREAMING);
|
|
*Retry = TRUE;
|
|
*RetryIntervalIn100ns = 0;
|
|
}
|
|
else
|
|
{
|
|
// if the failure looks transient, we simply fail the current request without retries
|
|
// to minimize the time of processing
|
|
*Retry = FALSE;
|
|
}
|
|
}
|
|
|
|
// Special-case handling of READ/WRITE commands. These commands now have a 120 second timeout,
|
|
// but the preferred behavior (and that taken by many drives) is to immediately report 2/4/x
|
|
// on OPC and similar commands. Thus, retries must occur for at least 160 seconds
|
|
// (120 seconds + four 10 second retries) as a conservative guess.
|
|
// Note: 160s retry time is also a result of discussion with OEMs for case of 2/4/7 and 2/4/8.
|
|
if (*Retry)
|
|
{
|
|
if ((Total100nsSinceFirstSend < 0) ||
|
|
(((senseBuffer->SenseKey &0xf) == SCSI_SENSE_HARDWARE_ERROR) && (senseBuffer->AdditionalSenseCode == 0x09)))
|
|
{
|
|
// time information is not valid. use default retry count.
|
|
// or if it's SERVO FAILURE, use retry count instead of 160s retry.
|
|
*Retry = (RetriedCount <= TOTAL_COUNT_RETRY_DEFAULT);
|
|
}
|
|
else if (Total100nsSinceFirstSend > SECONDS_TO_100NS_UNITS(TOTAL_SECONDS_RETRY_TIME_WRITE))
|
|
{
|
|
*Retry = FALSE;
|
|
}
|
|
|
|
// How long should we request a delay for during writing? This depends entirely on
|
|
// the current write speed of the drive. If we request retries too quickly,
|
|
// we can overload the processor on the drive (resulting in garbage being written),
|
|
// but too slowly results in lesser performance.
|
|
//
|
|
*RetryIntervalIn100ns = DeviceExtension->DeviceAdditionalData.ReadWriteRetryDelay100nsUnits;
|
|
|
|
} // end retry for 160 seconds modification
|
|
}
|
|
else if (opCode == SCSIOP_GET_PERFORMANCE)
|
|
{
|
|
if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN)
|
|
{
|
|
// This is a HACK.
|
|
// Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
|
|
// underrun (i.e. success, and the buffer is longer than needed).
|
|
// So treat this as a success.
|
|
// When the caller of this function sees that the status was changed to success,
|
|
// it will add the transferred length to the original irp.
|
|
*Status = STATUS_SUCCESS;
|
|
*Retry = FALSE;
|
|
}
|
|
|
|
if ((Srb->SenseInfoBufferLength < RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA,AdditionalSenseCodeQualifier)) ||
|
|
!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID))
|
|
{
|
|
// If get configuration command is failing and if the request type is TYPE ONE
|
|
// then most likely the device does not support this request type. Set the
|
|
// flag so that the TYPE ONE requests will be tried as TYPE ALL requets.
|
|
if ((SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) &&
|
|
(SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_DATA_OVERRUN) &&
|
|
(((PCDB)Srb->Cdb)->GET_CONFIGURATION.RequestType == SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE))
|
|
{
|
|
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"TYPE ONE GetConfiguration failed. Set hack flag and retry.\n"));
|
|
SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG);
|
|
*Retry = TRUE;
|
|
}
|
|
}
|
|
|
|
// limit retries to GET_PERFORMANCE commands to default retry count
|
|
if (RetriedCount > TOTAL_COUNT_RETRY_DEFAULT)
|
|
{
|
|
*Retry = FALSE;
|
|
}
|
|
}
|
|
else // default handler -- checks for retry count only.
|
|
{
|
|
if (RetriedCount > TOTAL_COUNT_RETRY_DEFAULT)
|
|
{
|
|
*Retry = FALSE;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SenseInfoInterpretRefineByIoControl(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ ULONG IoControlCode,
|
|
_In_ BOOLEAN OverrideVerifyVolume,
|
|
_Inout_ BOOLEAN* Retry,
|
|
_Inout_ NTSTATUS* Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Based on IOCTL code, modify the interpretion result.
|
|
|
|
Arguments:
|
|
|
|
Device - Supplies the device object associated with this request.
|
|
OriginalRequest - the irp that error occurs on.
|
|
Srb - Supplies the scsi request block which failed.
|
|
MajorFunctionCode - Supplies the function code to be used for logging.
|
|
IoDeviceCode - Supplies the device code to be used for logging.
|
|
PreviousRetryCount - retried count.
|
|
RequestHistory_DoNotUse - the history list
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN TRUE: Drivers should retry this request.
|
|
FALSE: Drivers should not retry this request.
|
|
Status - Returns the status for the request.
|
|
RetryInterval - Number of seconds before the request should be retried.
|
|
Zero indicates the request should be immediately retried.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if ((IoControlCode == IOCTL_CDROM_GET_LAST_SESSION) ||
|
|
(IoControlCode == IOCTL_CDROM_READ_TOC) ||
|
|
(IoControlCode == IOCTL_CDROM_READ_TOC_EX) ||
|
|
(IoControlCode == IOCTL_CDROM_GET_CONFIGURATION)||
|
|
(IoControlCode == IOCTL_CDROM_GET_VOLUME))
|
|
{
|
|
if (*Status == STATUS_DATA_OVERRUN)
|
|
{
|
|
*Status = STATUS_SUCCESS;
|
|
*Retry = FALSE;
|
|
}
|
|
}
|
|
|
|
if (IoControlCode == IOCTL_CDROM_READ_Q_CHANNEL)
|
|
{
|
|
PLAY_ACTIVE(DeviceExtension) = FALSE;
|
|
}
|
|
|
|
// If the status is verified required and the this request
|
|
// should bypass verify required then retry the request.
|
|
if (OverrideVerifyVolume && (*Status == STATUS_VERIFY_REQUIRED))
|
|
{
|
|
// note: status gets overwritten here
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*Retry = TRUE;
|
|
|
|
if ((IoControlCode == IOCTL_CDROM_CHECK_VERIFY) ||
|
|
(IoControlCode == IOCTL_STORAGE_CHECK_VERIFY) ||
|
|
(IoControlCode == IOCTL_STORAGE_CHECK_VERIFY2) ||
|
|
(IoControlCode == IOCTL_DISK_CHECK_VERIFY)
|
|
)
|
|
{
|
|
// Update the geometry information, as the media could have changed.
|
|
(VOID) MediaReadCapacity(DeviceExtension->Device);
|
|
} // end of ioctls to update capacity
|
|
}
|
|
|
|
if (!NT_SUCCESS(*Status) && (IoControlCode == IOCTL_CDROM_SET_SPEED))
|
|
{
|
|
// If set speed request fails then we should disable the restore speed option.
|
|
// Otherwise we will try to restore to default speed on next media change,
|
|
// if requested by the caller.
|
|
DeviceExtension->DeviceAdditionalData.RestoreDefaults = FALSE;
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "Disable restore default\n"));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOLEAN
|
|
SenseInfoInterpret(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ WDFREQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb,
|
|
_In_ ULONG RetriedCount,
|
|
_Out_ NTSTATUS* Status,
|
|
_Out_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
|
|
LONGLONG* RetryIntervalIn100ns
|
|
)
|
|
/*++
|
|
|
|
SenseInfoInterpret()
|
|
|
|
Routine Description:
|
|
|
|
This routine interprets the data returned from the SCSI request sense.
|
|
It determines the status to return in the IRP
|
|
and whether this request can be retried.
|
|
|
|
Arguments:
|
|
|
|
Device - Supplies the device object associated with this request.
|
|
Srb - Supplies the scsi request block which failed.
|
|
MajorFunctionCode - Supplies the function code to be used for logging.
|
|
IoDeviceCode - Supplies the device code to be used for logging.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN TRUE: Drivers should retry this request.
|
|
FALSE: Drivers should not retry this request.
|
|
Status - Returns the status for the request.
|
|
RetryInterval - Number of seconds before the request should be retried.
|
|
Zero indicates the request should be immediately retried.
|
|
|
|
--*/
|
|
{
|
|
ULONG retryIntervalInSeconds = 0;
|
|
BOOLEAN retry = TRUE;
|
|
PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
|
|
ULONG readSector = 0;
|
|
ERROR_LOG_CONTEXT logContext;
|
|
|
|
UCHAR majorFunctionCode = 0;
|
|
ULONG ioControlCode = 0;
|
|
BOOLEAN overrideVerifyVolume = FALSE;
|
|
ULONGLONG total100nsSinceFirstSend = 0;
|
|
PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
|
|
|
|
//
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
|
|
RtlZeroMemory(&logContext, sizeof(ERROR_LOG_CONTEXT));
|
|
logContext.ErrorCode = -1;
|
|
|
|
// Get Original Request related information
|
|
SenseInfoRequestGetInformation(Request,
|
|
&majorFunctionCode,
|
|
&ioControlCode,
|
|
&overrideVerifyVolume,
|
|
&total100nsSinceFirstSend);
|
|
|
|
if(TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING))
|
|
{
|
|
// Log anything remotely incorrect about paging i/o
|
|
logContext.LogError = TRUE;
|
|
logContext.UniqueErrorValue = 301;
|
|
logContext.ErrorCode = IO_WARNING_PAGING_FAILURE;
|
|
}
|
|
|
|
// must handle the SRB_STATUS_INTERNAL_ERROR case first,
|
|
// as it has all the flags set.
|
|
if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR)
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpret: Internal Error code is %x\n",
|
|
Srb->InternalStatus));
|
|
|
|
retry = FALSE;
|
|
*Status = Srb->InternalStatus;
|
|
}
|
|
else if (Srb->ScsiStatus == SCSISTAT_RESERVATION_CONFLICT)
|
|
{
|
|
retry = FALSE;
|
|
*Status = STATUS_DEVICE_BUSY;
|
|
logContext.LogError = FALSE;
|
|
}
|
|
else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
|
|
(Srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength)))
|
|
{
|
|
UCHAR senseKey = (UCHAR)(senseBuffer->SenseKey & 0x0f);
|
|
UCHAR additionalSenseCode = 0;
|
|
UCHAR additionalSenseCodeQual = 0;
|
|
|
|
// Zero the additional sense code and additional sense code qualifier
|
|
// if they were not returned by the device.
|
|
readSector = senseBuffer->AdditionalSenseLength + offsetof(SENSE_DATA, AdditionalSenseLength);
|
|
if (readSector > Srb->SenseInfoBufferLength)
|
|
{
|
|
readSector = Srb->SenseInfoBufferLength;
|
|
}
|
|
|
|
additionalSenseCode = (readSector >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode)) ?
|
|
senseBuffer->AdditionalSenseCode : 0;
|
|
additionalSenseCodeQual = (readSector >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCodeQualifier)) ?
|
|
senseBuffer->AdditionalSenseCodeQualifier : 0;
|
|
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"SCSI Error - \n"
|
|
"\tcdb: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
|
|
"\tsrb status: %X; sense: %02X/%02X/%02X; Retried count: %d\n\n",
|
|
Srb->Cdb[0], Srb->Cdb[1], Srb->Cdb[2], Srb->Cdb[3], Srb->Cdb[4], Srb->Cdb[5],
|
|
Srb->Cdb[6], Srb->Cdb[7], Srb->Cdb[8], Srb->Cdb[9], Srb->Cdb[10], Srb->Cdb[11],
|
|
Srb->Cdb[12], Srb->Cdb[13], Srb->Cdb[14], Srb->Cdb[15],
|
|
SRB_STATUS(Srb->SrbStatus),
|
|
senseKey,
|
|
additionalSenseCode,
|
|
additionalSenseCodeQual,
|
|
RetriedCount));
|
|
|
|
if (senseKey == SCSI_SENSE_UNIT_ATTENTION)
|
|
{
|
|
ULONG mediaChangeCount;
|
|
|
|
// A media change may have occured so increment the change count for the physical device
|
|
mediaChangeCount = InterlockedIncrement((PLONG)&DeviceExtension->MediaChangeCount);
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_MCN,
|
|
"SenseInfoInterpret: Media change count for device %d incremented to %#lx\n",
|
|
DeviceExtension->DeviceNumber, mediaChangeCount));
|
|
}
|
|
|
|
if ((zpoddInfo != NULL) &&
|
|
(((PCDB)Srb->Cdb)->CDB6GENERIC.OperationCode == SCSIOP_TEST_UNIT_READY))
|
|
{
|
|
// This sense code is in response to the Test Unit Ready sent during delayed power down
|
|
// request. Copy the sense data into the zpoddInfo structure for later processing.
|
|
zpoddInfo->SenseKey = senseKey;
|
|
zpoddInfo->AdditionalSenseCode = additionalSenseCode;
|
|
zpoddInfo->AdditionalSenseCodeQualifier = additionalSenseCodeQual;
|
|
}
|
|
|
|
// Interpret error by specific ASC & ASCQ first,
|
|
// If the error is not handled, interpret using f
|
|
{
|
|
BOOLEAN notHandled = FALSE;
|
|
notHandled = SenseInfoInterpretByAdditionalSenseCode(DeviceExtension,
|
|
Srb,
|
|
additionalSenseCode,
|
|
additionalSenseCodeQual,
|
|
Status,
|
|
&retry,
|
|
&retryIntervalInSeconds,
|
|
&logContext);
|
|
|
|
if (notHandled)
|
|
{
|
|
SenseInfoInterpretBySenseKey(DeviceExtension,
|
|
senseBuffer,
|
|
senseKey,
|
|
Status,
|
|
&retry,
|
|
&retryIntervalInSeconds,
|
|
&logContext);
|
|
}
|
|
}
|
|
|
|
// Try to determine the bad sector from the inquiry data.
|
|
if ((IS_SCSIOP_READWRITE(((PCDB)Srb->Cdb)->CDB10.OperationCode)) ||
|
|
(((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY) ||
|
|
(((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY16))
|
|
{
|
|
ULONG index;
|
|
readSector = 0;
|
|
|
|
for (index = 0; index < 4; index++)
|
|
{
|
|
logContext.BadSector = (logContext.BadSector << 8) | senseBuffer->Information[index];
|
|
}
|
|
|
|
for (index = 0; index < 4; index++)
|
|
{
|
|
readSector = (readSector << 8) | Srb->Cdb[index+2];
|
|
}
|
|
|
|
index = (((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8) |
|
|
((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb;
|
|
|
|
// Make sure the bad sector is within the read sectors.
|
|
if (!(logContext.BadSector >= readSector && logContext.BadSector < (readSector + index)))
|
|
{
|
|
logContext.BadSector = readSector;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Request sense buffer not valid. No sense information
|
|
// to pinpoint the error. Return general request fail.
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"SCSI Error - \n"
|
|
"\tcdb: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n"
|
|
"\tsrb status: %X; sense info not valid; Retried count: %d\n\n",
|
|
Srb->Cdb[0], Srb->Cdb[1], Srb->Cdb[2], Srb->Cdb[3], Srb->Cdb[4], Srb->Cdb[5],
|
|
Srb->Cdb[6], Srb->Cdb[7], Srb->Cdb[8], Srb->Cdb[9], Srb->Cdb[10], Srb->Cdb[11],
|
|
Srb->Cdb[12], Srb->Cdb[13], Srb->Cdb[14], Srb->Cdb[15],
|
|
SRB_STATUS(Srb->SrbStatus),
|
|
RetriedCount));
|
|
|
|
SenseInfoInterpretBySrbStatus(DeviceExtension,
|
|
Srb,
|
|
RetriedCount,
|
|
Status,
|
|
&retry,
|
|
&retryIntervalInSeconds,
|
|
&logContext);
|
|
}
|
|
|
|
// all functions using unit - seconds for retry Interval already be called.
|
|
*RetryIntervalIn100ns = SECONDS_TO_100NS_UNITS(retryIntervalInSeconds);
|
|
|
|
// call the device specific error handler if it has one.
|
|
// DeviceErrorHandlerForMmmc() for all MMC devices
|
|
// or DeviceErrorHandlerForHitachiGD2000() for HITACHI GD-2000, HITACHI DVD-ROM GD-2000
|
|
if (DeviceExtension->DeviceAdditionalData.ErrorHandler)
|
|
{
|
|
DeviceExtension->DeviceAdditionalData.ErrorHandler(DeviceExtension, Srb, Status, &retry);
|
|
}
|
|
|
|
// Refine retry based on SCSI command
|
|
SenseInfoInterpretRefineByScsiCommand(DeviceExtension,
|
|
Srb,
|
|
RetriedCount,
|
|
total100nsSinceFirstSend,
|
|
overrideVerifyVolume,
|
|
&retry,
|
|
Status,
|
|
RetryIntervalIn100ns);
|
|
|
|
// Refine retry based on IOCTL code.
|
|
if (majorFunctionCode == IRP_MJ_DEVICE_CONTROL)
|
|
{
|
|
SenseInfoInterpretRefineByIoControl(DeviceExtension,
|
|
ioControlCode,
|
|
overrideVerifyVolume,
|
|
&retry,
|
|
Status);
|
|
}
|
|
|
|
// LOG the error:
|
|
// Always log the error in our internal log.
|
|
// If logError is set, also log the error in the system log.
|
|
SenseInfoLogError(DeviceExtension,
|
|
Srb,
|
|
majorFunctionCode,
|
|
ioControlCode,
|
|
RetriedCount,
|
|
Status,
|
|
&retry,
|
|
&logContext);
|
|
|
|
// all process about the error done. check if the irp was cancelled.
|
|
if ((!NT_SUCCESS(*Status)) && retry)
|
|
{
|
|
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
|
|
|
|
if ((requestContext->OriginalRequest != NULL) &&
|
|
WdfRequestIsCanceled(requestContext->OriginalRequest)
|
|
)
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"Request %p was cancelled when it would have been retried\n",
|
|
requestContext->OriginalRequest));
|
|
|
|
*Status = STATUS_CANCELLED;
|
|
retry = FALSE;
|
|
*RetryIntervalIn100ns = 0;
|
|
}
|
|
}
|
|
|
|
// now, all decisions are made. display trace information.
|
|
if (retry)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"Command shall be retried in %2I64d.%03I64d seconds\n",
|
|
(*RetryIntervalIn100ns / UNIT_100NS_PER_SECOND),
|
|
(*RetryIntervalIn100ns / 10000) % 1000
|
|
));
|
|
}
|
|
else
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"Will not retry; Sense/ASC/ASCQ of %02x/%02x/%02x\n",
|
|
senseBuffer->SenseKey,
|
|
senseBuffer->AdditionalSenseCode,
|
|
senseBuffer->AdditionalSenseCodeQualifier
|
|
));
|
|
}
|
|
|
|
return retry;
|
|
|
|
} // end SenseInfoInterpret()
|
|
|
|
|
|
BOOLEAN
|
|
SenseInfoInterpretForZPODD(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb,
|
|
_Out_ NTSTATUS* Status,
|
|
_Out_ _Out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
|
|
LONGLONG* RetryIntervalIn100ns
|
|
)
|
|
/*++
|
|
|
|
SenseInfoInterpretForZPODD()
|
|
|
|
Routine Description:
|
|
|
|
This routine interprets the data returned from the SCSI request sense.
|
|
It determines the status to return in the IRP
|
|
and whether this request can be retried.
|
|
|
|
Arguments:
|
|
|
|
Device - Supplies the device object associated with this request.
|
|
Srb - Supplies the scsi request block which failed.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN TRUE: Drivers should retry this request.
|
|
FALSE: Drivers should not retry this request.
|
|
Status - Returns the status for the request.
|
|
RetryInterval - Number of seconds before the request should be retried.
|
|
Zero indicates the request should be immediately retried.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN retry = FALSE;
|
|
PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
|
|
ULONG readSector = 0;
|
|
PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
|
|
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
*RetryIntervalIn100ns = 0;
|
|
|
|
if (zpoddInfo->RetryFirstCommand != FALSE)
|
|
{
|
|
// The first command to the logical unit after power resumed will be terminated
|
|
// with CHECK CONDITION Status, 6/29/00 POWER ON, RESET, OR BUS DEVICE RESET OCCURRED
|
|
|
|
// We have observed some devices return a different sense code, and thus as long as
|
|
// the first command after power resume fails, we just retry one more time.
|
|
zpoddInfo->RetryFirstCommand = FALSE;
|
|
|
|
retry = TRUE;
|
|
}
|
|
else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
|
|
(Srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength)))
|
|
{
|
|
UCHAR senseKey = (UCHAR)(senseBuffer->SenseKey & 0x0f);
|
|
UCHAR additionalSenseCode = 0;
|
|
UCHAR additionalSenseCodeQual = 0;
|
|
|
|
// Zero the additional sense code and additional sense code qualifier
|
|
// if they were not returned by the device.
|
|
readSector = senseBuffer->AdditionalSenseLength + offsetof(SENSE_DATA, AdditionalSenseLength);
|
|
if (readSector > Srb->SenseInfoBufferLength)
|
|
{
|
|
readSector = Srb->SenseInfoBufferLength;
|
|
}
|
|
|
|
additionalSenseCode = (readSector >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode)) ?
|
|
senseBuffer->AdditionalSenseCode : 0;
|
|
additionalSenseCodeQual = (readSector >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCodeQualifier)) ?
|
|
senseBuffer->AdditionalSenseCodeQualifier : 0;
|
|
|
|
// If sense code is 2/4/1, device is becoming ready from ZPODD mode. According to Mt Fuji, device
|
|
// could take up to 800msec to be fully operational.
|
|
if ((senseKey == SCSI_SENSE_NOT_READY) &&
|
|
(additionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
|
|
(additionalSenseCodeQual == SCSI_SENSEQ_BECOMING_READY))
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"SenseInfoInterpretForZPODD: In process of becoming ready\n"));
|
|
|
|
zpoddInfo->BecomingReadyRetryCount--;
|
|
|
|
if (zpoddInfo->BecomingReadyRetryCount > 0)
|
|
{
|
|
DEVICE_EVENT_BECOMING_READY notReady = {0};
|
|
|
|
retry = TRUE;
|
|
*Status = STATUS_DEVICE_NOT_READY;
|
|
*RetryIntervalIn100ns = BECOMING_READY_RETRY_INTERNVAL_IN_100NS;
|
|
|
|
notReady.Version = 1;
|
|
notReady.Reason = 1;
|
|
notReady.Estimated100msToReady = (ULONG) *RetryIntervalIn100ns / (1000 * 1000);
|
|
DeviceSendNotification(DeviceExtension,
|
|
&GUID_IO_DEVICE_BECOMING_READY,
|
|
sizeof(DEVICE_EVENT_BECOMING_READY),
|
|
¬Ready);
|
|
}
|
|
}
|
|
}
|
|
|
|
// now, all decisions are made. display trace information.
|
|
if (retry)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
|
"Command shall be retried in %2I64d.%03I64d seconds\n",
|
|
(*RetryIntervalIn100ns / UNIT_100NS_PER_SECOND),
|
|
(*RetryIntervalIn100ns / 10000) % 1000
|
|
));
|
|
}
|
|
else
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
|
"Will not retry; Sense/ASC/ASCQ of %02x/%02x/%02x\n",
|
|
senseBuffer->SenseKey,
|
|
senseBuffer->AdditionalSenseCode,
|
|
senseBuffer->AdditionalSenseCodeQualifier
|
|
));
|
|
}
|
|
|
|
return retry;
|
|
|
|
} // end SenseInfoInterpret()
|
|
|
|
|
|
BOOLEAN
|
|
RequestSenseInfoInterpret(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ WDFREQUEST Request,
|
|
_In_ PSCSI_REQUEST_BLOCK Srb,
|
|
_In_ ULONG RetriedCount,
|
|
_Out_ NTSTATUS* Status,
|
|
_Out_opt_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
|
|
LONGLONG* RetryIntervalIn100ns
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Interpret the error, process it.
|
|
1. Release device queue if it's frozen.
|
|
2. Interpret and process the error.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies the device object associated with this request.
|
|
Request - the Request that error occurs on.
|
|
Srb - Supplies the scsi request block which failed.
|
|
RetriedCount - retried count.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN TRUE: Drivers should retry this request.
|
|
FALSE: Drivers should not retry this request.
|
|
Status - Returns the status for the request.
|
|
RetryIntervalIn100nsUnits - Number of 100ns before the request should be retried.
|
|
Zero indicates the request should be immediately retried.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN retry = FALSE;
|
|
LONGLONG retryIntervalIn100ns = 0;
|
|
PZERO_POWER_ODD_INFO zpoddInfo = DeviceExtension->ZeroPowerODDInfo;
|
|
|
|
if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS)
|
|
{
|
|
// request succeeded.
|
|
if ((zpoddInfo != NULL) &&
|
|
(zpoddInfo->BecomingReadyRetryCount > 0))
|
|
{
|
|
zpoddInfo->BecomingReadyRetryCount = 0;
|
|
}
|
|
|
|
*Status = STATUS_SUCCESS;
|
|
retry = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// request failed. We need to process the error.
|
|
|
|
// 1. Release the queue if it is frozen.
|
|
if (Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN)
|
|
{
|
|
DeviceReleaseQueue(DeviceExtension->Device);
|
|
}
|
|
|
|
if ((zpoddInfo != NULL) &&
|
|
((zpoddInfo->RetryFirstCommand != FALSE) || (zpoddInfo->BecomingReadyRetryCount > 0)))
|
|
{
|
|
retry = SenseInfoInterpretForZPODD(DeviceExtension,
|
|
Srb,
|
|
Status,
|
|
&retryIntervalIn100ns);
|
|
}
|
|
|
|
if (retry == FALSE)
|
|
{
|
|
// 2. Error Processing
|
|
if ((zpoddInfo != NULL) &&
|
|
(zpoddInfo->BecomingReadyRetryCount > 0))
|
|
{
|
|
zpoddInfo->BecomingReadyRetryCount = 0;
|
|
}
|
|
|
|
retry = SenseInfoInterpret(DeviceExtension,
|
|
Request,
|
|
Srb,
|
|
RetriedCount,
|
|
Status,
|
|
&retryIntervalIn100ns);
|
|
}
|
|
}
|
|
|
|
if (RetryIntervalIn100ns != NULL)
|
|
{
|
|
*RetryIntervalIn100ns = retryIntervalIn100ns;
|
|
}
|
|
|
|
return retry;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
RequestSenseInfoInterpretForScratchBuffer(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ ULONG RetriedCount,
|
|
_Out_ NTSTATUS* Status,
|
|
_Out_ _Deref_out_range_(0, MAXIMUM_RETRY_FOR_SINGLE_IO_IN_100NS_UNITS)
|
|
LONGLONG* RetryIntervalIn100ns
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
to analyze the error occurred and set the status, retry interval and decide to retry or not.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension
|
|
RetriedCount - already retried count.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE (should retry)
|
|
Status - NTSTATUS
|
|
RetryIntervalIn100nsUnits - retry interval
|
|
|
|
--*/
|
|
{
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse != 0);
|
|
|
|
return RequestSenseInfoInterpret(DeviceExtension,
|
|
DeviceExtension->ScratchContext.ScratchRequest,
|
|
DeviceExtension->ScratchContext.ScratchSrb,
|
|
RetriedCount,
|
|
Status,
|
|
RetryIntervalIn100ns);
|
|
}
|
|
|
|
|