#include #include #include #include #include #include #include #include "modesel.h" /* * This program was written initially by M. Calligaro for entirely * different purposes. Mistreated by A. Forin and M. Lucovsky. * None of the above thinks this code should look like this. */ VOID main( int argc, char *argv[] ) { SCSI_PASS_THROUGH_WITH_BUFFERS sptwb; DWORD accessMode, shareMode; HANDLE fileHandle; UCHAR string[25]; UCHAR szDriveNum[4]; ULONG errorCode; BOOLEAN changeSet = FALSE; ULONG i; ULONG driveNumber, sensePage, senseValues, saveAction; UCHAR changeBits[0x24]; ULONG pageLength; if (argc > 4 || (argc > 1 && argv[1][0] == '?')) { printf("Usage: %s [ModePage [Action [WhichValues]]]\n", argv[0] ); return; } shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; // default accessMode = GENERIC_WRITE | GENERIC_READ; // default printf("\nWindows NT SCSI Mode Sense and Select Utility\n"); selectDisk: printf("\n"); for (i=0;;i++) { sprintf(string, "\\\\.\\PhysicalDrive%d", i); fileHandle = CreateFile(string, accessMode, shareMode, NULL, OPEN_EXISTING, 0, NULL); if (fileHandle == INVALID_HANDLE_VALUE) { if (i == 0) { printf("Error opening PhysicalDrive0. Error: %d\n", errorCode = GetLastError()); PrintError(errorCode); return; } else { break; } } else { printf("Disk %d is a ", i); DoInquiry(&sptwb, fileHandle); printf("at SCSI ID %d\n", sptwb.spt.TargetId); CloseHandle(fileHandle); } } printf("Enter '99' to exit program\n\n"); while (TRUE) { printf("Which disk(0-%d)? ", i-1); scanf("%d", &driveNumber); if ((driveNumber >= 0) && (driveNumber < i)) { break; } else if (driveNumber == 99) { exit(0); } else { printf("There is no disk %d\n",driveNumber); } } newPage: printf("\n"); printf(" 0:Unit Attention Page\n"); printf(" 1:Error Recovery Page\n"); printf(" 2:Disconnect Page\n"); printf(" 3:Format Device Page\n"); printf(" 4:Rigid Geometry Page\n"); printf(" 5:Flexible Page\n"); printf(" 7:Verify Error Page\n"); printf(" 8:Caching Page\n"); printf(" 9:Peripheral Page\n"); printf("10:Control Page\n"); printf("11:Medium Types Page\n"); printf("12:Notch Partition Page\n"); printf("15:Data Compression Page\n"); printf("16:Device Configuration Page\n"); printf("17:Medium Partition Page\n"); printf("99:Select disk\n"); printf("\nWhich page? "); scanf("%d", &sensePage); if (sensePage > 17) { goto selectDisk; } printf("\n0:Get current values\n"); printf("1:Get default values\n"); printf("2:Get saved values\n"); printf("3:Get changable values\n"); if (sensePage == 8) { printf("4:Enable write cache\n"); printf("5:Disable write cache\n"); } printf("99:New page\n"); printf("\nWhat action? "); scanf("%d", &senseValues); if (senseValues > 5 || (sensePage != 8 && senseValues >3)) { goto newPage; } strcpy(string,"\\\\.\\PhysicalDrive"); sprintf(szDriveNum, "%d",driveNumber); strcat(string,szDriveNum); fileHandle = CreateFile(string, accessMode, shareMode, NULL, OPEN_EXISTING, 0, NULL); printf("\nMode Sense "); switch (sensePage) { case 0: printf("Unit Attention Page "); break; case 1: printf("Error Recovery Page "); break; case 2: printf("Disconnect Page "); break; case 3: printf("Format Device Page "); break; case 4: printf("Rigid Geometry Page "); break; case 5: printf("Flexible Page "); break; case 7: printf("Verify Error Page "); break; case 8: printf("Caching Page "); break; case 9: printf("Peripheral Page "); break; case 10: printf("Control Page "); break; case 11: printf("Medium Types Page "); break; case 12: printf("Notch Partition Page "); break; case 15: printf("Data Compression Page "); break; case 16: printf("Device Configuration Page "); break; case 17: printf("Medium Partition Page "); break; default: printf("Unknown Page "); break; } switch (senseValues) { case 0: printf("(current values)\n"); DoModeSense(&sptwb, fileHandle, sensePage | MODE_SENSE_CURRENT_VALUES); break; case 1: printf("(default values)\n"); DoModeSense(&sptwb, fileHandle, sensePage | MODE_SENSE_DEFAULT_VALUES); break; case 2: printf("(saved values)\n"); DoModeSense(&sptwb, fileHandle, sensePage | MODE_SENSE_SAVED_VALUES); break; case 3: printf("(changable values)\n"); DoModeSense(&sptwb, fileHandle, sensePage | MODE_SENSE_CHANGEABLE_VALUES); memcpy(changeBits, sptwb.ucDataBuf, sptwb.ucDataBuf[4+1]); pageLength = changeBits[4+1]; break; case 4: case 5: goto cachePage; default: printf("(no values)\n"); break; } goto newPage; cachePage: // // Get changable fields mask. // printf("(changable values)\n"); DoModeSense(&sptwb, fileHandle, sensePage | MODE_SENSE_CHANGEABLE_VALUES); // // Set up change mask. // memcpy(changeBits, sptwb.ucDataBuf, sptwb.ucDataBuf[4+1]); pageLength = changeBits[4+1]; // // Get current values. // printf("\nMode Sense Caching Page (old current values)\n"); DoModeSense(&sptwb, fileHandle, sensePage | MODE_SENSE_CURRENT_VALUES); // // Clear fields that can't be changed. // VerifyModePage(&sptwb.ucDataBuf[0], changeBits, pageLength); // // Set or clear write cache enable bit. // if (senseValues == 4) sptwb.ucDataBuf[4 + 2] |= 0x4; else sptwb.ucDataBuf[4+2] &= ~0x4; // // Determine whether this change should be saved. // while (TRUE) { printf("\n0:Do not save page\n"); printf("1:Save page\n"); printf("99:Select page\n"); printf("\nSave action? "); scanf("%d", &saveAction); if (saveAction == 99) { goto newPage; } if (saveAction == 0 || saveAction == 1) { break; } } DoModeSelect(&sptwb, fileHandle, saveAction, pageLength + 6); // // Get new current values. // printf("\nMode Sense Caching Page (new current values)\n"); DoModeSense(&sptwb, fileHandle, sensePage | MODE_SENSE_CURRENT_VALUES); goto newPage; } BOOL SetDiscPage(PUCHAR dataBuffer, PUCHAR mask, ULONG pageLength) { if (((dataBuffer[4+0] & 0x3f) != MODE_PAGE_DISCONNECT) || dataBuffer[4+1] != 0x0e) { return FALSE; } dataBuffer[4+2] = 0x80; /* buffer full ratio */ dataBuffer[4+3] = 0x80; /* buffer empty ratio */ VerifyModePage(dataBuffer, mask, pageLength); return( TRUE); } BOOL EnableWriteCache(PUCHAR dataBuffer, ULONG onoff, PUCHAR mask, ULONG pageLength) { if (((dataBuffer[4+0] & 0x3f) != MODE_PAGE_CACHING) || dataBuffer[4+1] != pageLength) { return FALSE; } return(TRUE); } BOOL VerifyModePage( PUCHAR dataBuffer, PUCHAR mask, ULONG length) { ULONG i; /* reserved, mbz, no block descs */ dataBuffer[0] = dataBuffer[2] = dataBuffer[1] = dataBuffer[3] = 0; /* Turn off what cannot be set back the way it was */ for (i = 0; i < length; i++) dataBuffer[4+i] &= mask[4+i]; return TRUE; } VOID DoInquiry(PSCSI_PASS_THROUGH_WITH_BUFFERS psptwb, HANDLE fileHandle) { ULONG length, returned; BOOLEAN status; ZeroMemory(psptwb,sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS)); psptwb->spt.Length = sizeof(SCSI_PASS_THROUGH); psptwb->spt.PathId = 0; psptwb->spt.TargetId = 0; /* kernel fixes this */ psptwb->spt.Lun = 0; psptwb->spt.CdbLength = CDB6GENERIC_LENGTH; psptwb->spt.SenseInfoLength = 24; psptwb->spt.DataIn = SCSI_IOCTL_DATA_IN; psptwb->spt.DataTransferLength = 36; psptwb->spt.TimeOutValue = 2; psptwb->spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf); psptwb->spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucSenseBuf); psptwb->spt.Cdb[0] = SCSIOP_INQUIRY; psptwb->spt.Cdb[1] = 0x0; psptwb->spt.Cdb[2] = 0x0; psptwb->spt.Cdb[3] = 0x0; psptwb->spt.Cdb[4] = 36; psptwb->spt.Cdb[5] = 0x0; length = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf) + psptwb->spt.DataTransferLength; status = DeviceIoControl(fileHandle, IOCTL_SCSI_PASS_THROUGH, psptwb, sizeof(SCSI_PASS_THROUGH), psptwb, length, &returned, FALSE); PrintInquiryStatusResults(status,returned,psptwb,length); } VOID DisplayModeSenseData( BOOLEAN status, DWORD returned, PSCSI_PASS_THROUGH_WITH_BUFFERS psptwb, ULONG length ) { ULONG errorCode; ULONG count; if (!status ) { printf( "Error: %d ", errorCode = GetLastError() ); PrintError(errorCode); return; } if (psptwb->spt.ScsiStatus) { PrintSenseInfo(psptwb); return; } else { switch ((((PUCHAR)psptwb->ucDataBuf)[4] & 0x0F)) { case MODE_PAGE_FORMAT_DEVICE: { PMODE_FORMAT_PAGE formatPage = (PMODE_FORMAT_PAGE)(((PUCHAR)psptwb->ucDataBuf) + sizeof(SCSI_MODE_HEADER)); printf("\nTracks per zone %x\n", formatPage->TracksPerZone[1] | formatPage->TracksPerZone[0] << 8); printf("Alternate sectors per zone %x\n", formatPage->AlternateSectorsPerZone[1] | formatPage->AlternateSectorsPerZone[0] << 8); printf("Alternate tracks per zone %x\n", formatPage->AlternateTracksPerZone[1] | formatPage->AlternateTracksPerZone[0] << 8); printf("Alternate tracks per logical unit %x\n", formatPage->AlternateTracksPerLogicalUnit[1] | formatPage->AlternateTracksPerLogicalUnit[0] << 8); printf("Sectors per track %x\n", formatPage->SectorsPerTrack[1] | formatPage->SectorsPerTrack[0] << 8); printf("Data bytes per physical sector %x\n", formatPage->BytesPerPhysicalSector[1] | formatPage->BytesPerPhysicalSector[0] << 8); printf("Interleave %x\n", formatPage->Interleave[1] | formatPage->Interleave[0] << 8); printf("Track skew factor %x\n", formatPage->TrackSkewFactor[1] | formatPage->TrackSkewFactor[0] << 8); printf("Cylinder skew factor %x\n", formatPage->CylinderSkewFactor[1] | formatPage->CylinderSkewFactor[0] << 8); if (formatPage->SurfaceFirst) { printf("Surface first "); } if (formatPage->RemovableMedia) { printf("Removable media "); } if (formatPage->HardSectorFormating) { printf("Hard sector formatting "); } if (formatPage->SoftSectorFormating) { printf("Soft sector formatting "); } printf("\n\n"); break; } case MODE_PAGE_CACHING: { PMODE_CACHING_PAGE cachingPage = (PMODE_CACHING_PAGE)(((PUCHAR)psptwb->ucDataBuf) + sizeof(SCSI_MODE_HEADER)); if (cachingPage->PageSavable) { printf("Page savable\n"); } if (cachingPage->ReadDisableCache) { printf("Read cache disabled\n"); } else { printf("Read cache enabled\n"); } if (cachingPage->WriteCacheEnable) { printf("Write cache enabled\n"); } else { printf("Write cache disabled\n"); } if (cachingPage->MultiplicationFactor) { printf("Multiplication factor\n"); } break; } default: printf("Bytes returned: %Xh, ", psptwb->spt.ScsiStatus,returned); printf("Data buffer length: %Xh\n\n\n", psptwb->spt.DataTransferLength); printf(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n"); printf(" ---------------------------------------------------------------\n"); for (count = 0; count < length - offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf); count++) { if ((count) % 16 == 0) { printf(" %03X ",count); } printf("%02X ", ((PUCHAR)psptwb->ucDataBuf)[count]); if ((count+1) % 8 == 0) { printf(" "); } if ((count+1) % 16 == 0) { printf("\n"); } } printf("\n\n"); break; } // end switch } // end else } // end DisplayModeSenseData() VOID DoModeSense( PSCSI_PASS_THROUGH_WITH_BUFFERS psptwb, HANDLE fileHandle, ULONG senseType) { ULONG length, returned; BOOLEAN status; char *pt[4] = { "current", "changeable", "default", "saved" }; //printf("Here is a MODE SENSE to return Mode Page %d, %s values\n", // senseType & 0x3f, pt[(senseType >> 6) & 0x3]); ZeroMemory(psptwb, sizeof(SCSI_PASS_THROUGH_WITH_BUFFERS)); psptwb->spt.Length = sizeof(SCSI_PASS_THROUGH); psptwb->spt.PathId = 0; psptwb->spt.TargetId = 0; /* kernel fixes this */ psptwb->spt.Lun = 0; psptwb->spt.CdbLength = CDB6GENERIC_LENGTH; psptwb->spt.SenseInfoLength = 24; psptwb->spt.DataIn = SCSI_IOCTL_DATA_IN; psptwb->spt.DataTransferLength = 0x1c; psptwb->spt.TimeOutValue = 2; psptwb->spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf); psptwb->spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucSenseBuf); psptwb->spt.Cdb[0] = SCSIOP_MODE_SENSE; psptwb->spt.Cdb[1] = MODE_SENSE_DISABLE_BLOCK_DESCRIPTORS; psptwb->spt.Cdb[2] = (UCHAR)senseType; psptwb->spt.Cdb[3] = 0x0; psptwb->spt.Cdb[4] = 0x1c; psptwb->spt.Cdb[5] = 0x0; length = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf) + psptwb->spt.DataTransferLength; status = DeviceIoControl(fileHandle, IOCTL_SCSI_PASS_THROUGH, psptwb, sizeof(SCSI_PASS_THROUGH), psptwb, length, &returned, FALSE); DisplayModeSenseData(status,returned,psptwb,length); } VOID DoModeSelect( PSCSI_PASS_THROUGH_WITH_BUFFERS psptwb, HANDLE fileHandle, ULONG save, ULONG datalength) { ULONG length, returned; psptwb->spt.Length = sizeof(SCSI_PASS_THROUGH); psptwb->spt.PathId = 0; psptwb->spt.TargetId = 0; /* kernel fixes this */ psptwb->spt.Lun = 0; psptwb->spt.CdbLength = CDB6GENERIC_LENGTH; psptwb->spt.SenseInfoLength = 26; psptwb->spt.DataIn = SCSI_IOCTL_DATA_OUT; psptwb->spt.DataTransferLength = datalength; psptwb->spt.TimeOutValue = 2000; psptwb->spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf); psptwb->spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucSenseBuf); psptwb->spt.Cdb[0] = SCSIOP_MODE_SELECT; psptwb->spt.Cdb[1] = (UCHAR)save; psptwb->spt.Cdb[2] = 0x0; psptwb->spt.Cdb[3] = 0x0; psptwb->spt.Cdb[4] = (UCHAR)datalength; psptwb->spt.Cdb[5] = 0x0; length = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf) + psptwb->spt.DataTransferLength; DeviceIoControl(fileHandle, IOCTL_SCSI_PASS_THROUGH, psptwb, length, psptwb, length, &returned, FALSE); } VOID PrintError(ULONG ErrorCode) { UCHAR errorBuffer[80]; ULONG count; count = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ErrorCode, 0, errorBuffer, sizeof(errorBuffer), NULL ); if (count != 0) { printf("%s\n", errorBuffer); } else { printf("Format message failed. Error: %d\n", GetLastError()); } } VOID PrintInquiryStatusResults( BOOLEAN status,DWORD returned,PSCSI_PASS_THROUGH_WITH_BUFFERS psptwb, ULONG length) { ULONG errorCode; if (!status ) { printf( "Error: %d ", errorCode = GetLastError() ); PrintError(errorCode); return; } if (psptwb->spt.ScsiStatus) { PrintSenseInfo(psptwb); return; } else { printf("%s", &psptwb->ucDataBuf[8]); return; PrintInquiryDataBuffer((PUCHAR)psptwb->ucDataBuf, length - offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS,ucDataBuf)); } } VOID PrintInquiryDataBuffer(PUCHAR DataBuffer, ULONG BufferLength) { char string[9]; printf("%s\n", &DataBuffer[8]); return; strncpy(string,&DataBuffer[8],8); string[8]= '\0'; printf(" %s ",string); strncpy(string,&DataBuffer[16],8); string[8]= '\0'; printf("%s ",string); strncpy(string,&DataBuffer[32],8); string[8]= '\0'; printf("%s ",string); } VOID PrintErrorStatus( PSCSI_PASS_THROUGH_WITH_BUFFERS psptwb ) { PrintError(GetLastError()); if (psptwb->spt.ScsiStatus) { PrintSenseInfo(psptwb); return; } } VOID PrintSenseInfo(PSCSI_PASS_THROUGH_WITH_BUFFERS psptwb) { PSENSE_DATA senseBuffer = (PSENSE_DATA)psptwb->ucSenseBuf; printf("SCSI request sense data\n"); if (psptwb->spt.SenseInfoLength == 0) { printf("No sense data\n"); return; } printf("Error code is %x\n", senseBuffer->ErrorCode); printf("Sense key is %x\n", senseBuffer->SenseKey); printf("Additional sense code is %x\n", senseBuffer->AdditionalSenseCode); printf("Additional sense code qualifier is %x\n", senseBuffer->AdditionalSenseCodeQualifier); printf("\n\n"); switch (senseBuffer->SenseKey & 0xf) { case SCSI_SENSE_NOT_READY: printf("Device not ready\n"); switch (senseBuffer->AdditionalSenseCode) { case SCSI_ADSENSE_LUN_NOT_READY: printf("Lun not ready\n"); switch (senseBuffer->AdditionalSenseCodeQualifier) { case SCSI_SENSEQ_BECOMING_READY: printf("In process of becoming ready\n"); break; case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: printf("Manual intervention required\n"); break; case SCSI_SENSEQ_FORMAT_IN_PROGRESS: printf("Format in progress\n"); break; case SCSI_SENSEQ_INIT_COMMAND_REQUIRED: default: printf("Initializing command required\n"); break; } // end switch (senseBuffer->AdditionalSenseCodeQualifier) break; case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: printf(" No Media in device.\n"); break; } // end switch (senseBuffer->AdditionalSenseCode) break; case SCSI_SENSE_DATA_PROTECT: printf("Media write protected\n"); break; case SCSI_SENSE_MEDIUM_ERROR: printf("Bad media\n"); break; case SCSI_SENSE_HARDWARE_ERROR: printf("Hardware error\n"); break; case SCSI_SENSE_ILLEGAL_REQUEST: printf("Illegal SCSI request\n"); switch (senseBuffer->AdditionalSenseCode) { case SCSI_ADSENSE_ILLEGAL_COMMAND: printf("Illegal command\n"); break; case SCSI_ADSENSE_ILLEGAL_BLOCK: printf("Illegal block address\n"); break; case SCSI_ADSENSE_INVALID_LUN: printf("Invalid LUN\n"); break; case SCSI_ADSENSE_MUSIC_AREA: printf("Music area\n"); break; case SCSI_ADSENSE_DATA_AREA: printf("Data area\n"); break; case SCSI_ADSENSE_VOLUME_OVERFLOW: printf("Volume overflow\n"); break; case SCSI_ADSENSE_INVALID_CDB: printf("Invalid field in CDB\n"); break; } // end switch (senseBuffer->AdditionalSenseCode) break; case SCSI_SENSE_UNIT_ATTENTION: switch (senseBuffer->AdditionalSenseCode) { case SCSI_ADSENSE_MEDIUM_CHANGED: printf("Media changed\n"); break; case SCSI_ADSENSE_BUS_RESET: printf("Bus reset\n"); break; default: printf("Unit attention\n"); break; } // end switch (senseBuffer->AdditionalSenseCode) break; case SCSI_SENSE_ABORTED_COMMAND: printf("Command aborted\n"); break; case SCSI_SENSE_RECOVERED_ERROR: printf("Recovered error\n"); switch(senseBuffer->AdditionalSenseCode) { case SCSI_ADSENSE_SEEK_ERROR: printf("Seek error\n"); break; case SCSI_ADSENSE_TRACK_ERROR: printf("Track error\n"); break; case SCSI_ADSENSE_REC_DATA_NOECC: printf("Recovered data - no ECC\n"); break; case SCSI_ADSENSE_REC_DATA_ECC: printf("Recovered data - ECC\n"); break; default: break; } // end switch(senseBuffer->AdditionalSenseCode) if (senseBuffer->IncorrectLength) { printf("Incorrect length detected.\n"); } break; case SCSI_SENSE_NO_SENSE: // // Check other indicators. // if (senseBuffer->IncorrectLength) { printf("Incorrect length detected.\n"); } else { printf("No specific sense key\n"); } break; default: printf("Unrecognized sense code\n"); break; } // end switch (senseBuffer->SenseKey & 0xf) }