Merge branch 'master' of ssh://webhost.codesrc.com/home/michael/projects/SCSI2SD
[SCSI2SD.git] / software / SCSI2SD / src / scsi.c
index 061818f..48b4c6d 100755 (executable)
@@ -14,6 +14,8 @@
 //\r
 //     You should have received a copy of the GNU General Public License\r
 //     along with SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.\r
+#pragma GCC push_options\r
+#pragma GCC optimize("-flto")\r
 \r
 #include "device.h"\r
 #include "scsi.h"\r
@@ -28,6 +30,7 @@
 #include "disk.h"\r
 #include "time.h"\r
 #include "cdrom.h"\r
+#include "debug.h"\r
 \r
 #include <string.h>\r
 \r
@@ -38,9 +41,7 @@ static void enter_SelectionPhase(void);
 static void process_SelectionPhase(void);\r
 static void enter_BusFree(void);\r
 static void enter_MessageIn(uint8 message);\r
-static void process_MessageIn(void);\r
 static void enter_Status(uint8 status);\r
-static void process_Status(void);\r
 static void enter_DataIn(int len);\r
 static void process_DataIn(void);\r
 static void process_DataOut(void);\r
@@ -52,7 +53,16 @@ static void enter_BusFree()
 {\r
        // This delay probably isn't needed for most SCSI hosts, but it won't\r
        // hurt either. It's possible some of the samplers needed this delay.\r
-       CyDelayUs(2);\r
+       if (scsiDev.compatMode < COMPAT_SCSI2)\r
+       {\r
+               CyDelayUs(2);\r
+       }\r
+\r
+       if (scsiDev.status != GOOD && isDebugEnabled())\r
+       {\r
+               // We want to capture debug information for failure cases.\r
+               CyDelay(64);\r
+       }\r
 \r
        SCSI_ClearPin(SCSI_Out_BSY);\r
        // We now have a Bus Clear Delay of 800ns to release remaining signals.\r
@@ -72,12 +82,12 @@ static void enter_MessageIn(uint8 message)
        scsiDev.phase = MESSAGE_IN;\r
 }\r
 \r
-static void process_MessageIn()\r
+void process_MessageIn()\r
 {\r
        scsiEnterPhase(MESSAGE_IN);\r
        scsiWriteByte(scsiDev.msgIn);\r
 \r
-       if (scsiDev.atnFlag)\r
+       if (unlikely(scsiDev.atnFlag))\r
        {\r
                // If there was a parity error, we go\r
                // back to MESSAGE_OUT first, get out parity error message, then come\r
@@ -115,9 +125,10 @@ static void enter_Status(uint8 status)
 \r
        scsiDev.lastStatus = scsiDev.status;\r
        scsiDev.lastSense = scsiDev.target->sense.code;\r
+       scsiDev.lastSenseASC = scsiDev.target->sense.asc;\r
 }\r
 \r
-static void process_Status()\r
+void process_Status()\r
 {\r
        scsiEnterPhase(STATUS);\r
 \r
@@ -145,6 +156,8 @@ static void process_Status()
 \r
        scsiDev.lastStatus = scsiDev.status;\r
        scsiDev.lastSense = scsiDev.target->sense.code;\r
+       scsiDev.lastSenseASC = scsiDev.target->sense.asc;\r
+\r
 \r
        // Command Complete occurs AFTER a valid status has been\r
        // sent. then we go bus-free.\r
@@ -201,7 +214,7 @@ static void process_DataOut()
 \r
                if (scsiDev.parityError &&\r
                        (scsiDev.target->cfg->flags & CONFIG_ENABLE_PARITY) &&\r
-                       !scsiDev.compatMode)\r
+                       (scsiDev.compatMode >= COMPAT_SCSI2))\r
                {\r
                        scsiDev.target->sense.code = ABORTED_COMMAND;\r
                        scsiDev.target->sense.asc = SCSI_PARITY_ERROR;\r
@@ -251,8 +264,9 @@ static void process_Command()
        control = scsiDev.cdb[scsiDev.cdbLen - 1];\r
 \r
        scsiDev.cmdCount++;\r
+       TargetConfig* cfg = scsiDev.target->cfg;\r
 \r
-       if (scsiDev.resetFlag)\r
+       if (unlikely(scsiDev.resetFlag))\r
        {\r
                // Don't log bogus commands\r
                scsiDev.cmdCount--;\r
@@ -260,8 +274,8 @@ static void process_Command()
                return;\r
        }\r
        else if (scsiDev.parityError &&\r
-               (scsiDev.target->cfg->flags & CONFIG_ENABLE_PARITY) &&\r
-               !scsiDev.compatMode)\r
+               (cfg->flags & CONFIG_ENABLE_PARITY) &&\r
+               (scsiDev.compatMode >= COMPAT_SCSI2))\r
        {\r
                scsiDev.target->sense.code = ABORTED_COMMAND;\r
                scsiDev.target->sense.asc = SCSI_PARITY_ERROR;\r
@@ -313,7 +327,7 @@ static void process_Command()
        // on receiving the unit attention response on boot, thus\r
        // triggering another unit attention condition.\r
        else if (scsiDev.target->unitAttention &&\r
-               (scsiDev.target->cfg->flags & CONFIG_ENABLE_UNIT_ATTENTION))\r
+               (cfg->flags & CONFIG_ENABLE_UNIT_ATTENTION))\r
        {\r
                scsiDev.target->sense.code = UNIT_ATTENTION;\r
                scsiDev.target->sense.asc = scsiDev.target->unitAttention;\r
@@ -339,6 +353,20 @@ static void process_Command()
        {\r
                enter_Status(CONFLICT);\r
        }\r
+       // Handle odd device types first that may override basic read and\r
+       // write commands. Will fall-through to generic disk handling.\r
+       else if (((cfg->deviceType == CONFIG_OPTICAL) && scsiCDRomCommand()) ||\r
+               ((cfg->deviceType == CONFIG_SEQUENTIAL) && scsiTapeCommand()) ||\r
+               ((cfg->deviceType == CONFIG_MO) && scsiMOCommand()))\r
+       {\r
+               // Already handled.\r
+       }\r
+       else if (scsiDiskCommand())\r
+       {\r
+               // Already handled.\r
+               // check for the performance-critical read/write\r
+               // commands ASAP.\r
+       }\r
        else if (command == 0x1C)\r
        {\r
                scsiReceiveDiagnostic();\r
@@ -347,14 +375,15 @@ static void process_Command()
        {\r
                scsiSendDiagnostic();\r
        }\r
+       else if (command == 0x3B)\r
+       {\r
+               scsiWriteBuffer();\r
+       }\r
        else if (command == 0x3C)\r
        {\r
                scsiReadBuffer();\r
        }\r
-       else if (\r
-               !scsiModeCommand() &&\r
-               !scsiDiskCommand() &&\r
-               !scsiCDRomCommand())\r
+       else if (!scsiModeCommand())\r
        {\r
                scsiDev.target->sense.code = ILLEGAL_REQUEST;\r
                scsiDev.target->sense.asc = INVALID_COMMAND_OPERATION_CODE;\r
@@ -437,6 +466,7 @@ static void scsiReset()
        scsiDev.atnFlag = 0;\r
        scsiDev.resetFlag = 0;\r
        scsiDev.lun = -1;\r
+       scsiDev.compatMode = COMPAT_UNKNOWN;\r
 \r
        if (scsiDev.target)\r
        {\r
@@ -460,8 +490,9 @@ static void scsiReset()
        // There is no guarantee that the RST line will be negated by then.\r
        // NOTE: We could be connected and powered by USB for configuration,\r
        // in which case TERMPWR cannot be supplied, and reset will ALWAYS\r
-       // be true.\r
-       CyDelay(10); // 10ms.\r
+       // be true. Therefore, the sleep here must be slow to avoid slowing\r
+       // USB comms\r
+       CyDelay(1); // 1ms.\r
 }\r
 \r
 static void enter_SelectionPhase()\r
@@ -477,7 +508,6 @@ static void enter_SelectionPhase()
        scsiDev.phase = SELECTION;\r
        scsiDev.lun = -1;\r
        scsiDev.discPriv = 0;\r
-       scsiDev.compatMode = 0;\r
 \r
        scsiDev.initiatorId = -1;\r
        scsiDev.target = NULL;\r
@@ -490,6 +520,12 @@ static void enter_SelectionPhase()
 \r
 static void process_SelectionPhase()\r
 {\r
+       if (scsiDev.compatMode < COMPAT_SCSI2)\r
+       {\r
+               // Required for some older SCSI1 devices using a 5380 chip.\r
+               CyDelay(1);\r
+       }\r
+\r
        int sel = SCSI_ReadFilt(SCSI_Filt_SEL);\r
        int bsy = SCSI_ReadFilt(SCSI_Filt_BSY);\r
 \r
@@ -513,7 +549,7 @@ static void process_SelectionPhase()
        if (!bsy && sel &&\r
                target &&\r
                (goodParity || !(target->cfg->flags & CONFIG_ENABLE_PARITY) || !atnFlag) &&\r
-               (maskBitCount <= 2))\r
+               likely(maskBitCount <= 2))\r
        {\r
                scsiDev.target = target;\r
 \r
@@ -529,7 +565,15 @@ static void process_SelectionPhase()
                if (!scsiDev.atnFlag)\r
                {\r
                        target->unitAttention = 0;\r
-                       scsiDev.compatMode = 1;\r
+                       scsiDev.compatMode = COMPAT_SCSI1;\r
+               }\r
+               else if (!(target->cfg->flags & CONFIG_ENABLE_SCSI2))\r
+               {\r
+                       scsiDev.compatMode = COMPAT_SCSI1;\r
+               }\r
+               else if (scsiDev.compatMode == COMPAT_UNKNOWN)\r
+               {\r
+                       scsiDev.compatMode = COMPAT_SCSI2;\r
                }\r
 \r
                // We've been selected!\r
@@ -544,7 +588,7 @@ static void process_SelectionPhase()
                scsiDev.selCount++;\r
 \r
                // Wait until the end of the selection phase.\r
-               while (!scsiDev.resetFlag)\r
+               while (likely(!scsiDev.resetFlag))\r
                {\r
                        if (!SCSI_ReadFilt(SCSI_Filt_SEL))\r
                        {\r
@@ -588,7 +632,7 @@ static void process_MessageOut()
 \r
        if (scsiDev.parityError &&\r
                (scsiDev.target->cfg->flags & CONFIG_ENABLE_PARITY) &&\r
-               !scsiDev.compatMode)\r
+               (scsiDev.compatMode >= COMPAT_SCSI2))\r
        {\r
                // Skip the remaining message bytes, and then start the MESSAGE_OUT\r
                // phase again from the start. The initiator will re-send the\r
@@ -668,7 +712,7 @@ static void process_MessageOut()
                }\r
 \r
                scsiDev.lun = scsiDev.msgOut & 0x7;\r
-               scsiDev.discPriv =\r
+               scsiDev.discPriv = \r
                        ((scsiDev.msgOut & 0x40) && (scsiDev.initiatorId >= 0))\r
                                ? 1 : 0;\r
        }\r
@@ -684,20 +728,32 @@ static void process_MessageOut()
                // Extended message.\r
                int msgLen = scsiReadByte();\r
                if (msgLen == 0) msgLen = 256;\r
+               uint8_t extmsg[256];\r
                for (i = 0; i < msgLen && !scsiDev.resetFlag; ++i)\r
                {\r
                        // Discard bytes.\r
-                       scsiReadByte();\r
+                       extmsg[i] = scsiReadByte();\r
                }\r
 \r
-               // We don't support ANY extended messages.\r
-               // Modify Data Pointer:  We don't support reselection.\r
-               // Wide Data Transfer Request: No. 8bit only.\r
-               // Synchronous data transfer request. No, we can't do that.\r
-               // We don't support any 2-byte messages either.\r
-               // And we don't support any optional 1-byte messages.\r
-               // In each case, the correct response is MESSAGE REJECT.\r
-               messageReject();\r
+               if (extmsg[0] == 3 && msgLen == 2) // Wide Data Request\r
+               {\r
+                       // Negotiate down to 8bit\r
+                       scsiEnterPhase(MESSAGE_IN);\r
+                       static const uint8_t WDTR[] = {0x01, 0x02, 0x03, 0x00};\r
+                       scsiWrite(WDTR, sizeof(WDTR));\r
+               }\r
+               else if (extmsg[0] == 1 && msgLen == 3) // Synchronous data request\r
+               {\r
+                       // Negotiate back to async\r
+                       scsiEnterPhase(MESSAGE_IN);\r
+                       static const uint8_t SDTR[] = {0x01, 0x03, 0x01, 0x00, 0x00};\r
+                       scsiWrite(SDTR, sizeof(SDTR));\r
+               }\r
+               else\r
+               {\r
+                       // Not supported\r
+                       messageReject();\r
+               }\r
        }\r
        else\r
        {\r
@@ -710,7 +766,7 @@ static void process_MessageOut()
 \r
 void scsiPoll(void)\r
 {\r
-       if (scsiDev.resetFlag)\r
+       if (unlikely(scsiDev.resetFlag))\r
        {\r
                scsiReset();\r
                if ((scsiDev.resetFlag = SCSI_ReadFilt(SCSI_Filt_RST)))\r
@@ -837,6 +893,7 @@ void scsiInit()
        scsiDev.resetFlag = 1;\r
        scsiDev.phase = BUS_FREE;\r
        scsiDev.target = NULL;\r
+       scsiDev.compatMode = COMPAT_UNKNOWN;\r
 \r
        int i;\r
        for (i = 0; i < MAX_SCSI_TARGETS; ++i)\r
@@ -846,6 +903,8 @@ void scsiInit()
                {\r
                        scsiDev.targets[i].targetId = cfg->scsiId & CONFIG_TARGET_ID_BITS;\r
                        scsiDev.targets[i].cfg = cfg;\r
+\r
+                       scsiDev.targets[i].liveCfg.bytesPerSector = cfg->bytesPerSector;\r
                }\r
                else\r
                {\r
@@ -928,7 +987,7 @@ int scsiReconnect()
                        while (\r
                                !bsy &&\r
                                !scsiDev.resetFlag &&\r
-                               (diffTime_ms(waitStart_ms, getTime_ms()) < 250))\r
+                               (elapsedTime_ms(waitStart_ms) < 250))\r
                        {\r
                                bsy = SCSI_ReadFilt(SCSI_Filt_BSY);\r
                        }\r
@@ -963,3 +1022,4 @@ int scsiReconnect()
        return reconnected;\r
 }\r
 \r
+#pragma GCC pop_options\r