ソースを参照

Merge pull request #390 from ZuluSCSI/feature/apple-cdrom-digital-audio

Add Apple CD-ROM digital audio over SCSI
Alex Perez 1 年間 前
コミット
dc376f64c3

+ 13 - 3
lib/SCSI2SD/src/firmware/mode.c

@@ -1,6 +1,7 @@
 //	Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
 //  Copyright (C) 2014 Doug Brown <doug@downtowndougbrown.com>
 //  Copyright (C) 2019 Landon Rodgers <g.landon.rodgers@gmail.com>
+//	Copyright (C) 2024 Rabbit Hole Computing LLC
 //
 //	This file is part of SCSI2SD.
 //
@@ -288,9 +289,18 @@ static void doModeSense(
 		break;
 
 	case S2S_CFG_OPTICAL:
-		mediumType = 0x02; // 120mm CDROM, data only.
-		deviceSpecificParam = 0;
-		density = 0x01; // User data only, 2048bytes per sector.
+		if (scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_APPLE)
+		{
+			mediumType = 0x00;
+			deviceSpecificParam = 0;
+			density = 0x00;
+		}
+		else
+		{
+			mediumType = 0x02; // 120mm CDROM, data only.
+			deviceSpecificParam = 0;
+			density = 0x01; // User data only, 2048bytes per sector.
+		}
 		break;
 
 	case S2S_CFG_SEQUENTIAL:

+ 8 - 2
lib/SCSI2SD/src/firmware/scsi.c

@@ -1,6 +1,7 @@
 //	Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
 //	Copyright (c) 2023 joshua stein <jcs@jcs.org>
 //	Copyright (c) 2023 Andrea Ottaviani <andrea.ottaviani.69@gmail.com>
+//	Copyright (C) 2024 Rabbit Hole Computing LLC
 //
 //	This file is part of SCSI2SD.
 //
@@ -304,16 +305,21 @@ static void process_Command()
 	memset(scsiDev.cdb + 6, 0, sizeof(scsiDev.cdb) - 6);
 	int parityError = 0;
 	scsiRead(scsiDev.cdb, 6, &parityError);
+	command = scsiDev.cdb[0];
 
 	group = scsiDev.cdb[0] >> 5;
 	scsiDev.cdbLen = CmdGroupBytes[group];
 
 	if (scsiDev.target->cfg->deviceType == S2S_CFG_OPTICAL)
 	{
+		if (scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_APPLE && (command == 0xD8 || command == 0xD9))
+		{
+			scsiDev.cdbLen =  12;
+		}
 		// Plextor CD-ROM vendor extensions 0xD8
 		if (unlikely(scsiDev.target->cfg->vendorExtensions & VENDOR_EXTENSION_OPTICAL_PLEXTOR))
 
-			if (scsiDev.cdb[0] == 0xD8)
+			if (command == 0xD8)
 			{
 				scsiDev.cdbLen =  12;
 			}
@@ -328,7 +334,7 @@ static void process_Command()
 	{
 		scsiRead(scsiDev.cdb + 6, scsiDev.cdbLen - 6, &parityError);
 	}
-	command = scsiDev.cdb[0];
+
 
 	// Prefer LUN's set by IDENTIFY messages for newer hosts.
 	if (scsiDev.lun < 0)

+ 11 - 1
lib/ZuluSCSI_platform_GD32F205/audio.cpp

@@ -55,7 +55,8 @@ extern bool g_audio_stopped;
 
 
 // historical playback status information
-static audio_status_code audio_last_status[8] = {ASC_NO_STATUS};
+static audio_status_code audio_last_status[8] = {ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS,
+                                                 ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS};
 
 // volume information for targets
 static volatile uint16_t volumes[8] = {
@@ -410,5 +411,14 @@ void audio_set_channel(uint8_t id, uint16_t chn) {
     channels[id & 7] = chn;
 }
 
+uint64_t audio_get_file_position()
+{
+    return fpos;
+}
+
+void audio_set_file_position(uint32_t lba)
+{
+    fpos = 2352 * (uint64_t)lba;
+}
 
 #endif // ENABLE_AUDIO_OUTPUT

+ 12 - 2
lib/ZuluSCSI_platform_RP2040/audio.cpp

@@ -138,8 +138,8 @@ static uint64_t fpos;
 static uint32_t fleft;
 
 // historical playback status information
-static audio_status_code audio_last_status[8] = {ASC_NO_STATUS};
-
+static audio_status_code audio_last_status[8] = {ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS,
+                                                 ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS};
 // volume information for targets
 static volatile uint16_t volumes[8] = {
     DEFAULT_VOLUME_LEVEL_2CH, DEFAULT_VOLUME_LEVEL_2CH, DEFAULT_VOLUME_LEVEL_2CH, DEFAULT_VOLUME_LEVEL_2CH,
@@ -585,4 +585,14 @@ void audio_set_channel(uint8_t id, uint16_t chn) {
     channels[id & 7] = chn;
 }
 
+uint64_t audio_get_file_position()
+{
+    return fpos;
+}
+
+void audio_set_file_position(uint32_t lba)
+{
+    fpos = 2352 * (uint64_t)lba;
+
+}
 #endif // ENABLE_AUDIO_OUTPUT

+ 2 - 2
platformio.ini

@@ -43,7 +43,7 @@ lib_deps =
 upload_protocol = stlink
 platform_packages = platformio/toolchain-gccarmnoneeabi@1.100301.220327
     framework-spl-gd32@https://github.com/CommunityGD32Cores/gd32-pio-spl-package.git
-debug_tool = cmsis-dap
+debug_tool = stlink
 extra_scripts = src/build_bootloader.py
 debug_build_flags = 
      -Os -Wall -Wno-sign-compare -ggdb -g3
@@ -176,7 +176,7 @@ build_flags =
     -DZULUSCSI_DAYNAPORT
 ; These take a large portion of the SRAM and can be adjusted
     -DLOGBUFSIZE=8192
-    -DPREFETCH_BUFFER_SIZE=5120
+    -DPREFETCH_BUFFER_SIZE=4608
     -DSCSI2SD_BUFFER_SIZE=57344
     ; This controls the depth of NETWORK_PACKET_MAX_SIZE (1520 bytes)
     ; For example a queue size of 10 would be 10 x 1520 = 15200 bytes

+ 14 - 1
src/ZuluSCSI_audio.h

@@ -145,4 +145,17 @@ uint16_t audio_get_channel(uint8_t id);
  * \param id    SCSI ID to set channel information for.
  * \param chn   The new channel information.
  */
-void audio_set_channel(uint8_t id, uint16_t chn);
+void audio_set_channel(uint8_t id, uint16_t chn);
+
+/**
+ * Gets the byte position in the audio image
+ * 
+ * \return byte position in the audio image
+*/
+uint64_t audio_get_file_position();
+
+/**
+ * Sets the playback position in the audio image via the lba
+ * 
+*/
+void audio_set_file_position(uint32_t lba);

+ 86 - 24
src/ZuluSCSI_cdrom.cpp

@@ -43,6 +43,8 @@ extern "C" {
 #include <scsi.h>
 }
 
+static const uint16_t AUDIO_CD_SECTOR_LEN = 2352;
+
 /******************************************/
 /* Basic TOC generation without cue sheet */
 /******************************************/
@@ -223,6 +225,7 @@ static const uint8_t TrackInformation[] =
 enum sector_type_t
 {
      SECTOR_TYPE_VENDOR_PLEXTOR = 100,
+     SECTOR_TYPE_VENDOR_APPLE_300plus = 200
 };
 
 // Convert logical block address to CD-ROM time
@@ -1096,7 +1099,7 @@ void doGetConfiguration(uint8_t rt, uint16_t startFeature, uint16_t allocationLe
 #endif
 
     // finally, rewrite data length to match
-    uint32_t dlen = len - 8;
+    uint32_t dlen = len - 4;
     scsiDev.data[0] = dlen >> 24;
     scsiDev.data[1] = dlen >> 16;
     scsiDev.data[2] = dlen >> 8;
@@ -1294,9 +1297,9 @@ static void doGetEventStatusNotification(bool immed)
 
 void cdromGetAudioPlaybackStatus(uint8_t *status, uint32_t *current_lba, bool current_only)
 {
-    image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
-
+    
 #ifdef ENABLE_AUDIO_OUTPUT
+    image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
     if (status) {
         uint8_t target = img.scsiId & 7;
         if (current_only) {
@@ -1305,17 +1308,11 @@ void cdromGetAudioPlaybackStatus(uint8_t *status, uint32_t *current_lba, bool cu
             *status = (uint8_t) audio_get_status_code(target);
         }
     }
+    *current_lba = audio_get_file_position() / 2352;
 #else
     if (status) *status = 0; // audio status code for 'unsupported/invalid' and not-playing indicator
 #endif
-    if (current_lba)
-    {
-        if (img.file.isOpen()) {
-            *current_lba = img.file.position() / 2352;
-        } else {
-            *current_lba = 0;
-        }
-    }
+    
 }
 
 static void doPlayAudio(uint32_t lba, uint32_t length)
@@ -1332,8 +1329,10 @@ static void doPlayAudio(uint32_t lba, uint32_t length)
 
     // if transfer length is zero no audio playback happens.
     // don't treat as an error per SCSI-2; handle via short-circuit
+
     if (length == 0)
     {
+        audio_set_file_position(lba);
         scsiDev.status = 0;
         scsiDev.phase = STATUS;
         return;
@@ -1349,7 +1348,7 @@ static void doPlayAudio(uint32_t lba, uint32_t length)
         if (lba == 0xFFFFFFFF)
         {
             // request to start playback from 'current position'
-            lba = img.file.position() / 2352;
+            lba = audio_get_file_position() / AUDIO_CD_SECTOR_LEN;
         }
 
         uint64_t offset = trackinfo.file_offset
@@ -1407,7 +1406,7 @@ static void doPlayAudio(uint32_t lba, uint32_t length)
 static void doPauseResumeAudio(bool resume)
 {
 #ifdef ENABLE_AUDIO_OUTPUT
-    logmsg("------ CD-ROM ", resume ? "resume" : "pause", " audio playback");
+    dbgmsg("------ CD-ROM ", resume ? "resume" : "pause", " audio playback");
     image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
     uint8_t target_id = img.scsiId & 7;
 
@@ -1448,7 +1447,7 @@ static void doMechanismStatus(uint16_t allocation_length)
     uint8_t *buf = scsiDev.data;
 
     uint8_t status;
-    uint32_t lba;
+    uint32_t lba = 0;
     cdromGetAudioPlaybackStatus(&status, &lba, true);
 
     *buf++ = 0x00; // No fault state
@@ -1513,12 +1512,20 @@ static void doReadCD(uint32_t lba, uint32_t length, uint8_t sector_type,
             return;
         }
 
-        const uint16_t PLEXTOR_D8_SECTOR_LENGTH =  2352;
-        trackinfo.sector_length = PLEXTOR_D8_SECTOR_LENGTH;
+        trackinfo.sector_length = AUDIO_CD_SECTOR_LEN;
         trackinfo.track_mode = CUETrack_AUDIO;
         offset = (uint64_t)(lba - 2) * trackinfo.sector_length;
         dbgmsg("------ Read CD Vendor Plextor (0xd8): ", (int)length, " sectors starting at ", (int)lba -2,
-               ", sector size ", (int) PLEXTOR_D8_SECTOR_LENGTH,
+               ", sector size ", (int) AUDIO_CD_SECTOR_LEN,
+               ", data offset in file ", (int)offset);
+    }
+    if (sector_type == SECTOR_TYPE_VENDOR_APPLE_300plus)
+    {
+        trackinfo.sector_length = AUDIO_CD_SECTOR_LEN;
+        trackinfo.track_mode = CUETrack_AUDIO;
+        offset = (uint64_t)(lba) * trackinfo.sector_length;
+        dbgmsg("------ Read CD Vendor Apple CDROM 300 plus (0xd8): ", (int)length, " sectors starting at ", (int)lba,
+               ", sector size ", (int) AUDIO_CD_SECTOR_LEN,
                ", data offset in file ", (int)offset);
     }
     else
@@ -1559,6 +1566,10 @@ static void doReadCD(uint32_t lba, uint32_t length, uint8_t sector_type,
         {
             sector_type_ok = true;
         }
+        else if (sector_type == SECTOR_TYPE_VENDOR_APPLE_300plus )
+        {
+            sector_type_ok = true;
+        }
 
         if (!sector_type_ok)
         {
@@ -1586,7 +1597,7 @@ static void doReadCD(uint32_t lba, uint32_t length, uint8_t sector_type,
     else if (trackinfo.track_mode == CUETrack_AUDIO)
     {
         // Transfer whole 2352 byte audio sectors from file to host
-        sector_length = 2352;
+        sector_length = AUDIO_CD_SECTOR_LEN;
     }
     else if (trackinfo.track_mode == CUETrack_MODE1_2048 && main_channel == 0x10)
     {
@@ -1609,7 +1620,7 @@ static void doReadCD(uint32_t lba, uint32_t length, uint8_t sector_type,
     else if (trackinfo.track_mode == CUETrack_MODE1_2352 && (main_channel & 0xB8) == 0xB8)
     {
         // Transfer whole 2352 byte data sector with ECC to host
-        sector_length = 2352;
+        sector_length = AUDIO_CD_SECTOR_LEN;
     }
     else
     {
@@ -1745,7 +1756,7 @@ static void doReadSubchannel(bool time, bool subq, uint8_t parameter, uint8_t tr
     if (parameter == 0x01)
     {
         uint8_t audiostatus;
-        uint32_t lba;
+        uint32_t lba = 0;
         cdromGetAudioPlaybackStatus(&audiostatus, &lba, false);
         dbgmsg("------ Get audio playback position: status ", (int)audiostatus, " lba ", (int)lba);
 
@@ -1876,11 +1887,16 @@ static bool doReadCapacity(uint32_t lba, uint8_t pmi)
     return true;
 }
 
-static void doReadD8(uint32_t lba, uint32_t length)
+static void doReadPlextorD8(uint32_t lba, uint32_t length)
 {
     doReadCD(lba, length, SECTOR_TYPE_VENDOR_PLEXTOR, true, false, false );
 }
 
+static void doAppleD8(uint32_t lba, uint32_t blocks)
+{
+    doReadCD(lba, blocks, SECTOR_TYPE_VENDOR_APPLE_300plus, true, false, false );
+}
+
 /**************************************/
 /* CD-ROM command dispatching         */
 /**************************************/
@@ -2077,8 +2093,9 @@ extern "C" int scsiCDRomCommand()
                 && scsiDev.cdb[5] == 0xFF)
         {
             // request to start playback from 'current position'
-            image_config_t &img = *(image_config_t*)scsiDev.target->cfg;
-            lba = img.file.position() / 2352;
+#ifdef ENABLE_AUDIO_OUTPUT
+            lba = audio_get_file_position() / AUDIO_CD_SECTOR_LEN;
+#endif
         }
 
         uint32_t length = end - lba;
@@ -2216,7 +2233,7 @@ extern "C" int scsiCDRomCommand()
                 (((uint32_t) scsiDev.cdb[7]) << 16) +
                 (((uint32_t) scsiDev.cdb[8]) << 8) +
                 scsiDev.cdb[9];
-            doReadD8(lba, blocks);
+            doReadPlextorD8(lba, blocks);
         }
     }
     else if (command == 0x4E)
@@ -2259,6 +2276,51 @@ extern "C" int scsiCDRomCommand()
         // Byte 5: 'F' in hex
         commandHandled = 0;
     }
+    else if (scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_APPLE
+            && command == 0xD8)
+    {
+        // vendor-specific command for Apple 300 plus
+        // plays CD audio over the SCSI bus
+        uint32_t lba =
+            (((uint32_t) scsiDev.cdb[2]) << 24) +
+            (((uint32_t) scsiDev.cdb[3]) << 16) +
+            (((uint32_t) scsiDev.cdb[4]) << 8) +
+            scsiDev.cdb[5];
+        uint32_t blocks =
+            (((uint32_t) scsiDev.cdb[6]) << 24) +
+            (((uint32_t) scsiDev.cdb[7]) << 16) +
+            (((uint32_t) scsiDev.cdb[8]) << 8) +
+            scsiDev.cdb[9];
+        uint8_t sub_sector_type = scsiDev.cdb[10];
+        if (sub_sector_type != 0)
+        {
+            dbgmsg("For Apple CD-ROM 0xD8 command, only 2352 sector length supported (type 0), got subsector type: ", sub_sector_type);
+        }
+        doAppleD8(lba, blocks);
+    }
+    else if (scsiDev.target->cfg->quirks == S2S_CFG_QUIRKS_APPLE
+            && command == 0xD9)
+    {
+        // vendor-specific command for Apple 300 plus
+        // plays CD audio over the SCSI bus using MSF
+
+        uint8_t m = scsiDev.cdb[3];
+        uint8_t s = scsiDev.cdb[4];
+        uint8_t f = scsiDev.cdb[5];
+        uint32_t lba = MSF2LBA(m, s, f, false);
+
+        m = scsiDev.cdb[7];
+        s = scsiDev.cdb[8];
+        f = scsiDev.cdb[9];
+        uint32_t blocks = MSF2LBA(m, s, f, false) - lba;
+        uint8_t sub_sector_type = scsiDev.cdb[10];
+        if (sub_sector_type != 0)
+        {
+            dbgmsg("For Apple CD-ROM 0xD9 command, only 2352 sector length supported (type 0), got subsector type: ", sub_sector_type);
+        }
+
+        doAppleD8(lba, blocks);
+    }
     else
     {
         commandHandled = 0;

+ 1 - 1
src/ZuluSCSI_config.h

@@ -89,7 +89,7 @@
 // Default SCSI drive information when Apple quirks are enabled
 #define APPLE_DRIVEINFO_FIXED     {"CDC",      "ZuluSCSI HDD",      PLATFORM_REVISION, "1.0"}
 #define APPLE_DRIVEINFO_REMOVABLE {"IOMEGA",   "BETA230",           PLATFORM_REVISION, "2.02"}
-#define APPLE_DRIVEINFO_OPTICAL   {"MATSHITA", "CD-ROM CR-8004A",   PLATFORM_REVISION, "2.0a"}
+#define APPLE_DRIVEINFO_OPTICAL   {"MATSHITA", "CD-ROM CR-8004",    PLATFORM_REVISION, "1.1f"}
 #define APPLE_DRIVEINFO_FLOPPY    {"IOMEGA",     "Io20S         *F", "PP33", ""}
 #define APPLE_DRIVEINFO_MAGOPT    {"MOST",     "RMD-5200",          PLATFORM_REVISION, "1.0"}
 #define APPLE_DRIVEINFO_NETWORK   {"Dayna",    "SCSI/Link",       "2.0f", ""}

+ 15 - 14
src/ZuluSCSI_log_trace.cpp

@@ -100,7 +100,8 @@ static const char *getCommandName(uint8_t cmd)
         case 0xA8: return "Read12";
         case 0xC0: return "OMTI-5204 DefineFlexibleDiskFormat";
         case 0xC2: return "OMTI-5204 AssignDiskParameters";
-        case 0xD8: return "Plextor ReadCD";
+        case 0xD8: return "Vendor 0xD8 Command";
+        case 0xD9: return "Vendor 0xD9 Command";
         case 0xE0: return "Xebec RAM Diagnostic";
         case 0xE4: return "Xebec Drive Diagnostic";              
         default:   return "Unknown";
@@ -178,9 +179,9 @@ static void printNewPhase(int phase, bool initiator = false)
             if (!initiator && scsiDev.target->syncOffset > 0)
                 dbgmsg("---- DATA_OUT, syncOffset ", (int)scsiDev.target->syncOffset,
                                     " syncPeriod ", (int)scsiDev.target->syncPeriod);
-	    // log Xebec vendor commands data
-	    else if (scsiDev.cdb[0] == 0x0C || scsiDev.cdb[0] == 0x0F)
-		    g_LogData = true;
+            // log Xebec vendor commands data
+            else if (scsiDev.cdb[0] == 0x0C || scsiDev.cdb[0] == 0x0F)
+                g_LogData = true;
             else
                 dbgmsg("---- DATA_OUT");
             break;
@@ -213,17 +214,17 @@ void scsiLogPhaseChange(int new_phase)
         {
             dbgmsg("---- Total IN: ", g_InByteCount, " OUT: ", g_OutByteCount, " CHECKSUM: ", (int)g_DataChecksum);
         }
-	// log Xebec vendor command
+        // log Xebec vendor command
         if (old_phase == DATA_OUT && scsiDev.cdb[0] == 0x0C && g_OutByteCount == 8)
-	{
-		int cylinders = ((uint16_t)scsiDev.data[0] << 8) + scsiDev.data[1];
-		int heads = scsiDev.data[2];
-		int reducedWrite = ((uint16_t)scsiDev.data[3] << 8) + scsiDev.data[4];
-		int writePrecomp = ((uint16_t)scsiDev.data[5] << 8) + scsiDev.data[6];
-		int eccBurst = scsiDev.data[7];
-		dbgmsg("---- Xebec Initialize Drive Characteristics: cylinders=", cylinders, " heads=", heads,
-				" reducedWrite=", reducedWrite, " writePrecomp=", writePrecomp, " eccBurst=", eccBurst);
-	}
+        {
+            int cylinders = ((uint16_t)scsiDev.data[0] << 8) + scsiDev.data[1];
+            int heads = scsiDev.data[2];
+            int reducedWrite = ((uint16_t)scsiDev.data[3] << 8) + scsiDev.data[4];
+            int writePrecomp = ((uint16_t)scsiDev.data[5] << 8) + scsiDev.data[6];
+            int eccBurst = scsiDev.data[7];
+            dbgmsg("---- Xebec Initialize Drive Characteristics: cylinders=", cylinders, " heads=", heads,
+                    " reducedWrite=", reducedWrite, " writePrecomp=", writePrecomp, " eccBurst=", eccBurst);
+        }
         g_InByteCount = g_OutByteCount = 0;
         g_DataChecksum = 0;
 

+ 2 - 2
src/ZuluSCSI_mode.cpp

@@ -53,8 +53,8 @@ static const uint8_t CDROMAudioControlParametersPage[] =
 0x04, // 'Immed' bit set, 'SOTC' bit not set
 0x00, // reserved
 0x00, // reserved
-0x80, // 1 LBAs/sec multip
-0x00, 0x4B, // 75 LBAs/sec
+0x00, // reserved was //  0x80, // 1 LBAs/sec multip
+0x00, 0x00, // obsolete was // 75 LBAs/sec 
 0x01, 0xFF, // output port 0 active, max volume
 0x02, 0xFF, // output port 1 active, max volume
 0x00, 0x00, // output port 2 inactive