diff --git a/src/BlueSCSI_initiator.cpp b/src/BlueSCSI_initiator.cpp index c9313f0d..1449274e 100644 --- a/src/BlueSCSI_initiator.cpp +++ b/src/BlueSCSI_initiator.cpp @@ -99,6 +99,12 @@ static struct { uint8_t ansi_version; uint8_t device_type; + // Sequencial Mode Information + bool fixed_blocksize; + uint32_t maxblocksize; + uint32_t minblocksize; + bool encountered_end_of_medium; + // Retry information for sector reads. // If a large read fails, retry is done sector-by-sector. int retrycount; @@ -153,6 +159,10 @@ void scsiInitiatorInit() g_initiator_state.ansi_version = 0; g_initiator_state.bad_sector_count = 0; g_initiator_state.device_type = SCSI_DEVICE_TYPE_DIRECT_ACCESS; + g_initiator_state.fixed_blocksize = false; + g_initiator_state.minblocksize = 0; + g_initiator_state.maxblocksize = 0; + g_initiator_state.encountered_end_of_medium = false; g_initiator_state.removable = false; g_initiator_state.eject_when_done = false; memset(g_initiator_state.removable_count, 0, sizeof(g_initiator_state.removable_count)); @@ -315,6 +325,10 @@ void scsiInitiatorMainLoop() g_initiator_state.ansi_version = 0; g_initiator_state.bad_sector_count = 0; g_initiator_state.device_type = SCSI_DEVICE_TYPE_DIRECT_ACCESS; + g_initiator_state.fixed_blocksize = false; + g_initiator_state.minblocksize = 0; + g_initiator_state.maxblocksize = 0; + g_initiator_state.encountered_end_of_medium = false; g_initiator_state.removable = false; g_initiator_state.eject_when_done = false; g_initiator_state.use_read10 = false; @@ -350,56 +364,11 @@ void scsiInitiatorMainLoop() } #endif - bool readcapok = startstopok && - scsiInitiatorReadCapacity(g_initiator_state.target_id, - &g_initiator_state.sectorcount, - &g_initiator_state.sectorsize); - dbgmsg("read capacity: ", readcapok ? "OK" : "FAILED"); - + //Run inquiry First bool inquiryok = startstopok && scsiInquiry(g_initiator_state.target_id, inquiry_data); dbgmsg("inquiry: ", inquiryok ? "OK" : "FAILED"); - LED_OFF(); - - uint64_t total_bytes = 0; - if (readcapok) - { - logmsg("SCSI ID ", g_initiator_state.target_id, - " capacity ", (int)g_initiator_state.sectorcount, - " sectors x ", (int)g_initiator_state.sectorsize, " bytes"); - - g_initiator_state.sectorcount_all = g_initiator_state.sectorcount; - - total_bytes = (uint64_t)g_initiator_state.sectorcount * g_initiator_state.sectorsize; - logmsg("Drive total size is ", (int)(total_bytes / (1024 * 1024)), " MiB"); - if (total_bytes >= 0xFFFFFFFF && SD.fatType() != FAT_TYPE_EXFAT) - { - // Note: the FAT32 limit is 4 GiB - 1 byte - logmsg("Target SCSI ID ", g_initiator_state.target_id, " image size is equal or larger than 4 GiB."); - logmsg("This is larger than the max filesize supported by SD card's filesystem"); - logmsg("Please reformat the SD card with exFAT format to image this target"); - g_initiator_state.drives_imaged |= 1 << g_initiator_state.target_id; - return; - } - } - else if (startstopok) - { - logmsg("SCSI ID ", g_initiator_state.target_id, " responds but ReadCapacity command failed"); - logmsg("Possibly SCSI-1 drive? Attempting to read up to 1 GB."); - g_initiator_state.sectorsize = 512; - g_initiator_state.sectorcount = g_initiator_state.sectorcount_all = 2097152; - g_initiator_state.max_sector_per_transfer = 128; - } - else - { -#ifndef BLUESCSI_NETWORK - dbgmsg("Failed to connect to SCSI ID ", g_initiator_state.target_id); -#endif - g_initiator_state.sectorsize = 0; - g_initiator_state.sectorcount = g_initiator_state.sectorcount_all = 0; - } - char filename_base[12]; strncpy(filename_base, "HD00_imaged", sizeof(filename_base)); const char *filename_extension = ".hda"; @@ -418,20 +387,6 @@ void scsiInitiatorMainLoop() memcpy(revision, &inquiry_data[32], 4); revision[4]=0; - g_initiator_state.use_read10 = scsiInitiatorTestSupportsRead10(g_initiator_state.target_id, g_initiator_state.sectorsize); - if(!g_initiator_state.use_read10) - { - // READ6 command can transfer up to 256 sectors - g_initiator_state.max_sector_per_transfer = 256; - } - - // Limit sectors per transfer based on buffer size - uint32_t max_by_buffer = sizeof(scsiDev.data) / g_initiator_state.sectorsize; - if (max_by_buffer < g_initiator_state.max_sector_per_transfer) - { - g_initiator_state.max_sector_per_transfer = max_by_buffer; - } - logmsg("SCSI Version ", (int) g_initiator_state.ansi_version); logmsg("[SCSI", g_initiator_state.target_id,"]"); logmsg(" Vendor = \"", vendor,"\""); @@ -458,6 +413,10 @@ void scsiInitiatorMainLoop() strncpy(filename_base, "MO00_imaged", sizeof(filename_base)); filename_extension = ".img"; } + else if (g_initiator_state.device_type == SCSI_DEVICE_TYPE_SEQUENTIAL){ + strncpy(filename_base, "TP00_imaged", sizeof(filename_base)); + filename_extension = ".dat"; + } else if (g_initiator_state.device_type != SCSI_DEVICE_TYPE_DIRECT_ACCESS) { logmsg(" No specific handler for the device type, treating as Direct Access Device."); @@ -477,12 +436,94 @@ void scsiInitiatorMainLoop() } } + uint64_t total_bytes = 0; + + if(g_initiator_state.device_type != SCSI_DEVICE_TYPE_SEQUENTIAL){//ReadCapacity is not a valid SEQUENCIAL command + + bool readcapok = startstopok && + scsiInitiatorReadCapacity(g_initiator_state.target_id, + &g_initiator_state.sectorcount, + &g_initiator_state.sectorsize); + dbgmsg("read capacity: ", readcapok ? "OK" : "FAILED"); + + LED_OFF(); + + if (readcapok) + { + logmsg("SCSI ID ", g_initiator_state.target_id, + " capacity ", (int)g_initiator_state.sectorcount, + " sectors x ", (int)g_initiator_state.sectorsize, " bytes"); + + g_initiator_state.sectorcount_all = g_initiator_state.sectorcount; + + total_bytes = (uint64_t)g_initiator_state.sectorcount * g_initiator_state.sectorsize; + logmsg("Drive total size is ", (int)(total_bytes / (1024 * 1024)), " MiB"); + if (total_bytes >= 0xFFFFFFFF && SD.fatType() != FAT_TYPE_EXFAT) + { + // Note: the FAT32 limit is 4 GiB - 1 byte + logmsg("Target SCSI ID ", g_initiator_state.target_id, " image size is equal or larger than 4 GiB."); + logmsg("This is larger than the max filesize supported by SD card's filesystem"); + logmsg("Please reformat the SD card with exFAT format to image this target"); + g_initiator_state.drives_imaged |= 1 << g_initiator_state.target_id; + return; + } + } + else if (startstopok) + { + logmsg("SCSI ID ", g_initiator_state.target_id, " responds but ReadCapacity command failed"); + logmsg("Possibly SCSI-1 drive? Attempting to read up to 1 GB."); + g_initiator_state.sectorsize = 512; + g_initiator_state.sectorcount = g_initiator_state.sectorcount_all = 2097152; + g_initiator_state.max_sector_per_transfer = 128; + } + else + { +#ifndef BLUESCSI_NETWORK + dbgmsg("Failed to connect to SCSI ID ", g_initiator_state.target_id); +#endif + g_initiator_state.sectorsize = 0; + g_initiator_state.sectorcount = g_initiator_state.sectorcount_all = 0; + } + }else{ + + bool readblockcapok = scsiInitiatorSequencialReadBlockLimits(g_initiator_state.target_id, + &g_initiator_state.maxblocksize, + &g_initiator_state.minblocksize, + &g_initiator_state.fixed_blocksize); + + if(readblockcapok){ + if(g_initiator_state.fixed_blocksize) + logmsg("SCSI ID ", g_initiator_state.target_id, + " fixed block size ", g_initiator_state.minblocksize); + else + logmsg("SCSI ID ", g_initiator_state.target_id, + " varible block size ", g_initiator_state.minblocksize, + " - ", g_initiator_state.maxblocksize); + } + } + + if(inquiryok){ + g_initiator_state.use_read10 = scsiInitiatorTestSupportsRead10(g_initiator_state.target_id, g_initiator_state.sectorsize); + if(!g_initiator_state.use_read10) + { + // READ6 command can transfer up to 256 sectors + g_initiator_state.max_sector_per_transfer = 256; + } + + // Limit sectors per transfer based on buffer size + uint32_t max_by_buffer = sizeof(scsiDev.data) / g_initiator_state.sectorsize; + if (max_by_buffer < g_initiator_state.max_sector_per_transfer) + { + g_initiator_state.max_sector_per_transfer = max_by_buffer; + } + } + if (g_initiator_state.eject_when_done && g_initiator_state.removable_count[g_initiator_state.target_id] == 0) { g_initiator_state.removable_count[g_initiator_state.target_id] = 1; } - if (g_initiator_state.sectorcount > 0) + if (g_initiator_state.sectorcount > 0 || g_initiator_state.maxblocksize != 0) { char filename[32] = {0}; filename_base[2] = scsiEncodeID(g_initiator_state.target_id); @@ -567,7 +608,7 @@ void scsiInitiatorMainLoop() uint64_t vhd_overhead = initiatorShouldWriteVhd() ? VHD_FOOTER_SIZE : 0; uint64_t sd_card_free_bytes = (uint64_t)SD.vol()->freeClusterCount() * SD.vol()->bytesPerCluster(); - if (sd_card_free_bytes < total_bytes + vhd_overhead) + if (sd_card_free_bytes < total_bytes + vhd_overhead)//Does not work for sequencial devices, they cannot READ CAPACITY { logmsg("SD Card only has ", (int)(sd_card_free_bytes / (1024 * 1024)), " MiB - not enough free space to image SCSI ID ", g_initiator_state.target_id); @@ -582,10 +623,12 @@ void scsiInitiatorMainLoop() return; } - if (SD.fatType() == FAT_TYPE_EXFAT) + if (SD.fatType() == FAT_TYPE_EXFAT && g_initiator_state.device_type != SCSI_DEVICE_TYPE_SEQUENTIAL) { + FsFile f; // Only preallocate on exFAT, on FAT32 preallocating can result in false garbage data in the // file if write is interrupted. + // Dont preallocate with Sequencial Devices, they do not know their own size logmsg("Preallocating image file"); g_initiator_state.target_file.preAllocate( (uint64_t)g_initiator_state.sectorcount * g_initiator_state.sectorsize + vhd_overhead); @@ -600,19 +643,30 @@ void scsiInitiatorMainLoop() logmsg("Using Alternate Start Sector ", g_initiator_state.start_sector[g_initiator_state.target_id], " For SCSI ID ", g_initiator_state.target_id); } + + if(g_initiator_state.device_type == SCSI_DEVICE_TYPE_SEQUENTIAL){ + uint8_t density_code = ini_getl("SCSI", "Density", 0x80, CONFIGFILE); + scsiInitiatorSequencialSetDensityCode(0x80, g_initiator_state.target_id); + logmsg("Density code ", density_code); + } } } } else { + bool finished_imaging = false; + if (g_initiator_state.device_type != SCSI_DEVICE_TYPE_SEQUENTIAL + && g_initiator_state.sectors_done >= g_initiator_state.sectorcount) finished_imaging = true; + if (g_initiator_state.device_type == SCSI_DEVICE_TYPE_SEQUENTIAL + && g_initiator_state.encountered_end_of_medium) finished_imaging = true; // Copy sectors from SCSI drive to file - if (g_initiator_state.sectors_done >= g_initiator_state.sectorcount) + if (finished_imaging) { scsiStartStopUnit(g_initiator_state.target_id, false); logmsg("Finished imaging drive with id ", g_initiator_state.target_id); LED_OFF(); - if (g_initiator_state.sectorcount != g_initiator_state.sectorcount_all) + if (g_initiator_state.sectorcount != g_initiator_state.sectorcount_all && g_initiator_state.device_type != SCSI_DEVICE_TYPE_SEQUENTIAL) { logmsg("NOTE: Image size was limited to first 4 GiB due to SD card filesystem limit"); logmsg("Please reformat the SD card with exFAT format to image this drive fully"); @@ -653,64 +707,89 @@ void scsiInitiatorMainLoop() return; } - scsiInitiatorUpdateLed(); + scsiInitiatorUpdateLed();//This doesnt make sense in Sequential mode, we dont know the length of the medium - // How many sectors to read in one batch? - int numtoread = g_initiator_state.sectorcount - g_initiator_state.sectors_done; - if (numtoread > g_initiator_state.max_sector_per_transfer) - numtoread = g_initiator_state.max_sector_per_transfer; + if(g_initiator_state.device_type == SCSI_DEVICE_TYPE_SEQUENTIAL){ - // Retry sector-by-sector after failure - if (g_initiator_state.sectors_done < g_initiator_state.failposition) - numtoread = 1; + uint32_t bytes_to_transfer = 5822; + uint32_t time_start = platform_millis(); + bool status = scsiInitiatorReadDataToFile(g_initiator_state.target_id, + 0, 1, bytes_to_transfer, + g_initiator_state.target_file); - uint32_t time_start = platform_millis(); - bool status = scsiInitiatorReadDataToFile(g_initiator_state.target_id, - g_initiator_state.sectors_done, numtoread, g_initiator_state.sectorsize, - g_initiator_state.target_file); + if(!status){ - if (!status) - { - logmsg("Failed to transfer ", numtoread, " sectors starting at ", (int)g_initiator_state.sectors_done); + logmsg("Failed to transfer ", bytes_to_transfer, " bytes at ", (int)g_initiator_state.sectors_done); + //Retrying is difficult and inefficent in sequencial mode + g_initiator_state.bad_sector_count++; + }else{ + + g_initiator_state.sectors_done += bytes_to_transfer; + g_initiator_state.target_file.flush(); + int speed_kbps = bytes_to_transfer / (platform_millis() - time_start); + logmsg("SCSI read succeeded, read ", + (int)g_initiator_state.sectors_done, " bytes", + " speed ", speed_kbps, " kB/s"); + } + + }else{ + // How many sectors to read in one batch? + int numtoread = g_initiator_state.sectorcount - g_initiator_state.sectors_done; + if (numtoread > g_initiator_state.max_sector_per_transfer) + numtoread = g_initiator_state.max_sector_per_transfer; + + // Retry sector-by-sector after failure + if (g_initiator_state.sectors_done < g_initiator_state.failposition) + numtoread = 1; - if (g_initiator_state.retrycount < g_initiator_state.max_retry_count) + uint32_t time_start = platform_millis(); + bool status = scsiInitiatorReadDataToFile(g_initiator_state.target_id, + g_initiator_state.sectors_done, numtoread, g_initiator_state.sectorsize, + g_initiator_state.target_file); + + if (!status) { - logmsg("Retrying.. ", g_initiator_state.retrycount + 1, "/", (int) g_initiator_state.max_retry_count); - delay_with_poll(200); - // This reset causes some drives to hang and seems to have no effect if left off. - // scsiHostPhyReset(); - delay_with_poll(200); + logmsg("Failed to transfer ", numtoread, " sectors starting at ", (int)g_initiator_state.sectors_done); - g_initiator_state.retrycount++; - g_initiator_state.target_file.seek((uint64_t)g_initiator_state.sectors_done * g_initiator_state.sectorsize); + if (g_initiator_state.retrycount < g_initiator_state.max_retry_count) + { + logmsg("Retrying.. ", g_initiator_state.retrycount + 1, "/", (int) g_initiator_state.max_retry_count); + delay_with_poll(200); + // This reset causes some drives to hang and seems to have no effect if left off. + // scsiHostPhyReset(); + delay_with_poll(200); - if (g_initiator_state.retrycount > 1 && numtoread > 1) + g_initiator_state.retrycount++; + g_initiator_state.target_file.seek((uint64_t)g_initiator_state.sectors_done * g_initiator_state.sectorsize); + + if (g_initiator_state.retrycount > 1 && numtoread > 1) + { + logmsg("Multiple failures, retrying sector-by-sector"); + g_initiator_state.failposition = g_initiator_state.sectors_done + numtoread; + } + } + else { - logmsg("Multiple failures, retrying sector-by-sector"); - g_initiator_state.failposition = g_initiator_state.sectors_done + numtoread; + logmsg("Retry limit exceeded, skipping one sector"); + g_initiator_state.retrycount = 0; + g_initiator_state.sectors_done++; + g_initiator_state.bad_sector_count++; + g_initiator_state.target_file.seek((uint64_t)g_initiator_state.sectors_done * g_initiator_state.sectorsize); } } else { - logmsg("Retry limit exceeded, skipping one sector"); g_initiator_state.retrycount = 0; - g_initiator_state.sectors_done++; - g_initiator_state.bad_sector_count++; - g_initiator_state.target_file.seek((uint64_t)g_initiator_state.sectors_done * g_initiator_state.sectorsize); + g_initiator_state.sectors_done += numtoread; + g_initiator_state.target_file.flush(); + + int speed_kbps = numtoread * g_initiator_state.sectorsize / (platform_millis() - time_start); + logmsg("SCSI read succeeded, sectors done: ", + (int)g_initiator_state.sectors_done, " / ", (int)g_initiator_state.sectorcount, + " speed ", speed_kbps, " kB/s - ", + (int)(100 * (int64_t)g_initiator_state.sectors_done / g_initiator_state.sectorcount), "%"); } } - else - { - g_initiator_state.retrycount = 0; - g_initiator_state.sectors_done += numtoread; - g_initiator_state.target_file.flush(); - - int speed_kbps = numtoread * g_initiator_state.sectorsize / (platform_millis() - time_start); - logmsg("SCSI read succeeded, sectors done: ", - (int)g_initiator_state.sectors_done, " / ", (int)g_initiator_state.sectorcount, - " speed ", speed_kbps, " kB/s - ", - (int)(100 * (int64_t)g_initiator_state.sectors_done / g_initiator_state.sectorcount), "%"); - } } } @@ -884,8 +963,40 @@ bool scsiInitiatorReadCapacity(int target_id, uint32_t *sectorcount, uint32_t *s } } +bool scsiInitiatorSequencialReadBlockLimits(int target_id, uint32_t *maxblocksize, uint32_t *minblocksize, bool *fixedsize) +{ + uint8_t command[6] = {0x05, 0, 0, 0, 0, 0}; + uint8_t response[6] = {0}; + int status = scsiInitiatorRunCommand(target_id, + command, sizeof(command), + response, sizeof(response), + NULL, 0); + + if (status == 0) + { + *maxblocksize = ((uint32_t)response[1] << 16) + | ((uint32_t)response[2] << 8) + | ((uint32_t)response[3] << 0); + + *minblocksize = ((uint32_t)response[4] << 8) + | ((uint32_t)response[5] << 0); + + + *fixedsize = *maxblocksize == *minblocksize; + + return true; + } + else + { + uint8_t sense_key; + scsiRequestSense(target_id, &sense_key); + scsiLogInitiatorCommandFailure("READ BLOCK LIMIT", target_id, status, sense_key); + return false; + } +} + // Execute REQUEST SENSE command to get more information about error status -bool scsiRequestSense(int target_id, uint8_t *sense_key, uint8_t *sense_asc, uint8_t *sense_ascq) +bool scsiRequestSense(int target_id, uint8_t *sense_key, uint8_t *sense_asc, uint8_t *sense_ascq, bool *filemark, bool *EOM) { uint8_t command[6] = {0x03, 0, 0, 0, 18, 0}; uint8_t response[18] = {0}; @@ -899,6 +1010,8 @@ bool scsiRequestSense(int target_id, uint8_t *sense_key, uint8_t *sense_asc, uin " sense_key ", (int)(response[2] & 0xF), " asc ", response[12], " ascq ", response[13]); + if (filemark) *filemark = response[2] & (1 << 7); + if (EOM) *EOM = response[2] & (1 << 6); if (sense_key) *sense_key = response[2] & 0xF; if (sense_asc) *sense_asc = response[12]; if (sense_ascq) *sense_ascq = response[13]; @@ -1322,7 +1435,32 @@ bool scsiInitiatorReadDataToFile(int target_id, uint32_t start_sector, uint32_t // Read6 command supports 21 bit LBA - max of 0x1FFFFF // ref: https://www.seagate.com/files/staticfiles/support/docs/manual/Interface%20manuals/100293068j.pdf pg 134 bool fits_read6 = (start_sector < 0x1FFFFF && sectorcount <= 256); - if (!g_initiator_state.use_read10 && fits_read6) + + if (g_initiator_state.device_type == SCSI_DEVICE_TYPE_SEQUENTIAL){ + + uint32_t bytes_to_read = sectorsize * sectorcount; + + uint8_t command[6] = {0x08, + 0x00, + (uint8_t)(bytes_to_read >> 16), + (uint8_t)(bytes_to_read >> 8), + (uint8_t)(bytes_to_read), + 0x00 + }; + + if(g_initiator_state.fixed_blocksize){ + command[1] += 1;//Set FIXED bit + //TODO + //With FIXED blocks it will transfer multiple blocks of the same size, + //so transfer length is the number of blocks, as opposed to bytes + //However the block size must be set in the mode parameters block descriptor, causeing this to fail + dbgmsg("Error: Fixed block size is not yet supported!"); + return false; + } + + status = scsiInitiatorRunCommand(target_id, command, sizeof(command), NULL, 0, NULL, 0, true); + } + else if (!g_initiator_state.use_read10 && fits_read6) { // Use READ6 command for compatibility with old SCSI1 drives // Note that even with SCSI1 drives we have no choice but to use READ10 if the drive @@ -1357,11 +1495,30 @@ bool scsiInitiatorReadDataToFile(int target_id, uint32_t start_sector, uint32_t if (status != 0) { uint8_t sense_key; - scsiRequestSense(target_id, &sense_key); - - scsiLogInitiatorCommandFailure("scsiInitiatorReadDataToFile command phase", target_id, status, sense_key); - scsiHostPhyRelease(); - return false; + uint8_t sense_asc; + uint8_t sense_ascq; + bool filemark; + bool end_of_medium;//Doesnt quite work + + scsiRequestSense(target_id, &sense_key, &sense_asc, &sense_ascq, &filemark, &end_of_medium); + + if(status == 2 && sense_key == 0x08 && g_initiator_state.device_type == SCSI_DEVICE_TYPE_SEQUENTIAL){//BLANK CHECK!!! either we are done, or we started with the tape in the drive + if(g_initiator_state.sectors_done){//Data present we done now! + g_initiator_state.encountered_end_of_medium = true; + return true; + }else{ + logmsg("Reload Tape!"); + return false; + } + } + else if(g_initiator_state.device_type != SCSI_DEVICE_TYPE_SEQUENTIAL || !end_of_medium){ + logmsg("Filemark Present: ", filemark, " EOM: ", end_of_medium); + scsiLogInitiatorCommandFailure("scsiInitiatorReadDataToFile command phase", target_id, status, sense_key); + scsiHostPhyRelease(); + scsiInitiatorSequencialSpace(1, 0, target_id); + return false; + } + } SCSI_PHASE phase; @@ -1473,6 +1630,68 @@ bool scsiInitiatorReadDataToFile(int target_id, uint32_t start_sector, uint32_t } } +bool scsiInitiatorSequencialRewind(int target_id){ + platform_poll(); + uint8_t command[6] = {0x01, 0, 0, 0, 0, 0}; + int status = scsiInitiatorRunCommand(target_id, + command, sizeof(command), + NULL, 0, + NULL, 0); + platform_poll(); + return status == 0; +} + +bool scsiInitiatorSequencialSetDensityCode(uint8_t code, int target_id) +{ + // Mode Select command for DAT drives + // This sets the density code in the block descriptor + uint8_t mode_data[12] = { + 0x00, 0x00, 0x10, 0x08, // Header + code, 0x00, 0x00, 0x00, // Block descriptor + 0x00, 0x00, 0x00, 0x02 + }; + + uint8_t command[6] = {0x15, 0x00, 0x00, 0x00, 12, 0x00}; + + int status = scsiInitiatorRunCommand(target_id, + command, sizeof(command), + NULL, 0, + mode_data, sizeof(mode_data)); + + if (status == 0) + { + logmsg("Density code set to ", code); + } + else + { + uint8_t sense_key, asc, ascq; + scsiRequestSense(target_id, &sense_key, &asc, &ascq); + scsiLogInitiatorCommandFailure("scsiInitiatorSequencialSetDensityCode", target_id, status, sense_key); + return false; + } + return true; +} + +bool scsiInitiatorSequencialSpace(uint32_t count, uint8_t type, int target_id){ + uint8_t command[6] = {0x11, + type & 0x07, + (uint8_t)(count >> 16), + (uint8_t)(count >> 8), + (uint8_t)(count), + 0x00}; + int status = scsiInitiatorRunCommand(target_id, + command, sizeof(command), + NULL, 0, + NULL, 0); + if(status){ + uint8_t sense_key, asc, ascq; + scsiRequestSense(target_id, &sense_key, &asc, &ascq); + scsiLogInitiatorCommandFailure("scsiInitiatorSequencialSpace", target_id, status, sense_key); + return false; + } + return true; +} + #ifdef UNIT_TEST /* Test accessors for initiator state */ bool test_initiator_get_use_vhd_format() { diff --git a/src/BlueSCSI_initiator.h b/src/BlueSCSI_initiator.h index fdf36e5c..a033797f 100644 --- a/src/BlueSCSI_initiator.h +++ b/src/BlueSCSI_initiator.h @@ -71,8 +71,11 @@ bool scsiInitiatorTestSupportsRead10(int target_id, uint32_t sectorsize); // Execute READ CAPACITY command bool scsiInitiatorReadCapacity(int target_id, uint32_t *sectorcount, uint32_t *sectorsize); +// Execute READ BLOCK LIMITS command to access the size of sequencial device frames +bool scsiInitiatorSequencialReadBlockLimits(int target_id, uint32_t *maxblocksize, uint32_t *minblocksize, bool *fixedsize); + // Execute REQUEST SENSE command to get more information about error status -bool scsiRequestSense(int target_id, uint8_t *sense_key, uint8_t *sense_asc = nullptr, uint8_t *sense_ascq = nullptr); +bool scsiRequestSense(int target_id, uint8_t *sense_key, uint8_t *sense_asc = nullptr, uint8_t *sense_ascq = nullptr, bool *filemark = nullptr, bool *EOM = nullptr); // Execute UNIT START STOP command to load/unload media bool scsiStartStopUnit(int target_id, bool start); @@ -96,3 +99,11 @@ bool scsiInitiatorResetBusConfig(int target_id); // Negotiate bus width with target bool scsiInitiatorSetBusWidth(int target_id, int busWidth); + +// Rewind Sequencial Devices to the beginning +bool scsiInitiatorSequencialRewind(int target_id); + +// Set the density code +bool scsiInitiatorSequencialSetDensityCode(uint8_t code, int target_id); + +bool scsiInitiatorSequencialSpace(uint32_t count, uint8_t type, int target_id); \ No newline at end of file