[This commit contains highly-experimental and possibly unstable alterations to core archeitcture on dvmhost] deprecate ThreadFunc (using C++ lambda's was a bad idea to begin with); refactor some startup messages; refactor use of ThreadFunc in FNE; refactor how threads were created in the FNE; [EXPERIMENTAL] refactor the host architecture to use threading for modem clocking and protocol readers;

pull/63/head
Bryan Biedenkapp 2 years ago
parent 4ba65c5bab
commit 355db0c9cc

@ -108,8 +108,8 @@ typedef unsigned long long ulong64_t;
#define __EXE_NAME__ "" #define __EXE_NAME__ ""
#define VERSION_MAJOR "04" #define VERSION_MAJOR "04"
#define VERSION_MINOR "01" #define VERSION_MINOR "02"
#define VERSION_REV "B" #define VERSION_REV "C"
#define __NETVER__ "DVM_R" VERSION_MAJOR VERSION_REV VERSION_MINOR #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__ ")" #define __VER__ VERSION_MAJOR "." VERSION_MINOR VERSION_REV " (R" VERSION_MAJOR VERSION_REV VERSION_MINOR " " __GIT_VER__ ")"

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX * Copyright (C) 2015,2016 Jonathan Naylor, G4KLX
* Copyright (C) 2023 Bryan Biedenkapp, N2PLL * Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "Thread.h" #include "Thread.h"
@ -82,7 +82,25 @@ void Thread::detach()
::pthread_detach(m_thread); ::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) void Thread::sleep(uint32_t ms, uint32_t us)
{ {

@ -5,7 +5,7 @@
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2015,2016 Jonathan Naylor, G4KLX * 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 <pthread.h> #include <pthread.h>
// ---------------------------------------------------------------------------
// 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 // Class Declaration
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -75,7 +88,16 @@ public:
virtual void detach(); 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 ms Time in milliseconds to sleep.
* @param us Time in microseconds to sleep. * @param us Time in microseconds to sleep.
*/ */

@ -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 <cassert>
#include <functional>
// ---------------------------------------------------------------------------
// 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<void()>&& 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<void()> m_entry;
};
#endif // __THREAD_FUNC_H__

@ -28,6 +28,7 @@ using namespace lookups;
#include <functional> #include <functional>
#include <random> #include <random>
#include <sys/utsname.h>
#include <unistd.h> #include <unistd.h>
#include <pwd.h> #include <pwd.h>
@ -152,8 +153,6 @@ int Dfsi::run()
if (!ret) if (!ret)
return EXIT_FAILURE; return EXIT_FAILURE;
::LogInfoEx(LOG_HOST, "DFSI peer network is up and running");
std::string dfsiModeStr = "Unknown"; std::string dfsiModeStr = "Unknown";
switch (dfsiMode) { switch (dfsiMode) {
@ -195,7 +194,15 @@ int Dfsi::run()
StopWatch stopWatch; StopWatch stopWatch;
stopWatch.start(); 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) { while (!g_killed) {
uint32_t ms = stopWatch.elapsed(); uint32_t ms = stopWatch.elapsed();

@ -213,13 +213,13 @@ int main(int argc, char** argv)
delete dfsi; delete dfsi;
if (g_signal == 2) if (g_signal == 2)
::LogInfoEx(LOG_HOST, "Exited on receipt of SIGINT"); ::LogInfoEx(LOG_HOST, "[STOP] dvmdfsi:main SIGINT");
if (g_signal == 15) if (g_signal == 15)
::LogInfoEx(LOG_HOST, "Exited on receipt of SIGTERM"); ::LogInfoEx(LOG_HOST, "[STOP] dvmdfsi:main SIGTERM");
if (g_signal == 1) if (g_signal == 1)
::LogInfoEx(LOG_HOST, "Restarting on receipt of SIGHUP"); ::LogInfoEx(LOG_HOST, "[RSTR] dvmdfsi:main SIGHUP");
} while (g_signal == 1); } while (g_signal == 1);
::LogFinalise(); ::LogFinalise();

@ -208,13 +208,13 @@ int main(int argc, char** argv)
delete fne; delete fne;
if (g_signal == 2) if (g_signal == 2)
::LogInfoEx(LOG_HOST, "Exited on receipt of SIGINT"); ::LogInfoEx(LOG_HOST, "[STOP] dvmfne:main SIGINT");
if (g_signal == 15) if (g_signal == 15)
::LogInfoEx(LOG_HOST, "Exited on receipt of SIGTERM"); ::LogInfoEx(LOG_HOST, "[STOP] dvmfne:main SIGTERM");
if (g_signal == 1) if (g_signal == 1)
::LogInfoEx(LOG_HOST, "Restarting on receipt of SIGHUP"); ::LogInfoEx(LOG_HOST, "[RSTR] dvmfne:main SIGHUP");
} while (g_signal == 1); } while (g_signal == 1);
::LogFinalise(); ::LogFinalise();

@ -12,7 +12,6 @@
#include "common/Log.h" #include "common/Log.h"
#include "common/StopWatch.h" #include "common/StopWatch.h"
#include "common/Thread.h" #include "common/Thread.h"
#include "common/ThreadFunc.h"
#include "network/callhandler/TagDMRData.h" #include "network/callhandler/TagDMRData.h"
#include "network/callhandler/TagP25Data.h" #include "network/callhandler/TagP25Data.h"
#include "network/callhandler/TagNXDNData.h" #include "network/callhandler/TagNXDNData.h"
@ -28,6 +27,7 @@ using namespace lookups;
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
#include <sys/utsname.h>
#include <unistd.h> #include <unistd.h>
#include <pwd.h> #include <pwd.h>
@ -181,75 +181,29 @@ int HostFNE::run()
if (!ret) if (!ret)
return EXIT_FAILURE; return EXIT_FAILURE;
::LogInfoEx(LOG_HOST, "FNE is up and running");
StopWatch stopWatch; StopWatch stopWatch;
stopWatch.start(); stopWatch.start();
// setup network loop threads /*
ThreadFunc networkLoop([&, this]() { ** Initialize Threads
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);
ssize_t len = m_tun->read(packet); if (!Thread::runAsThread(this, threadMasterNetwork))
if (len > 0) { return EXIT_FAILURE;
switch (m_packetDataMode) { if (!Thread::runAsThread(this, threadDiagNetwork))
case PacketDataMode::DMR: return EXIT_FAILURE;
// TODO: not supported yet if (!Thread::runAsThread(this, threadVirtualNetworking))
break; return EXIT_FAILURE;
case PacketDataMode::PROJECT25: /*
m_network->p25TrafficHandler()->processPacketFrame(packet, DEFAULT_MTU_SIZE); ** Main execution loop
break; */
}
}
Thread::sleep(5U); struct utsname utsinfo;
} ::memset(&utsinfo, 0, sizeof(utsinfo));
} ::uname(&utsinfo);
});
vtunLoop.run();
vtunLoop.setName("dvmfne:vtun-loop");
// 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) { while (!g_killed) {
uint32_t ms = stopWatch.elapsed(); uint32_t ms = stopWatch.elapsed();
@ -287,15 +241,6 @@ int HostFNE::run()
} }
// shutdown threads // shutdown threads
networkLoop.wait();
if (m_useAlternatePortForDiagnostics) {
diagNetworkLoop.wait();
}
if (m_vtunEnabled) {
vtunLoop.wait();
}
if (m_network != nullptr) { if (m_network != nullptr) {
m_network->close(); m_network->close();
delete m_network; delete m_network;
@ -629,6 +574,89 @@ bool HostFNE::createMasterNetwork()
return true; 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<HostFNE*>(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<HostFNE*>(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. */ /* Initializes peer FNE network connectivity. */
bool HostFNE::createPeerNetworks() bool HostFNE::createPeerNetworks()
@ -773,6 +801,65 @@ bool HostFNE::createVirtualNetworking()
return true; 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<HostFNE*>(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. */ /* Processes any peer network traffic. */
void HostFNE::processPeer(network::PeerNetwork* peerNetwork) void HostFNE::processPeer(network::PeerNetwork* peerNetwork)

@ -130,6 +130,18 @@ private:
* @returns bool True, if network connectivity was initialized, otherwise false. * @returns bool True, if network connectivity was initialized, otherwise false.
*/ */
bool createMasterNetwork(); 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. * @brief Initializes peer FNE network connectivity.
* @returns bool True, if network connectivity was initialized, otherwise false. * @returns bool True, if network connectivity was initialized, otherwise false.
@ -141,6 +153,12 @@ private:
* @returns bool True, if network connectivity was initialized, otherwise false. * @returns bool True, if network connectivity was initialized, otherwise false.
*/ */
bool createVirtualNetworking(); 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. * @brief Processes any peer network traffic.

@ -72,7 +72,6 @@ void DiagNetwork::processNetwork()
uint32_t peerId = fneHeader.getPeerId(); uint32_t peerId = fneHeader.getPeerId();
NetPacketRequest* req = new NetPacketRequest(); NetPacketRequest* req = new NetPacketRequest();
req->network = m_fneNetwork;
req->peerId = peerId; req->peerId = peerId;
req->address = address; req->address = address;
@ -84,8 +83,7 @@ void DiagNetwork::processNetwork()
req->buffer = new uint8_t[length]; req->buffer = new uint8_t[length];
::memcpy(req->buffer, buffer.get(), length); ::memcpy(req->buffer, buffer.get(), length);
if (::pthread_create(&req->thread, NULL, threadedNetworkRx, req) != 0) { if (!Thread::runAsThread(m_fneNetwork, threadedNetworkRx, req)) {
LogError(LOG_NET, "Error returned from pthread_create, err: %d", errno);
delete req; delete req;
return; return;
} }
@ -150,16 +148,21 @@ void* DiagNetwork::threadedNetworkRx(void* arg)
if (req != nullptr) { if (req != nullptr) {
::pthread_detach(req->thread); ::pthread_detach(req->thread);
FNENetwork* network = req->network; FNENetwork* network = static_cast<FNENetwork*>(req->obj);
if (network == nullptr) {
delete req;
return nullptr;
}
if (req->length > 0) { if (req->length > 0) {
uint32_t peerId = req->fneHeader.getPeerId(); uint32_t peerId = req->fneHeader.getPeerId();
uint32_t streamId = req->fneHeader.getStreamId(); uint32_t streamId = req->fneHeader.getStreamId();
std::stringstream peerName; std::stringstream peerName;
peerName << peerId << ":diag-rx-pckt"; peerName << peerId << ":diag-rx-pckt";
if (pthread_kill(req->thread, 0) == 0) { #ifdef _GNU_SOURCE
::pthread_setname_np(req->thread, peerName.str().c_str()); ::pthread_setname_np(req->thread, peerName.str().c_str());
} #endif // _GNU_SOURCE
// update current peer packet sequence and stream ID // update current peer packet sequence and stream ID
if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end()) && streamId != 0U) { if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end()) && streamId != 0U) {

@ -22,8 +22,6 @@
#include <string> #include <string>
#include <pthread.h>
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Prototypes // Class Prototypes
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

@ -229,7 +229,6 @@ void FNENetwork::processNetwork()
uint32_t peerId = fneHeader.getPeerId(); uint32_t peerId = fneHeader.getPeerId();
NetPacketRequest* req = new NetPacketRequest(); NetPacketRequest* req = new NetPacketRequest();
req->network = this;
req->peerId = peerId; req->peerId = peerId;
req->address = address; req->address = address;
@ -241,8 +240,7 @@ void FNENetwork::processNetwork()
req->buffer = new uint8_t[length]; req->buffer = new uint8_t[length];
::memcpy(req->buffer, buffer.get(), length); ::memcpy(req->buffer, buffer.get(), length);
if (::pthread_create(&req->thread, NULL, threadedNetworkRx, req) != 0) { if (!Thread::runAsThread(this, threadedNetworkRx, req)) {
LogError(LOG_NET, "Error returned from pthread_create, err: %d", errno);
delete req; delete req;
return; return;
} }
@ -392,16 +390,21 @@ void* FNENetwork::threadedNetworkRx(void* arg)
uint64_t now = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count(); uint64_t now = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
FNENetwork* network = req->network; FNENetwork* network = static_cast<FNENetwork*>(req->obj);
if (network == nullptr) {
delete req;
return nullptr;
}
if (req->length > 0) { if (req->length > 0) {
uint32_t peerId = req->fneHeader.getPeerId(); uint32_t peerId = req->fneHeader.getPeerId();
uint32_t streamId = req->fneHeader.getStreamId(); uint32_t streamId = req->fneHeader.getStreamId();
std::stringstream peerName; std::stringstream peerName;
peerName << peerId << ":rx-pckt"; peerName << peerId << ":rx-pckt";
if (pthread_kill(req->thread, 0) == 0) { #ifdef _GNU_SOURCE
::pthread_setname_np(req->thread, peerName.str().c_str()); ::pthread_setname_np(req->thread, peerName.str().c_str());
} #endif // _GNU_SOURCE
// update current peer packet sequence and stream ID // update current peer packet sequence and stream ID
if (peerId > 0 && (network->m_peers.find(peerId) != network->m_peers.end()) && streamId != 0U) { 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) void FNENetwork::peerACLUpdate(uint32_t peerId)
{ {
ACLUpdateRequest* req = new ACLUpdateRequest(); ACLUpdateRequest* req = new ACLUpdateRequest();
req->network = this;
req->peerId = peerId; req->peerId = peerId;
std::stringstream peerName; std::stringstream peerName;
peerName << peerId << ":acl-update"; peerName << peerId << ":acl-update";
if (::pthread_create(&req->thread, NULL, threadedACLUpdate, req) != 0) { if (!Thread::runAsThread(this, threadedACLUpdate, req)) {
LogError(LOG_NET, "Error returned from pthread_create, err: %d", errno);
delete req; delete req;
return; return;
} }
if (pthread_kill(req->thread, 0) == 0) { // pthread magic to rename the thread properly
::pthread_setname_np(req->thread, peerName.str().c_str()); #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. */ /* 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) { if (req != nullptr) {
::pthread_detach(req->thread); ::pthread_detach(req->thread);
std::string peerIdentity = req->network->resolvePeerIdentity(req->peerId); FNENetwork* network = static_cast<FNENetwork*>(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()); LogInfoEx(LOG_NET, "PEER %u (%s) sending ACL list updates", req->peerId, peerIdentity.c_str());
req->network->writeWhitelistRIDs(req->peerId); network->writeWhitelistRIDs(req->peerId);
req->network->writeBlacklistRIDs(req->peerId); network->writeBlacklistRIDs(req->peerId);
req->network->writeTGIDs(req->peerId); network->writeTGIDs(req->peerId);
req->network->writeDeactiveTGIDs(req->peerId); network->writeDeactiveTGIDs(req->peerId);
delete req; delete req;
} }

@ -39,8 +39,6 @@
#include <unordered_map> #include <unordered_map>
#include <mutex> #include <mutex>
#include <pthread.h>
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Prototypes // Class Prototypes
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -253,11 +251,8 @@ namespace network
* @brief Represents the data required for a peer ACL update request thread. * @brief Represents the data required for a peer ACL update request thread.
* @ingroup fne_network * @ingroup fne_network
*/ */
struct ACLUpdateRequest { struct ACLUpdateRequest : thread_t {
FNENetwork* network; //! Instance of the FNENetwork class.
uint32_t peerId; //! Peer ID for this request. 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. * @brief Represents the data required for a network packet handler thread.
* @ingroup fne_network * @ingroup fne_network
*/ */
struct NetPacketRequest { struct NetPacketRequest : thread_t {
FNENetwork* network; //! Instance of the FNENetwork class.
uint32_t peerId; //! Peer ID for this request. uint32_t peerId; //! Peer ID for this request.
sockaddr_storage address; //! IP Address and Port. sockaddr_storage address; //! IP Address and Port.
@ -278,8 +272,6 @@ namespace network
frame::RTPFNEHeader fneHeader; //! RTP FNE Header frame::RTPFNEHeader fneHeader; //! RTP FNE Header
int length = 0U; //! Length of raw data buffer int length = 0U; //! Length of raw data buffer
uint8_t *buffer; //! Raw data buffer uint8_t *buffer; //! Raw data buffer
pthread_t thread; //! Request Thread Handle.
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

@ -81,8 +81,6 @@ bool Host::readParams()
int8_t lto = (int8_t)systemConf["localTimeOffset"].as<int32_t>(0); int8_t lto = (int8_t)systemConf["localTimeOffset"].as<int32_t>(0);
removeLockFile();
LogInfo("General Parameters"); LogInfo("General Parameters");
if (!udpMasterMode) { if (!udpMasterMode) {
LogInfo(" DMR: %s", m_dmrEnabled ? "enabled" : "disabled"); LogInfo(" DMR: %s", m_dmrEnabled ? "enabled" : "disabled");
@ -100,7 +98,6 @@ bool Host::readParams()
LogInfo(" Net Mode Hang: %us", m_netModeHang); LogInfo(" Net Mode Hang: %us", m_netModeHang);
LogInfo(" Identity: %s", m_identity.c_str()); LogInfo(" Identity: %s", m_identity.c_str());
LogInfo(" Fixed Mode: %s", m_fixedMode ? "yes" : "no"); LogInfo(" Fixed Mode: %s", m_fixedMode ? "yes" : "no");
LogInfo(" Lock Filename: %s", g_lockFile.c_str());
LogInfo(" Local Time Offset: %dh", lto); LogInfo(" Local Time Offset: %dh", lto);
yaml::Node systemInfo = systemConf["info"]; yaml::Node systemInfo = systemConf["info"];

@ -12,6 +12,7 @@
*/ */
#include "Defines.h" #include "Defines.h"
#include "Host.h" #include "Host.h"
#include "HostMain.h"
using namespace modem; using namespace modem;
@ -20,284 +21,536 @@ using namespace modem;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Macro to start DMR duplex idle transmission (or beacon) // Macro to start DMR duplex idle transmission (or beacon)
#define START_DMR_DUPLEX_IDLE(x) \ #define START_DMR_DUPLEX_IDLE(x) \
if (control != nullptr) { \ if (host->m_dmr != nullptr) { \
if (m_duplex && !m_dmrTXTimer.isRunning()) { \ if (host->m_duplex && !host->m_dmrTXTimer.isRunning()) { \
m_modem->writeDMRStart(x); \ host->m_modem->writeDMRStart(x); \
m_dmrTXTimer.start(); \ host->m_dmrTXTimer.start(); \
} \ } \
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Private Class Members // 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) { thread_t* th = (thread_t*)arg;
if (m_dmrBeaconDurationTimer.isRunning() && !m_dmrBeaconDurationTimer.hasExpired()) { if (th != nullptr) {
if (m_dmrTSCCData && !m_dmrCtrlChannel) { ::pthread_detach(th->thread);
LogDebug(LOG_HOST, "interrupt DMR control, m_state = %u", m_state);
control->setCCHalted(true); std::string threadName("dmrd:frame1-r");
control->setCCRunning(false); Host* host = static_cast<Host*>(th->obj);
} if (host == nullptr) {
g_killed = true;
LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
} }
m_dmrBeaconDurationTimer.stop(); if (g_killed) {
} delete th;
} return nullptr;
}
/* Helper to read DMR slot 1 frames from modem. */
void Host::readFramesDMR1(dmr::Control* control, std::function<void()>&& afterReadCallback) LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
{ #ifdef _GNU_SOURCE
uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U]; ::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
if (control != nullptr) {
// read DMR slot 1 frames from the modem, and if there is any if (host->m_dmr != nullptr) {
// write those frames to the DMR controller while (!g_killed) {
uint32_t len = m_modem->readDMRFrame1(data); // scope is intentional
if (len > 0U) { {
if (m_state == STATE_IDLE) { // ------------------------------------------------------
// if the modem is in duplex -- process wakeup CSBKs // -- Read from Modem Processing --
if (m_duplex) { // ------------------------------------------------------
bool ret = control->processWakeup(data);
if (ret) { uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U];
m_modeTimer.setTimeout(m_rfModeHang); auto afterReadCallback = [&]() {
setState(STATE_DMR); if (host->m_dmr != nullptr) {
host->interruptDMRBeacon();
START_DMR_DUPLEX_IDLE(true);
if (afterReadCallback != nullptr) {
afterReadCallback();
} }
}
}
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) { // if there is a P25 CC running; halt the CC
afterReadCallback(); if (host->m_p25 != nullptr) {
} if (host->m_p25->getCCRunning() && !host->m_p25->getCCHalted()) {
} host->interruptP25Control();
} }
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();
} }
m_modeTimer.start(); // if there is a NXDN CC running; halt the CC
if (m_duplex) if (host->m_nxdn != nullptr) {
m_dmrTXTimer.start(); 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) { if (host->m_state != STATE_IDLE)
LogWarning(LOG_HOST, "DMR modem data received, state = %u", m_state); 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<void()>&& afterWriteCallback) void* Host::threadDMRWriter1(void* arg)
{ {
uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U]; thread_t* th = (thread_t*)arg;
if (th != nullptr) {
if (control != nullptr) { ::pthread_detach(th->thread);
// 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 std::string threadName("dmrd:frame1-w");
// to the modem Host* host = static_cast<Host*>(th->obj);
bool ret = m_modem->hasDMRSpace1(); if (host == nullptr) {
if (ret) { g_killed = true;
uint32_t nextLen = control->peekFrameLength(1U); LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
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);
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 LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
// any running DMR beacon #ifdef _GNU_SOURCE
if (!control->getCCRunning()) { ::pthread_setname_np(th->thread, threadName.c_str());
interruptDMRBeacon(control); #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<std::mutex> 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) { // if there is a NXDN CC running; halt the CC
afterWriteCallback(); 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); if (host->m_state != STATE_IDLE)
m_lastSrcId = control->getLastSrcId(1U); 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<void()>&& afterReadCallback) void* Host::threadDMRReader2(void* arg)
{ {
uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U]; thread_t* th = (thread_t*)arg;
if (th != nullptr) {
if (control != nullptr) { ::pthread_detach(th->thread);
// read DMR slot 2 frames from the modem, and if there is any
// write those frames to the DMR controller std::string threadName("dmrd:frame2-r");
uint32_t len = m_modem->readDMRFrame2(data); Host* host = static_cast<Host*>(th->obj);
if (len > 0U) { if (host == nullptr) {
if (m_state == STATE_IDLE) { g_killed = true;
// if the modem is in duplex -- process wakeup CSBKs LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
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);
control->processFrame(2U, data, len); if (g_killed) {
delete th;
return nullptr;
}
if (afterReadCallback != nullptr) { LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
afterReadCallback(); #ifdef _GNU_SOURCE
} ::pthread_setname_np(th->thread, threadName.c_str());
} #endif // _GNU_SOURCE
}
else if (m_state == STATE_DMR) { if (host->m_dmr != nullptr) {
// if the modem is in duplex, and hasn't started transmitting while (!g_killed) {
// process wakeup CSBKs // scope is intentional
if (m_duplex && !m_modem->hasTX()) { {
bool ret = control->processWakeup(data); // ------------------------------------------------------
if (ret) { // -- Read from Modem Processing --
m_modem->writeDMRStart(true); // ------------------------------------------------------
m_dmrTXTimer.start();
} uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U];
} auto afterReadCallback = [&]() {
else { if (host->m_dmr != nullptr) {
// process slot 2 frames host->interruptDMRBeacon();
bool ret = control->processFrame(2U, data, len);
if (ret) {
if (afterReadCallback != nullptr) {
afterReadCallback();
} }
m_modeTimer.start(); // if there is a P25 CC running; halt the CC
if (m_duplex) if (host->m_p25 != nullptr) {
m_dmrTXTimer.start(); 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) { if (host->m_state != STATE_IDLE)
LogWarning(LOG_HOST, "DMR modem data received, state = %u", m_state); 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<void()>&& afterWriteCallback) void* Host::threadDMRWriter2(void* arg)
{ {
uint8_t data[DMRDEF::DMR_FRAME_LENGTH_BYTES * 2U]; thread_t* th = (thread_t*)arg;
if (th != nullptr) {
if (control != nullptr) { ::pthread_detach(th->thread);
// 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 std::string threadName("dmrd:frame2-w");
// to the modem Host* host = static_cast<Host*>(th->obj);
bool ret = m_modem->hasDMRSpace2(); if (host == nullptr) {
if (ret) { g_killed = true;
uint32_t nextLen = control->peekFrameLength(1U); LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
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);
}
// if the state is DMR; start DMR idle frames and write DMR slot 2 data if (g_killed) {
if (m_state == STATE_DMR) { delete th;
START_DMR_DUPLEX_IDLE(true); 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<std::mutex> 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 // if there is a NXDN CC running; halt the CC
// any running DMR beacon if (host->m_nxdn != nullptr) {
if (!control->getCCRunning()) { if (host->m_nxdn->getCCRunning() && !host->m_nxdn->getCCHalted()) {
interruptDMRBeacon(control); 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) { if (host->m_state != STATE_IDLE)
afterWriteCallback(); 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); void Host::interruptDMRBeacon()
m_lastSrcId = control->getLastSrcId(2U); {
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();
} }
} }

@ -12,6 +12,7 @@
*/ */
#include "Defines.h" #include "Defines.h"
#include "Host.h" #include "Host.h"
#include "HostMain.h"
using namespace modem; using namespace modem;
@ -19,99 +20,219 @@ using namespace modem;
// Private Class Members // 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) { thread_t* th = (thread_t*)arg;
LogDebug(LOG_HOST, "interrupt NXDN control, m_state = %u", m_state); if (th != nullptr) {
control->setCCHalted(true); ::pthread_detach(th->thread);
if (m_nxdnBcastDurationTimer.isRunning() && !m_nxdnBcastDurationTimer.isPaused()) { std::string threadName("nxdd:frame-r");
m_nxdnBcastDurationTimer.pause(); Host* host = static_cast<Host*>(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<void()>&& afterReadCallback) LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
{ #ifdef _GNU_SOURCE
uint8_t data[NXDDEF::NXDN_FRAME_LENGTH_BYTES * 2U]; ::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
if (control != nullptr) {
uint32_t len = m_modem->readNXDNFrame(data); if (host->m_nxdn != nullptr) {
if (len > 0U) { while (!g_killed) {
if (m_state == STATE_IDLE) { // scope is intentional
// process NXDN frames {
bool ret = control->processFrame(data, len); // ------------------------------------------------------
if (ret) { // -- Read from Modem Processing --
m_modeTimer.setTimeout(m_rfModeHang); // ------------------------------------------------------
setState(STATE_NXDN);
uint8_t data[NXDDEF::NXDN_FRAME_LENGTH_BYTES * 2U];
if (afterReadCallback != nullptr) { auto afterReadCallback = [&]() {
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) { if (host->m_state != STATE_IDLE)
// process NXDN frames Thread::sleep(m_activeTickDelay);
bool ret = control->processFrame(data, len); if (host->m_state == STATE_IDLE)
if (ret) { Thread::sleep(m_idleTickDelay);
m_modeTimer.start();
}
}
else if (m_state != HOST_STATE_LOCKOUT) {
LogWarning(LOG_HOST, "NXDN modem data received, state = %u", m_state);
} }
} }
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<void()>&& afterWriteCallback) void* Host::threadNXDNWriter(void* arg)
{ {
uint8_t data[NXDDEF::NXDN_FRAME_LENGTH_BYTES * 2U]; thread_t* th = (thread_t*)arg;
if (th != nullptr) {
// check if there is space on the modem for NXDN frames, ::pthread_detach(th->thread);
// if there is read frames from the NXDN controller and write it
// to the modem std::string threadName("nxdd:frame-w");
if (control != nullptr) { Host* host = static_cast<Host*>(th->obj);
bool ret = m_modem->hasNXDNSpace(); if (host == nullptr) {
if (ret) { g_killed = true;
uint32_t nextLen = control->peekFrameLength(); LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
if (m_nxdnCtrlChannel) { }
if (m_nxdnDedicatedTxTestTimer.hasExpired() && !m_nxdnDedicatedTxTestTimer.isPaused()) {
m_nxdnDedicatedTxTestTimer.pause(); if (g_killed) {
if (!m_modem->hasTX() && m_modem->gotModemStatus() && m_state == STATE_NXDN && control->getCCRunning()) { delete th;
LogError(LOG_HOST, "NXDN dedicated control stopped transmitting, running = %u, halted = %u, frameLength = %u", control->getCCRunning(), control->getCCHalted(), nextLen); 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<std::mutex> 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); LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
if (len > 0U) { delete th;
// 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);
}
// if the state is NXDN; write NXDN data return nullptr;
if (m_state == STATE_NXDN) { }
m_modem->writeNXDNFrame(data, len);
if (afterWriteCallback != nullptr) { /* Helper to interrupt a running NXDN control channel. */
afterWriteCallback();
}
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(); if (m_nxdnBcastDurationTimer.isRunning() && !m_nxdnBcastDurationTimer.isPaused()) {
m_lastSrcId = control->getLastSrcId(); m_nxdnBcastDurationTimer.pause();
}
} }
} }
} }

@ -20,185 +20,303 @@ using namespace modem;
// Private Class Members // 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) { thread_t* th = (thread_t*)arg;
LogDebug(LOG_HOST, "interrupt P25 control, m_state = %u", m_state); if (th != nullptr) {
control->setCCHalted(true); ::pthread_detach(th->thread);
if (m_p25BcastDurationTimer.isRunning() && !m_p25BcastDurationTimer.isPaused()) { std::string threadName("p25d:frame-r");
m_p25BcastDurationTimer.pause(); Host* host = static_cast<Host*>(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<void()>&& afterReadCallback) LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
{ #ifdef _GNU_SOURCE
uint8_t data[P25DEF::P25_PDU_FRAME_LENGTH_BYTES * 2U]; ::pthread_setname_np(th->thread, threadName.c_str());
#endif // _GNU_SOURCE
// 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();
}
if (m_state == STATE_IDLE) { if (host->m_p25 != nullptr) {
m_modeTimer.setTimeout(m_rfModeHang); while (!g_killed) {
setState(STATE_P25); // scope is intentional
} {
// ------------------------------------------------------
// -- Read from Modem Processing --
// ------------------------------------------------------
if (m_state == STATE_P25) { uint8_t data[P25DEF::P25_PDU_FRAME_LENGTH_BYTES * 2U];
m_modeTimer.start(); auto afterReadCallback = [&]() {
if (host->m_dmr != nullptr) {
host->interruptDMRBeacon();
} }
// if the modem is in duplex -- handle P25 CC burst control // if there is a NXDN CC running; halt the CC
if (m_duplex) { if (host->m_nxdn != nullptr) {
if (m_p25BcastDurationTimer.isPaused() && !control->getCCHalted()) { if (host->m_nxdn->getCCRunning() && !host->m_nxdn->getCCHalted()) {
m_p25BcastDurationTimer.resume(); host->interruptNXDNControl();
} }
}
};
if (control->getCCHalted()) { // read P25 frames from modem, and if there are frames
control->setCCHalted(false); // 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) { afterReadCallback();
m_modeTimer.stop(); }
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) { if (host->m_state != STATE_IDLE)
// process P25 frames Thread::sleep(m_activeTickDelay);
bool ret = control->processFrame(data, len); if (host->m_state == STATE_IDLE)
if (ret) { Thread::sleep(m_idleTickDelay);
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);
} }
} }
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<void()>&& afterWriteCallback) void* Host::threadP25Writer(void* arg)
{ {
uint8_t data[P25DEF::P25_PDU_FRAME_LENGTH_BYTES * 2U]; thread_t* th = (thread_t*)arg;
if (th != nullptr) {
// check if there is space on the modem for P25 frames, ::pthread_detach(th->thread);
// if there is read frames from the P25 controller and write it
// to the modem std::string threadName("p25d:frame-w");
if (control != nullptr) { Host* host = static_cast<Host*>(th->obj);
uint8_t nextLen = control->peekFrameLength(); if (host == nullptr) {
if (m_p25CtrlChannel) { g_killed = true;
if (m_p25DedicatedTxTestTimer.hasExpired() && !m_p25DedicatedTxTestTimer.isPaused()) { LogDebug(LOG_HOST, "[FAIL] %s", threadName.c_str());
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);
}
}
} }
if (nextLen > 0U) { if (g_killed) {
bool ret = m_modem->hasP25Space(nextLen); delete th;
if (ret) { return nullptr;
uint32_t len = control->getFrame(data); }
if (len > 0U) {
// if the state is idle; set to P25 and start mode timer LogDebug(LOG_HOST, "[ OK ] %s", threadName.c_str());
if (m_state == STATE_IDLE) { #ifdef _GNU_SOURCE
m_modeTimer.setTimeout(m_netModeHang); ::pthread_setname_np(th->thread, threadName.c_str());
setState(STATE_P25); #endif // _GNU_SOURCE
}
StopWatch stopWatch;
stopWatch.start();
// if the state is P25; write P25 frame data if (host->m_p25 != nullptr) {
if (m_state == STATE_P25) { while (!g_killed) {
m_modem->writeP25Frame(data, len); host->m_p25TxWatchdogTimer.start();
if (afterWriteCallback != nullptr) { uint32_t ms = stopWatch.elapsed();
afterWriteCallback(); stopWatch.start();
host->m_p25TxLoopMS = ms;
// scope is intentional
{
std::lock_guard<std::mutex> 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(); // check if there is space on the modem for P25 frames,
m_lastSrcId = control->getLastSrcId(); // if there is read frames from the P25 controller and write it
} // to the modem
else { if (host->m_p25 != nullptr) {
nextLen = 0U; 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 (nextLen > 0U) {
// if we have no P25 data, and we're either idle or P25 state, check if we bool ret = host->m_modem->hasP25Space(nextLen);
// need to be starting the CC running flag or writing end of voice call data if (ret) {
if (m_state == STATE_IDLE || m_state == STATE_P25) { uint32_t len = host->m_p25->getFrame(data);
if (control->getCCHalted()) { if (len > 0U) {
control->setCCHalted(false); // 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 // if the state is P25; write P25 frame data
bool ret = control->writeRF_VoiceEnd(); if (host->m_state == STATE_P25) {
if (ret) { host->m_modem->writeP25Frame(data, len);
if (m_state == STATE_IDLE) {
m_modeTimer.setTimeout(m_netModeHang); afterWriteCallback();
setState(STATE_P25);
} 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) { // if the modem is in duplex -- handle P25 CC burst
m_modeTimer.start(); 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 LogDebug(LOG_HOST, "[STOP] %s", threadName.c_str());
if (m_duplex) { delete th;
if (m_p25BcastDurationTimer.isPaused() && !control->getCCHalted()) { }
m_p25BcastDurationTimer.resume();
}
if (control->getCCHalted()) { return nullptr;
control->setCCHalted(false); }
}
if (g_fireP25Control) { /* Helper to interrupt a running P25 control channel. */
m_modeTimer.stop();
} 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();
} }
} }
} }

File diff suppressed because it is too large Load Diff

@ -44,6 +44,9 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <functional> #include <functional>
#include <mutex>
#include <pthread.h>
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Prototypes // Class Prototypes
@ -190,14 +193,20 @@ private:
uint32_t m_adjSiteLoopMS; uint32_t m_adjSiteLoopMS;
Timer m_adjSiteLoopWatchdogTimer; Timer m_adjSiteLoopWatchdogTimer;
uint8_t m_activeTickDelay; static std::mutex m_clockingMutex;
uint8_t m_idleTickDelay;
static uint8_t m_activeTickDelay;
static uint8_t m_idleTickDelay;
friend class RESTAPI; friend class RESTAPI;
std::string m_restAddress; std::string m_restAddress;
uint16_t m_restPort; uint16_t m_restPort;
RESTAPI *m_RESTAPI; RESTAPI *m_RESTAPI;
std::unique_ptr<dmr::Control> m_dmr;
std::unique_ptr<p25::Control> m_p25;
std::unique_ptr<nxdn::Control> m_nxdn;
/** /**
* @brief Helper to generate the status of the host in JSON format. * @brief Helper to generate the status of the host in JSON format.
* @returns json::object Host status as a JSON object. * @returns json::object Host status as a JSON object.
@ -235,14 +244,29 @@ private:
void setState(uint8_t state); void setState(uint8_t state);
/** /**
* @brief Helper to create the state lock file. * @brief Entry point to modem clocking thread.
* @param state * @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) // Configuration (Host.Config.cpp)
/** /**
@ -263,75 +287,72 @@ private:
// Digital Mobile Radio (Host.DMR.cpp) // Digital Mobile Radio (Host.DMR.cpp)
/** /**
* @brief Helper to interrupt a running DMR beacon. * @brief Entry point to read DMR slot 1 frames from modem Rx queue.
* @param control Instance of the dmr::Control class. * @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. * @brief Entry point to write DMR slot 1 frames to modem.
* @param control Instance of the dmr::Control class. * @param arg Instance of the thread_t structure.
* @param afterReadCallback Function callback after reading frames. * @returns void* (Ignore)
*/ */
void readFramesDMR1(dmr::Control* control, std::function<void()>&& afterReadCallback); static void* threadDMRWriter1(void* arg);
/** /**
* @brief Helper to write DMR slot 1 frames to modem. * @brief Entry point to read DMR slot 2 frames from modem Rx queue.
* @param control Instance of the dmr::Control class. * @param arg Instance of the thread_t structure.
* @param afterWriteCallback Function callback writing reading frames. * @returns void* (Ignore)
*/ */
void writeFramesDMR1(dmr::Control* control, std::function<void()>&& afterWriteCallback); static void* threadDMRReader2(void* arg);
/** /**
* @brief Helper to read DMR slot 2 frames from modem. * @brief Entry point to write DMR slot 2 frames to modem.
* @param control Instance of the dmr::Control class. * @param arg Instance of the thread_t structure.
* @param afterReadCallback Function callback after reading frames. * @returns void* (Ignore)
*/ */
void readFramesDMR2(dmr::Control* control, std::function<void()>&& afterReadCallback); static void* threadDMRWriter2(void* arg);
/** /**
* @brief Helper to write DMR slot 2 frames to modem. * @brief Helper to interrupt a running DMR beacon.
* @param control Instance of the dmr::Control class.
* @param afterWriteCallback Function callback writing reading frames.
*/ */
void writeFramesDMR2(dmr::Control* control, std::function<void()>&& afterWriteCallback); void interruptDMRBeacon();
// Project 25 (Host.P25.cpp) // Project 25 (Host.P25.cpp)
/** /**
* @brief Helper to interrupt a running P25 control channel. * @brief Entry point to read P25 frames from modem Rx queue.
* @param control Instance of the p25::Control class. * @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. * @brief Entry point to write P25 frames to modem.
* @param control Instance of the p25::Control class. * @param arg Instance of the thread_t structure.
* @param afterReadCallback Function callback after reading frames. * @returns void* (Ignore)
*/ */
void readFramesP25(p25::Control* control, std::function<void()>&& afterReadCallback); static void* threadP25Writer(void* arg);
/** /**
* @brief Helper to write P25 frames to modem. * @brief Helper to interrupt a running P25 control channel.
* @param control Instance of the p25::Control class.
* @param afterWriteCallback Function callback writing reading frames.
*/ */
void writeFramesP25(p25::Control* control, std::function<void()>&& afterWriteCallback); void interruptP25Control();
// Next Generation Digital Narrowband (Host.NXDN.cpp) // Next Generation Digital Narrowband (Host.NXDN.cpp)
/** /**
* @brief Helper to interrupt a running NXDN control channel. * @brief Entry point to read NXDN frames from modem Rx queue.
* @param control Instance of the nxdn::Control class. * @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. * @brief Entry point to write NXDN frames to modem.
* @param control Instance of the nxdn::Control class. * @param arg Instance of the thread_t structure.
* @param afterReadCallback Function callback after reading frames. * @returns void* (Ignore)
*/ */
void readFramesNXDN(nxdn::Control* control, std::function<void()>&& afterReadCallback); static void* threadNXDNWriter(void* arg);
/** /**
* @brief Helper to write NXDN frames to modem. * @brief Helper to interrupt a running NXDN control channel.
* @param control Instance of the nxdn::Control class.
* @param afterWriteCallback Function callback writing reading frames.
*/ */
void writeFramesNXDN(nxdn::Control* control, std::function<void()>&& afterWriteCallback); void interruptNXDNControl();
}; };
#endif // __HOST_H__ #endif // __HOST_H__

@ -299,13 +299,13 @@ int main(int argc, char** argv)
} }
if (g_signal == 2) if (g_signal == 2)
::LogInfoEx(LOG_HOST, "Exited on receipt of SIGINT"); ::LogInfoEx(LOG_HOST, "[STOP] dvmhost:main SIGINT");
if (g_signal == 15) if (g_signal == 15)
::LogInfoEx(LOG_HOST, "Exited on receipt of SIGTERM"); ::LogInfoEx(LOG_HOST, "[STOP] dvmhost:main SIGTERM");
if (g_signal == 1) if (g_signal == 1)
::LogInfoEx(LOG_HOST, "Restarting on receipt of SIGHUP"); ::LogInfoEx(LOG_HOST, "[RSTR] dvmhost:main SIGHUP");
} while (g_signal == 1); } while (g_signal == 1);
::LogFinalise(); ::LogFinalise();

@ -139,7 +139,7 @@ Modem::Modem(port::IModemPort* port, bool duplex, bool rxInvert, bool txInvert,
m_rxDMRQueue2(dmrQueueSize, "Modem RX DMR2"), m_rxDMRQueue2(dmrQueueSize, "Modem RX DMR2"),
m_rxP25Queue(p25QueueSize, "Modem RX P25"), m_rxP25Queue(p25QueueSize, "Modem RX P25"),
m_rxNXDNQueue(nxdnQueueSize, "Modem RX NXDN"), m_rxNXDNQueue(nxdnQueueSize, "Modem RX NXDN"),
m_statusTimer(1000U, 0U, 250U), m_statusTimer(1000U, 0U, MODEM_POLL_TIME_IDLE),
m_inactivityTimer(1000U, 8U), m_inactivityTimer(1000U, 8U),
m_dmrSpace1(0U), m_dmrSpace1(0U),
m_dmrSpace2(0U), m_dmrSpace2(0U),
@ -149,6 +149,10 @@ Modem::Modem(port::IModemPort* port, bool duplex, bool rxInvert, bool txInvert,
m_cd(false), m_cd(false),
m_lockout(false), m_lockout(false),
m_error(false), m_error(false),
m_dmr1ReadLock(),
m_dmr2ReadLock(),
m_p25ReadLock(),
m_nxdnReadLock(),
m_ignoreModemConfigArea(ignoreModemConfigArea), m_ignoreModemConfigArea(ignoreModemConfigArea),
m_flashDisabled(false), m_flashDisabled(false),
m_gotModemStatus(false), m_gotModemStatus(false),
@ -507,7 +511,7 @@ bool Modem::open()
void Modem::clock(uint32_t ms) void Modem::clock(uint32_t ms)
{ {
// poll the modem status every 250ms // poll the modem status
m_statusTimer.clock(ms); m_statusTimer.clock(ms);
if (m_statusTimer.hasExpired()) { if (m_statusTimer.hasExpired()) {
getStatus(); getStatus();
@ -550,8 +554,7 @@ void Modem::clock(uint32_t ms)
case CMD_DMR_DATA1: case CMD_DMR_DATA1:
{ {
if (m_dmrEnabled) { if (m_dmrEnabled) {
//if (m_trace) std::lock_guard<std::mutex> lock(m_dmr1ReadLock);
// Utils::dump(1U, "RX DMR Data 1", m_buffer, m_length);
if (m_rspDoubleLength) { if (m_rspDoubleLength) {
LogError(LOG_MODEM, "CMD_DMR_DATA1 double length?; len = %u", m_length); 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: case CMD_DMR_DATA2:
{ {
if (m_dmrEnabled) { if (m_dmrEnabled) {
//if (m_trace) std::lock_guard<std::mutex> lock(m_dmr2ReadLock);
// Utils::dump(1U, "RX DMR Data 2", m_buffer, m_length);
if (m_rspDoubleLength) { if (m_rspDoubleLength) {
LogError(LOG_MODEM, "CMD_DMR_DATA2 double length?; len = %u", m_length); 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: case CMD_DMR_LOST1:
{ {
if (m_dmrEnabled) { if (m_dmrEnabled) {
//if (m_trace) std::lock_guard<std::mutex> lock(m_dmr1ReadLock);
// Utils::dump(1U, "RX DMR Lost 1", m_buffer, m_length);
if (m_rspDoubleLength) { if (m_rspDoubleLength) {
LogError(LOG_MODEM, "CMD_DMR_LOST1 double length?; len = %u", m_length); 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: case CMD_DMR_LOST2:
{ {
if (m_dmrEnabled) { if (m_dmrEnabled) {
//if (m_trace) std::lock_guard<std::mutex> lock(m_dmr2ReadLock);
// Utils::dump(1U, "RX DMR Lost 2", m_buffer, m_length);
if (m_rspDoubleLength) { if (m_rspDoubleLength) {
LogError(LOG_MODEM, "CMD_DMR_LOST2 double length?; len = %u", m_length); 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: case CMD_P25_DATA:
{ {
if (m_p25Enabled) { if (m_p25Enabled) {
//if (m_trace) std::lock_guard<std::mutex> lock(m_p25ReadLock);
// Utils::dump(1U, "RX P25 Data", m_buffer, m_length);
uint8_t length[2U]; uint8_t length[2U];
if (m_length > 255U) if (m_length > 255U)
@ -663,8 +662,7 @@ void Modem::clock(uint32_t ms)
case CMD_P25_LOST: case CMD_P25_LOST:
{ {
if (m_p25Enabled) { if (m_p25Enabled) {
//if (m_trace) std::lock_guard<std::mutex> lock(m_p25ReadLock);
// Utils::dump(1U, "RX P25 Lost", m_buffer, m_length);
if (m_rspDoubleLength) { if (m_rspDoubleLength) {
LogError(LOG_MODEM, "CMD_P25_LOST double length?; len = %u", m_length); 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: case CMD_NXDN_DATA:
{ {
if (m_nxdnEnabled) { if (m_nxdnEnabled) {
//if (m_trace) std::lock_guard<std::mutex> lock(m_nxdnReadLock);
// Utils::dump(1U, "RX NXDN Data", m_buffer, m_length);
if (m_rspDoubleLength) { if (m_rspDoubleLength) {
LogError(LOG_MODEM, "CMD_NXDN_DATA double length?; len = %u", m_length); 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: case CMD_NXDN_LOST:
{ {
if (m_nxdnEnabled) { if (m_nxdnEnabled) {
//if (m_trace) std::lock_guard<std::mutex> lock(m_nxdnReadLock);
// Utils::dump(1U, "RX NXDN Lost", m_buffer, m_length);
if (m_rspDoubleLength) { if (m_rspDoubleLength) {
LogError(LOG_MODEM, "CMD_NXDN_LOST double length?; len = %u", m_length); LogError(LOG_MODEM, "CMD_NXDN_LOST double length?; len = %u", m_length);
@ -726,9 +722,6 @@ void Modem::clock(uint32_t ms)
/** General */ /** General */
case CMD_GET_STATUS: case CMD_GET_STATUS:
{ {
//if (m_trace)
// Utils::dump(1U, "Get Status", m_buffer, m_length);
m_isHotspot = (m_buffer[3U] & 0x01U) == 0x01U; m_isHotspot = (m_buffer[3U] & 0x01U) == 0x01U;
// override hotspot flag if we're forcing hotspot // 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. */ /* Reads DMR Slot 1 frame data from the DMR Slot 1 ring buffer. */
uint32_t Modem::readDMRFrame1(uint8_t* data) uint32_t Modem::readDMRFrame1(uint8_t* data)
{ {
assert(data != nullptr); assert(data != nullptr);
std::lock_guard<std::mutex> lock(m_dmr1ReadLock);
if (m_rxDMRQueue1.isEmpty()) if (m_rxDMRQueue1.isEmpty())
return 0U; return 0U;
uint8_t len = 0U; uint8_t len = 0U;
m_rxDMRQueue1.peek(&len, 1U); 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) { if (m_rxDMRQueue1.dataSize() >= len) {
m_rxDMRQueue1.get(&len, 1U); // ensure we pop the length off m_rxDMRQueue1.get(&len, 1U); // ensure we pop the length off
m_rxDMRQueue1.get(data, len); m_rxDMRQueue1.get(data, len);
@ -918,17 +944,50 @@ uint32_t Modem::readDMRFrame1(uint8_t* data)
return 0U; 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. */ /* Reads DMR Slot 2 frame data from the DMR Slot 2 ring buffer. */
uint32_t Modem::readDMRFrame2(uint8_t* data) uint32_t Modem::readDMRFrame2(uint8_t* data)
{ {
assert(data != nullptr); assert(data != nullptr);
std::lock_guard<std::mutex> lock(m_dmr2ReadLock);
if (m_rxDMRQueue2.isEmpty()) if (m_rxDMRQueue2.isEmpty())
return 0U; return 0U;
uint8_t len = 0U; uint8_t len = 0U;
m_rxDMRQueue2.peek(&len, 1U); 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) { if (m_rxDMRQueue2.dataSize() >= len) {
m_rxDMRQueue2.get(&len, 1U); // ensure we pop the length off m_rxDMRQueue2.get(&len, 1U); // ensure we pop the length off
m_rxDMRQueue2.get(data, len); m_rxDMRQueue2.get(data, len);
@ -939,11 +998,41 @@ uint32_t Modem::readDMRFrame2(uint8_t* data)
return 0U; 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. */ /* Reads P25 frame data from the P25 ring buffer. */
uint32_t Modem::readP25Frame(uint8_t* data) uint32_t Modem::readP25Frame(uint8_t* data)
{ {
assert(data != nullptr); assert(data != nullptr);
std::lock_guard<std::mutex> lock(m_p25ReadLock);
if (m_rxP25Queue.isEmpty()) if (m_rxP25Queue.isEmpty())
return 0U; return 0U;
@ -971,17 +1060,50 @@ uint32_t Modem::readP25Frame(uint8_t* data)
return 0U; 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. */ /* Reads NXDN frame data from the NXDN ring buffer. */
uint32_t Modem::readNXDNFrame(uint8_t* data) uint32_t Modem::readNXDNFrame(uint8_t* data)
{ {
assert(data != nullptr); assert(data != nullptr);
std::lock_guard<std::mutex> lock(m_nxdnReadLock);
if (m_rxNXDNQueue.isEmpty()) if (m_rxNXDNQueue.isEmpty())
return 0U; return 0U;
uint8_t len = 0U; uint8_t len = 0U;
m_rxNXDNQueue.peek(&len, 1U); 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) { if (m_rxNXDNQueue.dataSize() >= len) {
m_rxNXDNQueue.get(&len, 1U); // ensure we pop the length off m_rxNXDNQueue.get(&len, 1U); // ensure we pop the length off
m_rxNXDNQueue.get(data, len); m_rxNXDNQueue.get(data, len);
@ -1569,6 +1691,12 @@ DVM_STATE Modem::getState() const
bool Modem::setState(DVM_STATE state) 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]; uint8_t buffer[4U];
buffer[0U] = DVM_SHORT_FRAME_START; buffer[0U] = DVM_SHORT_FRAME_START;

@ -34,6 +34,7 @@
#include <string> #include <string>
#include <functional> #include <functional>
#include <mutex>
/** /**
* @addtogroup modem * @addtogroup modem
@ -269,6 +270,9 @@ namespace modem
const uint32_t MAX_ADC_OVERFLOW = 128U; const uint32_t MAX_ADC_OVERFLOW = 128U;
const uint32_t MAX_DAC_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(); 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. * @brief Reads DMR Slot 1 frame data from the DMR Slot 1 ring buffer.
* @param[out] data Buffer to write frame data to. * @param[out] data Buffer to write frame data to.
* @returns uint32_t Length of data read from ring buffer. * @returns uint32_t Length of data read from ring buffer.
*/ */
uint32_t readDMRFrame1(uint8_t* data); 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. * @brief Reads DMR Slot 2 frame data from the DMR Slot 1 ring buffer.
* @param[out] data Buffer to write frame data to. * @param[out] data Buffer to write frame data to.
* @returns uint32_t Length of data read from ring buffer. * @returns uint32_t Length of data read from ring buffer.
*/ */
uint32_t readDMRFrame2(uint8_t* data); 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. * @brief Reads P25 frame data from the P25 ring buffer.
* @param[out] data Buffer to write frame data to. * @param[out] data Buffer to write frame data to.
* @returns uint32_t Length of data read from ring buffer. * @returns uint32_t Length of data read from ring buffer.
*/ */
uint32_t readP25Frame(uint8_t* data); 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. * @brief Reads NXDN frame data from the NXDN ring buffer.
* @param[out] data Buffer to write frame data to. * @param[out] data Buffer to write frame data to.
@ -763,6 +787,11 @@ namespace modem
bool m_lockout; bool m_lockout;
bool m_error; bool m_error;
std::mutex m_dmr1ReadLock;
std::mutex m_dmr2ReadLock;
std::mutex m_p25ReadLock;
std::mutex m_nxdnReadLock;
bool m_ignoreModemConfigArea; bool m_ignoreModemConfigArea;
bool m_flashDisabled; bool m_flashDisabled;

Loading…
Cancel
Save

Powered by TurnKey Linux.