/*++

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 <windows.h>
#include <devioctl.h>
#include <ntdddisk.h>
#include <ntddscsi.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>

#include "spti.h"

#define NAME_COUNT  25

#define BOOLEAN_TO_STRING(_b_) \
( (_b_) ? "True" : "False" )

#if defined(_X86_)
    #define PAGE_SIZE  0x1000
    #define PAGE_SHIFT 12L
#elif defined(_AMD64_)
    #define PAGE_SIZE  0x1000
    #define PAGE_SHIFT 12L
#elif defined(_IA64_)
    #define PAGE_SIZE 0x2000
    #define PAGE_SHIFT 13L
#else
    // undefined platform?
    #define PAGE_SIZE  0x1000
    #define PAGE_SHIFT 12L
#endif


PUCHAR BusTypeStrings[] = {
    "Unknown",
    "Scsi",
    "Atapi",
    "Ata",
    "1394",
    "Ssa",
    "Fibre",
    "Usb",
    "RAID",
    "Not Defined",
};
#define NUMBER_OF_BUS_TYPE_STRINGS (sizeof(BusTypeStrings)/sizeof(BusTypeStrings[0]))

SPTD	Sptd;    

/*++

Main entry point

--*/

VOID __cdecl main(int argc, char *argv[])
{
    BOOL	status = 0;
	SPTD	Sptd;
	PSPTD	pSptd;
    DWORD	accessMode = 0, shareMode = 0;
    HANDLE	fileHandle = NULL;
    ULONG	alignmentMask = 0; // default == no alignment requirement
    PUCHAR	dataBuffer = NULL;
    UCHAR	string[NAME_COUNT + 1];

    ULONG	length = 0,
			errorCode = 0,
			returned = 0,
			sectorSize = 512;

    if ((argc < 2) || (argc > 3)) {
       printf("Usage:  %s <port-name> [-mode]\n", argv[0] );
       printf("Examples:\n");
       printf("    spti g:       (open the disk class driver in SHARED READ/WRITE mode)\n");    
       printf("    spti Scsi2:   (open the miniport driver for the 3rd host adapter)\n");
       printf("    spti Tape0 w  (open the tape class driver in SHARED WRITE mode)\n");
       printf("    spti i: c     (open the CD-ROM class driver in SHARED READ mode)\n");
       return;
    }

    _snprintf(string, NAME_COUNT, "\\\\.\\%s", argv[1]);
    string[NAME_COUNT] = 0;

    shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;  // default
    accessMode = GENERIC_WRITE | GENERIC_READ;       // default

    if (argc == 3) {

        switch(tolower(argv[2][0])) {
            case 'r':
                shareMode = FILE_SHARE_READ;
                break;

            case 'w':
                shareMode = FILE_SHARE_WRITE;
                break;

            case 'c':
                shareMode = FILE_SHARE_READ;
                sectorSize = 2048;
                break;

            default:
                printf("%s is an invalid mode.\n", argv[2]);
                puts("\tr = read");
                puts("\tw = write");
                puts("\tc = read CD (2048 byte sector mode)");
                return;
        }
    }

    fileHandle = CreateFile(string,
       accessMode,
       shareMode,
       NULL,
       OPEN_EXISTING,
       0,
       NULL);

    if (fileHandle == INVALID_HANDLE_VALUE) {
        errorCode = GetLastError();
        printf("Error opening %s. Error: %d\n",
               string, errorCode);
        PrintError(errorCode);
        return;
    }

    //
    // Get the memory alignment requirements for this device
    //

    status = GetAlignmentMaskForDevice(fileHandle, &alignmentMask);
    if (!status ) {
        errorCode = GetLastError();
        printf("Error getting device and/or adapter properties; "
               "error was %d\n", errorCode);
        PrintError(errorCode);
        return;
    }

	puts("*********************************************************************");
	puts("*           Testing SCSI command: TEST UNIT READY                   *");
	puts("*********************************************************************\n");


    pSptd = AllocateSPTD(SPTD_TYPE_BUFFERED, 256, 0);
	Sptd.Bio.CdbLength = CDB6GENERIC_LENGTH;
    Sptd.Bio.DataIn = SCSI_IOCTL_DATA_IN;
    Sptd.Bio.TimeOutValue = 2;
    Sptd.Bio.Cdb[0] = SCSIOP_TEST_UNIT_READY;
    status = DeviceIoControl(fileHandle,
                             IOCTL_SCSI_PASS_THROUGH,
                             &(pSptd->Bio),
                             pSptd->Bio.Length,
                             &(pSptd->Bio),
                             pSptd->Bio.Length,
                             &returned,
                             FALSE);
    PrintSptStatus(status, returned, &Sptd,length);
	puts("\n");

	puts("*********************************************************************");
	puts("*           Testing SCSI command: MODE SENSE, ALL PAGES             *");
	puts("*********************************************************************\n");

    pSptd = AllocateSPTD(SPTD_TYPE_BUFFERED, 256, 0);
	assert(pSptd);
	Sptd.Bio.CdbLength = CDB6GENERIC_LENGTH;
    Sptd.Bio.DataIn = SCSI_IOCTL_DATA_IN;
    Sptd.Bio.TimeOutValue = 2;
    Sptd.Bio.Cdb[0] = SCSIOP_TEST_UNIT_READY;
    status = DeviceIoControl(fileHandle,
                             IOCTL_SCSI_PASS_THROUGH,
                             &(pSptd->Bio),
                             pSptd->dwIoctlLength,
                             &(pSptd->Bio),
                             pSptd->dwIoctlLength,
                             &returned,
                             FALSE);

    PrintSptStatus(status, returned, pSptd,length);
	if(pSptd->Bio.DataTransferLength)
	{
		puts("\n                         MODE PAGE DATA");
		puts("                         --------------\n\n");
		PrintDataBuffer(pSptd->Buffers, pSptd->Bio.DataTransferLength);
	}


	puts("*********************************************************************");
	puts("*           Testing SCSI command: WRITE (10)                        *");
	puts("*********************************************************************\n");

	// Allocate a BIO descriptor with 256 byte sense buffer, 512 byte data buffer

	pSptd = AllocateSPTD(SPTD_TYPE_BUFFERED, 256, 512);
	assert(pSptd);

	// Setup the direction and timeout for the transfer

    pSptd->Bio.DataIn = SCSI_IOCTL_DATA_OUT;
    pSptd->Bio.TimeOutValue = 2;
    
	// Setup the SCSI command data block

	pSptd->Bio.CdbLength = CDB10GENERIC_LENGTH;
    pSptd->Bio.Cdb[0] = SCSIOP_WRITE;			// Operation code
    pSptd->Bio.Cdb[3] = 0;                      // Starting LBN
    pSptd->Bio.Cdb[8] = 1;						// Transfer length in 512 byte blocks

	// Load the data into the output buffer

	SetMemory((((PUCHAR) &(pSptd->Bio)) + pSptd->Bio.DataBufferOffset), 512, "Osama slept here");

	// Call the driver

    status = DeviceIoControl(fileHandle,
                             IOCTL_SCSI_PASS_THROUGH,
                             &(pSptd->Bio),
                             length,
                             &(pSptd->Bio),
                             length,
                             &returned,
                             FALSE);

    PrintSptStatus(status, returned, pSptd,length);
	if(pSptd->Bio.DataTransferLength)
	{
		PrintDataBuffer(((PUCHAR) &(pSptd->Bio)) + pSptd->Bio.DataBufferOffset,
			            pSptd->Bio.DataTransferLength);
	}
	free(pSptd);

	puts("*********************************************************************");
	puts("*           Testing SCSI command: READ (10)                        *");
	puts("*********************************************************************\n");

	// Allocate a BIO descriptor with 256 byte sense buffer, 512 byte data buffer

	pSptd = AllocateSPTD(SPTD_TYPE_BUFFERED, 256, 512);
	assert(pSptd);

	// Setup the direction and timeout for the transfer

    pSptd->Bio.DataIn = SCSI_IOCTL_DATA_IN;
    pSptd->Bio.TimeOutValue = 2;
    
	// Setup the SCSI command data block

	pSptd->Bio.CdbLength = CDB10GENERIC_LENGTH;
    pSptd->Bio.Cdb[0] = SCSIOP_READ;			// Operation code
    pSptd->Bio.Cdb[3] = 0;                      // Starting LBN
    pSptd->Bio.Cdb[8] = 1;						// Transfer length in 512 byte blocks

	// Zero the input buffer

	ZeroMemory((&(pSptd->Bio)) + pSptd->Bio.DataBufferOffset, 512);

	// Call the driver

    status = DeviceIoControl(fileHandle,
                             IOCTL_SCSI_PASS_THROUGH,
                             &(pSptd->Bio),
                             length,
                             &(pSptd->Bio),
                             length,
                             &returned,
                             FALSE);

    PrintSptStatus(status, returned, pSptd,length);
	if(pSptd->Bio.DataTransferLength)
	{
		PrintDataBuffer(dataBuffer, pSptd->Bio.DataTransferLength);
	}
}
/*++

Get caching mode sense page.

Input:

	dHandle - a handle to the device to be queried

Ouput:

	None

Abstract:

	Issues a SenseMode request to a SCSI device to obtain the Caching
	Mode page and fprints the returned data to the standard out

--*/
/*
VOID PrintCachingModePage(HANDLE dHandle)
{
	BOOL status;
	ULONG length = 0;
	ULONG errorCode = 0;
	ULONG returned = 0;

	printf(" *****  Caching Mode Sense Page  *****\n");

    ZeroMemory(&sptwb,sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS));
    sptwb.spt.Length = sizeof(SCSI_PASS_THROUGH);
    sptwb.spt.PathId = 0;
    sptwb.spt.TargetId = 1;
    sptwb.spt.Lun = 0;
    sptwb.spt.CdbLength = CDB6GENERIC_LENGTH;
    sptwb.spt.SenseInfoLength = SPT_SENSE_LENGTH;
    sptwb.spt.DataIn = SCSI_IOCTL_DATA_IN;
    sptwb.spt.DataTransferLength = 192;
    sptwb.spt.TimeOutValue = 2;
    sptwb.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf);
    sptwb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucSenseBuf);
    sptwb.spt.Cdb[0] = SCSIOP_MODE_SENSE;
    sptwb.spt.Cdb[1] = 0x08; // target shall not return any block descriptors
    sptwb.spt.Cdb[2] = MODE_PAGE_CACHING;
    sptwb.spt.Cdb[4] = 192;
    length = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf) +  sptwb.spt.DataTransferLength;
    status = DeviceIoControl(dHandle,
                             IOCTL_SCSI_PASS_THROUGH,
                             &sptwb,
                             sizeof(SCSI_PASS_THROUGH),
                             &sptwb,
                             length,
                             &returned,
                             FALSE);

    PrintStatusResults(status,returned,&sptwb,length);
}
*/

VOID
PrintDataBuffer(PUCHAR DataBuffer, ULONG BufferLength)
{
    ULONG Cnt, index;
	TCHAR szData[17];;

	printf("                                      Dump of %d byte buffer @%8.8x\n", BufferLength,DataBuffer);
    printf("                                      ----------------------------\n\n");
    for (Cnt = 0, index=0; Cnt < BufferLength; Cnt++)
	{
       if ((Cnt) % 16 == 0)
	   {
          printf(" %03X  ",Cnt);
		  memset(szData, 0, sizeof(szData));
       }
       printf("%02X  ", DataBuffer[Cnt]);
	   szData[Cnt%16] = __toascii(DataBuffer[Cnt]);
       if ((Cnt+1) % 8 == 0)
	   {
          printf(" ");
       }
       if ((Cnt+1) % 16 == 0)
	   {
          printf(" %s\n", szData);

       }
    }
    printf("\n\n");
}

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 *** TARGET STORAGE ADAPTER DESCRIPTOR *****\n\n");
    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");
}

/*++

PrintDeviceDescriptor

Abstract

	Formats and prints the contents of a device descriptor to the
	standard output
	
Input

	DeviceDescriptor - a pointer to a STORAGE_DEVICE_DESCRIPTOR
	                   containing the data to be printed
					   
Output

	None
	
--*/

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 ***** TARGET STORAGE DEVICE DESCRIPTOR *****\n\n");
    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 ? "Follows\n" : "None\n"));
    if (DeviceDescriptor->RawPropertiesLength != 0) {
        PrintDataBuffer(DeviceDescriptor->RawDeviceProperties,
                        DeviceDescriptor->RawPropertiesLength);
    }
    printf("\n\n");
}



PUCHAR AllocateAlignedBuffer(ULONG size, ULONG AlignmentMask)
{
    PUCHAR ptr;

    // NOTE: This routine does not allow for a way to free
    //       memory.  This is an excercise left for the reader.
    UINT_PTR    align64 = (UINT_PTR)AlignmentMask;

    if (AlignmentMask == 0) {
       ptr = malloc(size);
    } else {
       ptr = malloc(size + AlignmentMask);
       ptr = (PUCHAR)(((UINT_PTR)ptr + align64) & ~align64);
    }
    
    if (ptr == NULL) {
       printf("Memory allocation error.  Terminating program\n");
       exit(1);
    } else {
       return ptr;
    }
}

/**+

Routine Name:

	PrintSptStatus

Description:

	This routine prints a summary of the return status of a DEVICEIOCONTROL call with a 
	function code of IOCTL_SCSI_PASS_THROUGH

Parameters:

	status		[IN} - the boolean status returned by DeviceIoControl()

	cbOut		[IN] - the number of bytes returned in the output buffer by DeviceIoControl()

	pOut		[IN] - a pointer to the output bufferpassed to DeviceIoControl()

    dwOutLen	[IN] - the length in bytes of the output buffer passed to DeviceIoControl()
	
Return value:

	None

--*/
	 
VOID PrintSptStatus(BOOL status, DWORD cbOut, PSPTD pSptd, ULONG cbSptd)
{
    ULONG errorCode;
	
	printf(" ");
	PrintError((status) ? 0 : GetLastError());
	switch (pSptd->Type)
	{
		case SPTD_TYPE_DIRECT:
			printf(" SCSI status %02Xh, %d bytes transferred\n",
				    pSptd->Dio.ScsiStatus,
					pSptd->Dio.DataTransferLength);
			return;

		case SPTD_TYPE_BUFFERED:

			printf(" SCSI status %02Xh, %d bytes transferred\n",
				    pSptd->Bio.ScsiStatus,
					pSptd->Bio.DataTransferLength);
			return;

		default:
			BUGCHECK("Invalid descriptor type");
	}
}

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);

}

/*++

Routine Description:

    Prints formatted error message

Arguments:

    ErrorCode   - Error code to print


Return Value:
    
      None
--*/

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", errorBuffer);
    } else {
        printf("Format message failed.  Error: %d\n", GetLastError());
    }
}

/*++

  SetMemory - set a region of memory to a user supplied data pattern
	

  Arguments:

    pDst		[IN] - Starting address of memory to be filled

	cbCount		[IN] - number of bytes to be filled

	pzPattern	[IN] - pointer to a string containing the fill pattern

 Description

	The pattern provided is repeatedly copied to the memory address
	specified until the entire range of addresses has been filled. The
	string terminator is not copied.

--*/

VOID SetMemory(PUCHAR pDst, DWORD cbCount, LPTSTR pszPattern)
{
	int plen, index;
	
	plen = strlen(pszPattern);
	index = 0;
	while (cbCount--)
	{
		*pDst++ = pszPattern[index++];
		if(index == plen)
			index = 0;
	}
}

/*++

  Routine:		AllocateSPTD

				Allocates and initializes an SCSI pass through IOCTL descriptor
  Parameters:

		dwType	[IN]			-	a DWORD value specifying the type of descriptor to be
									allocated

										SPTD_TYPE_BUFFERED - allocate a buffererd I/O descriptor
										SPTD_TYPE_DIRECT   - allocate a direct I/O descriptor

		dwSenseInfoLength [IN]	-	specifies the number of bytes of buffer space to allocate for the
									Sense data buffer and may be zero. This value is always rounded
									up to a multiple of DWORD length, The actual length allocated
									is returned in the SenseInfoLength member of the descriptor.
									The Sense info buffer always begins at SPDT.Buffers[0];

		dwDataTransferSize[IN]	-	specifies the number of bytes of data buffer space to allocate
									for the descriptor and may be zero. It must be zero if the
									descriptor type is SPTD_TYPE_DIRECT. The data buffer always
									begins at at SPTD.Buffers[SPTD.Bio.SenseInfLength]

  Returns:

		A pointer to the newly allocted descriptor or NULL if the call fails.  Use GetLastError()
		to determine the cause of the failure. 

 --*/

PSPTD AllocateSPTD(ULONG dwType, ULONG dwSenseInfoLength, ULONG dwDataTransferSize)
{							  
	PSPTD pSptd;
	DWORD dwBufferSize;

	// Validate parameters

	if((dwType == SPTD_TYPE_DIRECT) && dwDataTransferSize)
	{
		SetLastError(ERROR_INVALID_PARAMETER);
		return 0;
	}
	

	// Allocate descriptor and optional buffers

	dwSenseInfoLength += (dwSenseInfoLength % sizeof(DWORD));
	dwBufferSize = dwSenseInfoLength + dwDataTransferSize;
	pSptd = calloc(sizeof(SPTD) + dwBufferSize, 1);
	if(!pSptd)
	{
		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
		return pSptd;
	}
	pSptd->Type = dwType;

	// Initialize the buffer offsets and lengths in the descriptor

	if(pSptd->Type = SPTD_TYPE_BUFFERED)
	{
		pSptd->Bio.Length = (USHORT) (sizeof(SPTD) - offsetof(SPTD, Bio) + dwBufferSize);
		pSptd->Bio.SenseInfoOffset =  offsetof(SPTD, Buffers);
		pSptd->Bio.SenseInfoLength = (UCHAR) dwSenseInfoLength;
		if(dwDataTransferSize)
		{
			pSptd->Bio.DataTransferLength = dwDataTransferSize;
			pSptd->Bio.DataBufferOffset = pSptd->Bio.SenseInfoOffset + dwSenseInfoLength;
		}
		printf("BIO Descriptor\n______________\n");
		printf(" SPT Length:%d\n", pSptd->Bio.Length);
		printf(" Sense Info Offset: %d\n", pSptd->Bio.SenseInfoOffset);
		printf(" Sense Info Length: %d\n", pSptd->Bio.SenseInfoLength);
		printf(" Data Buffer Offset: %d\n", pSptd->Bio.DataBufferOffset);
		printf(" Data Transfer Length: %d\n", pSptd->Bio.DataTransferLength);
	}
	else
	{
		pSptd->Dio.Length = (USHORT) (sizeof(SPTD) - offsetof(SPTD, Dio) + dwBufferSize);
		pSptd->Dio.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
		if(dwSenseInfoLength)
		{
			pSptd->Dio.SenseInfoOffset = offsetof(SPTD, Buffers);
			pSptd->Dio.SenseInfoLength = (UCHAR) dwSenseInfoLength;
		}
		printf("DIO Descriptor\n______________\n");
		printf(" SPTD Length:%d\n", pSptd->Dio.Length);
		printf(" Sense Info Offset: %d\n", pSptd->Dio.SenseInfoOffset);
		printf(" Sense Info Length: %d\n", pSptd->Dio.SenseInfoLength);
	}

	// Return the completed descriptor

	return pSptd;
}
