From 6ebf2be856bd6253f4e156d5fc584d5b8b25b15d Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 9 Dec 2025 09:51:34 -0500 Subject: [PATCH] EXPERIMENTAL: add option for 1 second delayed network TDU when a subscriber dekeys; --- configs/config.example.yml | 3 +++ src/host/p25/Control.cpp | 31 +++++++++++++++++++++++++++++++ src/host/p25/Control.h | 5 +++++ src/host/p25/packet/Voice.cpp | 9 ++++++++- 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/configs/config.example.yml b/configs/config.example.yml index 233511bc..0cae6bb7 100644 --- a/configs/config.example.yml +++ b/configs/config.example.yml @@ -249,6 +249,9 @@ protocols: forceAllowTG0: false # Flag indicating whether or not a TGID will be tested for affiliations before being granted. ignoreAffiliationCheck: false + # Flag indicating whether or not a P25 TDU (call terminator) will occur to the network immmediately after a + # SU dekey, or on a fixed 1 second delay. (This is useful for systems that require fast call termination after dekeying.) + immediateCallTerm: true # Flag indicating that the host will attempt to automatically inhibit unauthorized RIDs (those not in the # RID ACL list). inhibitUnauthorized: false diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index d9112f7d..569dcc4a 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -37,6 +37,8 @@ const uint8_t MAX_SYNC_BYTES_ERRS = 4U; const uint32_t TSBK_PCH_CCH_CNT = 6U; const uint32_t MAX_PREAMBLE_TDU_CNT = 64U; +const uint32_t VOICE_CALL_TERM_TIMEOUT = 1000U; // ms + // --------------------------------------------------------------------------- // Static Class Members // --------------------------------------------------------------------------- @@ -83,6 +85,7 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q m_demandUnitRegForRefusedAff(true), m_dfsiFDX(false), m_forceAllowTG0(false), + m_immediateCallTerm(true), m_defaultNetIdleTalkgroup(0U), m_idenTable(idenTable), m_ridLookup(ridLookup), @@ -113,6 +116,7 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q m_adjSiteUpdate(1000U, 75U), m_activeTGUpdate(1000U, 5U), m_ccPacketInterval(1000U, 0U, 10U), + m_rfVoiceCallTermTimeout(1000U, VOICE_CALL_TERM_TIMEOUT), m_interval(), m_hangCount(3U * 8U), m_tduPreambleCount(8U), @@ -364,6 +368,8 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw LogWarning(LOG_P25, "TGID 0 (P25 blackhole talkgroup) will be allowed. This is not recommended, and can cause undesired behavior, it is typically only needed by poorly behaved systems."); } + m_immediateCallTerm = p25Protocol["immediateCallTerm"].as(true); + /* ** Voice Silence and Frame Loss Thresholds */ @@ -558,6 +564,8 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw LogInfo(" Conventional Network Grant Demand: %s", m_convNetGrantDemand ? "yes" : "no"); LogInfo(" Demand Unit Registration for Refused Affiliation: %s", m_demandUnitRegForRefusedAff ? "yes" : "no"); + LogInfo(" Immediate Call Termination: %s", m_immediateCallTerm ? "yes" : "no"); + LogInfo(" Notify VCs of Active TGs: %s", m_ccNotifyActiveTG ? "yes" : "no"); if (disableUnitRegTimeout) { @@ -930,6 +938,7 @@ void Control::clock() // handle timeouts and hang timers m_rfTimeout.clock(ms); m_netTimeout.clock(ms); + m_rfVoiceCallTermTimeout.clock(ms); if (m_rfTGHang.isRunning()) { m_rfTGHang.clock(ms); @@ -993,6 +1002,24 @@ void Control::clock() m_netTGHang.stop(); } + if (m_rfVoiceCallTermTimeout.isRunning() && m_rfVoiceCallTermTimeout.hasExpired()) { + m_rfVoiceCallTermTimeout.stop(); + + + p25::lc::LC lc = p25::lc::LC(); + lc.setLCO(P25DEF::LCO::GROUP); + lc.setDstId(m_rfCallTermDstId); + lc.setSrcId(m_rfCallTermSrcId); + + p25::data::LowSpeedData lsd = p25::data::LowSpeedData(); + + uint8_t controlByte = 0x00U; + m_network->writeP25TDU(lc, lsd); + + m_rfCallTermDstId = 0U; + m_rfCallTermSrcId = 0U; + } + if (m_netState == RS_NET_AUDIO || m_netState == RS_NET_DATA) { m_networkWatchdog.clock(ms); @@ -1664,6 +1691,10 @@ void Control::processFrameLoss() m_rfLastSrcId = 0U; m_rfTGHang.stop(); + m_rfVoiceCallTermTimeout.stop(); + m_rfCallTermSrcId = 0U; + m_rfCallTermDstId = 0U; + m_tailOnIdle = true; m_rfTimeout.stop(); diff --git a/src/host/p25/Control.h b/src/host/p25/Control.h index e14665cc..7cb9273a 100644 --- a/src/host/p25/Control.h +++ b/src/host/p25/Control.h @@ -315,6 +315,7 @@ namespace p25 bool m_demandUnitRegForRefusedAff; bool m_dfsiFDX; bool m_forceAllowTG0; + bool m_immediateCallTerm; uint32_t m_defaultNetIdleTalkgroup; @@ -358,6 +359,10 @@ namespace p25 Timer m_ccPacketInterval; + uint32_t m_rfCallTermSrcId; + uint32_t m_rfCallTermDstId; + Timer m_rfVoiceCallTermTimeout; + StopWatch m_interval; uint32_t m_hangCount; diff --git a/src/host/p25/packet/Voice.cpp b/src/host/p25/packet/Voice.cpp index ae4e4597..416c79ab 100644 --- a/src/host/p25/packet/Voice.cpp +++ b/src/host/p25/packet/Voice.cpp @@ -1080,7 +1080,14 @@ bool Voice::process(uint8_t* data, uint32_t len) } if (duid == DUID::TDU) { - m_p25->writeRF_TDU(false); + if (m_p25->m_immediateCallTerm) + m_p25->writeRF_TDU(false); + else { + m_p25->m_rfCallTermDstId = m_rfLC.getDstId(); + m_p25->m_rfCallTermSrcId = m_rfLC.getSrcId(); + m_p25->m_rfVoiceCallTermTimeout.start(); + m_p25->writeRF_TDU(true); + } m_lastDUID = duid;