diff --git a/src/common/Defines.h b/src/common/Defines.h index 6f3e3c5c..51467070 100644 --- a/src/common/Defines.h +++ b/src/common/Defines.h @@ -108,8 +108,8 @@ typedef unsigned long long ulong64_t; #define __EXE_NAME__ "" #define VERSION_MAJOR "04" -#define VERSION_MINOR "01" -#define VERSION_REV "B" +#define VERSION_MINOR "02" +#define VERSION_REV "C" #define __NETVER__ "DVM_R" VERSION_MAJOR VERSION_REV VERSION_MINOR #define __VER__ VERSION_MAJOR "." VERSION_MINOR VERSION_REV " (R" VERSION_MAJOR VERSION_REV VERSION_MINOR " " __GIT_VER__ ")" diff --git a/src/common/Thread.cpp b/src/common/Thread.cpp index 528fa15c..4c59799f 100644 --- a/src/common/Thread.cpp +++ b/src/common/Thread.cpp @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016 Jonathan Naylor, G4KLX - * Copyright (C) 2023 Bryan Biedenkapp, N2PLL + * Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL * */ #include "Thread.h" @@ -82,7 +82,25 @@ void Thread::detach() ::pthread_detach(m_thread); } -/* Helper to sleep the current thread. */ +/* Executes the specified start routine to run as a thread. */ + +bool Thread::runAsThread(void* obj, void *(*startRoutine)(void *), thread_t* thread) +{ + if (thread == nullptr) + thread = new thread_t(); + + thread->obj = obj; + + if (::pthread_create(&thread->thread, NULL, startRoutine, thread) != 0) { + LogError(LOG_NET, "Error returned from pthread_create, err: %d", errno); + delete thread; + return false; + } + + return true; +} + +/* Suspends the current thread for the specified amount of time. */ void Thread::sleep(uint32_t ms, uint32_t us) { diff --git a/src/common/Thread.h b/src/common/Thread.h index 2e766d4c..25134d08 100644 --- a/src/common/Thread.h +++ b/src/common/Thread.h @@ -5,7 +5,7 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016 Jonathan Naylor, G4KLX - * Copyright (C) 2023 Bryan Biedenkapp, N2PLL + * Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL * */ /** @@ -27,6 +27,19 @@ #include +// --------------------------------------------------------------------------- +// Structure Declaration +// --------------------------------------------------------------------------- + +/** + * @brief Represents the data passed to a thread runner. + * @ingroup common + */ +struct thread_t { + void* obj; //! Object that created this thread. + pthread_t thread; //! Thread Handle. +}; + // --------------------------------------------------------------------------- // Class Declaration // --------------------------------------------------------------------------- @@ -75,7 +88,16 @@ class HOST_SW_API Thread { virtual void detach(); /** - * @brief Helper to sleep the current thread. + * @brief Executes the specified start routine to run as a thread. + * @param obj Instance of a object to pass to the threaded function. + * @param startRoutine Represents the function that executes on a thread. + * @param[out] thread Instance of the thread data. + * @returns bool True, if successful, otherwise error occurred. + */ + static bool runAsThread(void* obj, void *(*startRoutine)(void *), thread_t* thread = nullptr); + + /** + * @brief Suspends the current thread for the specified amount of time. * @param ms Time in milliseconds to sleep. * @param us Time in microseconds to sleep. */ diff --git a/src/common/ThreadFunc.h b/src/common/ThreadFunc.h deleted file mode 100644 index e544b7b9..00000000 --- a/src/common/ThreadFunc.h +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - Common Library - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2023 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file ThreadFunc.h - * @ingroup threading - */ -#if !defined(__THREAD_FUNC_H__) -#define __THREAD_FUNC_H__ - -#include "common/Thread.h" - -#include -#include - -// --------------------------------------------------------------------------- -// Class Declaration -// --------------------------------------------------------------------------- - -/** - * @brief Creates and controls a thread based around an anonymous lambda function. - * @ingroup threading - */ -class HOST_SW_API ThreadFunc : public Thread { -public: - /** - * @brief Initializes a new instance of the ThreadFunc class. - * @param e Anonymous function to use as the thread main. - */ - ThreadFunc(std::function&& e) : Thread(), - m_entry(e) - { - assert(e != nullptr); - } - - /** - * @brief User-defined function to run for the thread main. - */ - void entry() override - { - if (m_entry != nullptr) - m_entry(); - } - -private: - std::function m_entry; -}; - -#endif // __THREAD_FUNC_H__ diff --git a/src/dfsi/Dfsi.cpp b/src/dfsi/Dfsi.cpp index bc001e71..3e9ec74c 100644 --- a/src/dfsi/Dfsi.cpp +++ b/src/dfsi/Dfsi.cpp @@ -28,6 +28,7 @@ using namespace lookups; #include #include +#include #include #include @@ -152,8 +153,6 @@ int Dfsi::run() if (!ret) return EXIT_FAILURE; - ::LogInfoEx(LOG_HOST, "DFSI peer network is up and running"); - std::string dfsiModeStr = "Unknown"; switch (dfsiMode) { @@ -195,7 +194,15 @@ int Dfsi::run() StopWatch stopWatch; stopWatch.start(); - // main execution loop + /* + ** Main execution loop + */ + + struct utsname utsinfo; + ::memset(&utsinfo, 0, sizeof(utsinfo)); + ::uname(&utsinfo); + + ::LogInfoEx(LOG_HOST, "[ OK ] DFSI is up and running on %s %s %s", utsinfo.sysname, utsinfo.release, utsinfo.machine); while (!g_killed) { uint32_t ms = stopWatch.elapsed(); diff --git a/src/dfsi/DfsiMain.cpp b/src/dfsi/DfsiMain.cpp index fde71371..2f038355 100644 --- a/src/dfsi/DfsiMain.cpp +++ b/src/dfsi/DfsiMain.cpp @@ -213,13 +213,13 @@ int main(int argc, char** argv) delete dfsi; if (g_signal == 2) - ::LogInfoEx(LOG_HOST, "Exited on receipt of SIGINT"); + ::LogInfoEx(LOG_HOST, "[STOP] dvmdfsi:main SIGINT"); if (g_signal == 15) - ::LogInfoEx(LOG_HOST, "Exited on receipt of SIGTERM"); + ::LogInfoEx(LOG_HOST, "[STOP] dvmdfsi:main SIGTERM"); if (g_signal == 1) - ::LogInfoEx(LOG_HOST, "Restarting on receipt of SIGHUP"); + ::LogInfoEx(LOG_HOST, "[RSTR] dvmdfsi:main SIGHUP"); } while (g_signal == 1); ::LogFinalise(); diff --git a/src/fne/FNEMain.cpp b/src/fne/FNEMain.cpp index b4b433cf..cee8ac4c 100644 --- a/src/fne/FNEMain.cpp +++ b/src/fne/FNEMain.cpp @@ -208,13 +208,13 @@ int main(int argc, char** argv) delete fne; if (g_signal == 2) - ::LogInfoEx(LOG_HOST, "Exited on receipt of SIGINT"); + ::LogInfoEx(LOG_HOST, "[STOP] dvmfne:main SIGINT"); if (g_signal == 15) - ::LogInfoEx(LOG_HOST, "Exited on receipt of SIGTERM"); + ::LogInfoEx(LOG_HOST, "[STOP] dvmfne:main SIGTERM"); if (g_signal == 1) - ::LogInfoEx(LOG_HOST, "Restarting on receipt of SIGHUP"); + ::LogInfoEx(LOG_HOST, "[RSTR] dvmfne:main SIGHUP"); } while (g_signal == 1); ::LogFinalise(); diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index f6b9c5ee..845a12fd 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -12,7 +12,6 @@ #include "common/Log.h" #include "common/StopWatch.h" #include "common/Thread.h" -#include "common/ThreadFunc.h" #include "network/callhandler/TagDMRData.h" #include "network/callhandler/TagP25Data.h" #include "network/callhandler/TagNXDNData.h" @@ -28,6 +27,7 @@ using namespace lookups; #include #include +#include #include #include @@ -181,75 +181,29 @@ int HostFNE::run() if (!ret) return EXIT_FAILURE; - ::LogInfoEx(LOG_HOST, "FNE is up and running"); - StopWatch stopWatch; stopWatch.start(); - // setup network loop threads - ThreadFunc networkLoop([&, this]() { - if (g_killed) - return; - - if (m_network != nullptr) { - while (!g_killed) { - m_network->processNetwork(); - Thread::sleep(5U); - } - } - }); - networkLoop.run(); - networkLoop.setName("dvmfne:network-loop"); - - ThreadFunc diagNetworkLoop([&, this]() { - if (g_killed) - return; - - if (m_diagNetwork != nullptr) { - while (!g_killed) { - m_diagNetwork->processNetwork(); - Thread::sleep(5U); - } - } - }); - if (m_useAlternatePortForDiagnostics) { - diagNetworkLoop.run(); - diagNetworkLoop.setName("dvmfne:diag-network-loop"); - } - - ThreadFunc vtunLoop([&, this]() { - if (g_killed) - return; - - if (!m_vtunEnabled) - return; - - if (m_tun != nullptr) { - while (!g_killed) { - uint8_t packet[DEFAULT_MTU_SIZE]; - ::memset(packet, 0x00U, DEFAULT_MTU_SIZE); + /* + ** Initialize Threads + */ - ssize_t len = m_tun->read(packet); - if (len > 0) { - switch (m_packetDataMode) { - case PacketDataMode::DMR: - // TODO: not supported yet - break; + if (!Thread::runAsThread(this, threadMasterNetwork)) + return EXIT_FAILURE; + if (!Thread::runAsThread(this, threadDiagNetwork)) + return EXIT_FAILURE; + if (!Thread::runAsThread(this, threadVirtualNetworking)) + return EXIT_FAILURE; - case PacketDataMode::PROJECT25: - m_network->p25TrafficHandler()->processPacketFrame(packet, DEFAULT_MTU_SIZE); - break; - } - } + /* + ** Main execution loop + */ - Thread::sleep(5U); - } - } - }); - vtunLoop.run(); - vtunLoop.setName("dvmfne:vtun-loop"); + struct utsname utsinfo; + ::memset(&utsinfo, 0, sizeof(utsinfo)); + ::uname(&utsinfo); - // main execution loop + ::LogInfoEx(LOG_HOST, "[ OK ] FNE is up and running on %s %s %s", utsinfo.sysname, utsinfo.release, utsinfo.machine); while (!g_killed) { uint32_t ms = stopWatch.elapsed(); @@ -287,15 +241,6 @@ int HostFNE::run() } // shutdown threads - networkLoop.wait(); - if (m_useAlternatePortForDiagnostics) { - diagNetworkLoop.wait(); - } - - if (m_vtunEnabled) { - vtunLoop.wait(); - } - if (m_network != nullptr) { m_network->close(); delete m_network; @@ -629,6 +574,89 @@ bool HostFNE::createMasterNetwork() return true; } +/* Entry point to master FNE network thread. */ + +void* HostFNE::threadMasterNetwork(void* arg) +{ + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("fne:network-loop"); + HostFNE* fne = static_cast(th->obj); + if (fne == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } + + if (g_killed) { + delete th; + return nullptr; + } + + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + if (fne->m_network != nullptr) { + while (!g_killed) { + fne->m_network->processNetwork(); + Thread::sleep(5U); + } + } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; + } + + return nullptr; +} + +/* Entry point to master FNE diagnostics network thread. */ + +void* HostFNE::threadDiagNetwork(void* arg) +{ + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("fne:diag-network-loop"); + HostFNE* fne = static_cast(th->obj); + if (fne == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } + + if (g_killed) { + delete th; + return nullptr; + } + + if (!fne->m_useAlternatePortForDiagnostics) { + delete th; + return nullptr; + } + + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + if (fne->m_diagNetwork != nullptr) { + while (!g_killed) { + fne->m_diagNetwork->processNetwork(); + Thread::sleep(5U); + } + } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; + } + + return nullptr; +} + /* Initializes peer FNE network connectivity. */ bool HostFNE::createPeerNetworks() @@ -773,6 +801,65 @@ bool HostFNE::createVirtualNetworking() return true; } +/* Entry point to virtual networking thread. */ + +void* HostFNE::threadVirtualNetworking(void* arg) +{ + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("fne:vtun-loop"); + HostFNE* fne = static_cast(th->obj); + if (fne == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } + + if (g_killed) { + delete th; + return nullptr; + } + + if (!fne->m_vtunEnabled) { + delete th; + return nullptr; + } + + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + if (fne->m_tun != nullptr) { + while (!g_killed) { + uint8_t packet[DEFAULT_MTU_SIZE]; + ::memset(packet, 0x00U, DEFAULT_MTU_SIZE); + + ssize_t len = fne->m_tun->read(packet); + if (len > 0) { + switch (fne->m_packetDataMode) { + case PacketDataMode::DMR: + // TODO: not supported yet + break; + + case PacketDataMode::PROJECT25: + fne->m_network->p25TrafficHandler()->processPacketFrame(packet, DEFAULT_MTU_SIZE); + break; + } + } + + Thread::sleep(5U); + } + } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; + } + + return nullptr; +} + /* Processes any peer network traffic. */ void HostFNE::processPeer(network::PeerNetwork* peerNetwork) diff --git a/src/fne/HostFNE.h b/src/fne/HostFNE.h index e5838b01..99974040 100644 --- a/src/fne/HostFNE.h +++ b/src/fne/HostFNE.h @@ -130,6 +130,18 @@ class HOST_SW_API HostFNE { * @returns bool True, if network connectivity was initialized, otherwise false. */ bool createMasterNetwork(); + /** + * @brief Entry point to master FNE network thread. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) + */ + static void* threadMasterNetwork(void* arg); + /** + * @brief Entry point to master FNE diagnostics network thread. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) + */ + static void* threadDiagNetwork(void* arg); /** * @brief Initializes peer FNE network connectivity. * @returns bool True, if network connectivity was initialized, otherwise false. @@ -141,6 +153,12 @@ class HOST_SW_API HostFNE { * @returns bool True, if network connectivity was initialized, otherwise false. */ bool createVirtualNetworking(); + /** + * @brief Entry point to virtual networking thread. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) + */ + static void* threadVirtualNetworking(void* arg); /** * @brief Processes any peer network traffic. diff --git a/src/fne/network/DiagNetwork.cpp b/src/fne/network/DiagNetwork.cpp index 42d61144..0ac444fc 100644 --- a/src/fne/network/DiagNetwork.cpp +++ b/src/fne/network/DiagNetwork.cpp @@ -72,7 +72,6 @@ void DiagNetwork::processNetwork() uint32_t peerId = fneHeader.getPeerId(); NetPacketRequest* req = new NetPacketRequest(); - req->network = m_fneNetwork; req->peerId = peerId; req->address = address; @@ -84,8 +83,7 @@ void DiagNetwork::processNetwork() req->buffer = new uint8_t[length]; ::memcpy(req->buffer, buffer.get(), length); - if (::pthread_create(&req->thread, NULL, threadedNetworkRx, req) != 0) { - LogError(LOG_NET, "Error returned from pthread_create, err: %d", errno); + if (!Thread::runAsThread(m_fneNetwork, threadedNetworkRx, req)) { delete req; return; } @@ -150,16 +148,21 @@ void* DiagNetwork::threadedNetworkRx(void* arg) if (req != nullptr) { ::pthread_detach(req->thread); - FNENetwork* network = req->network; + FNENetwork* network = static_cast(req->obj); + if (network == nullptr) { + delete req; + return nullptr; + } + if (req->length > 0) { uint32_t peerId = req->fneHeader.getPeerId(); uint32_t streamId = req->fneHeader.getStreamId(); std::stringstream peerName; peerName << peerId << ":diag-rx-pckt"; - if (pthread_kill(req->thread, 0) == 0) { - ::pthread_setname_np(req->thread, peerName.str().c_str()); - } +#ifdef _GNU_SOURCE + ::pthread_setname_np(req->thread, peerName.str().c_str()); +#endif // _GNU_SOURCE // update current peer packet sequence and stream ID if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end()) && streamId != 0U) { diff --git a/src/fne/network/DiagNetwork.h b/src/fne/network/DiagNetwork.h index 398c7597..220cab46 100644 --- a/src/fne/network/DiagNetwork.h +++ b/src/fne/network/DiagNetwork.h @@ -22,8 +22,6 @@ #include -#include - // --------------------------------------------------------------------------- // Class Prototypes // --------------------------------------------------------------------------- diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index b9f52ac8..55dfefd1 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -229,7 +229,6 @@ void FNENetwork::processNetwork() uint32_t peerId = fneHeader.getPeerId(); NetPacketRequest* req = new NetPacketRequest(); - req->network = this; req->peerId = peerId; req->address = address; @@ -241,8 +240,7 @@ void FNENetwork::processNetwork() req->buffer = new uint8_t[length]; ::memcpy(req->buffer, buffer.get(), length); - if (::pthread_create(&req->thread, NULL, threadedNetworkRx, req) != 0) { - LogError(LOG_NET, "Error returned from pthread_create, err: %d", errno); + if (!Thread::runAsThread(this, threadedNetworkRx, req)) { delete req; return; } @@ -392,16 +390,21 @@ void* FNENetwork::threadedNetworkRx(void* arg) uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - FNENetwork* network = req->network; + FNENetwork* network = static_cast(req->obj); + if (network == nullptr) { + delete req; + return nullptr; + } + if (req->length > 0) { uint32_t peerId = req->fneHeader.getPeerId(); uint32_t streamId = req->fneHeader.getStreamId(); std::stringstream peerName; peerName << peerId << ":rx-pckt"; - if (pthread_kill(req->thread, 0) == 0) { - ::pthread_setname_np(req->thread, peerName.str().c_str()); - } +#ifdef _GNU_SOURCE + ::pthread_setname_np(req->thread, peerName.str().c_str()); +#endif // _GNU_SOURCE // update current peer packet sequence and stream ID if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end()) && streamId != 0U) { @@ -1314,21 +1317,20 @@ void FNENetwork::setupRepeaterLogin(uint32_t peerId, FNEPeerConnection* connecti void FNENetwork::peerACLUpdate(uint32_t peerId) { ACLUpdateRequest* req = new ACLUpdateRequest(); - req->network = this; req->peerId = peerId; std::stringstream peerName; peerName << peerId << ":acl-update"; - if (::pthread_create(&req->thread, NULL, threadedACLUpdate, req) != 0) { - LogError(LOG_NET, "Error returned from pthread_create, err: %d", errno); + if (!Thread::runAsThread(this, threadedACLUpdate, req)) { delete req; return; } - if (pthread_kill(req->thread, 0) == 0) { - ::pthread_setname_np(req->thread, peerName.str().c_str()); - } + // pthread magic to rename the thread properly +#ifdef _GNU_SOURCE + ::pthread_setname_np(req->thread, peerName.str().c_str()); +#endif // _GNU_SOURCE } /* Helper to send the ACL lists to the specified peer in a separate thread. */ @@ -1339,13 +1341,19 @@ void* FNENetwork::threadedACLUpdate(void* arg) if (req != nullptr) { ::pthread_detach(req->thread); - std::string peerIdentity = req->network->resolvePeerIdentity(req->peerId); + FNENetwork* network = static_cast(req->obj); + if (network == nullptr) { + delete req; + return nullptr; + } + + std::string peerIdentity = network->resolvePeerIdentity(req->peerId); LogInfoEx(LOG_NET, "PEER %u (%s) sending ACL list updates", req->peerId, peerIdentity.c_str()); - req->network->writeWhitelistRIDs(req->peerId); - req->network->writeBlacklistRIDs(req->peerId); - req->network->writeTGIDs(req->peerId); - req->network->writeDeactiveTGIDs(req->peerId); + network->writeWhitelistRIDs(req->peerId); + network->writeBlacklistRIDs(req->peerId); + network->writeTGIDs(req->peerId); + network->writeDeactiveTGIDs(req->peerId); delete req; } diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index c75bda80..10706255 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -39,8 +39,6 @@ #include #include -#include - // --------------------------------------------------------------------------- // Class Prototypes // --------------------------------------------------------------------------- @@ -253,11 +251,8 @@ namespace network * @brief Represents the data required for a peer ACL update request thread. * @ingroup fne_network */ - struct ACLUpdateRequest { - FNENetwork* network; //! Instance of the FNENetwork class. + struct ACLUpdateRequest : thread_t { uint32_t peerId; //! Peer ID for this request. - - pthread_t thread; //! Request Thread Handle. }; // --------------------------------------------------------------------------- @@ -268,8 +263,7 @@ namespace network * @brief Represents the data required for a network packet handler thread. * @ingroup fne_network */ - struct NetPacketRequest { - FNENetwork* network; //! Instance of the FNENetwork class. + struct NetPacketRequest : thread_t { uint32_t peerId; //! Peer ID for this request. sockaddr_storage address; //! IP Address and Port. @@ -278,8 +272,6 @@ namespace network frame::RTPFNEHeader fneHeader; //! RTP FNE Header int length = 0U; //! Length of raw data buffer uint8_t *buffer; //! Raw data buffer - - pthread_t thread; //! Request Thread Handle. }; // --------------------------------------------------------------------------- diff --git a/src/host/Host.Config.cpp b/src/host/Host.Config.cpp index bb5f6de6..b9061b66 100644 --- a/src/host/Host.Config.cpp +++ b/src/host/Host.Config.cpp @@ -81,8 +81,6 @@ bool Host::readParams() int8_t lto = (int8_t)systemConf["localTimeOffset"].as(0); - removeLockFile(); - LogInfo("General Parameters"); if (!udpMasterMode) { LogInfo(" DMR: %s", m_dmrEnabled ? "enabled" : "disabled"); @@ -100,7 +98,6 @@ bool Host::readParams() LogInfo(" Net Mode Hang: %us", m_netModeHang); LogInfo(" Identity: %s", m_identity.c_str()); LogInfo(" Fixed Mode: %s", m_fixedMode ? "yes" : "no"); - LogInfo(" Lock Filename: %s", g_lockFile.c_str()); LogInfo(" Local Time Offset: %dh", lto); yaml::Node systemInfo = systemConf["info"]; diff --git a/src/host/Host.DMR.cpp b/src/host/Host.DMR.cpp index bed9bab4..2d937e41 100644 --- a/src/host/Host.DMR.cpp +++ b/src/host/Host.DMR.cpp @@ -12,6 +12,7 @@ */ #include "Defines.h" #include "Host.h" +#include "HostMain.h" using namespace modem; @@ -20,284 +21,536 @@ using namespace modem; // --------------------------------------------------------------------------- // Macro to start DMR duplex idle transmission (or beacon) -#define START_DMR_DUPLEX_IDLE(x) \ - if (control != nullptr) { \ - if (m_duplex && !m_dmrTXTimer.isRunning()) { \ - m_modem->writeDMRStart(x); \ - m_dmrTXTimer.start(); \ - } \ +#define START_DMR_DUPLEX_IDLE(x) \ + if (host->m_dmr != nullptr) { \ + if (host->m_duplex && !host->m_dmrTXTimer.isRunning()) { \ + host->m_modem->writeDMRStart(x); \ + host->m_dmrTXTimer.start(); \ + } \ } // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- -/* Helper to interrupt a running DMR beacon. */ +/* Entry point to read DMR slot 1 frames from modem Rx queue. */ -void Host::interruptDMRBeacon(dmr::Control* control) +void* Host::threadDMRReader1(void* arg) { - if (control != nullptr) { - if (m_dmrBeaconDurationTimer.isRunning() && !m_dmrBeaconDurationTimer.hasExpired()) { - if (m_dmrTSCCData && !m_dmrCtrlChannel) { - LogDebug(LOG_HOST, "interrupt DMR control, m_state = %u", m_state); - control->setCCHalted(true); - control->setCCRunning(false); - } + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("dmrd:frame1-r"); + Host* host = static_cast(th->obj); + if (host == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); } - m_dmrBeaconDurationTimer.stop(); - } -} - -/* Helper to read DMR slot 1 frames from modem. */ + if (g_killed) { + delete th; + return nullptr; + } -void Host::readFramesDMR1(dmr::Control* control, std::function&& afterReadCallback) -{ - uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U]; - - if (control != nullptr) { - // read DMR slot 1 frames from the modem, and if there is any - // write those frames to the DMR controller - uint32_t len = m_modem->readDMRFrame1(data); - if (len > 0U) { - if (m_state == STATE_IDLE) { - // if the modem is in duplex -- process wakeup CSBKs - if (m_duplex) { - bool ret = control->processWakeup(data); - if (ret) { - m_modeTimer.setTimeout(m_rfModeHang); - setState(STATE_DMR); - - START_DMR_DUPLEX_IDLE(true); - - if (afterReadCallback != nullptr) { - afterReadCallback(); + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + if (host->m_dmr != nullptr) { + while (!g_killed) { + // scope is intentional + { + // ------------------------------------------------------ + // -- Read from Modem Processing -- + // ------------------------------------------------------ + + uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U]; + auto afterReadCallback = [&]() { + if (host->m_dmr != nullptr) { + host->interruptDMRBeacon(); } - } - } - else { - // in simplex directly process slot 1 frames - m_modeTimer.setTimeout(m_rfModeHang); - setState(STATE_DMR); - START_DMR_DUPLEX_IDLE(true); - - control->processFrame(1U, data, len); - if (afterReadCallback != nullptr) { - afterReadCallback(); - } - } - } - else if (m_state == STATE_DMR) { - // if the modem is in duplex, and hasn't started transmitting - // process wakeup CSBKs - if (m_duplex && !m_modem->hasTX()) { - bool ret = control->processWakeup(data); - if (ret) { - m_modem->writeDMRStart(true); - m_dmrTXTimer.start(); - } - } - else { - // process slot 1 frames - bool ret = control->processFrame(1U, data, len); - if (ret) { - if (afterReadCallback != nullptr) { - afterReadCallback(); + // if there is a P25 CC running; halt the CC + if (host->m_p25 != nullptr) { + if (host->m_p25->getCCRunning() && !host->m_p25->getCCHalted()) { + host->interruptP25Control(); + } } - m_modeTimer.start(); - if (m_duplex) - m_dmrTXTimer.start(); + // if there is a NXDN CC running; halt the CC + if (host->m_nxdn != nullptr) { + if (host->m_nxdn->getCCRunning() && !host->m_nxdn->getCCHalted()) { + host->interruptNXDNControl(); + } + } + }; + + if (host->m_dmr != nullptr) { + uint8_t nextLen = host->m_modem->peekDMRFrame1Length(); + if (nextLen > 0U) { + // read DMR slot 1 frames from the modem, and if there is any + // write those frames to the DMR controller + uint32_t len = host->m_modem->readDMRFrame1(data); + if (len > 0U) { + if (host->m_state == STATE_IDLE) { + // if the modem is in duplex -- process wakeup CSBKs + if (host->m_duplex) { + bool ret = host->m_dmr->processWakeup(data); + if (ret) { + host->m_modeTimer.setTimeout(host->m_rfModeHang); + host->setState(STATE_DMR); + + START_DMR_DUPLEX_IDLE(true); + + afterReadCallback(); + } + } + else { + // in simplex directly process slot 1 frames + host->m_modeTimer.setTimeout(host->m_rfModeHang); + host->setState(STATE_DMR); + START_DMR_DUPLEX_IDLE(true); + + host->m_dmr->processFrame(1U, data, len); + + afterReadCallback(); + } + } + else if (host->m_state == STATE_DMR) { + // if the modem is in duplex, and hasn't started transmitting + // process wakeup CSBKs + if (host->m_duplex && !host->m_modem->hasTX()) { + bool ret = host->m_dmr->processWakeup(data); + if (ret) { + host->m_modem->writeDMRStart(true); + host->m_dmrTXTimer.start(); + } + } + else { + // process slot 1 frames + bool ret = host->m_dmr->processFrame(1U, data, len); + if (ret) { + afterReadCallback(); + + host->m_modeTimer.start(); + if (host->m_duplex) + host->m_dmrTXTimer.start(); + } + } + } + else if (host->m_state != HOST_STATE_LOCKOUT) { + LogWarning(LOG_HOST, "DMR modem data received, state = %u", host->m_state); + } + } + } } } - } - else if (m_state != HOST_STATE_LOCKOUT) { - LogWarning(LOG_HOST, "DMR modem data received, state = %u", m_state); + + if (host->m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (host->m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); } } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; } + + return nullptr; } -/* Helper to write DMR slot 1 frames to modem. */ +/* Entry point to write DMR slot 1 frames to modem. */ -void Host::writeFramesDMR1(dmr::Control* control, std::function&& afterWriteCallback) +void* Host::threadDMRWriter1(void* arg) { - uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U]; - - if (control != nullptr) { - // check if there is space on the modem for DMR slot 1 frames, - // if there is read frames from the DMR controller and write it - // to the modem - bool ret = m_modem->hasDMRSpace1(); - if (ret) { - uint32_t nextLen = control->peekFrameLength(1U); - if (m_dmrCtrlChannel) { - if (m_dmrDedicatedTxTestTimer.hasExpired() && !m_dmrDedicatedTxTestTimer.isPaused()) { - m_dmrDedicatedTxTestTimer.pause(); - if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_DMR && (control->getTSCCSlotNo() == 1U) && control->getCCRunning()) { - LogError(LOG_HOST, "DMR dedicated control not transmitting, running = %u, halted = %u, frameLength1 = %u", control->getCCRunning(), control->getCCHalted(), nextLen); - } - } - } - - uint32_t len = control->getFrame(1U, data); - if (len > 0U) { - // if the state is idle; set to DMR, start mode timer and start DMR idle frames - if (m_state == STATE_IDLE) { - m_modeTimer.setTimeout(m_netModeHang); - setState(STATE_DMR); - START_DMR_DUPLEX_IDLE(true); - } - - // if the state is DMR; start DMR idle frames and write DMR slot 1 data - if (m_state == STATE_DMR) { - START_DMR_DUPLEX_IDLE(true); + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("dmrd:frame1-w"); + Host* host = static_cast(th->obj); + if (host == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } - m_modem->writeDMRFrame1(data, len); + if (g_killed) { + delete th; + return nullptr; + } - // if there is no DMR CC running; run the interrupt macro to stop - // any running DMR beacon - if (!control->getCCRunning()) { - interruptDMRBeacon(control); - } + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + StopWatch stopWatch; + stopWatch.start(); + + if (host->m_dmr != nullptr) { + while (!g_killed) { + host->m_dmrTx1WatchdogTimer.start(); + + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + host->m_dmrTx1LoopMS = ms; + + // scope is intentional + { + std::lock_guard lock(m_clockingMutex); + + // ------------------------------------------------------ + // -- Write to Modem Processing -- + // ------------------------------------------------------ + + uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U]; + auto afterWriteCallback = [&]() { + // if there is a P25 CC running; halt the CC + if (host->m_p25 != nullptr) { + if (host->m_p25->getCCRunning() && !host->m_p25->getCCHalted()) { + host->interruptP25Control(); + } + } - if (afterWriteCallback != nullptr) { - afterWriteCallback(); + // if there is a NXDN CC running; halt the CC + if (host->m_nxdn != nullptr) { + if (host->m_nxdn->getCCRunning() && !host->m_nxdn->getCCHalted()) { + host->interruptNXDNControl(); + } + } + }; + + if (host->m_dmr != nullptr) { + // check if there is space on the modem for DMR slot 1 frames, + // if there is read frames from the DMR controller and write it + // to the modem + bool ret = host->m_modem->hasDMRSpace1(); + if (ret) { + uint32_t nextLen = host->m_dmr->peekFrameLength(1U); + if (host->m_dmrCtrlChannel) { + if (host->m_dmrDedicatedTxTestTimer.hasExpired() && !host->m_dmrDedicatedTxTestTimer.isPaused()) { + host->m_dmrDedicatedTxTestTimer.pause(); + if (!host->m_modem->hasTX() && host->m_modem->gotModemStatus() && host->m_state == STATE_DMR && (host->m_dmr->getTSCCSlotNo() == 1U) && host->m_dmr->getCCRunning()) { + LogError(LOG_HOST, "DMR dedicated m_dmr not transmitting, running = %u, halted = %u, frameLength1 = %u", host->m_dmr->getCCRunning(), host->m_dmr->getCCHalted(), nextLen); + } + } + } + + uint32_t len = host->m_dmr->getFrame(1U, data); + if (len > 0U) { + // if the state is idle; set to DMR, start mode timer and start DMR idle frames + if (host->m_state == STATE_IDLE) { + host->m_modeTimer.setTimeout(host->m_netModeHang); + host->setState(STATE_DMR); + START_DMR_DUPLEX_IDLE(true); + } + + // if the state is DMR; start DMR idle frames and write DMR slot 1 data + if (host->m_state == STATE_DMR) { + START_DMR_DUPLEX_IDLE(true); + + host->m_modem->writeDMRFrame1(data, len); + + // if there is no DMR CC running; run the interrupt macro to stop + // any running DMR beacon + if (!host->m_dmr->getCCRunning()) { + host->interruptDMRBeacon(); + } + + afterWriteCallback(); + + host->m_modeTimer.start(); + } + + host->m_lastDstId = host->m_dmr->getLastDstId(1U); + host->m_lastSrcId = host->m_dmr->getLastSrcId(1U); + } + } } - - m_modeTimer.start(); } - m_lastDstId = control->getLastDstId(1U); - m_lastSrcId = control->getLastSrcId(1U); + if (host->m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (host->m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); } } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; } + + return nullptr; } -/* Helper to read DMR slot 2 frames from modem. */ +/* Entry point to read DMR slot 2 frames from modem Rx queue. */ -void Host::readFramesDMR2(dmr::Control* control, std::function&& afterReadCallback) +void* Host::threadDMRReader2(void* arg) { - uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U]; - - if (control != nullptr) { - // read DMR slot 2 frames from the modem, and if there is any - // write those frames to the DMR controller - uint32_t len = m_modem->readDMRFrame2(data); - if (len > 0U) { - if (m_state == STATE_IDLE) { - // if the modem is in duplex -- process wakeup CSBKs - if (m_duplex) { - bool ret = control->processWakeup(data); - if (ret) { - m_modeTimer.setTimeout(m_rfModeHang); - setState(STATE_DMR); - START_DMR_DUPLEX_IDLE(true); - - if (afterReadCallback != nullptr) { - afterReadCallback(); - } - } - } - else { - // in simplex -- directly process slot 2 frames - m_modeTimer.setTimeout(m_rfModeHang); - setState(STATE_DMR); - START_DMR_DUPLEX_IDLE(true); + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("dmrd:frame2-r"); + Host* host = static_cast(th->obj); + if (host == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } - control->processFrame(2U, data, len); + if (g_killed) { + delete th; + return nullptr; + } - if (afterReadCallback != nullptr) { - afterReadCallback(); - } - } - } - else if (m_state == STATE_DMR) { - // if the modem is in duplex, and hasn't started transmitting - // process wakeup CSBKs - if (m_duplex && !m_modem->hasTX()) { - bool ret = control->processWakeup(data); - if (ret) { - m_modem->writeDMRStart(true); - m_dmrTXTimer.start(); - } - } - else { - // process slot 2 frames - bool ret = control->processFrame(2U, data, len); - if (ret) { - if (afterReadCallback != nullptr) { - afterReadCallback(); + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + if (host->m_dmr != nullptr) { + while (!g_killed) { + // scope is intentional + { + // ------------------------------------------------------ + // -- Read from Modem Processing -- + // ------------------------------------------------------ + + uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U]; + auto afterReadCallback = [&]() { + if (host->m_dmr != nullptr) { + host->interruptDMRBeacon(); } - m_modeTimer.start(); - if (m_duplex) - m_dmrTXTimer.start(); + // if there is a P25 CC running; halt the CC + if (host->m_p25 != nullptr) { + if (host->m_p25->getCCRunning() && !host->m_p25->getCCHalted()) { + host->interruptP25Control(); + } + } + + // if there is a NXDN CC running; halt the CC + if (host->m_nxdn != nullptr) { + if (host->m_nxdn->getCCRunning() && !host->m_nxdn->getCCHalted()) { + host->interruptNXDNControl(); + } + } + }; + + if (host->m_dmr != nullptr) { + uint8_t nextLen = host->m_modem->peekDMRFrame2Length(); + if (nextLen > 0U) { + // read DMR slot 2 frames from the modem, and if there is any + // write those frames to the DMR controller + uint32_t len = host->m_modem->readDMRFrame2(data); + if (len > 0U) { + if (host->m_state == STATE_IDLE) { + // if the modem is in duplex -- process wakeup CSBKs + if (host->m_duplex) { + bool ret = host->m_dmr->processWakeup(data); + if (ret) { + host->m_modeTimer.setTimeout(host->m_rfModeHang); + host->setState(STATE_DMR); + START_DMR_DUPLEX_IDLE(true); + + afterReadCallback(); + } + } + else { + // in simplex -- directly process slot 2 frames + host->m_modeTimer.setTimeout(host->m_rfModeHang); + host->setState(STATE_DMR); + START_DMR_DUPLEX_IDLE(true); + + host->m_dmr->processFrame(2U, data, len); + + afterReadCallback(); + } + } + else if (host->m_state == STATE_DMR) { + // if the modem is in duplex, and hasn't started transmitting + // process wakeup CSBKs + if (host->m_duplex && !host->m_modem->hasTX()) { + bool ret = host->m_dmr->processWakeup(data); + if (ret) { + host->m_modem->writeDMRStart(true); + host->m_dmrTXTimer.start(); + } + } + else { + // process slot 2 frames + bool ret = host->m_dmr->processFrame(2U, data, len); + if (ret) { + afterReadCallback(); + + host->m_modeTimer.start(); + if (host->m_duplex) + host->m_dmrTXTimer.start(); + } + } + } + else if (host->m_state != HOST_STATE_LOCKOUT) { + LogWarning(LOG_HOST, "DMR modem data received, state = %u", host->m_state); + } + } + } } } - } - else if (m_state != HOST_STATE_LOCKOUT) { - LogWarning(LOG_HOST, "DMR modem data received, state = %u", m_state); + + if (host->m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (host->m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); } } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; } + + return nullptr; } -/* Helper to write DMR slot 2 frames to modem. */ +/* Entry point to write DMR slot 2 frames to modem. */ -void Host::writeFramesDMR2(dmr::Control* control, std::function&& afterWriteCallback) +void* Host::threadDMRWriter2(void* arg) { - uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U]; - - if (control != nullptr) { - // check if there is space on the modem for DMR slot 2 frames, - // if there is read frames from the DMR controller and write it - // to the modem - bool ret = m_modem->hasDMRSpace2(); - if (ret) { - uint32_t nextLen = control->peekFrameLength(1U); - if (m_dmrCtrlChannel) { - if (m_dmrDedicatedTxTestTimer.hasExpired() && !m_dmrDedicatedTxTestTimer.isPaused()) { - m_dmrDedicatedTxTestTimer.pause(); - if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_DMR && (control->getTSCCSlotNo() == 2U) && control->getCCRunning()) { - LogError(LOG_HOST, "DMR dedicated control not transmitting, running = %u, halted = %u, frameLength2 = %u", control->getCCRunning(), control->getCCHalted(), nextLen); - } - } - } - - uint32_t len = control->getFrame(2U, data); - if (len > 0U) { - // if the state is idle; set to DMR, start mode timer and start DMR idle frames - if (m_state == STATE_IDLE) { - m_modeTimer.setTimeout(m_netModeHang); - setState(STATE_DMR); - START_DMR_DUPLEX_IDLE(true); - } + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("dmrd:frame2-w"); + Host* host = static_cast(th->obj); + if (host == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } - // if the state is DMR; start DMR idle frames and write DMR slot 2 data - if (m_state == STATE_DMR) { - START_DMR_DUPLEX_IDLE(true); + if (g_killed) { + delete th; + return nullptr; + } - m_modem->writeDMRFrame2(data, len); + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + StopWatch stopWatch; + stopWatch.start(); + + if (host->m_dmr != nullptr) { + while (!g_killed) { + host->m_dmrTx2WatchdogTimer.start(); + + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + host->m_dmrTx2LoopMS = ms; + + // scope is intentional + { + std::lock_guard lock(m_clockingMutex); + + // ------------------------------------------------------ + // -- Write to Modem Processing -- + // ------------------------------------------------------ + + uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U]; + auto afterWriteCallback = [&]() { + // if there is a P25 CC running; halt the CC + if (host->m_p25 != nullptr) { + if (host->m_p25->getCCRunning() && !host->m_p25->getCCHalted()) { + host->interruptP25Control(); + } + } - // if there is no DMR CC running; run the interrupt macro to stop - // any running DMR beacon - if (!control->getCCRunning()) { - interruptDMRBeacon(control); + // if there is a NXDN CC running; halt the CC + if (host->m_nxdn != nullptr) { + if (host->m_nxdn->getCCRunning() && !host->m_nxdn->getCCHalted()) { + host->interruptNXDNControl(); + } + } + }; + + if (host->m_dmr != nullptr) { + // check if there is space on the modem for DMR slot 2 frames, + // if there is read frames from the DMR controller and write it + // to the modem + bool ret = host->m_modem->hasDMRSpace2(); + if (ret) { + uint32_t nextLen = host->m_dmr->peekFrameLength(1U); + if (host->m_dmrCtrlChannel) { + if (host->m_dmrDedicatedTxTestTimer.hasExpired() && !host->m_dmrDedicatedTxTestTimer.isPaused()) { + host->m_dmrDedicatedTxTestTimer.pause(); + if (!host->m_modem->hasTX() && host->m_modem->gotModemStatus() && host->m_state == STATE_DMR && (host->m_dmr->getTSCCSlotNo() == 2U) && host->m_dmr->getCCRunning()) { + LogError(LOG_HOST, "DMR dedicated m_dmr not transmitting, running = %u, halted = %u, frameLength2 = %u", host->m_dmr->getCCRunning(), host->m_dmr->getCCHalted(), nextLen); + } + } + } + + uint32_t len = host->m_dmr->getFrame(2U, data); + if (len > 0U) { + // if the state is idle; set to DMR, start mode timer and start DMR idle frames + if (host->m_state == STATE_IDLE) { + host->m_modeTimer.setTimeout(host->m_netModeHang); + host->setState(STATE_DMR); + START_DMR_DUPLEX_IDLE(true); + } + + // if the state is DMR; start DMR idle frames and write DMR slot 2 data + if (host->m_state == STATE_DMR) { + START_DMR_DUPLEX_IDLE(true); + + host->m_modem->writeDMRFrame2(data, len); + + // if there is no DMR CC running; run the interrupt macro to stop + // any running DMR beacon + if (!host->m_dmr->getCCRunning()) { + host->interruptDMRBeacon(); + } + + afterWriteCallback(); + + host->m_modeTimer.start(); + } + + host->m_lastDstId = host->m_dmr->getLastDstId(2U); + host->m_lastSrcId = host->m_dmr->getLastSrcId(2U); + } + } } + } - if (afterWriteCallback != nullptr) { - afterWriteCallback(); - } + if (host->m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (host->m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); + } + } - m_modeTimer.start(); - } + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; + } + + return nullptr; +} + +/* Helper to interrupt a running DMR beacon. */ - m_lastDstId = control->getLastDstId(2U); - m_lastSrcId = control->getLastSrcId(2U); +void Host::interruptDMRBeacon() +{ + if (m_dmr != nullptr) { + if (m_dmrBeaconDurationTimer.isRunning() && !m_dmrBeaconDurationTimer.hasExpired()) { + if (m_dmrTSCCData && !m_dmrCtrlChannel) { + LogDebug(LOG_HOST, "interrupt DMR m_dmr, m_state = %u", m_state); + m_dmr->setCCHalted(true); + m_dmr->setCCRunning(false); } } + + m_dmrBeaconDurationTimer.stop(); } } diff --git a/src/host/Host.NXDN.cpp b/src/host/Host.NXDN.cpp index 626d4723..81d73735 100644 --- a/src/host/Host.NXDN.cpp +++ b/src/host/Host.NXDN.cpp @@ -12,6 +12,7 @@ */ #include "Defines.h" #include "Host.h" +#include "HostMain.h" using namespace modem; @@ -19,99 +20,219 @@ using namespace modem; // Private Class Members // --------------------------------------------------------------------------- -/* Helper to interrupt a running NXDN control channel. */ +/* Entry point to read NXDN frames from modem Rx queue. */ -void Host::interruptNXDNControl(nxdn::Control* control) +void* Host::threadNXDNReader(void* arg) { - if (control != nullptr) { - LogDebug(LOG_HOST, "interrupt NXDN control, m_state = %u", m_state); - control->setCCHalted(true); - - if (m_nxdnBcastDurationTimer.isRunning() && !m_nxdnBcastDurationTimer.isPaused()) { - m_nxdnBcastDurationTimer.pause(); + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("nxdd:frame-r"); + Host* host = static_cast(th->obj); + if (host == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); } - } -} -/* Helper to read NXDN frames from modem. */ + if (g_killed) { + delete th; + return nullptr; + } -void Host::readFramesNXDN(nxdn::Control* control, std::function&& afterReadCallback) -{ - uint8_t data[NXDDEF::NXDN_FRAME_LENGTH_BYTES * 2U]; - - if (control != nullptr) { - uint32_t len = m_modem->readNXDNFrame(data); - if (len > 0U) { - if (m_state == STATE_IDLE) { - // process NXDN frames - bool ret = control->processFrame(data, len); - if (ret) { - m_modeTimer.setTimeout(m_rfModeHang); - setState(STATE_NXDN); - - if (afterReadCallback != nullptr) { - afterReadCallback(); + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + if (host->m_nxdn != nullptr) { + while (!g_killed) { + // scope is intentional + { + // ------------------------------------------------------ + // -- Read from Modem Processing -- + // ------------------------------------------------------ + + uint8_t data[NXDDEF::NXDN_FRAME_LENGTH_BYTES * 2U]; + auto afterReadCallback = [&]() { + if (host->m_dmr != nullptr) { + host->interruptDMRBeacon(); + } + + // if there is a P25 CC running; halt the CC + if (host->m_p25 != nullptr) { + if (host->m_p25->getCCRunning() && !host->m_p25->getCCHalted()) { + host->interruptP25Control(); + } + } + }; + + if (host->m_nxdn != nullptr) { + uint8_t nextLen = host->m_modem->peekNXDNFrameLength(); + if (nextLen > 0U) { + uint32_t len = host->m_modem->readNXDNFrame(data); + if (len > 0U) { + if (host->m_state == STATE_IDLE) { + // process NXDN frames + bool ret = host->m_nxdn->processFrame(data, len); + if (ret) { + host->m_modeTimer.setTimeout(host->m_rfModeHang); + host->setState(STATE_NXDN); + + afterReadCallback(); + } + } + else if (host->m_state == STATE_NXDN) { + // process NXDN frames + bool ret = host->m_nxdn->processFrame(data, len); + if (ret) { + host->m_modeTimer.start(); + } + } + else if (host->m_state != HOST_STATE_LOCKOUT) { + LogWarning(LOG_HOST, "NXDN modem data received, state = %u", host->m_state); + } + } + } } } - } - else if (m_state == STATE_NXDN) { - // process NXDN frames - bool ret = control->processFrame(data, len); - if (ret) { - m_modeTimer.start(); - } - } - else if (m_state != HOST_STATE_LOCKOUT) { - LogWarning(LOG_HOST, "NXDN modem data received, state = %u", m_state); + + if (host->m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (host->m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); } } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; } + + return nullptr; } -/* Helper to write NXDN frames to modem. */ +/* Entry point to write NXDN frames to modem. */ -void Host::writeFramesNXDN(nxdn::Control* control, std::function&& afterWriteCallback) +void* Host::threadNXDNWriter(void* arg) { - uint8_t data[NXDDEF::NXDN_FRAME_LENGTH_BYTES * 2U]; - - // check if there is space on the modem for NXDN frames, - // if there is read frames from the NXDN controller and write it - // to the modem - if (control != nullptr) { - bool ret = m_modem->hasNXDNSpace(); - if (ret) { - uint32_t nextLen = control->peekFrameLength(); - if (m_nxdnCtrlChannel) { - if (m_nxdnDedicatedTxTestTimer.hasExpired() && !m_nxdnDedicatedTxTestTimer.isPaused()) { - m_nxdnDedicatedTxTestTimer.pause(); - if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_NXDN && control->getCCRunning()) { - LogError(LOG_HOST, "NXDN dedicated control stopped transmitting, running = %u, halted = %u, frameLength = %u", control->getCCRunning(), control->getCCHalted(), nextLen); + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("nxdd:frame-w"); + Host* host = static_cast(th->obj); + if (host == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } + + if (g_killed) { + delete th; + return nullptr; + } + + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + StopWatch stopWatch; + stopWatch.start(); + + if (host->m_nxdn != nullptr) { + while (!g_killed) { + host->m_nxdnTxWatchdogTimer.start(); + + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + host->m_nxdnTxLoopMS = ms; + + // scope is intentional + { + std::lock_guard lock(m_clockingMutex); + + // ------------------------------------------------------ + // -- Write to Modem Processing -- + // ------------------------------------------------------ + + uint8_t data[NXDDEF::NXDN_FRAME_LENGTH_BYTES * 2U]; + auto afterWriteCallback = [&]() { + if (host->m_dmr != nullptr) { + host->interruptDMRBeacon(); + } + + // if there is a P25 CC running; halt the CC + if (host->m_p25 != nullptr) { + if (host->m_p25->getCCRunning() && !host->m_p25->getCCHalted()) { + host->interruptP25Control(); + } + } + }; + + // check if there is space on the modem for NXDN frames, + // if there is read frames from the NXDN controller and write it + // to the modem + if (host->m_nxdn != nullptr) { + bool ret = host->m_modem->hasNXDNSpace(); + if (ret) { + uint32_t nextLen = host->m_nxdn->peekFrameLength(); + if (host->m_nxdnCtrlChannel) { + if (host->m_nxdnDedicatedTxTestTimer.hasExpired() && !host->m_nxdnDedicatedTxTestTimer.isPaused()) { + host->m_nxdnDedicatedTxTestTimer.pause(); + if (!host->m_modem->hasTX() && host->m_modem->gotModemStatus() && host->m_state == STATE_NXDN && host->m_nxdn->getCCRunning()) { + LogError(LOG_HOST, "NXDN dedicated m_nxdn stopped transmitting, running = %u, halted = %u, frameLength = %u", host->m_nxdn->getCCRunning(), host->m_nxdn->getCCHalted(), nextLen); + } + } + } + + uint32_t len = host->m_nxdn->getFrame(data); + if (len > 0U) { + // if the state is idle; set to NXDN and start mode timer + if (host->m_state == STATE_IDLE) { + host->m_modeTimer.setTimeout(host->m_netModeHang); + host->setState(STATE_NXDN); + } + + // if the state is NXDN; write NXDN data + if (host->m_state == STATE_NXDN) { + host->m_modem->writeNXDNFrame(data, len); + + afterWriteCallback(); + + host->m_modeTimer.start(); + } + + host->m_lastDstId = host->m_nxdn->getLastDstId(); + host->m_lastSrcId = host->m_nxdn->getLastSrcId(); + } + } } } + + if (host->m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (host->m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); } + } - uint32_t len = control->getFrame(data); - if (len > 0U) { - // if the state is idle; set to NXDN and start mode timer - if (m_state == STATE_IDLE) { - m_modeTimer.setTimeout(m_netModeHang); - setState(STATE_NXDN); - } + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; + } - // if the state is NXDN; write NXDN data - if (m_state == STATE_NXDN) { - m_modem->writeNXDNFrame(data, len); + return nullptr; +} - if (afterWriteCallback != nullptr) { - afterWriteCallback(); - } +/* Helper to interrupt a running NXDN control channel. */ - m_modeTimer.start(); - } +void Host::interruptNXDNControl() +{ + if (m_nxdn != nullptr) { + LogDebug(LOG_HOST, "interrupt NXDN m_nxdn, m_state = %u", m_state); + m_nxdn->setCCHalted(true); - m_lastDstId = control->getLastDstId(); - m_lastSrcId = control->getLastSrcId(); - } + if (m_nxdnBcastDurationTimer.isRunning() && !m_nxdnBcastDurationTimer.isPaused()) { + m_nxdnBcastDurationTimer.pause(); } } } diff --git a/src/host/Host.P25.cpp b/src/host/Host.P25.cpp index 3a6b94b0..186e53f0 100644 --- a/src/host/Host.P25.cpp +++ b/src/host/Host.P25.cpp @@ -20,185 +20,303 @@ using namespace modem; // Private Class Members // --------------------------------------------------------------------------- -/* Helper to interrupt a running P25 control channel. */ +/* Entry point to read P25 frames from modem Rx queue. */ -void Host::interruptP25Control(p25::Control* control) +void* Host::threadP25Reader(void* arg) { - if (control != nullptr) { - LogDebug(LOG_HOST, "interrupt P25 control, m_state = %u", m_state); - control->setCCHalted(true); + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); - if (m_p25BcastDurationTimer.isRunning() && !m_p25BcastDurationTimer.isPaused()) { - m_p25BcastDurationTimer.pause(); + std::string threadName("p25d:frame-r"); + Host* host = static_cast(th->obj); + if (host == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); } - } -} -/* Helper to read P25 frames from modem. */ + if (g_killed) { + delete th; + return nullptr; + } -void Host::readFramesP25(p25::Control* control, std::function&& afterReadCallback) -{ - uint8_t data[P25DEF::P25_PDU_FRAME_LENGTH_BYTES * 2U]; - - // read P25 frames from modem, and if there are frames - // write those frames to the P25 controller - if (control != nullptr) { - uint32_t len = m_modem->readP25Frame(data); - if (len > 0U) { - if (m_state == STATE_IDLE) { - // process P25 frames - bool ret = control->processFrame(data, len); - if (ret) { - m_modeTimer.setTimeout(m_rfModeHang); - setState(STATE_P25); - - if (afterReadCallback != nullptr) { - afterReadCallback(); - } - } - else { - ret = control->writeRF_VoiceEnd(); - if (ret) { - if (afterReadCallback != nullptr) { - afterReadCallback(); - } + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE - if (m_state == STATE_IDLE) { - m_modeTimer.setTimeout(m_rfModeHang); - setState(STATE_P25); - } + if (host->m_p25 != nullptr) { + while (!g_killed) { + // scope is intentional + { + // ------------------------------------------------------ + // -- Read from Modem Processing -- + // ------------------------------------------------------ - if (m_state == STATE_P25) { - m_modeTimer.start(); + uint8_t data[P25DEF::P25_PDU_FRAME_LENGTH_BYTES * 2U]; + auto afterReadCallback = [&]() { + if (host->m_dmr != nullptr) { + host->interruptDMRBeacon(); } - // if the modem is in duplex -- handle P25 CC burst control - if (m_duplex) { - if (m_p25BcastDurationTimer.isPaused() && !control->getCCHalted()) { - m_p25BcastDurationTimer.resume(); + // if there is a NXDN CC running; halt the CC + if (host->m_nxdn != nullptr) { + if (host->m_nxdn->getCCRunning() && !host->m_nxdn->getCCHalted()) { + host->interruptNXDNControl(); } + } + }; - if (control->getCCHalted()) { - control->setCCHalted(false); - } + // read P25 frames from modem, and if there are frames + // write those frames to the P25 controller + if (host->m_p25 != nullptr) { + uint8_t nextLen = host->m_modem->peekP25FrameLength(); + if (nextLen > 0U) { + uint32_t len = host->m_modem->readP25Frame(data); + if (len > 0U) { + if (host->m_state == STATE_IDLE) { + // process P25 frames + bool ret = host->m_p25->processFrame(data, len); + if (ret) { + host->m_modeTimer.setTimeout(host->m_rfModeHang); + host->setState(STATE_P25); - if (g_fireP25Control) { - m_modeTimer.stop(); + afterReadCallback(); + } + else { + ret = host->m_p25->writeRF_VoiceEnd(); + if (ret) { + afterReadCallback(); + + if (host->m_state == STATE_IDLE) { + host->m_modeTimer.setTimeout(host->m_rfModeHang); + host->setState(STATE_P25); + } + + if (host->m_state == STATE_P25) { + host->m_modeTimer.start(); + } + + // if the modem is in duplex -- handle P25 CC burst m_p25 + if (host->m_duplex) { + if (host->m_p25BcastDurationTimer.isPaused() && !host->m_p25->getCCHalted()) { + host->m_p25BcastDurationTimer.resume(); + } + + if (host->m_p25->getCCHalted()) { + host->m_p25->setCCHalted(false); + } + + if (g_fireP25Control) { + host->m_modeTimer.stop(); + } + } + else { + host->m_p25BcastDurationTimer.stop(); + } + } + } + } + else if (host->m_state == STATE_P25) { + // process P25 frames + bool ret = host->m_p25->processFrame(data, len); + if (ret) { + host->m_modeTimer.start(); + } + else { + ret = host->m_p25->writeRF_VoiceEnd(); + if (ret) { + host->m_modeTimer.start(); + } + } + } + else if (host->m_state != HOST_STATE_LOCKOUT) { + LogWarning(LOG_HOST, "P25 modem data received, state = %u", host->m_state); + } } } - else { - m_p25BcastDurationTimer.stop(); - } } } - } - else if (m_state == STATE_P25) { - // process P25 frames - bool ret = control->processFrame(data, len); - if (ret) { - m_modeTimer.start(); - } - else { - ret = control->writeRF_VoiceEnd(); - if (ret) { - m_modeTimer.start(); - } - } - } - else if (m_state != HOST_STATE_LOCKOUT) { - LogWarning(LOG_HOST, "P25 modem data received, state = %u", m_state); + + if (host->m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (host->m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); } } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; } + + return nullptr; } -/* Helper to write P25 frames to modem. */ +/* Entry point to write P25 frames to modem. */ -void Host::writeFramesP25(p25::Control* control, std::function&& afterWriteCallback) +void* Host::threadP25Writer(void* arg) { - uint8_t data[P25DEF::P25_PDU_FRAME_LENGTH_BYTES * 2U]; - - // check if there is space on the modem for P25 frames, - // if there is read frames from the P25 controller and write it - // to the modem - if (control != nullptr) { - uint8_t nextLen = control->peekFrameLength(); - if (m_p25CtrlChannel) { - if (m_p25DedicatedTxTestTimer.hasExpired() && !m_p25DedicatedTxTestTimer.isPaused()) { - m_p25DedicatedTxTestTimer.pause(); - if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_P25 && control->getCCRunning()) { - LogError(LOG_HOST, "P25 dedicated control not transmitting, running = %u, halted = %u, frameLength = %u", control->getCCRunning(), control->getCCHalted(), nextLen); - } - } + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("p25d:frame-w"); + Host* host = static_cast(th->obj); + if (host == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); } - if (nextLen > 0U) { - bool ret = m_modem->hasP25Space(nextLen); - if (ret) { - uint32_t len = control->getFrame(data); - if (len > 0U) { - // if the state is idle; set to P25 and start mode timer - if (m_state == STATE_IDLE) { - m_modeTimer.setTimeout(m_netModeHang); - setState(STATE_P25); - } + if (g_killed) { + delete th; + return nullptr; + } + + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + StopWatch stopWatch; + stopWatch.start(); - // if the state is P25; write P25 frame data - if (m_state == STATE_P25) { - m_modem->writeP25Frame(data, len); + if (host->m_p25 != nullptr) { + while (!g_killed) { + host->m_p25TxWatchdogTimer.start(); - if (afterWriteCallback != nullptr) { - afterWriteCallback(); + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + host->m_p25TxLoopMS = ms; + + // scope is intentional + { + std::lock_guard lock(m_clockingMutex); + + // ------------------------------------------------------ + // -- Write to Modem Processing -- + // ------------------------------------------------------ + + uint8_t data[P25DEF::P25_PDU_FRAME_LENGTH_BYTES * 2U]; + auto afterWriteCallback = [&]() { + if (host->m_dmr != nullptr) { + host->interruptDMRBeacon(); } - m_modeTimer.start(); - } + // if there is a NXDN CC running; halt the CC + if (host->m_nxdn != nullptr) { + if (host->m_nxdn->getCCRunning() && !host->m_nxdn->getCCHalted()) { + host->interruptNXDNControl(); + } + } + }; - m_lastDstId = control->getLastDstId(); - m_lastSrcId = control->getLastSrcId(); - } - else { - nextLen = 0U; - } - } - } + // check if there is space on the modem for P25 frames, + // if there is read frames from the P25 controller and write it + // to the modem + if (host->m_p25 != nullptr) { + uint8_t nextLen = host->m_p25->peekFrameLength(); + if (host->m_p25CtrlChannel) { + if (host->m_p25DedicatedTxTestTimer.hasExpired() && !host->m_p25DedicatedTxTestTimer.isPaused()) { + host->m_p25DedicatedTxTestTimer.pause(); + if (!host->m_modem->hasTX() && host->m_modem->gotModemStatus() && host->m_state == STATE_P25 && host->m_p25->getCCRunning()) { + LogError(LOG_HOST, "P25 dedicated m_p25 not transmitting, running = %u, halted = %u, frameLength = %u", host->m_p25->getCCRunning(), host->m_p25->getCCHalted(), nextLen); + } + } + } - if (nextLen == 0U) { - // if we have no P25 data, and we're either idle or P25 state, check if we - // need to be starting the CC running flag or writing end of voice call data - if (m_state == STATE_IDLE || m_state == STATE_P25) { - if (control->getCCHalted()) { - control->setCCHalted(false); - } + if (nextLen > 0U) { + bool ret = host->m_modem->hasP25Space(nextLen); + if (ret) { + uint32_t len = host->m_p25->getFrame(data); + if (len > 0U) { + // if the state is idle; set to P25 and start mode timer + if (host->m_state == STATE_IDLE) { + host->m_modeTimer.setTimeout(host->m_netModeHang); + host->setState(STATE_P25); + } - // write end of voice if necessary - bool ret = control->writeRF_VoiceEnd(); - if (ret) { - if (m_state == STATE_IDLE) { - m_modeTimer.setTimeout(m_netModeHang); - setState(STATE_P25); - } + // if the state is P25; write P25 frame data + if (host->m_state == STATE_P25) { + host->m_modem->writeP25Frame(data, len); + + afterWriteCallback(); + + host->m_modeTimer.start(); + } + + host->m_lastDstId = host->m_p25->getLastDstId(); + host->m_lastSrcId = host->m_p25->getLastSrcId(); + } + else { + nextLen = 0U; + } + } + } + + if (nextLen == 0U) { + // if we have no P25 data, and we're either idle or P25 state, check if we + // need to be starting the CC running flag or writing end of voice call data + if (host->m_state == STATE_IDLE || host->m_state == STATE_P25) { + if (host->m_p25->getCCHalted()) { + host->m_p25->setCCHalted(false); + } + + // write end of voice if necessary + bool ret = host->m_p25->writeRF_VoiceEnd(); + if (ret) { + if (host->m_state == STATE_IDLE) { + host->m_modeTimer.setTimeout(host->m_netModeHang); + host->setState(STATE_P25); + } + + if (host->m_state == STATE_P25) { + host->m_modeTimer.start(); + } + } + } + } - if (m_state == STATE_P25) { - m_modeTimer.start(); + // if the modem is in duplex -- handle P25 CC burst + if (host->m_duplex) { + if (host->m_p25BcastDurationTimer.isPaused() && !host->m_p25->getCCHalted()) { + host->m_p25BcastDurationTimer.resume(); + } + + if (host->m_p25->getCCHalted()) { + host->m_p25->setCCHalted(false); + } + + if (g_fireP25Control) { + host->m_modeTimer.stop(); + } + } } } + + if (host->m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (host->m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); } } - // if the modem is in duplex -- handle P25 CC burst control - if (m_duplex) { - if (m_p25BcastDurationTimer.isPaused() && !control->getCCHalted()) { - m_p25BcastDurationTimer.resume(); - } + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; + } - if (control->getCCHalted()) { - control->setCCHalted(false); - } + return nullptr; +} - if (g_fireP25Control) { - m_modeTimer.stop(); - } +/* Helper to interrupt a running P25 control channel. */ + +void Host::interruptP25Control() +{ + if (m_p25 != nullptr) { + LogDebug(LOG_HOST, "interrupt P25 m_p25, m_state = %u", m_state); + m_p25->setCCHalted(true); + + if (m_p25BcastDurationTimer.isRunning() && !m_p25BcastDurationTimer.isPaused()) { + m_p25BcastDurationTimer.pause(); } } } diff --git a/src/host/Host.cpp b/src/host/Host.cpp index c75f8702..afdba2a8 100644 --- a/src/host/Host.cpp +++ b/src/host/Host.cpp @@ -15,7 +15,6 @@ #include "common/Log.h" #include "common/StopWatch.h" #include "common/Thread.h" -#include "common/ThreadFunc.h" #include "common/Utils.h" #include "remote/RESTClient.h" #include "host/Host.h" @@ -29,10 +28,19 @@ using namespace lookups; #include #include #include -#include +#include #include +// --------------------------------------------------------------------------- +// Static Class Members +// --------------------------------------------------------------------------- + +std::mutex Host::m_clockingMutex; + +uint8_t Host::m_activeTickDelay = 5U; +uint8_t Host::m_idleTickDelay = 5U; + // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- @@ -130,11 +138,12 @@ Host::Host(const std::string& confFile) : m_mainLoopWatchdogTimer(1000U, 1U), m_adjSiteLoopMS(0U), m_adjSiteLoopWatchdogTimer(1000U, 1U), - m_activeTickDelay(5U), - m_idleTickDelay(5U), m_restAddress("0.0.0.0"), m_restPort(REST_API_DEFAULT_PORT), - m_RESTAPI(nullptr) + m_RESTAPI(nullptr), + m_dmr(nullptr), + m_p25(nullptr), + m_nxdn(nullptr) { /* stub */ } @@ -232,7 +241,7 @@ int Host::run() // is the modem slaved to a remote DVM host? if (m_modemRemote) { - ::LogInfoEx(LOG_HOST, "Host is up and running in remote modem mode"); + ::LogInfoEx(LOG_HOST, "[ OK ] Host is running in remote modem mode"); StopWatch stopWatch; stopWatch.start(); @@ -348,8 +357,6 @@ int Host::run() // initialize DMR Timer dmrBeaconIntervalTimer(1000U); - - std::unique_ptr dmr = nullptr; LogInfo("DMR Parameters"); LogInfo(" Enabled: %s", m_dmrEnabled ? "yes" : "no"); if (m_dmrEnabled) { @@ -417,14 +424,14 @@ int Host::run() g_fireDMRBeacon = true; } - dmr = std::make_unique(m_authoritative, m_dmrColorCode, callHang, m_dmrQueueSizeBytes, + m_dmr = std::make_unique(m_authoritative, m_dmrColorCode, callHang, m_dmrQueueSizeBytes, embeddedLCOnly, dumpTAData, m_timeout, m_rfTalkgroupHang, m_modem, m_network, m_duplex, m_channelLookup, m_ridLookup, m_tidLookup, m_idenTable, rssi, jitter, dmrDumpDataPacket, dmrRepeatDataPacket, dmrDumpCsbkData, dmrDebug, dmrVerbose); - dmr->setOptions(m_conf, m_supervisor, m_controlChData, m_dmrNetId, m_siteId, m_channelId, + m_dmr->setOptions(m_conf, m_supervisor, m_controlChData, m_dmrNetId, m_siteId, m_channelId, m_channelNo, true); if (dmrCtrlChannel) { - dmr->setCCRunning(true); + m_dmr->setCCRunning(true); } m_dmrTXTimer.setTimeout(txHang); @@ -439,8 +446,6 @@ int Host::run() // initialize P25 Timer p25BcastIntervalTimer(1000U); - - std::unique_ptr p25 = nullptr; LogInfo("P25 Parameters"); LogInfo(" Enabled: %s", m_p25Enabled ? "yes" : "no"); if (m_p25Enabled) { @@ -488,14 +493,14 @@ int Host::run() } } - p25 = std::make_unique(m_authoritative, m_p25NAC, callHang, m_p25QueueSizeBytes, m_modem, + m_p25 = std::make_unique(m_authoritative, m_p25NAC, callHang, m_p25QueueSizeBytes, m_modem, m_network, m_timeout, m_rfTalkgroupHang, m_duplex, m_channelLookup, m_ridLookup, m_tidLookup, m_idenTable, rssi, p25DumpDataPacket, p25RepeatDataPacket, p25DumpTsbkData, p25Debug, p25Verbose); - p25->setOptions(m_conf, m_supervisor, m_cwCallsign, m_controlChData, + m_p25->setOptions(m_conf, m_supervisor, m_cwCallsign, m_controlChData, m_p25NetId, m_sysId, m_p25RfssId, m_siteId, m_channelId, m_channelNo, true); if (p25CtrlChannel) { - p25->setCCRunning(true); + m_p25->setCCRunning(true); } if (p25Verbose) { @@ -508,8 +513,6 @@ int Host::run() // initialize NXDN Timer nxdnBcastIntervalTimer(1000U); - - std::unique_ptr nxdn = nullptr; LogInfo("NXDN Parameters"); LogInfo(" Enabled: %s", m_nxdnEnabled ? "yes" : "no"); if (m_nxdnEnabled) { @@ -550,14 +553,14 @@ int Host::run() } } - nxdn = std::make_unique(m_authoritative, m_nxdnRAN, callHang, m_nxdnQueueSizeBytes, + m_nxdn = std::make_unique(m_authoritative, m_nxdnRAN, callHang, m_nxdnQueueSizeBytes, m_timeout, m_rfTalkgroupHang, m_modem, m_network, m_duplex, m_channelLookup, m_ridLookup, m_tidLookup, m_idenTable, rssi, nxdnDumpRcchData, nxdnDebug, nxdnVerbose); - nxdn->setOptions(m_conf, m_supervisor, m_cwCallsign, m_controlChData, m_siteId, + m_nxdn->setOptions(m_conf, m_supervisor, m_cwCallsign, m_controlChData, m_siteId, m_sysId, m_channelId, m_channelNo, true); if (nxdnCtrlChannel) { - nxdn->setCCRunning(true); + m_nxdn->setCCRunning(true); } if (nxdnVerbose) { @@ -568,6 +571,10 @@ int Host::run() } } + /* + ** Error Condition Checking + */ + if (!m_dmrEnabled && !m_p25Enabled && !m_nxdnEnabled) { ::LogError(LOG_HOST, "No protocols enabled? DMR, P25 and/or NXDN must be enabled!"); g_killed = true; @@ -654,13 +661,13 @@ int Host::run() if (!g_killed) { // fixed mode will force a state change if (m_fixedMode) { - if (dmr != nullptr) + if (m_dmr != nullptr) setState(STATE_DMR); - if (p25 != nullptr) + if (m_p25 != nullptr) setState(STATE_P25); - if (nxdn != nullptr) + if (m_nxdn != nullptr) setState(STATE_NXDN); } else { @@ -683,10 +690,15 @@ int Host::run() } if (m_RESTAPI != nullptr) { - m_RESTAPI->setProtocols(dmr.get(), p25.get(), nxdn.get()); + m_RESTAPI->setProtocols(m_dmr.get(), m_p25.get(), m_nxdn.get()); } - ::LogInfoEx(LOG_HOST, "Host is performing late initialization and warmup"); + ::LogInfoEx(LOG_HOST, "[WAIT] Host is performing late initialization and warmup"); + + m_modem->clearNXDNFrame(); + m_modem->clearP25Frame(); + m_modem->clearDMRFrame2(); + m_modem->clearDMRFrame1(); // perform early pumping of the modem clock (this is so the DSP has time to setup its buffers), // and clock the network (so it may perform early connect) @@ -725,437 +737,86 @@ int Host::run() } } - ::LogInfoEx(LOG_HOST, "Host is up and running"); stopWatch.start(); } bool hasTxShutdown = false; - static std::mutex clockingMutex; // Macro to start DMR duplex idle transmission (or beacon) #define START_DMR_DUPLEX_IDLE(x) \ - if (dmr != nullptr) { \ + if (m_dmr != nullptr) { \ if (m_duplex) { \ m_modem->writeDMRStart(x); \ m_dmrTXTimer.start(); \ } \ } - /** Watchdog */ - ThreadFunc watchdogThread([&, this]() { - if (g_killed) - return; + /* + ** Initialize Threads + */ - StopWatch stopWatch; - stopWatch.start(); - - LogDebug(LOG_HOST, "started watchdog"); - while (!g_killed) { - uint32_t ms = stopWatch.elapsed(); - stopWatch.start(); - - // scope is intentional - { - /** Digital Mobile Radio */ - if (dmr != nullptr) { - if (m_dmrTx1WatchdogTimer.isRunning()) - m_dmrTx1WatchdogTimer.clock(ms); - if (m_dmrTx1WatchdogTimer.isRunning() && m_dmrTx1WatchdogTimer.hasExpired() && !m_dmrTx1WatchdogTimer.isPaused()) { - m_dmrTx1WatchdogTimer.pause(); - LogError(LOG_HOST, "DMR, slot 1 frame processor hung >%us, ms = %u", m_dmrTx1WatchdogTimer.getTimeout(), m_dmrTx1LoopMS); - } - - if (m_dmrTx2WatchdogTimer.isRunning()) - m_dmrTx2WatchdogTimer.clock(ms); - if (m_dmrTx2WatchdogTimer.isRunning() && m_dmrTx2WatchdogTimer.hasExpired() && !m_dmrTx2WatchdogTimer.isPaused()) { - m_dmrTx2WatchdogTimer.pause(); - LogError(LOG_HOST, "DMR, slot 2 frame processor hung >%us, ms = %u", m_dmrTx2WatchdogTimer.getTimeout(), m_dmrTx2LoopMS); - } - } - - /** Project 25 */ - if (p25 != nullptr) { - if (m_p25TxWatchdogTimer.isRunning()) - m_p25TxWatchdogTimer.clock(ms); - if (m_p25TxWatchdogTimer.isRunning() && m_p25TxWatchdogTimer.hasExpired() && !m_p25TxWatchdogTimer.isPaused()) { - m_p25TxWatchdogTimer.pause(); - LogError(LOG_HOST, "P25, frame processor hung >%us, ms = %u", m_p25TxWatchdogTimer.getTimeout(), m_p25TxLoopMS); - } - } - - /** Next Generation Digital Narrowband */ - if (nxdn != nullptr) { - if (m_nxdnTxWatchdogTimer.isRunning()) - m_nxdnTxWatchdogTimer.clock(ms); - if (m_nxdnTxWatchdogTimer.isRunning() && m_nxdnTxWatchdogTimer.hasExpired() && !m_nxdnTxWatchdogTimer.isPaused()) { - m_nxdnTxWatchdogTimer.pause(); - LogError(LOG_HOST, "NXDN, frame processor hung >%us, ms = %u", m_nxdnTxWatchdogTimer.getTimeout(), m_nxdnTxLoopMS); - } - } - } - - // scope is intentional - { - if (m_mainLoopWatchdogTimer.isRunning()) - m_mainLoopWatchdogTimer.clock(ms); - if (m_mainLoopWatchdogTimer.isRunning() && m_mainLoopWatchdogTimer.hasExpired() && !m_mainLoopWatchdogTimer.isPaused()) { - m_mainLoopWatchdogTimer.pause(); - LogError(LOG_HOST, "main processor hung >%us, stage = %u, ms = %u", m_mainLoopWatchdogTimer.getTimeout(), m_mainLoopStage, m_mainLoopMS); - } - - if (m_adjSiteLoopWatchdogTimer.isRunning()) - m_adjSiteLoopWatchdogTimer.clock(ms); - if (m_adjSiteLoopWatchdogTimer.isRunning() && m_adjSiteLoopWatchdogTimer.hasExpired() && !m_adjSiteLoopWatchdogTimer.isPaused()) { - m_adjSiteLoopWatchdogTimer.pause(); - LogError(LOG_HOST, "adj. site update hung >%us, ms = %u", m_adjSiteLoopWatchdogTimer.getTimeout(), m_adjSiteLoopMS); - } - } + /** Watchdog */ + if (!Thread::runAsThread(this, threadWatchdog)) + return EXIT_FAILURE; - if (m_state != STATE_IDLE) - Thread::sleep(m_activeTickDelay); - if (m_state == STATE_IDLE) - Thread::sleep(m_idleTickDelay); - } - }); - watchdogThread.run(); - watchdogThread.setName("host:watchdog"); + /** Modem */ + if (!Thread::runAsThread(this, threadModem)) + return EXIT_FAILURE; /** Digital Mobile Radio Frame Processor */ - ThreadFunc dmrFrame1WriteThread([&, this]() { - if (g_killed) - return; - - StopWatch stopWatch; - stopWatch.start(); - - if (dmr != nullptr) { - LogDebug(LOG_HOST, "DMR, started slot 1 frame processor (modem write)"); - while (!g_killed) { - m_dmrTx1WatchdogTimer.start(); - - uint32_t ms = stopWatch.elapsed(); - stopWatch.start(); - m_dmrTx1LoopMS = ms; - - // scope is intentional - { - std::lock_guard lock(clockingMutex); - - // ------------------------------------------------------ - // -- Write to Modem Processing -- - // ------------------------------------------------------ - - // write DMR slot 1 frames to modem - writeFramesDMR1(dmr.get(), [&, this]() { - // if there is a P25 CC running; halt the CC - if (p25 != nullptr) { - if (p25->getCCRunning() && !p25->getCCHalted()) { - this->interruptP25Control(p25.get()); - } - } - - // if there is a NXDN CC running; halt the CC - if (nxdn != nullptr) { - if (nxdn->getCCRunning() && !nxdn->getCCHalted()) { - this->interruptNXDNControl(nxdn.get()); - } - } - }); - } - - if (m_state != STATE_IDLE) - Thread::sleep(m_activeTickDelay); - if (m_state == STATE_IDLE) - Thread::sleep(m_idleTickDelay); - } - } - }); - dmrFrame1WriteThread.run(); - dmrFrame1WriteThread.setName("dmr:frame1-w"); - - ThreadFunc dmrFrame2WriteThread([&, this]() { - if (g_killed) - return; - - StopWatch stopWatch; - stopWatch.start(); - - if (dmr != nullptr) { - LogDebug(LOG_HOST, "DMR, started slot 2 frame processor (modem write)"); - while (!g_killed) { - m_dmrTx2WatchdogTimer.start(); - - uint32_t ms = stopWatch.elapsed(); - stopWatch.start(); - m_dmrTx2LoopMS = ms; - - // scope is intentional - { - std::lock_guard lock(clockingMutex); - - // ------------------------------------------------------ - // -- Write to Modem Processing -- - // ------------------------------------------------------ - - // write DMR slot 2 frames to modem - writeFramesDMR2(dmr.get(), [&, this]() { - // if there is a P25 CC running; halt the CC - if (p25 != nullptr) { - if (p25->getCCRunning() && !p25->getCCHalted()) { - this->interruptP25Control(p25.get()); - } - } - - // if there is a NXDN CC running; halt the CC - if (nxdn != nullptr) { - if (nxdn->getCCRunning() && !nxdn->getCCHalted()) { - this->interruptNXDNControl(nxdn.get()); - } - } - }); - } - - if (m_state != STATE_IDLE) - Thread::sleep(m_activeTickDelay); - if (m_state == STATE_IDLE) - Thread::sleep(m_idleTickDelay); - } - } - }); - dmrFrame2WriteThread.run(); - dmrFrame2WriteThread.setName("dmr:frame2-w"); + if (m_dmr != nullptr) { + if (!Thread::runAsThread(this, threadDMRReader1)) + return EXIT_FAILURE; + if (!Thread::runAsThread(this, threadDMRWriter1)) + return EXIT_FAILURE; + if (!Thread::runAsThread(this, threadDMRReader2)) + return EXIT_FAILURE; + if (!Thread::runAsThread(this, threadDMRWriter2)) + return EXIT_FAILURE; + } /** Project 25 Frame Processor */ - ThreadFunc p25FrameWriteThread([&, this]() { - if (g_killed) - return; - - StopWatch stopWatch; - stopWatch.start(); - - if (p25 != nullptr) { - LogDebug(LOG_HOST, "P25, started frame processor (modem write)"); - while (!g_killed) { - m_p25TxWatchdogTimer.start(); - - uint32_t ms = stopWatch.elapsed(); - stopWatch.start(); - m_p25TxLoopMS = ms; - - // scope is intentional - { - std::lock_guard lock(clockingMutex); - - // ------------------------------------------------------ - // -- Write to Modem Processing -- - // ------------------------------------------------------ - - // write P25 frames to modem - writeFramesP25(p25.get(), [&, this]() { - if (dmr != nullptr) { - this->interruptDMRBeacon(dmr.get()); - } - - // if there is a NXDN CC running; halt the CC - if (nxdn != nullptr) { - if (nxdn->getCCRunning() && !nxdn->getCCHalted()) { - this->interruptNXDNControl(nxdn.get()); - } - } - }); - } - - if (m_state != STATE_IDLE) - Thread::sleep(m_activeTickDelay); - if (m_state == STATE_IDLE) - Thread::sleep(m_idleTickDelay); - } - } - }); - p25FrameWriteThread.run(); - p25FrameWriteThread.setName("p25:frame-w"); + if (m_p25 != nullptr) { + if (!Thread::runAsThread(this, threadP25Reader)) + return EXIT_FAILURE; + if (!Thread::runAsThread(this, threadP25Writer)) + return EXIT_FAILURE; + } /** Next Generation Digital Narrowband Frame Processor */ - ThreadFunc nxdnFrameWriteThread([&, this]() { - if (g_killed) - return; - - StopWatch stopWatch; - stopWatch.start(); - - if (nxdn != nullptr) { - LogDebug(LOG_HOST, "NXDN, started frame processor (modem write)"); - while (!g_killed) { - m_nxdnTxWatchdogTimer.start(); - - uint32_t ms = stopWatch.elapsed(); - stopWatch.start(); - m_nxdnTxLoopMS = ms; - - // scope is intentional - { - std::lock_guard lock(clockingMutex); - - // ------------------------------------------------------ - // -- Write to Modem Processing -- - // ------------------------------------------------------ - - // write NXDN frames to modem - writeFramesNXDN(nxdn.get(), [&, this]() { - if (dmr != nullptr) { - this->interruptDMRBeacon(dmr.get()); - } - - // if there is a P25 CC running; halt the CC - if (p25 != nullptr) { - if (p25->getCCRunning() && !p25->getCCHalted()) { - this->interruptP25Control(p25.get()); - } - } - }); - } - - if (m_state != STATE_IDLE) - Thread::sleep(m_activeTickDelay); - if (m_state == STATE_IDLE) - Thread::sleep(m_idleTickDelay); - } - } - }); - nxdnFrameWriteThread.run(); - nxdnFrameWriteThread.setName("nxdn:frame-w"); + if (m_nxdn != nullptr) { + if (!Thread::runAsThread(this, threadNXDNReader)) + return EXIT_FAILURE; + if (!Thread::runAsThread(this, threadNXDNWriter)) + return EXIT_FAILURE; + } /** Adjacent Site and Affiliation Update */ - ThreadFunc siteDataUpdateThread([&, this]() { - if (g_killed) - return; - - Timer networkPeerStatusNotify(1000U, 5U); - networkPeerStatusNotify.start(); - - StopWatch stopWatch; - stopWatch.start(); - - LogDebug(LOG_HOST, "started adj. site and affiliation processor"); - while (!g_killed) { - uint32_t ms = stopWatch.elapsed(); - stopWatch.start(); - m_adjSiteLoopMS = ms; - - if (dmr != nullptr) - dmr->clockSiteData(ms); - if (p25 != nullptr) - p25->clockSiteData(ms); - if (nxdn != nullptr) - nxdn->clockSiteData(ms); - - if (m_allowStatusTransfer) { - networkPeerStatusNotify.clock(ms); - if (networkPeerStatusNotify.isRunning() && networkPeerStatusNotify.hasExpired()) { - networkPeerStatusNotify.start(); - json::object statusObj = getStatus(); - m_network->writePeerStatus(statusObj); - } - } - - if (m_state != STATE_IDLE) - Thread::sleep(m_activeTickDelay); - if (m_state == STATE_IDLE) - Thread::sleep(m_idleTickDelay); - } - }); - siteDataUpdateThread.run(); - siteDataUpdateThread.setName("host:site-data"); + if (!Thread::runAsThread(this, threadSiteData)) + return EXIT_FAILURE; /** Network Presence Notification */ - ThreadFunc presenceThread([&, this]() { - if (g_killed) - return; - - Timer presenceNotifyTimer(1000U, m_presenceTime); - presenceNotifyTimer.start(); - bool hasInitialRegistered = false; - - StopWatch stopWatch; - stopWatch.start(); - - LogDebug(LOG_HOST, "started presence notifier"); - while (!g_killed) { - // scope is intentional - { - uint32_t ms = stopWatch.elapsed(); - stopWatch.start(); - - presenceNotifyTimer.clock(ms); - - // VC -> CC presence registration - if (!m_controlChData.address().empty() && m_controlChData.port() != 0 && m_network != nullptr && m_RESTAPI != nullptr && - !m_dmrCtrlChannel && !m_p25CtrlChannel && !m_nxdnCtrlChannel) { - if ((presenceNotifyTimer.isRunning() && presenceNotifyTimer.hasExpired()) || !hasInitialRegistered) { - LogMessage(LOG_HOST, "CC %s:%u, notifying CC of VC registration, peerId = %u", m_controlChData.address().c_str(), m_controlChData.port(), m_network->getPeerId()); - hasInitialRegistered = true; - - std::string localAddress = network::udp::Socket::getLocalAddress(); - if (m_restAddress == "0.0.0.0") { - m_restAddress = localAddress; - } - - // callback REST API to release the granted TG on the specified control channel - json::object req = json::object(); - req["channelNo"].set(m_channelNo); - uint32_t peerId = m_network->getPeerId(); - req["peerId"].set(peerId); - req["restAddress"].set(m_restAddress); - req["restPort"].set(m_restPort); - - int ret = RESTClient::send(m_controlChData.address(), m_controlChData.port(), m_controlChData.password(), - HTTP_PUT, PUT_REGISTER_CC_VC, req, m_controlChData.ssl(), REST_QUICK_WAIT, false); - if (ret != network::rest::http::HTTPPayload::StatusType::OK) { - ::LogError(LOG_HOST, "failed to notify the CC %s:%u of VC registration", m_controlChData.address().c_str(), m_controlChData.port()); - } - - presenceNotifyTimer.start(); - } - } - - // CC -> FNE registered VC announcement - if (m_dmrCtrlChannel || m_p25CtrlChannel || m_nxdnCtrlChannel) { - if ((presenceNotifyTimer.isRunning() && presenceNotifyTimer.hasExpired()) || g_fireCCVCNotification) { - g_fireCCVCNotification = false; - if (m_network != nullptr && m_voiceChPeerId.size() > 0) { - LogMessage(LOG_HOST, "notifying FNE of VC registrations, peerId = %u", m_network->getPeerId()); - - std::vector peers; - for (auto it : m_voiceChPeerId) { - peers.push_back(it.second); - } - - m_network->announceSiteVCs(peers); - } - - presenceNotifyTimer.start(); - } - } - } - - if (m_state != STATE_IDLE) - Thread::sleep(m_activeTickDelay); - if (m_state == STATE_IDLE) - Thread::sleep(m_idleTickDelay); + { + if (!m_controlChData.address().empty() && m_controlChData.port() != 0 && m_network != nullptr) { + if (!Thread::runAsThread(this, threadPresence)) + return EXIT_FAILURE; } - }); - if (!m_controlChData.address().empty() && m_controlChData.port() != 0 && m_network != nullptr) { - presenceThread.run(); - presenceThread.setName("host:presence"); + if (m_dmrCtrlChannel || m_p25CtrlChannel || m_nxdnCtrlChannel) { + if (!Thread::runAsThread(this, threadPresence)) + return EXIT_FAILURE; + } } - if (m_dmrCtrlChannel || m_p25CtrlChannel || m_nxdnCtrlChannel) { - presenceThread.run(); - presenceThread.setName("host:presence"); - } + /* + ** Main execution loop + */ - // main execution loop + struct utsname utsinfo; + ::memset(&utsinfo, 0, sizeof(utsinfo)); + ::uname(&utsinfo); + + ::LogInfoEx(LOG_HOST, "[ OK ] Host is up and running on %s %s %s", utsinfo.sysname, utsinfo.release, utsinfo.machine); while (!killed) { if (m_modem->hasLockout() && m_state != HOST_STATE_LOCKOUT) setState(HOST_STATE_LOCKOUT); @@ -1168,139 +829,35 @@ int Host::run() setState(STATE_IDLE); uint32_t ms = stopWatch.elapsed(); - if (ms > 1U) - m_modem->clock(ms); + stopWatch.start(); - if (!m_fixedMode) { - if (m_modeTimer.isRunning() && m_modeTimer.hasExpired()) { - setState(STATE_IDLE); - } - } - else { - m_modeTimer.stop(); - if (dmr != nullptr && m_state != STATE_DMR && !m_modem->hasTX()) { - LogDebug(LOG_HOST, "fixed mode state abnormal, m_state = %u, state = %u", m_state, STATE_DMR); - setState(STATE_DMR); - } - if (p25 != nullptr && m_state != STATE_P25 && !m_modem->hasTX()) { - LogDebug(LOG_HOST, "fixed mode state abnormal, m_state = %u, state = %u", m_state, STATE_P25); - setState(STATE_P25); + if (!m_modem->hasError()) { + if (!m_fixedMode) { + if (m_modeTimer.isRunning() && m_modeTimer.hasExpired()) { + setState(STATE_IDLE); + } } - if (nxdn != nullptr && m_state != STATE_NXDN && !m_modem->hasTX()) { - LogDebug(LOG_HOST, "fixed mode state abnormal, m_state = %u, state = %u", m_state, STATE_NXDN); - setState(STATE_NXDN); + else { + m_modeTimer.stop(); + if (m_dmr != nullptr && m_state != STATE_DMR && !m_modem->hasTX()) { + LogDebug(LOG_HOST, "fixed mode state abnormal, m_state = %u, state = %u", m_state, STATE_DMR); + setState(STATE_DMR); + } + if (m_p25 != nullptr && m_state != STATE_P25 && !m_modem->hasTX()) { + LogDebug(LOG_HOST, "fixed mode state abnormal, m_state = %u, state = %u", m_state, STATE_P25); + setState(STATE_P25); + } + if (m_nxdn != nullptr && m_state != STATE_NXDN && !m_modem->hasTX()) { + LogDebug(LOG_HOST, "fixed mode state abnormal, m_state = %u, state = %u", m_state, STATE_NXDN); + setState(STATE_NXDN); + } } } m_mainLoopWatchdogTimer.start(); m_mainLoopStage = 0U; // intentional magic number - - // scope is intentional - { - std::lock_guard lock(clockingMutex); - - // ------------------------------------------------------ - // -- Modem Clocking -- - // ------------------------------------------------------ - - ms = stopWatch.elapsed(); - stopWatch.start(); - - m_modem->clock(ms); - m_mainLoopStage = 1U; // intentional magic number - } - m_mainLoopMS = ms; - // ------------------------------------------------------ - // -- Read from Modem Processing -- - // ------------------------------------------------------ - - /** Digital Mobile Radio */ - if (dmr != nullptr) { - m_mainLoopStage = 2U; // intentional magic number - - // read DMR slot 1 frames from modem - readFramesDMR1(dmr.get(), [&, this]() { - if (dmr != nullptr) { - this->interruptDMRBeacon(dmr.get()); - } - - // if there is a P25 CC running; halt the CC - if (p25 != nullptr) { - if (p25->getCCRunning() && !p25->getCCHalted()) { - this->interruptP25Control(p25.get()); - } - } - - // if there is a NXDN CC running; halt the CC - if (nxdn != nullptr) { - if (nxdn->getCCRunning() && !nxdn->getCCHalted()) { - this->interruptNXDNControl(nxdn.get()); - } - } - }); - - // read DMR slot 2 frames from modem - readFramesDMR2(dmr.get(), [&, this]() { - if (dmr != nullptr) { - this->interruptDMRBeacon(dmr.get()); - } - - // if there is a P25 CC running; halt the CC - if (p25 != nullptr) { - if (p25->getCCRunning() && !p25->getCCHalted()) { - this->interruptP25Control(p25.get()); - } - } - - // if there is a NXDN CC running; halt the CC - if (nxdn != nullptr) { - if (nxdn->getCCRunning() && !nxdn->getCCHalted()) { - this->interruptNXDNControl(nxdn.get()); - } - } - }); - } - - /** Project 25 */ - if (p25 != nullptr) { - m_mainLoopStage = 3U; // intentional magic number - - // read P25 frames from modem - readFramesP25(p25.get(), [&, this]() { - if (dmr != nullptr) { - this->interruptDMRBeacon(dmr.get()); - } - - // if there is a NXDN CC running; halt the CC - if (nxdn != nullptr) { - if (nxdn->getCCRunning() && !nxdn->getCCHalted()) { - this->interruptNXDNControl(nxdn.get()); - } - } - }); - } - - /** Next Generation Digital Narrowband */ - if (nxdn != nullptr) { - m_mainLoopStage = 4U; // intentional magic number - - // read NXDN frames from modem - readFramesNXDN(nxdn.get(), [&, this]() { - if (dmr != nullptr) { - this->interruptDMRBeacon(dmr.get()); - } - - // if there is a P25 CC running; halt the CC - if (p25 != nullptr) { - if (p25->getCCRunning() && !p25->getCCHalted()) { - this->interruptP25Control(p25.get()); - } - } - }); - } - // ------------------------------------------------------ // -- Network, DMR, and P25 Clocking -- // ------------------------------------------------------ @@ -1310,9 +867,9 @@ int Host::run() m_network->clock(ms); } - if (dmr != nullptr) { + if (m_dmr != nullptr) { m_mainLoopStage = 6U; // intentional magic number - dmr->clock(); + m_dmr->clock(); if (m_dmrCtrlChannel) { if (!m_dmrDedicatedTxTestTimer.isRunning()) { @@ -1323,9 +880,9 @@ int Host::run() } } - if (p25 != nullptr) { + if (m_p25 != nullptr) { m_mainLoopStage = 7U; // intentional magic number - p25->clock(); + m_p25->clock(); if (m_p25CtrlChannel) { if (!m_p25DedicatedTxTestTimer.isRunning()) { @@ -1336,9 +893,9 @@ int Host::run() } } - if (nxdn != nullptr) { + if (m_nxdn != nullptr) { m_mainLoopStage = 8U; // intentional magic number - nxdn->clock(); + m_nxdn->clock(); if (m_nxdnCtrlChannel) { if (!m_nxdnDedicatedTxTestTimer.isRunning()) { @@ -1369,7 +926,7 @@ int Host::run() LogMessage(LOG_HOST, "CW, start transmitting"); m_isTxCW = true; - std::lock_guard lock(clockingMutex); + std::lock_guard lock(m_clockingMutex); setState(STATE_IDLE); m_modem->sendCWId(m_cwCallsign); @@ -1412,7 +969,7 @@ int Host::run() m_mainLoopStage = 10U; // intentional magic number /** Digial Mobile Radio */ - if (dmr != nullptr) { + if (m_dmr != nullptr) { if (m_dmrTSCCData && m_dmrCtrlChannel) { if (m_state != STATE_DMR) setState(STATE_DMR); @@ -1438,7 +995,7 @@ int Host::run() } if (m_dmrTSCCData) { - dmr->setCCRunning(true); + m_dmr->setCCRunning(true); } g_fireDMRBeacon = false; @@ -1466,7 +1023,7 @@ int Host::run() } if (m_dmrTSCCData) { - dmr->setCCRunning(false); + m_dmr->setCCRunning(false); } } @@ -1483,7 +1040,7 @@ int Host::run() } /** Project 25 */ - if (p25 != nullptr) { + if (m_p25 != nullptr) { if (m_p25CCData) { p25BcastIntervalTimer.clock(ms); @@ -1498,7 +1055,7 @@ int Host::run() if (m_state != STATE_P25) setState(STATE_P25); - p25->setCCRunning(true); + m_p25->setCCRunning(true); // hide this message for continuous CC -- otherwise display every time we process if (!m_p25CtrlChannel) { @@ -1525,7 +1082,7 @@ int Host::run() if (m_p25BcastDurationTimer.isRunning() && m_p25BcastDurationTimer.hasExpired()) { m_p25BcastDurationTimer.stop(); - p25->setCCRunning(false); + m_p25->setCCRunning(false); if (m_state == STATE_P25 && !m_modeTimer.isRunning()) { m_modeTimer.setTimeout(m_rfModeHang); @@ -1537,7 +1094,7 @@ int Host::run() } /** Next Generation Digital Narrowband */ - if (nxdn != nullptr) { + if (m_nxdn != nullptr) { if (m_nxdnCCData) { nxdnBcastIntervalTimer.clock(ms); @@ -1552,7 +1109,7 @@ int Host::run() if (m_state != STATE_NXDN) setState(STATE_NXDN); - nxdn->setCCRunning(true); + m_nxdn->setCCRunning(true); // hide this message for continuous CC -- otherwise display every time we process if (!m_nxdnCtrlChannel) { @@ -1579,7 +1136,7 @@ int Host::run() if (m_nxdnBcastDurationTimer.isRunning() && m_nxdnBcastDurationTimer.hasExpired()) { m_nxdnBcastDurationTimer.stop(); - nxdn->setCCRunning(false); + m_nxdn->setCCRunning(false); if (m_state == STATE_NXDN && !m_modeTimer.isRunning()) { m_modeTimer.setTimeout(m_rfModeHang); @@ -1600,22 +1157,7 @@ int Host::run() } if (g_killed) { - // shutdown helper threads - presenceThread.wait(); - siteDataUpdateThread.wait(); - - if (dmr != nullptr) { - dmrFrame1WriteThread.wait(); - dmrFrame2WriteThread.wait(); - } - if (p25 != nullptr) - p25FrameWriteThread.wait(); - if (nxdn != nullptr) - nxdnFrameWriteThread.wait(); - - watchdogThread.wait(); - - if (dmr != nullptr) { + if (m_dmr != nullptr) { if (m_state == STATE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); @@ -1627,36 +1169,36 @@ int Host::run() m_modem->clearDMRFrame2(); } - dmr->setCCRunning(false); - dmr->setCCHalted(true); + m_dmr->setCCRunning(false); + m_dmr->setCCHalted(true); m_dmrBeaconDurationTimer.stop(); dmrBeaconIntervalTimer.stop(); } } - if (p25 != nullptr) { + if (m_p25 != nullptr) { if (m_p25CtrlChannel) { if (!hasTxShutdown) { m_modem->clearP25Frame(); - p25->reset(); + m_p25->reset(); } - p25->setCCRunning(false); + m_p25->setCCRunning(false); m_p25BcastDurationTimer.stop(); p25BcastIntervalTimer.stop(); } } - if (nxdn != nullptr) { + if (m_nxdn != nullptr) { if (m_nxdnCtrlChannel) { if (!hasTxShutdown) { m_modem->clearNXDNFrame(); - nxdn->reset(); + m_nxdn->reset(); } - nxdn->setCCRunning(false); + m_nxdn->setCCRunning(false); m_nxdnBcastDurationTimer.stop(); nxdnBcastIntervalTimer.stop(); @@ -1908,7 +1450,6 @@ void Host::setState(uint8_t state) m_state = STATE_DMR; m_modeTimer.start(); //m_cwIdTimer.stop(); - createLockFile("DMR"); break; case STATE_P25: @@ -1916,7 +1457,6 @@ void Host::setState(uint8_t state) m_state = STATE_P25; m_modeTimer.start(); //m_cwIdTimer.stop(); - createLockFile("P25"); break; case STATE_NXDN: @@ -1924,7 +1464,6 @@ void Host::setState(uint8_t state) m_state = STATE_NXDN; m_modeTimer.start(); //m_cwIdTimer.stop(); - createLockFile("NXDN"); break; case HOST_STATE_LOCKOUT: @@ -1941,7 +1480,6 @@ void Host::setState(uint8_t state) m_state = HOST_STATE_LOCKOUT; m_modeTimer.stop(); //m_cwIdTimer.stop(); - removeLockFile(); break; case HOST_STATE_ERROR: @@ -1957,7 +1495,6 @@ void Host::setState(uint8_t state) m_state = HOST_STATE_ERROR; m_modeTimer.stop(); m_cwIdTimer.stop(); - removeLockFile(); break; default: @@ -1982,7 +1519,6 @@ void Host::setState(uint8_t state) m_cwIdTimer.start(); } - removeLockFile(); m_modeTimer.stop(); if (m_state == HOST_STATE_QUIT) { @@ -2024,20 +1560,330 @@ void Host::setState(uint8_t state) } } -/* Helper to create the state lock file. */ +/* Entry point to modem clock thread. */ + +void* Host::threadModem(void* arg) +{ + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("host:modem"); + Host* host = static_cast(th->obj); + if (host == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } + + if (g_killed) { + delete th; + return nullptr; + } + + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + StopWatch stopWatch; + stopWatch.start(); + + while (!g_killed) { + uint32_t ms = stopWatch.elapsed(); + if (ms > 1U) + host->m_modem->clock(ms); + + // scope is intentional + { + std::lock_guard lock(m_clockingMutex); + + // ------------------------------------------------------ + // -- Modem Clocking -- + // ------------------------------------------------------ + + ms = stopWatch.elapsed(); + stopWatch.start(); + + host->m_modem->clock(ms); + } + + Thread::sleep(0U, 50U); + } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; + } + + return nullptr; +} + +/* Entry point to watchdog thread. */ + +void* Host::threadWatchdog(void* arg) +{ + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("host:watchdog"); + Host* host = static_cast(th->obj); + if (host == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } + + if (g_killed) { + delete th; + return nullptr; + } + + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + StopWatch stopWatch; + stopWatch.start(); + + while (!g_killed) { + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + + // scope is intentional + { + /** Digital Mobile Radio */ + if (host->m_dmr != nullptr) { + if (host->m_dmrTx1WatchdogTimer.isRunning()) + host->m_dmrTx1WatchdogTimer.clock(ms); + if (host->m_dmrTx1WatchdogTimer.isRunning() && host->m_dmrTx1WatchdogTimer.hasExpired() && !host->m_dmrTx1WatchdogTimer.isPaused()) { + host->m_dmrTx1WatchdogTimer.pause(); + LogError(LOG_HOST, "DMR, slot 1 frame processor hung >%us, ms = %u", host->m_dmrTx1WatchdogTimer.getTimeout(), host->m_dmrTx1LoopMS); + } + + if (host->m_dmrTx2WatchdogTimer.isRunning()) + host->m_dmrTx2WatchdogTimer.clock(ms); + if (host->m_dmrTx2WatchdogTimer.isRunning() && host->m_dmrTx2WatchdogTimer.hasExpired() && !host->m_dmrTx2WatchdogTimer.isPaused()) { + host->m_dmrTx2WatchdogTimer.pause(); + LogError(LOG_HOST, "DMR, slot 2 frame processor hung >%us, ms = %u", host->m_dmrTx2WatchdogTimer.getTimeout(), host->m_dmrTx2LoopMS); + } + } + + /** Project 25 */ + if (host->m_p25 != nullptr) { + if (host->m_p25TxWatchdogTimer.isRunning()) + host->m_p25TxWatchdogTimer.clock(ms); + if (host->m_p25TxWatchdogTimer.isRunning() && host->m_p25TxWatchdogTimer.hasExpired() && !host->m_p25TxWatchdogTimer.isPaused()) { + host->m_p25TxWatchdogTimer.pause(); + LogError(LOG_HOST, "P25, frame processor hung >%us, ms = %u", host->m_p25TxWatchdogTimer.getTimeout(), host->m_p25TxLoopMS); + } + } + + /** Next Generation Digital Narrowband */ + if (host->m_nxdn != nullptr) { + if (host->m_nxdnTxWatchdogTimer.isRunning()) + host->m_nxdnTxWatchdogTimer.clock(ms); + if (host->m_nxdnTxWatchdogTimer.isRunning() && host->m_nxdnTxWatchdogTimer.hasExpired() && !host->m_nxdnTxWatchdogTimer.isPaused()) { + host->m_nxdnTxWatchdogTimer.pause(); + LogError(LOG_HOST, "NXDN, frame processor hung >%us, ms = %u", host->m_nxdnTxWatchdogTimer.getTimeout(), host->m_nxdnTxLoopMS); + } + } + } + + // scope is intentional + { + if (host->m_mainLoopWatchdogTimer.isRunning()) + host->m_mainLoopWatchdogTimer.clock(ms); + if (host->m_mainLoopWatchdogTimer.isRunning() && host->m_mainLoopWatchdogTimer.hasExpired() && !host->m_mainLoopWatchdogTimer.isPaused()) { + host->m_mainLoopWatchdogTimer.pause(); + LogError(LOG_HOST, "main processor hung >%us, stage = %u, ms = %u", host->m_mainLoopWatchdogTimer.getTimeout(), host->m_mainLoopStage, host->m_mainLoopMS); + } + + if (host->m_adjSiteLoopWatchdogTimer.isRunning()) + host->m_adjSiteLoopWatchdogTimer.clock(ms); + if (host->m_adjSiteLoopWatchdogTimer.isRunning() && host->m_adjSiteLoopWatchdogTimer.hasExpired() && !host->m_adjSiteLoopWatchdogTimer.isPaused()) { + host->m_adjSiteLoopWatchdogTimer.pause(); + LogError(LOG_HOST, "adj. site update hung >%us, ms = %u", host->m_adjSiteLoopWatchdogTimer.getTimeout(), host->m_adjSiteLoopMS); + } + } + + if (host->m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (host->m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); + } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; + } + + return nullptr; +} + +/* Entry point to site data update thread. */ -void Host::createLockFile(const char* mode) const +void* Host::threadSiteData(void* arg) { - FILE* fp = ::fopen(g_lockFile.c_str(), "wt"); - if (fp != nullptr) { - ::fprintf(fp, "%s\n", mode); - ::fclose(fp); + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("host:site-data"); + Host* host = static_cast(th->obj); + if (host == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } + + if (g_killed) { + delete th; + return nullptr; + } + + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + Timer networkPeerStatusNotify(1000U, 5U); + networkPeerStatusNotify.start(); + + StopWatch stopWatch; + stopWatch.start(); + + while (!g_killed) { + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + host->m_adjSiteLoopMS = ms; + + if (host->m_dmr != nullptr) + host->m_dmr->clockSiteData(ms); + if (host->m_p25 != nullptr) + host->m_p25->clockSiteData(ms); + if (host->m_nxdn != nullptr) + host->m_nxdn->clockSiteData(ms); + + if (host->m_allowStatusTransfer && host->m_network != nullptr) { + networkPeerStatusNotify.clock(ms); + if (networkPeerStatusNotify.isRunning() && networkPeerStatusNotify.hasExpired()) { + networkPeerStatusNotify.start(); + json::object statusObj = host->getStatus(); + host->m_network->writePeerStatus(statusObj); + } + } + + if (host->m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (host->m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); + } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; } + + return nullptr; } -/* Helper to remove the state lock file. */ +/* Entry point to presence update thread. */ -void Host::removeLockFile() const +void* Host::threadPresence(void* arg) { - ::remove(g_lockFile.c_str()); + thread_t* th = (thread_t*)arg; + if (th != nullptr) { + ::pthread_detach(th->thread); + + std::string threadName("host:presence"); + Host* host = static_cast(th->obj); + if (host == nullptr) { + g_killed = true; + LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str()); + } + + if (g_killed) { + delete th; + return nullptr; + } + + LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str()); +#ifdef _GNU_SOURCE + ::pthread_setname_np(th->thread, threadName.c_str()); +#endif // _GNU_SOURCE + + Timer presenceNotifyTimer(1000U, host->m_presenceTime); + presenceNotifyTimer.start(); + bool hasInitialRegistered = false; + + StopWatch stopWatch; + stopWatch.start(); + + LogDebug(LOG_HOST, "started presence notifier"); + while (!g_killed) { + // scope is intentional + { + uint32_t ms = stopWatch.elapsed(); + stopWatch.start(); + + presenceNotifyTimer.clock(ms); + + // VC -> CC presence registration + if (!host->m_controlChData.address().empty() && host->m_controlChData.port() != 0 && host->m_network != nullptr && host->m_RESTAPI != nullptr && + !host->m_dmrCtrlChannel && !host->m_p25CtrlChannel && !host->m_nxdnCtrlChannel) { + if ((presenceNotifyTimer.isRunning() && presenceNotifyTimer.hasExpired()) || !hasInitialRegistered) { + LogMessage(LOG_HOST, "CC %s:%u, notifying CC of VC registration, peerId = %u", host->m_controlChData.address().c_str(), host->m_controlChData.port(), host->m_network->getPeerId()); + hasInitialRegistered = true; + + std::string localAddress = network::udp::Socket::getLocalAddress(); + if (host->m_restAddress == "0.0.0.0") { + host->m_restAddress = localAddress; + } + + // callback REST API to release the granted TG on the specified control channel + json::object req = json::object(); + req["channelNo"].set(host->m_channelNo); + uint32_t peerId = host->m_network->getPeerId(); + req["peerId"].set(peerId); + req["restAddress"].set(host->m_restAddress); + req["restPort"].set(host->m_restPort); + + int ret = RESTClient::send(host->m_controlChData.address(), host->m_controlChData.port(), host->m_controlChData.password(), + HTTP_PUT, PUT_REGISTER_CC_VC, req, host->m_controlChData.ssl(), REST_QUICK_WAIT, false); + if (ret != network::rest::http::HTTPPayload::StatusType::OK) { + ::LogError(LOG_HOST, "failed to notify the CC %s:%u of VC registration", host->m_controlChData.address().c_str(), host->m_controlChData.port()); + } + + presenceNotifyTimer.start(); + } + } + + // CC -> FNE registered VC announcement + if (host->m_dmrCtrlChannel || host->m_p25CtrlChannel || host->m_nxdnCtrlChannel) { + if ((presenceNotifyTimer.isRunning() && presenceNotifyTimer.hasExpired()) || g_fireCCVCNotification) { + g_fireCCVCNotification = false; + if (host->m_network != nullptr && host->m_voiceChPeerId.size() > 0) { + LogMessage(LOG_HOST, "notifying FNE of VC registrations, peerId = %u", host->m_network->getPeerId()); + + std::vector peers; + for (auto it : host->m_voiceChPeerId) { + peers.push_back(it.second); + } + + host->m_network->announceSiteVCs(peers); + } + + presenceNotifyTimer.start(); + } + } + } + + if (host->m_state != STATE_IDLE) + Thread::sleep(m_activeTickDelay); + if (host->m_state == STATE_IDLE) + Thread::sleep(m_idleTickDelay); + } + + LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str()); + delete th; + } + + return nullptr; } diff --git a/src/host/Host.h b/src/host/Host.h index aa478ce3..4a26e7a3 100644 --- a/src/host/Host.h +++ b/src/host/Host.h @@ -44,6 +44,9 @@ #include #include #include +#include + +#include // --------------------------------------------------------------------------- // Class Prototypes @@ -190,14 +193,20 @@ class HOST_SW_API Host { uint32_t m_adjSiteLoopMS; Timer m_adjSiteLoopWatchdogTimer; - uint8_t m_activeTickDelay; - uint8_t m_idleTickDelay; + static std::mutex m_clockingMutex; + + static uint8_t m_activeTickDelay; + static uint8_t m_idleTickDelay; friend class RESTAPI; std::string m_restAddress; uint16_t m_restPort; RESTAPI *m_RESTAPI; + std::unique_ptr m_dmr; + std::unique_ptr m_p25; + std::unique_ptr m_nxdn; + /** * @brief Helper to generate the status of the host in JSON format. * @returns json::object Host status as a JSON object. @@ -235,14 +244,29 @@ class HOST_SW_API Host { void setState(uint8_t state); /** - * @brief Helper to create the state lock file. - * @param state + * @brief Entry point to modem clocking thread. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) + */ + static void* threadModem(void* arg); + /** + * @brief Entry point to watchdog thread. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) */ - void createLockFile(const char* state) const; + static void* threadWatchdog(void* arg); /** - * @brief Helper to remove the state lock file. + * @brief Entry point to site data update thread. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) */ - void removeLockFile() const; + static void* threadSiteData(void* arg); + /** + * @brief Entry point to presence update thread. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) + */ + static void* threadPresence(void* arg); // Configuration (Host.Config.cpp) /** @@ -263,75 +287,72 @@ class HOST_SW_API Host { // Digital Mobile Radio (Host.DMR.cpp) /** - * @brief Helper to interrupt a running DMR beacon. - * @param control Instance of the dmr::Control class. + * @brief Entry point to read DMR slot 1 frames from modem Rx queue. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) */ - void interruptDMRBeacon(dmr::Control* control); - + static void* threadDMRReader1(void* arg); /** - * @brief Helper to read DMR slot 1 frames from modem. - * @param control Instance of the dmr::Control class. - * @param afterReadCallback Function callback after reading frames. + * @brief Entry point to write DMR slot 1 frames to modem. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) */ - void readFramesDMR1(dmr::Control* control, std::function&& afterReadCallback); + static void* threadDMRWriter1(void* arg); /** - * @brief Helper to write DMR slot 1 frames to modem. - * @param control Instance of the dmr::Control class. - * @param afterWriteCallback Function callback writing reading frames. + * @brief Entry point to read DMR slot 2 frames from modem Rx queue. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) */ - void writeFramesDMR1(dmr::Control* control, std::function&& afterWriteCallback); + static void* threadDMRReader2(void* arg); /** - * @brief Helper to read DMR slot 2 frames from modem. - * @param control Instance of the dmr::Control class. - * @param afterReadCallback Function callback after reading frames. + * @brief Entry point to write DMR slot 2 frames to modem. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) */ - void readFramesDMR2(dmr::Control* control, std::function&& afterReadCallback); + static void* threadDMRWriter2(void* arg); + /** - * @brief Helper to write DMR slot 2 frames to modem. - * @param control Instance of the dmr::Control class. - * @param afterWriteCallback Function callback writing reading frames. + * @brief Helper to interrupt a running DMR beacon. */ - void writeFramesDMR2(dmr::Control* control, std::function&& afterWriteCallback); + void interruptDMRBeacon(); // Project 25 (Host.P25.cpp) /** - * @brief Helper to interrupt a running P25 control channel. - * @param control Instance of the p25::Control class. + * @brief Entry point to read P25 frames from modem Rx queue. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) */ - void interruptP25Control(p25::Control* control); - + static void* threadP25Reader(void* arg); /** - * @brief Helper to read P25 frames from modem. - * @param control Instance of the p25::Control class. - * @param afterReadCallback Function callback after reading frames. + * @brief Entry point to write P25 frames to modem. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) */ - void readFramesP25(p25::Control* control, std::function&& afterReadCallback); + static void* threadP25Writer(void* arg); + /** - * @brief Helper to write P25 frames to modem. - * @param control Instance of the p25::Control class. - * @param afterWriteCallback Function callback writing reading frames. + * @brief Helper to interrupt a running P25 control channel. */ - void writeFramesP25(p25::Control* control, std::function&& afterWriteCallback); + void interruptP25Control(); // Next Generation Digital Narrowband (Host.NXDN.cpp) /** - * @brief Helper to interrupt a running NXDN control channel. - * @param control Instance of the nxdn::Control class. + * @brief Entry point to read NXDN frames from modem Rx queue. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) */ - void interruptNXDNControl(nxdn::Control* control); - + static void* threadNXDNReader(void* arg); /** - * @brief Helper to read NXDN frames from modem. - * @param control Instance of the nxdn::Control class. - * @param afterReadCallback Function callback after reading frames. + * @brief Entry point to write NXDN frames to modem. + * @param arg Instance of the thread_t structure. + * @returns void* (Ignore) */ - void readFramesNXDN(nxdn::Control* control, std::function&& afterReadCallback); + static void* threadNXDNWriter(void* arg); + /** - * @brief Helper to write NXDN frames to modem. - * @param control Instance of the nxdn::Control class. - * @param afterWriteCallback Function callback writing reading frames. + * @brief Helper to interrupt a running NXDN control channel. */ - void writeFramesNXDN(nxdn::Control* control, std::function&& afterWriteCallback); + void interruptNXDNControl(); }; #endif // __HOST_H__ diff --git a/src/host/HostMain.cpp b/src/host/HostMain.cpp index 857ba5c5..f104f371 100644 --- a/src/host/HostMain.cpp +++ b/src/host/HostMain.cpp @@ -299,13 +299,13 @@ int main(int argc, char** argv) } if (g_signal == 2) - ::LogInfoEx(LOG_HOST, "Exited on receipt of SIGINT"); + ::LogInfoEx(LOG_HOST, "[STOP] dvmhost:main SIGINT"); if (g_signal == 15) - ::LogInfoEx(LOG_HOST, "Exited on receipt of SIGTERM"); + ::LogInfoEx(LOG_HOST, "[STOP] dvmhost:main SIGTERM"); if (g_signal == 1) - ::LogInfoEx(LOG_HOST, "Restarting on receipt of SIGHUP"); + ::LogInfoEx(LOG_HOST, "[RSTR] dvmhost:main SIGHUP"); } while (g_signal == 1); ::LogFinalise(); diff --git a/src/host/modem/Modem.cpp b/src/host/modem/Modem.cpp index 8ef25066..0c445e7f 100644 --- a/src/host/modem/Modem.cpp +++ b/src/host/modem/Modem.cpp @@ -139,7 +139,7 @@ Modem::Modem(port::IModemPort* port, bool duplex, bool rxInvert, bool txInvert, m_rxDMRQueue2(dmrQueueSize, "Modem RX DMR2"), m_rxP25Queue(p25QueueSize, "Modem RX P25"), m_rxNXDNQueue(nxdnQueueSize, "Modem RX NXDN"), - m_statusTimer(1000U, 0U, 250U), + m_statusTimer(1000U, 0U, MODEM_POLL_TIME_IDLE), m_inactivityTimer(1000U, 8U), m_dmrSpace1(0U), m_dmrSpace2(0U), @@ -149,6 +149,10 @@ Modem::Modem(port::IModemPort* port, bool duplex, bool rxInvert, bool txInvert, m_cd(false), m_lockout(false), m_error(false), + m_dmr1ReadLock(), + m_dmr2ReadLock(), + m_p25ReadLock(), + m_nxdnReadLock(), m_ignoreModemConfigArea(ignoreModemConfigArea), m_flashDisabled(false), m_gotModemStatus(false), @@ -507,7 +511,7 @@ bool Modem::open() void Modem::clock(uint32_t ms) { - // poll the modem status every 250ms + // poll the modem status m_statusTimer.clock(ms); if (m_statusTimer.hasExpired()) { getStatus(); @@ -550,8 +554,7 @@ void Modem::clock(uint32_t ms) case CMD_DMR_DATA1: { if (m_dmrEnabled) { - //if (m_trace) - // Utils::dump(1U, "RX DMR Data 1", m_buffer, m_length); + std::lock_guard lock(m_dmr1ReadLock); if (m_rspDoubleLength) { LogError(LOG_MODEM, "CMD_DMR_DATA1 double length?; len = %u", m_length); @@ -575,8 +578,7 @@ void Modem::clock(uint32_t ms) case CMD_DMR_DATA2: { if (m_dmrEnabled) { - //if (m_trace) - // Utils::dump(1U, "RX DMR Data 2", m_buffer, m_length); + std::lock_guard lock(m_dmr2ReadLock); if (m_rspDoubleLength) { LogError(LOG_MODEM, "CMD_DMR_DATA2 double length?; len = %u", m_length); @@ -600,8 +602,7 @@ void Modem::clock(uint32_t ms) case CMD_DMR_LOST1: { if (m_dmrEnabled) { - //if (m_trace) - // Utils::dump(1U, "RX DMR Lost 1", m_buffer, m_length); + std::lock_guard lock(m_dmr1ReadLock); if (m_rspDoubleLength) { LogError(LOG_MODEM, "CMD_DMR_LOST1 double length?; len = %u", m_length); @@ -620,8 +621,7 @@ void Modem::clock(uint32_t ms) case CMD_DMR_LOST2: { if (m_dmrEnabled) { - //if (m_trace) - // Utils::dump(1U, "RX DMR Lost 2", m_buffer, m_length); + std::lock_guard lock(m_dmr2ReadLock); if (m_rspDoubleLength) { LogError(LOG_MODEM, "CMD_DMR_LOST2 double length?; len = %u", m_length); @@ -641,8 +641,7 @@ void Modem::clock(uint32_t ms) case CMD_P25_DATA: { if (m_p25Enabled) { - //if (m_trace) - // Utils::dump(1U, "RX P25 Data", m_buffer, m_length); + std::lock_guard lock(m_p25ReadLock); uint8_t length[2U]; if (m_length > 255U) @@ -663,8 +662,7 @@ void Modem::clock(uint32_t ms) case CMD_P25_LOST: { if (m_p25Enabled) { - //if (m_trace) - // Utils::dump(1U, "RX P25 Lost", m_buffer, m_length); + std::lock_guard lock(m_p25ReadLock); if (m_rspDoubleLength) { LogError(LOG_MODEM, "CMD_P25_LOST double length?; len = %u", m_length); @@ -684,8 +682,7 @@ void Modem::clock(uint32_t ms) case CMD_NXDN_DATA: { if (m_nxdnEnabled) { - //if (m_trace) - // Utils::dump(1U, "RX NXDN Data", m_buffer, m_length); + std::lock_guard lock(m_nxdnReadLock); if (m_rspDoubleLength) { LogError(LOG_MODEM, "CMD_NXDN_DATA double length?; len = %u", m_length); @@ -706,8 +703,7 @@ void Modem::clock(uint32_t ms) case CMD_NXDN_LOST: { if (m_nxdnEnabled) { - //if (m_trace) - // Utils::dump(1U, "RX NXDN Lost", m_buffer, m_length); + std::lock_guard lock(m_nxdnReadLock); if (m_rspDoubleLength) { LogError(LOG_MODEM, "CMD_NXDN_LOST double length?; len = %u", m_length); @@ -726,9 +722,6 @@ void Modem::clock(uint32_t ms) /** General */ case CMD_GET_STATUS: { - //if (m_trace) - // Utils::dump(1U, "Get Status", m_buffer, m_length); - m_isHotspot = (m_buffer[3U] & 0x01U) == 0x01U; // override hotspot flag if we're forcing hotspot @@ -897,17 +890,50 @@ void Modem::close() } } +/* Get the frame data length for the next frame in the DMR Slot 1 ring buffer. */ + +uint32_t Modem::peekDMRFrame1Length() +{ + if (m_rxDMRQueue1.isEmpty()) + return 0U; + + uint8_t len = 0U; + m_rxDMRQueue1.peek(&len, 1U); +#if DEBUG_MODEM + LogDebug(LOG_MODEM, "Modem::peekDMRFrame1Length() len = %u, dataSize = %u", len, m_rxDMRQueue1.dataSize()); +#endif + // this ensures we never get in a situation where we have length stuck on the queue + if (m_rxDMRQueue1.dataSize() == 1U && len > m_rxDMRQueue1.dataSize()) { + m_rxDMRQueue1.get(&len, 1U); // ensure we pop the length off + return 0U; + } + + if (m_rxDMRQueue1.dataSize() >= len) { + return len; + } + + return 0U; +} + /* Reads DMR Slot 1 frame data from the DMR Slot 1 ring buffer. */ uint32_t Modem::readDMRFrame1(uint8_t* data) { assert(data != nullptr); + std::lock_guard lock(m_dmr1ReadLock); if (m_rxDMRQueue1.isEmpty()) return 0U; uint8_t len = 0U; m_rxDMRQueue1.peek(&len, 1U); + + // this ensures we never get in a situation where we have length stuck on the queue + if (m_rxDMRQueue1.dataSize() == 1U && len > m_rxDMRQueue1.dataSize()) { + m_rxDMRQueue1.get(&len, 1U); // ensure we pop the length off + return 0U; + } + if (m_rxDMRQueue1.dataSize() >= len) { m_rxDMRQueue1.get(&len, 1U); // ensure we pop the length off m_rxDMRQueue1.get(data, len); @@ -918,17 +944,50 @@ uint32_t Modem::readDMRFrame1(uint8_t* data) return 0U; } +/* Get the frame data length for the next frame in the DMR Slot 2 ring buffer. */ + +uint32_t Modem::peekDMRFrame2Length() +{ + if (m_rxDMRQueue2.isEmpty()) + return 0U; + + uint8_t len = 0U; + m_rxDMRQueue2.peek(&len, 1U); +#if DEBUG_MODEM + LogDebug(LOG_MODEM, "Modem::peekDMRFrame2Length() len = %u, dataSize = %u", len, m_rxDMRQueue2.dataSize()); +#endif + // this ensures we never get in a situation where we have length stuck on the queue + if (m_rxDMRQueue2.dataSize() == 1U && len > m_rxDMRQueue2.dataSize()) { + m_rxDMRQueue2.get(&len, 1U); // ensure we pop the length off + return 0U; + } + + if (m_rxDMRQueue2.dataSize() >= len) { + return len; + } + + return 0U; +} + /* Reads DMR Slot 2 frame data from the DMR Slot 2 ring buffer. */ uint32_t Modem::readDMRFrame2(uint8_t* data) { assert(data != nullptr); + std::lock_guard lock(m_dmr2ReadLock); if (m_rxDMRQueue2.isEmpty()) return 0U; uint8_t len = 0U; m_rxDMRQueue2.peek(&len, 1U); + + // this ensures we never get in a situation where we have length stuck on the queue + if (m_rxDMRQueue2.dataSize() == 1U && len > m_rxDMRQueue2.dataSize()) { + m_rxDMRQueue2.get(&len, 1U); // ensure we pop the length off + return 0U; + } + if (m_rxDMRQueue2.dataSize() >= len) { m_rxDMRQueue2.get(&len, 1U); // ensure we pop the length off m_rxDMRQueue2.get(data, len); @@ -939,11 +998,41 @@ uint32_t Modem::readDMRFrame2(uint8_t* data) return 0U; } +/* Get the frame data length for the next frame in the P25 ring buffer. */ + +uint32_t Modem::peekP25FrameLength() +{ + if (m_rxP25Queue.isEmpty()) + return 0U; + + uint8_t length[2U]; + ::memset(length, 0x00U, 2U); + m_rxP25Queue.peek(length, 2U); + + uint16_t len = 0U; + len = (length[0U] << 8) + length[1U]; +#if DEBUG_MODEM + LogDebug(LOG_MODEM, "Modem::peekP25FrameLength() len = %u, dataSize = %u", len, m_rxP25Queue.dataSize()); +#endif + // this ensures we never get in a situation where we have length stuck on the queue + if (m_rxP25Queue.dataSize() == 2U && len > m_rxP25Queue.dataSize()) { + m_rxP25Queue.get(length, 2U); // ensure we pop the length off + return 0U; + } + + if (m_rxP25Queue.dataSize() >= len) { + return len; + } + + return 0U; +} + /* Reads P25 frame data from the P25 ring buffer. */ uint32_t Modem::readP25Frame(uint8_t* data) { assert(data != nullptr); + std::lock_guard lock(m_p25ReadLock); if (m_rxP25Queue.isEmpty()) return 0U; @@ -971,17 +1060,50 @@ uint32_t Modem::readP25Frame(uint8_t* data) return 0U; } +/* Get the frame data length for the next frame in the NXDN ring buffer. */ + +uint32_t Modem::peekNXDNFrameLength() +{ + if (m_rxNXDNQueue.isEmpty()) + return 0U; + + uint8_t len = 0U; + m_rxNXDNQueue.peek(&len, 1U); +#if DEBUG_MODEM + LogDebug(LOG_MODEM, "Modem::peekNXDNFrameLength() len = %u, dataSize = %u", len, m_rxNXDNQueue.dataSize()); +#endif + // this ensures we never get in a situation where we have length stuck on the queue + if (m_rxNXDNQueue.dataSize() == 1U && len > m_rxNXDNQueue.dataSize()) { + m_rxNXDNQueue.get(&len, 1U); // ensure we pop the length off + return 0U; + } + + if (m_rxNXDNQueue.dataSize() >= len) { + return len; + } + + return 0U; +} + /* Reads NXDN frame data from the NXDN ring buffer. */ uint32_t Modem::readNXDNFrame(uint8_t* data) { assert(data != nullptr); + std::lock_guard lock(m_nxdnReadLock); if (m_rxNXDNQueue.isEmpty()) return 0U; uint8_t len = 0U; m_rxNXDNQueue.peek(&len, 1U); + + // this ensures we never get in a situation where we have length stuck on the queue + if (m_rxNXDNQueue.dataSize() == 1U && len > m_rxNXDNQueue.dataSize()) { + m_rxNXDNQueue.get(&len, 1U); // ensure we pop the length off + return 0U; + } + if (m_rxNXDNQueue.dataSize() >= len) { m_rxNXDNQueue.get(&len, 1U); // ensure we pop the length off m_rxNXDNQueue.get(data, len); @@ -1569,6 +1691,12 @@ DVM_STATE Modem::getState() const bool Modem::setState(DVM_STATE state) { + if (state != STATE_IDLE) { + m_statusTimer.setTimeout(0U, MODEM_POLL_TIME_ACTIVE); + } else { + m_statusTimer.setTimeout(0U, MODEM_POLL_TIME_IDLE); + } + uint8_t buffer[4U]; buffer[0U] = DVM_SHORT_FRAME_START; diff --git a/src/host/modem/Modem.h b/src/host/modem/Modem.h index fee4214e..309c64d9 100644 --- a/src/host/modem/Modem.h +++ b/src/host/modem/Modem.h @@ -34,6 +34,7 @@ #include #include +#include /** * @addtogroup modem @@ -269,6 +270,9 @@ namespace modem const uint32_t MAX_ADC_OVERFLOW = 128U; const uint32_t MAX_DAC_OVERFLOW = 128U; + const uint32_t MODEM_POLL_TIME_IDLE = 100U; + const uint32_t MODEM_POLL_TIME_ACTIVE = 175U; + /** @} */ // --------------------------------------------------------------------------- @@ -428,24 +432,44 @@ namespace modem */ void close(); + /** + * @brief Get the frame data length for the next frame in the DMR Slot 1 ring buffer. + * @returns uint32_t Length of frame data retrieved. + */ + uint32_t peekDMRFrame1Length(); /** * @brief Reads DMR Slot 1 frame data from the DMR Slot 1 ring buffer. * @param[out] data Buffer to write frame data to. * @returns uint32_t Length of data read from ring buffer. */ uint32_t readDMRFrame1(uint8_t* data); + /** + * @brief Get the frame data length for the next frame in the DMR Slot 2 ring buffer. + * @returns uint32_t Length of frame data retrieved. + */ + uint32_t peekDMRFrame2Length(); /** * @brief Reads DMR Slot 2 frame data from the DMR Slot 1 ring buffer. * @param[out] data Buffer to write frame data to. * @returns uint32_t Length of data read from ring buffer. */ uint32_t readDMRFrame2(uint8_t* data); + /** + * @brief Get the frame data length for the next frame in the P25 ring buffer. + * @returns uint32_t Length of frame data retrieved. + */ + uint32_t peekP25FrameLength(); /** * @brief Reads P25 frame data from the P25 ring buffer. * @param[out] data Buffer to write frame data to. * @returns uint32_t Length of data read from ring buffer. */ uint32_t readP25Frame(uint8_t* data); + /** + * @brief Get the frame data length for the next frame in the NXDN ring buffer. + * @returns uint32_t Length of frame data retrieved. + */ + uint32_t peekNXDNFrameLength(); /** * @brief Reads NXDN frame data from the NXDN ring buffer. * @param[out] data Buffer to write frame data to. @@ -763,6 +787,11 @@ namespace modem bool m_lockout; bool m_error; + std::mutex m_dmr1ReadLock; + std::mutex m_dmr2ReadLock; + std::mutex m_p25ReadLock; + std::mutex m_nxdnReadLock; + bool m_ignoreModemConfigArea; bool m_flashDisabled;