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

RP2040 Initiator mode: tolerate glitches in SCSI phase signals

SCSI spec allows CD/MSG/IO signals to change state between
byte transfers. Previously this would cause initiator mode
to abort transfer.

On Seagate ST-296N it was observed that 6 µs long glitches on
IO signal occur when the drive is trying to perform bad sector
recovery.

This commit uses a combination of methods to tolerate glitches
on these signals, while still aborting the transfer if it hangs.
Petteri Aimonen 10 месяцев назад
Родитель
Сommit
0fe68f6616

+ 20 - 6
lib/ZuluSCSI_platform_RP2MCU/scsiHostPhy.cpp

@@ -263,13 +263,27 @@ uint32_t scsiHostRead(uint8_t *data, uint32_t count)
     {
         for (uint32_t i = 0; i < count; i++)
         {
-            while (!SCSI_IN(REQ))
+            uint32_t start = millis();
+            while (!SCSI_IN(REQ) && (millis() - start) < 10000)
             {
-                if (g_scsiHostPhyReset || !SCSI_IN(IO) || SCSI_IN(CD) != cd_start || SCSI_IN(MSG) != msg_start)
-                {
-                    // Target switched out of DATA_IN mode
-                    count = i;
-                }
+                // Wait for REQ asserted
+            }
+
+            int io = SCSI_IN(IO);
+            int cd = SCSI_IN(CD);
+            int msg = SCSI_IN(MSG);
+
+            if (g_scsiHostPhyReset)
+            {
+                dbgmsg("sciHostRead: aborting due to reset request");
+                count = i;
+                break;
+            }
+            else if (!io || cd != cd_start || msg != msg_start)
+            {
+                dbgmsg("scsiHostRead: aborting because target switched transfer phase (IO: ", io, ", CD: ", cd, ", MSG: ", msg, ")");
+                count = i;
+                break;
             }
 
             data[i] = scsiHostReadOneByte(&parityError);

+ 38 - 3
lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host.cpp

@@ -105,15 +105,50 @@ uint32_t scsi_accel_host_read(uint8_t *buf, uint32_t count, int *parityError, vo
     uint8_t *dst = buf;
     uint8_t *end = buf + count;
     uint32_t paritycheck = 0;
+    uint32_t prev_rx_time = millis();
     while (dst < end)
     {
         uint32_t available = pio_sm_get_rx_fifo_level(SCSI_PIO, SCSI_SM);
 
         if (available == 0)
         {
-            if (*resetFlag || !SCSI_IN(IO) || SCSI_IN(CD) != cd_start || SCSI_IN(MSG) != msg_start)
+            // No new data has been received by PIO, check if there is a need to abort
+
+            bool abort = false;
+            if (*resetFlag)
+            {
+                dbgmsg("scsi_accel_host_read: Aborting due to reset request");
+                abort = true;
+            }
+            else if ((millis() - prev_rx_time) > 10000)
+            {
+                dbgmsg("scsi_accel_host_read: Aborting due to timeout");
+                abort = true;
+            }
+            else
+            {
+                // Some drives such as ST-296N may have glitches on phase signals in between
+                // byte transfers. This is allowed by SCSI spec, and officially we should only
+                // check the phase signals when REQ is active. However the PIO logic currently
+                // does not do this. Instead, when we detect a phase change, wait for 10 milliseconds
+                // to see if it is real.
+                int debounce = 100;
+                while (debounce > 0 && (!SCSI_IN(IO) || SCSI_IN(CD) != cd_start || SCSI_IN(MSG) != msg_start))
+                {
+                    debounce--;
+                    delayMicroseconds(100);
+                }
+
+                if (debounce == 0)
+                {
+                    dbgmsg("scsi_accel_host_read: aborting because target switched transfer phase (IO: ",
+                        (int)SCSI_IN(IO), ", CD: ", (int)SCSI_IN(CD), ", MSG: ", (int)SCSI_IN(MSG), ")");
+                    abort = true;
+                }
+            }
+
+            if (abort)
             {
-                // Target switched out of DATA_IN mode
                 count = dst - buf;
                 break;
             }
@@ -171,4 +206,4 @@ void scsi_accel_host_init()
     sm_config_set_in_shift(&g_scsi_host.pio_cfg_async_read, true, true, 32);
 }
 
-#endif // PLATFORM_HAS_INITIATOR_MODE
+#endif // PLATFORM_HAS_INITIATOR_MODE