1008 lines
26 KiB
C
1008 lines
26 KiB
C
/*++
|
||
|
||
Copyright (c) 1995 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
spt.c
|
||
|
||
Abstract:
|
||
|
||
A user mode library that allows simple commands to be sent to a
|
||
selected scsi device.
|
||
|
||
Environment:
|
||
|
||
User mode only
|
||
|
||
Revision History:
|
||
|
||
4/10/2000 - created
|
||
|
||
--*/
|
||
|
||
#include "sptlibp.h"
|
||
|
||
|
||
BOOL
|
||
SptUtilValidateCdbLength(
|
||
IN PCDB Cdb,
|
||
IN UCHAR CdbSize
|
||
)
|
||
{
|
||
UCHAR commandGroup = (Cdb->AsByte[0] >> 5) & 0x7;
|
||
|
||
switch (commandGroup) {
|
||
case 0:
|
||
return (CdbSize == 6);
|
||
case 1:
|
||
case 2:
|
||
return (CdbSize == 10);
|
||
case 5:
|
||
return (CdbSize == 12);
|
||
default:
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
BOOL
|
||
SptSendCdbToDevice(
|
||
IN HANDLE DeviceHandle,
|
||
IN PCDB Cdb,
|
||
IN UCHAR CdbSize,
|
||
IN PUCHAR Buffer,
|
||
IN OUT PDWORD BufferSize,
|
||
IN BOOLEAN GetDataFromDevice
|
||
)
|
||
{
|
||
return SptSendCdbToDeviceEx(DeviceHandle,
|
||
Cdb,
|
||
CdbSize,
|
||
Buffer,
|
||
BufferSize,
|
||
NULL,
|
||
0,
|
||
GetDataFromDevice,
|
||
SPT_DEFAULT_TIMEOUT
|
||
);
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
BOOL
|
||
SptSendCdbToDeviceEx(
|
||
IN HANDLE DeviceHandle,
|
||
IN PCDB Cdb,
|
||
IN UCHAR CdbSize,
|
||
IN OUT PUCHAR Buffer,
|
||
IN OUT PDWORD BufferSize,
|
||
OUT PSENSE_DATA SenseData OPTIONAL,
|
||
IN UCHAR SenseDataSize,
|
||
IN BOOLEAN GetDataFromDevice,
|
||
IN DWORD TimeOut // in seconds
|
||
)
|
||
{
|
||
PSPT_WITH_BUFFERS p;
|
||
DWORD packetSize;
|
||
DWORD returnedBytes;
|
||
BOOL returnValue;
|
||
PSENSE_DATA senseBuffer;
|
||
BOOL copyData;
|
||
|
||
if ((SenseDataSize == 0) && (SenseData != NULL)) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
if ((SenseDataSize != 0) && (SenseData == NULL)) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
if (SenseData && SenseDataSize) {
|
||
RtlZeroMemory(SenseData, SenseDataSize);
|
||
}
|
||
|
||
if (Cdb == NULL) {
|
||
// cannot send NULL cdb
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
if (CdbSize < 1 || CdbSize > 16) {
|
||
// Cdb size too large or too small for this library currently
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
if (!SptUtilValidateCdbLength(Cdb, CdbSize)) {
|
||
// OpCode Cdb->AsByte[0] is not size CdbSize
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
if (BufferSize == NULL) {
|
||
// BufferSize pointer cannot be NULL
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
if ((*BufferSize != 0) && (Buffer == NULL)) {
|
||
// buffer cannot be NULL if *BufferSize is non-zero
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
if ((*BufferSize == 0) && (Buffer != NULL)) {
|
||
// buffer must be NULL if *BufferSize is zero
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
if ((*BufferSize) && GetDataFromDevice) {
|
||
|
||
//
|
||
// pre-zero output buffer (not input buffer)
|
||
//
|
||
|
||
memset(Buffer, 0, (*BufferSize));
|
||
}
|
||
|
||
packetSize = sizeof(SPT_WITH_BUFFERS) + (*BufferSize);
|
||
p = (PSPT_WITH_BUFFERS)LocalAlloc(LPTR, packetSize);
|
||
if (p == NULL) {
|
||
// could not allocate memory for pass-through
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// this has the side effect of pre-zeroing the output buffer
|
||
// if DataIn is TRUE, the SenseData (always), etc.
|
||
//
|
||
|
||
memset(p, 0, packetSize);
|
||
memcpy(p->Spt.Cdb, Cdb, CdbSize);
|
||
|
||
p->Spt.Length = sizeof(SCSI_PASS_THROUGH);
|
||
p->Spt.CdbLength = CdbSize;
|
||
p->Spt.SenseInfoLength = SENSE_BUFFER_SIZE;
|
||
p->Spt.DataIn = (GetDataFromDevice ? 1 : 0);
|
||
p->Spt.DataTransferLength = (*BufferSize);
|
||
p->Spt.TimeOutValue = TimeOut;
|
||
p->Spt.SenseInfoOffset =
|
||
FIELD_OFFSET(SPT_WITH_BUFFERS, SenseInfoBuffer[0]);
|
||
p->Spt.DataBufferOffset =
|
||
FIELD_OFFSET(SPT_WITH_BUFFERS, DataBuffer[0]);
|
||
|
||
if ((*BufferSize != 0) && (!GetDataFromDevice)) {
|
||
|
||
//
|
||
// if we're sending the device data, copy the user's buffer
|
||
//
|
||
|
||
RtlCopyMemory(&(p->DataBuffer[0]), Buffer, *BufferSize);
|
||
|
||
}
|
||
|
||
returnedBytes = 0;
|
||
|
||
returnValue = DeviceIoControl(DeviceHandle,
|
||
IOCTL_SCSI_PASS_THROUGH,
|
||
p,
|
||
packetSize,
|
||
p,
|
||
packetSize,
|
||
&returnedBytes,
|
||
FALSE);
|
||
|
||
senseBuffer = (PSENSE_DATA)p->SenseInfoBuffer;
|
||
|
||
copyData = FALSE;
|
||
|
||
if (senseBuffer->SenseKey & 0xf) {
|
||
|
||
UCHAR length;
|
||
|
||
// determine appropriate length to return
|
||
length = senseBuffer->AdditionalSenseLength;
|
||
length += RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength);
|
||
if (length > SENSE_BUFFER_SIZE) {
|
||
length = SENSE_BUFFER_SIZE;
|
||
}
|
||
length = min(length, SenseDataSize);
|
||
|
||
// copy the sense data back to the user regardless
|
||
RtlCopyMemory(SenseData, senseBuffer, length);
|
||
|
||
returnValue = FALSE; // some error (possibly recovered) occurred
|
||
copyData = TRUE; // copy data anyways
|
||
|
||
|
||
} else if (p->Spt.ScsiStatus != 0) { // scsi protocol error
|
||
|
||
returnValue = FALSE; // command failed
|
||
|
||
} else if (!returnValue) {
|
||
|
||
// returnValue = returnValue;
|
||
|
||
} else {
|
||
|
||
copyData = TRUE;
|
||
|
||
}
|
||
|
||
if (copyData && GetDataFromDevice) {
|
||
|
||
//
|
||
// upon successful completion of a command getting data from the
|
||
// device, copy the returned data back to the user.
|
||
//
|
||
|
||
if (*BufferSize > p->Spt.DataTransferLength) {
|
||
*BufferSize = p->Spt.DataTransferLength;
|
||
}
|
||
memcpy(Buffer, p->DataBuffer, *BufferSize);
|
||
|
||
}
|
||
|
||
//
|
||
// free our memory and return
|
||
//
|
||
|
||
LocalFree(p);
|
||
return returnValue;
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
VOID
|
||
SptDeleteModePageInfo(
|
||
IN PSPT_MODE_PAGE_INFO ModePageInfo
|
||
)
|
||
{
|
||
// ASSERT(ModePageInfo != NULL); BUGBUG
|
||
// ASSERT(ModePageInfo->Signature == SPT_MODE_PAGE_INFO_SIGNATURE); BUGBUG
|
||
LocalFree(ModePageInfo);
|
||
return;
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
BOOL
|
||
SptUpdateModePageInfo(
|
||
IN HANDLE DeviceHandle,
|
||
IN PSPT_MODE_PAGE_INFO ModePageInfo
|
||
)
|
||
{
|
||
SENSE_DATA senseData;
|
||
BOOLEAN retry = FALSE;
|
||
DWORD error;
|
||
DWORD delay;
|
||
|
||
CDB cdb;
|
||
DWORD attemptNumber = 0;
|
||
BOOL success;
|
||
|
||
if ((DeviceHandle == INVALID_HANDLE_VALUE) ||
|
||
(ModePageInfo == NULL) ||
|
||
(!ModePageInfo->IsInitialized) ||
|
||
(ModePageInfo->Signature != SPT_MODE_PAGE_INFO_SIGNATURE)) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
if ((ModePageInfo->ModePageSize != SptModePageSizeModeSense6) &&
|
||
(ModePageInfo->ModePageSize != SptModePageSizeModeSense10)) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
RetryUpdateModePage:
|
||
|
||
attemptNumber ++;
|
||
|
||
RtlZeroMemory(&cdb, sizeof(CDB));
|
||
RtlZeroMemory(&senseData, sizeof(SENSE_DATA));
|
||
|
||
if (ModePageInfo->ModePageSize == SptModePageSizeModeSense6) {
|
||
|
||
DWORD sizeToGet = sizeof(MODE_PARAMETER_HEADER);
|
||
|
||
cdb.MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
|
||
cdb.MODE_SENSE.PageCode = 0x3f;
|
||
cdb.MODE_SENSE.AllocationLength = sizeof(MODE_PARAMETER_HEADER);
|
||
|
||
success = SptSendCdbToDeviceEx(DeviceHandle,
|
||
&cdb,
|
||
6,
|
||
&(ModePageInfo->H.AsChar),
|
||
&sizeToGet,
|
||
&senseData,
|
||
sizeof(SENSE_DATA),
|
||
TRUE,
|
||
SPT_MODE_SENSE_TIMEOUT);
|
||
|
||
} else {
|
||
|
||
DWORD sizeToGet = sizeof(MODE_PARAMETER_HEADER10);
|
||
|
||
cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
||
cdb.MODE_SENSE10.PageCode = 0x3f;
|
||
cdb.MODE_SENSE10.AllocationLength[0] = sizeof(MODE_PARAMETER_HEADER10);
|
||
|
||
success = SptSendCdbToDeviceEx(DeviceHandle,
|
||
&cdb,
|
||
10,
|
||
&(ModePageInfo->H.AsChar),
|
||
&sizeToGet,
|
||
&senseData,
|
||
sizeof(SENSE_DATA),
|
||
TRUE,
|
||
SPT_MODE_SENSE_TIMEOUT);
|
||
}
|
||
|
||
if (!success) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// if the pass-through succeeded, we need to still determine if the
|
||
// actual CDB succeeded. use the InterpretSenseInfo() routine.
|
||
//
|
||
|
||
SptUtilInterpretSenseInfo(&senseData,
|
||
sizeof(SENSE_DATA),
|
||
&error,
|
||
&retry,
|
||
&delay);
|
||
|
||
if (error != ERROR_SUCCESS) {
|
||
|
||
if (retry && (attemptNumber < MAXIMUM_DEFAULT_RETRIES)) {
|
||
|
||
if (delay != 0) {
|
||
// sleep?
|
||
}
|
||
|
||
goto RetryUpdateModePage;
|
||
}
|
||
|
||
// else the error was not worth retrying, or we've retried too often.
|
||
SetLastError(error);
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
//
|
||
// the command succeeded.
|
||
//
|
||
|
||
if (ModePageInfo->ModePageSize == SptModePageSizeModeSense6) {
|
||
ModePageInfo->H.Header6.ModeDataLength = 0;
|
||
} else {
|
||
ModePageInfo->H.Header10.ModeDataLength[0] = 0;
|
||
ModePageInfo->H.Header10.ModeDataLength[1] = 0;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
PSPT_MODE_PAGE_INFO
|
||
SptInitializeModePageInfo(
|
||
IN HANDLE DeviceHandle
|
||
)
|
||
{
|
||
PSPT_MODE_PAGE_INFO localInfo;
|
||
DWORD i;
|
||
|
||
localInfo = LocalAlloc(LPTR, sizeof(SPT_MODE_PAGE_INFO));
|
||
if (localInfo == NULL) {
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// try both 10 and 6 byte mode sense commands
|
||
//
|
||
|
||
for (i = 0; i < 2; i++) {
|
||
|
||
RtlZeroMemory(localInfo, sizeof(SPT_MODE_PAGE_INFO));
|
||
localInfo->IsInitialized = TRUE;
|
||
localInfo->Signature = SPT_MODE_PAGE_INFO_SIGNATURE;
|
||
|
||
if ( i == 0 ) {
|
||
localInfo->ModePageSize = SptModePageSizeModeSense10;
|
||
} else {
|
||
localInfo->ModePageSize = SptModePageSizeModeSense6;
|
||
}
|
||
|
||
if (SptUpdateModePageInfo(DeviceHandle, localInfo)) {
|
||
return localInfo;
|
||
}
|
||
}
|
||
LocalFree(localInfo);
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
BOOL
|
||
SptGetModePage(
|
||
IN HANDLE DeviceHandle,
|
||
IN PSPT_MODE_PAGE_INFO ModePageInfo,
|
||
IN UCHAR ModePage,
|
||
OUT PUCHAR Buffer,
|
||
IN OUT PDWORD BufferSize,
|
||
OUT PDWORD AvailableModePageData OPTIONAL
|
||
)
|
||
{
|
||
PUCHAR localBuffer = NULL;
|
||
DWORD localBufferSize = 0;
|
||
SENSE_DATA senseData;
|
||
CDB cdb;
|
||
DWORD thisAttempt = 0;
|
||
BOOL success;
|
||
|
||
DWORD finalPageSize;
|
||
PUCHAR finalPageStart;
|
||
|
||
|
||
if ((DeviceHandle == INVALID_HANDLE_VALUE) ||
|
||
(ModePageInfo == NULL) ||
|
||
(!ModePageInfo->IsInitialized) ||
|
||
(ModePageInfo->Signature != SPT_MODE_PAGE_INFO_SIGNATURE) ||
|
||
(ModePageInfo->ModePageSize == SptModePageSizeUndefined) ||
|
||
(ModePage & 0xC0)) {
|
||
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
|
||
}
|
||
if (((BufferSize == 0) && (Buffer != NULL)) ||
|
||
((BufferSize != 0) && (Buffer == NULL))) {
|
||
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
if ((ModePageInfo->ModePageSize != SptModePageSizeModeSense6) &&
|
||
(ModePageInfo->ModePageSize != SptModePageSizeModeSense10)) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
localBufferSize = 0;
|
||
|
||
if (BufferSize != NULL) {
|
||
localBufferSize += *BufferSize;
|
||
}
|
||
|
||
if (ModePageInfo->ModePageSize == SptModePageSizeModeSense6) {
|
||
|
||
localBufferSize += sizeof(MODE_PARAMETER_HEADER);
|
||
localBufferSize += ModePageInfo->H.Header6.BlockDescriptorLength;
|
||
if (localBufferSize > 0xff) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
} else {
|
||
|
||
localBufferSize += sizeof(MODE_PARAMETER_HEADER);
|
||
localBufferSize += ModePageInfo->H.Header10.BlockDescriptorLength[0] * 256;
|
||
localBufferSize += ModePageInfo->H.Header10.BlockDescriptorLength[1];
|
||
if (localBufferSize > 0xffff) {
|
||
SetLastError(ERROR_INVALID_PARAMETER);
|
||
return FALSE;
|
||
}
|
||
|
||
}
|
||
|
||
localBuffer = LocalAlloc(LPTR, localBufferSize);
|
||
if (localBuffer == NULL) {
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
RetryGetModePage:
|
||
|
||
thisAttempt ++;
|
||
|
||
RtlZeroMemory(&cdb, sizeof(CDB));
|
||
RtlZeroMemory(&senseData, sizeof(SENSE_DATA));
|
||
RtlZeroMemory(localBuffer, localBufferSize);
|
||
|
||
if (ModePageInfo->ModePageSize == SptModePageSizeModeSense6) {
|
||
|
||
DWORD sizeToGet = localBufferSize;
|
||
cdb.MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
|
||
cdb.MODE_SENSE.PageCode = ModePage;
|
||
cdb.MODE_SENSE.AllocationLength = (UCHAR)localBufferSize;
|
||
|
||
success = SptSendCdbToDeviceEx(DeviceHandle,
|
||
&cdb,
|
||
6,
|
||
localBuffer,
|
||
&sizeToGet,
|
||
&senseData,
|
||
sizeof(SENSE_DATA),
|
||
TRUE,
|
||
SPT_MODE_SENSE_TIMEOUT);
|
||
|
||
} else {
|
||
|
||
DWORD sizeToGet = localBufferSize;
|
||
cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
||
cdb.MODE_SENSE10.PageCode = ModePage;
|
||
cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(localBufferSize >> 8);
|
||
cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(localBufferSize & 0xff);
|
||
|
||
success = SptSendCdbToDeviceEx(DeviceHandle,
|
||
&cdb,
|
||
10,
|
||
localBuffer,
|
||
&sizeToGet,
|
||
&senseData,
|
||
sizeof(SENSE_DATA),
|
||
TRUE,
|
||
SPT_MODE_SENSE_TIMEOUT);
|
||
}
|
||
|
||
//
|
||
// if the pass-through failed, we need to still determine if the
|
||
// actual CDB succeeded. use the InterpretSenseInfo() routine.
|
||
//
|
||
|
||
if (!success) {
|
||
LocalFree( localBuffer );
|
||
return FALSE;
|
||
}
|
||
|
||
{
|
||
DWORD interpretedError;
|
||
BOOLEAN retry;
|
||
DWORD delay;
|
||
|
||
SptUtilInterpretSenseInfo(&senseData,
|
||
sizeof(SENSE_DATA),
|
||
&interpretedError,
|
||
&retry,
|
||
&delay);
|
||
|
||
if (interpretedError != ERROR_SUCCESS) {
|
||
if ((retry == FALSE) || (thisAttempt > MAXIMUM_DEFAULT_RETRIES)){
|
||
SetLastError(interpretedError);
|
||
LocalFree( localBuffer );
|
||
return FALSE;
|
||
}
|
||
|
||
// BUGBUG - sleep?
|
||
goto RetryGetModePage;
|
||
}
|
||
}
|
||
|
||
//
|
||
// the transfer succeeded. now to transfer the info back to
|
||
// the caller.
|
||
//
|
||
|
||
if (ModePageInfo->ModePageSize == SptModePageSizeModeSense6) {
|
||
|
||
PMODE_PARAMETER_HEADER header6 = (PMODE_PARAMETER_HEADER)localBuffer;
|
||
finalPageSize = header6->ModeDataLength + 1; // length field itself.
|
||
|
||
finalPageStart = localBuffer;
|
||
finalPageStart += sizeof(MODE_PARAMETER_HEADER);
|
||
finalPageSize -= sizeof(MODE_PARAMETER_HEADER);
|
||
finalPageStart += header6->BlockDescriptorLength;
|
||
finalPageSize -= header6->BlockDescriptorLength;
|
||
|
||
} else {
|
||
|
||
PMODE_PARAMETER_HEADER10 header10 = (PMODE_PARAMETER_HEADER10)localBuffer;
|
||
finalPageSize = header10->ModeDataLength[0] * 256;
|
||
finalPageSize += header10->ModeDataLength[1];
|
||
finalPageSize += 2; // length field itself.
|
||
|
||
finalPageStart = localBuffer;
|
||
finalPageStart += sizeof(MODE_PARAMETER_HEADER10);
|
||
finalPageSize -= sizeof(MODE_PARAMETER_HEADER10);
|
||
finalPageStart += header10->BlockDescriptorLength[0] * 256;
|
||
finalPageStart += header10->BlockDescriptorLength[1];
|
||
finalPageSize -= header10->BlockDescriptorLength[0] * 256;
|
||
finalPageSize -= header10->BlockDescriptorLength[1];
|
||
|
||
}
|
||
|
||
//
|
||
// finalPageStart now points to the page start
|
||
// finalPageSize says how much of the data is valid
|
||
//
|
||
|
||
//
|
||
// allow the user to distinguish between available data
|
||
// and the amount we actually returned that was usable.
|
||
//
|
||
|
||
if (AvailableModePageData != NULL) {
|
||
*AvailableModePageData = finalPageSize;
|
||
}
|
||
|
||
//
|
||
// if the expected buffer size is greater than the device
|
||
// returned, update the user's available size info
|
||
//
|
||
|
||
if (*BufferSize > finalPageSize) {
|
||
*BufferSize = finalPageSize;
|
||
}
|
||
|
||
//
|
||
// finally, copy whatever data is available to the user's buffer.
|
||
//
|
||
|
||
if (*BufferSize != 0) {
|
||
RtlCopyMemory(Buffer, localBuffer, *BufferSize);
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
BOOL
|
||
SptSetModePage(
|
||
IN HANDLE DeviceHandle,
|
||
IN PSPT_MODE_PAGE_INFO ModePageSize,
|
||
IN SPT_MODE_PAGE_TYPE ModePageType,
|
||
IN UCHAR ModePage,
|
||
OUT PUCHAR Buffer,
|
||
IN OUT PDWORD BufferSize
|
||
)
|
||
{
|
||
SetLastError(ERROR_INVALID_FUNCTION);
|
||
return FALSE;
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
NOTE: we default to RETRY==TRUE except for known error classes
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
VOID
|
||
SptUtilInterpretSenseInfo(
|
||
IN PSENSE_DATA SenseData,
|
||
IN UCHAR SenseDataSize,
|
||
OUT PDWORD ErrorValue, // from WinError.h
|
||
OUT PBOOLEAN SuggestRetry OPTIONAL,
|
||
OUT PDWORD SuggestRetryDelay OPTIONAL
|
||
)
|
||
{
|
||
DWORD error;
|
||
DWORD retryDelay;
|
||
BOOLEAN retry;
|
||
UCHAR senseKey;
|
||
UCHAR asc;
|
||
UCHAR ascq;
|
||
|
||
if (SenseDataSize == 0) {
|
||
retry = FALSE;
|
||
retryDelay = 0;
|
||
error = ERROR_IO_DEVICE;
|
||
goto SetAndExit;
|
||
|
||
}
|
||
|
||
//
|
||
// default to suggesting a retry in 1/10 of a second,
|
||
// with a status of ERROR_IO_DEVICE.
|
||
//
|
||
retry = TRUE;
|
||
retryDelay = 1;
|
||
error = ERROR_IO_DEVICE;
|
||
|
||
//
|
||
// if the device didn't provide any sense this time, return.
|
||
//
|
||
|
||
if ((SenseData->SenseKey & 0xf) == 0) {
|
||
retry = FALSE;
|
||
retryDelay = 0;
|
||
error = ERROR_SUCCESS;
|
||
goto SetAndExit;
|
||
}
|
||
|
||
|
||
//
|
||
// if we can't even see the sense key, just return.
|
||
// can't use bitfields in these macros, so use next field.
|
||
//
|
||
|
||
if (SenseDataSize < FIELD_OFFSET(SENSE_DATA, Information)) {
|
||
goto SetAndExit;
|
||
}
|
||
|
||
senseKey = SenseData->SenseKey;
|
||
|
||
|
||
{ // set the size to what's actually useful.
|
||
UCHAR validLength;
|
||
// figure out what we could have gotten with a large sense buffer
|
||
if (SenseDataSize <
|
||
RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength)) {
|
||
validLength = SenseDataSize;
|
||
} else {
|
||
validLength =
|
||
RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength);
|
||
validLength += SenseData->AdditionalSenseLength;
|
||
}
|
||
// use the smaller of the two values.
|
||
SenseDataSize = min(SenseDataSize, validLength);
|
||
}
|
||
|
||
if (SenseDataSize <
|
||
RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode)) {
|
||
asc = SCSI_ADSENSE_NO_SENSE;
|
||
} else {
|
||
asc = SenseData->AdditionalSenseCode;
|
||
}
|
||
|
||
if (SenseDataSize <
|
||
RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCodeQualifier)) {
|
||
ascq = SCSI_SENSEQ_CAUSE_NOT_REPORTABLE; // 0x00
|
||
} else {
|
||
ascq = SenseData->AdditionalSenseCodeQualifier;
|
||
}
|
||
|
||
//
|
||
// interpret :P
|
||
//
|
||
|
||
switch (senseKey & 0xf) {
|
||
|
||
case SCSI_SENSE_RECOVERED_ERROR: { // 0x01
|
||
if (SenseData->IncorrectLength) {
|
||
error = ERROR_INVALID_BLOCK_LENGTH;
|
||
} else {
|
||
error = ERROR_SUCCESS;
|
||
}
|
||
retry = FALSE;
|
||
break;
|
||
} // end SCSI_SENSE_RECOVERED_ERROR
|
||
|
||
case SCSI_SENSE_NOT_READY: { // 0x02
|
||
error = ERROR_NOT_READY;
|
||
|
||
switch (asc) {
|
||
|
||
case SCSI_ADSENSE_LUN_NOT_READY: {
|
||
|
||
switch (ascq) {
|
||
|
||
case SCSI_SENSEQ_BECOMING_READY:
|
||
case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
|
||
retryDelay = SPT_NOT_READY_RETRY_INTERVAL;
|
||
break;
|
||
}
|
||
|
||
case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE:
|
||
case SCSI_SENSEQ_FORMAT_IN_PROGRESS:
|
||
case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
|
||
retry = FALSE;
|
||
break;
|
||
}
|
||
|
||
case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
|
||
retry = FALSE;
|
||
break;
|
||
}
|
||
|
||
} // end switch (senseBuffer->AdditionalSenseCodeQualifier)
|
||
break;
|
||
}
|
||
|
||
case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
|
||
error = ERROR_NOT_READY;
|
||
retry = FALSE;
|
||
break;
|
||
}
|
||
} // end switch (senseBuffer->AdditionalSenseCode)
|
||
|
||
break;
|
||
} // end SCSI_SENSE_NOT_READY
|
||
|
||
case SCSI_SENSE_MEDIUM_ERROR: { // 0x03
|
||
error = ERROR_CRC;
|
||
retry = FALSE;
|
||
|
||
//
|
||
// Check if this error is due to unknown format
|
||
//
|
||
if (asc == SCSI_ADSENSE_INVALID_MEDIA) {
|
||
|
||
switch (ascq) {
|
||
|
||
case SCSI_SENSEQ_UNKNOWN_FORMAT: {
|
||
error = ERROR_UNRECOGNIZED_MEDIA;
|
||
break;
|
||
}
|
||
|
||
case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
|
||
error = ERROR_UNRECOGNIZED_MEDIA;
|
||
//error = ERROR_CLEANER_CARTRIDGE_INSTALLED;
|
||
break;
|
||
}
|
||
|
||
} // end switch AdditionalSenseCodeQualifier
|
||
|
||
} // end SCSI_ADSENSE_INVALID_MEDIA
|
||
break;
|
||
} // end SCSI_SENSE_MEDIUM_ERROR
|
||
|
||
case SCSI_SENSE_ILLEGAL_REQUEST: { // 0x05
|
||
error = ERROR_INVALID_FUNCTION;
|
||
retry = FALSE;
|
||
|
||
switch (asc) {
|
||
|
||
case SCSI_ADSENSE_ILLEGAL_BLOCK: {
|
||
error = ERROR_SECTOR_NOT_FOUND;
|
||
break;
|
||
}
|
||
|
||
case SCSI_ADSENSE_INVALID_LUN: {
|
||
error = ERROR_FILE_NOT_FOUND;
|
||
break;
|
||
}
|
||
|
||
case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
|
||
error = ERROR_FILE_ENCRYPTED;
|
||
//error = ERROR_SPT_LIB_COPY_PROTECTION_FAILURE;
|
||
switch (ascq) {
|
||
case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
|
||
//error = ERROR_SPT_LIB_AUTHENTICATION_FAILURE;
|
||
break;
|
||
case SCSI_SENSEQ_KEY_NOT_PRESENT:
|
||
//error = ERROR_SPT_LIB_KEY_NOT_PRESENT;
|
||
break;
|
||
case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
|
||
//error = ERROR_SPT_LIB_KEY_NOT_ESTABLISHED;
|
||
break;
|
||
case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
|
||
//error = ERROR_SPT_LIB_SCRAMBLED_SECTOR;
|
||
break;
|
||
case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
|
||
//error = ERROR_SPT_LIB_REGION_MISMATCH;
|
||
break;
|
||
case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
|
||
//error = ERROR_SPT_LIB_RESETS_EXHAUSTED;
|
||
break;
|
||
} // end switch of ASCQ for COPY_PROTECTION_FAILURE
|
||
break;
|
||
}
|
||
|
||
} // end switch (senseBuffer->AdditionalSenseCode)
|
||
break;
|
||
|
||
} // end SCSI_SENSE_ILLEGAL_REQUEST
|
||
|
||
case SCSI_SENSE_DATA_PROTECT: { // 0x07
|
||
error = ERROR_WRITE_PROTECT;
|
||
retry = FALSE;
|
||
break;
|
||
} // end SCSI_SENSE_DATA_PROTECT
|
||
|
||
case SCSI_SENSE_BLANK_CHECK: { // 0x08
|
||
error = ERROR_NO_DATA_DETECTED;
|
||
break;
|
||
} // end SCSI_SENSE_BLANK_CHECK
|
||
|
||
case SCSI_SENSE_NO_SENSE: { // 0x00
|
||
if (SenseData->IncorrectLength) {
|
||
error = ERROR_INVALID_BLOCK_LENGTH;
|
||
retry = FALSE;
|
||
} else {
|
||
error = ERROR_IO_DEVICE;
|
||
}
|
||
break;
|
||
} // end SCSI_SENSE_NO_SENSE
|
||
|
||
case SCSI_SENSE_HARDWARE_ERROR: // 0x04
|
||
case SCSI_SENSE_UNIT_ATTENTION: // 0x06
|
||
case SCSI_SENSE_UNIQUE: // 0x09
|
||
case SCSI_SENSE_COPY_ABORTED: // 0x0A
|
||
case SCSI_SENSE_ABORTED_COMMAND: // 0x0B
|
||
case SCSI_SENSE_EQUAL: // 0x0C
|
||
case SCSI_SENSE_VOL_OVERFLOW: // 0x0D
|
||
case SCSI_SENSE_MISCOMPARE: // 0x0E
|
||
case SCSI_SENSE_RESERVED: // 0x0F
|
||
default: {
|
||
error = ERROR_IO_DEVICE;
|
||
break;
|
||
}
|
||
|
||
} // end switch(SenseKey)
|
||
|
||
SetAndExit:
|
||
|
||
if (ARGUMENT_PRESENT(SuggestRetry)) {
|
||
*SuggestRetry = retry;
|
||
}
|
||
if (ARGUMENT_PRESENT(SuggestRetryDelay)) {
|
||
*SuggestRetryDelay = retryDelay;
|
||
}
|
||
*ErrorValue = error;
|
||
|
||
return;
|
||
|
||
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
|
||
|