Просмотр исходного кода

Initiator: Autodetect support of READ10 command

IBM H3171-S2 reports SCSI version 1 and supports both READ10 and READ6.
But READ6 fails in weird ways.

Changed the logic so that:
1) If InitiatorUseRead10 is specified in zuluscsi.ini, it forces the mode.
2) Otherwise READ10 support is detected by trying to run the command.
Petteri Aimonen 8 месяцев назад
Родитель
Сommit
2d5f416dc9
4 измененных файлов с 60 добавлено и 10 удалено
  1. 36 4
      src/ZuluSCSI_initiator.cpp
  2. 5 0
      src/ZuluSCSI_initiator.h
  3. 18 6
      src/ZuluSCSI_msc_initiator.cpp
  4. 1 0
      zuluscsi.ini

+ 36 - 4
src/ZuluSCSI_initiator.cpp

@@ -74,7 +74,10 @@ static struct {
     // Bitmap of all drives that have been imaged
     uint32_t drives_imaged;
 
+    // Configuration from .ini
     uint8_t initiator_id;
+    uint8_t max_retry_count;
+    bool use_read10; // Always use read10 commands
 
     // Is imaging a drive in progress, or are we scanning?
     bool imaging;
@@ -88,7 +91,6 @@ static struct {
     uint32_t max_sector_per_transfer;
     uint32_t bad_sector_count;
     uint8_t ansi_version;
-    uint8_t max_retry_count;
     uint8_t device_type;
 
     // Retry information for sector reads.
@@ -121,6 +123,7 @@ void scsiInitiatorInit()
         logmsg("InitiatorID set to ID ", g_initiator_state.initiator_id);
     }
     g_initiator_state.max_retry_count = ini_getl("SCSI", "InitiatorMaxRetry", 5, CONFIGFILE);
+    g_initiator_state.use_read10 = ini_getbool("SCSI", "InitiatorUseRead10", false, CONFIGFILE);
 
     // treat initiator id as already imaged drive so it gets skipped
     g_initiator_state.drives_imaged = 1 << g_initiator_state.initiator_id;
@@ -252,6 +255,7 @@ void scsiInitiatorMainLoop()
         g_initiator_state.device_type = SCSI_DEVICE_TYPE_DIRECT_ACCESS;
         g_initiator_state.removable = false;
         g_initiator_state.eject_when_done = false;
+        g_initiator_state.use_read10 = false;
 
         if (!(g_initiator_state.drives_imaged & (1 << g_initiator_state.target_id)))
         {
@@ -330,11 +334,13 @@ void scsiInitiatorMainLoop()
                 memcpy(revision, &inquiry_data[32], 4);
                 revision[4]=0;
 
-                if(g_initiator_state.ansi_version < 0x02)
+                g_initiator_state.use_read10 = scsiInitiatorTestSupportsRead10(g_initiator_state.target_id, g_initiator_state.sectorsize);
+                if(!g_initiator_state.use_read10)
                 {
-                    // this is a SCSI-1 drive, use READ6 and 256 bytes to be safe.
+                    // READ6 command can transfer up to 256 sectors
                     g_initiator_state.max_sector_per_transfer = 256;
                 }
+
                 int ini_type = scsiTypeToIniType(g_initiator_state.device_type, g_initiator_state.removable);
                 logmsg("SCSI Version ", (int) g_initiator_state.ansi_version);
                 logmsg("[SCSI", g_initiator_state.target_id,"]");
@@ -682,6 +688,29 @@ int scsiInitiatorRunCommand(int target_id,
     return status;
 }
 
+bool scsiInitiatorTestSupportsRead10(int target_id, uint32_t sectorsize)
+{
+    if (ini_haskey("SCSI", "InitiatorUseRead10", CONFIGFILE))
+    {
+        return ini_getbool("SCSI", "InitiatorUseRead10", false, CONFIGFILE);
+    }
+
+    uint8_t command[10] = {0x28, 0x00, 0, 0, 0, 0, 0, 0, 1, 0}; // READ10, LBA 0, 1 sector
+    int status = scsiInitiatorRunCommand(target_id, command, sizeof(command),
+        scsiDev.data, sectorsize, NULL, 0);
+
+    if (status == 0)
+    {
+        dbgmsg("Target supports READ10 command");
+        return true;
+    }
+    else
+    {
+        dbgmsg("Target does not support READ10 command");
+        return false;
+    }
+}
+
 bool scsiInitiatorReadCapacity(int target_id, uint32_t *sectorcount, uint32_t *sectorsize)
 {
     uint8_t command[10] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -961,9 +990,12 @@ 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
-    if (g_initiator_state.ansi_version < 0x02 || (start_sector < 0x1FFFFF && sectorcount <= 256))
+    bool fits_read6 = (start_sector < 0x1FFFFF && sectorcount <= 256);
+    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
+        // size is larger than 1 GB, as the sector number wouldn't fit in the command.
         uint8_t command[6] = {0x08,
             (uint8_t)(start_sector >> 16),
             (uint8_t)(start_sector >> 8),

+ 5 - 0
src/ZuluSCSI_initiator.h

@@ -46,6 +46,11 @@ int scsiInitiatorRunCommand(int target_id,
                             bool returnDataPhase = false,
                             uint32_t timeout = 30000);
 
+// Detect support of read10 commands.
+// Returns true if supported.
+// Return value can be overridden by .ini file, in which case test is not done.
+bool scsiInitiatorTestSupportsRead10(int target_id, uint32_t sectorsize);
+
 // Execute READ CAPACITY command
 bool scsiInitiatorReadCapacity(int target_id, uint32_t *sectorcount, uint32_t *sectorsize);
 

+ 18 - 6
src/ZuluSCSI_msc_initiator.cpp

@@ -60,6 +60,7 @@ static struct {
     int target_id;
     uint32_t sectorsize;
     uint32_t sectorcount;
+    bool use_read10; // Always use read10/write10 commands for this target
 } g_msc_initiator_targets[NUM_SCSIID];
 static int g_msc_initiator_target_count;
 
@@ -71,6 +72,7 @@ static struct {
     int prefetch_target_id; // Target to read from
     size_t prefetch_sectorcount; // Number of sectors to fetch
     size_t prefetch_sectorsize;
+    bool prefetch_use_read10;
     bool prefetch_done; // True after prefetch is complete
 
     // Periodic status reporting to log output
@@ -83,7 +85,7 @@ static struct {
     uint32_t last_scan_time;
 } g_msc_initiator_state;
 
-static int do_read6_or_10(int target_id, uint32_t start_sector, uint32_t sectorcount, uint32_t sectorsize, void *buffer);
+static int do_read6_or_10(int target_id, uint32_t start_sector, uint32_t sectorcount, uint32_t sectorsize, void *buffer, bool use_read10);
 
 static void scan_targets()
 {
@@ -119,6 +121,7 @@ static void scan_targets()
                     g_msc_initiator_targets[found_count].target_id = target_id;
                     g_msc_initiator_targets[found_count].sectorcount = sectorcount;
                     g_msc_initiator_targets[found_count].sectorsize = sectorsize;
+                    g_msc_initiator_targets[found_count].use_read10 = scsiInitiatorTestSupportsRead10(target_id, sectorsize);
                     found_count++;
                 }
                 else
@@ -128,6 +131,7 @@ static void scan_targets()
                     g_msc_initiator_targets[found_count].target_id = target_id;
                     g_msc_initiator_targets[found_count].sectorcount = 2097152;
                     g_msc_initiator_targets[found_count].sectorsize = 512;
+                    g_msc_initiator_targets[found_count].use_read10 = false;
                     found_count++;
                 }
             }
@@ -209,7 +213,8 @@ void poll_msc_initiator()
                                     g_msc_initiator_state.prefetch_lba,
                                     g_msc_initiator_state.prefetch_sectorcount,
                                     g_msc_initiator_state.prefetch_sectorsize,
-                                    g_msc_initiator_state.prefetch_buffer);
+                                    g_msc_initiator_state.prefetch_buffer,
+                                    g_msc_initiator_state.prefetch_use_read10);
         if (status == 0)
         {
             g_msc_initiator_state.prefetch_done = true;
@@ -399,15 +404,18 @@ int32_t init_msc_scsi_cb(uint8_t lun, const uint8_t scsi_cmd[16], void *buffer,
     return status;
 }
 
-static int do_read6_or_10(int target_id, uint32_t start_sector, uint32_t sectorcount, uint32_t sectorsize, void *buffer)
+static int do_read6_or_10(int target_id, uint32_t start_sector, uint32_t sectorcount, uint32_t sectorsize, void *buffer, bool use_read10)
 {
     int status;
 
     // 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
-    if (start_sector < 0x1FFFFF && sectorcount <= 256)
+    bool fits_read6 = (start_sector < 0x1FFFFF && sectorcount <= 256);
+    if (!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
+        // size is larger than 1 GB, as the sector number wouldn't fit in the command.
         uint8_t command[6] = {0x08,
             (uint8_t)(start_sector >> 16),
             (uint8_t)(start_sector >> 8),
@@ -448,6 +456,7 @@ int32_t init_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buf
 
     int target_id = get_target(lun);
     int sectorsize = g_msc_initiator_targets[lun].sectorsize;
+    bool use_read10 = g_msc_initiator_targets[lun].use_read10;
     uint32_t sectorcount = bufsize / sectorsize;
     uint32_t total_sectorcount = sectorcount;
     uint32_t orig_lba = lba;
@@ -477,7 +486,7 @@ int32_t init_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buf
     {
         dbgmsg("USB Read command ", (int)orig_lba, " + ", (int)total_sectorcount, "x", (int)sectorsize,
                " got ", (int)(total_sectorcount - sectorcount), " sectors from prefetch");
-        status = do_read6_or_10(target_id, lba, sectorcount, sectorsize, buffer);
+        status = do_read6_or_10(target_id, lba, sectorcount, sectorsize, buffer, use_read10);
         lba += sectorcount;
     }
     else
@@ -522,6 +531,7 @@ int32_t init_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buf
         g_msc_initiator_state.prefetch_target_id = target_id;
         g_msc_initiator_state.prefetch_sectorcount = total_sectorcount;
         g_msc_initiator_state.prefetch_sectorsize = sectorsize;
+        g_msc_initiator_state.prefetch_use_read10 = use_read10;
         g_msc_initiator_state.prefetch_done = false;
     }
 
@@ -539,6 +549,7 @@ int32_t init_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t
 
     int target_id = get_target(lun);
     int sectorsize = g_msc_initiator_targets[lun].sectorsize;
+    bool use_read10 = g_msc_initiator_targets[lun].use_read10;
     uint32_t start_sector = lba;
     uint32_t sectorcount = bufsize / sectorsize;
 
@@ -551,7 +562,8 @@ int32_t init_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t
     LED_ON();
 
     // Write6 command supports 21 bit LBA - max of 0x1FFFFF
-    if (start_sector < 0x1FFFFF && sectorcount <= 256)
+    bool fits_write6 = (start_sector < 0x1FFFFF && sectorcount <= 256);
+    if (!use_read10 && fits_write6)
     {
         // Use WRITE6 command for compatibility with old SCSI1 drives
         uint8_t command[6] = {0x0A,

+ 1 - 0
zuluscsi.ini

@@ -45,6 +45,7 @@
 #InitiatorID = 7 # SCSI ID, 0-7, when the device is in initiator mode, default is 7
 #InitiatorMaxRetry = 5 #  number of retries on failed reads 0-255, default is 5
 #InitiatorImageHandling = 0 # 0: skip existing images, 1: create new image with incrementing suffix, 2: overwrite existing image
+#InitiatorUseRead10 = 0 # 0: Always use READ6 command, 1: Always use READ10 command, not set: Autodetect READ10 support
 
 #InitiatorMSC = 0 # Force USB MSC mode for initiator. By default enabled only if SD card is not inserted.
 #InitiatorMSCDisablePrefetch = 0 # Disable read prefetching in USB MSC mode