diff --git a/DVMHost.vcxproj b/DVMHost.vcxproj index 8fb674d4..90ee1af0 100644 --- a/DVMHost.vcxproj +++ b/DVMHost.vcxproj @@ -155,6 +155,7 @@ + @@ -230,6 +231,7 @@ + diff --git a/DVMHost.vcxproj.filters b/DVMHost.vcxproj.filters index 1e1614a2..4c9d2516 100644 --- a/DVMHost.vcxproj.filters +++ b/DVMHost.vcxproj.filters @@ -332,6 +332,9 @@ Header Files\dmr + + Header Files\dmr + @@ -532,6 +535,9 @@ Source Files\p25\data + + Source Files\dmr + diff --git a/Makefile b/Makefile index d584a7d0..3f108828 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ OBJECTS = \ dmr/lc/LC.o \ dmr/lc/ShortLC.o \ dmr/Control.o \ + dmr/ControlPacket.o \ dmr/DataPacket.o \ dmr/Slot.o \ dmr/SlotType.o \ diff --git a/Makefile.arm b/Makefile.arm index 2970f99e..bbe2443a 100644 --- a/Makefile.arm +++ b/Makefile.arm @@ -27,6 +27,7 @@ OBJECTS = \ dmr/lc/LC.o \ dmr/lc/ShortLC.o \ dmr/Control.o \ + dmr/ControlPacket.o \ dmr/DataPacket.o \ dmr/Slot.o \ dmr/SlotType.o \ diff --git a/Makefile.rpi-arm b/Makefile.rpi-arm index c34be7ed..c7b705ff 100644 --- a/Makefile.rpi-arm +++ b/Makefile.rpi-arm @@ -27,6 +27,7 @@ OBJECTS = \ dmr/lc/LC.o \ dmr/lc/ShortLC.o \ dmr/Control.o \ + dmr/ControlPacket.o \ dmr/DataPacket.o \ dmr/Slot.o \ dmr/SlotType.o \ diff --git a/dmr/ControlPacket.cpp b/dmr/ControlPacket.cpp new file mode 100644 index 00000000..c91cc879 --- /dev/null +++ b/dmr/ControlPacket.cpp @@ -0,0 +1,415 @@ +/** +* 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 +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX +* Copyright (C) 2017-2020 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; version 2 of the License. +* +* 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. +*/ +#include "Defines.h" +#include "dmr/ControlPacket.h" +#include "dmr/acl/AccessControl.h" +#include "dmr/data/DataHeader.h" +#include "dmr/data/EMB.h" +#include "dmr/edac/Trellis.h" +#include "dmr/lc/ShortLC.h" +#include "dmr/lc/FullLC.h" +#include "dmr/lc/CSBK.h" +#include "dmr/SlotType.h" +#include "dmr/Sync.h" +#include "edac/BPTC19696.h" +#include "edac/CRC.h" +#include "Log.h" +#include "Utils.h" + +using namespace dmr; + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// Macros +// --------------------------------------------------------------------------- +// Don't process RF frames if the network isn't in a idle state. +#define CHECK_TRAFFIC_COLLISION(_DST_ID) \ + if (m_slot->m_netState != RS_NET_IDLE && _DST_ID == m_slot->m_netLastDstId) { \ + LogWarning(LOG_RF, "DMR Slot %u, Traffic collision detect, preempting new RF traffic to existing network traffic!", m_slot->m_slotNo); \ + return false; \ + } + +#define CHECK_TRAFFIC_COLLISION_DELLC(_DST_ID) \ + if (m_slot->m_netState != RS_NET_IDLE && _DST_ID == m_slot->m_netLastDstId) { \ + LogWarning(LOG_RF, "DMR Slot %u, Traffic collision detect, preempting new RF traffic to existing network traffic!", m_slot->m_slotNo); \ + delete lc; \ + return false; \ + } + +#define CHECK_TG_HANG(_DST_ID) \ + if (m_slot->m_rfLastDstId != 0U) { \ + if (m_slot->m_rfLastDstId != _DST_ID && (m_slot->m_rfTGHang.isRunning() && !m_slot->m_rfTGHang.hasExpired())) { \ + return; \ + } \ + } + +#define CHECK_TG_HANG_DELLC(_DST_ID) \ + if (m_slot->m_rfLastDstId != 0U) { \ + if (m_slot->m_rfLastDstId != _DST_ID && (m_slot->m_rfTGHang.isRunning() && !m_slot->m_rfTGHang.hasExpired())) { \ + delete lc; \ + return; \ + } \ + } + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- +/// +/// Process DMR data frame from the RF interface. +/// +/// Buffer containing data frame. +/// Length of data frame. +/// +bool ControlPacket::process(uint8_t* data, uint32_t len) +{ + assert(data != NULL); + + // Get the type from the packet metadata + uint8_t dataType = data[1U] & 0x0FU; + + SlotType slotType; + slotType.setColorCode(m_slot->m_colorCode); + slotType.setDataType(dataType); + + if (dataType == DT_CSBK) { + // generate a new CSBK and check validity + lc::CSBK csbk = lc::CSBK(); + csbk.setVerbose(m_dumpCSBKData); + + bool valid = csbk.decode(data + 2U); + if (!valid) + return false; + + uint8_t csbko = csbk.getCSBKO(); + if (csbko == CSBKO_BSDWNACT) + return false; + + bool gi = csbk.getGI(); + uint32_t srcId = csbk.getSrcId(); + uint32_t dstId = csbk.getDstId(); + + if (srcId != 0U || dstId != 0U) { + CHECK_TRAFFIC_COLLISION(dstId); + + // validate the source RID + if (!acl::AccessControl::validateSrcId(srcId)) { + LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK denial, RID rejection, srcId = %u", m_slot->m_slotNo, srcId); + return false; + } + + // validate the target ID + if (gi) { + if (!acl::AccessControl::validateTGId(m_slot->m_slotNo, dstId)) { + LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK denial, TGID rejection, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); + return false; + } + } + } + + // Regenerate the CSBK data + csbk.encode(data + 2U); + + // Regenerate the Slot Type + slotType.encode(data + 2U); + + // Convert the Data Sync to be from the BS or MS as needed + Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + + m_slot->m_rfSeqNo = 0U; + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + if (m_slot->m_duplex) + m_slot->writeQueueRF(data); + + m_slot->writeNetworkRF(data, DT_CSBK, gi ? FLCO_GROUP : FLCO_PRIVATE, srcId, dstId); + + if (m_verbose) { + switch (csbko) { + case CSBKO_UU_V_REQ: + if (m_verbose) { + LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_UU_V_REQ (Unit to Unit Voice Service Request), src = %u, dst = %s%u", + m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); + } + break; + case CSBKO_UU_ANS_RSP: + if (m_verbose) { + LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_UU_ANS_RSP (Unit to Unit Voice Service Answer Response), src = %u, dst = %s%u", + m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); + } + break; + case CSBKO_NACK_RSP: + if (m_verbose) { + LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_NACK_RSP (Negative Acknowledgment Response), src = %u, dst = %s%u", + m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); + } + break; + case CSBKO_CALL_ALRT: + if (m_verbose) { + LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_CALL_ALRT (Call Alert), src = %u, dst = %s%u", + m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); + } + + ::ActivityLog("DMR", true, "Slot %u call alert request from %u to %u", m_slot->m_slotNo, srcId, dstId); + break; + case CSBKO_ACK_RSP: + if (m_verbose) { + LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_ACK_RSP (Acknowledge Response), src = %u, dst = %s%u", + m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); + } + + ::ActivityLog("DMR", true, "Slot %u ack response from %u to %u", m_slot->m_slotNo, srcId, dstId); + break; + case CSBKO_EXT_FNCT: + if (m_verbose) { + LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_EXT_FNCT (Extended Function), op = $%02X, arg = %u, tgt = %u", + m_slot->m_slotNo, csbk.getCBF(), dstId, srcId); + } + + // generate activity log entry + if (csbk.getCBF() == DMR_EXT_FNCT_CHECK) { + ::ActivityLog("DMR", true, "Slot %u radio check request from %u to %u", m_slot->m_slotNo, dstId, srcId); + } + else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT) { + ::ActivityLog("DMR", true, "Slot %u radio inhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId); + } + else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT) { + ::ActivityLog("DMR", true, "Slot %u radio uninhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId); + } + else if (csbk.getCBF() == DMR_EXT_FNCT_CHECK_ACK) { + ::ActivityLog("DMR", true, "Slot %u radio check response from %u to %u", m_slot->m_slotNo, dstId, srcId); + } + else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT_ACK) { + ::ActivityLog("DMR", true, "Slot %u radio inhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId); + } + else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT_ACK) { + ::ActivityLog("DMR", true, "Slot %u radio uninhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId); + } + break; + case CSBKO_PRECCSBK: + if (m_verbose) { + LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_PRECCSBK (%s Preamble CSBK), toFollow = %u, src = %u, dst = %s%u", + m_slot->m_slotNo, csbk.getDataContent() ? "Data" : "CSBK", csbk.getCBF(), srcId, gi ? "TG " : "", dstId); + } + break; + default: + LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, unhandled CSBK, csbko = $%02X, fid = $%02X", m_slot->m_slotNo, csbko, csbk.getFID()); + break; + } + } + + if (m_debug) { + Utils::dump(2U, "!!! *TX DMR Frame - DT_CSBK", data + 2U, DMR_FRAME_LENGTH_BYTES); + } + + return true; + } + + return false; +} + +/// +/// Process a data frame from the network. +/// +/// +void ControlPacket::processNetwork(const data::Data & dmrData) +{ + uint8_t dataType = dmrData.getDataType(); + + uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; + dmrData.getData(data + 2U); + + if (dataType == DT_CSBK) { + lc::CSBK csbk = lc::CSBK(); + csbk.setVerbose(m_dumpCSBKData); + + bool valid = csbk.decode(data + 2U); + if (!valid) { + LogError(LOG_NET, "DMR Slot %u, DT_CSBK, unable to decode the network CSBK", m_slot->m_slotNo); + return; + } + + uint8_t csbko = csbk.getCSBKO(); + if (csbko == CSBKO_BSDWNACT) + return; + + bool gi = csbk.getGI(); + uint32_t srcId = csbk.getSrcId(); + uint32_t dstId = csbk.getDstId(); + + CHECK_TG_HANG(dstId); + + // Regenerate the CSBK data + csbk.encode(data + 2U); + + // Regenerate the Slot Type + SlotType slotType; + slotType.decode(data + 2U); + slotType.setColorCode(m_slot->m_colorCode); + slotType.encode(data + 2U); + + // Convert the Data Sync to be from the BS or MS as needed + Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + if (csbko == CSBKO_PRECCSBK && csbk.getDataContent()) { + uint32_t cbf = NO_PREAMBLE_CSBK + csbk.getCBF() - 1U; + for (uint32_t i = 0U; i < NO_PREAMBLE_CSBK; i++, cbf--) { + // Change blocks to follow + csbk.setCBF(cbf); + + // Regenerate the CSBK data + csbk.encode(data + 2U); + + // Regenerate the Slot Type + SlotType slotType; + slotType.decode(data + 2U); + slotType.setColorCode(m_slot->m_colorCode); + slotType.encode(data + 2U); + + // Convert the Data Sync to be from the BS or MS as needed + Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + + m_slot->writeQueueNet(data); + } + } + else + m_slot->writeQueueNet(data); + + if (m_verbose) { + switch (csbko) { + case CSBKO_UU_V_REQ: + if (m_verbose) { + LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_UU_V_REQ (Unit to Unit Voice Service Request), src = %u, dst = %s%u", + m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); + } + break; + case CSBKO_UU_ANS_RSP: + if (m_verbose) { + LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_UU_ANS_RSP (Unit to Unit Voice Service Answer Response), src = %u, dst = %s%u", + m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); + } + break; + case CSBKO_NACK_RSP: + if (m_verbose) { + LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_NACK_RSP (Negative Acknowledgment Response), src = %u, dst = %s%u", + m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); + } + break; + case CSBKO_CALL_ALRT: + if (m_verbose) { + LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_CALL_ALRT (Call Alert), src = %u, dst = %s%u", + m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); + } + + ::ActivityLog("DMR", false, "Slot %u call alert request from %u to %u", m_slot->m_slotNo, srcId, dstId); + break; + case CSBKO_ACK_RSP: + if (m_verbose) { + LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_ACK_RSP (Acknowledge Response), src = %u, dst = %s%u", + m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); + } + + ::ActivityLog("DMR", false, "Slot %u ack response from %u to %u", m_slot->m_slotNo, srcId, dstId); + break; + case CSBKO_EXT_FNCT: + if (m_verbose) { + LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_EXT_FNCT (Extended Function), op = $%02X, arg = %u, tgt = %u", + m_slot->m_slotNo, csbk.getCBF(), dstId, srcId); + } + + // generate activity log entry + if (csbk.getCBF() == DMR_EXT_FNCT_CHECK) { + ::ActivityLog("DMR", false, "Slot %u radio check request from %u to %u", m_slot->m_slotNo, dstId, srcId); + } + else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT) { + ::ActivityLog("DMR", false, "Slot %u radio inhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId); + } + else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT) { + ::ActivityLog("DMR", false, "Slot %u radio uninhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId); + } + else if (csbk.getCBF() == DMR_EXT_FNCT_CHECK_ACK) { + ::ActivityLog("DMR", false, "Slot %u radio check response from %u to %u", m_slot->m_slotNo, dstId, srcId); + } + else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT_ACK) { + ::ActivityLog("DMR", false, "Slot %u radio inhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId); + } + else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT_ACK) { + ::ActivityLog("DMR", false, "Slot %u radio uninhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId); + } + break; + case CSBKO_PRECCSBK: + if (m_verbose) { + LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_PRECCSBK (%s Preamble CSBK), toFollow = %u, src = %u, dst = %s%u", + m_slot->m_slotNo, csbk.getDataContent() ? "Data" : "CSBK", csbk.getCBF(), srcId, gi ? "TG " : "", dstId); + } + break; + default: + LogWarning(LOG_NET, "DMR Slot %u, DT_CSBK, unhandled network CSBK, csbko = $%02X, fid = $%02X", m_slot->m_slotNo, csbko, csbk.getFID()); + break; + } + } + } + else { + // Unhandled data type + LogWarning(LOG_NET, "DMR Slot %u, unhandled network data, type = $%02X", m_slot->m_slotNo, dataType); + } +} + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- +/// +/// Initializes a new instance of the ControlPacket class. +/// +/// DMR slot. +/// Instance of the BaseNetwork class. +/// +/// Flag indicating whether DMR debug is enabled. +/// Flag indicating whether DMR verbose logging is enabled. +ControlPacket::ControlPacket(Slot * slot, network::BaseNetwork * network, bool dumpCSBKData, bool debug, bool verbose) : + m_slot(slot), + m_dumpCSBKData(dumpCSBKData), + m_verbose(verbose), + m_debug(debug) +{ + /* stub */ +} + +/// +/// Finalizes a instance of the ControlPacket class. +/// +ControlPacket::~ControlPacket() +{ + /* stub */ +} diff --git a/dmr/ControlPacket.h b/dmr/ControlPacket.h new file mode 100644 index 00000000..1ce59e20 --- /dev/null +++ b/dmr/ControlPacket.h @@ -0,0 +1,85 @@ +/** +* 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 +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX +* Copyright (C) 2017-2020 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_CONTROL_PACKET_H__) +#define __DMR_CONTROL_PACKET_H__ + +#include "Defines.h" +#include "dmr/data/Data.h" +#include "dmr/data/EmbeddedData.h" +#include "dmr/lc/LC.h" +#include "dmr/Slot.h" +#include "modem/Modem.h" +#include "network/BaseNetwork.h" +#include "RingBuffer.h" +#include "lookups/RadioIdLookup.h" +#include "lookups/TalkgroupIdLookup.h" +#include "StopWatch.h" +#include "Timer.h" + +#include + +namespace dmr +{ + // --------------------------------------------------------------------------- + // Class Prototypes + // --------------------------------------------------------------------------- + class HOST_SW_API DataPacket; + class HOST_SW_API Slot; + + // --------------------------------------------------------------------------- + // Class Declaration + // This class implements core logic for handling DMR control (CSBK) packets. + // --------------------------------------------------------------------------- + + class HOST_SW_API ControlPacket { + public: + /// Process a data frame from the RF interface. + bool process(uint8_t* data, uint32_t len); + /// Process a data frame from the network. + void processNetwork(const data::Data& dmrData); + + private: + friend class DataPacket; + friend class Slot; + Slot* m_slot; + + bool m_dumpCSBKData; + bool m_verbose; + bool m_debug; + + /// Initializes a new instance of the ControlPacket class. + ControlPacket(Slot* slot, network::BaseNetwork* network, bool dumpCSBKData, bool debug, bool verbose); + /// Finalizes a instance of the DataPacket class. + ~ControlPacket(); + }; +} // namespace dmr + +#endif // __DMR_CONTROL_PACKET_H__ diff --git a/dmr/DataPacket.cpp b/dmr/DataPacket.cpp index c42a6fb0..07585ee0 100644 --- a/dmr/DataPacket.cpp +++ b/dmr/DataPacket.cpp @@ -98,160 +98,13 @@ bool DataPacket::process(uint8_t* data, uint32_t len) slotType.setColorCode(m_slot->m_colorCode); slotType.setDataType(dataType); - if (dataType == DT_VOICE_LC_HEADER) { - if (m_slot->m_rfState == RS_RF_AUDIO) - return true; - - lc::FullLC fullLC; - lc::LC* lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER); - if (lc == NULL) - return false; - - uint32_t srcId = lc->getSrcId(); - uint32_t dstId = lc->getDstId(); - uint8_t flco = lc->getFLCO(); - - CHECK_TRAFFIC_COLLISION_DELLC(dstId); - - // validate source RID - if (!acl::AccessControl::validateSrcId(srcId)) { - if (m_lastRejectId == 0U || m_lastRejectId == srcId) { - LogWarning(LOG_RF, "DMR Slot %u, DT_VOICE_LC_HEADER denial, RID rejection, srcId = %u", m_slot->m_slotNo, srcId); - ::ActivityLog("DMR", true, "Slot %u RF voice rejection from %u to %s%u ", m_slot->m_slotNo, srcId, flco == FLCO_GROUP ? "TG " : "", dstId); - } - - m_slot->m_rfLastDstId = 0U; - m_slot->m_rfTGHang.stop(); - - delete lc; - return false; - } - - // validate target TID, if the target is a talkgroup - if (flco == FLCO_GROUP) { - if (!acl::AccessControl::validateTGId(m_slot->m_slotNo, dstId)) { - if (m_lastRejectId == 0U || m_lastRejectId == dstId) { - LogWarning(LOG_RF, "DMR Slot %u, DT_VOICE_LC_HEADER denial, TGID rejection, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); - ::ActivityLog("DMR", true, "Slot %u RF voice rejection from %u to TG %u ", m_slot->m_slotNo, srcId, dstId); - } - - m_slot->m_rfLastDstId = 0U; - m_slot->m_rfTGHang.stop(); - - delete lc; - return false; - } - } - - m_lastRejectId = 0U; - - if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, DT_VOICE_LC_HEADER, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, lc->getSrcId(), lc->getDstId(), lc->getFLCO(), lc->getFID(), lc->getPF()); - } - - uint8_t fid = lc->getFID(); - - // NOTE: this is fiddly -- on Motorola a FID of 0x10 indicates a SU has transmitted with Enhanced Privacy enabled -- this might change - // and is not exact science! - bool encrypted = (fid & 0x10U) == 0x10U; - - m_rfLC = lc; - - // The standby LC data - m_slot->m_voice->m_rfEmbeddedLC.setLC(*m_rfLC); - m_slot->m_voice->m_rfEmbeddedData[0U].setLC(*m_rfLC); - m_slot->m_voice->m_rfEmbeddedData[1U].setLC(*m_rfLC); - - // Regenerate the LC data - fullLC.encode(*m_rfLC, data + 2U, DT_VOICE_LC_HEADER); - - // Regenerate the Slot Type - slotType.encode(data + 2U); - - // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - m_slot->m_rfTimeoutTimer.start(); - m_slot->m_rfTimeout = false; - - m_slot->m_rfFrames = 0U; - m_slot->m_rfSeqNo = 0U; - m_slot->m_rfBits = 1U; - m_slot->m_rfErrs = 0U; - - m_slot->m_voice->m_rfEmbeddedReadN = 0U; - m_slot->m_voice->m_rfEmbeddedWriteN = 1U; - m_slot->m_voice->m_rfTalkerId = TALKER_ID_NONE; - - m_slot->m_minRSSI = m_slot->m_rssi; - m_slot->m_maxRSSI = m_slot->m_rssi; - m_slot->m_aveRSSI = m_slot->m_rssi; - m_slot->m_rssiCount = 1U; - - if (m_slot->m_duplex) { - m_slot->m_queue.clear(); - m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); - - for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) - m_slot->writeQueueRF(data); - } - - m_slot->writeNetworkRF(data, DT_VOICE_LC_HEADER); - - m_slot->m_rfState = RS_RF_AUDIO; - m_slot->m_rfLastDstId = dstId; - - if (m_slot->m_netState == RS_NET_IDLE) { - m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, true); - } - - if (m_debug) { - Utils::dump(2U, "!!! *TX DMR Frame - DT_VOICE_LC_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES); - } - - ::ActivityLog("DMR", true, "Slot %u RF %svoice header from %u to %s%u", m_slot->m_slotNo, encrypted ? "encrypted " : "", srcId, flco == FLCO_GROUP ? "TG " : "", dstId); - return true; - } - else if (dataType == DT_VOICE_PI_HEADER) { - if (m_slot->m_rfState != RS_RF_AUDIO) - return false; - - // Regenerate the Slot Type - slotType.encode(data + 2U); - - // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); - - // Regenerate the payload and the BPTC (196,96) FEC - edac::BPTC19696 bptc; - uint8_t payload[12U]; - bptc.decode(data + 2U, payload); - bptc.encode(payload, data + 2U); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - if (m_slot->m_duplex) - m_slot->writeQueueRF(data); - - m_slot->writeNetworkRF(data, DT_VOICE_PI_HEADER); - - if (m_debug) { - Utils::dump(2U, "!!! *TX DMR Frame - DT_VOICE_PI_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES); - } - - return true; - } - else if (dataType == DT_TERMINATOR_WITH_LC) { + if (dataType == DT_TERMINATOR_WITH_LC) { if (m_slot->m_rfState != RS_RF_AUDIO) return false; // Regenerate the LC data lc::FullLC fullLC; - fullLC.encode(*m_rfLC, data + 2U, DT_TERMINATOR_WITH_LC); + fullLC.encode(*m_slot->m_rfLC, data + 2U, DT_TERMINATOR_WITH_LC); // Regenerate the Slot Type slotType.encode(data + 2U); @@ -289,148 +142,14 @@ bool DataPacket::process(uint8_t* data, uint32_t len) m_slot->m_slotNo, m_slot->m_rfFrames, m_slot->m_rfBits, m_slot->m_rfErrs, float(m_slot->m_rfErrs * 100U) / float(m_slot->m_rfBits)); if (m_slot->m_rfTimeout) { - writeEndRF(); + m_slot->writeEndRF(); return false; } else { - writeEndRF(); + m_slot->writeEndRF(); return true; } } - else if (dataType == DT_CSBK) { - // generate a new CSBK and check validity - lc::CSBK csbk = lc::CSBK(); - csbk.setVerbose(m_dumpCSBKData); - - bool valid = csbk.decode(data + 2U); - if (!valid) - return false; - - uint8_t csbko = csbk.getCSBKO(); - if (csbko == CSBKO_BSDWNACT) - return false; - - bool gi = csbk.getGI(); - uint32_t srcId = csbk.getSrcId(); - uint32_t dstId = csbk.getDstId(); - - if (srcId != 0U || dstId != 0U) { - CHECK_TRAFFIC_COLLISION(dstId); - - // validate the source RID - if (!acl::AccessControl::validateSrcId(srcId)) { - LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK denial, RID rejection, srcId = %u", m_slot->m_slotNo, srcId); - return false; - } - - // validate the target ID - if (gi) { - if (!acl::AccessControl::validateTGId(m_slot->m_slotNo, dstId)) { - LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK denial, TGID rejection, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); - return false; - } - } - } - - // Regenerate the CSBK data - csbk.encode(data + 2U); - - // Regenerate the Slot Type - slotType.encode(data + 2U); - - // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); - - m_slot->m_rfSeqNo = 0U; - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - if (m_slot->m_duplex) - m_slot->writeQueueRF(data); - - m_slot->writeNetworkRF(data, DT_CSBK, gi ? FLCO_GROUP : FLCO_PRIVATE, srcId, dstId); - - if (m_verbose) { - switch (csbko) { - case CSBKO_UU_V_REQ: - if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_UU_V_REQ (Unit to Unit Voice Service Request), src = %u, dst = %s%u", - m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); - } - break; - case CSBKO_UU_ANS_RSP: - if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_UU_ANS_RSP (Unit to Unit Voice Service Answer Response), src = %u, dst = %s%u", - m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); - } - break; - case CSBKO_NACK_RSP: - if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_NACK_RSP (Negative Acknowledgment Response), src = %u, dst = %s%u", - m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); - } - break; - case CSBKO_CALL_ALRT: - if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_CALL_ALRT (Call Alert), src = %u, dst = %s%u", - m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); - } - - ::ActivityLog("DMR", true, "Slot %u call alert request from %u to %u", m_slot->m_slotNo, srcId, dstId); - break; - case CSBKO_ACK_RSP: - if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_ACK_RSP (Acknowledge Response), src = %u, dst = %s%u", - m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); - } - - ::ActivityLog("DMR", true, "Slot %u ack response from %u to %u", m_slot->m_slotNo, srcId, dstId); - break; - case CSBKO_EXT_FNCT: - if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_EXT_FNCT (Extended Function), op = $%02X, arg = %u, tgt = %u", - m_slot->m_slotNo, csbk.getCBF(), dstId, srcId); - } - - // generate activity log entry - if (csbk.getCBF() == DMR_EXT_FNCT_CHECK) { - ::ActivityLog("DMR", true, "Slot %u radio check request from %u to %u", m_slot->m_slotNo, dstId, srcId); - } - else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT) { - ::ActivityLog("DMR", true, "Slot %u radio inhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId); - } - else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT) { - ::ActivityLog("DMR", true, "Slot %u radio uninhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId); - } - else if (csbk.getCBF() == DMR_EXT_FNCT_CHECK_ACK) { - ::ActivityLog("DMR", true, "Slot %u radio check response from %u to %u", m_slot->m_slotNo, dstId, srcId); - } - else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT_ACK) { - ::ActivityLog("DMR", true, "Slot %u radio inhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId); - } - else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT_ACK) { - ::ActivityLog("DMR", true, "Slot %u radio uninhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId); - } - break; - case CSBKO_PRECCSBK: - if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_PRECCSBK (%s Preamble CSBK), toFollow = %u, src = %u, dst = %s%u", - m_slot->m_slotNo, csbk.getDataContent() ? "Data" : "CSBK", csbk.getCBF(), srcId, gi ? "TG " : "", dstId); - } - break; - default: - LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, unhandled CSBK, csbko = $%02X, fid = $%02X", m_slot->m_slotNo, csbko, csbk.getFID()); - break; - } - } - - if (m_debug) { - Utils::dump(2U, "!!! *TX DMR Frame - DT_CSBK", data + 2U, DMR_FRAME_LENGTH_BYTES); - } - - return true; - } else if (dataType == DT_DATA_HEADER) { if (m_slot->m_rfState == RS_RF_DATA) return true; @@ -462,7 +181,7 @@ bool DataPacket::process(uint8_t* data, uint32_t len) m_slot->m_rfFrames = dataHeader.getBlocks(); m_slot->m_rfSeqNo = 0U; - m_rfLC = new lc::LC(gi ? FLCO_GROUP : FLCO_PRIVATE, srcId, dstId); + m_slot->m_rfLC = new lc::LC(gi ? FLCO_GROUP : FLCO_PRIVATE, srcId, dstId); // Regenerate the data header dataHeader.encode(data + 2U); @@ -499,7 +218,7 @@ bool DataPacket::process(uint8_t* data, uint32_t len) if (m_slot->m_rfFrames == 0U) { ::ActivityLog("DMR", true, "Slot %u ended RF data transmission", m_slot->m_slotNo); - writeEndRF(); + m_slot->writeEndRF(); } return true; @@ -566,7 +285,7 @@ bool DataPacket::process(uint8_t* data, uint32_t len) } LogMessage(LOG_RF, "DMR Slot %u, DT_RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo); - writeEndRF(); + m_slot->writeEndRF(); } if (m_debug) { @@ -590,190 +309,13 @@ void DataPacket::processNetwork(const data::Data& dmrData) uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; dmrData.getData(data + 2U); - if (dataType == DT_VOICE_LC_HEADER) { - if (m_slot->m_netState == RS_NET_AUDIO) - return; - - lc::FullLC fullLC; - lc::LC * lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER); - if (lc == NULL) { - LogWarning(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, bad LC received from the network, replacing", m_slot->m_slotNo); - lc = new lc::LC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId()); - } - - uint32_t srcId = lc->getSrcId(); - uint32_t dstId = lc->getDstId(); - uint8_t flco = lc->getFLCO(); - - CHECK_TG_HANG_DELLC(dstId); - - if (dstId != dmrData.getDstId() || srcId != dmrData.getSrcId() || flco != dmrData.getFLCO()) - LogWarning(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, header doesn't match the DMR RF header: %u->%s%u %u->%s%u", m_slot->m_slotNo, - dmrData.getSrcId(), dmrData.getFLCO() == FLCO_GROUP ? "TG" : "", dmrData.getDstId(), - srcId, flco == FLCO_GROUP ? "TG" : "", dstId); - - if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, lc->getSrcId(), lc->getDstId(), lc->getFLCO(), lc->getFID(), lc->getPF()); - } - - m_netLC = lc; - - // The standby LC data - m_slot->m_voice->m_netEmbeddedLC.setLC(*m_netLC); - m_slot->m_voice->m_netEmbeddedData[0U].setLC(*m_netLC); - m_slot->m_voice->m_netEmbeddedData[1U].setLC(*m_netLC); - - // Regenerate the LC data - fullLC.encode(*m_netLC, data + 2U, DT_VOICE_LC_HEADER); - - // Regenerate the Slot Type - SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); - slotType.setDataType(DT_VOICE_LC_HEADER); - slotType.encode(data + 2U); - - // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - m_slot->m_voice->m_lastFrameValid = false; - - m_slot->m_netTimeoutTimer.start(); - m_slot->m_netTimeout = false; - - m_slot->m_netFrames = 0U; - m_slot->m_netLost = 0U; - m_slot->m_netBits = 1U; - m_slot->m_netErrs = 0U; - - m_slot->m_voice->m_netEmbeddedReadN = 0U; - m_slot->m_voice->m_netEmbeddedWriteN = 1U; - m_slot->m_voice->m_netTalkerId = TALKER_ID_NONE; - - if (m_slot->m_duplex) { - m_slot->m_queue.clear(); - m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); - } - - for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++) - m_slot->writeQueueNet(m_slot->m_idle); - - if (m_slot->m_duplex) { - for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) - m_slot->writeQueueNet(data); - } - else { - for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++) - m_slot->writeQueueNet(data); - } - - m_slot->m_netState = RS_NET_AUDIO; - m_slot->m_netLastDstId = dstId; - - m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, true); - - if (m_debug) { - Utils::dump(2U, "!!! *TX DMR Network Frame - DT_VOICE_LC_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES); - } - - ::ActivityLog("DMR", false, "Slot %u network voice header from %u to %s%u", m_slot->m_slotNo, srcId, flco == FLCO_GROUP ? "TG " : "", dstId); - } - else if (dataType == DT_VOICE_PI_HEADER) { - if (m_slot->m_netState != RS_NET_AUDIO) { - lc::LC* lc = new lc::LC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId()); - - uint32_t srcId = lc->getSrcId(); - uint32_t dstId = lc->getDstId(); - - CHECK_TG_HANG_DELLC(dstId); - - m_netLC = lc; - - m_slot->m_voice->m_lastFrameValid = false; - - m_slot->m_netTimeoutTimer.start(); - m_slot->m_netTimeout = false; - - if (m_slot->m_duplex) { - m_slot->m_queue.clear(); - m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); - } - - for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++) - m_slot->writeQueueNet(m_slot->m_idle); - - // Create a dummy start frame - uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U]; - - Sync::addDMRDataSync(start + 2U, m_slot->m_duplex); - - lc::FullLC fullLC; - fullLC.encode(*m_netLC, start + 2U, DT_VOICE_LC_HEADER); - - SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); - slotType.setDataType(DT_VOICE_LC_HEADER); - slotType.encode(start + 2U); - - start[0U] = TAG_DATA; - start[1U] = 0x00U; - - if (m_slot->m_duplex) { - for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) - m_slot->writeQueueRF(start); - } - else { - for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++) - m_slot->writeQueueRF(start); - } - - m_slot->m_netFrames = 0U; - m_slot->m_netLost = 0U; - m_slot->m_netBits = 1U; - m_slot->m_netErrs = 0U; - - m_slot->m_netState = RS_NET_AUDIO; - m_slot->m_netLastDstId = dstId; - - m_slot->setShortLC(m_slot->m_slotNo, dstId, m_netLC->getFLCO(), true); - - ::ActivityLog("DMR", false, "Slot %u network late entry from %u to %s%u", - m_slot->m_slotNo, srcId, m_netLC->getFLCO() == FLCO_GROUP ? "TG " : "", dstId); - } - - // Regenerate the Slot Type - SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); - slotType.setDataType(DT_VOICE_PI_HEADER); - slotType.encode(data + 2U); - - // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); - - // Regenerate the payload and the BPTC (196,96) FEC - edac::BPTC19696 bptc; - uint8_t payload[12U]; - bptc.decode(data + 2U, payload); - bptc.encode(payload, data + 2U); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - m_slot->writeQueueNet(data); - - if (m_debug) { - Utils::dump(2U, "!!! *TX DMR Network Frame - DT_VOICE_PI_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES); - } - } - else if (dataType == DT_TERMINATOR_WITH_LC) { + if (dataType == DT_TERMINATOR_WITH_LC) { if (m_slot->m_netState != RS_NET_AUDIO) return; // Regenerate the LC data lc::FullLC fullLC; - fullLC.encode(*m_netLC, data + 2U, DT_TERMINATOR_WITH_LC); + fullLC.encode(*m_slot->m_netLC, data + 2U, DT_TERMINATOR_WITH_LC); // Regenerate the Slot Type SlotType slotType; @@ -807,140 +349,7 @@ void DataPacket::processNetwork(const data::Data& dmrData) ::ActivityLog("DMR", false, "Slot %u network end of voice transmission, %.1f seconds, %u%% packet loss, BER: %.1f%%", m_slot->m_slotNo, float(m_slot->m_netFrames) / 16.667F, (m_slot->m_netLost * 100U) / m_slot->m_netFrames, float(m_slot->m_netErrs * 100U) / float(m_slot->m_netBits)); - writeEndNet(); - } - else if (dataType == DT_CSBK) { - lc::CSBK csbk = lc::CSBK(); - csbk.setVerbose(m_dumpCSBKData); - - bool valid = csbk.decode(data + 2U); - if (!valid) { - LogError(LOG_NET, "DMR Slot %u, DT_CSBK, unable to decode the network CSBK", m_slot->m_slotNo); - return; - } - - uint8_t csbko = csbk.getCSBKO(); - if (csbko == CSBKO_BSDWNACT) - return; - - bool gi = csbk.getGI(); - uint32_t srcId = csbk.getSrcId(); - uint32_t dstId = csbk.getDstId(); - - CHECK_TG_HANG(dstId); - - // Regenerate the CSBK data - csbk.encode(data + 2U); - - // Regenerate the Slot Type - SlotType slotType; - slotType.decode(data + 2U); - slotType.setColorCode(m_slot->m_colorCode); - slotType.encode(data + 2U); - - // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - if (csbko == CSBKO_PRECCSBK && csbk.getDataContent()) { - uint32_t cbf = NO_PREAMBLE_CSBK + csbk.getCBF() - 1U; - for (uint32_t i = 0U; i < NO_PREAMBLE_CSBK; i++, cbf--) { - // Change blocks to follow - csbk.setCBF(cbf); - - // Regenerate the CSBK data - csbk.encode(data + 2U); - - // Regenerate the Slot Type - SlotType slotType; - slotType.decode(data + 2U); - slotType.setColorCode(m_slot->m_colorCode); - slotType.encode(data + 2U); - - // Convert the Data Sync to be from the BS or MS as needed - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); - - m_slot->writeQueueNet(data); - } - } - else - m_slot->writeQueueNet(data); - - if (m_verbose) { - switch (csbko) { - case CSBKO_UU_V_REQ: - if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_UU_V_REQ (Unit to Unit Voice Service Request), src = %u, dst = %s%u", - m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); - } - break; - case CSBKO_UU_ANS_RSP: - if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_UU_ANS_RSP (Unit to Unit Voice Service Answer Response), src = %u, dst = %s%u", - m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); - } - break; - case CSBKO_NACK_RSP: - if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_NACK_RSP (Negative Acknowledgment Response), src = %u, dst = %s%u", - m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); - } - break; - case CSBKO_CALL_ALRT: - if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_CALL_ALRT (Call Alert), src = %u, dst = %s%u", - m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); - } - - ::ActivityLog("DMR", false, "Slot %u call alert request from %u to %u", m_slot->m_slotNo, srcId, dstId); - break; - case CSBKO_ACK_RSP: - if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_ACK_RSP (Acknowledge Response), src = %u, dst = %s%u", - m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId); - } - - ::ActivityLog("DMR", false, "Slot %u ack response from %u to %u", m_slot->m_slotNo, srcId, dstId); - break; - case CSBKO_EXT_FNCT: - if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_EXT_FNCT (Extended Function), op = $%02X, arg = %u, tgt = %u", - m_slot->m_slotNo, csbk.getCBF(), dstId, srcId); - } - - // generate activity log entry - if (csbk.getCBF() == DMR_EXT_FNCT_CHECK) { - ::ActivityLog("DMR", false, "Slot %u radio check request from %u to %u", m_slot->m_slotNo, dstId, srcId); - } - else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT) { - ::ActivityLog("DMR", false, "Slot %u radio inhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId); - } - else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT) { - ::ActivityLog("DMR", false, "Slot %u radio uninhibit request from %u to %u", m_slot->m_slotNo, dstId, srcId); - } - else if (csbk.getCBF() == DMR_EXT_FNCT_CHECK_ACK) { - ::ActivityLog("DMR", false, "Slot %u radio check response from %u to %u", m_slot->m_slotNo, dstId, srcId); - } - else if (csbk.getCBF() == DMR_EXT_FNCT_INHIBIT_ACK) { - ::ActivityLog("DMR", false, "Slot %u radio inhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId); - } - else if (csbk.getCBF() == DMR_EXT_FNCT_UNINHIBIT_ACK) { - ::ActivityLog("DMR", false, "Slot %u radio uninhibit response from %u to %u", m_slot->m_slotNo, dstId, srcId); - } - break; - case CSBKO_PRECCSBK: - if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_PRECCSBK (%s Preamble CSBK), toFollow = %u, src = %u, dst = %s%u", - m_slot->m_slotNo, csbk.getDataContent() ? "Data" : "CSBK", csbk.getCBF(), srcId, gi ? "TG " : "", dstId); - } - break; - default: - LogWarning(LOG_NET, "DMR Slot %u, DT_CSBK, unhandled network CSBK, csbko = $%02X, fid = $%02X", m_slot->m_slotNo, csbko, csbk.getFID()); - break; - } - } + m_slot->writeEndNet(); } else if (dataType == DT_DATA_HEADER) { if (m_slot->m_netState == RS_NET_DATA) @@ -999,12 +408,12 @@ void DataPacket::processNetwork(const data::Data& dmrData) if (m_slot->m_netFrames == 0U) { ::ActivityLog("DMR", false, "Slot %u ended network data transmission", m_slot->m_slotNo); - writeEndNet(); + m_slot->writeEndNet(); } } else if (dataType == DT_RATE_12_DATA || dataType == DT_RATE_34_DATA || dataType == DT_RATE_1_DATA) { if (m_slot->m_netState != RS_NET_DATA || m_slot->m_netFrames == 0U) { - writeEndNet(); + m_slot->writeEndNet(); return; } @@ -1070,7 +479,7 @@ void DataPacket::processNetwork(const data::Data& dmrData) } LogMessage(LOG_NET, "DMR Slot %u, DT_RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo); - writeEndNet(); + m_slot->writeEndNet(); } } else { @@ -1089,19 +498,15 @@ void DataPacket::processNetwork(const data::Data& dmrData) /// Instance of the BaseNetwork class. /// /// -/// /// Flag indicating whether DMR debug is enabled. /// Flag indicating whether DMR verbose logging is enabled. -DataPacket::DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose) : +DataPacket::DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose) : m_slot(slot), - m_rfLC(NULL), - m_netLC(NULL), m_pduUserData(NULL), m_pduDataOffset(0U), m_lastRejectId(0U), m_dumpDataPacket(dumpDataPacket), m_repeatDataPacket(repeatDataPacket), - m_dumpCSBKData(dumpCSBKData), m_verbose(verbose), m_debug(debug) { @@ -1116,113 +521,3 @@ DataPacket::~DataPacket() { delete[] m_pduUserData; } - -/// -/// Helper to write RF end of frame data. -/// -/// -void DataPacket::writeEndRF(bool writeEnd) -{ - m_slot->m_rfState = RS_RF_LISTENING; - - if (m_slot->m_netState == RS_NET_IDLE) { - m_slot->setShortLC(m_slot->m_slotNo, 0U); - } - - if (writeEnd) { - if (m_slot->m_netState == RS_NET_IDLE && m_slot->m_duplex && !m_slot->m_rfTimeout) { - // Create a dummy start end frame - uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; - - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); - - lc::FullLC fullLC; - fullLC.encode(*m_rfLC, data + 2U, DT_TERMINATOR_WITH_LC); - - SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); - slotType.setDataType(DT_TERMINATOR_WITH_LC); - slotType.encode(data + 2U); - - data[0U] = TAG_EOT; - data[1U] = 0x00U; - - for (uint32_t i = 0U; i < m_slot->m_hangCount; i++) - m_slot->writeQueueRF(data); - } - } - - m_pduDataOffset = 0U; - - if (m_slot->m_network != NULL) - m_slot->m_network->resetDMR(m_slot->m_slotNo); - - m_slot->m_rfTimeoutTimer.stop(); - m_slot->m_rfTimeout = false; - - m_slot->m_rfFrames = 0U; - m_slot->m_rfErrs = 0U; - m_slot->m_rfBits = 1U; - - delete m_rfLC; - m_rfLC = NULL; -} - -/// -/// Helper to write network end of frame data. -/// -/// -void DataPacket::writeEndNet(bool writeEnd) -{ - m_slot->m_netState = RS_NET_IDLE; - - m_slot->setShortLC(m_slot->m_slotNo, 0U); - - m_slot->m_voice->m_lastFrameValid = false; - - if (writeEnd && !m_slot->m_netTimeout) { - // Create a dummy start end frame - uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; - - Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); - - lc::FullLC fullLC; - fullLC.encode(*m_netLC, data + 2U, DT_TERMINATOR_WITH_LC); - - SlotType slotType; - slotType.setColorCode(m_slot->m_colorCode); - slotType.setDataType(DT_TERMINATOR_WITH_LC); - slotType.encode(data + 2U); - - data[0U] = TAG_EOT; - data[1U] = 0x00U; - - if (m_slot->m_duplex) { - for (uint32_t i = 0U; i < m_slot->m_hangCount; i++) - m_slot->writeQueueNet(data); - } - else { - for (uint32_t i = 0U; i < 3U; i++) - m_slot->writeQueueNet(data); - } - } - - m_pduDataOffset = 0U; - - if (m_slot->m_network != NULL) - m_slot->m_network->resetDMR(m_slot->m_slotNo); - - m_slot->m_networkWatchdog.stop(); - m_slot->m_netTimeoutTimer.stop(); - m_slot->m_packetTimer.stop(); - m_slot->m_netTimeout = false; - - m_slot->m_netFrames = 0U; - m_slot->m_netLost = 0U; - - m_slot->m_netErrs = 0U; - m_slot->m_netBits = 1U; - - delete m_netLC; - m_netLC = NULL; -} diff --git a/dmr/DataPacket.h b/dmr/DataPacket.h index 95973c79..436f08d3 100644 --- a/dmr/DataPacket.h +++ b/dmr/DataPacket.h @@ -53,6 +53,7 @@ namespace dmr // Class Prototypes // --------------------------------------------------------------------------- class HOST_SW_API VoicePacket; + class HOST_SW_API ControlPacket; class HOST_SW_API Slot; // --------------------------------------------------------------------------- @@ -69,12 +70,10 @@ namespace dmr private: friend class VoicePacket; + friend class ControlPacket; friend class Slot; Slot* m_slot; - lc::LC* m_rfLC; - lc::LC* m_netLC; - uint8_t* m_pduUserData; uint32_t m_pduDataOffset; uint32_t m_lastRejectId; @@ -82,19 +81,13 @@ namespace dmr bool m_dumpDataPacket; bool m_repeatDataPacket; - bool m_dumpCSBKData; bool m_verbose; bool m_debug; /// Initializes a new instance of the DataPacket class. - DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool dumpCSBKData, bool debug, bool verbose); + DataPacket(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose); /// Finalizes a instance of the DataPacket class. ~DataPacket(); - - /// Helper to write RF end of frame data. - void writeEndRF(bool writeEnd = false); - /// Helper to write network end of frame data. - void writeEndNet(bool writeEnd = false); }; } // namespace dmr diff --git a/dmr/Slot.cpp b/dmr/Slot.cpp index b821639d..8320c5e9 100644 --- a/dmr/Slot.cpp +++ b/dmr/Slot.cpp @@ -112,7 +112,9 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz m_rfLastDstId(0U), m_netState(RS_NET_IDLE), m_netLastDstId(0U), + m_rfLC(NULL), m_rfSeqNo(0U), + m_netLC(NULL), m_networkWatchdog(1000U, 0U, 1500U), m_rfTimeoutTimer(1000U, timeout), m_rfTGHang(1000U, tgHang), @@ -142,7 +144,8 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz m_interval.start(); m_voice = new VoicePacket(this, m_network, m_embeddedLCOnly, m_dumpTAData, debug, verbose); - m_data = new DataPacket(this, m_network, dumpDataPacket, repeatDataPacket, dumpCSBKData, debug, verbose); + m_data = new DataPacket(this, m_network, dumpDataPacket, repeatDataPacket, debug, verbose); + m_control = new ControlPacket(this, m_network, dumpCSBKData, debug, verbose); } /// @@ -152,6 +155,7 @@ Slot::~Slot() { delete m_voice; delete m_data; + delete m_control; } /// @@ -180,18 +184,18 @@ bool Slot::processFrame(uint8_t *data, uint32_t len) m_slotNo, m_rfFrames, m_rfBits, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits)); if (m_rfTimeout) { - m_data->writeEndRF(); + writeEndRF(); return false; } else { - m_data->writeEndRF(true); + writeEndRF(true); return true; } } if (data[0U] == TAG_LOST && m_rfState == RS_RF_DATA) { ::ActivityLog("DMR", true, "Slot %u, RF data transmission lost", m_slotNo); - m_data->writeEndRF(); + writeEndRF(); return false; } @@ -255,7 +259,23 @@ bool Slot::processFrame(uint8_t *data, uint32_t len) m_rfTGHang.start(); if (dataSync) { - return m_data->process(data, len); + uint8_t dataType = data[1U] & 0x0FU; + + 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); + case DT_TERMINATOR_WITH_LC: + case DT_DATA_HEADER: + case DT_RATE_12_DATA: + case DT_RATE_34_DATA: + case DT_RATE_1_DATA: + default: + return m_data->process(data, len); + } } return m_voice->process(data, len); @@ -310,20 +330,23 @@ void Slot::processNetwork(const data::Data& dmrData) switch (dataType) { + case DT_CSBK: + m_control->processNetwork(dmrData); + break; case DT_VOICE_LC_HEADER: case DT_VOICE_PI_HEADER: + case DT_VOICE_SYNC: + case DT_VOICE: + m_voice->processNetwork(dmrData); + break; case DT_TERMINATOR_WITH_LC: case DT_DATA_HEADER: - case DT_CSBK: case DT_RATE_12_DATA: case DT_RATE_34_DATA: case DT_RATE_1_DATA: + default: m_data->processNetwork(dmrData); break; - case DT_VOICE_SYNC: - case DT_VOICE: - default: - m_voice->processNetwork(dmrData); } } @@ -380,11 +403,11 @@ void Slot::clock() m_netFrames += 1U; ::ActivityLog("DMR", false, "Slot %u network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%", m_slotNo, float(m_netFrames) / 16.667F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); - m_data->writeEndNet(true); + writeEndNet(true); } else { ::ActivityLog("DMR", false, "Slot %u network watchdog has expired", m_slotNo); - m_data->writeEndNet(); + writeEndNet(); } } } @@ -556,9 +579,9 @@ void Slot::writeQueueNet(const uint8_t *data) void Slot::writeNetworkRF(const uint8_t* data, uint8_t dataType, uint8_t errors) { assert(data != NULL); - assert(m_data->m_rfLC != NULL); + assert(m_rfLC != NULL); - writeNetworkRF(data, dataType, m_data->m_rfLC->getFLCO(), m_data->m_rfLC->getSrcId(), m_data->m_rfLC->getDstId(), errors); + writeNetworkRF(data, dataType, m_rfLC->getFLCO(), m_rfLC->getSrcId(), m_rfLC->getDstId(), errors); } /// @@ -820,6 +843,116 @@ void Slot::writeRF_TSCC_Bcast_Sys_Parm() writeQueueRF(data); } +/// +/// Helper to write RF end of frame data. +/// +/// +void Slot::writeEndRF(bool writeEnd) +{ + m_rfState = RS_RF_LISTENING; + + if (m_netState == RS_NET_IDLE) { + setShortLC(m_slotNo, 0U); + } + + if (writeEnd) { + if (m_netState == RS_NET_IDLE && m_duplex && !m_rfTimeout) { + // Create a dummy start end frame + uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; + + Sync::addDMRDataSync(data + 2U, m_duplex); + + lc::FullLC fullLC; + fullLC.encode(*m_rfLC, data + 2U, DT_TERMINATOR_WITH_LC); + + SlotType slotType; + slotType.setColorCode(m_colorCode); + slotType.setDataType(DT_TERMINATOR_WITH_LC); + slotType.encode(data + 2U); + + data[0U] = TAG_EOT; + data[1U] = 0x00U; + + for (uint32_t i = 0U; i < m_hangCount; i++) + writeQueueRF(data); + } + } + + m_data->m_pduDataOffset = 0U; + + if (m_network != NULL) + m_network->resetDMR(m_slotNo); + + m_rfTimeoutTimer.stop(); + m_rfTimeout = false; + + m_rfFrames = 0U; + m_rfErrs = 0U; + m_rfBits = 1U; + + delete m_rfLC; + m_rfLC = NULL; +} + +/// +/// Helper to write network end of frame data. +/// +/// +void Slot::writeEndNet(bool writeEnd) +{ + m_netState = RS_NET_IDLE; + + setShortLC(m_slotNo, 0U); + + m_voice->m_lastFrameValid = false; + + if (writeEnd && !m_netTimeout) { + // Create a dummy start end frame + uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; + + Sync::addDMRDataSync(data + 2U, m_duplex); + + lc::FullLC fullLC; + fullLC.encode(*m_netLC, data + 2U, DT_TERMINATOR_WITH_LC); + + SlotType slotType; + slotType.setColorCode(m_colorCode); + slotType.setDataType(DT_TERMINATOR_WITH_LC); + slotType.encode(data + 2U); + + data[0U] = TAG_EOT; + data[1U] = 0x00U; + + if (m_duplex) { + for (uint32_t i = 0U; i < m_hangCount; i++) + writeQueueNet(data); + } + else { + for (uint32_t i = 0U; i < 3U; i++) + writeQueueNet(data); + } + } + + m_data->m_pduDataOffset = 0U; + + if (m_network != NULL) + m_network->resetDMR(m_slotNo); + + m_networkWatchdog.stop(); + m_netTimeoutTimer.stop(); + m_packetTimer.stop(); + m_netTimeout = false; + + m_netFrames = 0U; + m_netLost = 0U; + + m_netErrs = 0U; + m_netBits = 1U; + + delete m_netLC; + m_netLC = NULL; +} + /// /// /// diff --git a/dmr/Slot.h b/dmr/Slot.h index ea4b5c3f..f1237079 100644 --- a/dmr/Slot.h +++ b/dmr/Slot.h @@ -34,6 +34,7 @@ #include "Defines.h" #include "dmr/Control.h" #include "dmr/SiteData.h" +#include "dmr/ControlPacket.h" #include "dmr/DataPacket.h" #include "dmr/VoicePacket.h" #include "modem/Modem.h" @@ -56,6 +57,7 @@ namespace dmr class HOST_SW_API Control; class HOST_SW_API VoicePacket; class HOST_SW_API DataPacket; + class HOST_SW_API ControlPacket; // --------------------------------------------------------------------------- // Class Declaration @@ -94,6 +96,8 @@ namespace dmr VoicePacket* m_voice; friend class DataPacket; DataPacket* m_data; + friend class ControlPacket; + ControlPacket* m_control; uint32_t m_slotNo; @@ -104,8 +108,12 @@ namespace dmr RPT_NET_STATE m_netState; uint32_t m_netLastDstId; + lc::LC* m_rfLC; + uint8_t m_rfSeqNo; + lc::LC* m_netLC; + Timer m_networkWatchdog; Timer m_rfTimeoutTimer; Timer m_rfTGHang; @@ -197,6 +205,11 @@ namespace dmr /// Helper to write a TSCC Sys_Parm broadcast packet on the RF interface. void writeRF_TSCC_Bcast_Sys_Parm(); + /// Helper to write RF end of frame data. + void writeEndRF(bool writeEnd = false); + /// Helper to write network end of frame data. + void writeEndNet(bool writeEnd = false); + /// static void setShortLC(uint32_t slotNo, uint32_t id, uint8_t flco = FLCO_GROUP, bool voice = true); /// diff --git a/dmr/VoicePacket.cpp b/dmr/VoicePacket.cpp index 32b6eb0e..35a376a1 100644 --- a/dmr/VoicePacket.cpp +++ b/dmr/VoicePacket.cpp @@ -56,6 +56,14 @@ using namespace dmr; return false; \ } +#define CHECK_TG_HANG_DELLC(_DST_ID) \ + if (m_slot->m_rfLastDstId != 0U) { \ + if (m_slot->m_rfLastDstId != _DST_ID && (m_slot->m_rfTGHang.isRunning() && !m_slot->m_rfTGHang.hasExpired())) { \ + delete lc; \ + return; \ + } \ + } + // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -69,8 +77,167 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) { assert(data != NULL); + bool dataSync = (data[1U] & DMR_SYNC_DATA) == DMR_SYNC_DATA; bool voiceSync = (data[1U] & DMR_SYNC_VOICE) == DMR_SYNC_VOICE; + if (dataSync) { + uint8_t dataType = data[1U] & 0x0FU; + + SlotType slotType; + slotType.setColorCode(m_slot->m_colorCode); + slotType.setDataType(dataType); + + if (dataType == DT_VOICE_LC_HEADER) { + if (m_slot->m_rfState == RS_RF_AUDIO) + return true; + + lc::FullLC fullLC; + lc::LC * lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER); + if (lc == NULL) + return false; + + uint32_t srcId = lc->getSrcId(); + uint32_t dstId = lc->getDstId(); + uint8_t flco = lc->getFLCO(); + + CHECK_TRAFFIC_COLLISION_DELLC(dstId); + + // validate source RID + if (!acl::AccessControl::validateSrcId(srcId)) { + if (m_slot->m_data->m_lastRejectId == 0U || m_slot->m_data->m_lastRejectId == srcId) { + LogWarning(LOG_RF, "DMR Slot %u, DT_VOICE_LC_HEADER denial, RID rejection, srcId = %u", m_slot->m_slotNo, srcId); + ::ActivityLog("DMR", true, "Slot %u RF voice rejection from %u to %s%u ", m_slot->m_slotNo, srcId, flco == FLCO_GROUP ? "TG " : "", dstId); + } + + m_slot->m_rfLastDstId = 0U; + m_slot->m_rfTGHang.stop(); + + delete lc; + return false; + } + + // validate target TID, if the target is a talkgroup + if (flco == FLCO_GROUP) { + if (!acl::AccessControl::validateTGId(m_slot->m_slotNo, dstId)) { + if (m_slot->m_data->m_lastRejectId == 0U || m_slot->m_data->m_lastRejectId == dstId) { + LogWarning(LOG_RF, "DMR Slot %u, DT_VOICE_LC_HEADER denial, TGID rejection, srcId = %u, dstId = %u", m_slot->m_slotNo, srcId, dstId); + ::ActivityLog("DMR", true, "Slot %u RF voice rejection from %u to TG %u ", m_slot->m_slotNo, srcId, dstId); + } + + m_slot->m_rfLastDstId = 0U; + m_slot->m_rfTGHang.stop(); + + delete lc; + return false; + } + } + + m_slot->m_data->m_lastRejectId = 0U; + + if (m_verbose) { + LogMessage(LOG_RF, "DMR Slot %u, DT_VOICE_LC_HEADER, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, lc->getSrcId(), lc->getDstId(), lc->getFLCO(), lc->getFID(), lc->getPF()); + } + + uint8_t fid = lc->getFID(); + + // NOTE: this is fiddly -- on Motorola a FID of 0x10 indicates a SU has transmitted with Enhanced Privacy enabled -- this might change + // and is not exact science! + bool encrypted = (fid & 0x10U) == 0x10U; + + m_slot->m_rfLC = lc; + + // The standby LC data + m_slot->m_voice->m_rfEmbeddedLC.setLC(*m_slot->m_rfLC); + m_slot->m_voice->m_rfEmbeddedData[0U].setLC(*m_slot->m_rfLC); + m_slot->m_voice->m_rfEmbeddedData[1U].setLC(*m_slot->m_rfLC); + + // Regenerate the LC data + fullLC.encode(*m_slot->m_rfLC, data + 2U, DT_VOICE_LC_HEADER); + + // Regenerate the Slot Type + slotType.encode(data + 2U); + + // Convert the Data Sync to be from the BS or MS as needed + Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + m_slot->m_rfTimeoutTimer.start(); + m_slot->m_rfTimeout = false; + + m_slot->m_rfFrames = 0U; + m_slot->m_rfSeqNo = 0U; + m_slot->m_rfBits = 1U; + m_slot->m_rfErrs = 0U; + + m_slot->m_voice->m_rfEmbeddedReadN = 0U; + m_slot->m_voice->m_rfEmbeddedWriteN = 1U; + m_slot->m_voice->m_rfTalkerId = TALKER_ID_NONE; + + m_slot->m_minRSSI = m_slot->m_rssi; + m_slot->m_maxRSSI = m_slot->m_rssi; + m_slot->m_aveRSSI = m_slot->m_rssi; + m_slot->m_rssiCount = 1U; + + if (m_slot->m_duplex) { + m_slot->m_queue.clear(); + m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); + + for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) + m_slot->writeQueueRF(data); + } + + m_slot->writeNetworkRF(data, DT_VOICE_LC_HEADER); + + m_slot->m_rfState = RS_RF_AUDIO; + m_slot->m_rfLastDstId = dstId; + + if (m_slot->m_netState == RS_NET_IDLE) { + m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, true); + } + + if (m_debug) { + Utils::dump(2U, "!!! *TX DMR Frame - DT_VOICE_LC_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES); + } + + ::ActivityLog("DMR", true, "Slot %u RF %svoice header from %u to %s%u", m_slot->m_slotNo, encrypted ? "encrypted " : "", srcId, flco == FLCO_GROUP ? "TG " : "", dstId); + return true; + } + else if (dataType == DT_VOICE_PI_HEADER) { + if (m_slot->m_rfState != RS_RF_AUDIO) + return false; + + // Regenerate the Slot Type + slotType.encode(data + 2U); + + // Convert the Data Sync to be from the BS or MS as needed + Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + + // Regenerate the payload and the BPTC (196,96) FEC + edac::BPTC19696 bptc; + uint8_t payload[12U]; + bptc.decode(data + 2U, payload); + bptc.encode(payload, data + 2U); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + if (m_slot->m_duplex) + m_slot->writeQueueRF(data); + + m_slot->writeNetworkRF(data, DT_VOICE_PI_HEADER); + + if (m_debug) { + Utils::dump(2U, "!!! *TX DMR Frame - DT_VOICE_PI_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES); + } + + return true; + } + + return false; + } + if (voiceSync) { if (m_slot->m_rfState == RS_RF_AUDIO) { m_lastRfN = 0U; @@ -79,7 +246,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) Sync::addDMRAudioSync(data + 2U, m_slot->m_duplex); uint32_t errors = 0U; - uint8_t fid = m_slot->m_data->m_rfLC->getFID(); + uint8_t fid = m_slot->m_rfLC->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) { errors = m_fec.regenerateDMR(data + 2U); if (m_verbose) { @@ -94,7 +261,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) m_slot->m_rfFrames++; m_slot->m_rfTGHang.start(); - m_slot->m_rfLastDstId = m_slot->m_data->m_rfLC->getDstId(); + m_slot->m_rfLastDstId = m_slot->m_rfLC->getDstId(); m_rfEmbeddedReadN = (m_rfEmbeddedReadN + 1U) % 2U; m_rfEmbeddedWriteN = (m_rfEmbeddedWriteN + 1U) % 2U; @@ -132,7 +299,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) m_lastRfN = m_rfN; uint32_t errors = 0U; - uint8_t fid = m_slot->m_data->m_rfLC->getFID(); + uint8_t fid = m_slot->m_rfLC->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) { errors = m_fec.regenerateDMR(data + 2U); if (m_verbose) { @@ -147,7 +314,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) m_slot->m_rfFrames++; m_slot->m_rfTGHang.start(); - m_slot->m_rfLastDstId = m_slot->m_data->m_rfLC->getDstId(); + m_slot->m_rfLastDstId = m_slot->m_rfLC->getDstId(); // Get the LCSS from the EMB data::EMB emb; @@ -177,7 +344,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) } if (m_verbose) { - logGPSPosition(m_slot->m_data->m_rfLC->getSrcId(), data); + logGPSPosition(m_slot->m_rfLC->getSrcId(), data); } break; @@ -301,12 +468,12 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) } } - m_slot->m_data->m_rfLC = lc; + m_slot->m_rfLC = lc; // The standby LC data - m_rfEmbeddedLC.setLC(*m_slot->m_data->m_rfLC); - m_rfEmbeddedData[0U].setLC(*m_slot->m_data->m_rfLC); - m_rfEmbeddedData[1U].setLC(*m_slot->m_data->m_rfLC); + m_rfEmbeddedLC.setLC(*m_slot->m_rfLC); + m_rfEmbeddedData[0U].setLC(*m_slot->m_rfLC); + m_rfEmbeddedData[1U].setLC(*m_slot->m_rfLC); // Create a dummy start frame to replace the received frame uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U]; @@ -314,7 +481,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) Sync::addDMRDataSync(start + 2U, m_slot->m_duplex); lc::FullLC fullLC; - fullLC.encode(*m_slot->m_data->m_rfLC, start + 2U, DT_VOICE_LC_HEADER); + fullLC.encode(*m_slot->m_rfLC, start + 2U, DT_VOICE_LC_HEADER); SlotType slotType; slotType.setColorCode(m_slot->m_colorCode); @@ -364,7 +531,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) // Send the original audio frame out uint32_t errors = 0U; - uint8_t fid = m_slot->m_data->m_rfLC->getFID(); + uint8_t fid = m_slot->m_rfLC->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) { errors = m_fec.regenerateDMR(data + 2U); if (m_verbose) { @@ -415,19 +582,196 @@ void VoicePacket::processNetwork(const data::Data& dmrData) uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; dmrData.getData(data + 2U); - if (dataType == DT_VOICE_SYNC) { + if (dataType == DT_VOICE_LC_HEADER) { + if (m_slot->m_netState == RS_NET_AUDIO) + return; + + lc::FullLC fullLC; + lc::LC * lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER); + if (lc == NULL) { + LogWarning(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, bad LC received from the network, replacing", m_slot->m_slotNo); + lc = new lc::LC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId()); + } + + uint32_t srcId = lc->getSrcId(); + uint32_t dstId = lc->getDstId(); + uint8_t flco = lc->getFLCO(); + + CHECK_TG_HANG_DELLC(dstId); + + if (dstId != dmrData.getDstId() || srcId != dmrData.getSrcId() || flco != dmrData.getFLCO()) + LogWarning(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, header doesn't match the DMR RF header: %u->%s%u %u->%s%u", m_slot->m_slotNo, + dmrData.getSrcId(), dmrData.getFLCO() == FLCO_GROUP ? "TG" : "", dmrData.getDstId(), + srcId, flco == FLCO_GROUP ? "TG" : "", dstId); + + if (m_verbose) { + LogMessage(LOG_NET, "DMR Slot %u, DT_VOICE_LC_HEADER, srcId = %u, dstId = %u, FLCO = $%02X, FID = $%02X, PF = %u", m_slot->m_slotNo, lc->getSrcId(), lc->getDstId(), lc->getFLCO(), lc->getFID(), lc->getPF()); + } + + m_slot->m_netLC = lc; + + // The standby LC data + m_slot->m_voice->m_netEmbeddedLC.setLC(*m_slot->m_netLC); + m_slot->m_voice->m_netEmbeddedData[0U].setLC(*m_slot->m_netLC); + m_slot->m_voice->m_netEmbeddedData[1U].setLC(*m_slot->m_netLC); + + // Regenerate the LC data + fullLC.encode(*m_slot->m_netLC, data + 2U, DT_VOICE_LC_HEADER); + + // Regenerate the Slot Type + SlotType slotType; + slotType.setColorCode(m_slot->m_colorCode); + slotType.setDataType(DT_VOICE_LC_HEADER); + slotType.encode(data + 2U); + + // Convert the Data Sync to be from the BS or MS as needed + Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + m_slot->m_voice->m_lastFrameValid = false; + + m_slot->m_netTimeoutTimer.start(); + m_slot->m_netTimeout = false; + + m_slot->m_netFrames = 0U; + m_slot->m_netLost = 0U; + m_slot->m_netBits = 1U; + m_slot->m_netErrs = 0U; + + m_slot->m_voice->m_netEmbeddedReadN = 0U; + m_slot->m_voice->m_netEmbeddedWriteN = 1U; + m_slot->m_voice->m_netTalkerId = TALKER_ID_NONE; + + if (m_slot->m_duplex) { + m_slot->m_queue.clear(); + m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); + } + + for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++) + m_slot->writeQueueNet(m_slot->m_idle); + + if (m_slot->m_duplex) { + for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) + m_slot->writeQueueNet(data); + } + else { + for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++) + m_slot->writeQueueNet(data); + } + + m_slot->m_netState = RS_NET_AUDIO; + m_slot->m_netLastDstId = dstId; + + m_slot->setShortLC(m_slot->m_slotNo, dstId, flco, true); + + if (m_debug) { + Utils::dump(2U, "!!! *TX DMR Network Frame - DT_VOICE_LC_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES); + } + + ::ActivityLog("DMR", false, "Slot %u network voice header from %u to %s%u", m_slot->m_slotNo, srcId, flco == FLCO_GROUP ? "TG " : "", dstId); + } + else if (dataType == DT_VOICE_PI_HEADER) { + if (m_slot->m_netState != RS_NET_AUDIO) { + lc::LC* lc = new lc::LC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId()); + + uint32_t srcId = lc->getSrcId(); + uint32_t dstId = lc->getDstId(); + + CHECK_TG_HANG_DELLC(dstId); + + m_slot->m_netLC = lc; + + m_slot->m_voice->m_lastFrameValid = false; + + m_slot->m_netTimeoutTimer.start(); + m_slot->m_netTimeout = false; + + if (m_slot->m_duplex) { + m_slot->m_queue.clear(); + m_slot->m_modem->writeDMRAbort(m_slot->m_slotNo); + } + + for (uint32_t i = 0U; i < m_slot->m_jitterSlots; i++) + m_slot->writeQueueNet(m_slot->m_idle); + + // Create a dummy start frame + uint8_t start[DMR_FRAME_LENGTH_BYTES + 2U]; + + Sync::addDMRDataSync(start + 2U, m_slot->m_duplex); + + lc::FullLC fullLC; + fullLC.encode(*m_slot->m_netLC, start + 2U, DT_VOICE_LC_HEADER); + + SlotType slotType; + slotType.setColorCode(m_slot->m_colorCode); + slotType.setDataType(DT_VOICE_LC_HEADER); + slotType.encode(start + 2U); + + start[0U] = TAG_DATA; + start[1U] = 0x00U; + + if (m_slot->m_duplex) { + for (uint32_t i = 0U; i < NO_HEADERS_DUPLEX; i++) + m_slot->writeQueueRF(start); + } + else { + for (uint32_t i = 0U; i < NO_HEADERS_SIMPLEX; i++) + m_slot->writeQueueRF(start); + } + + m_slot->m_netFrames = 0U; + m_slot->m_netLost = 0U; + m_slot->m_netBits = 1U; + m_slot->m_netErrs = 0U; + + m_slot->m_netState = RS_NET_AUDIO; + m_slot->m_netLastDstId = dstId; + + m_slot->setShortLC(m_slot->m_slotNo, dstId, m_slot->m_netLC->getFLCO(), true); + + ::ActivityLog("DMR", false, "Slot %u network late entry from %u to %s%u", + m_slot->m_slotNo, srcId, m_slot->m_netLC->getFLCO() == FLCO_GROUP ? "TG " : "", dstId); + } + + // Regenerate the Slot Type + SlotType slotType; + slotType.setColorCode(m_slot->m_colorCode); + slotType.setDataType(DT_VOICE_PI_HEADER); + slotType.encode(data + 2U); + + // Convert the Data Sync to be from the BS or MS as needed + Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); + + // Regenerate the payload and the BPTC (196,96) FEC + edac::BPTC19696 bptc; + uint8_t payload[12U]; + bptc.decode(data + 2U, payload); + bptc.encode(payload, data + 2U); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + m_slot->writeQueueNet(data); + + if (m_debug) { + Utils::dump(2U, "!!! *TX DMR Network Frame - DT_VOICE_PI_HEADER", data + 2U, DMR_FRAME_LENGTH_BYTES); + } + } + else if (dataType == DT_VOICE_SYNC) { if (m_slot->m_netState == RS_NET_IDLE) { lc::LC* lc = new lc::LC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId()); uint32_t dstId = lc->getDstId(); uint32_t srcId = lc->getSrcId(); - m_slot->m_data->m_netLC = lc; + m_slot->m_netLC = lc; // The standby LC data - m_netEmbeddedLC.setLC(*m_slot->m_data->m_netLC); - m_netEmbeddedData[0U].setLC(*m_slot->m_data->m_netLC); - m_netEmbeddedData[1U].setLC(*m_slot->m_data->m_netLC); + m_netEmbeddedLC.setLC(*m_slot->m_netLC); + m_netEmbeddedData[0U].setLC(*m_slot->m_netLC); + m_netEmbeddedData[1U].setLC(*m_slot->m_netLC); m_lastFrameValid = false; @@ -448,7 +792,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData) Sync::addDMRDataSync(start + 2U, m_slot->m_duplex); lc::FullLC fullLC; - fullLC.encode(*m_slot->m_data->m_netLC, start + 2U, DT_VOICE_LC_HEADER); + fullLC.encode(*m_slot->m_netLC, start + 2U, DT_VOICE_LC_HEADER); SlotType slotType; slotType.setColorCode(m_slot->m_colorCode); @@ -479,14 +823,14 @@ void VoicePacket::processNetwork(const data::Data& dmrData) m_slot->m_netState = RS_NET_AUDIO; m_slot->m_netLastDstId = dstId; - m_slot->setShortLC(m_slot->m_slotNo, dstId, m_slot->m_data->m_netLC->getFLCO(), true); + m_slot->setShortLC(m_slot->m_slotNo, dstId, m_slot->m_netLC->getFLCO(), true); ::ActivityLog("DMR", false, "Slot %u network late entry from %u to %s%u", - m_slot->m_slotNo, srcId, m_slot->m_data->m_netLC->getFLCO() == FLCO_GROUP ? "TG " : "", dstId); + m_slot->m_slotNo, srcId, m_slot->m_netLC->getFLCO() == FLCO_GROUP ? "TG " : "", dstId); } if (m_slot->m_netState == RS_NET_AUDIO) { - uint8_t fid = m_slot->m_data->m_netLC->getFID(); + uint8_t fid = m_slot->m_netLC->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) { m_slot->m_netErrs += m_fec.regenerateDMR(data + 2U); if (m_verbose) { @@ -531,7 +875,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData) if (m_slot->m_netState != RS_NET_AUDIO) return; - uint8_t fid = m_slot->m_data->m_netLC->getFID(); + uint8_t fid = m_slot->m_netLC->getFID(); if (fid == FID_ETSI || fid == FID_DMRA) { m_slot->m_netErrs += m_fec.regenerateDMR(data + 2U); if (m_verbose) { @@ -567,7 +911,7 @@ void VoicePacket::processNetwork(const data::Data& dmrData) Utils::dump(2U, text, data, 9U); } - logGPSPosition(m_slot->m_data->m_netLC->getSrcId(), data); + logGPSPosition(m_slot->m_netLC->getSrcId(), data); break; case FLCO_TALKER_ALIAS_HEADER: if (!(m_netTalkerId & TALKER_ID_HEADER)) { @@ -822,7 +1166,7 @@ void VoicePacket::insertSilence(uint32_t count) uint8_t n = (m_netN + 1U) % 6U; - uint8_t fid = m_slot->m_data->m_netLC->getFID(); + uint8_t fid = m_slot->m_netLC->getFID(); data::EMB emb; emb.setColorCode(m_slot->m_colorCode);