/*++ 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 #include #include #include #include #include #include #include #include #include #include #include #define _NTSRB_ // to keep srb.h from being included #include #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", " 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: ±..................... 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 [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 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 - 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; }