You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1090 lines
32 KiB
1090 lines
32 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
spti.c
|
|
|
|
Abstract:
|
|
|
|
Win32 application that can communicate directly with SCSI devices via
|
|
IOCTLs.
|
|
|
|
Author:
|
|
|
|
|
|
Environment:
|
|
|
|
User mode.
|
|
|
|
Notes:
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "spti.h"
|
|
|
|
|
|
PUCHAR BusTypeStrings[] = {
|
|
"Unknown",
|
|
"Scsi",
|
|
"Atapi",
|
|
"Ata",
|
|
"1394",
|
|
"Ssa",
|
|
"Fibre",
|
|
"Usb",
|
|
"RAID",
|
|
"Not Defined",
|
|
};
|
|
#define NUMBER_OF_BUS_TYPE_STRINGS (sizeof(BusTypeStrings)/sizeof(BusTypeStrings[0]))
|
|
|
|
|
|
typedef struct _SPTI_OPTIONS {
|
|
|
|
PUCHAR PortName;
|
|
BOOLEAN SharedRead;
|
|
BOOLEAN SharedWrite;
|
|
BOOLEAN LockVolume;
|
|
BOOLEAN ForceLock;
|
|
|
|
} SPTI_OPTIONS, *PSPTI_OPTIONS;
|
|
|
|
typedef enum _RETURN_VALUE {
|
|
RETURN_BAD_ARGS = -1,
|
|
RETURN_NAME_TOO_LONG = -2,
|
|
RETURN_UNABLE_TO_OPEN_DEVICE = -3,
|
|
RETURN_UNABLE_TO_LOCK_VOLUME = -4,
|
|
RETURN_UNABLE_TO_GET_ALIGNMENT_MASK = -5,
|
|
|
|
} RETURN_VALUE, *PRETURN_VALUE;
|
|
|
|
BOOL
|
|
LockVolume(
|
|
HANDLE DeviceHandle,
|
|
PSPTI_OPTIONS Options
|
|
)
|
|
{
|
|
BOOL lockSucceeded;
|
|
|
|
lockSucceeded = SptUtilLockVolumeByHandle(DeviceHandle, FALSE);
|
|
|
|
if (!lockSucceeded && !Options->ForceLock)
|
|
{
|
|
|
|
// save the error, and restore after getting user input
|
|
ULONG lastError = GetLastError();
|
|
int input;
|
|
|
|
do {
|
|
printf("This program cannot run because the volume is in use\n"
|
|
"by another process. It may run if this volume is\n"
|
|
"dismounted first.\n"
|
|
"ALL OPENED HANDLES TO THIS VOLUME WOULD THEN BE INVALID.\n"
|
|
"Would you like to force a dismount on this volume? (Y/N) "
|
|
);
|
|
input = _getch();
|
|
input = toupper( input );
|
|
|
|
} while ( (input != EOF) &&
|
|
(input != 'Y') &&
|
|
(input != 'N') );
|
|
|
|
if (input == 'Y')
|
|
{
|
|
Options->ForceLock = TRUE;
|
|
}
|
|
|
|
// now restore the error from the IOCTL
|
|
SetLastError(lastError);
|
|
}
|
|
|
|
if (!lockSucceeded && Options->ForceLock)
|
|
{
|
|
lockSucceeded = SptUtilLockVolumeByHandle(DeviceHandle, TRUE);
|
|
}
|
|
return lockSucceeded;
|
|
}
|
|
|
|
BOOL
|
|
ParseArguments(
|
|
int argc,
|
|
char * argv[],
|
|
PSPTI_OPTIONS Options
|
|
)
|
|
{
|
|
int i;
|
|
|
|
if (Options == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory( Options, sizeof(SPTI_OPTIONS) );
|
|
Options->ForceLock = FALSE;
|
|
Options->LockVolume = FALSE;
|
|
Options->PortName = NULL;
|
|
Options->SharedRead = FALSE;
|
|
Options->SharedWrite = FALSE;
|
|
|
|
|
|
|
|
// loop through the arguments....
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if ( (argv[i][0] == '/') || (argv[i][0] == '-') )
|
|
{
|
|
// switch found. parse it.
|
|
PUCHAR option = &(argv[i][1]);
|
|
|
|
if (_strnicmp(option, "r", strlen("r")) == 0)
|
|
{
|
|
Options->SharedRead = TRUE;
|
|
}
|
|
else if (_strnicmp(option, "w", strlen("w")) == 0)
|
|
{
|
|
Options->SharedWrite = TRUE;
|
|
}
|
|
else if (_strnicmp(option, "lock", strlen("lock")) == 0)
|
|
{
|
|
Options->LockVolume = TRUE;
|
|
}
|
|
else if (_strnicmp(option, "forcelock", strlen("forcelock")) == 0)
|
|
{
|
|
Options->ForceLock = TRUE;
|
|
}
|
|
else
|
|
{
|
|
printf("Unknown option: %s\n", argv[i]);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// previously set one?
|
|
if (Options->PortName != NULL)
|
|
{
|
|
printf("Can only have one non-option argument.\n"
|
|
"Two were supplied: '%s', '%s'\n",
|
|
Options->PortName,
|
|
argv[i]
|
|
);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
// set to this argument
|
|
Options->PortName = argv[i];
|
|
}
|
|
}
|
|
} // end argument loop
|
|
|
|
// validate non-conflicting options.
|
|
if (Options->ForceLock || Options->LockVolume)
|
|
{
|
|
// locking the volume requires shared read/write access
|
|
if ( (( Options->SharedRead) && (!Options->SharedWrite)) ||
|
|
((!Options->SharedRead) && ( Options->SharedWrite)) )
|
|
{
|
|
printf("Locking the volume requires both read and write shared "
|
|
"access\n");
|
|
return FALSE;
|
|
}
|
|
Options->LockVolume = TRUE;
|
|
}
|
|
|
|
// validate a port name was supplied
|
|
if ( Options->PortName == NULL )
|
|
{
|
|
printf("A port name is a required argument\n");
|
|
return FALSE;
|
|
}
|
|
|
|
// if they passed neither read nor write, set both as default
|
|
if ((!Options->SharedRead) && (!Options->SharedWrite))
|
|
{
|
|
Options->SharedRead = TRUE;
|
|
Options->SharedWrite = TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Prints the built-in help for this program
|
|
//
|
|
VOID
|
|
PrintUsageInfo(
|
|
char * programName
|
|
)
|
|
{
|
|
printf("Usage: %s <port-name> [-r | -w | -lock | -forcelock]\n", programName );
|
|
printf("Examples:\n");
|
|
printf(" %s g: (open the G: drive in SHARED READ/WRITE mode)\n", programName);
|
|
printf(" %s i: r (open the I: drive in SHARED READ mode)\n", programName);
|
|
printf(" %s r: lock (lock the R: volume for exclusive access\n", programName);
|
|
printf(" %s Tape0 w (open the tape class driver in SHARED WRITE mode)\n", programName);
|
|
printf(" %s Scsi2: (open the miniport driver specified)\n", programName);
|
|
}
|
|
|
|
//
|
|
// don't print non-printable chars
|
|
//
|
|
VOID PrintChar( IN UCHAR Char ) {
|
|
if ( (Char >= 0x21) && (Char <= 0x7E) ) {
|
|
printf("%c", Char);
|
|
} else {
|
|
printf("%c", '.');
|
|
}
|
|
}
|
|
|
|
//
|
|
// Prints a buffer to the screen in hex and ASCII
|
|
//
|
|
VOID
|
|
PrintBuffer(
|
|
IN PVOID InputBuffer,
|
|
IN SIZE_T Size
|
|
)
|
|
{
|
|
DWORD offset = 0;
|
|
PUCHAR buffer = InputBuffer;
|
|
|
|
while (Size >= 0x10) {
|
|
|
|
DWORD i;
|
|
|
|
printf( "%08x:"
|
|
" %02x %02x %02x %02x %02x %02x %02x %02x"
|
|
" %02x %02x %02x %02x %02x %02x %02x %02x"
|
|
" ",
|
|
offset,
|
|
*(buffer + 0), *(buffer + 1), *(buffer + 2), *(buffer + 3),
|
|
*(buffer + 4), *(buffer + 5), *(buffer + 6), *(buffer + 7),
|
|
*(buffer + 8), *(buffer + 9), *(buffer + 10), *(buffer + 11),
|
|
*(buffer + 12), *(buffer + 13), *(buffer + 14), *(buffer + 15)
|
|
);
|
|
|
|
for (i=0; i < 0x10; i++) {
|
|
PrintChar(*(buffer+i));
|
|
}
|
|
printf("\n");
|
|
|
|
|
|
Size -= 0x10;
|
|
offset += 0x10;
|
|
buffer += 0x10;
|
|
}
|
|
|
|
if (Size != 0) {
|
|
|
|
DWORD i;
|
|
|
|
printf("%08x:", offset);
|
|
|
|
//
|
|
// print the hex values
|
|
//
|
|
for (i=0; i<Size; i++) {
|
|
|
|
if ((i%8)==0) {
|
|
printf(" "); // extra space every eight chars
|
|
}
|
|
printf(" %02x", *(buffer+i));
|
|
|
|
}
|
|
// add an extra space for half-way mark
|
|
if (Size <= 0x8)
|
|
{
|
|
printf(" ");
|
|
}
|
|
|
|
//
|
|
// fill in the blanks
|
|
//
|
|
for (; i < 0x10; i++) {
|
|
printf(" ");
|
|
}
|
|
printf(" ");
|
|
//
|
|
// print the ascii
|
|
//
|
|
for (i=0; i<Size; i++) {
|
|
PrintChar(*(buffer+i));
|
|
}
|
|
printf("\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Prints the formatted message for a given error code
|
|
//
|
|
VOID
|
|
PrintError(
|
|
ULONG ErrorCode
|
|
)
|
|
{
|
|
UCHAR errorBuffer[80];
|
|
ULONG count;
|
|
|
|
count = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
ErrorCode,
|
|
0,
|
|
errorBuffer,
|
|
sizeof(errorBuffer),
|
|
NULL
|
|
);
|
|
|
|
if (count != 0) {
|
|
printf("%s\n", errorBuffer);
|
|
} else {
|
|
printf("Format message failed. Error: %d\n", GetLastError());
|
|
}
|
|
}
|
|
|
|
//
|
|
// Prints the device descriptor in a formatted manner
|
|
//
|
|
VOID
|
|
PrintAdapterDescriptor(
|
|
PSTORAGE_ADAPTER_DESCRIPTOR AdapterDescriptor
|
|
)
|
|
{
|
|
ULONG trueMaximumTransferLength;
|
|
PUCHAR busType;
|
|
|
|
if (AdapterDescriptor->BusType <= NUMBER_OF_BUS_TYPE_STRINGS) {
|
|
busType = BusTypeStrings[AdapterDescriptor->BusType];
|
|
} else {
|
|
busType = BusTypeStrings[NUMBER_OF_BUS_TYPE_STRINGS-1];
|
|
}
|
|
|
|
// subtract one page, as transfers do not always start on a page boundary
|
|
if (AdapterDescriptor->MaximumPhysicalPages > 1) {
|
|
trueMaximumTransferLength = AdapterDescriptor->MaximumPhysicalPages - 1;
|
|
} else {
|
|
trueMaximumTransferLength = 1;
|
|
}
|
|
// make it into a byte value
|
|
trueMaximumTransferLength <<= PAGE_SHIFT;
|
|
|
|
// take the minimum of the two
|
|
if (trueMaximumTransferLength > AdapterDescriptor->MaximumTransferLength) {
|
|
trueMaximumTransferLength = AdapterDescriptor->MaximumTransferLength;
|
|
}
|
|
|
|
// always allow at least a single page transfer
|
|
if (trueMaximumTransferLength < PAGE_SIZE) {
|
|
trueMaximumTransferLength = PAGE_SIZE;
|
|
}
|
|
|
|
puts("\n ***** STORAGE ADAPTER DESCRIPTOR DATA *****");
|
|
printf(" Version: %08x\n"
|
|
" TotalSize: %08x\n"
|
|
"MaximumTransferLength: %08x (bytes)\n"
|
|
" MaximumPhysicalPages: %08x\n"
|
|
" TrueMaximumTransfer: %08x (bytes)\n"
|
|
" AlignmentMask: %08x\n"
|
|
" AdapterUsesPio: %s\n"
|
|
" AdapterScansDown: %s\n"
|
|
" CommandQueueing: %s\n"
|
|
" AcceleratedTransfer: %s\n"
|
|
" Bus Type: %s\n"
|
|
" Bus Major Version: %04x\n"
|
|
" Bus Minor Version: %04x\n",
|
|
AdapterDescriptor->Version,
|
|
AdapterDescriptor->Size,
|
|
AdapterDescriptor->MaximumTransferLength,
|
|
AdapterDescriptor->MaximumPhysicalPages,
|
|
trueMaximumTransferLength,
|
|
AdapterDescriptor->AlignmentMask,
|
|
BOOLEAN_TO_STRING(AdapterDescriptor->AdapterUsesPio),
|
|
BOOLEAN_TO_STRING(AdapterDescriptor->AdapterScansDown),
|
|
BOOLEAN_TO_STRING(AdapterDescriptor->CommandQueueing),
|
|
BOOLEAN_TO_STRING(AdapterDescriptor->AcceleratedTransfer),
|
|
busType,
|
|
AdapterDescriptor->BusMajorVersion,
|
|
AdapterDescriptor->BusMinorVersion);
|
|
|
|
|
|
|
|
|
|
printf("\n\n");
|
|
}
|
|
|
|
//
|
|
// Prints the adapter descriptor in a formatted manner
|
|
//
|
|
VOID
|
|
PrintDeviceDescriptor(
|
|
PSTORAGE_DEVICE_DESCRIPTOR DeviceDescriptor
|
|
)
|
|
{
|
|
PUCHAR vendorId = "";
|
|
PUCHAR productId = "";
|
|
PUCHAR productRevision = "";
|
|
PUCHAR serialNumber = "";
|
|
PUCHAR busType;
|
|
|
|
if (DeviceDescriptor->BusType <= NUMBER_OF_BUS_TYPE_STRINGS) {
|
|
busType = BusTypeStrings[DeviceDescriptor->BusType];
|
|
} else {
|
|
busType = BusTypeStrings[NUMBER_OF_BUS_TYPE_STRINGS-1];
|
|
}
|
|
|
|
if ((DeviceDescriptor->ProductIdOffset != 0) &&
|
|
(DeviceDescriptor->ProductIdOffset != -1)) {
|
|
productId = (PUCHAR)(DeviceDescriptor);
|
|
productId += (ULONG_PTR)DeviceDescriptor->ProductIdOffset;
|
|
}
|
|
if ((DeviceDescriptor->VendorIdOffset != 0) &&
|
|
(DeviceDescriptor->VendorIdOffset != -1)) {
|
|
vendorId = (PUCHAR)(DeviceDescriptor);
|
|
vendorId += (ULONG_PTR)DeviceDescriptor->VendorIdOffset;
|
|
}
|
|
if ((DeviceDescriptor->ProductRevisionOffset != 0) &&
|
|
(DeviceDescriptor->ProductRevisionOffset != -1)) {
|
|
productRevision = (PUCHAR)(DeviceDescriptor);
|
|
productRevision += (ULONG_PTR)DeviceDescriptor->ProductRevisionOffset;
|
|
}
|
|
if ((DeviceDescriptor->SerialNumberOffset != 0) &&
|
|
(DeviceDescriptor->SerialNumberOffset != -1)) {
|
|
serialNumber = (PUCHAR)(DeviceDescriptor);
|
|
serialNumber += (ULONG_PTR)DeviceDescriptor->SerialNumberOffset;
|
|
}
|
|
|
|
|
|
puts("\n ***** STORAGE DEVICE DESCRIPTOR DATA *****");
|
|
printf(" Version: %08x\n"
|
|
" TotalSize: %08x\n"
|
|
" DeviceType: %08x\n"
|
|
" DeviceTypeModifier: %08x\n"
|
|
" RemovableMedia: %s\n"
|
|
" CommandQueueing: %s\n"
|
|
" Vendor Id: %s\n"
|
|
" Product Id: %s\n"
|
|
" Product Revision: %s\n"
|
|
" Serial Number: %s\n"
|
|
" Bus Type: %s\n"
|
|
" Raw Properties: %s\n",
|
|
DeviceDescriptor->Version,
|
|
DeviceDescriptor->Size,
|
|
DeviceDescriptor->DeviceType,
|
|
DeviceDescriptor->DeviceTypeModifier,
|
|
BOOLEAN_TO_STRING(DeviceDescriptor->RemovableMedia),
|
|
BOOLEAN_TO_STRING(DeviceDescriptor->CommandQueueing),
|
|
vendorId,
|
|
productId,
|
|
productRevision,
|
|
serialNumber,
|
|
busType,
|
|
(DeviceDescriptor->RawPropertiesLength ? "Follow" : "None"));
|
|
|
|
if (DeviceDescriptor->RawPropertiesLength != 0) {
|
|
PrintBuffer(DeviceDescriptor->RawDeviceProperties,
|
|
DeviceDescriptor->RawPropertiesLength);
|
|
}
|
|
|
|
printf("\n\n");
|
|
}
|
|
|
|
//
|
|
// Gets (and prints) the device and adapter descriptor
|
|
// for a device. Returns the alignment mask (required
|
|
// for allocating a properly aligned buffer).
|
|
//
|
|
BOOL
|
|
GetAlignmentMaskForDevice(
|
|
IN HANDLE DeviceHandle,
|
|
OUT PULONG AlignmentMask
|
|
)
|
|
{
|
|
PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor = NULL;
|
|
PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = NULL;
|
|
STORAGE_DESCRIPTOR_HEADER header = {0};
|
|
|
|
BOOL ok = TRUE;
|
|
BOOL failed = TRUE;
|
|
ULONG i;
|
|
|
|
*AlignmentMask = 0; // default to no alignment
|
|
|
|
// Loop twice:
|
|
// First, get size required for storage adapter descriptor
|
|
// Second, allocate and retrieve storage adapter descriptor
|
|
// Third, get size required for storage device descriptor
|
|
// Fourth, allocate and retrieve storage device descriptor
|
|
for (i=0;i<4;i++) {
|
|
|
|
PVOID buffer;
|
|
ULONG bufferSize;
|
|
ULONG returnedData;
|
|
|
|
STORAGE_PROPERTY_QUERY query = {0};
|
|
|
|
switch(i) {
|
|
case 0: {
|
|
query.QueryType = PropertyStandardQuery;
|
|
query.PropertyId = StorageAdapterProperty;
|
|
bufferSize = sizeof(STORAGE_DESCRIPTOR_HEADER);
|
|
buffer = &header;
|
|
break;
|
|
}
|
|
case 1: {
|
|
query.QueryType = PropertyStandardQuery;
|
|
query.PropertyId = StorageAdapterProperty;
|
|
bufferSize = header.Size;
|
|
if (bufferSize != 0) {
|
|
adapterDescriptor = LocalAlloc(LPTR, bufferSize);
|
|
if (adapterDescriptor == NULL) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
buffer = adapterDescriptor;
|
|
break;
|
|
}
|
|
case 2: {
|
|
query.QueryType = PropertyStandardQuery;
|
|
query.PropertyId = StorageDeviceProperty;
|
|
bufferSize = sizeof(STORAGE_DESCRIPTOR_HEADER);
|
|
buffer = &header;
|
|
break;
|
|
}
|
|
case 3: {
|
|
query.QueryType = PropertyStandardQuery;
|
|
query.PropertyId = StorageDeviceProperty;
|
|
bufferSize = header.Size;
|
|
|
|
if (bufferSize != 0) {
|
|
deviceDescriptor = LocalAlloc(LPTR, bufferSize);
|
|
if (deviceDescriptor == NULL) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
buffer = deviceDescriptor;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// buffer can be NULL if the property queried DNE.
|
|
if (buffer != NULL) {
|
|
RtlZeroMemory(buffer, bufferSize);
|
|
|
|
// all setup, do the ioctl
|
|
ok = DeviceIoControl(DeviceHandle,
|
|
IOCTL_STORAGE_QUERY_PROPERTY,
|
|
&query,
|
|
sizeof(STORAGE_PROPERTY_QUERY),
|
|
buffer,
|
|
bufferSize,
|
|
&returnedData,
|
|
FALSE);
|
|
if (!ok) {
|
|
if (GetLastError() == ERROR_MORE_DATA) {
|
|
// this is ok, we'll ignore it here
|
|
} else if (GetLastError() == ERROR_INVALID_FUNCTION) {
|
|
// this is also ok, the property DNE
|
|
} else if (GetLastError() == ERROR_NOT_SUPPORTED) {
|
|
// this is also ok, the property DNE
|
|
} else {
|
|
// some unexpected error -- exit out
|
|
goto Cleanup;
|
|
}
|
|
// zero it out, just in case it was partially filled in.
|
|
RtlZeroMemory(buffer, bufferSize);
|
|
}
|
|
}
|
|
} // end i loop
|
|
|
|
// adapterDescriptor is now allocated and full of data.
|
|
// deviceDescriptor is now allocated and full of data.
|
|
|
|
if (adapterDescriptor == NULL) {
|
|
printf(" ***** No adapter descriptor supported on the device *****\n");
|
|
} else {
|
|
PrintAdapterDescriptor(adapterDescriptor);
|
|
*AlignmentMask = adapterDescriptor->AlignmentMask;
|
|
}
|
|
|
|
if (deviceDescriptor == NULL) {
|
|
printf(" ***** No device descriptor supported on the device *****\n");
|
|
} else {
|
|
PrintDeviceDescriptor(deviceDescriptor);
|
|
}
|
|
|
|
failed = FALSE;
|
|
|
|
Cleanup:
|
|
if (adapterDescriptor != NULL) {
|
|
LocalFree( adapterDescriptor );
|
|
}
|
|
if (deviceDescriptor != NULL) {
|
|
LocalFree( deviceDescriptor );
|
|
}
|
|
|
|
return (!failed);
|
|
}
|
|
|
|
|
|
BOOL
|
|
pAllocateAlignedBuffer(
|
|
PSPT_ALIGNED_MEMORY Allocation,
|
|
ULONG AlignmentMask,
|
|
SIZE_T AllocationSize,
|
|
PUCHAR File,
|
|
ULONG Line
|
|
)
|
|
{
|
|
SIZE_T allocSize;
|
|
|
|
if (Allocation->A != NULL)
|
|
{
|
|
// ASSERT(FALSE);
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
if (Allocation->U != NULL)
|
|
{
|
|
// ASSERT(FALSE);
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
if (Allocation->File != NULL)
|
|
{
|
|
// ASSERT(FALSE);
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
if (Allocation->Line != 0)
|
|
{
|
|
// ASSERT(FALSE);
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(Allocation, sizeof( SPT_ALIGNED_MEMORY ));
|
|
|
|
if ( AllocationSize > (((SIZE_T)-1) >> 2) )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
if (AlignmentMask == ((ULONG)-1))
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
if (CountOfSetBits(AlignmentMask+1) != 1)
|
|
{
|
|
printf("Alignment mask (%x) is invalid -- all bits from the highest set "
|
|
"bit must be set to one\n", AlignmentMask);
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer large enough to guarantee that there exists
|
|
// within the allocation an aligned address with sufficient length
|
|
//
|
|
Allocation->U = LocalAlloc( LPTR, AllocationSize + AlignmentMask );
|
|
if (Allocation->U == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now fill in the remainder of the structure
|
|
//
|
|
Allocation->A = (PVOID)( ((ULONG_PTR)Allocation->U) + AlignmentMask );
|
|
Allocation->A = (PVOID)( ((ULONG_PTR)Allocation->A) & (~((ULONG_PTR)AlignmentMask)) );
|
|
Allocation->File = File;
|
|
Allocation->Line = Line;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
FreeAlignedBuffer(
|
|
PSPT_ALIGNED_MEMORY Allocation
|
|
)
|
|
{
|
|
// ASSERT( Allocation->U != NULL );
|
|
// ASSERT( Allocation->A != NULL );
|
|
// ASSERT( Allocation->File != NULL );
|
|
// ASSERT( Allocation->Line != 0 );
|
|
|
|
if (Allocation->U != NULL)
|
|
{
|
|
LocalFree( Allocation->U );
|
|
}
|
|
RtlZeroMemory( Allocation, sizeof(SPT_ALIGNED_MEMORY) );
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
__cdecl
|
|
main(
|
|
int argc,
|
|
char *argv[]
|
|
)
|
|
{
|
|
HANDLE deviceHandle;
|
|
SPTI_OPTIONS options;
|
|
ULONG alignmentMask;
|
|
RtlZeroMemory(&options, sizeof(SPTI_OPTIONS));
|
|
|
|
|
|
// first, verify we have proper arguments
|
|
if (!ParseArguments(argc, argv, &options))
|
|
{
|
|
PrintUsageInfo(argv[0]);
|
|
return RETURN_BAD_ARGS;
|
|
}
|
|
|
|
// open the device as requested
|
|
{
|
|
#define MAX_LENGTH 250
|
|
|
|
UCHAR buffer[MAX_LENGTH];
|
|
HRESULT hr;
|
|
DWORD shareFlags;
|
|
|
|
hr = StringCchPrintf(buffer,
|
|
sizeof(buffer)/sizeof(buffer[0]),
|
|
"\\\\.\\%s",
|
|
options.PortName
|
|
);
|
|
if (!SUCCEEDED(hr)) {
|
|
puts("Port name exceeded internal length limit");
|
|
return RETURN_NAME_TOO_LONG;
|
|
}
|
|
|
|
shareFlags = 0;
|
|
if (options.SharedRead)
|
|
{
|
|
SET_FLAG( shareFlags, FILE_SHARE_READ );
|
|
}
|
|
if (options.SharedWrite)
|
|
{
|
|
SET_FLAG( shareFlags, FILE_SHARE_WRITE );
|
|
}
|
|
|
|
deviceHandle = CreateFile(buffer,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
shareFlags,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if(deviceHandle == INVALID_HANDLE_VALUE) {
|
|
printf("Error opening device %s\n", buffer);
|
|
PrintError(GetLastError());
|
|
return RETURN_UNABLE_TO_OPEN_DEVICE;
|
|
}
|
|
|
|
}
|
|
|
|
// lock the volume if requested
|
|
if (options.LockVolume)
|
|
{
|
|
if (!LockVolume(deviceHandle, &options))
|
|
{
|
|
puts("Error locking volume");
|
|
PrintError(GetLastError());
|
|
return RETURN_UNABLE_TO_LOCK_VOLUME;
|
|
}
|
|
}
|
|
|
|
// get device information, such as the alignment mask
|
|
if (!GetAlignmentMaskForDevice(deviceHandle, &alignmentMask))
|
|
{
|
|
puts("Unable to get alignment mask for device");
|
|
PrintError(GetLastError());
|
|
return RETURN_UNABLE_TO_GET_ALIGNMENT_MASK;
|
|
}
|
|
|
|
printf("\n"
|
|
" ***** Detected Alignment Mask *****\n"
|
|
" ***** was %08x *****\n\n",
|
|
alignmentMask);
|
|
|
|
|
|
|
|
|
|
|
|
puts("");
|
|
puts(" ***** MODE SENSE10 -- return all pages *****");
|
|
puts(" ***** with SenseInfo buffer *****\n");
|
|
{
|
|
SPT_ALIGNED_MEMORY alignedAllocation;
|
|
DWORD allocationSize = MAXIMUM_BUFFER_SIZE;
|
|
|
|
|
|
RtlZeroMemory( &alignedAllocation, sizeof(SPT_ALIGNED_MEMORY) );
|
|
|
|
if (!AllocateAlignedBuffer(&alignedAllocation,
|
|
alignmentMask,
|
|
allocationSize))
|
|
{
|
|
puts("Unable to allocate memory for MODE_SENSE10");
|
|
PrintError(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
CDB cdb;
|
|
SENSE_DATA sense;
|
|
RtlZeroMemory( &cdb, sizeof(CDB) );
|
|
RtlZeroMemory( &sense, sizeof(SENSE_DATA) );
|
|
|
|
cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
|
cdb.MODE_SENSE10.PageCode = MODE_SENSE_RETURN_ALL;
|
|
cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(allocationSize >> (8*1));
|
|
cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(allocationSize >> (8*0));
|
|
|
|
if (!SptSendCdbToDeviceEx(deviceHandle,
|
|
&cdb,
|
|
sizeof( cdb.MODE_SENSE10 ),
|
|
alignedAllocation.A,
|
|
&allocationSize,
|
|
&sense,
|
|
sizeof( SENSE_DATA ),
|
|
TRUE,
|
|
SPT_MODE_SENSE_TIMEOUT))
|
|
{
|
|
puts("Unable to send command to device");
|
|
|
|
if (GetLastError() == ERROR_SUCCESS)
|
|
{
|
|
puts("Inquiry data may be valid\n");
|
|
printf("Failed %02x/%02x/%02x, full sense data:\n",
|
|
sense.SenseKey,
|
|
sense.AdditionalSenseCode,
|
|
sense.AdditionalSenseCodeQualifier
|
|
);
|
|
PrintBuffer(&sense, sizeof(SENSE_DATA));
|
|
}
|
|
else
|
|
{
|
|
PrintError(GetLastError());
|
|
}
|
|
|
|
}
|
|
else if (sense.SenseKey != SCSI_SENSE_NO_SENSE)
|
|
{
|
|
printf("Failed %02x/%02x/%02x, full sense data:\n",
|
|
sense.SenseKey,
|
|
sense.AdditionalSenseCode,
|
|
sense.AdditionalSenseCodeQualifier
|
|
);
|
|
PrintBuffer(&sense, sizeof(SENSE_DATA));
|
|
}
|
|
else
|
|
{
|
|
printf("%x bytes returned:\n", allocationSize);
|
|
PrintBuffer(alignedAllocation.A, allocationSize);
|
|
}
|
|
|
|
FreeAlignedBuffer( &alignedAllocation );
|
|
}
|
|
}
|
|
|
|
puts("");
|
|
puts(" ***** MODE SENSE10 -- return all pages *****");
|
|
puts(" ***** without SenseInfo buffer *****\n");
|
|
{
|
|
SPT_ALIGNED_MEMORY alignedAllocation;
|
|
DWORD allocationSize = MAXIMUM_BUFFER_SIZE;
|
|
|
|
RtlZeroMemory( &alignedAllocation, sizeof(SPT_ALIGNED_MEMORY) );
|
|
|
|
if (!AllocateAlignedBuffer(&alignedAllocation,
|
|
alignmentMask,
|
|
allocationSize))
|
|
{
|
|
puts("Unable to allocate memory for MODE_SENSE10");
|
|
PrintError(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
CDB cdb;
|
|
RtlZeroMemory( &cdb, sizeof(CDB) );
|
|
|
|
cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
|
cdb.MODE_SENSE10.PageCode = MODE_SENSE_RETURN_ALL;
|
|
cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(allocationSize >> (8*1));
|
|
cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(allocationSize >> (8*0));
|
|
|
|
if (!SptSendCdbToDeviceEx(deviceHandle,
|
|
&cdb,
|
|
sizeof( cdb.MODE_SENSE10 ),
|
|
alignedAllocation.A,
|
|
&allocationSize,
|
|
NULL,
|
|
0,
|
|
TRUE,
|
|
SPT_MODE_SENSE_TIMEOUT))
|
|
{
|
|
PrintError(GetLastError());
|
|
puts("Unable to send command to device");
|
|
}
|
|
else
|
|
{
|
|
printf("%x bytes returned:\n", allocationSize);
|
|
PrintBuffer(alignedAllocation.A, allocationSize);
|
|
}
|
|
|
|
FreeAlignedBuffer( &alignedAllocation );
|
|
}
|
|
}
|
|
|
|
puts("");
|
|
puts(" ***** TEST UNIT READY *****\n");
|
|
puts(" ***** DataBufferLength = 0 *****\n\n");
|
|
{
|
|
CDB cdb;
|
|
SENSE_DATA sense;
|
|
DWORD allocationSize = 0;
|
|
RtlZeroMemory( &cdb, sizeof(CDB) );
|
|
RtlZeroMemory( &sense, sizeof(SENSE_DATA) );
|
|
|
|
cdb.CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
|
|
|
|
if (!SptSendCdbToDeviceEx(deviceHandle,
|
|
&cdb,
|
|
sizeof( cdb.CDB6GENERIC ),
|
|
NULL,
|
|
&allocationSize,
|
|
&sense,
|
|
sizeof( SENSE_DATA ),
|
|
FALSE, // not sending anything to the device
|
|
SPT_DEFAULT_TIMEOUT))
|
|
{
|
|
puts("Unable to send command to device");
|
|
|
|
if (GetLastError() == ERROR_SUCCESS)
|
|
{
|
|
puts("Inquiry data may be valid\n");
|
|
printf("Failed %02x/%02x/%02x, full sense data:\n",
|
|
sense.SenseKey,
|
|
sense.AdditionalSenseCode,
|
|
sense.AdditionalSenseCodeQualifier
|
|
);
|
|
PrintBuffer(&sense, sizeof(SENSE_DATA));
|
|
}
|
|
else
|
|
{
|
|
PrintError(GetLastError());
|
|
}
|
|
}
|
|
else if (sense.SenseKey != SCSI_SENSE_NO_SENSE)
|
|
{
|
|
printf("Failed %02x/%02x/%02x, full sense data:\n",
|
|
sense.SenseKey,
|
|
sense.AdditionalSenseCode,
|
|
sense.AdditionalSenseCodeQualifier
|
|
);
|
|
PrintBuffer(&sense, sizeof(SENSE_DATA));
|
|
}
|
|
else
|
|
{
|
|
printf("%x bytes returned\n", allocationSize);
|
|
}
|
|
}
|
|
|
|
|
|
puts("");
|
|
puts(" ***** MODE SENSE10 -- return C/DVD *****");
|
|
puts(" ***** capabalities page only *****\n");
|
|
{
|
|
SPT_ALIGNED_MEMORY alignedAllocation;
|
|
DWORD allocationSize = MAXIMUM_BUFFER_SIZE;
|
|
|
|
RtlZeroMemory( &alignedAllocation, sizeof(SPT_ALIGNED_MEMORY) );
|
|
|
|
if (!AllocateAlignedBuffer(&alignedAllocation,
|
|
alignmentMask,
|
|
allocationSize))
|
|
{
|
|
puts("Unable to allocate memory for MODE_SENSE10");
|
|
PrintError(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
CDB cdb;
|
|
SENSE_DATA sense;
|
|
RtlZeroMemory( &cdb, sizeof(CDB) );
|
|
RtlZeroMemory( &sense, sizeof(SENSE_DATA) );
|
|
|
|
cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
|
cdb.MODE_SENSE10.PageCode = MODE_PAGE_CAPABILITIES;
|
|
cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(allocationSize >> (8*1));
|
|
cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(allocationSize >> (8*0));
|
|
|
|
if (!SptSendCdbToDeviceEx(deviceHandle,
|
|
&cdb,
|
|
sizeof( cdb.MODE_SENSE10 ),
|
|
alignedAllocation.A,
|
|
&allocationSize,
|
|
&sense,
|
|
sizeof( SENSE_DATA ),
|
|
TRUE,
|
|
SPT_MODE_SENSE_TIMEOUT))
|
|
{
|
|
puts("Unable to send command to device");
|
|
|
|
if (GetLastError() == ERROR_SUCCESS)
|
|
{
|
|
puts("Inquiry data may be valid\n");
|
|
printf("Failed %02x/%02x/%02x, full sense data:\n",
|
|
sense.SenseKey,
|
|
sense.AdditionalSenseCode,
|
|
sense.AdditionalSenseCodeQualifier
|
|
);
|
|
PrintBuffer(&sense, sizeof(SENSE_DATA));
|
|
}
|
|
else
|
|
{
|
|
PrintError(GetLastError());
|
|
}
|
|
}
|
|
else if (sense.SenseKey != SCSI_SENSE_NO_SENSE)
|
|
{
|
|
printf("Failed %02x/%02x/%02x, full sense data:\n",
|
|
sense.SenseKey,
|
|
sense.AdditionalSenseCode,
|
|
sense.AdditionalSenseCodeQualifier
|
|
);
|
|
PrintBuffer(&sense, sizeof(SENSE_DATA));
|
|
}
|
|
else
|
|
{
|
|
printf("%x bytes returned:\n", allocationSize);
|
|
PrintBuffer(alignedAllocation.A, allocationSize);
|
|
}
|
|
|
|
FreeAlignedBuffer( &alignedAllocation );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|