Browse Source

Fix sync negotiation bug

Michael McMaster 8 years ago
parent
commit
0318a0c6b0

+ 3 - 0
lib/SCSI2SD/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.

+ 25 - 1
lib/SCSI2SD/src/firmware/disk.c

@@ -684,9 +684,10 @@ void scsiDiskPoll()
 		int i = 0;
 		// int scsiDisconnected = 0;
 		int scsiComplete = 0;
-		// uint32_t lastActivityTime = s2s_getTime_ms();
+		//uint32_t lastActivityTime = s2s_getTime_ms();
 		// int scsiActive = 0;
 		// int sdActive = 0;
+		int clearBSY = 0;
 
 		int parityError = 0;
 		while ((i < totalSDSectors) &&
@@ -704,6 +705,24 @@ void scsiDiskPoll()
 				rem < maxSectors ? rem : maxSectors;
 			scsiRead(&scsiDev.data[0], sectors * SD_SECTOR_SIZE, &parityError);
 
+			if (i + sectors >= totalSDSectors)
+			{
+				// We're transferring over the SCSI bus faster than the SD card
+				// can write.  All data is buffered, and we're just waiting for
+				// the SD card to complete. The host won't let us disconnect.
+				// Some drivers set a 250ms timeout on transfers to complete.
+				// SD card writes are supposed to complete
+				// within 200ms, but sometimes they don'to.
+				// Just pretend we're finished.
+				process_Status();
+				process_MessageIn(); // Will go to BUS_FREE state
+
+				// Try and prevent anyone else using the SCSI bus while we're not ready.
+				*SCSI_CTRL_BSY = 1;
+				clearBSY = 1;
+			}
+
+
 			if (!parityError)
 			{
 				sdTmpWrite(&scsiDev.data[0], i + sdLBA, sectors);
@@ -830,6 +849,11 @@ void scsiDiskPoll()
 #endif
 		}
 
+		if (clearBSY)
+		{
+			*SCSI_CTRL_BSY = 0;
+		}
+
 #if 0
 		if (scsiComplete)
 		{

+ 38 - 7
lib/SCSI2SD/src/firmware/scsi.c

@@ -477,6 +477,8 @@ static void doReserveRelease()
 	}
 }
 
+static uint32_t resetUntil = 0;
+
 static void scsiReset()
 {
 	scsiDev.rstCount++;
@@ -515,6 +517,7 @@ static void scsiReset()
 
 	scsiDev.postDataOutHook = NULL;
 
+
 	// Sleep to allow the bus to settle down a bit.
 	// We must be ready again within the "Reset to selection time" of
 	// 250ms.
@@ -523,7 +526,7 @@ static void scsiReset()
 	// in which case TERMPWR cannot be supplied, and reset will ALWAYS
 	// be true. Therefore, the sleep here must be slow to avoid slowing
 	// USB comms
-	s2s_delay_ms(1); // 1ms.
+	resetUntil = s2s_getTime_ms() + 2; // At least 1ms.
 }
 
 static void enter_SelectionPhase()
@@ -646,6 +649,9 @@ static void process_SelectionPhase()
 
 static void process_MessageOut()
 {
+	int wasNeedSyncNegotiationAck = scsiDev.needSyncNegotiationAck;
+	scsiDev.needSyncNegotiationAck = 0; // Successful on -most- messages.
+
 	scsiEnterPhase(MESSAGE_OUT);
 
 	scsiDev.atnFlag = 0;
@@ -716,11 +722,10 @@ static void process_MessageOut()
 		// Message Reject
 		// Oh well.
 
-		if (scsiDev.needSyncNegotiationAck)
+		if (wasNeedSyncNegotiationAck)
 		{
 			scsiDev.target->syncOffset = 0;
 			scsiDev.target->syncPeriod = 0;
-			scsiDev.needSyncNegotiationAck = 0;
 		}
 	}
 	else if (scsiDev.msgOut == 0x08)
@@ -732,6 +737,12 @@ static void process_MessageOut()
 		// Message Parity Error
 		// Go back and re-send the last message.
 		scsiDev.phase = MESSAGE_IN;
+
+		if (wasNeedSyncNegotiationAck)
+		{
+			scsiDev.target->syncOffset = 0;
+			scsiDev.target->syncPeriod = 0;
+		}
 	}
 	else if (scsiDev.msgOut & 0x80) // 0x80 -> 0xFF
 	{
@@ -778,9 +789,16 @@ static void process_MessageOut()
 			scsiEnterPhase(MESSAGE_IN);
 			static const uint8_t WDTR[] = {0x01, 0x02, 0x03, 0x00};
 			scsiWrite(WDTR, sizeof(WDTR));
+
+			// SDTR becomes invalidated.
+			scsiDev.target->syncOffset = 0;
+			scsiDev.target->syncPeriod = 0;
 		}
 		else if (extmsg[0] == 1 && msgLen == 3) // Synchronous data request
 		{
+			int oldPeriod = scsiDev.target->syncPeriod;
+			int oldOffset = scsiDev.target->syncOffset;
+
 			int transferPeriod = extmsg[1];
 			int offset = extmsg[2];
 
@@ -836,10 +854,17 @@ static void process_MessageOut()
 				}
 			}
 
-			scsiEnterPhase(MESSAGE_IN);
-			uint8_t SDTR[] = {0x01, 0x03, 0x01, scsiDev.target->syncPeriod, scsiDev.target->syncOffset};
-			scsiWrite(SDTR, sizeof(SDTR));
-			scsiDev.needSyncNegotiationAck = 1; // Check if this message is rejected.
+			if (transferPeriod != oldPeriod ||
+				scsiDev.target->syncPeriod != oldPeriod ||
+				offset != oldOffset ||
+				scsiDev.target->syncOffset != oldOffset ||
+				!wasNeedSyncNegotiationAck) // Don't get into infinite loops negotiating.
+			{
+				scsiEnterPhase(MESSAGE_IN);
+				uint8_t SDTR[] = {0x01, 0x03, 0x01, scsiDev.target->syncPeriod, scsiDev.target->syncOffset};
+				scsiWrite(SDTR, sizeof(SDTR));
+				scsiDev.needSyncNegotiationAck = 1; // Check if this message is rejected.
+			}
 		}
 		else
 		{
@@ -864,6 +889,12 @@ static void process_MessageOut()
 
 void scsiPoll(void)
 {
+	if (resetUntil != 0 && resetUntil > s2s_getTime_ms())
+	{
+		return;
+	}
+	resetUntil = 0;
+
 	if (unlikely(scsiDev.resetFlag))
 	{
 		scsiReset();

+ 8 - 3
lib/SCSI2SD/src/firmware/scsiPhy.c

@@ -66,10 +66,10 @@ static uint8_t asyncTimings[][4] =
 #define SCSI_FAST20_ASSERT 2
 
 
-#define syncDeskew(period) ((period) < 45 ? \
+#define syncDeskew(period) ((period) < 35 ? \
 	SCSI_FAST10_DESKEW : SCSI_FAST5_DESKEW)
 
-#define syncHold(period) ((period) < 45 ? \
+#define syncHold(period) ((period) < 35 ? \
 	((period) == 25 ? SCSI_FAST10_HOLD : 4) /* 25ns/33ns */\
 	: SCSI_FAST5_HOLD)
 
@@ -509,11 +509,16 @@ void scsiEnterPhase(int phase)
 			}
 			else
 			{
+				// Amiga A3000 OS3.9 sets period to 35 and fails with
+				// glitch == 1.
+				int glitch =
+					scsiDev.target->syncPeriod < 35 ? 1 :
+						(scsiDev.target->syncPeriod < 45 ? 2 : 5);
 				scsiSetTiming(
 					syncAssertion(scsiDev.target->syncPeriod),
 					syncDeskew(scsiDev.target->syncPeriod),
 					syncHold(scsiDev.target->syncPeriod),
-					scsiDev.target->syncPeriod < 45 ? 1 : 5);
+					glitch);
 			}
 
 			// See note 26 in SCSI 2 standard: SCSI 1 implementations may assume