From 2ccc9fcac6e1877d55da8764315e0f4bbfc202a6 Mon Sep 17 00:00:00 2001 From: andrewocyeah Date: Tue, 14 Jul 2026 10:34:43 -0400 Subject: [PATCH 1/4] initiator: add sequential-device state and detect tape drives --- src/BlueSCSI_initiator.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/BlueSCSI_initiator.cpp b/src/BlueSCSI_initiator.cpp index c9313f0d..2f9cb42d 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; @@ -458,6 +472,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."); From 9d22d32fc17dda0ea61e6a3830527b6221392de8 Mon Sep 17 00:00:00 2001 From: andrewocyeah Date: Tue, 14 Jul 2026 10:40:19 -0400 Subject: [PATCH 2/4] initiator: use READ BLOCK LIMITS + density code for tape READ CAPACITY is not a valid command for sequential-access devices, so the drive's capacity/geometry can no longer be queried the same way once tape is a supported device type. Move the Inquiry call ahead of the capacity query so device_type is known before deciding which capacity command to issue, then branch: - Non-tape: unchanged READ CAPACITY path. - Tape: call the new scsiInitiatorSequencialReadBlockLimits() to get min/max block size and whether it's fixed, then set the density code via scsiInitiatorSequencialSetDensityCode() before transfer. Also skip SD image preallocation for tape, since block limits give no indication of total medium length, and preallocating would either be wrong or impossible to size correctly. This is a refactor of existing capacity-detection logic combined with the minimum new logic required to make it tape-aware; no unrelated changes are included. --- src/BlueSCSI_initiator.cpp | 153 ++++++++++++++++++++++--------------- src/BlueSCSI_initiator.h | 3 + 2 files changed, 94 insertions(+), 62 deletions(-) diff --git a/src/BlueSCSI_initiator.cpp b/src/BlueSCSI_initiator.cpp index 2f9cb42d..dc60102f 100644 --- a/src/BlueSCSI_initiator.cpp +++ b/src/BlueSCSI_initiator.cpp @@ -364,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"; @@ -432,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,"\""); @@ -495,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); @@ -600,7 +623,7 @@ void scsiInitiatorMainLoop() return; } - if (SD.fatType() == FAT_TYPE_EXFAT) + if (SD.fatType() == FAT_TYPE_EXFAT && g_initiator_state.device_type != SCSI_DEVICE_TYPE_SEQUENTIAL) { // Only preallocate on exFAT, on FAT32 preallocating can result in false garbage data in the // file if write is interrupted. @@ -618,6 +641,12 @@ 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); + } } } } diff --git a/src/BlueSCSI_initiator.h b/src/BlueSCSI_initiator.h index fdf36e5c..20444de7 100644 --- a/src/BlueSCSI_initiator.h +++ b/src/BlueSCSI_initiator.h @@ -71,6 +71,9 @@ 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); From 207eab89eb6ad4c103b1bf6e4bdee92551a90469 Mon Sep 17 00:00:00 2001 From: andrewocyeah Date: Tue, 14 Jul 2026 10:57:09 -0400 Subject: [PATCH 3/4] initiator: add tape SCSI helpers and extend sense reporting --- src/BlueSCSI_initiator.cpp | 98 +++++++++++++++++++++++++++++++++++++- src/BlueSCSI_initiator.h | 10 +++- 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/src/BlueSCSI_initiator.cpp b/src/BlueSCSI_initiator.cpp index dc60102f..0de137de 100644 --- a/src/BlueSCSI_initiator.cpp +++ b/src/BlueSCSI_initiator.cpp @@ -931,8 +931,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}; @@ -946,6 +978,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]; @@ -1520,6 +1554,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 20444de7..a033797f 100644 --- a/src/BlueSCSI_initiator.h +++ b/src/BlueSCSI_initiator.h @@ -75,7 +75,7 @@ bool scsiInitiatorReadCapacity(int target_id, uint32_t *sectorcount, uint32_t *s 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); @@ -99,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 From 5d66998ee48edd84cc12402732b79ffbb4856ef3 Mon Sep 17 00:00:00 2001 From: andrewocyeah Date: Tue, 14 Jul 2026 11:01:34 -0400 Subject: [PATCH 4/4] initiator: implement tape imaging loop with end-of-medium handling --- src/BlueSCSI_initiator.cpp | 176 ++++++++++++++++++++++++++----------- 1 file changed, 126 insertions(+), 50 deletions(-) diff --git a/src/BlueSCSI_initiator.cpp b/src/BlueSCSI_initiator.cpp index 0de137de..1449274e 100644 --- a/src/BlueSCSI_initiator.cpp +++ b/src/BlueSCSI_initiator.cpp @@ -608,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); @@ -625,8 +625,10 @@ void scsiInitiatorMainLoop() 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); @@ -652,14 +654,19 @@ void scsiInitiatorMainLoop() } 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"); @@ -700,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"); + } - if (g_initiator_state.retrycount < g_initiator_state.max_retry_count) + }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; + + 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); + + 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); - g_initiator_state.retrycount++; - g_initiator_state.target_file.seek((uint64_t)g_initiator_state.sectors_done * g_initiator_state.sectorsize); + 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) + 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), "%"); - } } } @@ -1403,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 @@ -1438,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); + uint8_t sense_asc; + uint8_t sense_ascq; + bool filemark; + bool end_of_medium;//Doesnt quite work - scsiLogInitiatorCommandFailure("scsiInitiatorReadDataToFile command phase", target_id, status, sense_key); - scsiHostPhyRelease(); - return false; + 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;