windows-nt/Source/XPSP1/NT/sdktools/spt/cdp/cdp.c
2020-09-26 16:20:57 +08:00

2406 lines
67 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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)
#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;
////////////////////////////////////////////////////////////////////////////////
#pragma pack(push, 1)
#define MODE_PAGE_MRW 0x2C // cdrom
typedef struct _MRW_MODE_PAGE {
UCHAR PageCode : 6; // 0x2C
UCHAR Reserved : 1;
UCHAR PSBit : 1; // offset 0
UCHAR PageLength; // 0x06 // offset 1
UCHAR Reserved1; // offset 2
UCHAR UseGAA : 1;
UCHAR Reserved2 : 7; // offset 3
UCHAR Reserved3[4]; // offset 4-7
} MRW_MODE_PAGE, *PMRW_MODE_PAGE;
C_ASSERT(sizeof(MRW_MODE_PAGE) == 8);
#pragma pack(pop)
////////////////////////////////////////////////////////////////////////////////
#define LBA_TO_MSF(Lba,Minutes,Seconds,Frames) \
{ \
(Minutes) = (UCHAR)(Lba / (60 * 75)); \
(Seconds) = (UCHAR)((Lba % (60 * 75)) / 75); \
(Frames) = (UCHAR)((Lba % (60 * 75)) % 75); \
}
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[]);
//
// 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},
{"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},
// {"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<80;i++) {
putchar('\b');
}
printf("Complete: ");
// each block is 2%
// ----=----1----=----2----=----3----=----4----=----5----=----6----=----7----=----8
// Complete: <20>.....................
for (i=1; i<100; i+=2) {
if (i < percent) {
putchar(178);
} else if (i == percent) {
putchar(177);
} else {
putchar(176);
}
}
printf(" %d%% (%x/%x)", percent, Numerator, Denominator);
}
int __cdecl main(int argc, char *argv[])
{
int i = 0;
HANDLE h;
char buffer[32];
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(&params, 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,
&params,
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(&params, 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,
&params,
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 - sectorsPerMaxRead;
}
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(currentSector, currentSector);
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;
}
////////////////////////////////////////////////////////////////////////////////
// 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),
SPT_DEFAULT_TIMEOUT)
) {
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 = 0x80030306;
DWORD numOfChars = 0;
DWORD flags;
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;
UCHAR formatBuffer[MRW_FORMAT_BUFFER_SIZE];
RtlZeroMemory(&cdb, sizeof(CDB));
RtlZeroMemory(formatBuffer, size);
cdb.CDB6FORMAT.OperationCode = SCSIOP_FORMAT_UNIT;
cdb.CDB6FORMAT.FormatControl = 0x11;
//formatBuffer[0x0] = 0x00;
formatBuffer[0x1] = 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;
//formatBuffer[0x9] = 0x00;
//formatBuffer[0xa] = 0x00;
//formatBuffer[0xb] = 0x00;
if (!SptSendCdbToDevice(device,
&cdb,
6,
formatBuffer,
&size,
FALSE)) {
printf("Unable to format, %x\n", GetLastError());
return -1;
}
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 = 0;
//
// loop, displaying percentage done.
//
ignoredLoopCount = 0;
while (1) {
Sleep(100);
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;
done =
((sense.SenseKeySpecific[0] & 0x7f) << (8*2)) |
((sense.SenseKeySpecific[1] & 0xff) << (8*1)) |
((sense.SenseKeySpecific[2] & 0xff) << (8*0)) ;
UpdatePercentageDisplay(done, 0x10000);
} else {
ignoredLoopCount++;
if (ignoredLoopCount > 12) {
printf("\nSenseData not as expected. Format may have completed?\n");
return -1;
}
// else let it go on
}
}
}
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;
}
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 = 0; 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);
}
// RtlZeroMemory(data, capacity.BytesPerBlock);
for (j=0; 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;
}
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
--*/
{
MRW_MODE_PAGE savedModePage;
RtlZeroMemory(&savedModePage, sizeof(MRW_MODE_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 MRW_MODE_PAGE_DATA_SIZE (sizeof(MODE_PARAMETER_HEADER10) + sizeof(MRW_MODE_PAGE))
PMODE_PARAMETER_HEADER10 header;
PMRW_MODE_PAGE page;
PUCHAR data [ MRW_MODE_PAGE_DATA_SIZE ];
CDB cdb;
ULONG size;
ULONG t1, t2;
RtlZeroMemory(&cdb, sizeof(CDB));
RtlZeroMemory(data, MRW_MODE_PAGE_DATA_SIZE);
size = MRW_MODE_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)(MRW_MODE_PAGE_DATA_SIZE >> 8);
cdb.MODE_SENSE10.AllocationLength[1] = (UCHAR)(MRW_MODE_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, MRW_MODE_PAGE_DATA_SIZE);
header->ModeDataLength[0] = 0;
header->ModeDataLength[1] = 0xE;
page = (PMRW_MODE_PAGE)(header+1);
page->PageCode = MODE_PAGE_MRW;
page->PageLength = 0x6;
page->UseGAA = 0;
}
HELP_ME();
header = (PMODE_PARAMETER_HEADER10)data;
t1 = (header->ModeDataLength[0] << (8*1)) |
(header->ModeDataLength[1] << (8*0)) ;
t2 = MRW_MODE_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 = (PMRW_MODE_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->UseGAA) {
printf("MRW mode page is set to GAA\n",
page->PageCode, MODE_PAGE_MRW);
// ModeSelect()...
return -1;
}
RtlCopyMemory(&savedModePage, page, sizeof(MRW_MODE_PAGE));
}
savedModePage.UseGAA = 1;
if (!ModeSelect(device, &savedModePage, sizeof(MRW_MODE_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.UseGAA = 0;
if (!ModeSelect(device, &savedModePage, sizeof(MRW_MODE_PAGE))) {
printf("Unable to revert from GAA space -- disc may be unusable! (%x)\n",
GetLastError());
return -1;
}
if (!FillDisk(device, '\0WRM')) {
printf("Unable to fill the disc (%x)\n", GetLastError());
return -1;
}
printf("\nFinished Writing Defect-managed Area!\n");
return 0;
}