From a43efddc1fe76d25a00ad07e2d2c38b8d4607930 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 26 Jan 2026 11:56:56 -0500 Subject: [PATCH] add explicit TDU TG release option, this option allows a CC to process incoming TDUs to determine if a channel grant should be released; add more explicit TDU process logging to the FNE, ensure TDUs being sent outside a call are being logged properly; during a TG unpermit on a non-authoritative VC, transmit a burst of TDUs on the outbound VC RF interface; add extra verbosity to the active TG logging; --- configs/config.example.yml | 2 + src/fne/network/callhandler/TagP25Data.cpp | 6 +++ src/host/p25/Control.cpp | 43 ++++++++++++++++++++-- src/host/p25/Control.h | 1 + 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/configs/config.example.yml b/configs/config.example.yml index 2587763b..8c4c6f59 100644 --- a/configs/config.example.yml +++ b/configs/config.example.yml @@ -231,6 +231,8 @@ protocols: disableGrantSourceIdCheck: false # Flag indicating whether or not the adjacent site broadcasts are disabled. disableAdjSiteBroadcast: false + # Flag indicating a dedicated CC while process call TDUs and explicitly release grants when a call TDU occurs. + explicitTDUGrantRelease: true # Flag indicating immediate TSDUs will be sent twice. redundantImmediate: true # Flag indicating whether redundant grant responses should be transmitted. diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 346db37e..af7f2295 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -316,6 +316,12 @@ bool TagP25Data::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId m_network->eraseStreamPktSeq(peerId, streamId); } + } else { + #define NONCALL_TDU_LOG "P25, Non-Call TDU, peer = %u, ssrc = %u, sysId = $%03X, netId = $%05X, srcId = %u, dstId = %u, duration = %u, streamId = %u, fromUpstream = %u", peerId, ssrc, sysId, netId, srcId, dstId, duration / 1000, streamId, fromUpstream + if (m_network->m_logUpstreamCallStartEnd && fromUpstream) + LogInfoEx(LOG_PEER, NONCALL_TDU_LOG); + else if (!fromUpstream) + LogInfoEx(LOG_MASTER, NONCALL_TDU_LOG); } } diff --git a/src/host/p25/Control.cpp b/src/host/p25/Control.cpp index 2f7afa5e..6f369f61 100644 --- a/src/host/p25/Control.cpp +++ b/src/host/p25/Control.cpp @@ -86,6 +86,7 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q m_dfsiFDX(false), m_forceAllowTG0(false), m_immediateCallTerm(true), + m_explicitTDUGrantRelease(true), m_defaultNetIdleTalkgroup(0U), m_idenTable(idenTable), m_ridLookup(ridLookup), @@ -369,6 +370,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw } m_immediateCallTerm = p25Protocol["immediateCallTerm"].as(true); + m_explicitTDUGrantRelease = p25Protocol["explicitTDUGrantRelease"].as(true); /* ** Voice Silence and Frame Loss Thresholds @@ -565,6 +567,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, const std::string cw LogInfo(" Demand Unit Registration for Refused Affiliation: %s", m_demandUnitRegForRefusedAff ? "yes" : "no"); LogInfo(" Immediate Call Termination: %s", m_immediateCallTerm ? "yes" : "no"); + LogInfo(" Explicit TDU Grant Release: %s", m_explicitTDUGrantRelease ? "yes" : "no"); LogInfo(" Notify VCs of Active TGs: %s", m_ccNotifyActiveTG ? "yes" : "no"); @@ -1247,6 +1250,12 @@ void Control::permittedTG(uint32_t dstId, bool dataPermit) LogInfoEx(LOG_P25, "non-authoritative TG permit, dstId = %u", dstId); } + // if this a unpermit ensure we write TDUs + if (m_permittedDstId != 0U && dstId == 0U) { + for (uint8_t i = 0; i < 2; i++) + writeRF_TDU(true); + } + m_permittedDstId = dstId; // is this a data permit? @@ -1648,6 +1657,32 @@ void Control::processNetwork() return; } + // if we're a control channel and this is a TDU trying to terminate a call release the channel + if (duid == DUID::TDU && m_enableControl && m_dedicatedControl && m_explicitTDUGrantRelease) { + if (srcId == 0U && dstId == 0U) { + LogError(LOG_NET, "P25, invalid call, srcId = %u, dstId = %u", srcId, dstId); + return; + } + + if (m_affiliations->isGranted(dstId)) { + // validate source RID + if (!acl::AccessControl::validateSrcId(srcId)) { + return; + } + + // validate the target ID, if the target is a talkgroup + if (!acl::AccessControl::validateTGId(dstId)) { + return; + } + + if (m_verbose) { + LogInfoEx(LOG_NET, P25_TDU_STR ", srcId = %u", srcId); + } + + m_affiliations->releaseGrant(dstId, false); + } + } + m_voice->processNetwork(data.get(), frameLength, control, lsd, duid, frameType); break; @@ -2040,11 +2075,13 @@ void Control::RPC_activeTG(json::object& req, json::object& reply) continue; } - m_activeTG.push_back(entry.get()); + uint32_t dstId = entry.get(); + if (dstId != 0U) { + ::LogInfoEx(LOG_P25, "active TG update, dstId = %u", dstId); + m_activeTG.push_back(dstId); + } } } - - ::LogInfoEx(LOG_P25, "active TG update, activeCnt = %u", m_activeTG.size()); } /* (RPC Handler) Clear active TGID list from the authoritative CC host. */ diff --git a/src/host/p25/Control.h b/src/host/p25/Control.h index 7cb9273a..b2f35052 100644 --- a/src/host/p25/Control.h +++ b/src/host/p25/Control.h @@ -316,6 +316,7 @@ namespace p25 bool m_dfsiFDX; bool m_forceAllowTG0; bool m_immediateCallTerm; + bool m_explicitTDUGrantRelease; uint32_t m_defaultNetIdleTalkgroup;