diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3f3a2fd..33f24a8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,11 +64,15 @@ jobs: run: | set -ex export PATH=$(readlink -f ./gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf/bin):$PATH - cd ./circle-stdlib/libs/circle/boot - make - make armstub64 - cd - - cp -r ./circle-stdlib/libs/circle/boot/* sdcard + # cd ./circle-stdlib/libs/circle/boot + # make + # make armstub64 + # cd - + # cp -r ./circle-stdlib/libs/circle/boot/* sdcard + cd ./sdcard + wget https://github.com/probonopd/MiniDexed/releases/download/assets/boot.zip + unzip boot.zip && rm boot.zip + cd .. rm -rf sdcard/config*.txt sdcard/README sdcard/Makefile sdcard/armstub sdcard/COPYING.linux cp ./src/config.txt ./src/minidexed.ini ./src/performance.ini sdcard/ cp ./getsysex.sh sdcard/ diff --git a/src/minidexed.cpp b/src/minidexed.cpp index 0f28f37..dc32644 100644 --- a/src/minidexed.cpp +++ b/src/minidexed.cpp @@ -1123,6 +1123,7 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT case TGParameterATAmplitude: setModController(3, 2, nValue, nTG); break; case TGParameterATEGBias: setModController(3, 3, nValue, nTG); break; + case TGParameterMIDIChannel: assert (0 <= nValue && nValue <= 255); SetMIDIChannel ((uint8_t) nValue, nTG); @@ -2367,7 +2368,7 @@ void CMiniDexed::UpdateNetwork() } if (m_pConfig->GetNetworkFTPEnabled()) { - m_pFTPDaemon = new CFTPDaemon(FTPUSERNAME, FTPPASSWORD); + m_pFTPDaemon = new CFTPDaemon(FTPUSERNAME, FTPPASSWORD, m_pmDNSPublisher, m_pConfig); if (!m_pFTPDaemon->Initialize()) { diff --git a/src/net/applemidi.cpp b/src/net/applemidi.cpp index 8440426..8cbd3a4 100644 --- a/src/net/applemidi.cpp +++ b/src/net/applemidi.cpp @@ -460,7 +460,7 @@ bool ParseMIDIPacket(const u8* pBuffer, size_t nSize, TRTPMIDI* pOutPacket, CApp return ParseMIDICommandSection(pMIDICommandSection, nRemaining, pHandler); } -CAppleMIDIParticipant::CAppleMIDIParticipant(CBcmRandomNumberGenerator* pRandom, CAppleMIDIHandler* pHandler) +CAppleMIDIParticipant::CAppleMIDIParticipant(CBcmRandomNumberGenerator* pRandom, CAppleMIDIHandler* pHandler, const char* pSessionName) : CTask(TASK_STACK_SIZE, true), m_pRandom(pRandom), @@ -492,7 +492,9 @@ CAppleMIDIParticipant::CAppleMIDIParticipant(CBcmRandomNumberGenerator* pRandom, m_nSequence(0), m_nLastFeedbackSequence(0), - m_nLastFeedbackTime(0) + m_nLastFeedbackTime(0), + + m_pSessionName(pSessionName) { } @@ -805,8 +807,11 @@ bool CAppleMIDIParticipant::SendAcceptInvitationPacket(CSocket* pSocket, CIPAddr {'\0'} }; - // TODO: configurable name - strncpy(AcceptPacket.Name, "MiniDexed", sizeof(AcceptPacket.Name)); + // Use hostname as the session name + if (m_pSessionName && m_pSessionName[0]) + strncpy(AcceptPacket.Name, m_pSessionName, sizeof(AcceptPacket.Name)); + else + strncpy(AcceptPacket.Name, "MiniDexed", sizeof(AcceptPacket.Name)); #ifdef APPLEMIDI_DEBUG LOGNOTE("--> Accept invitation"); diff --git a/src/net/applemidi.h b/src/net/applemidi.h index bc8d2b8..492be10 100644 --- a/src/net/applemidi.h +++ b/src/net/applemidi.h @@ -39,7 +39,7 @@ public: class CAppleMIDIParticipant : protected CTask { public: - CAppleMIDIParticipant(CBcmRandomNumberGenerator* pRandom, CAppleMIDIHandler* pHandler); + CAppleMIDIParticipant(CBcmRandomNumberGenerator* pRandom, CAppleMIDIHandler* pHandler, const char* pSessionName); virtual ~CAppleMIDIParticipant() override; bool Initialize(); @@ -109,6 +109,8 @@ private: u16 m_nSequence = 0; u16 m_nLastFeedbackSequence = 0; u64 m_nLastFeedbackTime = 0; + + const char* m_pSessionName; }; #endif \ No newline at end of file diff --git a/src/net/ftpdaemon.cpp b/src/net/ftpdaemon.cpp index 0cab51c..015dbc9 100644 --- a/src/net/ftpdaemon.cpp +++ b/src/net/ftpdaemon.cpp @@ -34,11 +34,13 @@ LOGMODULE("ftpd"); constexpr u16 ListenPort = 21; constexpr u8 MaxConnections = 1; -CFTPDaemon::CFTPDaemon(const char* pUser, const char* pPassword) +CFTPDaemon::CFTPDaemon(const char* pUser, const char* pPassword, CmDNSPublisher* pMDNSPublisher, CConfig* pConfig) : CTask(TASK_STACK_SIZE, true), m_pListenSocket(nullptr), m_pUser(pUser), - m_pPassword(pPassword) + m_pPassword(pPassword), + m_pmDNSPublisher(pMDNSPublisher), + m_pConfig(pConfig) { } @@ -106,6 +108,6 @@ void CFTPDaemon::Run() } // Spawn new worker - new CFTPWorker(pConnection, m_pUser, m_pPassword); + new CFTPWorker(pConnection, m_pUser, m_pPassword, m_pmDNSPublisher, m_pConfig); } } \ No newline at end of file diff --git a/src/net/ftpdaemon.h b/src/net/ftpdaemon.h index 4d75762..77afb14 100644 --- a/src/net/ftpdaemon.h +++ b/src/net/ftpdaemon.h @@ -25,11 +25,13 @@ #include #include +#include "mdnspublisher.h" +#include "../config.h" class CFTPDaemon : protected CTask { public: - CFTPDaemon(const char* pUser, const char* pPassword); + CFTPDaemon(const char* pUser, const char* pPassword, CmDNSPublisher* pMDNSPublisher, CConfig* pConfig); virtual ~CFTPDaemon() override; bool Initialize(); @@ -42,6 +44,8 @@ private: const char* m_pUser; const char* m_pPassword; + CmDNSPublisher* m_pmDNSPublisher; + CConfig* m_pConfig; }; #endif \ No newline at end of file diff --git a/src/net/ftpworker.cpp b/src/net/ftpworker.cpp index 28415e2..a19cfde 100644 --- a/src/net/ftpworker.cpp +++ b/src/net/ftpworker.cpp @@ -126,7 +126,7 @@ inline bool DirectoryCaseInsensitiveAscending(const TDirectoryListEntry& EntryA, } -CFTPWorker::CFTPWorker(CSocket* pControlSocket, const char* pExpectedUser, const char* pExpectedPassword) +CFTPWorker::CFTPWorker(CSocket* pControlSocket, const char* pExpectedUser, const char* pExpectedPassword, CmDNSPublisher* pMDNSPublisher, CConfig* pConfig) : CTask(TASK_STACK_SIZE), m_LogName(), m_pExpectedUser(pExpectedUser), @@ -142,7 +142,9 @@ CFTPWorker::CFTPWorker(CSocket* pControlSocket, const char* pExpectedUser, const m_DataType(TDataType::ASCII), m_TransferMode(TTransferMode::Active), m_CurrentPath(), - m_RenameFrom() + m_RenameFrom(), + m_pmDNSPublisher(pMDNSPublisher), + m_pConfig(pConfig) { ++s_nInstanceCount; m_LogName.Format("ftpd[%d]", s_nInstanceCount); @@ -562,7 +564,7 @@ bool CFTPWorker::Passive(const char* pArgs) IPAddress[2], IPAddress[3], (m_nDataSocketPort >> 8) & 0xFF, - m_nDataSocketPort & 0xFF + (m_nDataSocketPort & 0xFF) ); SendStatus(TFTPStatus::EnteringPassiveMode, Buffer); @@ -1058,9 +1060,24 @@ bool CFTPWorker::Bye(const char* pArgs) SendStatus(TFTPStatus::ClosingControl, "Goodbye."); delete m_pControlSocket; m_pControlSocket = nullptr; - + + // Unpublish the mDNS services + if (m_pmDNSPublisher && m_pConfig) + { + m_pmDNSPublisher->UnpublishService(m_pConfig->GetNetworkHostname()); + m_pmDNSPublisher->UnpublishService(m_pConfig->GetNetworkHostname(), CmDNSPublisher::ServiceTypeAppleMIDI, 5004); + m_pmDNSPublisher->UnpublishService(m_pConfig->GetNetworkHostname(), CmDNSPublisher::ServiceTypeFTP, 21); + } + + // Non-blocking 2 second delay before reboot + CTimer* const pTimer = CTimer::Get(); + unsigned int start = pTimer->GetTicks(); + while ((pTimer->GetTicks() - start) < 2 * HZ) { + CScheduler::Get()->Yield(); + } + // Reboot the system if the user disconnects in order to apply any changes made - reboot (); + reboot(); return true; } diff --git a/src/net/ftpworker.h b/src/net/ftpworker.h index 62e60ed..17abfda 100644 --- a/src/net/ftpworker.h +++ b/src/net/ftpworker.h @@ -27,6 +27,8 @@ #include #include #include +#include "../config.h" +#include "mdnspublisher.h" // TODO: These may be incomplete/inaccurate enum TFTPStatus @@ -79,7 +81,7 @@ struct TDirectoryListEntry; class CFTPWorker : protected CTask { public: - CFTPWorker(CSocket* pControlSocket, const char* pExpectedUser, const char* pExpectedPassword); + CFTPWorker(CSocket* pControlSocket, const char* pExpectedUser, const char* pExpectedPassword, CmDNSPublisher* pMDNSPublisher, CConfig* pConfig); virtual ~CFTPWorker() override; virtual void Run() override; @@ -142,6 +144,9 @@ private: CString m_CurrentPath; CString m_RenameFrom; + CmDNSPublisher* m_pmDNSPublisher; + CConfig* m_pConfig; + static void FatFsPathToFTPPath(const char* pInBuffer, char* pOutBuffer, size_t nSize); static void FTPPathToFatFsPath(const char* pInBuffer, char* pOutBuffer, size_t nSize); diff --git a/src/net/mdnspublisher.cpp b/src/net/mdnspublisher.cpp index fbeb549..49ae79b 100644 --- a/src/net/mdnspublisher.cpp +++ b/src/net/mdnspublisher.cpp @@ -131,6 +131,45 @@ boolean CmDNSPublisher::UnpublishService (const char *pServiceName) delete pService; return TRUE; } +boolean CmDNSPublisher::UnpublishService(const char *pServiceName, const char *pServiceType, u16 usServicePort) +{ + if (!m_bRunning) + { + return FALSE; + } + assert(pServiceName); + assert(pServiceType); + m_Mutex.Acquire(); + TService *pService = nullptr; + TPtrListElement *pElement = m_ServiceList.GetFirst(); + while (pElement) + { + pService = static_cast(CPtrList::GetPtr(pElement)); + assert(pService); + if (pService->ServiceName.Compare(pServiceName) == 0 && + pService->ServiceType.Compare(pServiceType) == 0 && + pService->usServicePort == usServicePort) + { + m_ServiceList.Remove(pElement); + break; + } + pService = nullptr; + pElement = m_ServiceList.GetNext(pElement); + } + m_Mutex.Release(); + if (!pService) + { + return FALSE; + } + LOGDBG("Unpublish service %s %s %u", (const char *)pService->ServiceName, (const char *)pService->ServiceType, pService->usServicePort); + SendResponse(pService, FALSE); + for (unsigned i = 0; i < pService->nTextRecords; i++) + { + delete pService->ppText[i]; + } + delete pService; + return TRUE; +} void CmDNSPublisher::Run (void) { assert (m_pNet); diff --git a/src/net/mdnspublisher.h b/src/net/mdnspublisher.h index 6b132a7..c26f6ea 100644 --- a/src/net/mdnspublisher.h +++ b/src/net/mdnspublisher.h @@ -32,6 +32,7 @@ class CmDNSPublisher : public CTask /// mDNS / Bonjour client task { public: static constexpr const char *ServiceTypeAppleMIDI = "_apple-midi._udp"; + static constexpr const char *ServiceTypeFTP = "_ftp._tcp"; public: /// \param pNet Pointer to the network subsystem object CmDNSPublisher (CNetSubSystem *pNet); @@ -50,6 +51,12 @@ public: /// \param pServiceName Name of the service to be unpublished (same as when published) /// \return Operation successful? boolean UnpublishService (const char *pServiceName); + /// \brief Stop publishing a service + /// \param pServiceName Name of the service to be unpublished + /// \param pServiceType Type of the service to be unpublished + /// \param usServicePort Port number of the service to be unpublished + /// \return Operation successful? + boolean UnpublishService (const char *pServiceName, const char *pServiceType, u16 usServicePort); void Run (void) override; private: static const unsigned MaxTextRecords = 10; diff --git a/src/performanceconfig.cpp b/src/performanceconfig.cpp index d0ce2de..dfff8a9 100644 --- a/src/performanceconfig.cpp +++ b/src/performanceconfig.cpp @@ -33,7 +33,6 @@ LOGMODULE ("Performance"); #define PERFORMANCE_DIR "performance" #define DEFAULT_PERFORMANCE_FILENAME "performance.ini" #define DEFAULT_PERFORMANCE_NAME "Default" -#define DEFAULT_PERFORMANCE_BANK_NAME "Default" CPerformanceConfig::CPerformanceConfig (FATFS *pFileSystem) : m_Properties (DEFAULT_PERFORMANCE_FILENAME, pFileSystem) @@ -1168,10 +1167,6 @@ bool CPerformanceConfig::ListPerformanceBanks() } unsigned nNumBanks = 0; - - // Add in the default performance directory as the first bank - m_PerformanceBankName[0] = DEFAULT_PERFORMANCE_BANK_NAME; - nNumBanks = 1; m_nLastPerformanceBank = 0; // List directories with names in format 01_Perf Bank Name @@ -1277,10 +1272,7 @@ std::string CPerformanceConfig::GetPerformanceBankName(unsigned nBankID) { return m_PerformanceBankName[nBankID]; } - else - { - return DEFAULT_PERFORMANCE_BANK_NAME; - } + return ""; } std::string CPerformanceConfig::AddPerformanceBankDirName(unsigned nBankID) @@ -1290,12 +1282,6 @@ std::string CPerformanceConfig::AddPerformanceBankDirName(unsigned nBankID) { // Performance Banks directories in format "001_Bank Name" std::string Index; - if (nBankID == 0) - { - // Legacy: Bank 1 is the default performance directory - return ""; - } - if (nBankID < 9) { Index = "00" + std::to_string(nBankID+1); diff --git a/src/udpmididevice.cpp b/src/udpmididevice.cpp index 8a33e98..c915989 100644 --- a/src/udpmididevice.cpp +++ b/src/udpmididevice.cpp @@ -48,7 +48,7 @@ CUDPMIDIDevice::~CUDPMIDIDevice (void) boolean CUDPMIDIDevice::Initialize (void) { - m_pAppleMIDIParticipant = new CAppleMIDIParticipant(&m_Random, this); + m_pAppleMIDIParticipant = new CAppleMIDIParticipant(&m_Random, this, m_pConfig->GetNetworkHostname()); if (!m_pAppleMIDIParticipant->Initialize()) { LOGERR("Failed to init RTP listener"); diff --git a/submod.sh b/submod.sh index 6ef767a..32408ef 100755 --- a/submod.sh +++ b/submod.sh @@ -23,5 +23,5 @@ cd - # Use fixed master branch of Synth_Dexed cd Synth_Dexed/ git reset --hard -git checkout 65d8383ad5 -f +git checkout 2ad9e43095 -f cd - diff --git a/syslogserver.py b/syslogserver.py index 16f86f1..db45e7f 100644 --- a/syslogserver.py +++ b/syslogserver.py @@ -15,6 +15,7 @@ class SyslogServer: self.port = port self.server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.server.bind((self.host, self.port)) + self.server.settimeout(0.5) # Set timeout to allow checking self.running self.start_time = None self.running = True @@ -28,6 +29,8 @@ class SyslogServer: try: data, address = self.server.recvfrom(1024) self.handle_message(data) + except socket.timeout: + continue # Check self.running again except KeyboardInterrupt: self.running = False @@ -60,4 +63,4 @@ class SyslogServer: if __name__ == "__main__": server = SyslogServer() server.start() - print("Syslog server stopped.") \ No newline at end of file + print("Syslog server stopped.") diff --git a/updater.py b/updater.py index 30e6749..bc33058 100644 --- a/updater.py +++ b/updater.py @@ -345,13 +345,18 @@ if __name__ == "__main__": print(f"\nUploaded {file} to {selected_ip}.") except ftplib.all_errors as e: print(f"FTP error: {e}") + ftp = None # Mark ftp as unusable + # Only attempt to send BYE if ftp is connected and has a socket try: - ftp.sendcmd("BYE") - print(f"Disconnected from {selected_ip}.") - except ftplib.all_errors as e: - if str(e).strip().lower() == "timed out": - # Suppress expected timeout after BYE + if ftp is not None and getattr(ftp, 'sock', None) is not None: + ftp.sendcmd("BYE") print(f"Disconnected from {selected_ip}.") + else: + print(f"No active FTP connection to disconnect from.") + except (*ftplib.all_errors, AttributeError) as e: + # Handle timeout or already closed connection + if isinstance(e, TimeoutError) or (str(e).strip().lower() == "timed out"): + print(f"Disconnected from {selected_ip} (timeout after BYE, device likely restarted).") else: print(f"FTP error after BYE: {e}")