فهرست منبع

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 ماه پیش
والد
کامیت
0fe68f6616
2فایلهای تغییر یافته به همراه58 افزوده شده و 9 حذف شده
  1. 20 6
      lib/ZuluSCSI_platform_RP2MCU/scsiHostPhy.cpp
  2. 38 3
      lib/ZuluSCSI_platform_RP2MCU/scsi_accel_host.cpp

+ 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