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:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|