From 86748665baf45ff126a9182973ca8de38829f289 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 25 Mar 2023 00:26:25 -0400 Subject: [PATCH] ensure TSCC SLCO's will not be sent at all if DMR CC mode is disabled; hide display of TSCC Slot number if DMR TSCC is not enabled; add payload channel CSBKs; prohibit processing of voice/data traffic on a TSCC slot; fix issue where writeRF_CSBK was only functioning for TSCC slots; fix issue handling slot number in some CSBKs; change order of operations for when TSCC payload channels are activated; --- src/dmr/Control.cpp | 26 ++- src/dmr/Control.h | 4 +- src/dmr/DMRDefines.h | 1 + src/dmr/Slot.cpp | 30 ++-- src/dmr/Slot.h | 4 +- src/dmr/lc/csbk/CSBKFactory.h | 2 + src/dmr/lc/csbk/CSBK_PD_GRANT.cpp | 2 +- src/dmr/lc/csbk/CSBK_PV_GRANT.cpp | 2 +- src/dmr/lc/csbk/CSBK_P_CLEAR.cpp | 83 ++++++++++ src/dmr/lc/csbk/CSBK_P_CLEAR.h | 57 +++++++ src/dmr/lc/csbk/CSBK_P_GRANT.cpp | 84 ++++++++++ src/dmr/lc/csbk/CSBK_P_GRANT.h | 57 +++++++ src/dmr/lc/csbk/CSBK_TD_GRANT.cpp | 2 +- src/dmr/lc/csbk/CSBK_TV_GRANT.cpp | 2 +- src/dmr/packet/ControlSignaling.cpp | 238 ++++++++++++++++------------ src/dmr/packet/ControlSignaling.h | 2 + src/network/RESTAPI.cpp | 9 +- 17 files changed, 482 insertions(+), 123 deletions(-) create mode 100644 src/dmr/lc/csbk/CSBK_P_CLEAR.cpp create mode 100644 src/dmr/lc/csbk/CSBK_P_CLEAR.h create mode 100644 src/dmr/lc/csbk/CSBK_P_GRANT.cpp create mode 100644 src/dmr/lc/csbk/CSBK_P_GRANT.h diff --git a/src/dmr/Control.cpp b/src/dmr/Control.cpp index 13e6b148..baa0d231 100644 --- a/src/dmr/Control.cpp +++ b/src/dmr/Control.cpp @@ -77,6 +77,7 @@ Control::Control(bool authoritative, uint32_t colorCode, uint32_t callHang, uint m_idenTable(idenTable), m_ridLookup(ridLookup), m_tidLookup(tidLookup), + m_enableTSCC(false), m_tsccCnt(0U), m_tsccCntInterval(1000U, 0U, DMR_SLOT_TIME / 2U), m_tsccSlotNo(0U), @@ -170,6 +171,8 @@ void Control::setOptions(yaml::Node& conf, bool controlPermitTG, const std::vect } } + m_enableTSCC = enableTSCC; + uint32_t silenceThreshold = dmrProtocol["silenceThreshold"].as(dmr::DEFAULT_SILENCE_THRESHOLD); if (silenceThreshold > MAX_DMR_VOICE_ERRORS) { LogWarning(LOG_DMR, "Silence threshold > %u, defaulting to %u", dmr::MAX_DMR_VOICE_ERRORS, dmr::DEFAULT_SILENCE_THRESHOLD); @@ -186,8 +189,8 @@ void Control::setOptions(yaml::Node& conf, bool controlPermitTG, const std::vect m_slot2->setSilenceThreshold(silenceThreshold); if (printOptions) { - LogInfo(" TSCC Slot: %u", m_tsccSlotNo); if (enableTSCC) { + LogInfo(" TSCC Slot: %u", m_tsccSlotNo); LogInfo(" TSCC Aloha Random Access Wait: %u", nRandWait); LogInfo(" TSCC Aloha Backoff: %u", backOff); } @@ -204,6 +207,11 @@ void Control::setOptions(yaml::Node& conf, bool controlPermitTG, const std::vect /// void Control::setCCRunning(bool ccRunning) { + if (!m_enableTSCC) { + m_ccRunning = false; + return; + } + m_ccRunning = ccRunning; switch (m_tsccSlotNo) { case 1U: @@ -224,6 +232,11 @@ void Control::setCCRunning(bool ccRunning) /// void Control::setCCHalted(bool ccHalted) { + if (!m_enableTSCC) { + m_ccHalted = true; + return; + } + m_ccHalted = ccHalted; switch (m_tsccSlotNo) { case 1U: @@ -419,13 +432,14 @@ Slot* Control::getTSCCSlot() const /// /// DMR slot number. /// +/// /// /// -void Control::tsccActivateSlot(uint32_t slotNo, uint32_t dstId, bool group, bool voice) +void Control::tsccActivateSlot(uint32_t slotNo, uint32_t dstId, uint32_t srcId, bool group, bool voice) { if (m_verbose) { - LogMessage(LOG_DMR, "DMR Slot %u, payload activation, group = %u, dstId = %u", - slotNo, group, dstId); + LogMessage(LOG_DMR, "DMR Slot %u, payload activation, srcId = %u, group = %u, dstId = %u", + slotNo, srcId, group, dstId); } // never allow the TSCC to become payload activated @@ -437,11 +451,11 @@ void Control::tsccActivateSlot(uint32_t slotNo, uint32_t dstId, bool group, bool switch (slotNo) { case 1U: m_tsccPayloadActive = true; - m_slot1->setTSCCActivated(dstId, group, voice); + m_slot1->setTSCCActivated(dstId, srcId, group, voice); break; case 2U: m_tsccPayloadActive = true; - m_slot2->setTSCCActivated(dstId, group, voice); + m_slot2->setTSCCActivated(dstId, srcId, group, voice); break; default: LogError(LOG_DMR, "DMR, invalid slot, TSCC payload activation, slotNo = %u", slotNo); diff --git a/src/dmr/Control.h b/src/dmr/Control.h index ce6a1674..6f07a88f 100644 --- a/src/dmr/Control.h +++ b/src/dmr/Control.h @@ -100,7 +100,7 @@ namespace dmr /// Helper to return the slot carrying the TSCC. Slot* getTSCCSlot() const; /// Helper to payload activate the slot carrying granted payload traffic. - void tsccActivateSlot(uint32_t slotNo, uint32_t dstId, bool group, bool voice); + void tsccActivateSlot(uint32_t slotNo, uint32_t dstId, uint32_t srcId, bool group, bool voice); /// Helper to clear an activated payload slot. void tsccClearActivatedSlot(uint32_t slotNo); @@ -141,6 +141,8 @@ namespace dmr ::lookups::RadioIdLookup* m_ridLookup; ::lookups::TalkgroupIdLookup* m_tidLookup; + bool m_enableTSCC; + uint16_t m_tsccCnt; Timer m_tsccCntInterval; diff --git a/src/dmr/DMRDefines.h b/src/dmr/DMRDefines.h index 82c6fd2d..f3553a1e 100644 --- a/src/dmr/DMRDefines.h +++ b/src/dmr/DMRDefines.h @@ -302,6 +302,7 @@ namespace dmr const uint8_t CSBKO_EXT_FNCT = 0x24U; // (DMRA) EXT FNCT - Extended Function const uint8_t CSBKO_NACK_RSP = 0x26U; // NACK RSP - Negative Acknowledgement Response const uint8_t CSBKO_BROADCAST = 0x28U; // BCAST - Announcement PDUs + const uint8_t CSBKO_P_CLEAR = 0x2EU; // P_CLEAR - Payload Channel Clear const uint8_t CSBKO_PV_GRANT = 0x30U; // PV_GRANT - Private Voice Channel Grant const uint8_t CSBKO_TV_GRANT = 0x31U; // TV_GRANT - Talkgroup Voice Channel Grant const uint8_t CSBKO_BTV_GRANT = 0x32U; // BTV_GRANT - Broadcast Talkgroup Voice Channel Grant diff --git a/src/dmr/Slot.cpp b/src/dmr/Slot.cpp index f2d75525..312470b4 100644 --- a/src/dmr/Slot.cpp +++ b/src/dmr/Slot.cpp @@ -289,23 +289,15 @@ bool Slot::processFrame(uint8_t *data, uint32_t len) if (dataSync) { uint8_t dataType = data[1U] & 0x0FU; - // write and process TSCC CSBKs and short LC - if (m_enableTSCC && m_dedicatedTSCC && m_slotNo == m_dmr->m_tsccSlotNo) { - switch (dataType) - { - case DT_CSBK: - return m_control->process(data, len); - default: - break; - } + if (dataType == DT_CSBK) { + return m_control->process(data, len); + } + if (m_enableTSCC && m_dedicatedTSCC) return false; - } switch (dataType) { - case DT_CSBK: - return m_control->process(data, len); case DT_VOICE_LC_HEADER: case DT_VOICE_PI_HEADER: return m_voice->process(data, len); @@ -600,6 +592,20 @@ void Slot::setTSCC(bool enable, bool dedicated) } } +/// +/// Helper to activate a TSCC payload slot. +/// +/// +/// +/// +/// +void Slot::setTSCCActivated(uint32_t dstId, uint32_t srcId, bool group, bool voice) +{ + m_tsccPayloadDstId = dstId; + m_tsccPayloadGroup = group; + m_tsccPayloadVoice = voice; +} + /// /// Helper to set the voice error silence threshold. /// diff --git a/src/dmr/Slot.h b/src/dmr/Slot.h index d98b58aa..a3ae083c 100644 --- a/src/dmr/Slot.h +++ b/src/dmr/Slot.h @@ -105,8 +105,8 @@ namespace dmr /// Helper to enable and configure TSCC support for this slot. void setTSCC(bool enable, bool dedicated); - /// Sets a flag indicating whether the slot is a TSCC payload slot. - void setTSCCActivated(uint32_t dstId, bool group, bool voice) { m_tsccPayloadDstId = dstId; m_tsccPayloadGroup = group; m_tsccPayloadVoice = voice; } + /// Helper to activate a TSCC payload slot. + void setTSCCActivated(uint32_t dstId, uint32_t srcId, bool group, bool voice); /// Sets a flag indicating whether the DMR control channel can send permit-tg to voice channels. void setControlPermitTG(bool controlPermitTG) { m_controlPermitTG = controlPermitTG; } /// Helper to set the voice error silence threshold. diff --git a/src/dmr/lc/csbk/CSBKFactory.h b/src/dmr/lc/csbk/CSBKFactory.h index c36dad6e..fc46eaed 100644 --- a/src/dmr/lc/csbk/CSBKFactory.h +++ b/src/dmr/lc/csbk/CSBKFactory.h @@ -37,6 +37,8 @@ #include "dmr/lc/csbk/CSBK_DVM_GIT_HASH.h" #include "dmr/lc/csbk/CSBK_EXT_FNCT.h" #include "dmr/lc/csbk/CSBK_NACK_RSP.h" +#include "dmr/lc/csbk/CSBK_P_CLEAR.h" +#include "dmr/lc/csbk/CSBK_P_GRANT.h" #include "dmr/lc/csbk/CSBK_PD_GRANT.h" #include "dmr/lc/csbk/CSBK_PRECCSBK.h" #include "dmr/lc/csbk/CSBK_PV_GRANT.h" diff --git a/src/dmr/lc/csbk/CSBK_PD_GRANT.cpp b/src/dmr/lc/csbk/CSBK_PD_GRANT.cpp index f7ca5a5a..86dfa602 100644 --- a/src/dmr/lc/csbk/CSBK_PD_GRANT.cpp +++ b/src/dmr/lc/csbk/CSBK_PD_GRANT.cpp @@ -72,7 +72,7 @@ void CSBK_PD_GRANT::encode(uint8_t* data) ulong64_t csbkValue = 0U; csbkValue = (csbkValue << 12) + (m_logicalCh1 & 0xFFFU); // Logical Physical Channel 1 - csbkValue = (csbkValue << 1) + (m_slotNo & 0x3U); // Logical Slot Number + csbkValue = (csbkValue << 1) + ((m_slotNo == 2U) ? 1U : 0U); // Logical Slot Number csbkValue = (csbkValue << 1) + 0U; // High Rate Flag - Always Single Slot Data csbkValue = (csbkValue << 1) + ((m_emergency) ? 1U : 0U); // Emergency csbkValue = (csbkValue << 1) + ((m_siteOffsetTiming) ? 1U : 0U); // Site Timing: Aligned or Offset diff --git a/src/dmr/lc/csbk/CSBK_PV_GRANT.cpp b/src/dmr/lc/csbk/CSBK_PV_GRANT.cpp index 2094cab4..f0f749be 100644 --- a/src/dmr/lc/csbk/CSBK_PV_GRANT.cpp +++ b/src/dmr/lc/csbk/CSBK_PV_GRANT.cpp @@ -72,7 +72,7 @@ void CSBK_PV_GRANT::encode(uint8_t* data) ulong64_t csbkValue = 0U; csbkValue = (csbkValue << 12) + (m_logicalCh1 & 0xFFFU); // Logical Physical Channel 1 - csbkValue = (csbkValue << 1) + (m_slotNo & 0x3U); // Logical Slot Number + csbkValue = (csbkValue << 1) + ((m_slotNo == 2U) ? 1U : 0U); // Logical Slot Number csbkValue = (csbkValue << 1) + 0U; // Reserved csbkValue = (csbkValue << 1) + ((m_emergency) ? 1U : 0U); // Emergency csbkValue = (csbkValue << 1) + ((m_siteOffsetTiming) ? 1U : 0U); // Site Timing: Aligned or Offset diff --git a/src/dmr/lc/csbk/CSBK_P_CLEAR.cpp b/src/dmr/lc/csbk/CSBK_P_CLEAR.cpp new file mode 100644 index 00000000..25056b6e --- /dev/null +++ b/src/dmr/lc/csbk/CSBK_P_CLEAR.cpp @@ -0,0 +1,83 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +/* +* Copyright (C) 2023 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include "Defines.h" +#include "dmr/lc/csbk/CSBK_P_CLEAR.h" +#include "Log.h" +#include "Utils.h" + +using namespace dmr::lc::csbk; +using namespace dmr::lc; +using namespace dmr; + +#include +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/// +/// Initializes a new instance of the CSBK_P_CLEAR class. +/// +CSBK_P_CLEAR::CSBK_P_CLEAR() : CSBK() +{ + m_CSBKO = CSBKO_P_CLEAR; +} + +/// +/// Decode a control signalling block. +/// +/// +/// True, if CSBK was decoded, otherwise false. +bool CSBK_P_CLEAR::decode(const uint8_t* data) +{ + assert(data != NULL); + + /* stub */ + + return true; +} + +/// +/// Encode a control signalling block. +/// +/// +void CSBK_P_CLEAR::encode(uint8_t* data) +{ + assert(data != NULL); + + ulong64_t csbkValue = 0U; + + csbkValue = (csbkValue << 12) + (m_logicalCh1 & 0xFFFU); // Logical Physical Channel 1 + csbkValue = (csbkValue << 1) + 0U; // Reserved + csbkValue = (csbkValue << 3) + 0U; // Reserved + csbkValue = (csbkValue << 1) + ((m_GI) ? 1U : 0U); // Group/Individual Flag + csbkValue = (csbkValue << 24) + m_dstId; // Talkgroup ID + csbkValue = (csbkValue << 24) + m_srcId; // Source Radio Address + + std::unique_ptr csbk = CSBK::fromValue(csbkValue); + CSBK::encode(data, csbk.get()); +} diff --git a/src/dmr/lc/csbk/CSBK_P_CLEAR.h b/src/dmr/lc/csbk/CSBK_P_CLEAR.h new file mode 100644 index 00000000..a7d58274 --- /dev/null +++ b/src/dmr/lc/csbk/CSBK_P_CLEAR.h @@ -0,0 +1,57 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +/* +* Copyright (C) 2023 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#if !defined(__DMR_LC_CSBK__CSBK_P_CLEAR_H__) +#define __DMR_LC_CSBK__CSBK_P_CLEAR_H__ + +#include "Defines.h" +#include "dmr/lc/CSBK.h" + +namespace dmr +{ + namespace lc + { + namespace csbk + { + // --------------------------------------------------------------------------- + // Class Declaration + // Implements P_CLEAR - Payload Channel Clear + // --------------------------------------------------------------------------- + + class HOST_SW_API CSBK_P_CLEAR : public CSBK { + public: + /// Initializes a new instance of the CSBK_P_CLEAR class. + CSBK_P_CLEAR(); + + /// Decode a control signalling block. + virtual bool decode(const uint8_t* data); + /// Encode a control signalling block. + virtual void encode(uint8_t* data); + }; + } // namespace csbk + } // namespace lc +} // namespace dmr + +#endif // __DMR_LC_CSBK__CSBK_P_CLEAR_H__ diff --git a/src/dmr/lc/csbk/CSBK_P_GRANT.cpp b/src/dmr/lc/csbk/CSBK_P_GRANT.cpp new file mode 100644 index 00000000..ae44214b --- /dev/null +++ b/src/dmr/lc/csbk/CSBK_P_GRANT.cpp @@ -0,0 +1,84 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +/* +* Copyright (C) 2023 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include "Defines.h" +#include "dmr/lc/csbk/CSBK_P_GRANT.h" +#include "Log.h" +#include "Utils.h" + +using namespace dmr::lc::csbk; +using namespace dmr::lc; +using namespace dmr; + +#include +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/// +/// Initializes a new instance of the CSBK_P_GRANT class. +/// +CSBK_P_GRANT::CSBK_P_GRANT() : CSBK() +{ + m_CSBKO = CSBKO_TV_GRANT; +} + +/// +/// Decode a control signalling block. +/// +/// +/// True, if CSBK was decoded, otherwise false. +bool CSBK_P_GRANT::decode(const uint8_t* data) +{ + assert(data != NULL); + + /* stub */ + + return true; +} + +/// +/// Encode a control signalling block. +/// +/// +void CSBK_P_GRANT::encode(uint8_t* data) +{ + assert(data != NULL); + + ulong64_t csbkValue = 0U; + + csbkValue = (csbkValue << 12) + (m_logicalCh1 & 0xFFFU); // Logical Physical Channel 1 + csbkValue = (csbkValue << 1) + ((m_slotNo == 2U) ? 1U : 0U); // Logical Slot Number + csbkValue = (csbkValue << 1) + 0U; // High Rate Flag - Always Single Slot Data + csbkValue = (csbkValue << 1) + ((m_emergency) ? 1U : 0U); // Emergency + csbkValue = (csbkValue << 1) + ((m_siteOffsetTiming) ? 1U : 0U); // Site Timing: Aligned or Offset + csbkValue = (csbkValue << 24) + m_dstId; // Talkgroup ID + csbkValue = (csbkValue << 24) + m_srcId; // Source Radio Address + + std::unique_ptr csbk = CSBK::fromValue(csbkValue); + CSBK::encode(data, csbk.get()); +} diff --git a/src/dmr/lc/csbk/CSBK_P_GRANT.h b/src/dmr/lc/csbk/CSBK_P_GRANT.h new file mode 100644 index 00000000..d6aa4be0 --- /dev/null +++ b/src/dmr/lc/csbk/CSBK_P_GRANT.h @@ -0,0 +1,57 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +/* +* Copyright (C) 2023 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#if !defined(__DMR_LC_CSBK__CSBK_P_GRANT_H__) +#define __DMR_LC_CSBK__CSBK_P_GRANT_H__ + +#include "Defines.h" +#include "dmr/lc/CSBK.h" + +namespace dmr +{ + namespace lc + { + namespace csbk + { + // --------------------------------------------------------------------------- + // Class Declaration + // Implements P_GRANT - Payload Channel Talkgroup Voice Channel Grant + // --------------------------------------------------------------------------- + + class HOST_SW_API CSBK_P_GRANT : public CSBK { + public: + /// Initializes a new instance of the CSBK_P_GRANT class. + CSBK_P_GRANT(); + + /// Decode a control signalling block. + virtual bool decode(const uint8_t* data); + /// Encode a control signalling block. + virtual void encode(uint8_t* data); + }; + } // namespace csbk + } // namespace lc +} // namespace dmr + +#endif // __DMR_LC_CSBK__CSBK_P_GRANT_H__ diff --git a/src/dmr/lc/csbk/CSBK_TD_GRANT.cpp b/src/dmr/lc/csbk/CSBK_TD_GRANT.cpp index f29c450b..7c439b55 100644 --- a/src/dmr/lc/csbk/CSBK_TD_GRANT.cpp +++ b/src/dmr/lc/csbk/CSBK_TD_GRANT.cpp @@ -72,7 +72,7 @@ void CSBK_TD_GRANT::encode(uint8_t* data) ulong64_t csbkValue = 0U; csbkValue = (csbkValue << 12) + (m_logicalCh1 & 0xFFFU); // Logical Physical Channel 1 - csbkValue = (csbkValue << 1) + (m_slotNo & 0x3U); // Logical Slot Number + csbkValue = (csbkValue << 1) + ((m_slotNo == 2U) ? 1U : 0U); // Logical Slot Number csbkValue = (csbkValue << 1) + 0U; // High Rate Flag - Always Single Slot Data csbkValue = (csbkValue << 1) + ((m_emergency) ? 1U : 0U); // Emergency csbkValue = (csbkValue << 1) + ((m_siteOffsetTiming) ? 1U : 0U); // Site Timing: Aligned or Offset diff --git a/src/dmr/lc/csbk/CSBK_TV_GRANT.cpp b/src/dmr/lc/csbk/CSBK_TV_GRANT.cpp index d54957f5..51670a8f 100644 --- a/src/dmr/lc/csbk/CSBK_TV_GRANT.cpp +++ b/src/dmr/lc/csbk/CSBK_TV_GRANT.cpp @@ -73,7 +73,7 @@ void CSBK_TV_GRANT::encode(uint8_t* data) ulong64_t csbkValue = 0U; csbkValue = (csbkValue << 12) + (m_logicalCh1 & 0xFFFU); // Logical Physical Channel 1 - csbkValue = (csbkValue << 1) + (m_slotNo & 0x3U); // Logical Slot Number + csbkValue = (csbkValue << 1) + ((m_slotNo == 2U) ? 1U : 0U); // Logical Slot Number csbkValue = (csbkValue << 1) + ((m_lateEntry) ? 1U : 0U);; // Late Entry csbkValue = (csbkValue << 1) + ((m_emergency) ? 1U : 0U); // Emergency csbkValue = (csbkValue << 1) + ((m_siteOffsetTiming) ? 1U : 0U); // Site Timing: Aligned or Offset diff --git a/src/dmr/packet/ControlSignaling.cpp b/src/dmr/packet/ControlSignaling.cpp index 79c32de3..64b64ab7 100644 --- a/src/dmr/packet/ControlSignaling.cpp +++ b/src/dmr/packet/ControlSignaling.cpp @@ -650,50 +650,44 @@ ControlSignaling::~ControlSignaling() /// void ControlSignaling::writeRF_CSBK(lc::CSBK* csbk, bool clearBeforeWrite) { - Slot *m_tscc = m_slot->m_dmr->getTSCCSlot(); - if (m_tscc != nullptr) { - if (!m_tscc->m_enableTSCC) - return; - - // don't add any frames if the queue is full - uint8_t len = DMR_FRAME_LENGTH_BYTES + 2U; - uint32_t space = m_tscc->m_queue.freeSpace(); - if (space < (len + 1U)) { - return; - } - - uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; - ::memset(data + 2U, 0x00U, DMR_FRAME_LENGTH_BYTES); + // don't add any frames if the queue is full + uint8_t len = DMR_FRAME_LENGTH_BYTES + 2U; + uint32_t space = m_slot->m_queue.freeSpace(); + if (space < (len + 1U)) { + return; + } - SlotType slotType; - slotType.setColorCode(m_tscc->m_colorCode); - slotType.setDataType(DT_CSBK); + uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; + ::memset(data + 2U, 0x00U, DMR_FRAME_LENGTH_BYTES); - // Regenerate the CSBK data - csbk->encode(data + 2U); + SlotType slotType; + slotType.setColorCode(m_slot->m_colorCode); + slotType.setDataType(DT_CSBK); - // Regenerate the Slot Type - slotType.encode(data + 2U); + // Regenerate the CSBK data + csbk->encode(data + 2U); - // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_tscc->m_duplex); + // Regenerate the Slot Type + slotType.encode(data + 2U); - m_tscc->m_rfSeqNo = 0U; + // Convert the Data Sync to be from the BS or MS as needed + Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); - data[0U] = modem::TAG_DATA; - data[1U] = 0x00U; + m_slot->m_rfSeqNo = 0U; - if (clearBeforeWrite) { - if (m_tscc->m_slotNo == 1U) - m_tscc->m_modem->clearDMRData1(); - if (m_tscc->m_slotNo == 2U) - m_tscc->m_modem->clearDMRData2(); - m_tscc->m_queue.clear(); - } + data[0U] = modem::TAG_DATA; + data[1U] = 0x00U; - if (m_tscc->m_duplex) - m_tscc->addFrame(data); + if (clearBeforeWrite) { + if (m_slot->m_slotNo == 1U) + m_slot->m_modem->clearDMRData1(); + if (m_slot->m_slotNo == 2U) + m_slot->m_modem->clearDMRData2(); + m_slot->m_queue.clear(); } + + if (m_slot->m_duplex) + m_slot->addFrame(data); } /// @@ -841,28 +835,6 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ ::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); } - // if the channel granted isn't the same as the TSCC; remote activate the payload channel - if (chNo != m_tscc->m_channelNo) { - ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); - if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { - json::object req = json::object(); - req["dstId"].set(dstId); - req["slot"].set(slot); - req["group"].set(grp); - bool voice = true; - req["voice"].set(voice); - - RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), - 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); - } - } - else { - m_slot->m_dmr->tsccActivateSlot(slot, dstId, grp, true); - } - // callback RCON to permit-tg on the specified voice channel if (m_tscc->m_authoritative && m_tscc->m_controlPermitTG) { ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); @@ -881,11 +853,13 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } } + writeRF_CSBK_ACK_RSP(srcId, TS_ACK_RSN_MSG, (grp) ? 1U : 0U); + std::unique_ptr csbk = new_unique(CSBK_TV_GRANT); if (broadcast) csbk->setCSBKO(CSBKO_BTV_GRANT); csbk->setLogicalCh1(chNo); - csbk->setSlotNo(slot - 1U); + csbk->setSlotNo(slot); if (m_verbose) { LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_VOICE_CALL (Group Voice Call), emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", @@ -899,11 +873,6 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ // transmit group grant (x2) for (uint8_t i = 0; i < 2U; i++) writeRF_CSBK(csbk.get()); - } - else { - if (!net) { - ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); - } // if the channel granted isn't the same as the TSCC; remote activate the payload channel if (chNo != m_tscc->m_channelNo) { @@ -911,20 +880,26 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); + req["srcId"].set(srcId); req["slot"].set(slot); req["group"].set(grp); bool voice = true; req["voice"].set(voice); RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), - HTTP_PUT, PUT_DMR_TSCC_PAYLOAD_ACT, 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); } } else { - m_slot->m_dmr->tsccActivateSlot(slot, dstId, grp, true); + m_slot->m_dmr->tsccActivateSlot(slot, dstId, srcId, grp, true); + } + } + else { + if (!net) { + ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); } // callback RCON to permit-tg on the specified voice channel @@ -945,9 +920,11 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ } } + writeRF_CSBK_ACK_RSP(srcId, TS_ACK_RSN_MSG, (grp) ? 1U : 0U); + std::unique_ptr csbk = new_unique(CSBK_PV_GRANT); csbk->setLogicalCh1(chNo); - csbk->setSlotNo(slot - 1U); + csbk->setSlotNo(slot); if (m_verbose) { LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_VOICE_CALL (Individual Voice Call), emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", @@ -961,6 +938,29 @@ bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_ // transmit private grant (x2) for (uint8_t i = 0; i < 2U; i++) writeRF_CSBK(csbk.get()); + + // if the channel granted isn't the same as the TSCC; remote activate the payload channel + if (chNo != m_tscc->m_channelNo) { + ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); + if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { + json::object req = json::object(); + req["dstId"].set(dstId); + req["srcId"].set(srcId); + req["slot"].set(slot); + req["group"].set(grp); + bool voice = true; + req["voice"].set(voice); + + RESTClient::send(voiceChData.address(), voiceChData.port(), voiceChData.password(), + 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); + } + } + else { + m_slot->m_dmr->tsccActivateSlot(slot, dstId, srcId, grp, true); + } } return true; @@ -1078,12 +1078,32 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u ::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); } + writeRF_CSBK_ACK_RSP(srcId, TS_ACK_RSN_MSG, (grp) ? 1U : 0U); + + std::unique_ptr csbk = new_unique(CSBK_TD_GRANT); + csbk->setLogicalCh1(chNo); + csbk->setSlotNo(slot); + + if (m_verbose) { + LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_DATA_CALL (Group Data Call), emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", + m_tscc->m_slotNo, emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); + } + + csbk->setEmergency(emergency); + csbk->setSrcId(srcId); + csbk->setDstId(dstId); + + // transmit group grant (x2) + for (uint8_t i = 0; i < 2U; i++) + writeRF_CSBK(csbk.get()); + // if the channel granted isn't the same as the TSCC; remote activate the payload channel if (chNo != m_tscc->m_channelNo) { ::lookups::VoiceChData voiceChData = m_tscc->m_affiliations->getRFChData(chNo); if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); + req["srcId"].set(srcId); req["slot"].set(slot); req["group"].set(grp); bool voice = false; @@ -1097,15 +1117,22 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u } } else { - m_slot->m_dmr->tsccActivateSlot(slot, dstId, grp, false); + m_slot->m_dmr->tsccActivateSlot(slot, dstId, srcId, grp, false); + } + } + else { + if (!net) { + ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); } - std::unique_ptr csbk = new_unique(CSBK_TD_GRANT); + writeRF_CSBK_ACK_RSP(srcId, TS_ACK_RSN_MSG, (grp) ? 1U : 0U); + + std::unique_ptr csbk = new_unique(CSBK_PD_GRANT); csbk->setLogicalCh1(chNo); - csbk->setSlotNo(slot - 1U); + csbk->setSlotNo(slot); if (m_verbose) { - LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_DATA_CALL (Group Data Call), emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", + LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_DATA_CALL (Individual Data Call), emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", m_tscc->m_slotNo, emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); } @@ -1113,14 +1140,9 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u csbk->setSrcId(srcId); csbk->setDstId(dstId); - // transmit group grant (x2) + // transmit private grant (x2) for (uint8_t i = 0; i < 2U; i++) writeRF_CSBK(csbk.get()); - } - else { - if (!net) { - ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); - } // if the channel granted isn't the same as the TSCC; remote activate the payload channel if (chNo != m_tscc->m_channelNo) { @@ -1128,6 +1150,7 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u if (voiceChData.isValidCh() && !voiceChData.address().empty() && voiceChData.port() > 0) { json::object req = json::object(); req["dstId"].set(dstId); + req["srcId"].set(srcId); req["slot"].set(slot); req["group"].set(grp); bool voice = false; @@ -1141,25 +1164,8 @@ bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, u } } else { - m_slot->m_dmr->tsccActivateSlot(slot, dstId, grp, false); + m_slot->m_dmr->tsccActivateSlot(slot, dstId, srcId, grp, false); } - - std::unique_ptr csbk = new_unique(CSBK_PD_GRANT); - csbk->setLogicalCh1(chNo); - csbk->setSlotNo(slot - 1U); - - if (m_verbose) { - LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_DATA_CALL (Individual Data Call), emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", - m_tscc->m_slotNo, emergency, privacy, broadcast, priority, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); - } - - csbk->setEmergency(emergency); - csbk->setSrcId(srcId); - csbk->setDstId(dstId); - - // transmit private grant (x2) - for (uint8_t i = 0; i < 2U; i++) - writeRF_CSBK(csbk.get()); } return true; @@ -1219,9 +1225,8 @@ void ControlSignaling::writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOpt writeRF_CSBK(csbk.get()); } - /// -/// Helper to write a grant packet. +/// Helper to write a TSCC late entry channel grant packet on the RF interface. /// /// /// @@ -1234,11 +1239,50 @@ void ControlSignaling::writeRF_CSBK_Grant_LateEntry(uint32_t dstId, uint32_t src std::unique_ptr csbk = new_unique(CSBK_TV_GRANT); csbk->setLogicalCh1(chNo); - csbk->setSlotNo(slot - 1U); + csbk->setSlotNo(slot); + + csbk->setSrcId(srcId); + csbk->setDstId(dstId); + + writeRF_CSBK(csbk.get()); +} + +/// +/// Helper to write a payload grant to a TSCC payload channel on the RF interface. +/// +/// +/// +void ControlSignaling::writeRF_CSBK_Payload_Grant(uint32_t dstId, uint32_t srcId, bool grp, bool voice) +{ + std::unique_ptr csbk = new_unique(CSBK_P_GRANT); + if (voice) { + if (grp) { + csbk->setCSBKO(CSBKO_TV_GRANT); + } + else { + csbk->setCSBKO(CSBKO_PV_GRANT); + } + } + else { + if (grp) { + csbk->setCSBKO(CSBKO_TD_GRANT); + } + else { + csbk->setCSBKO(CSBKO_PD_GRANT); + } + } + + csbk->setLogicalCh1(m_slot->m_channelNo); + csbk->setSlotNo(m_slot->m_slotNo); csbk->setSrcId(srcId); csbk->setDstId(dstId); + if (m_verbose) { + LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBK_P_GRANT (Payload Grant), chNo = %u, slot = %u, srcId = %u, dstId = %u", + m_slot->m_slotNo, csbk->getLogicalCh1(), csbk->getSlotNo(), srcId, dstId); + } + writeRF_CSBK(csbk.get()); } diff --git a/src/dmr/packet/ControlSignaling.h b/src/dmr/packet/ControlSignaling.h index a75a18c2..c702f162 100644 --- a/src/dmr/packet/ControlSignaling.h +++ b/src/dmr/packet/ControlSignaling.h @@ -103,6 +103,8 @@ namespace dmr /// Helper to write a TSCC late entry channel grant packet on the RF interface. void writeRF_CSBK_Grant_LateEntry(uint32_t dstId, uint32_t srcId); + /// Helper to write a payload grant to a TSCC payload channel on the RF interface. + void writeRF_CSBK_Payload_Grant(uint32_t dstId, uint32_t srcId, bool grp, bool voice); /// Helper to write a TSCC Aloha broadcast packet on the RF interface. void writeRF_TSCC_Aloha(); diff --git a/src/network/RESTAPI.cpp b/src/network/RESTAPI.cpp index 0f80a748..987fb4fe 100644 --- a/src/network/RESTAPI.cpp +++ b/src/network/RESTAPI.cpp @@ -1432,6 +1432,12 @@ void RESTAPI::restAPI_PutTSCCPayloadActivate(const HTTPPayload& request, HTTPPay return; } + // validate destination ID is a integer within the JSON blob + if (!req["srcId"].is()) { + errorPayload(reply, "source ID was not valid"); + return; + } + // validate group flag is a boolean within the JSON blob if (!req["group"].is()) { errorPayload(reply, "group flag was not valid"); @@ -1445,6 +1451,7 @@ void RESTAPI::restAPI_PutTSCCPayloadActivate(const HTTPPayload& request, HTTPPay } uint32_t dstId = req["dstId"].get(); + uint32_t srcId = req["srcId"].get(); bool group = req["group"].get(); bool voice = req["voice"].get(); @@ -1453,7 +1460,7 @@ void RESTAPI::restAPI_PutTSCCPayloadActivate(const HTTPPayload& request, HTTPPay return; } - m_dmr->tsccActivateSlot(slot, dstId, group, voice); + m_dmr->tsccActivateSlot(slot, dstId, srcId, group, voice); } #else errorPayload(reply, "DMR operations are unavailable", HTTPPayload::SERVICE_UNAVAILABLE);