From 299656a157eacf6ed3b5981ce67476a264935779 Mon Sep 17 00:00:00 2001 From: saybur Date: Sat, 20 May 2023 09:26:40 -0500 Subject: [PATCH] Support for platform eject buttons. --- .../ZuluSCSI_platform.cpp | 5 ++ .../ZuluSCSI_platform.h | 7 +++ .../ZuluSCSI_platform.cpp | 13 +++++ .../ZuluSCSI_platform.h | 7 +++ .../ZuluSCSI_platform.cpp | 7 +++ .../ZuluSCSI_platform.h | 7 +++ src/ZuluSCSI.cpp | 1 + src/ZuluSCSI_cdrom.cpp | 24 +++++--- src/ZuluSCSI_cdrom.h | 3 + src/ZuluSCSI_disk.cpp | 58 ++++++++++++++++++- src/ZuluSCSI_disk.h | 9 +++ 11 files changed, 132 insertions(+), 9 deletions(-) diff --git a/lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.cpp b/lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.cpp index fe9192f3..51bbcb8d 100644 --- a/lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.cpp +++ b/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 */ /***********************/ diff --git a/lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.h b/lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.h index bf03635c..8c7918f5 100644 --- a/lib/ZuluSCSI_platform_GD32F205/ZuluSCSI_platform.h +++ b/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(); diff --git a/lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.cpp b/lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.cpp index eeab5f6e..ab3ad092 100644 --- a/lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.cpp +++ b/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 */ /*****************************************/ diff --git a/lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.h b/lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.h index ffea6b98..e9768e8f 100644 --- a/lib/ZuluSCSI_platform_RP2040/ZuluSCSI_platform.h +++ b/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); diff --git a/lib/ZuluSCSI_platform_template/ZuluSCSI_platform.cpp b/lib/ZuluSCSI_platform_template/ZuluSCSI_platform.cpp index c74f7291..c4f1d67c 100644 --- a/lib/ZuluSCSI_platform_template/ZuluSCSI_platform.cpp +++ b/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 */ /**********************************************/ diff --git a/lib/ZuluSCSI_platform_template/ZuluSCSI_platform.h b/lib/ZuluSCSI_platform_template/ZuluSCSI_platform.h index 5728df89..0bb3b014 100644 --- a/lib/ZuluSCSI_platform_template/ZuluSCSI_platform.h +++ b/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); diff --git a/src/ZuluSCSI.cpp b/src/ZuluSCSI.cpp index a9e981c5..b613cb5a 100644 --- a/src/ZuluSCSI.cpp +++ b/src/ZuluSCSI.cpp @@ -707,6 +707,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()) diff --git a/src/ZuluSCSI_cdrom.cpp b/src/ZuluSCSI_cdrom.cpp index 7a45e559..8d44fb3f 100644 --- a/src/ZuluSCSI_cdrom.cpp +++ b/src/ZuluSCSI_cdrom.cpp @@ -812,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) { @@ -1232,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); @@ -1247,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; @@ -1405,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 diff --git a/src/ZuluSCSI_cdrom.h b/src/ZuluSCSI_cdrom.h index c47d0c2e..f347c516 100644 --- a/src/ZuluSCSI_cdrom.h +++ b/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); diff --git a/src/ZuluSCSI_disk.cpp b/src/ZuluSCSI_disk.cpp index 7bc78254..cdc03864 100644 --- a/src/ZuluSCSI_disk.cpp +++ b/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(); diff --git a/src/ZuluSCSI_disk.h b/src/ZuluSCSI_disk.h index 589ea06a..dc7a776b 100644 --- a/src/ZuluSCSI_disk.h +++ b/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();