Explorar el Código

Merge branch 'main' into f4-usb

Morio hace 2 años
padre
commit
55ab10d115

+ 4 - 4
.github/workflows/analyze_crashlogs.yml

@@ -12,11 +12,11 @@ jobs:
     runs-on: ubuntu-20.04
     if: |
       (github.event_name == 'issue_comment' && (
-        contains(github.event.comment.body, 'CRASH') ||
-        contains(github.event.comment.body, 'WATCHDOG'))) ||
+        contains(github.event.comment.body, 'CRASH!') ||
+        contains(github.event.comment.body, 'WATCHDOG TIMEOUT'))) ||
       (github.event_name == 'issues' && (
-          contains(github.event.issue.body, 'CRASH') ||
-          contains(github.event.issue.body, 'WATCHDOG')))
+          contains(github.event.issue.body, 'CRASH!') ||
+          contains(github.event.issue.body, 'WATCHDOG TIMEOUT')))
     permissions:
       issues: write
 

+ 5 - 0
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.cpp

@@ -493,6 +493,11 @@ void platform_poll()
     adc_poll();
 }
 
+uint8_t platform_get_buttons()
+{
+    return 0;
+}
+
 /***********************/
 /* Flash reprogramming */
 /***********************/

+ 7 - 0
lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.h

@@ -101,6 +101,13 @@ void platform_reset_watchdog();
 // few milliseconds shouldn't disturb SCSI communication.
 void platform_poll();
 
+// Returns the state of any platform-specific buttons.
+// The returned value should be a mask for buttons 1-8 in bits 0-7 respectively,
+// where '1' is a button pressed and '0' is a button released.
+// Debouncing logic is left up to the specific implementation.
+// This function should return without significantly delay.
+uint8_t platform_get_buttons();
+
 // Reinitialize SD card connection and save log from interrupt context.
 // This can be used in crash handlers.
 void platform_emergency_log_save();

+ 13 - 0
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.cpp

@@ -196,6 +196,7 @@ void platform_init()
 #else
     //        pin             function       pup   pdown  out    state fast
     gpio_conf(GPIO_EXP_AUDIO, GPIO_FUNC_SPI, true,false, false,  true, true);
+    gpio_conf(GPIO_EXP_SPARE, GPIO_FUNC_SIO, true,false, false,  true, false);
     // configuration of corresponding SPI unit occurs in audio_setup()
 #endif
 }
@@ -618,6 +619,18 @@ void platform_poll()
 #endif
 }
 
+uint8_t platform_get_buttons()
+{
+#ifdef ENABLE_AUDIO_OUTPUT
+    uint8_t pins = 0x00;
+    // pulled to VCC via resistor, sinking when pressed
+    if (!gpio_get(GPIO_EXP_SPARE)) pins |= 0x01;
+    return pins;
+#else
+    return 0;
+#endif
+}
+
 /*****************************************/
 /* Flash reprogramming from bootloader   */
 /*****************************************/

+ 7 - 0
lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.h

@@ -110,6 +110,13 @@ void platform_reset_watchdog();
 // few milliseconds shouldn't disturb SCSI communication.
 void platform_poll();
 
+// Returns the state of any platform-specific buttons.
+// The returned value should be a mask for buttons 1-8 in bits 0-7 respectively,
+// where '1' is a button pressed and '0' is a button released.
+// Debouncing logic is left up to the specific implementation.
+// This function should return without significantly delay.
+uint8_t platform_get_buttons();
+
 // Set callback that will be called during data transfer to/from SD card.
 // This can be used to implement simultaneous transfer to SCSI bus.
 typedef void (*sd_callback_t)(uint32_t bytes_complete);

+ 7 - 0
lib/ZuluSCSI_platform_template/ZuluSCSI_platform.cpp

@@ -80,6 +80,13 @@ void platform_poll()
 
 }
 
+// Called periodically to get the state of any buttons installed on the platform.
+// If none are installed the below function is fine.
+uint8_t platform_get_buttons()
+{
+    return 0;
+}
+
 /**********************************************/
 /* Mapping from data bytes to GPIO BOP values */
 /**********************************************/

+ 7 - 0
lib/ZuluSCSI_platform_template/ZuluSCSI_platform.h

@@ -78,6 +78,13 @@ void platform_reset_watchdog();
 // few milliseconds shouldn't disturb SCSI communication.
 void platform_poll();
 
+// Returns the state of any platform-specific buttons.
+// The returned value should be a mask for buttons 1-8 in bits 0-7 respectively,
+// where '1' is a button pressed and '0' is a button released.
+// Debouncing logic is left up to the specific implementation.
+// This function should return without significantly delay.
+uint8_t platform_get_buttons();
+
 // Set callback that will be called during data transfer to/from SD card.
 // This can be used to implement simultaneous transfer to SCSI bus.
 typedef void (*sd_callback_t)(uint32_t bytes_complete);

+ 1 - 0
src/ZuluSCSI.cpp

@@ -706,6 +706,7 @@ extern "C" void zuluscsi_main_loop(void)
 
   platform_reset_watchdog();
   platform_poll();
+  diskEjectButtonUpdate(true);
   
 #ifdef PLATFORM_HAS_INITIATOR_MODE
   if (platform_is_initiator_mode_enabled())

+ 75 - 71
src/ZuluSCSI_cdrom.cpp

@@ -219,15 +219,34 @@ static const uint8_t DiscInformation[] =
     0x00,   // 33: number of opc tables
 };
 
-// Convert logical block address to CD-ROM time in formatted TOC format
+// Convert logical block address to CD-ROM time
 static void LBA2MSF(uint32_t LBA, uint8_t* MSF)
 {
-    MSF[0] = 0; // reserved.
-    MSF[3] = LBA % 75; // Frames
+    MSF[2] = LBA % 75; // Frames
     uint32_t rem = LBA / 75;
 
-    MSF[2] = rem % 60; // Seconds
-    MSF[1] = rem / 60; // Minutes
+    MSF[1] = rem % 60; // Seconds
+    MSF[0] = rem / 60; // Minutes
+}
+
+// Convert logical block address to CD-ROM time in binary coded decimal format
+static void LBA2MSFBCD(uint32_t LBA, uint8_t* MSF)
+{
+    uint8_t fra = LBA % 75;
+    uint32_t rem = LBA / 75;
+    uint8_t sec = rem % 60;
+    uint8_t min = rem / 60;
+
+    MSF[0] = ((min / 10) << 4) | (min % 10);
+    MSF[1] = ((sec / 10) << 4) | (sec % 10);
+    MSF[2] = ((fra / 10) << 4) | (fra % 10);
+}
+
+// Convert CD-ROM time to logical block address
+static uint32_t MSF2LBA(uint8_t m, uint8_t s, uint8_t f)
+{
+    uint32_t lba = (m * 60 + s) * 75 + f;
+    return lba;
 }
 
 static void doReadTOCSimple(bool MSF, uint8_t track, uint16_t allocationLength)
@@ -246,7 +265,8 @@ static void doReadTOCSimple(bool MSF, uint8_t track, uint16_t allocationLength)
         // Replace start of leadout track
         if (MSF)
         {
-            LBA2MSF(capacity, scsiDev.data + 8);
+            scsiDev.data[8] = 0;
+            LBA2MSF(capacity, scsiDev.data + 9);
         }
         else
         {
@@ -278,7 +298,8 @@ static void doReadTOCSimple(bool MSF, uint8_t track, uint16_t allocationLength)
         // Replace start of leadout track
         if (MSF)
         {
-            LBA2MSF(capacity, scsiDev.data + 0x10);
+            scsiDev.data[0x10] = 0;
+            LBA2MSF(capacity, scsiDev.data + 0x11);
         }
         else
         {
@@ -433,7 +454,8 @@ static void formatTrackInfo(const CUETrackInfo *track, uint8_t *dest, bool use_M
     if (use_MSF_time)
     {
         // Time in minute-second-frame format
-        LBA2MSF(track->data_start, &dest[4]);
+        dest[4] = 0;
+        LBA2MSF(track->data_start, &dest[5]);
     }
     else
     {
@@ -492,7 +514,7 @@ static void doReadTOC(bool MSF, uint8_t track, uint16_t allocationLength)
         if (firsttrack < 0) firsttrack = trackinfo->track_number;
         lasttrack = trackinfo->track_number;
 
-        if (track == 0 || track == trackinfo->track_number)
+        if (track <= trackinfo->track_number)
         {
             formatTrackInfo(trackinfo, &trackdata[8 * trackcount], MSF);
             trackcount += 1;
@@ -500,15 +522,12 @@ static void doReadTOC(bool MSF, uint8_t track, uint16_t allocationLength)
     }
 
     // Format lead-out track info
-    if (track == 0 || track == 0xAA)
-    {
-        CUETrackInfo leadout = {};
-        leadout.track_number = 0xAA;
-        leadout.track_mode = CUETrack_MODE1_2048;
-        leadout.data_start = img.scsiSectors;
-        formatTrackInfo(&leadout, &trackdata[8 * trackcount], MSF);
-        trackcount += 1;
-    }
+    CUETrackInfo leadout = {};
+    leadout.track_number = 0xAA;
+    leadout.track_mode = CUETrack_MODE1_2048;
+    leadout.data_start = img.scsiSectors;
+    formatTrackInfo(&leadout, &trackdata[8 * trackcount], MSF);
+    trackcount += 1;
 
     // Format response header
     uint16_t toc_length = 2 + trackcount * 8;
@@ -517,7 +536,7 @@ static void doReadTOC(bool MSF, uint8_t track, uint16_t allocationLength)
     scsiDev.data[2] = firsttrack;
     scsiDev.data[3] = lasttrack;
 
-    if (trackcount == 0)
+    if (track != 0xAA && trackcount < 2)
     {
         // Unknown track requested
         scsiDev.status = CHECK_CONDITION;
@@ -568,29 +587,6 @@ static void doReadSessionInfo(uint8_t session, uint16_t allocationLength)
     scsiDev.phase = DATA_IN;
 }
 
-// Convert logical block address to CD-ROM time in the raw TOC format
-static void LBA2MSFRaw(uint32_t LBA, uint8_t* MSF)
-{
-    MSF[2] = LBA % 75; // Frames
-    uint32_t rem = LBA / 75;
-
-    MSF[1] = rem % 60; // Seconds
-    MSF[0] = rem / 60; // Minutes
-}
-
-// Convert logical block address to CD-ROM time in binary coded decimal format
-static void LBA2MSFBCD(uint32_t LBA, uint8_t* MSF)
-{
-    uint8_t fra = LBA % 75;
-    uint32_t rem = LBA / 75;
-    uint8_t sec = rem % 60;
-    uint8_t min = rem / 60;
-
-    MSF[0] = ((min / 10) << 4) | (min % 10);
-    MSF[1] = ((sec / 10) << 4) | (sec % 10);
-    MSF[2] = ((fra / 10) << 4) | (fra % 10);
-}
-
 // Format track info read from cue sheet into the format used by ReadFullTOC command.
 // Refer to T10/1545-D MMC-4 Revision 5a, "Response Format 0010b: Raw TOC"
 static void formatRawTrackInfo(const CUETrackInfo *track, uint8_t *dest)
@@ -606,16 +602,13 @@ static void formatRawTrackInfo(const CUETrackInfo *track, uint8_t *dest)
     dest[1] = control_adr;
     dest[2] = 0x00; // "TNO", always 0?
     dest[3] = track->track_number; // "POINT", contains track number
-
-    if (track->pregap_start > 0)
-    {
-        LBA2MSFRaw(track->pregap_start, &dest[4]);
-    }
-    else
-    {
-        LBA2MSFRaw(track->data_start, &dest[4]);
-    }
-
+    // Next three are ATIME. The spec doesn't directly address how these
+    // should be reported in the TOC, just giving a description of Q-channel
+    // data from Red Book/ECMA-130. On all disks tested so far these are
+    // given as 00/00/00.
+    dest[4] = 0x00;
+    dest[5] = 0x00;
+    dest[6] = 0x00;
     dest[7] = 0; // HOUR
 
     LBA2MSFBCD(track->data_start, &dest[8]);
@@ -719,7 +712,8 @@ void doReadHeader(bool MSF, uint32_t lba, uint16_t allocationLength)
     // Track start
     if (MSF)
     {
-        LBA2MSF(trackinfo.data_start, &scsiDev.data[4]);
+        scsiDev.data[4] = 0;
+        LBA2MSF(trackinfo.data_start, &scsiDev.data[5]);
     }
     else
     {
@@ -818,6 +812,18 @@ bool cdromValidateCueSheet(image_config_t &img)
 /* Ejection and image switching logic */
 /**************************************/
 
+void cdromPerformEject(image_config_t &img)
+{
+    uint8_t target = img.scsiId & 7;
+#if ENABLE_AUDIO_OUTPUT
+    // terminate audio playback if active on this target (MMC-1 Annex C)
+    audio_stop(target);
+#endif
+    dbgmsg("------ CDROM open tray on ID ", (int)target);
+    img.ejected = true;
+    img.cdrom_events = 3; // Media removal
+}
+
 // Reinsert any ejected CDROMs on reboot
 void cdromReinsertFirstImage(image_config_t &img)
 {
@@ -1238,6 +1244,7 @@ static void doReadCD(uint32_t lba, uint32_t length, uint8_t sector_type,
     for (uint32_t idx = 0; idx < length; idx++)
     {
         platform_poll();
+        diskEjectButtonUpdate(false);
 
         img.file.seek(offset + idx * trackinfo.sector_length + skip_begin);
 
@@ -1253,6 +1260,7 @@ static void doReadCD(uint32_t lba, uint32_t length, uint8_t sector_type,
                 scsiDev.resetFlag = 1;
             }
             platform_poll();
+            diskEjectButtonUpdate(false);
         }
         if (scsiDev.resetFlag) break;
 
@@ -1293,9 +1301,9 @@ static void doReadCD(uint32_t lba, uint32_t length, uint8_t sector_type,
             *buf++ = (trackinfo.track_mode == CUETrack_AUDIO ? 0x10 : 0x14); // Control & ADR
             *buf++ = trackinfo.track_number;
             *buf++ = (lba + idx >= trackinfo.data_start) ? 1 : 0; // Index number (0 = pregap)
-            LBA2MSFRaw(lba + idx, buf); buf += 3;
+            LBA2MSF(lba + idx, buf); buf += 3;
             *buf++ = 0;
-            LBA2MSFRaw(lba + idx, buf); buf += 3;
+            LBA2MSF(lba + idx, buf); buf += 3;
             *buf++ = 0; *buf++ = 0; // CRC (optional)
             *buf++ = 0; *buf++ = 0; *buf++ = 0; // (pad)
             *buf++ = 0; // No P subchannel
@@ -1345,9 +1353,10 @@ static void doReadSubchannel(bool time, bool subq, uint8_t parameter, uint8_t tr
             *buf++ = (lba >= trackinfo.data_start) ? 1 : 0; // Index number (0 = pregap)
             if (time)
             {
+                *buf++ = 0;
                 LBA2MSF(lba, buf);
-                dbgmsg("------ ABS M ", *(buf+1), " S ", *(buf+2), " F ", *(buf+3));
-                *buf += 4;
+                dbgmsg("------ ABS M ", *buf, " S ", *(buf+1), " F ", *(buf+2));
+                buf += 3;
             }
             else
             {
@@ -1360,9 +1369,10 @@ static void doReadSubchannel(bool time, bool subq, uint8_t parameter, uint8_t tr
             uint32_t relpos = (uint32_t)((int32_t)lba - (int32_t)trackinfo.data_start);
             if (time)
             {
+                *buf++ = 0;
                 LBA2MSF(relpos, buf);
-                dbgmsg("------ REL M ", *(buf+1), " S ", *(buf+2), " F ", *(buf+3));
-                *buf += 4;
+                dbgmsg("------ REL M ", *buf, " S ", *(buf+1), " F ", *(buf+2));
+                buf += 3;
             }
             else
             {
@@ -1409,25 +1419,19 @@ extern "C" int scsiCDRomCommand()
     uint8_t command = scsiDev.cdb[0];
     if (command == 0x1B)
     {
-#if ENABLE_AUDIO_OUTPUT
-        // terminate audio playback if active on this target (Annex C)
-        audio_stop(img.scsiId & 7);
-#endif
         if ((scsiDev.cdb[4] & 2))
         {
             // CD-ROM load & eject
             int start = scsiDev.cdb[4] & 1;
             if (start)
             {
-                dbgmsg("------ CDROM close tray");
+                dbgmsg("------ CDROM close tray on ID ", (int)(img.scsiId & 7));
                 img.ejected = false;
                 img.cdrom_events = 2; // New media
             }
             else
             {
-                dbgmsg("------ CDROM open tray");
-                img.ejected = true;
-                img.cdrom_events = 3; // Media removal
+                cdromPerformEject(img);
             }
         }
         else
@@ -1520,8 +1524,8 @@ extern "C" int scsiCDRomCommand()
     else if (command == 0x47)
     {
         // PLAY AUDIO (MSF)
-        uint32_t start = (scsiDev.cdb[3] * 60 + scsiDev.cdb[4]) * 75 + scsiDev.cdb[5];
-        uint32_t end   = (scsiDev.cdb[6] * 60 + scsiDev.cdb[7]) * 75 + scsiDev.cdb[8];
+        uint32_t start = MSF2LBA(scsiDev.cdb[3], scsiDev.cdb[4], scsiDev.cdb[5]);
+        uint32_t end   = MSF2LBA(scsiDev.cdb[6], scsiDev.cdb[7], scsiDev.cdb[8]);
 
         uint32_t lba = start;
         if (scsiDev.cdb[3] == 0xFF
@@ -1575,8 +1579,8 @@ extern "C" int scsiCDRomCommand()
     {
         // ReadCD MSF
         uint8_t sector_type = (scsiDev.cdb[1] >> 2) & 7;
-        uint32_t start = (scsiDev.cdb[3] * 60 + scsiDev.cdb[4]) * 75 + scsiDev.cdb[5];
-        uint32_t end   = (scsiDev.cdb[6] * 60 + scsiDev.cdb[7]) * 75 + scsiDev.cdb[8];
+        uint32_t start = MSF2LBA(scsiDev.cdb[3], scsiDev.cdb[4], scsiDev.cdb[5]);
+        uint32_t end   = MSF2LBA(scsiDev.cdb[6], scsiDev.cdb[7], scsiDev.cdb[8]);
         uint8_t main_channel = scsiDev.cdb[9];
         uint8_t sub_channel = scsiDev.cdb[10];
 

+ 3 - 0
src/ZuluSCSI_cdrom.h

@@ -11,6 +11,9 @@
 // Called by scsi.c from SCSI2SD
 extern "C" int scsiCDRomCommand(void);
 
+// Eject the given CD-ROM
+void cdromPerformEject(image_config_t &img);
+
 // Reinsert ejected CD-ROM and restart from first image
 void cdromReinsertFirstImage(image_config_t &img);
 

+ 1 - 1
src/ZuluSCSI_config.h

@@ -27,7 +27,7 @@
 #include <ZuluSCSI_platform.h>
 
 // Use variables for version number
-#define FW_VER_NUM      "23.05.11"
+#define FW_VER_NUM      "23.05.17"
 #define FW_VER_SUFFIX   "devel"
 #define ZULU_FW_VERSION FW_VER_NUM "-" FW_VER_SUFFIX
 

+ 57 - 1
src/ZuluSCSI_disk.cpp

@@ -477,7 +477,8 @@ static void scsiDiskLoadConfig(int target_idx, const char *section)
     img.rightAlignStrings = ini_getbool(section, "RightAlignStrings", 0, CONFIGFILE);
     img.prefetchbytes = ini_getl(section, "PrefetchBytes", img.prefetchbytes, CONFIGFILE);
     img.reinsert_on_inquiry = ini_getbool(section, "ReinsertCDOnInquiry", 1, CONFIGFILE);
-    
+    img.ejectButton = ini_getl(section, "EjectButton", 0, CONFIGFILE);
+
     char tmp[32];
     memset(tmp, 0, sizeof(tmp));
     ini_gets(section, "Vendor", "", tmp, sizeof(tmp), CONFIGFILE);
@@ -555,6 +556,55 @@ image_config_t &scsiDiskGetImageConfig(int target_idx)
     return g_DiskImages[target_idx];
 }
 
+static void diskEjectAction(uint8_t buttonId)
+{
+    logmsg("Eject button pressed for channel ", buttonId);
+    for (uint8_t i = 0; i < S2S_MAX_TARGETS; i++)
+    {
+        image_config_t img = g_DiskImages[i];
+        if (img.ejectButton == buttonId)
+        {
+            if (img.deviceType == S2S_CFG_OPTICAL)
+            {
+                cdromPerformEject(img);
+            }
+        }
+    }
+}
+
+uint8_t diskEjectButtonUpdate(bool immediate)
+{
+    // treat '1' to '0' transitions as eject actions
+    static uint8_t previous = 0x00;
+    uint8_t bitmask = platform_get_buttons();
+    uint8_t ejectors = (previous ^ bitmask) & previous;
+    previous = bitmask;
+
+    // defer ejection until the bus is idle
+    static uint8_t deferred = 0x00;
+    if (!immediate)
+    {
+        deferred |= ejectors;
+        return 0;
+    }
+    else
+    {
+        ejectors |= deferred;
+        deferred = 0;
+
+        if (ejectors)
+        {
+            uint8_t mask = 1;
+            for (uint8_t i = 0; i < 8; i++)
+            {
+                if (ejectors & mask) diskEjectAction(i + 1);
+                mask = mask << 1;
+            }
+        }
+        return ejectors;
+    }
+}
+
 /*******************************/
 /* Config handling for SCSI2SD */
 /*******************************/
@@ -1059,6 +1109,7 @@ void diskDataOut()
            && !scsiDev.resetFlag)
     {
         platform_poll();
+        diskEjectButtonUpdate(false);
 
         // Figure out how many contiguous bytes are available for writing to SD card.
         uint32_t bufsize = sizeof(scsiDev.data);
@@ -1226,6 +1277,7 @@ void scsiDiskStartRead(uint32_t lba, uint32_t blocks)
             while (!scsiIsWriteFinished(NULL))
             {
                 platform_poll();
+                diskEjectButtonUpdate(false);
             }
 
             scsiFinishWrite();
@@ -1298,6 +1350,7 @@ static void start_dataInTransfer(uint8_t *buffer, uint32_t count)
         }
 
         platform_poll();
+        diskEjectButtonUpdate(false);
     }
     if (scsiDev.resetFlag) return;
 
@@ -1318,6 +1371,7 @@ static void start_dataInTransfer(uint8_t *buffer, uint32_t count)
     platform_set_sd_callback(NULL, NULL);
 
     platform_poll();
+    diskEjectButtonUpdate(false);
 }
 
 static void diskDataIn()
@@ -1372,6 +1426,7 @@ static void diskDataIn()
         while (!scsiIsWriteFinished(NULL) && prefetch_sectors > 0 && !scsiDev.resetFlag)
         {
             platform_poll();
+            diskEjectButtonUpdate(false);
 
             // Check if prefetch buffer is free
             g_disk_transfer.buffer = g_scsi_prefetch.buffer + g_scsi_prefetch.bytes;
@@ -1402,6 +1457,7 @@ static void diskDataIn()
         while (!scsiIsWriteFinished(NULL))
         {
             platform_poll();
+            diskEjectButtonUpdate(false);
         }
 
         scsiFinishWrite();

+ 9 - 0
src/ZuluSCSI_disk.h

@@ -49,6 +49,10 @@ struct image_config_t: public S2S_TargetCfg
     uint8_t cdrom_events;
     bool reinsert_on_inquiry;
 
+    // selects a physical button channel that will cause an eject action
+    // default option of '0' disables this functionality
+    uint8_t ejectButton;
+
     // For tape drive emulation, current position in blocks
     uint32_t tape_pos;
 
@@ -70,6 +74,11 @@ struct image_config_t: public S2S_TargetCfg
     bool geometrywarningprinted;
 };
 
+// Should be polled intermittently to update the platform eject buttons.
+// Call with 'true' only if ejections should be performed immediately (typically when not busy)
+// Returns a mask of the buttons that registered an 'eject' action.
+uint8_t diskEjectButtonUpdate(bool immediate);
+
 // Reset all image configuration to empty reset state, close all images.
 void scsiDiskResetImages();
 

+ 3 - 2
src/ZuluSCSI_log_trace.cpp

@@ -42,7 +42,7 @@ static const char *getCommandName(uint8_t cmd)
     switch (cmd)
     {
         case 0x00: return "TestUnitReady";
-        case 0x01: return "RezeroUnit";
+        case 0x01: return "RezeroUnit/Rewind";
         case 0x03: return "RequestSense";
         case 0x04: return "FormatUnit";
         case 0x05: return "ReadBlockLimits";
@@ -57,6 +57,7 @@ static const char *getCommandName(uint8_t cmd)
         case 0x15: return "ModeSelect6";
         case 0x16: return "Reserve";
         case 0x17: return "Release";
+        case 0x19: return "Erase";
         case 0x1A: return "ModeSense";
         case 0x1B: return "StartStopUnit";
         case 0x1C: return "ReceiveDiagnostic";
@@ -69,7 +70,7 @@ static const char *getCommandName(uint8_t cmd)
         case 0x2C: return "Erase10";
         case 0x2E: return "WriteVerify";
         case 0x2F: return "Verify";
-        case 0x34: return "PreFetch";
+        case 0x34: return "PreFetch/ReadPosition";
         case 0x35: return "SynchronizeCache";
         case 0x36: return "LockUnlockCache";
         case 0x37: return "ReadDefectData";

+ 72 - 0
src/ZuluSCSI_tape.cpp

@@ -31,6 +31,31 @@ extern "C" {
 #include <scsi.h>
 }
 
+static void doSeek(uint32_t lba)
+{
+    image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
+    uint32_t bytesPerSector = scsiDev.target->liveCfg.bytesPerSector;
+    uint32_t capacity = img.file.size() / bytesPerSector;
+
+    dbgmsg("------ Locate tape to LBA ", (int)lba);
+
+    if (lba >= capacity)
+    {
+        scsiDev.status = CHECK_CONDITION;
+        scsiDev.target->sense.code = ILLEGAL_REQUEST;
+        scsiDev.target->sense.asc = LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+        scsiDev.phase = STATUS;
+    }
+    else
+    {
+        delay(10);
+        img.tape_pos = lba;
+
+        scsiDev.status = GOOD;
+        scsiDev.phase = STATUS;
+    }
+}
+
 extern "C" int scsiTapeCommand()
 {
     image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
@@ -158,6 +183,12 @@ extern "C" int scsiTapeCommand()
             img.tape_pos += length;
         }
     }
+    else if (command == 0x19)
+    {
+        // Erase
+        // Just a stub implementation, fake erase to end of tape
+        img.tape_pos = img.scsiSectors;
+    }
     else if (command == 0x01)
     {
         // REWIND
@@ -230,6 +261,47 @@ extern "C" int scsiTapeCommand()
             scsiDev.phase = STATUS;
         }
     }
+    else if (command == 0x2B)
+    {
+        // Seek/Locate 10
+        uint32_t lba =
+            (((uint32_t) scsiDev.cdb[3]) << 24) +
+            (((uint32_t) scsiDev.cdb[4]) << 16) +
+            (((uint32_t) scsiDev.cdb[5]) << 8) +
+            scsiDev.cdb[6];
+
+        doSeek(lba);
+    }
+    else if (command == 0x34)
+    {
+        // ReadPosition
+        uint32_t lba = img.tape_pos;
+        scsiDev.data[0] = 0x00;
+        if (lba == 0) scsiDev.data[0] |= 0x80;
+        if (lba >= img.scsiSectors) scsiDev.data[0] |= 0x40;
+        scsiDev.data[1] = 0x00;
+        scsiDev.data[2] = 0x00;
+        scsiDev.data[3] = 0x00;
+        scsiDev.data[4] = (lba >> 24) & 0xFF; // Next block on tape
+        scsiDev.data[5] = (lba >> 16) & 0xFF;
+        scsiDev.data[6] = (lba >>  8) & 0xFF;
+        scsiDev.data[7] = (lba >>  0) & 0xFF;
+        scsiDev.data[8] = (lba >> 24) & 0xFF; // Last block in buffer
+        scsiDev.data[9] = (lba >> 16) & 0xFF;
+        scsiDev.data[10] = (lba >>  8) & 0xFF;
+        scsiDev.data[11] = (lba >>  0) & 0xFF;
+        scsiDev.data[12] = 0x00;
+        scsiDev.data[13] = 0x00;
+        scsiDev.data[14] = 0x00;
+        scsiDev.data[15] = 0x00;
+        scsiDev.data[16] = 0x00;
+        scsiDev.data[17] = 0x00;
+        scsiDev.data[18] = 0x00;
+        scsiDev.data[19] = 0x00;
+
+        scsiDev.phase = DATA_IN;
+        scsiDev.dataLen = 20;
+    }
     else
     {
         commandHandled = 0;

+ 1 - 1
utils/analyze_crashlog.sh

@@ -19,7 +19,7 @@ fi
 repo="ZuluSCSI/ZuluSCSI-firmware"
 
 # Find firmware compilation time
-fwtime=$(grep 'FW Version' $logfile | tail -n 1 | egrep -o '[A-Z][a-z][a-z] [0-9]+ [0-9]+ [0-9:]+')
+fwtime=$(grep 'FW Version' $logfile | tail -n 1 | egrep -o '[A-Z][a-z][a-z]\s+[0-9]+\s+[0-9]+\s+[0-9:]+')
 
 # Check if the firmware file is available locally
 echo "Searching for firmware compiled at $fwtime"