diff --git a/src/dmr/Control.cpp b/src/dmr/Control.cpp index c8572a7c..b13f583b 100644 --- a/src/dmr/Control.cpp +++ b/src/dmr/Control.cpp @@ -78,6 +78,7 @@ Control::Control(bool authoritative, uint32_t colorCode, uint32_t callHang, uint m_ridLookup(ridLookup), m_tidLookup(tidLookup), m_tsccSlotNo(0U), + m_tsccPayloadActive(false), m_ccRunning(false), m_ccHalted(false), m_dumpCSBKData(dumpCSBKData), @@ -408,14 +409,16 @@ void Control::tsccActivateSlot(uint32_t slotNo, uint32_t dstId, bool group) { if (m_verbose) { LogMessage(LOG_DMR, "DMR Slot %u, payload activation, group = %u, dstId = %u", - group, dstId); + slotNo, group, dstId); } switch (slotNo) { case 1U: + m_tsccPayloadActive = true; m_slot1->setTSCCActivated(dstId, group); break; case 2U: + m_tsccPayloadActive = true; m_slot2->setTSCCActivated(dstId, group); break; default: @@ -431,7 +434,7 @@ void Control::tsccActivateSlot(uint32_t slotNo, uint32_t dstId, bool group) void Control::tsccClearActivatedSlot(uint32_t slotNo) { if (m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, payload activation clear"); + LogMessage(LOG_DMR, "DMR Slot %u, payload activation clear", slotNo); } switch (slotNo) { @@ -445,6 +448,10 @@ void Control::tsccClearActivatedSlot(uint32_t slotNo) LogError(LOG_DMR, "DMR, invalid slot, TSCC payload activation, slotNo = %u", slotNo); break; } + + if (m_tsccPayloadActive && m_slot1->m_tsccPayloadDstId == 0U && m_slot2->m_tsccPayloadDstId == 0U) { + m_tsccPayloadActive = false; + } } /// diff --git a/src/dmr/Control.h b/src/dmr/Control.h index 5ff858c1..7f0266b6 100644 --- a/src/dmr/Control.h +++ b/src/dmr/Control.h @@ -142,6 +142,7 @@ namespace dmr ::lookups::TalkgroupIdLookup* m_tidLookup; uint8_t m_tsccSlotNo; + bool m_tsccPayloadActive; bool m_ccRunning; bool m_ccHalted; diff --git a/src/dmr/DMRDefines.h b/src/dmr/DMRDefines.h index 5791a04d..b8dbc6c2 100644 --- a/src/dmr/DMRDefines.h +++ b/src/dmr/DMRDefines.h @@ -171,7 +171,7 @@ namespace dmr // Feature IDs const uint8_t FID_ETSI = 0x00U; // ETSI Standard Feature Set const uint8_t FID_DMRA = 0x10U; // - const uint8_t FID_DVM = 0xFEU; // internal DMR FID used for internal signalling + const uint8_t FID_DVM = 0x3FU; // internal DMR FID used for internal signalling // LC Service Options const uint8_t LC_SVC_OPT_EMERGENCY = 0x80U; diff --git a/src/dmr/Slot.cpp b/src/dmr/Slot.cpp index a7ee5379..9dc223b8 100644 --- a/src/dmr/Slot.cpp +++ b/src/dmr/Slot.cpp @@ -33,6 +33,7 @@ #include "dmr/Sync.h" #include "edac/BPTC19696.h" #include "edac/CRC.h" +#include "remote/RESTClient.h" #include "Log.h" #include "Utils.h" @@ -419,12 +420,6 @@ void Slot::clock() } } - // increment the TSCC counter on every slot 1 clock - m_tsccCnt++; - if (m_tsccCnt == TSCC_MAX_CNT) { - m_tsccCnt = 0U; - } - // if we have control enabled; do clocking to generate a CC data stream if (m_enableTSCC) { // clock all the grant timers @@ -448,12 +443,25 @@ void Slot::clock() } if (m_ccPacketInterval.isRunning() && m_ccPacketInterval.hasExpired()) { + m_tsccCnt++; + if (m_tsccCnt == TSCC_MAX_CNT) { + m_tsccCnt = 0U; + } + if (m_ccRunning) { if (m_ccSeq == 3U) { m_ccSeq = 0U; } - setShortLC_TSCC(m_siteData, m_tsccCnt); + if (m_dmr->m_tsccPayloadActive) { + if ((m_tsccCnt % 2) == 0) { + setShortLC_TSCC(m_siteData, m_tsccCnt); + } + } + else { + setShortLC_TSCC(m_siteData, m_tsccCnt); + } + writeRF_ControlData(m_tsccCnt, m_ccSeq); m_ccSeq++; @@ -470,31 +478,22 @@ void Slot::clock() } // never allow the TSCC to become payload activated - if (m_tsccActivated && m_enableTSCC) { + if (m_tsccActivated && m_slotNo == m_dmr->m_tsccSlotNo) { ::LogDebug(LOG_DMR, "DMR Slot %u, BUG BUG tried to payload activate the TSCC slot", m_slotNo); clearTSCCActivated(); } // activate payload channel if requested from the TSCC - if (m_tsccActivated && !m_enableTSCC) { + if (m_tsccActivated) { if (m_rfState == RS_RF_LISTENING && m_netState == RS_NET_IDLE) { if (m_tsccPayloadDstId > 0U) { - if (m_dmr->m_tsccSlotNo > 0U) { - // every 4 frames transmit the payload TSCC - if ((m_tsccCnt % 4) == 0) { - setShortLC_Payload(m_siteData, m_tsccCnt); - } - else { - // only transmit the payload short LC every 2 frames - if ((m_tsccCnt % 2) == 0) { - setShortLC(m_slotNo, m_tsccPayloadDstId, m_tsccPayloadGroup ? FLCO_GROUP : FLCO_PRIVATE, true); - } - } - } - else { - setShortLC(m_slotNo, m_tsccPayloadDstId, m_tsccPayloadGroup ? FLCO_GROUP : FLCO_PRIVATE, true); + if ((m_tsccCnt % 2) > 0) { + setShortLC(m_slotNo, m_tsccPayloadDstId, m_tsccPayloadGroup ? FLCO_GROUP : FLCO_PRIVATE, false); } } + else { + setShortLC_TSCC(m_siteData, m_tsccCnt); + } } else { clearTSCCActivated(); @@ -563,7 +562,9 @@ void Slot::clock() } if (m_rfState == RS_RF_REJECTED) { - m_queue.clear(); + if (!m_enableTSCC) { + m_queue.clear(); + } m_rfFrames = 0U; m_rfErrs = 0U; @@ -677,6 +678,29 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s m_ridLookup = ridLookup; m_tidLookup = tidLookup; m_affiliations = new dmr::lookups::DMRAffiliationLookup(verbose); + m_affiliations->setReleaseGrantCallback([=](uint32_t chNo, uint32_t dstId, uint8_t slot) { + Slot* tscc = m_dmr->getTSCCSlot(); + if (tscc != nullptr) { + if (chNo == tscc->m_channelNo) { + m_dmr->tsccClearActivatedSlot(slot); + return; + } + + ::lookups::VoiceChData voiceChData = tscc->m_affiliations->getRFChData(chNo); + if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { + json::object req = json::object(); + req["slot"].set(slot); + bool clear = true; + req["clear"].set(clear); + + RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), + HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, tscc->m_debug); + } + else { + ::LogError(LOG_DMR, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to clear payload channel, chNo = %u", tscc->m_slotNo, chNo); + } + } + }); m_hangCount = callHang * 17U; diff --git a/src/dmr/lookups/DMRAffiliationLookup.cpp b/src/dmr/lookups/DMRAffiliationLookup.cpp index cb2f7006..d5d28375 100644 --- a/src/dmr/lookups/DMRAffiliationLookup.cpp +++ b/src/dmr/lookups/DMRAffiliationLookup.cpp @@ -44,7 +44,8 @@ using namespace dmr::lookups; DMRAffiliationLookup::DMRAffiliationLookup(bool verbose) : ::lookups::AffiliationLookup("DMR Affiliation", verbose), m_grantChSlotTable(), m_tsccChNo(0U), - m_tsccSlot(0U) + m_tsccSlot(0U), + m_releaseGrant(nullptr) { /* stub */ } @@ -156,6 +157,10 @@ bool DMRAffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) m_name, chNo, slot, dstId); } + if (m_releaseGrant != nullptr) { + m_releaseGrant(chNo, dstId, slot); + } + m_grantChTable[dstId] = 0U; m_grantChSlotTable.erase(dstId); diff --git a/src/dmr/lookups/DMRAffiliationLookup.h b/src/dmr/lookups/DMRAffiliationLookup.h index 149ea0cc..19eedb5a 100644 --- a/src/dmr/lookups/DMRAffiliationLookup.h +++ b/src/dmr/lookups/DMRAffiliationLookup.h @@ -30,6 +30,7 @@ #include "lookups/AffiliationLookup.h" #include +#include namespace dmr { @@ -65,13 +66,18 @@ namespace dmr /// Helper to determine the first available slot for given the channel number. uint8_t getAvailableSlotForChannel(uint32_t chNo) const; + /// Helper to set the release grant callback. + void setReleaseGrantCallback(std::function&& callback) { m_releaseGrant = callback; } + protected: std::unordered_map> m_grantChSlotTable; uint32_t m_tsccChNo; uint8_t m_tsccSlot; + + std::function m_releaseGrant; }; } // namespace lookups -} // namespace dmr +} // namespace dmr #endif // __DMR_AFFILIATION_LOOKUP_H__ diff --git a/src/dmr/packet/ControlSignaling.cpp b/src/dmr/packet/ControlSignaling.cpp index 4b02432a..e29994a2 100644 --- a/src/dmr/packet/ControlSignaling.cpp +++ b/src/dmr/packet/ControlSignaling.cpp @@ -851,7 +851,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ req["group"].set(grp); RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), - HTTP_PUT, PUT_PERMIT_TG, req, m_tscc->m_debug); + HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, m_tscc->m_debug); } else { ::LogError(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot); @@ -913,7 +913,7 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ req["group"].set(grp); RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), - HTTP_PUT, PUT_PERMIT_TG, req, m_tscc->m_debug); + HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, m_tscc->m_debug); } else { ::LogError(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot); @@ -1084,7 +1084,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u req["group"].set(grp); RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), - HTTP_PUT, PUT_PERMIT_TG, req, m_tscc->m_debug); + HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, m_tscc->m_debug); } else { ::LogError(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot); @@ -1126,7 +1126,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u req["group"].set(grp); RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), - HTTP_PUT, PUT_PERMIT_TG, req, m_tscc->m_debug); + HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, req, m_tscc->m_debug); } else { ::LogError(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), failed to activate payload channel, chNo = %u, slot = %u", m_tscc->m_slotNo, chNo, slot);