|
|
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
cdp.c
Abstract:
A user mode app that allows simple commands to be sent to a selected scsi device.
Environment:
User mode only
Revision History:
03-26-96 : Created
--*/
//
// this module may be compiled at warning level 4 with the following
// warnings disabled:
//
#pragma warning(disable:4200) // array[0]
#pragma warning(disable:4201) // nameless struct/unions
#pragma warning(disable:4214) // bit fields other than int
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <windows.h>
#include <devioctl.h>
#include <ntddscsi.h>
#include <ntddstor.h>
#include <ntddcdrm.h>
#include <ntdddisk.h>
#include <ntddcdvd.h>
#include <ntddmmc.h>
#define _NTSRB_ // to keep srb.h from being included
#include <scsi.h>
#include "sptlib.h"
#include "cmdhelp.h"
#define MAX_IOCTL_INPUT_SIZE 0x040
#define MAX_IOCTL_OUTPUT_SIZE 0x930 // IOCTL_CDROM_RAW_READ is this large
#define MAX_IOCTL_BUFFER_SIZE (max(MAX_IOCTL_INPUT_SIZE, MAX_IOCTL_OUTPUT_SIZE))
// read no more than 64k at a time -- lots of things just don't support it.
#define MAX_READ_SIZE (64 * 1024)
#define CDRW_WRITE_SECTORS (32)
#define CDRW_WRITE_BYTES (CDRW_WRITE_SECTORS*2048)
////////////////////////////////////////////////////////////////////////////////
#define MSF_TO_LBA(Minutes,Seconds,Frames) \
(ULONG)((60 * 75 * (Minutes) ) + (75 * (Seconds)) + ((Frames) - 150))
#define LBA_TO_MSF(Lba, Minutes, Seconds, Frames) \
{ \ (Minutes) = (UCHAR)( ((Lba) + 150) / (60 * 75) ); \ (Seconds) = (UCHAR)((((Lba) + 150) % (60 * 75)) / 75); \ (Frames) = (UCHAR)((((Lba) + 150) % (60 * 75)) % 75); \ } ////////////////////////////////////////////////////////////////////////////////
#ifdef DBG
#define dbg(x) x
#define HELP_ME() fprintf(stderr, "Reached line %4d\n", __LINE__)
#else
#define dbg(x) /* x */
#define HELP_ME() /* printf("Reached line %4d\n", __LINE__) */
#endif
#define ARGUMENT_USED(x) (x == NULL)
typedef struct { char *Name; char *Description; DWORD (*Function)(HANDLE device, int argc, char *argv[]); } COMMAND;
typedef struct { SCSI_PASS_THROUGH Spt; char SenseInfoBuffer[18]; char DataBuffer[0]; // Allocate buffer space
// after this
} SPT_WITH_BUFFERS, *PSPT_WITH_BUFFERS;
DWORD WaitForReadDiscInfoCommand(HANDLE device, int argc, char *argv[]); DWORD ShowMrwProgressCommand(HANDLE device, int argc, char *argv[]); DWORD FormatMrwCommand(HANDLE device, int argc, char *argv[]); DWORD DvdReadStructure(HANDLE device, int argc, char *argv[]); DWORD StartStopCommand(HANDLE device, int argc, char *argv[]); DWORD TestCommand(HANDLE device, int argc, char *argv[]); DWORD ReadTOCCommand(HANDLE device, int argc, char *argv[]); DWORD ReadTOCExCommand(HANDLE device, int argc, char *argv[]); DWORD ReadCdTextCommand(HANDLE device, int argc, char *argv[]); DWORD PlayCommand(HANDLE device, int argc, char *argv[]); DWORD PauseResumeCommand(HANDLE device, int argc, char *argv[]); DWORD SendCommand(HANDLE device, int argc, char *argv[]); DWORD IoctlCommand(HANDLE device, int argc, char *argv[]); DWORD ListCommand(HANDLE device, int argc, char *argv[]); DWORD DiskGetPartitionInfo( HANDLE device, int argc, char *argv[]); DWORD FormatErrorCommand(HANDLE device, int argc, char *argv[]); DWORD ImageDiskCommand(HANDLE device, int argc, char *argv[]); DWORD MrwInitTestPatternCommand(HANDLE device, int argc, char *argv[]); DWORD MrwInitGaaFileSystem(HANDLE device, int argc, char *argv[]);
//
// List of commands
// all command names are case sensitive
// arguments are passed into command routines
// list must be terminated with NULL command
// command will not be listed in help if description == NULL
//
COMMAND CommandArray[] = { {"cdtext", "read cd text info ", ReadCdTextCommand }, {"dvdstruct", "Reads a dvd structure from the drive ", DvdReadStructure }, {"eject", "spins down and ejects the specified drive ", StartStopCommand }, {"error", "provides the error text for a winerror ", FormatErrorCommand }, {"help", "help for all commands ", ListCommand }, {"ioctl", "ioctl [quoted hex input] [output] sends an arbitrary ioctl", IoctlCommand }, {"image", "<file> images the storage device into the file ", ImageDiskCommand }, {"load", "loads the specified drive ", StartStopCommand }, {"mrwformat", NULL , FormatMrwCommand }, {"mrwgaa", NULL , MrwInitGaaFileSystem }, {"mrwprogress", NULL , ShowMrwProgressCommand }, {"mrwtest", NULL , MrwInitTestPatternCommand }, {"partition", "reads partition information ", DiskGetPartitionInfo }, {"pause", "pauses audio playback ", PauseResumeCommand }, {"play", "[start track [end track]] plays audio tracks ", PlayCommand }, {"resume", "resumes paused audio playback ", PauseResumeCommand }, {"send", NULL , SendCommand }, {"start", "spins up the drive ", StartStopCommand }, {"stop", "spinds down the drive ", StartStopCommand }, {"test", NULL , TestCommand }, {"toc", "prints the table of contents ", ReadTOCCommand }, {"tocex", NULL , ReadTOCExCommand }, {"wait", "Waits for the READ_DISC_INFO command to work ", WaitForReadDiscInfoCommand}, // {"tocex", "[Format [Session/Track [MSF]]] Read toc/cdtext/atip/etc.", ReadTOCExCommand},
{NULL, NULL, NULL} };
#define STATUS_SUCCESS 0
VOID PrintChar( IN UCHAR Char ) {
if ( (Char >= 0x21) && (Char <= 0x7E) ) { printf("%c", Char); } else { printf("%c", '.'); }
}
VOID UpdatePercentageDisplay(IN ULONG Numerator, IN ULONG Denominator) {
ULONG percent; ULONG i;
if (Numerator > Denominator) { return; }
// NOTE: Overflow possibility exists for large numerators.
percent = (Numerator * 100) / Denominator;
for (i=0;i<90;i++) { putchar('\b'); } printf("Complete: ");
// each block is 2%
// ----=----1----=----2----=----3----=----4----=----5----=----6----=----7----=----8
// Complete: �.....................
for (i=1; i<100; i+=2) { if (i < percent) { putchar(178); } else if (i == percent) { putchar(177); } else { putchar(176); } }
printf(" %d%% (%8x/%8x)", percent, Numerator, Denominator); }
int __cdecl main(int argc, char *argv[]) { int i = 0; HANDLE h; char buffer[80]; // ~50 chars for mountvol names
if(argc < 3) { printf("Usage: cdp <drive> <command> [parameters]\n"); printf("possible commands: \n"); ListCommand(NULL, argc, argv); printf("\n"); return -1; }
sprintf(buffer, "\\\\.\\%s", argv[1]); dbg(printf("Sending command %s to drive %s\n", argv[2], buffer));
h = CreateFile(buffer, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(h == INVALID_HANDLE_VALUE) { printf("Error %d opening device %s\n", GetLastError(), buffer); return -2; }
//
// Iterate through the command array and find the correct function to
// call.
//
while(CommandArray[i].Name != NULL) {
if(strcmp(argv[2], CommandArray[i].Name) == 0) {
(CommandArray[i].Function)(h, (argc - 2), &(argv[2]));
break; }
i++; }
if(CommandArray[i].Name == NULL) { printf("Unknown command %s\n", argv[2]); }
CloseHandle(h);
return 0; }
//
// take a PVOID as input -- it's cleaner throughout
//
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));
} //
// 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; }
DWORD StartStopCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Sends down a startstop command.
Arguments: device - a file handle to send the ioctl to
argc - the number of additional arguments. should be zero
argv[0] - "eject", "load", "start" or "stop"
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { DWORD errorValue = STATUS_SUCCESS; DWORD bufferSize; CDB cdb;
UCHAR loadEject = 0; UCHAR start = 0;
UNREFERENCED_PARAMETER(argc);
if(strcmp("eject", argv[0]) == 0) { loadEject = 1; start = 0; } else if(strcmp("load", argv[0]) == 0) { loadEject = 1; start = 1; } else if(strcmp("start", argv[0]) == 0) { loadEject = 0; start = 1; } else if(strcmp("stop", argv[0]) == 0) { loadEject = 0; start = 0; } else { assert(0); }
memset(&cdb, 0, sizeof(CDB)); cdb.START_STOP.OperationCode = SCSIOP_START_STOP_UNIT; cdb.START_STOP.Immediate = 0; cdb.START_STOP.Start = start; cdb.START_STOP.LoadEject = loadEject;
bufferSize = 0;
if (!SptSendCdbToDevice(device, &cdb, 6, NULL, &bufferSize, FALSE)) { errorValue = GetLastError(); printf("Eject - error sending IOCTL (%d)\n", errorValue); } return errorValue; }
DWORD ReadCdTextCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Reads and prints out the cdrom's table of contents, ATIP, PMA, or CDTEXT data
Arguments: device - a file handle to send the ioctl to
argc - the number of additional arguments. (1-4 is valid)
argv - the additional arguments
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { DWORD returned; LONG bufferSize = 4; // to get the header
DWORD i;
CDROM_READ_TOC_EX params; BOOLEAN isText = TRUE; PUCHAR buffer = NULL;
UNREFERENCED_PARAMETER(argv);
if (argc > 3) { printf("Too many args\n"); return 1; }
//
// set defaults - FORMAT_TOC, 0, 0
//
RtlZeroMemory(¶ms, sizeof(CDROM_READ_TOC_EX));
params.Msf = 0; params.SessionTrack = 1; params.Format = 5;
if(argc > 1) params.SessionTrack = (char)atoi(argv[1]);
printf("Session = 0x%x\n", params.SessionTrack);
for (i = 0; i < 2; i++) {
if (i != 0) { LocalFree(buffer); } buffer = LocalAlloc(LPTR, bufferSize);
if (buffer == NULL) { printf("No Memory %d\n", __LINE__); return 1; }
returned = 0; if (!DeviceIoControl(device, IOCTL_CDROM_READ_TOC_EX, ¶ms, sizeof(CDROM_READ_TOC_EX), buffer, bufferSize, &returned, FALSE)) { DWORD errorValue = GetLastError(); LocalFree(buffer); printf("Eject - error sending IOCTL (%d)\n", errorValue); return errorValue; } bufferSize = (buffer[0] << 8) | (buffer[1]); bufferSize += 2; }
if (argc > 2) { //
// this block is for debugging the various idiosynchracies found
// in CD-TEXT discs. Many discs encode multiple tracks in a single
// block. ie. if one song is called "ABBA", the second "Baby", and
// the third "Longer Name", the Text portion would be encoded as:
// Track 1 'ABBA\0Baby\0Lo'
// Track 3 'nger Name\0'
// This effectively "skips" the name available for Track 2 ?!
// How to work around this....
//
{ HANDLE h; DWORD temp; h = CreateFile("OUTPUT.TXT", GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if(h == INVALID_HANDLE_VALUE) { printf("Error %d creating new file \"OUTPUT.TXT\"\n", GetLastError()); LocalFree(buffer); return GetLastError(); } if (!WriteFile(h, buffer, bufferSize, &temp, NULL)) { printf("Error %d writing file to disk\n", GetLastError()); LocalFree(buffer); return GetLastError(); } // continue to output to screen....
}
for (i=0; FIELD_OFFSET(CDROM_TOC_CD_TEXT_DATA, Descriptors[i+1]) < bufferSize; i++) {
PCDROM_TOC_CD_TEXT_DATA_BLOCK block; PCDROM_TOC_CD_TEXT_DATA_BLOCK prevBlock; DWORD j;
block = (PCDROM_TOC_CD_TEXT_DATA_BLOCK)(buffer + 4); block += i; prevBlock = block - 1;
if (block->Unicode) { continue; // ignore unicode -- this is examplary only
}
for (j=0;j<12;j++) { // replace NULLs with *, Tabs with hashes
if (block->Text[j] == 0) block->Text[j] = '*'; if (block->Text[j] == 9) block->Text[j] = '#'; }
if (block->SequenceNumber > 0x2b && block->SequenceNumber < 0x32) {
UCHAR text[13]; RtlZeroMemory(text, 13); RtlCopyMemory(text, block->Text, 12);
printf("PackType %02x TrackNo %02x ExtensionFlag %d\n" "Sequence Number %02x CharacterPosition %02x\n" "Text: \"%s\"\n\n", block->PackType, block->TrackNumber, block->ExtensionFlag, block->SequenceNumber, block->CharacterPosition, text );
} } printf("\n");
} else {
for (i=0; FIELD_OFFSET(CDROM_TOC_CD_TEXT_DATA, Descriptors[i+1]) < bufferSize; i++) {
PCDROM_TOC_CD_TEXT_DATA_BLOCK block; PCDROM_TOC_CD_TEXT_DATA_BLOCK prevBlock; DWORD j;
block = (PCDROM_TOC_CD_TEXT_DATA_BLOCK)(buffer + 4); block += i; prevBlock = block - 1;
if (block->Unicode) { continue; // ignore unicode -- this is examplary only
}
//
// set the CRC's to zero so we can hack the data inside to more
// easily handle wierd cases....
//
block->CRC[0] = block->CRC[1] = 0;
//
// set the tab characters to '*' for now.
// i have not yet seen one using this "feature" of cd-text
//
for (j=0;j<12;j++) { if (block->Text[j] == 9) { block->Text[j] = '*'; } }
if ((i != 0) && (prevBlock->PackType == block->PackType) && (prevBlock->TrackNumber == block->TrackNumber) ) {
// continuation of previous setting.
} else if ((!(block->ExtensionFlag)) && (block->TrackNumber != 0) && (block->TrackNumber == (prevBlock->TrackNumber + 2)) && (block->PackType == prevBlock->PackType) ) {
UCHAR *goodText; UCHAR *midText; // printf("\"\n\"HACK DETECTED! (seq %x & %x)",
// prevBlock->SequenceNumber, block->SequenceNumber);
// hack for when prevBlock has two names encoded....
// the TrackNumber/PackType are already equal, just
// move the middle string to the start.
midText = prevBlock->Text; while (*midText != '\0') { midText++; } midText++;
goodText = prevBlock->Text; while (*midText != '\0') { *goodText++ = *midText++; } *goodText = '\0'; // printf(" %s", prevBlock->Text);
prevBlock->CharacterPosition = 0; prevBlock->TrackNumber++; prevBlock->ExtensionFlag = 1; i-= 2; continue; // re-run the previous, modified block
} else {
printf("\"\n"); switch (block->PackType) { case CDROM_CD_TEXT_PACK_ALBUM_NAME: { if (block->TrackNumber == 0) { printf("%-12s", "Album Name"); printf(" : \""); } else { printf("%-12s", "Track Name"); printf("(%02d): \"", block->TrackNumber); } break; } case CDROM_CD_TEXT_PACK_PERFORMER: { printf("%-12s", "Performer"); printf("(%02d): \"", block->TrackNumber); break; } case CDROM_CD_TEXT_PACK_SONGWRITER: { printf("%-12s", "Songwriter"); printf("(%02d): \"", block->TrackNumber); break; } case CDROM_CD_TEXT_PACK_COMPOSER: { printf("%-12s", "Composer"); printf("(%02d): \"", block->TrackNumber); break; } case CDROM_CD_TEXT_PACK_ARRANGER: { printf("%-12s", "Arranger"); printf("(%02d): \"", block->TrackNumber); break; } case CDROM_CD_TEXT_PACK_MESSAGES: { printf("%-12s", "Messages"); printf("(%02d): \"", block->TrackNumber); break; } case CDROM_CD_TEXT_PACK_DISC_ID: { printf("%-12s", "Disc ID"); printf(" : \""); break; } case CDROM_CD_TEXT_PACK_GENRE: { printf("%-12s", "Genre"); printf("(%02d): \"", block->TrackNumber); break; } case CDROM_CD_TEXT_PACK_UPC_EAN: { if (block->TrackNumber == 0) { printf("%-12s", "UPC/EAN"); printf(" : \""); } else { printf("%-12s", "ISRC"); printf("(%02d): \"", block->TrackNumber); } break; } case CDROM_CD_TEXT_PACK_TOC_INFO: case CDROM_CD_TEXT_PACK_TOC_INFO2: case CDROM_CD_TEXT_PACK_SIZE_INFO: default: { isText = FALSE; printf("Unknown type 0x%x: \"", block->PackType); } } // end switch
//
// have to print previous block's info, if available
//
if (isText && block->CharacterPosition != 0) { UCHAR text[13]; RtlZeroMemory(text, sizeof(text)); RtlCopyMemory(text, prevBlock->Text + 12 - block->CharacterPosition, block->CharacterPosition * sizeof(UCHAR)); printf("%s", text); }
} // end continuation case
if (isText) { UCHAR text[13]; RtlZeroMemory(text, sizeof(text)); RtlCopyMemory(text, block->Text, 12); printf("%s", text); }
} // end loop through all blocks
printf("\n");
} // end normal printout case
return 0; }
DWORD ReadTOCExCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Reads and prints out the cdrom's table of contents, ATIP, PMA, or CDTEXT data
Arguments: device - a file handle to send the ioctl to
argc - the number of additional arguments. (1-4 is valid)
argv - the additional arguments
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { DWORD returned; DWORD bufferSize = 4; // to get the header
DWORD i;
CDROM_READ_TOC_EX params;
UNREFERENCED_PARAMETER(argv);
if (argc > 4) { printf("Too many args\n"); return 1; }
//
// set defaults - FORMAT_TOC, 0, 0
//
RtlZeroMemory(¶ms, sizeof(CDROM_READ_TOC_EX));
if(argc > 3) params.Msf = (char)atoi(argv[3]); if(argc > 2) params.SessionTrack = (char)atoi(argv[2]); if(argc > 1) params.Format = (char)atoi(argv[1]);
printf("Params.Format = 0x%x\n", params.Format); printf("Params.SessionTrack = 0x%x\n", params.SessionTrack); printf("Params.MSF = 0x%x\n", params.Msf);
for (i = 0; i < 2; i++) {
PUCHAR buffer = LocalAlloc(LPTR, bufferSize);
if (buffer == NULL) { printf("No Memory %d\n", __LINE__); return 1; }
returned = 0; if (!DeviceIoControl(device, IOCTL_CDROM_READ_TOC_EX, ¶ms, sizeof(CDROM_READ_TOC_EX), buffer, bufferSize, &returned, FALSE)) { DWORD errorValue = GetLastError(); LocalFree(buffer); printf("Eject - error sending IOCTL (%d)\n", errorValue); return errorValue; }
printf("Successfully got %x bytes:\n", returned); PrintBuffer(buffer, returned);
bufferSize = (buffer[0] << 8) | (buffer[1]); LocalFree(buffer); bufferSize += 2; printf("Now getting %x bytes:\n", bufferSize);
} return 0; }
DWORD ReadTOCCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Reads and prints out the cdrom's table of contents
Arguments: device - a file handle to send the ioctl to
argc - the number of additional arguments. should be zero
argv - the additional arguments
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { DWORD errorValue = STATUS_SUCCESS; DWORD returned = 0; CDB cdb; CDROM_TOC toc;
int numTracks, i; PTRACK_DATA track;
UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv);
printf("Reading Table of Contents\n");
//
// Get the 4 byte TOC header
//
returned = FIELD_OFFSET(CDROM_TOC, TrackData[0]); memset(&cdb, 0, sizeof(CDB)); cdb.READ_TOC.OperationCode = SCSIOP_READ_TOC; cdb.READ_TOC.Msf = 0; cdb.READ_TOC.StartingTrack = 0; cdb.READ_TOC.AllocationLength[0] = (UCHAR)(returned >> 8); cdb.READ_TOC.AllocationLength[1] = (UCHAR)(returned & 0xff);
if (!SptSendCdbToDevice(device, &cdb, 10, (PUCHAR)&toc, &returned, TRUE)) { errorValue = GetLastError(); printf("Error %d sending READ_TOC pass through\n", errorValue); return errorValue; }
dbg(printf("READ_TOC pass through returned %d bytes\n", returned)); numTracks = toc.LastTrack - toc.FirstTrack + 1; dbg(printf("Getting %d tracks\n", numTracks));
returned = FIELD_OFFSET(CDROM_TOC, TrackData[0]) + (numTracks * sizeof(TRACK_DATA));
memset(&cdb, 0, sizeof(CDB)); cdb.READ_TOC.OperationCode = SCSIOP_READ_TOC; cdb.READ_TOC.Msf = 0; cdb.READ_TOC.StartingTrack = 1; cdb.READ_TOC.AllocationLength[0] = (UCHAR)(returned >> 8); cdb.READ_TOC.AllocationLength[1] = (UCHAR)(returned & 0xff);
if (!SptSendCdbToDevice(device, &cdb, 10, (PUCHAR)&toc, &returned, TRUE)) { errorValue = GetLastError(); printf("Error %d sending READ_TOC pass through\n", errorValue); return errorValue; }
dbg(printf("READ_TOC pass through returned %d bytes\n", returned));
printf("TOC Data Length: %d\n", (toc.Length[0] << 16) | (toc.Length[1]));
printf("First Track Number: %d\n", toc.FirstTrack); printf("Last Track Number: %d\n", toc.LastTrack);
track = &(toc.TrackData[0]);
printf("Number ADR Control Address (LBA)\n"); printf("------ --- ------- -------------\n");
for(i = 0; i < numTracks; i++) {
DWORD lba = (track->Address[0] << 24) | (track->Address[1] << 16) | (track->Address[2] << 8) | (track->Address[3] << 0); UCHAR m,s,f; LBA_TO_MSF(lba, m, s, f);
printf("%6d %3d %7d %3d:%02d:%02d\n", track->TrackNumber, track->Adr, track->Control, m,s,f);
track++; } return errorValue; }
DWORD PlayCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Plays an audio track
Arguments: device - a file handle to send the ioctl to
argc - the number of additional arguments.
argv[1] - the starting track. Starts at zero if this is not here argv[2] - the ending track. Let track if not specified
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv); UNREFERENCED_PARAMETER(device); printf("This command is not implemented\n"); return 1; }
DWORD PauseResumeCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
pauses or resumes audio playback
Arguments: device - a file handle to send the ioctl to
argc - the number of additional arguments.
argv[0] - "pause" or "resume"
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { DWORD errorValue = STATUS_SUCCESS; CDB cdb;
char resume;
UNREFERENCED_PARAMETER(argc);
if(strcmp("pause", argv[0]) == 0) { resume = 0; } else { resume = 1; }
printf("%s cdrom playback\n", (resume ? "Resuming" : "Pausing"));
//
// Unfortunately no one defined the PLAY_INDEX command for us so
// cheat and use MSF
//
memset(&cdb, 0, sizeof(CDB)); cdb.PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME; cdb.PAUSE_RESUME.Action = resume;
if (!SptSendCdbToDevice(device, &cdb, 10, NULL, 0, FALSE)) { errorValue = GetLastError(); printf("Error %d sending PAUSE_RESUME pass through\n", errorValue); return errorValue; }
// dbg(printf("PAUSE_RESUME pass through returned %d bytes\n", returned));
return errorValue; } DWORD ImageDiskCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
creates an image of the device by reading from sector 0 to N.
Arguments:
device - a file handle to send the ioctl to
argc - the number of additional arguments. should be 2.
argv[1] - the file to output to
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ {
HANDLE file; PUCHAR buffer; READ_CAPACITY_DATA capacityData; ULONG dataSize; CDB cdb;
ULONG sectorsPerMaxRead; ULONG currentSector;
if(argc < 2) { printf("not correct number of args\n"); return -1; }
printf("Opening file %s\n", argv[1]);
file = CreateFile(argv[1], GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (file == INVALID_HANDLE_VALUE) { printf("Error %d creating file %s\n", GetLastError(), argv[1]); return -2; }
// read the sector size from the device
RtlZeroMemory(&cdb, sizeof(CDB)); RtlZeroMemory(&capacityData, sizeof(READ_CAPACITY_DATA)); cdb.CDB10.OperationCode = SCSIOP_READ_CAPACITY; dataSize = sizeof(READ_CAPACITY_DATA);
if (!SptSendCdbToDevice(device, &cdb, 10, (PUCHAR)&capacityData, &dataSize, TRUE)) { printf("Error %d getting capacity info\n", GetLastError()); return -3; } // convert the numbers
PrintBuffer(&capacityData, sizeof(READ_CAPACITY_DATA)); REVERSE_LONG(&capacityData.BytesPerBlock); REVERSE_LONG(&capacityData.LogicalBlockAddress); if ( (MAX_READ_SIZE % capacityData.BytesPerBlock) != 0 ) { printf("Sector size of %x is not power of 2?!\n", capacityData.BytesPerBlock); // capacityData.BytesPerBlock = 512;
return -5; }
buffer = (PUCHAR)malloc(MAX_READ_SIZE); if (!buffer) { printf("Unable to alloc %x bytes\n", MAX_READ_SIZE); return -4; } sectorsPerMaxRead = MAX_READ_SIZE / capacityData.BytesPerBlock;
// read the data from disk and dump to file
for (currentSector = 0; currentSector <= capacityData.LogicalBlockAddress; currentSector += sectorsPerMaxRead) {
ULONG sectorsThisRead = sectorsPerMaxRead;
UpdatePercentageDisplay(currentSector, capacityData.LogicalBlockAddress); if (currentSector > capacityData.LogicalBlockAddress - sectorsPerMaxRead) { sectorsThisRead = capacityData.LogicalBlockAddress - currentSector; }
RtlZeroMemory(&cdb, sizeof(CDB)); RtlZeroMemory(buffer, MAX_READ_SIZE);
cdb.CDB10.OperationCode = SCSIOP_READ; cdb.CDB10.LogicalBlockByte0 = (UCHAR)(currentSector >> (8*3)); cdb.CDB10.LogicalBlockByte1 = (UCHAR)(currentSector >> (8*2)); cdb.CDB10.LogicalBlockByte2 = (UCHAR)(currentSector >> (8*1)); cdb.CDB10.LogicalBlockByte3 = (UCHAR)(currentSector >> (8*0)); cdb.CDB10.TransferBlocksMsb = (UCHAR)(sectorsThisRead >> (8*1)); cdb.CDB10.TransferBlocksLsb = (UCHAR)(sectorsThisRead >> (8*0));
dataSize = sectorsThisRead * capacityData.BytesPerBlock; if (!SptSendCdbToDevice(device, &cdb, 10, buffer, &dataSize, TRUE)) { printf("Error %d reading %x sectors starting at %x\n", GetLastError(), sectorsThisRead, currentSector); free(buffer); return -6; } if (dataSize != sectorsThisRead * capacityData.BytesPerBlock) { printf("Only got %x of %x bytes reading %x sectors starting at %x\n", dataSize, sectorsThisRead * capacityData.BytesPerBlock, sectorsThisRead, currentSector); free(buffer); return -7; }
dataSize = sectorsThisRead * capacityData.BytesPerBlock; if (!WriteFile(file, buffer, dataSize, &dataSize, NULL)) { printf("Error %d writing %x bytes starting at sector %x\n", GetLastError(), dataSize, currentSector); free(buffer); return -8; }
if (dataSize != sectorsThisRead * capacityData.BytesPerBlock) { printf("Only wrote %x of %x bytes writing %x sectors starting at %x\n", dataSize, sectorsThisRead * capacityData.BytesPerBlock, sectorsThisRead, currentSector); free(buffer); return -9; } } UpdatePercentageDisplay(capacityData.LogicalBlockAddress, capacityData.LogicalBlockAddress ); free(buffer); printf("\nSuccess!\n"); return 0; }
DWORD SendCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Parses a hex byte string and creates a cdb to send down.
Arguments: device - a file handle to send the ioctl to
argc - the number of additional arguments. should be 2 or 4
argv[1] - The CDB to send in a quoted hex byte string "47 00 00 00 01 00 00 ff 00 00"
argv[2] - "SET" or "GET"
argv[3] - for GET commands: the number of bytes (decimal) to expect from the target for SET commands: a quoted hex byte string to send to the target
NOTE: Due to the potentially damaging nature of making sending an arbitrary SCSI command to an arbitrary device, this command should not be documented outside of this source code.
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { DWORD errorValue = STATUS_SUCCESS;
UCHAR cdbSize; CDB cdb;
DWORD setData = FALSE; PUCHAR returnedData = NULL;
DWORD i;
DWORD dataSize = 0;
////////////////////////////////////////////////////////////////////////////////
// verify the arguments
////////////////////////////////////////////////////////////////////////////////
if ( argc == 4 ) { if (strcmp(argv[2], "get") != 0 && strcmp(argv[2], "set") != 0 && strcmp(argv[2], "GET") != 0 && strcmp(argv[2], "SET") != 0 ) { printf("argv2 == %s\n", argv[2]); argc = 0; // this will cause help to print
} if (strcmp(argv[2], "set") == 0 || strcmp(argv[2], "SET") == 0 ) { setData = TRUE; } }
if ( argc != 2 && argc != 4 ) { printf("requires one or three args:\n" "1)\tquoted hex string for cdb\n" "2)\t(optional) GET or SET\n" "3)\t(optional) GET: number of bytes to expect\n" "\t(optional) SET: quoted hex string for cdb\n");
printf("\n"); printf("Example commands:\n" "Send STOP_UNIT to eject drive q:\n" "\tcdp q: send \"1b 00 00 00 02 00\"\n" "Get CDVD_CAPABILITIES_PAGE from drive q:\n" "\tcdp q: send \"5a 40 2a 00 00 00 00 00 1a 00\" get 21\n" );
return 1; }
if (!SptUtilLockVolumeByHandle(device, TRUE)) { printf("Failed %x\n", GetLastError()); return 1; }
////////////////////////////////////////////////////////////////////////////////
// parse the arguments
////////////////////////////////////////////////////////////////////////////////
if (!CmdHelpValidateStringHex(argv[1])) { printf("Hex string must be two (0-9,a-f) then one space (repeated)\n"); return 1; }
//
// Determine the length of the CDB first
// sscanf returns the number of things read in (ie. cdb size)
//
cdbSize = (UCHAR)sscanf(argv[1], "%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x", cdb.AsByte + 0, cdb.AsByte + 1, cdb.AsByte + 2, cdb.AsByte + 3, cdb.AsByte + 4, cdb.AsByte + 5, cdb.AsByte + 6, cdb.AsByte + 7, cdb.AsByte + 8, cdb.AsByte + 9, cdb.AsByte + 10, cdb.AsByte + 11, cdb.AsByte + 12, cdb.AsByte + 13, cdb.AsByte + 14, cdb.AsByte + 15 );
//
// now figure out how much memory we need to allocate
//
if (argc == 4) {
if (setData) {
if (!CmdHelpValidateStringHexQuoted(argv[3])) { printf("Hex string must be two (0-9,a-f) then one space (repeated)\n"); return 1; }
dataSize = strlen(argv[3]);
if (dataSize % 3) { dataSize /= 3; dataSize ++; } else { dataSize /= 3; }
if (dataSize == 0) { printf("Cannot set zero bytes of data\n"); return 1; }
} else {
i = sscanf(argv[3], "%x", &dataSize);
if (i != 1) { printf("Error scanning second argument\n"); return 1; }
} }
//
// allocate the memory we may need
//
if (dataSize != 0) { returnedData = (PUCHAR)malloc(dataSize); if (returnedData == NULL) { printf("Unable to allocate %x bytes for data\n", dataSize); return 1; } memset(returnedData, 0, dataSize); }
//
// now scan in the data to set, if that's what the user wants.
// note that since it's already been validated, we can presume
// the format is (number)(number)(space) repeated
//
if (setData) { ULONG index; PCHAR location = argv[3];
for (index = 0; index < dataSize; index++) {
if (sscanf(location, "%x", returnedData + index) != 1) { printf("sscanf did not return 1 for index %i\n", index); return 1; }
if ((*location + 0 == '\0') || (*location + 1 == '\0')) { printf("string too short!\n"); return 1; } location += 3;
} }
#if DBG
////////////////////////////////////////////////////////////////////////////////
// provide some user feedback
////////////////////////////////////////////////////////////////////////////////
//
// it is the amount of data expected back from the command
//
printf("\nSending %x byte Command:\n", cdbSize); PrintBuffer(cdb.AsByte, cdbSize); if (setData) { printf("Setting Buffer:\n"); PrintBuffer(returnedData, dataSize); } else { printf("Expecting %#x bytes of data\n", dataSize); } #endif // DBG
////////////////////////////////////////////////////////////////////////////////
// send the command
////////////////////////////////////////////////////////////////////////////////
while (1) {
UCHAR senseSize = sizeof(SENSE_DATA); SENSE_DATA senseData; BOOLEAN retry = FALSE; DWORD retryDelay = 0;
if (!SptSendCdbToDeviceEx(device, &cdb, cdbSize, returnedData, &dataSize, &senseData, senseSize, (BOOLEAN)(setData ? FALSE : TRUE), 360) // six minutes
) {
errorValue = 0; if (senseSize == 0) { errorValue = GetLastError(); if (errorValue == ERROR_SUCCESS) { errorValue = ERROR_IO_DEVICE; } } else { printf("Sense Data: (%x bytes) Sense %x ASC %x ASCQ %x\n", senseSize, senseData.SenseKey & 0xf, senseData.AdditionalSenseCode, senseData.AdditionalSenseCodeQualifier); PrintBuffer(&senseData, senseSize); SptUtilInterpretSenseInfo(&senseData, senseSize, &errorValue, &retry, &retryDelay); }
if (retry) { printf("Command should be retried in %d.%d seconds\n", (retryDelay / 10), (retryDelay % 10)); Sleep(retryDelay*10); } else { printf("Error %d sending command via pass through\n", errorValue); break; }
}
if (!setData) { printf("(%x bytes returned)\n",dataSize); PrintBuffer(returnedData, dataSize); } else { printf("Successfully sent the command\n"); }
break; // out of for loop
}
return errorValue;
}
DWORD TestCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Tests the command "parsing"
Arguments: device - a file handle to send the ioctl to
argc - the number of additional arguments. should be zero
argv - the additional arguments
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/
{ int i;
UNREFERENCED_PARAMETER(device);
printf("Test - %d additional arguments\n", argc);
for(i = 0; i < argc; i++) { printf("arg %d: %s\n", i, argv[i]); }
return STATUS_SUCCESS; }
DWORD ListCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Prints out the command list
Arguments: device - unused
argc - unused
argv - unused
Return Value:
STATUS_SUCCESS
--*/
{ int i = 0;
UNREFERENCED_PARAMETER(device); UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv);
while(CommandArray[i].Name != NULL) {
if(CommandArray[i].Description != NULL) {
printf("\t%s - %s\n", CommandArray[i].Name, CommandArray[i].Description); }
i++; }
return STATUS_SUCCESS; }
DWORD DvdReadStructure(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Sends down a startstop command.
Arguments: device - a file handle to send the ioctl to
argc - the number of additional arguments. should be zero
argv[0] - "eject", "load", "start" or "stop"
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { DVD_READ_STRUCTURE readStructure; PUCHAR buffer; PDVD_DESCRIPTOR_HEADER header;
DWORD returned; DWORD errorValue = STATUS_SUCCESS;
printf("argument count is %d\n", argc);
if(argc <= 1) { printf("\tValid structure types are Physical, Copyright, DiskKey, " "BCA or Manufacturer\n"); return STATUS_SUCCESS; }
printf("Argv[1] = %s\n", argv[1]);
buffer = malloc(sizeof(DVD_DISK_KEY_DESCRIPTOR) + sizeof(DVD_DESCRIPTOR_HEADER));
if (buffer == NULL) { printf("Insufficient memory\n"); return STATUS_SUCCESS; }
header = (PDVD_DESCRIPTOR_HEADER) buffer;
if(_stricmp("physical", argv[1]) == 0) {
if(argc < 1) {
printf("reading physical descriptor requires layer number\n"); return STATUS_SUCCESS; }
readStructure.Format = DvdPhysicalDescriptor; readStructure.LayerNumber = (UCHAR)atoi(argv[1]);
} else if(_stricmp("copyright", argv[1]) == 0) { readStructure.Format = DvdCopyrightDescriptor;
} else if(_stricmp("diskkey", argv[1]) == 0) { if(argc < 1) {
printf("reading physical descriptor requires a session ID\n"); return STATUS_SUCCESS; }
readStructure.Format = DvdPhysicalDescriptor; readStructure.SessionId = atoi(argv[1]);
} else if(_stricmp("bca", argv[1]) == 0) { readStructure.Format = DvdBCADescriptor;
} else if(_stricmp("manufacturer", argv[1]) == 0) { readStructure.Format = DvdManufacturerDescriptor;
} else { printf("\tValid structure types are Physical, Copyright, DiskKey, " "BCA or Manufacturer\n"); return STATUS_SUCCESS; }
returned = 0;
if(!DeviceIoControl(device, IOCTL_DVD_READ_STRUCTURE, &readStructure, sizeof(DVD_READ_STRUCTURE), buffer, sizeof(DVD_DISK_KEY_DESCRIPTOR), &returned, FALSE)) {
errorValue = GetLastError(); printf("Eject - error sending IOCTL (%d)\n", errorValue); return errorValue; }
printf("DvdReadStructure returned %d bytes\n", returned);
printf("Header Length is %#08lx\n", header->Length);
printf("Header @ %p\n", header);
printf("Data @ %p\n", &(header->Data[0]));
if(_stricmp("physical", argv[1]) == 0) {
PDVD_LAYER_DESCRIPTOR layer = (PDVD_LAYER_DESCRIPTOR) ((PUCHAR) &(header->Data[0])); int i;
printf("\tBook Version: %d\n", layer->BookVersion); printf("\tBook Type: %d\n", layer->BookType); printf("\tMinimumRate: %d\n", layer->MinimumRate); printf("\tDiskSize: %d\n", layer->DiskSize); printf("\tLayerType: %d\n", layer->LayerType); printf("\tTrackPath: %d\n", layer->TrackPath); printf("\tNumberOfLayers: %d\n", layer->NumberOfLayers); printf("\tTrackDensity: %d\n", layer->TrackDensity); printf("\tLinearDensity: %d\n", layer->LinearDensity); printf("\tStartingDataSector: %#08lx\n", layer->StartingDataSector); printf("\tEndDataSector: %#08lx\n", layer->EndDataSector); printf("\tEndLayerZeroSector: %#08lx\n", layer->EndLayerZeroSector); printf("\tBCAFlag: %d\n", layer->BCAFlag);
printf("\n");
for(i = 0; i < sizeof(DVD_LAYER_DESCRIPTOR); i++) { printf("byte %d: %#x\n", i, header->Data[i]); }
} else if(_stricmp("copyright", argv[1]) == 0) {
} else if(_stricmp("diskkey", argv[1]) == 0) {
} else if(_stricmp("bca", argv[1]) == 0) {
} else if(_stricmp("manufacturer", argv[1]) == 0) {
}
printf("final status %d\n", errorValue);
return errorValue; }
DWORD DiskGetPartitionInfo(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Sends down a startstop command.
Arguments: device - a file handle to send the ioctl to
argc - the number of additional arguments. should be zero
argv[0] - "eject", "load", "start" or "stop"
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { PARTITION_INFORMATION partitionInformation;
DWORD returned; DWORD errorValue = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv);
returned = 0;
if(!DeviceIoControl(device, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0L, &partitionInformation, sizeof(PARTITION_INFORMATION), &returned, FALSE)) {
errorValue = GetLastError(); printf("Eject - error sending IOCTL (%d)\n", errorValue); return errorValue; }
printf("IOCTL_DISK_GET_PARTITION_INFO returned %d bytes\n", returned);
printf("Starting Offset = %#016I64x\n", partitionInformation.StartingOffset.QuadPart); printf("Partition Length = %#016I64x\n", partitionInformation.PartitionLength.QuadPart); printf("Hidden Sectors = %#08lx\n", partitionInformation.HiddenSectors); printf("PartitionNumber = %#08lx\n", partitionInformation.PartitionNumber); printf("PartitionType = %#08lx\n", partitionInformation.PartitionType); printf("BootIndicator = %s\n", partitionInformation.BootIndicator ? "TRUE" : "FALSE"); printf("RecognizedPartition = %s\n", partitionInformation.RecognizedPartition ? "TRUE" : "FALSE"); printf("RewritePartition = %s\n", partitionInformation.RewritePartition ? "TRUE" : "FALSE");
return errorValue; }
DWORD IoctlCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Sends down a specified ioctl.
Arguments: device - a file handle to send the ioctl to
argc - the number of additional arguments. should be two
argv[0] - ioctl code in hexadecimal argv[1] - quoted string, bytes to send, "" if none argv[2] - number of bytes to get back [optional]
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/
{
ULONG ctlCode = 0; ULONG returned; ULONG inputSize = 0; ULONG outputSize = 0; UCHAR buffer[MAX_IOCTL_BUFFER_SIZE]; BOOLEAN get;
if (argc < 3) { // n+1 -- require two args, accept three
ctlCode = 0;
} else if (!CmdHelpValidateStringHexQuoted(argv[2])) {
printf("input hex string invalid\n"); ctlCode = 0;
} else {
//
// retrieve the ioctl
//
(void)sscanf(argv[1], "%x", &ctlCode);
}
if (argc > 3) { // n+1 -- require three args.
(void)sscanf(argv[3], "%x", &outputSize); printf("output size: %x\n", outputSize); } else { outputSize = 0; }
if (outputSize > MAX_IOCTL_OUTPUT_SIZE) {
printf("output size too large\n"); ctlCode = 0;
}
if (ctlCode == 0) { printf("args:\n" "1)\tioctl in hex\n" "2)\tquoted string of bytes to send, \"\" if none\n" "3)\tnumber of bytes to expect\n"); return -1; }
//////////////////////////////////////////////////////////////////////////
// ioctl and args are valid.
//
RtlZeroMemory(buffer, sizeof(UCHAR)*MAX_IOCTL_BUFFER_SIZE);
if (strlen(argv[2])) { inputSize = MAX_IOCTL_INPUT_SIZE; if (!CmdHelpScanQuotedHexString(argv[2], buffer, &inputSize)) { printf("Error scanning hex string\n"); return -1; } } else { inputSize = 0; }
// inputSize of zero is valid as input
printf("Sending ioctl %x to device %p\n" "using input buffer %p of size %x\n" "and output buffer %p of size %x\n", ctlCode, device, ((inputSize == 0) ? NULL : buffer), inputSize, ((outputSize == 0) ? NULL : buffer), outputSize);
if (!DeviceIoControl(device, ctlCode, ((inputSize == 0) ? NULL : buffer), inputSize, ((outputSize == 0) ? NULL : buffer), outputSize, &returned, FALSE)) { printf("Failed with %d\n", GetLastError()); return GetLastError(); }
if (returned != 0) { printf("Returned data (%x of %x bytes):\n", returned, outputSize); PrintBuffer(buffer, returned); } else { printf("Command completed successfully\n"); }
return 0; }
DWORD FormatErrorCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Gets and displays the message string associated with an error code.
Arguments: device - not used, but required :P
argc - the number of additional arguments. should be one
argv[0] - error code in hexadecimal
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { LPVOID stringBuffer = NULL; DWORD errorCode = 0; DWORD numOfChars = 0; DWORD flags; ULONG i;
//
// verify number of args and give help.
//
if (argc != 2) { // n+1
printf("requires one argument: the error code in *hex*\n" "Example commands:\n" "\tcdp c: error 80030306\n" "\t(DVD Copy Protection Error)\n" ); return 1; } i = sscanf(argv[1], "%x", &errorCode); if (i < 1) { printf("Unable to find a valid hex number in '%s'.\n", argv[1]); return 1; } printf("Formatting error code for 0x%x (%d)\n", errorCode, errorCode);
flags = 0; flags |= FORMAT_MESSAGE_ALLOCATE_BUFFER; flags |= FORMAT_MESSAGE_FROM_SYSTEM; flags |= FORMAT_MESSAGE_IGNORE_INSERTS;
numOfChars = FormatMessageA(flags, NULL, errorCode, 0, // language indifferent
(LPSTR)&stringBuffer, // double pointer
0, NULL );
if (stringBuffer == NULL) { printf("No buffer returned?\n"); return -1; }
if (numOfChars == 0) { printf("Size zero buffer returned?\n"); return -1; }
printf("ERROR MESSAGE RETURNED:\n"); printf("%s\n", stringBuffer);
LocalFree(stringBuffer);
return 0; }
DWORD FormatMrwCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Formats an MRW-Compliant drive and shows percentage complete
Arguments:
device - drive to format media as MRW in
argc - the number of additional arguments. should be zero
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ {
#define MRW_FORMAT_BUFFER_SIZE 0xc
CDB cdb; ULONG size = MRW_FORMAT_BUFFER_SIZE; SENSE_DATA senseData; UCHAR formatBuffer[MRW_FORMAT_BUFFER_SIZE];
RtlZeroMemory(&cdb, sizeof(CDB)); RtlZeroMemory(formatBuffer, size); RtlZeroMemory(&senseData, sizeof(SENSE_DATA));
cdb.CDB6FORMAT.OperationCode = SCSIOP_FORMAT_UNIT; cdb.CDB6FORMAT.FormatControl = 0x11; // FmtData == 1, FormatCode = 1
//formatBuffer[0x0] = 0x00;
//formatBuffer[0x1] = 0x00; // (same as 0x82)
//formatBuffer[0x2] = 0x00;
formatBuffer[0x3] = 0x08; formatBuffer[0x4] = 0xff; //---vvv
formatBuffer[0x5] = 0xff; // NumberOfBlocks must be set to 0xffffffff
formatBuffer[0x6] = 0xff; //
formatBuffer[0x7] = 0xff; //--^^^^
formatBuffer[0x8] = 0x90; // format code == 0x24 ( << 2 == 0x90 )
//formatBuffer[0x9] = 0x00;
//formatBuffer[0xa] = 0x00;
//formatBuffer[0xb] = 0x00;
if (!SptSendCdbToDeviceEx(device, &cdb, 6, formatBuffer, &size, &senseData, sizeof(SENSE_DATA), FALSE, 60)) { printf("Unable to format, %x\n", GetLastError()); printf("Sense Buffer: %02x/%02x/%02x\n", senseData.SenseKey, senseData.AdditionalSenseCode, senseData.AdditionalSenseCodeQualifier); PrintBuffer((PUCHAR)&senseData, sizeof(SENSE_DATA)); return -1; } else if (senseData.SenseKey != SCSI_SENSE_NO_SENSE) { printf("Sense Buffer: %02x/%02x/%02x\n", senseData.SenseKey, senseData.AdditionalSenseCode, senseData.AdditionalSenseCodeQualifier); PrintBuffer((PUCHAR)&senseData, sizeof(SENSE_DATA)); return -1; }
printf("format command succeeded (%02x/%02x/%02x)\n", senseData.SenseKey, senseData.AdditionalSenseCode, senseData.AdditionalSenseCodeQualifier);
return ShowMrwProgressCommand(device, argc, argv); }
DWORD ShowMrwProgressCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Formats an MRW-Compliant drive and shows percentage complete
Arguments:
device - drive to format media as MRW in
argc - the number of additional arguments. should be zero
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { CDB cdb; SENSE_DATA sense; ULONG size; ULONG ignoredLoopCount; BOOLEAN succeededOnce; BOOLEAN senseHeaderPrinted;
//
// loop, displaying percentage done.
//
ignoredLoopCount = 0; succeededOnce = FALSE; senseHeaderPrinted = FALSE; while (1) {
#if 0
RtlZeroMemory(&cdb, sizeof(CDB));
size = 0; if (!SptSendCdbToDevice(device, &cdb, 6, NULL, &size, TRUE)) { printf("\nUnable to send TUR to get percentage done! %x\n", GetLastError()); return -1; } Sleep(500); #endif // 0
RtlZeroMemory(&cdb, sizeof(CDB)); RtlZeroMemory(&sense, sizeof(SENSE_DATA));
size = sizeof(SENSE_DATA);
cdb.AsByte[0] = SCSIOP_REQUEST_SENSE; cdb.AsByte[4] = (UCHAR)(sizeof(SENSE_DATA));
if (!SptSendCdbToDevice(device, &cdb, 6, (PUCHAR)&sense, &size, TRUE)) { printf("\nUnable to get percentage done! %x\n", GetLastError()); return -1; }
if (sense.AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY && sense.AdditionalSenseCodeQualifier == SCSI_SENSEQ_FORMAT_IN_PROGRESS && (sense.SenseKeySpecific[0] & 0x80) ) {
ULONG done; succeededOnce = TRUE;
if (senseHeaderPrinted) { printf("\n"); } senseHeaderPrinted = FALSE;
done = ((sense.SenseKeySpecific[0] & 0x7f) << (8*2)) | ((sense.SenseKeySpecific[1] & 0xff) << (8*1)) | ((sense.SenseKeySpecific[2] & 0xff) << (8*0)) ; UpdatePercentageDisplay(done, 0x10000); ignoredLoopCount = 0;
} else {
if (!succeededOnce) {
if (!senseHeaderPrinted) {
senseHeaderPrinted = TRUE; printf("\nSenseData not showing format progress yet: %x/%x/%x ", sense.SenseKey, sense.AdditionalSenseCode, sense.AdditionalSenseCodeQualifier ); } printf(".");
} else {
if (senseHeaderPrinted) { printf("\n"); } senseHeaderPrinted = FALSE;
ignoredLoopCount++; if (ignoredLoopCount > 50) { printf("\nSenseData: %x/%x/%x\n", sense.SenseKey, sense.AdditionalSenseCode, sense.AdditionalSenseCodeQualifier ); printf("\nSenseData not as expected. Format has " "probably completed.\n"); return -1; }
} // else let it go on
}
Sleep(1000);
} // while(1) loop
}
BOOLEAN ModeSelect( HANDLE Device, PVOID ModePage, ULONG ModePageSize ) { CDB cdb; ULONG tmp; ULONG size; PMODE_PARAMETER_HEADER10 header;
tmp = sizeof(MODE_PARAMETER_HEADER10) + ModePageSize;
header = malloc(tmp); if (header == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } RtlZeroMemory(header, tmp);
tmp -= 2; // sizeof through field
header->ModeDataLength[0] = (UCHAR)(tmp >> (8*1)); header->ModeDataLength[1] = (UCHAR)(tmp >> (8*0));
tmp += 2; // still used for size...
RtlCopyMemory(header+1, // pointer math
ModePage, ModePageSize);
RtlZeroMemory(&cdb, sizeof(CDB)); cdb.MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10; cdb.MODE_SELECT10.PFBit = 1; cdb.MODE_SELECT10.ParameterListLength[0] = (UCHAR)(tmp >> (8*1)); cdb.MODE_SELECT10.ParameterListLength[1] = (UCHAR)(tmp >> (8*0)); size = tmp;
if (!SptSendCdbToDevice(Device, &cdb, 10, (PUCHAR)header, &size, FALSE)) { printf("Unable to set mode page %x\n", GetLastError()); return FALSE; } return TRUE; }
BOOLEAN FillDisk( HANDLE Device, ULONG Signature ) { READ_CAPACITY_DATA capacity; ULONG currentLba; PULONGLONG data;
//
// do a READ_CAPACITY to find the drive's sector size
// and number of LBAs
//
{ CDB cdb; ULONG size; RtlZeroMemory(&cdb, sizeof(CDB)); RtlZeroMemory(&capacity, sizeof(READ_CAPACITY_DATA));
cdb.CDB10.OperationCode = SCSIOP_READ_CAPACITY; size = sizeof(READ_CAPACITY_DATA);
if (!SptSendCdbToDevice(Device, &cdb, 10, (PUCHAR)&capacity, &size, TRUE)) { printf("Unable to get capacity %x\n", GetLastError()); return FALSE; } //
// convert the numbers
//
REVERSE_LONG(&capacity.BytesPerBlock); REVERSE_LONG(&capacity.LogicalBlockAddress);
if ( (capacity.BytesPerBlock % 512) != 0 ) { printf("Sector size of %x is not a multiple of 512?!\n", capacity.BytesPerBlock); // capacity.BytesPerBlock = 512;
return FALSE; } }
//
// print for kicks...
//
printf(" Bytes Per Block %10d (%8x)\n" "Number Of Sectors %10d (%8x)\n", capacity.BytesPerBlock, capacity.BytesPerBlock, capacity.LogicalBlockAddress, capacity.LogicalBlockAddress );
//
// allocate a sector's worth of data
//
data = (PLONGLONG)malloc( capacity.BytesPerBlock ); if (data == NULL) { printf("Not enough memory to allocate data\n"); return FALSE; }
for (currentLba = 0x0; currentLba <= capacity.LogicalBlockAddress; currentLba++) {
CDB cdb; PULONGLONG t = data; ULONG size; ULONG iterate = capacity.BytesPerBlock / sizeof(ULONGLONG); ULONG j;
if ((currentLba % 100) == 0) { UpdatePercentageDisplay(currentLba, capacity.LogicalBlockAddress); }
// for the first ULONGLONG of each sector, put "Sector %08x"
// in plain text.
sprintf((PCHAR)t, "Sector %08x", currentLba );
// RtlZeroMemory(data, capacity.BytesPerBlock);
for (j=1; j < iterate ; j++, t++) { *t = ((ULONGLONG)Signature) << 32; // signature
*t += currentLba; // etc.
}
//
// prepare the "write" operation for this sector
//
RtlZeroMemory(&cdb, sizeof(CDB)); cdb.CDB10.OperationCode = SCSIOP_WRITE; cdb.CDB10.LogicalBlockByte0 = (UCHAR)(currentLba >> (8*3)); cdb.CDB10.LogicalBlockByte1 = (UCHAR)(currentLba >> (8*2)); cdb.CDB10.LogicalBlockByte2 = (UCHAR)(currentLba >> (8*1)); cdb.CDB10.LogicalBlockByte3 = (UCHAR)(currentLba >> (8*0)); cdb.CDB10.TransferBlocksMsb = 0; cdb.CDB10.TransferBlocksLsb = 1;
size = capacity.BytesPerBlock;
if (!SptSendCdbToDevice(Device, &cdb, 10, (PUCHAR)data, &size, FALSE)) { printf("Error %d writing sectors at %x\n", GetLastError(), currentLba); free(data); return FALSE; } } UpdatePercentageDisplay(capacity.LogicalBlockAddress, capacity.LogicalBlockAddress); free(data); data = NULL; return TRUE; }
BOOLEAN WriteImageSpt(HANDLE Device, HANDLE FsHandle, ULONG SectorsToWrite) { ULONG currentLba; PULONGLONG data;
//
// allocate a packet's worth of data
//
data = (PULONGLONG)malloc( CDRW_WRITE_BYTES ); if (data == NULL) { printf("Not enough memory to allocate data\n"); return FALSE; }
for (currentLba = 0x0; currentLba <= SectorsToWrite; currentLba += CDRW_WRITE_SECTORS) { CDB cdb; ULONG size; ULONG sectorsThisRead;
sectorsThisRead = SectorsToWrite - currentLba; if ( sectorsThisRead > CDRW_WRITE_SECTORS ) { sectorsThisRead = CDRW_WRITE_SECTORS; }
if ((currentLba % 100) == 0) { UpdatePercentageDisplay(currentLba, SectorsToWrite); }
// read in the next bits of data
{ ULONG bytesToRead; ULONG bytesActuallyRead;
RtlZeroMemory(data, CDRW_WRITE_BYTES); bytesToRead = sectorsThisRead * 2048; bytesActuallyRead = 0;
if ( !ReadFile(FsHandle, data, bytesToRead, &bytesActuallyRead, NULL) ) { printf("Failed to read from sector %x of %x\n", currentLba, SectorsToWrite); LocalFree(data); return FALSE; } if ( bytesActuallyRead != bytesToRead ) { printf("Only read %x of %x bytes reading from sector %x of %x\n", bytesActuallyRead, bytesToRead, currentLba, SectorsToWrite); LocalFree(data); return FALSE; } }
//
// prepare the "write" operation for this sector
//
RtlZeroMemory(&cdb, sizeof(CDB)); cdb.CDB10.OperationCode = SCSIOP_WRITE; cdb.CDB10.LogicalBlockByte0 = (UCHAR)(currentLba >> (8*3)); cdb.CDB10.LogicalBlockByte1 = (UCHAR)(currentLba >> (8*2)); cdb.CDB10.LogicalBlockByte2 = (UCHAR)(currentLba >> (8*1)); cdb.CDB10.LogicalBlockByte3 = (UCHAR)(currentLba >> (8*0)); cdb.CDB10.TransferBlocksMsb = (UCHAR)(sectorsThisRead >> (8*1)); cdb.CDB10.TransferBlocksLsb = (UCHAR)(sectorsThisRead >> (8*0)); size = sectorsThisRead * 2048;
if (!SptSendCdbToDevice(Device, &cdb, 10, (PUCHAR)data, &size, FALSE)) { printf("Error %d writing sectors at %x\n", GetLastError(), currentLba); free(data); return FALSE; } } UpdatePercentageDisplay(SectorsToWrite, SectorsToWrite);
free(data); data = NULL; return TRUE; }
BOOLEAN WriteImage(HANDLE device, HANDLE fsHandle, ULONG sectorsToWrite) { PUCHAR buffer = NULL; ULONG currentSector = 0;
buffer = LocalAlloc(LPTR, CDRW_WRITE_BYTES); if (buffer == NULL) { return FALSE; }
for (currentSector = 0; currentSector < sectorsToWrite; currentSector+=CDRW_WRITE_SECTORS) { ULONG bytesToRead = 0; ULONG bytesActuallyRead = 0;
if (sectorsToWrite - currentSector >= CDRW_WRITE_SECTORS) { bytesToRead = CDRW_WRITE_BYTES; } else { bytesToRead = (sectorsToWrite - currentSector) * 2048; RtlZeroMemory(buffer, CDRW_WRITE_BYTES); }
if (!ReadFile(fsHandle, buffer, bytesToRead, &bytesActuallyRead, NULL) ) { printf("Failed to read from sector %x of %x\n", currentSector, sectorsToWrite); LocalFree(buffer); return FALSE; } if (bytesActuallyRead != bytesToRead) { printf("Only read %x of %x bytes readinf from sector %x of %x\n", bytesActuallyRead, bytesToRead, currentSector, sectorsToWrite); LocalFree(buffer); return FALSE; }
// always write 64k at a time
bytesToRead = CDRW_WRITE_BYTES; if (!WriteFile(device, buffer, bytesToRead, &bytesActuallyRead, NULL)) { printf("Failed to write from sector %x of %x\n", currentSector, sectorsToWrite); LocalFree(buffer); return FALSE; } if (bytesActuallyRead != bytesToRead) { printf("Only wrote %x of %x bytes readinf from sector %x of %x\n", bytesActuallyRead, bytesToRead, currentSector, sectorsToWrite); LocalFree(buffer); return FALSE; } printf("."); } printf("Success!\n"); LocalFree(buffer); return TRUE; }
DWORD MrwInitGaaFileSystem(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Writes the GAA with the given FS image
Arguments:
device - drive to write to...
argc - the number of additional arguments. should be one
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { MODE_MRW_PAGE savedModePage; HANDLE fsHandle = INVALID_HANDLE_VALUE; ULONG sectorsToWrite = 0;
// open the fs to write
{ BY_HANDLE_FILE_INFORMATION fileInfo = {0};
if (argc != 2) { printf("Requires an image to write\n"); return -1; } printf("Opening %s for FS handle\n", argv[1]); fsHandle = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fsHandle == INVALID_HANDLE_VALUE) { printf("Unable to open FS file (%x)\n", GetLastError()); return -1; }
if (!GetFileInformationByHandle(fsHandle, &fileInfo)) { printf("Unable to query file info (%x)\n", GetLastError()); return -1; }
if (fileInfo.nFileSizeHigh != 0) { printf("File too large (high size not zero)\n"); return -1; } if (fileInfo.nFileSizeLow % 2048) { printf("File not multiple of 2048 bytes in size\n"); return -1; } if (fileInfo.nFileSizeLow > (2048 * 32 * 32)) { printf("File size of %x won't fit in GAA (%x)\n", fileInfo.nFileSizeLow, 2048*32*32); return -1; } printf("File passed verification\n"); sectorsToWrite = fileInfo.nFileSizeLow / 2048; }
RtlZeroMemory(&savedModePage, sizeof(MODE_MRW_PAGE)); savedModePage.PageCode = 0x3f; // illegal value for MODE_SELECT10
//
// first use GET_CONFIGURATION to verify that we're
// actually on an MRW capable device
//
{ #define MRW_FEATURE_DATA_SIZE (sizeof(GET_CONFIGURATION_HEADER)+sizeof(FEATURE_DATA_MRW))
GET_CONFIGURATION_IOCTL_INPUT input; PGET_CONFIGURATION_HEADER header; PFEATURE_DATA_MRW mrwFeature; UCHAR data[ MRW_FEATURE_DATA_SIZE ]; DWORD dataSize; DWORD expectedSize; DWORD feature; ULONG size;
RtlZeroMemory(&input, sizeof(GET_CONFIGURATION_IOCTL_INPUT)); RtlZeroMemory(&data, MRW_FEATURE_DATA_SIZE);
input.Feature = FeatureMrw; input.RequestType = SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE; size = 0;
if (!DeviceIoControl(device, IOCTL_CDROM_GET_CONFIGURATION, &input, sizeof(GET_CONFIGURATION_IOCTL_INPUT), data, MRW_FEATURE_DATA_SIZE, &size, FALSE)) { DWORD errorValue = GetLastError(); printf("error requesting GET_CONFIG data for MRW feature (%d)\n", errorValue); return errorValue; }
header = (PGET_CONFIGURATION_HEADER)data; mrwFeature = (PFEATURE_DATA_MRW)header->Data;
expectedSize = MRW_FEATURE_DATA_SIZE - RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength); dataSize = (header->DataLength[0] << (8 * 3)) | (header->DataLength[1] << (8 * 2)) | (header->DataLength[2] << (8 * 1)) | (header->DataLength[3] << (8 * 0));
if ( dataSize < expectedSize ) { printf("data size too small -- drive may not support MRW? (%x)\n", expectedSize); return -1; }
feature = (mrwFeature->Header.FeatureCode[0] << (8 * 1)) | (mrwFeature->Header.FeatureCode[1] << (8 * 0));
if (feature != FeatureMrw) { printf("data size too small -- drive may not support MRW? (%x)\n", feature); return -1; }
if (!mrwFeature->Write) { printf("Drive supports MRW, but as Read-Only\n"); return -1; } if (!mrwFeature->Header.Current) { printf("Drive supports MRW, but not with the current medium (may need to be formatted MRW first\n"); return -1; }
} // end verification
//
// ensure we're in the correct mode (data area vs. GAA)
//
{ #define MODE_MRW_PAGE_DATA_SIZE (sizeof(MODE_PARAMETER_HEADER10) + sizeof(MODE_MRW_PAGE))
PMODE_PARAMETER_HEADER10 header; PMODE_MRW_PAGE page; PUCHAR data [ MODE_MRW_PAGE_DATA_SIZE ]; CDB cdb; ULONG size; ULONG t1, t2;
RtlZeroMemory(&cdb, sizeof(CDB)); RtlZeroMemory(data, MODE_MRW_PAGE_DATA_SIZE);
size = MODE_MRW_PAGE_DATA_SIZE; cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; cdb.MODE_SENSE10.Dbd = 1; cdb.MODE_SENSE10.PageCode = MODE_PAGE_MRW; cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_MRW_PAGE_DATA_SIZE >> 8); cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_MRW_PAGE_DATA_SIZE & 0xff);
PrintBuffer(&cdb, 10);
if (!SptSendCdbToDevice(device, &cdb, 10, (PUCHAR)&data, &size, TRUE)) { printf("Unable to get MRW mode page %x\n", GetLastError());
// FAKE IT FOR NOW... BUGBUG
header = (PMODE_PARAMETER_HEADER10)data; RtlZeroMemory(data, MODE_MRW_PAGE_DATA_SIZE); header->ModeDataLength[0] = 0; header->ModeDataLength[1] = 0xE; page = (PMODE_MRW_PAGE)(header+1); page->PageCode = MODE_PAGE_MRW; page->PageLength = 0x6; page->LbaSpace = 0; }
header = (PMODE_PARAMETER_HEADER10)data; t1 = (header->ModeDataLength[0] << (8*1)) | (header->ModeDataLength[1] << (8*0)) ; t2 = MODE_MRW_PAGE_DATA_SIZE - RTL_SIZEOF_THROUGH_FIELD(MODE_PARAMETER_HEADER10, ModeDataLength);
if (t1 != t2) { // size is wrong
printf("MRW mode page wrong size, %x != %x\n", t1, t2); return -1; } if ((header->BlockDescriptorLength[0] != 0) || (header->BlockDescriptorLength[1] != 0) ) { printf("MRW drive force a block descriptor %x %x\n", header->BlockDescriptorLength[0], header->BlockDescriptorLength[1]); return -1; }
page = (PMODE_MRW_PAGE)(header+1); // pointer arithmetic
if (page->PageCode != MODE_PAGE_MRW) { printf("MRW mode page has wrong page code, %x != %x\n", page->PageCode, MODE_PAGE_MRW); return -1; } if (page->LbaSpace) { printf("MRW mode page is set to GAA\n", page->PageCode, MODE_PAGE_MRW); // ModeSelect()...
//return -1;
}
RtlCopyMemory(&savedModePage, page, sizeof(MODE_MRW_PAGE)); }
savedModePage.LbaSpace = 1; if (!ModeSelect(device, &savedModePage, sizeof(MODE_MRW_PAGE))) { printf("Unable to set MRW mode page to use GAA (%x)\n", GetLastError()); return -1; }
if (!WriteImageSpt(device, fsHandle, sectorsToWrite)) { printf("Unable to fill the GAA with the FS (%x)\n", GetLastError()); } printf("\nFinished Writing General Application Area!\n");
savedModePage.LbaSpace = 0; if (!ModeSelect(device, &savedModePage, sizeof(MODE_MRW_PAGE))) { printf("Unable to revert from GAA space -- disc may be unusable! (%x)\n", GetLastError()); return -1; } return 0; }
DWORD MrwInitTestPatternCommand(HANDLE device, int argc, char *argv[]) /*++
Routine Description:
Initializes a disk to contain 64-bit numbers that equate to the sector's LBA.
Arguments:
device - drive to write to...
argc - the number of additional arguments. should be zero
Return Value:
STATUS_SUCCESS if successful The value of GetLastError() from the point of failure
--*/ { MODE_MRW_PAGE savedModePage;
RtlZeroMemory(&savedModePage, sizeof(MODE_MRW_PAGE)); savedModePage.PageCode = 0x3f; // illegal value for MODE_SELECT10
//
// first use GET_CONFIGURATION to verify that we're
// actually on an MRW capable device
//
{ #define MRW_FEATURE_DATA_SIZE (sizeof(GET_CONFIGURATION_HEADER)+sizeof(FEATURE_DATA_MRW))
GET_CONFIGURATION_IOCTL_INPUT input; PGET_CONFIGURATION_HEADER header; PFEATURE_DATA_MRW mrwFeature; UCHAR data[ MRW_FEATURE_DATA_SIZE ]; DWORD dataSize; DWORD expectedSize; DWORD feature; ULONG size;
RtlZeroMemory(&input, sizeof(GET_CONFIGURATION_IOCTL_INPUT)); RtlZeroMemory(&data, MRW_FEATURE_DATA_SIZE);
input.Feature = FeatureMrw; input.RequestType = SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE; size = 0;
if (!DeviceIoControl(device, IOCTL_CDROM_GET_CONFIGURATION, &input, sizeof(GET_CONFIGURATION_IOCTL_INPUT), data, MRW_FEATURE_DATA_SIZE, &size, FALSE)) { DWORD errorValue = GetLastError(); printf("error requesting GET_CONFIG data for MRW feature (%d)\n", errorValue); return errorValue; }
header = (PGET_CONFIGURATION_HEADER)data; mrwFeature = (PFEATURE_DATA_MRW)header->Data;
expectedSize = MRW_FEATURE_DATA_SIZE - RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength); dataSize = (header->DataLength[0] << (8 * 3)) | (header->DataLength[1] << (8 * 2)) | (header->DataLength[2] << (8 * 1)) | (header->DataLength[3] << (8 * 0));
if ( dataSize < expectedSize ) { printf("data size too small -- drive may not support MRW? (%x)\n", expectedSize); return -1; }
feature = (mrwFeature->Header.FeatureCode[0] << (8 * 1)) | (mrwFeature->Header.FeatureCode[1] << (8 * 0));
if (feature != FeatureMrw) { printf("data size too small -- drive may not support MRW? (%x)\n", feature); return -1; }
if (!mrwFeature->Write) { printf("Drive supports MRW, but as Read-Only\n"); return -1; } if (!mrwFeature->Header.Current) { printf("Drive supports MRW, but not with the current medium (may need to be formatted MRW first\n"); return -1; }
} // end verification
//
// ensure we're in the correct mode (data area vs. GAA)
//
#if 0
{ #define MODE_MRW_PAGE_DATA_SIZE (sizeof(MODE_PARAMETER_HEADER10) + sizeof(MODE_MRW_PAGE))
PMODE_PARAMETER_HEADER10 header; PMODE_MRW_PAGE page; PUCHAR data [ MODE_MRW_PAGE_DATA_SIZE ]; CDB cdb; ULONG size; ULONG t1, t2;
RtlZeroMemory(&cdb, sizeof(CDB)); RtlZeroMemory(data, MODE_MRW_PAGE_DATA_SIZE);
size = MODE_MRW_PAGE_DATA_SIZE; cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; cdb.MODE_SENSE10.Dbd = 1; cdb.MODE_SENSE10.PageCode = MODE_PAGE_MRW; cdb.MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_MRW_PAGE_DATA_SIZE >> 8); cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_MRW_PAGE_DATA_SIZE & 0xff);
PrintBuffer(&cdb, 10);
if (!SptSendCdbToDevice(device, &cdb, 10, (PUCHAR)&data, &size, TRUE)) { printf("Unable to get MRW mode page %x\n", GetLastError());
// FAKE IT FOR NOW... BUGBUG
header = (PMODE_PARAMETER_HEADER10)data; RtlZeroMemory(data, MODE_MRW_PAGE_DATA_SIZE); header->ModeDataLength[0] = 0; header->ModeDataLength[1] = 0xE; page = (PMODE_MRW_PAGE)(header+1); page->PageCode = MODE_PAGE_MRW; page->PageLength = 0x6; page->LbaSpace = 0; }
header = (PMODE_PARAMETER_HEADER10)data; t1 = (header->ModeDataLength[0] << (8*1)) | (header->ModeDataLength[1] << (8*0)) ; t2 = MODE_MRW_PAGE_DATA_SIZE - RTL_SIZEOF_THROUGH_FIELD(MODE_PARAMETER_HEADER10, ModeDataLength);
if (t1 != t2) { // size is wrong
printf("MRW mode page wrong size, %x != %x\n", t1, t2); return -1; } if ((header->BlockDescriptorLength[0] != 0) || (header->BlockDescriptorLength[1] != 0) ) { printf("MRW drive force a block descriptor %x %x\n", header->BlockDescriptorLength[0], header->BlockDescriptorLength[1]); return -1; }
page = (PMODE_MRW_PAGE)(header+1); // pointer arithmetic
if (page->PageCode != MODE_PAGE_MRW) { printf("MRW mode page has wrong page code, %x != %x\n", page->PageCode, MODE_PAGE_MRW); return -1; } if (page->LbaSpace) { printf("MRW mode page is set to GAA\n", page->PageCode, MODE_PAGE_MRW); // ModeSelect()...
return -1; }
RtlCopyMemory(&savedModePage, page, sizeof(MODE_MRW_PAGE)); }
savedModePage.LbaSpace = 1; if (!ModeSelect(device, &savedModePage, sizeof(MODE_MRW_PAGE))) { printf("Unable to set MRW mode page to use GAA (%x)\n", GetLastError()); return -1; }
if (!FillDisk(device, '\0wrm')) { printf("Unable to fill the GAA (%x)\n", GetLastError()); } printf("\nFinished Writing General Application Area!\n");
savedModePage.LbaSpace = 0; if (!ModeSelect(device, &savedModePage, sizeof(MODE_MRW_PAGE))) { printf("Unable to revert from GAA space -- disc may be unusable! (%x)\n", GetLastError()); return -1; } #endif // 0
if (!FillDisk(device, '\0WRM')) { printf("Unable to fill the disc (%x)\n", GetLastError()); return -1; } printf("\nFinished Writing Defect-managed Area!\n");
return 0; }
DWORD WaitForReadDiscInfoCommand( HANDLE device, int argc, char *argv[] ) { CDB cdb; DWORD size; DISK_INFORMATION diskInfo; DWORD i;
//
// loop using SCSIOP_READ_DISK_INFORMATION (0x51) since
// that seems to fail for *ALL* drives until the drive is ready
//
printf("Waiting for ReadDiscInfo"); for (i=0; ; i++) {
size = sizeof(DISK_INFORMATION); RtlZeroMemory(&diskInfo, sizeof(DISK_INFORMATION)); RtlZeroMemory(&cdb, sizeof(CDB));
cdb.READ_DISK_INFORMATION.OperationCode = SCSIOP_READ_DISK_INFORMATION; cdb.READ_DISK_INFORMATION.AllocationLength[0] = (UCHAR)(size >> 8); cdb.READ_DISK_INFORMATION.AllocationLength[1] = (UCHAR)(size & 0xff);
if (SptSendCdbToDeviceEx(device, &cdb, 10, (PUCHAR)&diskInfo, &size, NULL, 0, TRUE, 10)) { printf("Succeeded! (%d seconds)\n", i); return 0; } // should verify the errors are valid errors (AllowedReadDiscInfo[])?
// need to sleep here so we don't overload the unit!
Sleep(1000); // one second
if (i%10 == 0) { printf("."); } } return -1; }
|