Fix sync negotiation bug
authorMichael McMaster <michael@codesrc.com>
Thu, 21 Sep 2017 10:51:25 +0000 (20:51 +1000)
committerMichael McMaster <michael@codesrc.com>
Thu, 21 Sep 2017 10:51:25 +0000 (20:51 +1000)
CHANGELOG
src/firmware/disk.c
src/firmware/scsi.c
src/firmware/scsiPhy.c

index 6af0a61..a308438 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,6 @@
+2017XXXX               6.1.2
+       - Fix synchronous negotiation bugs
+
 20170520               6.1.1
        - Performance improvements to improve throughput at all scsi speeds
        - Add new "turbo" speed option to boost speeds.
index 4496c28..e7c097f 100755 (executable)
@@ -684,9 +684,10 @@ void scsiDiskPoll()
                int i = 0;\r
                // int scsiDisconnected = 0;\r
                int scsiComplete = 0;\r
-               // uint32_t lastActivityTime = s2s_getTime_ms();\r
+               //uint32_t lastActivityTime = s2s_getTime_ms();\r
                // int scsiActive = 0;\r
                // int sdActive = 0;\r
+               int clearBSY = 0;\r
 \r
                int parityError = 0;\r
                while ((i < totalSDSectors) &&\r
@@ -704,6 +705,24 @@ void scsiDiskPoll()
                                rem < maxSectors ? rem : maxSectors;\r
                        scsiRead(&scsiDev.data[0], sectors * SD_SECTOR_SIZE, &parityError);\r
 \r
+                       if (i + sectors >= totalSDSectors)\r
+                       {\r
+                               // We're transferring over the SCSI bus faster than the SD card\r
+                               // can write.  All data is buffered, and we're just waiting for\r
+                               // the SD card to complete. The host won't let us disconnect.\r
+                               // Some drivers set a 250ms timeout on transfers to complete.\r
+                               // SD card writes are supposed to complete\r
+                               // within 200ms, but sometimes they don'to.\r
+                               // Just pretend we're finished.\r
+                               process_Status();\r
+                               process_MessageIn(); // Will go to BUS_FREE state\r
+\r
+                               // Try and prevent anyone else using the SCSI bus while we're not ready.\r
+                               *SCSI_CTRL_BSY = 1;\r
+                               clearBSY = 1;\r
+                       }\r
+\r
+\r
                        if (!parityError)\r
                        {\r
                                sdTmpWrite(&scsiDev.data[0], i + sdLBA, sectors);\r
@@ -830,6 +849,11 @@ void scsiDiskPoll()
 #endif\r
                }\r
 \r
+               if (clearBSY)\r
+               {\r
+                       *SCSI_CTRL_BSY = 0;\r
+               }\r
+\r
 #if 0\r
                if (scsiComplete)\r
                {\r
index 19fb476..78dfbf4 100755 (executable)
@@ -477,6 +477,8 @@ static void doReserveRelease()
        }\r
 }\r
 \r
+static uint32_t resetUntil = 0;\r
+\r
 static void scsiReset()\r
 {\r
        scsiDev.rstCount++;\r
@@ -515,6 +517,7 @@ static void scsiReset()
 \r
        scsiDev.postDataOutHook = NULL;\r
 \r
+\r
        // Sleep to allow the bus to settle down a bit.\r
        // We must be ready again within the "Reset to selection time" of\r
        // 250ms.\r
@@ -523,7 +526,7 @@ static void scsiReset()
        // in which case TERMPWR cannot be supplied, and reset will ALWAYS\r
        // be true. Therefore, the sleep here must be slow to avoid slowing\r
        // USB comms\r
-       s2s_delay_ms(1); // 1ms.\r
+       resetUntil = s2s_getTime_ms() + 2; // At least 1ms.\r
 }\r
 \r
 static void enter_SelectionPhase()\r
@@ -646,6 +649,9 @@ static void process_SelectionPhase()
 \r
 static void process_MessageOut()\r
 {\r
+       int wasNeedSyncNegotiationAck = scsiDev.needSyncNegotiationAck;\r
+       scsiDev.needSyncNegotiationAck = 0; // Successful on -most- messages.\r
+\r
        scsiEnterPhase(MESSAGE_OUT);\r
 \r
        scsiDev.atnFlag = 0;\r
@@ -716,11 +722,10 @@ static void process_MessageOut()
                // Message Reject\r
                // Oh well.\r
 \r
-               if (scsiDev.needSyncNegotiationAck)\r
+               if (wasNeedSyncNegotiationAck)\r
                {\r
                        scsiDev.target->syncOffset = 0;\r
                        scsiDev.target->syncPeriod = 0;\r
-                       scsiDev.needSyncNegotiationAck = 0;\r
                }\r
        }\r
        else if (scsiDev.msgOut == 0x08)\r
@@ -732,6 +737,12 @@ static void process_MessageOut()
                // Message Parity Error\r
                // Go back and re-send the last message.\r
                scsiDev.phase = MESSAGE_IN;\r
+\r
+               if (wasNeedSyncNegotiationAck)\r
+               {\r
+                       scsiDev.target->syncOffset = 0;\r
+                       scsiDev.target->syncPeriod = 0;\r
+               }\r
        }\r
        else if (scsiDev.msgOut & 0x80) // 0x80 -> 0xFF\r
        {\r
@@ -778,9 +789,16 @@ static void process_MessageOut()
                        scsiEnterPhase(MESSAGE_IN);\r
                        static const uint8_t WDTR[] = {0x01, 0x02, 0x03, 0x00};\r
                        scsiWrite(WDTR, sizeof(WDTR));\r
+\r
+                       // SDTR becomes invalidated.\r
+                       scsiDev.target->syncOffset = 0;\r
+                       scsiDev.target->syncPeriod = 0;\r
                }\r
                else if (extmsg[0] == 1 && msgLen == 3) // Synchronous data request\r
                {\r
+                       int oldPeriod = scsiDev.target->syncPeriod;\r
+                       int oldOffset = scsiDev.target->syncOffset;\r
+\r
                        int transferPeriod = extmsg[1];\r
                        int offset = extmsg[2];\r
 \r
@@ -836,10 +854,17 @@ static void process_MessageOut()
                                }\r
                        }\r
 \r
-                       scsiEnterPhase(MESSAGE_IN);\r
-                       uint8_t SDTR[] = {0x01, 0x03, 0x01, scsiDev.target->syncPeriod, scsiDev.target->syncOffset};\r
-                       scsiWrite(SDTR, sizeof(SDTR));\r
-                       scsiDev.needSyncNegotiationAck = 1; // Check if this message is rejected.\r
+                       if (transferPeriod != oldPeriod ||\r
+                               scsiDev.target->syncPeriod != oldPeriod ||\r
+                               offset != oldOffset ||\r
+                               scsiDev.target->syncOffset != oldOffset ||\r
+                               !wasNeedSyncNegotiationAck) // Don't get into infinite loops negotiating.\r
+                       {\r
+                               scsiEnterPhase(MESSAGE_IN);\r
+                               uint8_t SDTR[] = {0x01, 0x03, 0x01, scsiDev.target->syncPeriod, scsiDev.target->syncOffset};\r
+                               scsiWrite(SDTR, sizeof(SDTR));\r
+                               scsiDev.needSyncNegotiationAck = 1; // Check if this message is rejected.\r
+                       }\r
                }\r
                else\r
                {\r
@@ -864,6 +889,12 @@ static void process_MessageOut()
 \r
 void scsiPoll(void)\r
 {\r
+       if (resetUntil != 0 && resetUntil > s2s_getTime_ms())\r
+       {\r
+               return;\r
+       }\r
+       resetUntil = 0;\r
+\r
        if (unlikely(scsiDev.resetFlag))\r
        {\r
                scsiReset();\r
index d0e7002..fadddcb 100755 (executable)
@@ -66,10 +66,10 @@ static uint8_t asyncTimings[][4] =
 #define SCSI_FAST20_ASSERT 2\r
 \r
 \r
-#define syncDeskew(period) ((period) < 45 ? \\r
+#define syncDeskew(period) ((period) < 35 ? \\r
        SCSI_FAST10_DESKEW : SCSI_FAST5_DESKEW)\r
 \r
-#define syncHold(period) ((period) < 45 ? \\r
+#define syncHold(period) ((period) < 35 ? \\r
        ((period) == 25 ? SCSI_FAST10_HOLD : 4) /* 25ns/33ns */\\r
        : SCSI_FAST5_HOLD)\r
 \r
@@ -509,11 +509,16 @@ void scsiEnterPhase(int phase)
                        }\r
                        else\r
                        {\r
+                               // Amiga A3000 OS3.9 sets period to 35 and fails with\r
+                               // glitch == 1.\r
+                               int glitch =\r
+                                       scsiDev.target->syncPeriod < 35 ? 1 :\r
+                                               (scsiDev.target->syncPeriod < 45 ? 2 : 5);\r
                                scsiSetTiming(\r
                                        syncAssertion(scsiDev.target->syncPeriod),\r
                                        syncDeskew(scsiDev.target->syncPeriod),\r
                                        syncHold(scsiDev.target->syncPeriod),\r
-                                       scsiDev.target->syncPeriod < 45 ? 1 : 5);\r
+                                       glitch);\r
                        }\r
 \r
                        // See note 26 in SCSI 2 standard: SCSI 1 implementations may assume\r