From dc29208e702dd22d86f28e0adbd5dafe90b3895d Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 8 Jul 2024 23:59:19 -0400 Subject: [PATCH] refactor DMR PDU processing to be more in-line with how P25 PDU processing is done; implement support on the FNE to inspect and dispatch DMR PDUs properly; --- src/common/dmr/DMRDefines.h | 8 +- src/common/dmr/data/DataBlock.cpp | 369 ++++++++++++++++++ src/common/dmr/data/DataBlock.h | 124 ++++++ src/common/dmr/data/DataHeader.cpp | 160 ++++++-- src/common/dmr/data/DataHeader.h | 63 ++- src/common/dmr/data/{Data.cpp => NetData.cpp} | 20 +- src/common/dmr/data/{Data.h => NetData.h} | 30 +- src/common/network/BaseNetwork.cpp | 4 +- src/common/network/BaseNetwork.h | 8 +- src/common/p25/data/DataBlock.cpp | 12 +- src/fne/HostFNE.h | 2 + src/fne/network/FNENetwork.h | 2 + src/fne/network/callhandler/TagDMRData.cpp | 27 +- src/fne/network/callhandler/TagDMRData.h | 22 +- src/fne/network/callhandler/TagP25Data.cpp | 2 +- src/fne/network/callhandler/TagP25Data.h | 1 + .../callhandler/packetdata/DMRPacketData.cpp | 327 ++++++++++++++++ .../callhandler/packetdata/DMRPacketData.h | 138 +++++++ .../callhandler/packetdata/P25PacketData.cpp | 29 +- .../callhandler/packetdata/P25PacketData.h | 5 +- src/host/dmr/Control.cpp | 2 +- src/host/dmr/Control.h | 2 +- src/host/dmr/Slot.cpp | 8 +- src/host/dmr/Slot.h | 4 +- src/host/dmr/packet/ControlSignaling.cpp | 28 +- src/host/dmr/packet/ControlSignaling.h | 6 +- src/host/dmr/packet/Data.cpp | 254 ++++++------ src/host/dmr/packet/Data.h | 13 +- src/host/dmr/packet/Voice.cpp | 2 +- src/host/dmr/packet/Voice.h | 6 +- 30 files changed, 1377 insertions(+), 301 deletions(-) create mode 100644 src/common/dmr/data/DataBlock.cpp create mode 100644 src/common/dmr/data/DataBlock.h rename src/common/dmr/data/{Data.cpp => NetData.cpp} (82%) rename src/common/dmr/data/{Data.h => NetData.h} (78%) create mode 100644 src/fne/network/callhandler/packetdata/DMRPacketData.cpp create mode 100644 src/fne/network/callhandler/packetdata/DMRPacketData.h diff --git a/src/common/dmr/DMRDefines.h b/src/common/dmr/DMRDefines.h index a19dccef..cca1cb4a 100644 --- a/src/common/dmr/DMRDefines.h +++ b/src/common/dmr/DMRDefines.h @@ -110,9 +110,11 @@ namespace dmr const uint32_t MAX_PDU_COUNT = 32U; - const uint32_t PDU_UNCONFIRMED_LENGTH_BYTES = 12U; - const uint32_t PDU_CONFIRMED_LENGTH_BYTES = 18U; - const uint32_t PDU_UNCODED_LENGTH_BYTES = 24U; + const uint32_t DMR_PDU_UNCONFIRMED_LENGTH_BYTES = 12U; + const uint32_t DMR_PDU_CONFIRMED_LENGTH_BYTES = 18U; + const uint32_t DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES = 16U; + const uint32_t DMR_PDU_CONFIRMED_HALFRATE_DATA_LENGTH_BYTES = 10U; + const uint32_t DMR_PDU_UNCODED_LENGTH_BYTES = 24U; const uint32_t MI_LENGTH_BYTES = 4U; // This was guessed based on OTA data captures -- the message indicator seems to be the same length as a source/destination address /** @} */ diff --git a/src/common/dmr/data/DataBlock.cpp b/src/common/dmr/data/DataBlock.cpp new file mode 100644 index 00000000..484dd31d --- /dev/null +++ b/src/common/dmr/data/DataBlock.cpp @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2018-2024 Bryan Biedenkapp, N2PLL + * + */ +#include "Defines.h" +#include "dmr/DMRDefines.h" +#include "dmr/data/DataBlock.h" +#include "edac/CRC.h" +#include "Log.h" +#include "Utils.h" + +using namespace dmr; +using namespace dmr::defines; +using namespace dmr::data; + +#include +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the DataBlock class. */ + +DataBlock::DataBlock() : + m_serialNo(0U), + m_lastBlock(false), + m_bptc(), + m_trellis(), + m_dataType(DataType::RATE_34_DATA), + m_DPF(DPF::CONFIRMED_DATA), + m_data(nullptr) +{ + m_data = new uint8_t[DMR_PDU_UNCODED_LENGTH_BYTES]; +} + +/* Finalizes a instance of the DataBlock class. */ + +DataBlock::~DataBlock() +{ + delete[] m_data; +} + +/* Decodes DMR PDU data block. */ + +bool DataBlock::decode(const uint8_t* data, const DataHeader& header) +{ + assert(data != nullptr); + assert(m_data != nullptr); + + uint8_t buffer[DMR_PDU_UNCODED_LENGTH_BYTES]; + ::memset(buffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); + + m_DPF = header.getDPF(); + + // set these to reasonable defaults + m_serialNo = 0U; + m_lastBlock = false; + + if (m_DPF == DPF::CONFIRMED_DATA) { + try { + if (m_dataType == DataType::RATE_34_DATA) { + bool valid = m_trellis.decode34(data, buffer, true); + if (!valid) { + LogError(LOG_DMR, "DataBlock::decode(), failed to decode Trellis 3/4 rate coding"); + return false; + } + } + else if (m_dataType == DataType::RATE_12_DATA) { + m_bptc.decode(data, buffer); + } + else { + LogError(LOG_DMR, "DataBlock::decode(), cowardly refusing to decode confirmed full-rate (rate 1) data"); + return false; + } + +#if DEBUG_DMR_PDU_DATA + Utils::dump(1U, "DMR, DataBlock::decode(), Confirmed PDU Data Block", buffer, DMR_PDU_UNCODED_LENGTH_BYTES); +#endif + + m_serialNo = (buffer[0] & 0xFEU) >> 1; // Confirmed Data Serial No. + uint16_t crc = ((buffer[0] & 0x01U) << 8) + buffer[1]; // CRC-9 Check Sum + + ::memset(m_data, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); + if (m_dataType == DataType::RATE_34_DATA) + ::memcpy(m_data, buffer + 2U, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data + else if (m_dataType == DataType::RATE_12_DATA) + ::memcpy(m_data, buffer + 2U, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); // Payload Data + else { + LogError(LOG_DMR, "DataBlock::decode(), failed to decode block, invalid dataType = $%02X", m_dataType); + return false; + } + + uint8_t crcBuffer[DMR_PDU_UNCODED_LENGTH_BYTES]; + ::memset(crcBuffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); + + // generate CRC buffer + uint32_t bufferLen = DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES; + if (m_dataType == DataType::RATE_12_DATA) + bufferLen = DMR_PDU_UNCONFIRMED_LENGTH_BYTES; + uint32_t crcBitLength = 144U; + if (m_dataType == DataType::RATE_12_DATA) + crcBitLength = 96U; + + for (uint32_t i = 16U; i < crcBitLength; i++) { + bool b = READ_BIT(buffer, i); + WRITE_BIT(crcBuffer, i - 16U, b); + } + + for (uint32_t i = 0U; i < 6U; i++) { + bool b = READ_BIT(buffer, i); + WRITE_BIT(crcBuffer, i + (crcBitLength - 16U), b); + } + + // compute CRC-9 for the packet + uint16_t calculated = edac::CRC::createCRC9(crcBuffer, crcBitLength - 9U); + calculated = ~calculated & 0x1FFU; + + if ((crc ^ calculated) != 0) { + LogWarning(LOG_DMR, "DMR, dataType = $%02X, invalid crc = $%04X != $%04X (computed)", m_dataType, crc, calculated); + } + +#if DEBUG_DMR_PDU_DATA + LogDebug(LOG_DMR, "DMR, dataType = $%02X, crc = $%04X, calculated = $%04X", m_dataType, crc, calculated); +#endif + } + catch (...) { + Utils::dump(2U, "DMR, decoding excepted with input data", data, DMR_PDU_UNCODED_LENGTH_BYTES); + return false; + } + } + else if (m_DPF == DPF::UNCONFIRMED_DATA || m_DPF == DPF::RESPONSE || m_DPF == DPF::DEFINED_RAW || + m_DPF == DPF::DEFINED_SHORT || m_DPF == DPF::UDT) { + try { + if (m_dataType == DataType::RATE_34_DATA) { + bool valid = m_trellis.decode34(data, buffer, true); + if (!valid) { + LogError(LOG_DMR, "DataBlock::decode(), failed to decode Trellis 3/4 rate coding"); + return false; + } + } + else if (m_dataType == DataType::RATE_12_DATA) { + m_bptc.decode(data, buffer); + } + else { + ::memcpy(buffer, data, DMR_PDU_UNCODED_LENGTH_BYTES); + return true; // never do any further processing for uncoded + } + + ::memset(m_data, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); + if (m_dataType == DataType::RATE_34_DATA) + ::memcpy(m_data, buffer, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data + else if (m_dataType == DataType::RATE_12_DATA) + ::memcpy(m_data, buffer, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); // Payload Data + else { + LogError(LOG_DMR, "DataBlock::decode(), failed to decode block, invalid dataType = $%02X", m_dataType); + return false; + } + } + catch (...) { + Utils::dump(2U, "DMR, decoding excepted with input data", data, DMR_PDU_UNCODED_LENGTH_BYTES); + return false; + } + } + else { + LogError(LOG_P25, "unknown DPF value in PDU, dpf = $%02X", m_DPF); + } + + return true; +} + +/* Encodes a DMR PDU data block. */ + +void DataBlock::encode(uint8_t* data) +{ + assert(data != nullptr); + assert(m_data != nullptr); + + if (m_DPF == DPF::CONFIRMED_DATA) { + if (m_dataType == DataType::RATE_34_DATA) { + uint8_t buffer[DMR_PDU_CONFIRMED_LENGTH_BYTES]; + ::memset(buffer, 0x00U, DMR_PDU_CONFIRMED_LENGTH_BYTES); + + buffer[0U] = ((m_serialNo << 1) & 0xFEU); // Confirmed Data Serial No. + + ::memcpy(buffer + 2U, m_data, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data + + uint8_t crcBuffer[DMR_PDU_UNCODED_LENGTH_BYTES]; + ::memset(crcBuffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); + + // generate CRC buffer + uint32_t bufferLen = DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES; + uint32_t crcBitLength = 144U; + + for (uint32_t i = 2U; i < bufferLen; i++) + crcBuffer[i - 2U] = buffer[2U]; + for (uint32_t i = 0; i < 6U; i++) { + bool b = READ_BIT(buffer, i); + WRITE_BIT(crcBuffer, i + (crcBitLength - 15U), b); + } + + uint16_t crc = edac::CRC::createCRC9(crcBuffer, 135U); + buffer[0U] = buffer[0U] + ((crc >> 8) & 0x01U); // CRC-9 Check Sum (b8) + buffer[1U] = (crc & 0xFFU); // CRC-9 Check Sum (b0 - b7) + +#if DEBUG_DMR_PDU_DATA + Utils::dump(1U, "DMR, DataBlock::encode(), Confirmed PDU Data Block", buffer, DMR_PDU_CONFIRMED_LENGTH_BYTES); +#endif + + m_trellis.encode34(buffer, data, true); + } + else if (m_dataType == DataType::RATE_12_DATA) { + uint8_t buffer[DMR_PDU_UNCONFIRMED_LENGTH_BYTES]; + ::memset(buffer, 0x00U, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + + buffer[0U] = ((m_serialNo << 1) & 0xFEU); // Confirmed Data Serial No. + + ::memcpy(buffer + 2U, m_data, DMR_PDU_CONFIRMED_HALFRATE_DATA_LENGTH_BYTES); // Payload Data + + uint8_t crcBuffer[DMR_PDU_UNCODED_LENGTH_BYTES]; + ::memset(crcBuffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); + + // generate CRC buffer + uint32_t bufferLen = DMR_PDU_CONFIRMED_HALFRATE_DATA_LENGTH_BYTES; + uint32_t crcBitLength = 96U; + + for (uint32_t i = 2U; i < bufferLen; i++) + crcBuffer[i - 2U] = buffer[2U]; + for (uint32_t i = 0; i < 6U; i++) { + bool b = READ_BIT(buffer, i); + WRITE_BIT(crcBuffer, i + (crcBitLength - 15U), b); + } + + uint16_t crc = edac::CRC::createCRC9(crcBuffer, 87U); + buffer[0U] = buffer[0U] + ((crc >> 8) & 0x01U); // CRC-9 Check Sum (b8) + buffer[1U] = (crc & 0xFFU); // CRC-9 Check Sum (b0 - b7) + + ::memcpy(buffer, m_data, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + +#if DEBUG_DMR_PDU_DATA + Utils::dump(1U, "DMR, DataBlock::encode(), Unconfirmed PDU Data Block", buffer, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); +#endif + + m_bptc.encode(buffer, data); + } + else { + LogError(LOG_DMR, "DataBlock::encode(), cowardly refusing to encode confirmed full-rate (rate 1) data"); + return; + } + } + else if (m_DPF == DPF::UNCONFIRMED_DATA || m_DPF == DPF::RESPONSE || m_DPF == DPF::DEFINED_RAW || + m_DPF == DPF::DEFINED_SHORT || m_DPF == DPF::UDT) { + if (m_dataType == DataType::RATE_34_DATA) { + uint8_t buffer[DMR_PDU_CONFIRMED_LENGTH_BYTES]; + ::memset(buffer, 0x00U, DMR_PDU_CONFIRMED_LENGTH_BYTES); + + ::memcpy(buffer, m_data, DMR_PDU_CONFIRMED_LENGTH_BYTES); + +#if DEBUG_DMR_PDU_DATA + Utils::dump(1U, "DMR, DataBlock::encode(), Unconfirmed PDU Data Block", buffer, DMR_PDU_CONFIRMED_LENGTH_BYTES); +#endif + + m_trellis.encode34(buffer, data, true); + } + else if (m_dataType == DataType::RATE_12_DATA) { + uint8_t buffer[DMR_PDU_UNCONFIRMED_LENGTH_BYTES]; + ::memset(buffer, 0x00U, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + + ::memcpy(buffer, m_data, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + +#if DEBUG_DMR_PDU_DATA + Utils::dump(1U, "DMR, DataBlock::encode(), Unconfirmed PDU Data Block", buffer, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); +#endif + + m_bptc.encode(buffer, data); + } + else { + uint8_t buffer[DMR_PDU_UNCODED_LENGTH_BYTES]; + ::memset(buffer, 0x00U, DMR_PDU_UNCODED_LENGTH_BYTES); + + ::memcpy(buffer, m_data, DMR_PDU_UNCODED_LENGTH_BYTES); + + ::memcpy(data, buffer, DMR_PDU_UNCODED_LENGTH_BYTES); + } + } + else { + LogError(LOG_P25, "unknown DPF value in PDU, dpf = $%02X", m_DPF); + return; + } +} + +/* Sets the data type. */ + +void DataBlock::setDataType(const DataType::E& dataType) +{ + m_dataType = dataType; +} + +/* Gets the data type. */ + +DataType::E DataBlock::getDataType() const +{ + return m_dataType; +} + +/* Sets the data format. */ + +void DataBlock::setFormat(const DPF::E fmt) +{ + m_DPF = fmt; +} + +/* Sets the data format from the data header. */ + +void DataBlock::setFormat(const DataHeader& header) +{ + m_DPF = header.getDPF(); +} + +/* Gets the data format. */ + +DPF::E DataBlock::getFormat() const +{ + return m_DPF; +} + +/* Sets the raw data stored in the data block. */ + +void DataBlock::setData(const uint8_t* buffer) +{ + assert(buffer != nullptr); + assert(m_data != nullptr); + + if (m_dataType == DataType::RATE_34_DATA) + ::memcpy(m_data, buffer, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); + else if (m_dataType == DataType::RATE_12_DATA) + ::memcpy(m_data, buffer, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + else if (m_dataType == DataType::RATE_1_DATA) + ::memcpy(m_data, buffer, DMR_PDU_UNCODED_LENGTH_BYTES); + else + LogError(LOG_DMR, "unknown dataType value in PDU, dataType = $%02X", m_dataType); +} + +/* Gets the raw data stored in the data block. */ + +uint32_t DataBlock::getData(uint8_t* buffer) const +{ + assert(buffer != nullptr); + assert(m_data != nullptr); + + if (m_dataType == DataType::RATE_34_DATA) { + ::memcpy(buffer, m_data, DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); + return DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES; + } else if (m_dataType == DataType::RATE_12_DATA) { + ::memcpy(buffer, m_data, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + return DMR_PDU_UNCONFIRMED_LENGTH_BYTES; + } else if (m_dataType == DataType::RATE_1_DATA) { + ::memcpy(buffer, m_data, DMR_PDU_UNCODED_LENGTH_BYTES); + return DMR_PDU_UNCODED_LENGTH_BYTES; + } else { + LogError(LOG_DMR, "unknown dataType value in PDU, dataType = $%02X", m_dataType); + return 0U; + } +} diff --git a/src/common/dmr/data/DataBlock.h b/src/common/dmr/data/DataBlock.h new file mode 100644 index 00000000..5a1d5eef --- /dev/null +++ b/src/common/dmr/data/DataBlock.h @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Common Library + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file DataBlock.h + * @ingroup dmr_pdu + * @file DataBlock.cpp + * @ingroup dmr_pdu + */ +#if !defined(__DMR_DATA__DATA_BLOCK_H__) +#define __DMR_DATA__DATA_BLOCK_H__ + +#include "common/Defines.h" +#include "common/dmr/DMRDefines.h" +#include "common/dmr/data/DataHeader.h" +#include "common/edac/BPTC19696.h" +#include "common/edac/Trellis.h" + +#include + +namespace dmr +{ + namespace data + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Represents a data block for PDU DMR packets. + * @ingroup dmr_pdu + */ + class HOST_SW_API DataBlock { + public: + /** + * @brief Initializes a new instance of the DataBlock class. + */ + DataBlock(); + /** + * @brief Finalizes a instance of the DataBlock class. + */ + ~DataBlock(); + + /** + * @brief Decodes DMR PDU data block. + * @param[in] data Buffer containing a PDU data block to decode. + * @param header DMR PDU data header. + * @returns bool True, if PDU data block decoded, otherwise false. + */ + bool decode(const uint8_t* data, const DataHeader& header); + /** + * @brief Encodes a DMR PDU data block. + * @param[out] data Buffer to encode a PDU data block. + */ + void encode(uint8_t* data); + + /** + * @brief Sets the data format from the data header. + * @param dataType DMR data type. + */ + void setDataType(const defines::DataType::E& dataType); + /** + * @brief Gets the data type. + * @returns defines::DataType::E DMR data type. + */ + defines::DataType::E getDataType() const; + /** + * @brief Sets the data format. + * @param fmt PDU data foramt. + */ + void setFormat(const defines::DPF::E fmt); + /** + * @brief Sets the data format from the data header. + * @param header P25 PDU data header. + */ + void setFormat(const DataHeader& header); + /** + * @brief Gets the data format. + * @returns defines::DPF::E PDU data format. + */ + defines::DPF::E getFormat() const; + + /** + * @brief Sets the raw data stored in the data block. + * @param[in] buffer Buffer containing bytes to store in data block. + */ + void setData(const uint8_t* buffer); + /** + * @brief Gets the raw data stored in the data block. + * @param[out] buffer Buffer to copy bytes in data block to. + * @returns uint32_t Number of bytes copied. + */ + uint32_t getData(uint8_t* buffer) const; + + public: + /** + * @brief Sets the data block serial number. + */ + __PROPERTY(uint8_t, serialNo, SerialNo); + + /** + * @brief Flag indicating this is the last block in a sequence of block. + */ + __PROPERTY(bool, lastBlock, LastBlock); + + private: + edac::BPTC19696 m_bptc; + edac::Trellis m_trellis; + + defines::DataType::E m_dataType; + defines::DPF::E m_DPF; + + uint8_t* m_data; + }; + } // namespace data +} // namespace dmr + +#endif // __P25_DATA__DATA_BLOCK_H__ diff --git a/src/common/dmr/data/DataHeader.cpp b/src/common/dmr/data/DataHeader.cpp index 669ddcb4..abc44d48 100644 --- a/src/common/dmr/data/DataHeader.cpp +++ b/src/common/dmr/data/DataHeader.cpp @@ -12,7 +12,6 @@ #include "Defines.h" #include "dmr/DMRDefines.h" #include "dmr/data/DataHeader.h" -#include "edac/BPTC19696.h" #include "edac/CRC.h" #include "Utils.h" @@ -22,6 +21,7 @@ using namespace dmr::data; #include #include +#include // --------------------------------------------------------------------------- // Constants @@ -42,24 +42,26 @@ DataHeader::DataHeader() : m_sap(0U), m_fsn(0U), m_Ns(0U), - m_padCount(0U), + m_blocksToFollow(0U), + m_padLength(0U), m_F(false), m_S(false), m_dataFormat(0U), m_srcId(0U), m_dstId(0U), - m_blocks(0U), m_rspClass(PDUResponseClass::NACK), m_rspType(PDUResponseType::NACK_ILLEGAL), m_rspStatus(0U), m_srcPort(0U), m_dstPort(0U), + m_bptc(), m_data(nullptr), m_SF(false), m_PF(false), m_UDTO(0U) { m_data = new uint8_t[DMR_LC_HEADER_LENGTH_BYTES]; + ::memset(m_data, 0x00U, DMR_LC_HEADER_LENGTH_BYTES); } /* Finalizes a instance of the DataHeader class. */ @@ -79,13 +81,13 @@ DataHeader& DataHeader::operator=(const DataHeader& header) m_sap = header.m_sap; m_fsn = header.m_fsn; m_Ns = header.m_Ns; - m_padCount = header.m_padCount; + m_blocksToFollow = header.m_blocksToFollow; + m_padLength = header.m_padLength; m_F = header.m_F; m_S = header.m_S; m_dataFormat = header.m_dataFormat; m_srcId = header.m_srcId; m_dstId = header.m_dstId; - m_blocks = header.m_blocks; m_rspClass = header.m_rspClass; m_rspType = header.m_rspType; m_rspStatus = header.m_rspStatus; @@ -109,8 +111,7 @@ bool DataHeader::decode(const uint8_t* data) assert(data != nullptr); // decode BPTC (196,96) FEC - edac::BPTC19696 bptc; - bptc.decode(data, m_data); + m_bptc.decode(data, m_data); // make sure the CRC-CCITT 16 was actually included (the network tends to zero the CRC) if (m_data[10U] != 0x00U && m_data[11U] != 0x00U) { @@ -144,8 +145,8 @@ bool DataHeader::decode(const uint8_t* data) #endif m_sap = ((m_data[1U] & 0xF0U) >> 4); // Service Access Point m_dataFormat = (m_data[1U] & 0x0FU); // UDT Format - m_blocks = (m_data[8U] & 0x03U) + 1U; // Blocks To Follow - m_padCount = (m_data[8U] & 0xF8U) >> 3; // Pad Nibble + m_blocksToFollow = (m_data[8U] & 0x03U) + 1U; // Blocks To Follow + m_padLength = (m_data[8U] & 0xF8U) >> 3; // Pad Nibble m_SF = (m_data[9U] & 0x80U) == 0x80U; // Supplemental Flag m_PF = (m_data[9U] & 0x40U) == 0x40U; // Protect Flag m_UDTO = m_data[9U] & 0x3FU; // UDT Opcode @@ -156,9 +157,9 @@ bool DataHeader::decode(const uint8_t* data) Utils::dump(1U, "DMR, DataHeader::decode(), Unconfirmed Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES); #endif m_sap = ((m_data[1U] & 0xF0U) >> 4); // Service Access Point - m_padCount = (m_data[0U] & 0x10U) + (m_data[1U] & 0x0FU); // Octet Pad Count + m_padLength = (m_data[0U] & 0x10U) + (m_data[1U] & 0x0FU); // Octet Pad Count m_F = (m_data[8U] & 0x80U) == 0x80U; // Full Message Flag - m_blocks = m_data[8U] & 0x7FU; // Blocks To Follow + m_blocksToFollow = m_data[8U] & 0x7FU; // Blocks To Follow m_fsn = m_data[9U] & 0x0FU; // Fragment Sequence Number break; @@ -167,9 +168,9 @@ bool DataHeader::decode(const uint8_t* data) Utils::dump(1U, "DMR, DataHeader::decode(), Confirmed Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES); #endif m_sap = ((m_data[1U] & 0xF0U) >> 4); // Service Access Point - m_padCount = (m_data[0U] & 0x10U) + (m_data[1U] & 0x0FU); // Octet Pad Count + m_padLength = (m_data[0U] & 0x10U) + (m_data[1U] & 0x0FU); // Octet Pad Count m_F = (m_data[8U] & 0x80U) == 0x80U; // Full Message Flag - m_blocks = m_data[8U] & 0x7FU; // Blocks To Follow + m_blocksToFollow = m_data[8U] & 0x7FU; // Blocks To Follow m_S = (m_data[9U] & 0x80U) == 0x80U; // Synchronize Flag m_Ns = (m_data[9U] >> 4) & 0x07U; // Send Sequence Number m_fsn = m_data[9U] & 0x0FU; // Fragement Sequence Number @@ -180,7 +181,7 @@ bool DataHeader::decode(const uint8_t* data) Utils::dump(1U, "DMR, DataHeader::decode(), Response Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES); #endif m_sap = ((m_data[1U] & 0xF0U) >> 4); // Service Access Point - m_blocks = m_data[8U] & 0x7FU; // Blocks To Follow + m_blocksToFollow = m_data[8U] & 0x7FU; // Blocks To Follow m_rspClass = (m_data[9U] >> 6) & 0x03U; // Response Class m_rspType = (m_data[9U] >> 3) & 0x07U; // Response Type m_rspStatus = m_data[9U] & 0x07U; // Response Status @@ -191,11 +192,11 @@ bool DataHeader::decode(const uint8_t* data) Utils::dump(1U, "DMR, DataHeader::decode(), Defined Short Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES); #endif m_sap = ((m_data[1U] & 0xF0U) >> 4); // Service Access Point - m_blocks = (m_data[0U] & 0x30U) + (m_data[1U] & 0x0FU); // Blocks To Follow + m_blocksToFollow = (m_data[0U] & 0x30U) + (m_data[1U] & 0x0FU); // Blocks To Follow m_F = (m_data[8U] & 0x01U) == 0x01U; // Full Message Flag m_S = (m_data[8U] & 0x02U) == 0x02U; // Synchronize Flag m_dataFormat = (m_data[8U] & 0xFCU) >> 2; // Defined Data Format - m_padCount = m_data[9U]; // Bit Padding + m_padLength = m_data[9U]; // Bit Padding break; case DPF::DEFINED_RAW: @@ -203,7 +204,7 @@ bool DataHeader::decode(const uint8_t* data) Utils::dump(1U, "DMR, DataHeader::decode(), Raw Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES); #endif m_sap = ((m_data[1U] & 0xF0U) >> 4); // Service Access Point - m_blocks = (m_data[0U] & 0x30U) + (m_data[1U] & 0x0FU); // Blocks To Follow + m_blocksToFollow = (m_data[0U] & 0x30U) + (m_data[1U] & 0x0FU); // Blocks To Follow m_F = (m_data[8U] & 0x01U) == 0x01U; // Full Message Flag m_S = (m_data[8U] & 0x02U) == 0x02U; // Synchronize Flag m_dstPort = (m_data[8U] & 0x1CU) >> 2; // Destination Port @@ -220,7 +221,7 @@ bool DataHeader::decode(const uint8_t* data) /* Encodes a DMR data header. */ -void DataHeader::encode(uint8_t* data) const +void DataHeader::encode(uint8_t* data) { assert(data != nullptr); @@ -262,8 +263,8 @@ void DataHeader::encode(uint8_t* data) const case DPF::UDT: m_data[1U] = ((m_sap & 0x0FU) << 4) + // Service Access Point (m_dataFormat & 0x0FU); // UDT Format - m_data[8U] = ((m_padCount & 0x1FU) << 3) + // Pad Nibble - (m_blocks - 1U); // Blocks To Follow + m_data[8U] = ((m_padLength & 0x1FU) << 3) + // Pad Nibble + (m_blocksToFollow - 1U); // Blocks To Follow m_data[9U] = (m_SF ? 0x80U : 0x00U) + // Supplemental Flag (m_PF ? 0x40U : 0x00U) + // Protect Flag (m_UDTO & 0x3F); // UDT Opcode @@ -273,11 +274,11 @@ void DataHeader::encode(uint8_t* data) const break; case DPF::UNCONFIRMED_DATA: - m_data[0U] = m_data[0U] + (m_padCount & 0x10U); // Octet Pad Count MSB + m_data[0U] = m_data[0U] + (m_padLength & 0x10U); // Octet Pad Count MSB m_data[1U] = ((m_sap & 0x0FU) << 4) + // Service Access Point - (m_padCount & 0x0FU); // Octet Pad Count LSB + (m_padLength & 0x0FU); // Octet Pad Count LSB m_data[8U] = (m_F ? 0x80U : 0x00U) + // Full Message Flag - (m_blocks & 0x7FU); // Blocks To Follow + (m_blocksToFollow & 0x7FU); // Blocks To Follow m_data[9U] = m_fsn; // Fragment Sequence Number #if DEBUG_DMR_PDU_DATA Utils::dump(1U, "DMR, DataHeader::decode(), Unconfirmed Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES); @@ -285,11 +286,11 @@ void DataHeader::encode(uint8_t* data) const break; case DPF::CONFIRMED_DATA: - m_data[0U] = m_data[0U] + (m_padCount & 0x10U); // Octet Pad Count MSB + m_data[0U] = m_data[0U] + (m_padLength & 0x10U); // Octet Pad Count MSB m_data[1U] = ((m_sap & 0x0FU) << 4) + // Service Access Point - (m_padCount & 0x0FU); // Octet Pad Count LSB + (m_padLength & 0x0FU); // Octet Pad Count LSB m_data[8U] = (m_F ? 0x80U : 0x00U) + // Full Message Flag - (m_blocks & 0x7FU); // Blocks To Follow + (m_blocksToFollow & 0x7FU); // Blocks To Follow m_data[9U] = (m_S ? 0x80U : 0x00U) + // Synchronize Flag ((m_Ns & 0x07U) << 4) + // Send Sequence Number (m_fsn & 0x0FU); // Fragment Sequence Number @@ -300,7 +301,7 @@ void DataHeader::encode(uint8_t* data) const case DPF::RESPONSE: m_data[1U] = ((m_sap & 0x0FU) << 4); // Service Access Point - m_data[8U] = m_blocks & 0x7FU; // Blocks To Follow + m_data[8U] = m_blocksToFollow & 0x7FU; // Blocks To Follow m_data[9U] = ((m_rspClass & 0x03U) << 6) + // Response Class ((m_rspType & 0x07U) << 3) + // Response Type ((m_rspStatus & 0x07U)); // Response Status @@ -310,22 +311,22 @@ void DataHeader::encode(uint8_t* data) const break; case DPF::DEFINED_SHORT: - m_data[0U] = m_data[0U] + (m_blocks & 0x30U); // Blocks To Follow MSB + m_data[0U] = m_data[0U] + (m_blocksToFollow & 0x30U); // Blocks To Follow MSB m_data[1U] = ((m_sap & 0x0FU) << 4) + // Service Access Point - (m_blocks & 0x0FU); // Blocks To Follow LSB + (m_blocksToFollow & 0x0FU); // Blocks To Follow LSB m_data[8U] = (m_F ? 0x01U : 0x00U) + // Full Message Flag (m_S ? 0x02U : 0x00U) + // Synchronize Flag ((m_dataFormat & 0xFCU) << 2); // Defined Data Format - m_data[9U] = m_padCount; // Bit Padding + m_data[9U] = m_padLength; // Bit Padding #if DEBUG_DMR_PDU_DATA Utils::dump(1U, "DMR, DataHeader::decode(), Defined Short Data Header", m_data, DMR_LC_HEADER_LENGTH_BYTES); #endif break; case DPF::DEFINED_RAW: - m_data[0U] = m_data[0U] + (m_blocks & 0x30U); // Blocks To Follow MSB + m_data[0U] = m_data[0U] + (m_blocksToFollow & 0x30U); // Blocks To Follow MSB m_data[1U] = ((m_sap & 0x0FU) << 4) + // Service Access Point - (m_blocks & 0x0FU); // Blocks To Follow LSB + (m_blocksToFollow & 0x0FU); // Blocks To Follow LSB m_data[8U] = (m_F ? 0x01U : 0x00U) + // Full Message Flag (m_S ? 0x02U : 0x00U) + // Synchronize Flag ((m_dstPort & 0x07U) << 2) + // Destination Port @@ -362,6 +363,97 @@ void DataHeader::encode(uint8_t* data) const } // encode BPTC (196,96) FEC - edac::BPTC19696 bptc; - bptc.encode(m_data, data); + m_bptc.encode(m_data, data); +} + +/* Helper to reset data values to defaults. */ + +void DataHeader::reset() +{ + m_GI = false; + m_A = false; + + m_DPF = DPF::UDT; + + m_sap = 0U; + m_fsn = 0U; + m_Ns = 0U; + m_blocksToFollow = 0U; + m_padLength = 0U; + m_F = false; + m_S = false; + + m_dataFormat = 0U; + + m_srcId = 0U; + m_dstId = 0U; + + m_rspClass = PDUResponseClass::NACK; + m_rspType = PDUResponseType::NACK_ILLEGAL; + m_rspStatus = 0U; + + m_srcPort = 0U; + m_dstPort = 0U; + + ::memset(m_data, 0x00U, DMR_LC_HEADER_LENGTH_BYTES); + + m_SF = false; + m_PF = false; + m_UDTO = 0U; +} + +/* Gets the total length in bytes of enclosed packet data. */ + +uint32_t DataHeader::getPacketLength() const +{ + if (m_DPF == DPF::RESPONSE) { + return 0U; // responses have no packet length as they are header only + } + + if (m_DPF == DPF::CONFIRMED_DATA) { + return DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES * m_blocksToFollow - 4 - m_padLength; + } + else { + return DMR_PDU_UNCONFIRMED_LENGTH_BYTES * m_blocksToFollow - 4 - m_padLength; + } +} + +/* Gets the raw header data. */ + +uint32_t DataHeader::getData(uint8_t* buffer) const +{ + assert(buffer != nullptr); + assert(m_data != nullptr); + + ::memcpy(buffer, m_data, DMR_LC_HEADER_LENGTH_BYTES); + return DMR_LC_HEADER_LENGTH_BYTES; +} + +/* Helper to calculate the number of blocks to follow and padding length for a PDU. */ + +void DataHeader::calculateLength(uint32_t packetLength) +{ + uint32_t len = packetLength + 4U; // packet length + CRC32 + uint32_t blockLen = (m_DPF == DPF::CONFIRMED_DATA) ? DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES : DMR_PDU_UNCONFIRMED_LENGTH_BYTES; + + if (len > blockLen) { + m_padLength = blockLen - (len % blockLen); + m_blocksToFollow = (uint8_t)ceilf((float)len / (float)blockLen); + } else { + m_padLength = 0U; + m_blocksToFollow = 1U; + } +} + +/* Helper to determine the pad length for a given packet length. */ + +uint32_t DataHeader::calculatePadLength(DPF::E dpf, uint32_t packetLength) +{ + uint32_t len = packetLength + 4U; // packet length + CRC32 + if (dpf == DPF::CONFIRMED_DATA) { + return DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES - (len % DMR_PDU_CONFIRMED_DATA_LENGTH_BYTES); + } + else { + return DMR_PDU_UNCONFIRMED_LENGTH_BYTES - (len % DMR_PDU_UNCONFIRMED_LENGTH_BYTES); + } } diff --git a/src/common/dmr/data/DataHeader.h b/src/common/dmr/data/DataHeader.h index c193310a..b05f40c5 100644 --- a/src/common/dmr/data/DataHeader.h +++ b/src/common/dmr/data/DataHeader.h @@ -5,19 +5,24 @@ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright (C) 2015,2016,2017 Jonathan Naylor, G4KLX - * Copyright (C) 2021 Bryan Biedenkapp, N2PLL + * Copyright (C) 2021,2024 Bryan Biedenkapp, N2PLL * */ /** - * @file DataHeader.h + * @defgroup dmr_pdu Packet Data Unit + * @brief Implementation for the data handling of ETSI TS-102 packet data. * @ingroup dmr + * + * @file DataHeader.h + * @ingroup dmr_pdu * @file DataHeader.cpp - * @ingroup dmr + * @ingroup dmr_pdu */ #if !defined(__DMR_DATA__DATA_HEADER_H__) #define __DMR_DATA__DATA_HEADER_H__ #include "common/Defines.h" +#include "common/edac/BPTC19696.h" namespace dmr { @@ -28,8 +33,8 @@ namespace dmr // --------------------------------------------------------------------------- /** - * @brief Represents a DMR data header. - * @ingroup dmr + * @brief Represents the data header for PDU DMR packets. + * @ingroup dmr_pdu */ class HOST_SW_API DataHeader { public: @@ -58,7 +63,39 @@ namespace dmr * @brief Encodes a DMR data header. * @param[out] data Buffer to encode a DMR data header. */ - void encode(uint8_t* data) const; + void encode(uint8_t* data); + + /** + * @brief Helper to reset data values to defaults. + */ + void reset(); + + /** + * @brief Gets the total length in bytes of enclosed packet data. + * @returns uint32_t Total length of packet in bytes. + */ + uint32_t getPacketLength() const; + + /** + * @brief Gets the raw header data. + * @param[out] buffer Buffer to copy raw header data to. + * @returns uint32_t Length of data copied. + */ + uint32_t getData(uint8_t* buffer) const; + + /** + * @brief Helper to calculate the number of blocks to follow and padding length for a PDU. + * @param packetLength Length of PDU. + */ + void calculateLength(uint32_t packetLength); + + /** + * @brief Helper to determine the pad length for a given packet length. + * @param dpf PDU format type. + * @param packetLength Length of PDU. + * @returns uint32_t Number of pad bytes. + */ + static uint32_t calculatePadLength(defines::DPF::E dpf, uint32_t packetLength); public: /** @@ -88,11 +125,14 @@ namespace dmr */ __PROPERTY(uint8_t, Ns, Ns); + /** + * @brief Gets the number of data blocks following the header. + */ + __PROPERTY(uint32_t, blocksToFollow, BlocksToFollow); /** * @brief Count of block padding. */ - __PROPERTY(uint8_t, padCount, PadCount); - + __PROPERTY(uint8_t, padLength, PadLength); /** * @brief Full Message Flag. */ @@ -116,11 +156,6 @@ namespace dmr */ __PROPERTY(uint32_t, dstId, DstId); - /** - * @brief Gets the number of data blocks following the header. - */ - __PROPERTY(uint32_t, blocks, Blocks); - /** * @brief Response class. */ @@ -144,6 +179,8 @@ namespace dmr __PROPERTY(uint8_t, dstPort, DstPort); private: + edac::BPTC19696 m_bptc; + uint8_t* m_data; bool m_SF; bool m_PF; diff --git a/src/common/dmr/data/Data.cpp b/src/common/dmr/data/NetData.cpp similarity index 82% rename from src/common/dmr/data/Data.cpp rename to src/common/dmr/data/NetData.cpp index fa71e81c..6e07b65a 100644 --- a/src/common/dmr/data/Data.cpp +++ b/src/common/dmr/data/NetData.cpp @@ -10,7 +10,7 @@ */ #include "Defines.h" #include "dmr/DMRDefines.h" -#include "dmr/data/Data.h" +#include "dmr/data/NetData.h" using namespace dmr; using namespace dmr::defines; @@ -23,9 +23,9 @@ using namespace dmr::data; // Public Class Members // --------------------------------------------------------------------------- -/* Initializes a new instance of the Data class. */ +/* Initializes a new instance of the NetData class. */ -Data::Data(const Data& data) : +NetData::NetData(const NetData& data) : m_slotNo(data.m_slotNo), m_srcId(data.m_srcId), m_dstId(data.m_dstId), @@ -41,9 +41,9 @@ Data::Data(const Data& data) : ::memcpy(m_data, data.m_data, 2U * DMR_FRAME_LENGTH_BYTES); } -/* Initializes a new instance of the Data class. */ +/* Initializes a new instance of the NetData class. */ -Data::Data() : +NetData::NetData() : m_slotNo(1U), m_srcId(0U), m_dstId(0U), @@ -58,16 +58,16 @@ Data::Data() : m_data = new uint8_t[2U * DMR_FRAME_LENGTH_BYTES]; } -/* Finalizes a instance of the Data class. */ +/* Finalizes a instance of the NetData class. */ -Data::~Data() +NetData::~NetData() { delete[] m_data; } /* Equals operator. */ -Data& Data::operator=(const Data& data) +NetData& NetData::operator=(const NetData& data) { if (this != &data) { ::memcpy(m_data, data.m_data, DMR_FRAME_LENGTH_BYTES); @@ -88,7 +88,7 @@ Data& Data::operator=(const Data& data) /* Sets raw data. */ -void Data::setData(const uint8_t* buffer) +void NetData::setData(const uint8_t* buffer) { assert(buffer != nullptr); @@ -97,7 +97,7 @@ void Data::setData(const uint8_t* buffer) /* Gets raw data. */ -uint32_t Data::getData(uint8_t* buffer) const +uint32_t NetData::getData(uint8_t* buffer) const { assert(buffer != nullptr); diff --git a/src/common/dmr/data/Data.h b/src/common/dmr/data/NetData.h similarity index 78% rename from src/common/dmr/data/Data.h rename to src/common/dmr/data/NetData.h index d42f4d7d..b089ca23 100644 --- a/src/common/dmr/data/Data.h +++ b/src/common/dmr/data/NetData.h @@ -9,13 +9,13 @@ * */ /** - * @file Data.h + * @file NetData.h * @ingroup dmr - * @file Data.cpp + * @file NetData.cpp * @ingroup dmr */ -#if !defined(__DMR_DATA__DATA_H__) -#define __DMR_DATA__DATA_H__ +#if !defined(__DMR_DATA__NET_DATA_H__) +#define __DMR_DATA__NET_DATA_H__ #include "common/Defines.h" #include "common/dmr/DMRDefines.h" @@ -32,27 +32,27 @@ namespace dmr * @brief Represents network DMR data. * @ingroup dmr */ - class HOST_SW_API Data { + class HOST_SW_API NetData { public: /** - * @brief Initializes a new instance of the Data class. - * @param data Instance of Data class to copy from. + * @brief Initializes a new instance of the NetData class. + * @param data Instance of NetData class to copy from. */ - Data(const Data& data); + NetData(const NetData& data); /** - * @brief Initializes a new instance of the Data class. + * @brief Initializes a new instance of the NetData class. */ - Data(); + NetData(); /** - * @brief Finalizes a instance of the Data class. + * @brief Finalizes a instance of the NetData class. */ - ~Data(); + ~NetData(); /** * @brief Equals operator. - * @param data Instance of Data class to copy from. + * @param data Instance of NetData class to copy from. */ - Data& operator=(const Data& data); + NetData& operator=(const NetData& data); /** * @brief Sets raw data. @@ -116,4 +116,4 @@ namespace dmr } // namespace data } // namespace dmr -#endif // __DMR_DATA__DATA_H__ +#endif // __DMR_DATA__NET_DATA_H__ diff --git a/src/common/network/BaseNetwork.cpp b/src/common/network/BaseNetwork.cpp index 6af398f9..c4d79791 100644 --- a/src/common/network/BaseNetwork.cpp +++ b/src/common/network/BaseNetwork.cpp @@ -369,7 +369,7 @@ UInt8Array BaseNetwork::readDMR(bool& ret, uint32_t& frameLength) /* Writes DMR frame data to the network. */ -bool BaseNetwork::writeDMR(const dmr::data::Data& data, bool noSequence) +bool BaseNetwork::writeDMR(const dmr::data::NetData& data, bool noSequence) { using namespace dmr::defines; if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING) @@ -679,7 +679,7 @@ uint16_t BaseNetwork::pktSeq(bool reset) /* Creates an DMR frame message. */ -UInt8Array BaseNetwork::createDMR_Message(uint32_t& length, const uint32_t streamId, const dmr::data::Data& data) +UInt8Array BaseNetwork::createDMR_Message(uint32_t& length, const uint32_t streamId, const dmr::data::NetData& data) { using namespace dmr::defines; uint8_t* buffer = new uint8_t[DMR_PACKET_LENGTH + PACKET_PAD]; diff --git a/src/common/network/BaseNetwork.h b/src/common/network/BaseNetwork.h index 04b8dfa6..95a83283 100644 --- a/src/common/network/BaseNetwork.h +++ b/src/common/network/BaseNetwork.h @@ -25,7 +25,7 @@ #define __BASE_NETWORK_H__ #include "common/Defines.h" -#include "common/dmr/data/Data.h" +#include "common/dmr/data/NetData.h" #include "common/p25/data/DataHeader.h" #include "common/p25/data/LowSpeedData.h" #include "common/p25/lc/LC.h" @@ -316,11 +316,11 @@ namespace network virtual UInt8Array readDMR(bool& ret, uint32_t& frameLength); /** * @brief Writes DMR frame data to the network. - * @param[in] data Instance of the dmr::data::Data class containing the DMR message. + * @param[in] data Instance of the dmr::data::NetData class containing the DMR message. * @param noSequence Flag indicating the message should be sent with no RTP sequence (65535). * @returns bool True, if message was sent, otherwise false. */ - virtual bool writeDMR(const dmr::data::Data& data, bool noSequence = false); + virtual bool writeDMR(const dmr::data::NetData& data, bool noSequence = false); /** * @brief Helper to test if the DMR ring buffer has data. @@ -513,7 +513,7 @@ namespace network * @param data Instance of the dmr::data::Data class containing the DMR message. * @returns UInt8Array Buffer containing the built network message. */ - UInt8Array createDMR_Message(uint32_t& length, const uint32_t streamId, const dmr::data::Data& data); + UInt8Array createDMR_Message(uint32_t& length, const uint32_t streamId, const dmr::data::NetData& data); /** * @brief Creates an P25 frame message header. diff --git a/src/common/p25/data/DataBlock.cpp b/src/common/p25/data/DataBlock.cpp index ea6c00e9..782fcd94 100644 --- a/src/common/p25/data/DataBlock.cpp +++ b/src/common/p25/data/DataBlock.cpp @@ -81,11 +81,11 @@ bool DataBlock::decode(const uint8_t* data, const DataHeader& header) ::memset(m_data, 0x00U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); ::memcpy(m_data, buffer + 2U, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data - uint8_t crcBuffer[18U]; - ::memset(crcBuffer, 0x00U, 18U); + uint8_t crcBuffer[P25_PDU_CONFIRMED_LENGTH_BYTES]; + ::memset(crcBuffer, 0x00U, P25_PDU_CONFIRMED_LENGTH_BYTES); // generate CRC buffer - for (uint32_t i = 0; i < 144U; i++) { + for (uint32_t i = 0U; i < 144U; i++) { bool b = READ_BIT(buffer, i); if (i < 7U) { WRITE_BIT(crcBuffer, i, b); @@ -153,11 +153,11 @@ void DataBlock::encode(uint8_t* data) ::memcpy(buffer + 2U, m_data, P25_PDU_CONFIRMED_DATA_LENGTH_BYTES); // Payload Data - uint8_t crcBuffer[18U]; - ::memset(crcBuffer, 0x00U, 18U); + uint8_t crcBuffer[P25_PDU_CONFIRMED_LENGTH_BYTES]; + ::memset(crcBuffer, 0x00U, P25_PDU_CONFIRMED_LENGTH_BYTES); // generate CRC buffer - for (uint32_t i = 0; i < 144U; i++) { + for (uint32_t i = 0U; i < 144U; i++) { bool b = READ_BIT(buffer, i); if (i < 7U) { WRITE_BIT(crcBuffer, i, b); diff --git a/src/fne/HostFNE.h b/src/fne/HostFNE.h index 033fd213..8598b849 100644 --- a/src/fne/HostFNE.h +++ b/src/fne/HostFNE.h @@ -36,6 +36,7 @@ // --------------------------------------------------------------------------- namespace network { namespace callhandler { class HOST_SW_API TagDMRData; } } +namespace network { namespace callhandler { namespace packetdata { class HOST_SW_API DMRPacketData; } } } namespace network { namespace callhandler { class HOST_SW_API TagP25Data; } } namespace network { namespace callhandler { namespace packetdata { class HOST_SW_API P25PacketData; } } } namespace network { namespace callhandler { class HOST_SW_API TagNXDNData; } } @@ -72,6 +73,7 @@ private: friend class network::FNENetwork; friend class network::callhandler::TagDMRData; + friend class network::callhandler::packetdata::DMRPacketData; friend class network::callhandler::TagP25Data; friend class network::callhandler::packetdata::P25PacketData; friend class network::callhandler::TagNXDNData; diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 0ea07066..c75bda80 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -48,6 +48,7 @@ class HOST_SW_API HostFNE; class HOST_SW_API RESTAPI; namespace network { namespace callhandler { class HOST_SW_API TagDMRData; } } +namespace network { namespace callhandler { namespace packetdata { class HOST_SW_API DMRPacketData; } } } namespace network { namespace callhandler { class HOST_SW_API TagP25Data; } } namespace network { namespace callhandler { namespace packetdata { class HOST_SW_API P25PacketData; } } } namespace network { namespace callhandler { class HOST_SW_API TagNXDNData; } } @@ -393,6 +394,7 @@ namespace network private: friend class DiagNetwork; friend class callhandler::TagDMRData; + friend class callhandler::packetdata::DMRPacketData; callhandler::TagDMRData* m_tagDMR; friend class callhandler::TagP25Data; friend class callhandler::packetdata::P25PacketData; diff --git a/src/fne/network/callhandler/TagDMRData.cpp b/src/fne/network/callhandler/TagDMRData.cpp index 53624435..4e2f5bf2 100644 --- a/src/fne/network/callhandler/TagDMRData.cpp +++ b/src/fne/network/callhandler/TagDMRData.cpp @@ -23,6 +23,7 @@ using namespace system_clock; using namespace network; using namespace network::callhandler; +using namespace network::callhandler::packetdata; using namespace dmr; using namespace dmr::defines; @@ -43,6 +44,8 @@ TagDMRData::TagDMRData(FNENetwork* network, bool debug) : m_debug(debug) { assert(network != nullptr); + + m_packetData = new DMRPacketData(network, this, debug); } /* Finalizes a instance of the TagDMRData class. */ @@ -70,7 +73,7 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId DataType::E dataType = (DataType::E)(data[15U] & 0x0FU); - data::Data dmrData; + data::NetData dmrData; dmrData.setSeqNo(seqNo); dmrData.setSlotNo(slotNo); dmrData.setSrcId(srcId); @@ -97,14 +100,18 @@ bool TagDMRData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId dmrData.setN(n); } - if (dataType == DataType::DATA_HEADER || - dataType == DataType::RATE_12_DATA || - dataType == DataType::RATE_34_DATA || - dataType == DataType::RATE_1_DATA) { + if (dataSync && ((dataType == DataType::DATA_HEADER) || + (dataType == DataType::RATE_12_DATA) || + (dataType == DataType::RATE_34_DATA) || + (dataType == DataType::RATE_1_DATA))) { if (m_network->m_disablePacketData) return false; + return m_packetData->processFrame(data, len, peerId, pktSeq, streamId, external); } + uint8_t frame[DMR_FRAME_LENGTH_BYTES]; + dmrData.getData(frame); + // perform TGID route rewrites if configured routeRewrite(buffer, peerId, dmrData, dataType, dstId, slotNo, false); dstId = __GET_UINT16(buffer, 8U); @@ -446,7 +453,7 @@ void TagDMRData::write_Call_Alrt(uint32_t peerId, uint8_t slot, uint32_t srcId, /* Helper to route rewrite the network data buffer. */ -void TagDMRData::routeRewrite(uint8_t* buffer, uint32_t peerId, dmr::data::Data& dmrData, DataType::E dataType, uint32_t dstId, uint32_t slotNo, bool outbound) +void TagDMRData::routeRewrite(uint8_t* buffer, uint32_t peerId, dmr::data::NetData& dmrData, DataType::E dataType, uint32_t dstId, uint32_t slotNo, bool outbound) { uint32_t rewriteDstId = dstId; uint32_t rewriteSlotNo = slotNo; @@ -537,7 +544,7 @@ bool TagDMRData::peerRewrite(uint32_t peerId, uint32_t& dstId, uint32_t& slotNo, /* Helper to process CSBKs being passed from a peer. */ -bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::Data& dmrData) +bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::NetData& dmrData) { // are we receiving a CSBK? if (dmrData.getDataType() == DataType::CSBK) { @@ -598,7 +605,7 @@ bool TagDMRData::processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::Data& /* Helper to determine if the peer is permitted for traffic. */ -bool TagDMRData::isPeerPermitted(uint32_t peerId, data::Data& data, uint32_t streamId, bool external) +bool TagDMRData::isPeerPermitted(uint32_t peerId, data::NetData& data, uint32_t streamId, bool external) { if (data.getFLCO() == FLCO::PRIVATE) { if (!m_network->checkU2UDroppedPeer(peerId)) @@ -682,7 +689,7 @@ bool TagDMRData::isPeerPermitted(uint32_t peerId, data::Data& data, uint32_t str /* Helper to validate the DMR call stream. */ -bool TagDMRData::validate(uint32_t peerId, data::Data& data, uint32_t streamId) +bool TagDMRData::validate(uint32_t peerId, data::NetData& data, uint32_t streamId) { // is the source ID a blacklisted ID? lookups::RadioId rid = m_network->m_ridLookup->find(data.getSrcId()); @@ -896,7 +903,7 @@ void TagDMRData::write_CSBK(uint32_t peerId, uint8_t slot, lc::CSBK* csbk) // Convert the Data Sync to be from the BS or MS as needed Sync::addDMRDataSync(data + 2U, true); - data::Data dmrData; + data::NetData dmrData; dmrData.setSlotNo(slot); dmrData.setDataType(DataType::CSBK); dmrData.setSrcId(csbk->getSrcId()); diff --git a/src/fne/network/callhandler/TagDMRData.h b/src/fne/network/callhandler/TagDMRData.h index 035b256c..cba156c7 100644 --- a/src/fne/network/callhandler/TagDMRData.h +++ b/src/fne/network/callhandler/TagDMRData.h @@ -18,10 +18,11 @@ #include "fne/Defines.h" #include "common/dmr/DMRDefines.h" -#include "common/dmr/data/Data.h" +#include "common/dmr/data/NetData.h" #include "common/dmr/lc/CSBK.h" #include "common/Clock.h" #include "network/FNENetwork.h" +#include "network/callhandler/packetdata/DMRPacketData.h" #include @@ -140,19 +141,22 @@ namespace network typedef std::pair StatusMapPair; std::unordered_map m_status; + friend class packetdata::DMRPacketData; + packetdata::DMRPacketData *m_packetData; + bool m_debug; /** * @brief Helper to route rewrite the network data buffer. * @param buffer Frame buffer. * @param peerId Peer ID. - * @param dmrData Instance of data::Data DMR data container class. + * @param dmrData Instance of data::NetData DMR data container class. * @param dataType DMR Data Type. * @param dstId Destination ID. * @param slotNo DMR slot number. * @param outbound Flag indicating whether or not this is outbound traffic. */ - void routeRewrite(uint8_t* buffer, uint32_t peerId, dmr::data::Data& dmrData, DMRDEF::DataType::E dataType, uint32_t dstId, uint32_t slotNo, bool outbound = true); + void routeRewrite(uint8_t* buffer, uint32_t peerId, dmr::data::NetData& dmrData, DMRDEF::DataType::E dataType, uint32_t dstId, uint32_t slotNo, bool outbound = true); /** * @brief Helper to route rewrite destination ID and slot. * @param peerId Peer ID. @@ -167,28 +171,28 @@ namespace network * @brief Helper to process CSBKs being passed from a peer. * @param buffer Frame buffer. * @param peerId Peer ID. - * @param dmrData Instance of data::Data DMR data container class. + * @param dmrData Instance of data::NetData DMR data container class. * @returns bool True, if allowed to pass, otherwise false. */ - bool processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::Data& dmrData); + bool processCSBK(uint8_t* buffer, uint32_t peerId, dmr::data::NetData& dmrData); /** * @brief Helper to determine if the peer is permitted for traffic. * @param peerId Peer ID. - * @param dmrData Instance of data::Data DMR data container class. + * @param dmrData Instance of data::NetData DMR data container class. * @param streamId Stream ID. * @param external Flag indicating this traffic came from an external peer. * @returns bool True, if valid, otherwise false. */ - bool isPeerPermitted(uint32_t peerId, dmr::data::Data& data, uint32_t streamId, bool external = false); + bool isPeerPermitted(uint32_t peerId, dmr::data::NetData& data, uint32_t streamId, bool external = false); /** * @brief Helper to validate the DMR call stream. * @param peerId Peer ID. - * @param dmrData Instance of data::Data DMR data container class. + * @param dmrData Instance of data::NetData DMR data container class. * @param streamId Stream ID. * @returns bool True, if valid, otherwise false. */ - bool validate(uint32_t peerId, dmr::data::Data& data, uint32_t streamId); + bool validate(uint32_t peerId, dmr::data::NetData& data, uint32_t streamId); /** * @brief Helper to write a grant packet. diff --git a/src/fne/network/callhandler/TagP25Data.cpp b/src/fne/network/callhandler/TagP25Data.cpp index 1a7fd149..117743e5 100644 --- a/src/fne/network/callhandler/TagP25Data.cpp +++ b/src/fne/network/callhandler/TagP25Data.cpp @@ -51,7 +51,7 @@ TagP25Data::TagP25Data(FNENetwork* network, bool debug) : { assert(network != nullptr); - m_packetData = new P25PacketData(network, debug); + m_packetData = new P25PacketData(network, this, debug); } /* Finalizes a instance of the TagP25Data class. */ diff --git a/src/fne/network/callhandler/TagP25Data.h b/src/fne/network/callhandler/TagP25Data.h index b844a35c..00421335 100644 --- a/src/fne/network/callhandler/TagP25Data.h +++ b/src/fne/network/callhandler/TagP25Data.h @@ -161,6 +161,7 @@ namespace network typedef std::pair StatusMapPair; std::unordered_map m_status; + friend class packetdata::P25PacketData; packetdata::P25PacketData *m_packetData; bool m_debug; diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.cpp b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp new file mode 100644 index 00000000..1b407e37 --- /dev/null +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.cpp @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Converged FNE Software + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * + */ +#include "fne/Defines.h" +#include "common/dmr/data/DataBlock.h" +#include "common/dmr/SlotType.h" +#include "common/dmr/Sync.h" +#include "common/edac/CRC.h" +#include "common/Clock.h" +#include "common/Log.h" +#include "common/Thread.h" +#include "common/Utils.h" +#include "network/FNENetwork.h" +#include "network/callhandler/packetdata/DMRPacketData.h" +#include "HostFNE.h" + +using namespace system_clock; +using namespace network; +using namespace network::callhandler; +using namespace network::callhandler::packetdata; +using namespace dmr; +using namespace dmr::defines; + +#include +#include + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +const uint8_t DATA_CALL_COLL_TIMEOUT = 60U; + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/* Initializes a new instance of the DMRPacketData class. */ + +DMRPacketData::DMRPacketData(FNENetwork* network, TagDMRData* tag, bool debug) : + m_network(network), + m_tag(tag), + m_status(), + m_debug(debug) +{ + assert(network != nullptr); + assert(tag != nullptr); +} + +/* Finalizes a instance of the DMRPacketData class. */ + +DMRPacketData::~DMRPacketData() = default; + +/* Process a data frame from the network. */ + +bool DMRPacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool external) +{ + hrc::hrc_t pktTime = hrc::now(); + + uint8_t seqNo = data[4U]; + + uint32_t srcId = __GET_UINT16(data, 5U); + uint32_t dstId = __GET_UINT16(data, 8U); + + FLCO::E flco = (data[15U] & 0x40U) == 0x40U ? FLCO::PRIVATE : FLCO::GROUP; + + uint32_t slotNo = (data[15U] & 0x80U) == 0x80U ? 2U : 1U; + + DataType::E dataType = (DataType::E)(data[15U] & 0x0FU); + + data::NetData dmrData; + dmrData.setSeqNo(seqNo); + dmrData.setSlotNo(slotNo); + dmrData.setSrcId(srcId); + dmrData.setDstId(dstId); + dmrData.setFLCO(flco); + + bool dataSync = (data[15U] & 0x20U) == 0x20U; + bool voiceSync = (data[15U] & 0x10U) == 0x10U; + + if (dataSync) { + dmrData.setData(data + 20U); + dmrData.setDataType(dataType); + dmrData.setN(0U); + } + + uint8_t frame[DMR_FRAME_LENGTH_BYTES]; + dmrData.getData(frame); + + // is the stream valid? + if (m_tag->validate(peerId, dmrData, streamId)) { + // is this peer ignored? + if (!m_tag->isPeerPermitted(peerId, dmrData, streamId)) { + return false; + } + + auto it = std::find_if(m_status.begin(), m_status.end(), [&](StatusMapPair x) { return x.second->peerId == peerId; }); + if (it != m_status.end()) { + RxStatus* status = m_status[peerId]; + if (streamId != status->streamId) { + LogWarning(LOG_NET, "DMR, Data Call Collision, peer = %u, streamId = %u, rxPeer = %u, rxLlId = %u, rxSlotNo = %u, rxStreamId = %u, external = %u", + peerId, streamId, status->peerId, status->srcId, status->slotNo, status->streamId, external); + + uint64_t duration = hrc::diff(pktTime, status->callStartTime); + + if ((duration / 1000) > DATA_CALL_COLL_TIMEOUT) { + LogWarning(LOG_NET, "DMR, force clearing stuck data call, timeout, peer = %u, streamId = %u, rxPeer = %u, rxLlId = %u, rxStreamId = %u, external = %u", + peerId, streamId, status->peerId, status->srcId, status->slotNo, status->streamId, external); + + delete status; + m_status.erase(peerId); + } + + return false; + } + } else { + if (dataSync && (dataType == DataType::DATA_HEADER)) { + // this is a new call stream + RxStatus* status = new RxStatus(); + status->callStartTime = pktTime; + status->srcId = srcId; + status->dstId = dstId; + status->slotNo = slotNo; + status->streamId = streamId; + status->peerId = peerId; + + bool ret = status->header.decode(frame); + if (!ret) { + LogError(LOG_NET, "DMR Slot %u, DataType::DATA_HEADER, unable to decode the network data header", status->slotNo); + Utils::dump(1U, "Unfixable PDU Data", frame, DMR_FRAME_LENGTH_BYTES); + + delete status; + m_status.erase(peerId); + return false; + } + + status->frames = status->header.getBlocksToFollow(); + status->dataBlockCnt = 0U; + + bool gi = status->header.getGI(); + uint32_t srcId = status->header.getSrcId(); + uint32_t dstId = status->header.getDstId(); + + LogMessage(LOG_NET, DMR_DT_DATA_HEADER ", peerId = %u, slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", + peerId, status->slotNo, status->header.getDPF(), status->header.getA(), status->header.getSAP(), status->header.getFullMesage(), status->header.getBlocksToFollow(), status->header.getPadLength(), status->header.getPacketLength(), + status->header.getFSN(), dstId, srcId, gi); + + // make sure we don't get a PDU with more blocks then we support + if (status->header.getBlocksToFollow() >= MAX_PDU_COUNT) { + LogError(LOG_NET, P25_PDU_STR ", too many PDU blocks to process, %u > %u", status->header.getBlocksToFollow(), MAX_PDU_COUNT); + return false; + } + + m_status[peerId] = status; + + LogMessage(LOG_NET, "DMR, Data Call Start, peer = %u, slot = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", peerId, status->slotNo, status->srcId, status->dstId, streamId, external); + dispatch(peerId, dmrData, data, len, seqNo, pktSeq, streamId); + + return true; + } else { + return false; + } + } + + RxStatus* status = m_status[peerId]; + + // a PDU header only with no blocks to follow is usually a response header + if (status->header.getBlocksToFollow() == 0U) { + LogMessage(LOG_NET, "DMR, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, streamId = %u, external = %u", + peerId, status->slotNo, status->srcId, status->dstId, streamId, external); + + delete status; + m_status.erase(peerId); + return true; + } + + data::DataBlock dataBlock; + dataBlock.setDataType(dataType); + + bool ret = dataBlock.decode(frame, status->header); + if (ret) { + uint32_t len = dataBlock.getData(status->pduUserData + status->pduDataOffset); + status->pduDataOffset += len; + + status->frames--; + if (status->frames == 0U) + dataBlock.setLastBlock(true); + + if (dataType == DataType::RATE_34_DATA) { + LogMessage(LOG_NET, DMR_DT_RATE_34_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); + } else if (dataType == DataType::RATE_12_DATA) { + LogMessage(LOG_NET, DMR_DT_RATE_12_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); + } + else { + LogMessage(LOG_NET, DMR_DT_RATE_1_DATA ", ISP, block %u, peer = %u, dataType = $%02X, dpf = $%02X", status->dataBlockCnt, peerId, dataBlock.getDataType(), dataBlock.getFormat()); + } + + dispatch(peerId, dmrData, data, len, seqNo, pktSeq, streamId); + status->dataBlockCnt++; + } + + // dispatch the PDU data + if (status->dataBlockCnt > 0U && status->frames == 0U) { + if (status->header.getBlocksToFollow() > 0U && status->frames == 0U) { + bool crcRet = edac::CRC::checkCRC32(status->pduUserData, status->pduDataOffset); + if (!crcRet) { + LogWarning(LOG_NET, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", status->header.getBlocksToFollow(), status->pduDataOffset); + } + + if (m_network->m_dumpDataPacket) { + Utils::dump(1U, "PDU Packet", status->pduUserData, status->pduDataOffset); + } + } + + uint64_t duration = hrc::diff(pktTime, status->callStartTime); + bool gi = status->header.getGI(); + uint32_t srcId = status->header.getSrcId(); + uint32_t dstId = status->header.getDstId(); + LogMessage(LOG_NET, "P25, Data Call End, peer = %u, slot = %u, srcId = %u, dstId = %u, blocks = %u, duration = %u, streamId = %u, external = %u", + peerId, srcId, dstId, status->header.getBlocksToFollow(), duration / 1000, streamId, external); + + // report call event to InfluxDB + if (m_network->m_enableInfluxDB) { + influxdb::QueryBuilder() + .meas("call_event") + .tag("peerId", std::to_string(peerId)) + .tag("mode", "DMR") + .tag("streamId", std::to_string(streamId)) + .tag("srcId", std::to_string(srcId)) + .tag("dstId", std::to_string(dstId)) + .field("duration", duration) + .field("slot", slotNo) + .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + .request(m_network->m_influxServer); + } + + delete status; + m_status.erase(peerId); + } + } + + return true; +} + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- + +/* Helper to dispatch PDU user data. */ + +void DMRPacketData::dispatch(uint32_t peerId, dmr::data::NetData& dmrData, const uint8_t* data, uint32_t len, uint8_t seqNo, uint16_t pktSeq, uint32_t streamId) +{ + RxStatus* status = m_status[peerId]; + + bool gi = status->header.getGI(); + uint32_t srcId = status->header.getSrcId(); + uint32_t dstId = status->header.getDstId(); + + // repeat traffic to the connected peers + if (m_network->m_peers.size() > 0U) { + uint32_t i = 0U; + for (auto peer : m_network->m_peers) { + if (peerId != peer.first) { + // is this peer ignored? + if (!m_tag->isPeerPermitted(peer.first, dmrData, status->streamId)) { + continue; + } + + // every 5 peers flush the queue + if (i % 5U == 0U) { + m_network->m_frameQueue->flushQueue(); + } + + m_network->writePeer(peer.first, { NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, data, len, pktSeq, streamId, true); + if (m_network->m_debug) { + LogDebug(LOG_NET, "DMR, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, slotNo = %u, len = %u, pktSeq = %u, stream = %u", + peerId, peer.first, seqNo, srcId, dstId, status->slotNo, len, pktSeq, streamId); + } + + if (!m_network->m_callInProgress) + m_network->m_callInProgress = true; + i++; + } + } + m_network->m_frameQueue->flushQueue(); + } + + // repeat traffic to external peers + if (m_network->m_host->m_peerNetworks.size() > 0U) { + for (auto peer : m_network->m_host->m_peerNetworks) { + uint32_t dstPeerId = peer.second->getPeerId(); + + // don't try to repeat traffic to the source peer...if this traffic + // is coming from a external peer + if (dstPeerId != peerId) { + // is this peer ignored? + if (!m_tag->isPeerPermitted(dstPeerId, dmrData, status->streamId, true)) { + continue; + } + + // check if the source peer is blocked from sending to this peer + if (peer.second->checkBlockedPeer(peerId)) { + continue; + } + + // skip peer if it isn't enabled + if (!peer.second->isEnabled()) { + continue; + } + + peer.second->writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_DMR }, data, len, pktSeq, streamId); + if (m_network->m_debug) { + LogDebug(LOG_NET, "DMR, srcPeer = %u, dstPeer = %u, seqNo = %u, srcId = %u, dstId = %u, slotNo = %u, len = %u, pktSeq = %u, stream = %u", + peerId, dstPeerId, seqNo, srcId, dstId, status->slotNo, len, pktSeq, streamId); + } + + if (!m_network->m_callInProgress) + m_network->m_callInProgress = true; + } + } + } +} diff --git a/src/fne/network/callhandler/packetdata/DMRPacketData.h b/src/fne/network/callhandler/packetdata/DMRPacketData.h new file mode 100644 index 00000000..2b7263d5 --- /dev/null +++ b/src/fne/network/callhandler/packetdata/DMRPacketData.h @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital Voice Modem - Converged FNE Software + * GPLv2 Open Source. Use is subject to license terms. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright (C) 2024 Bryan Biedenkapp, N2PLL + * + */ +/** + * @file DMRPacketData.h + * @ingroup fne_callhandler + * @file DMRPacketData.cpp + * @ingroup fne_callhandler + */ +#if !defined(__PACKETDATA__DMR_PACKET_DATA_H__) +#define __PACKETDATA__DMR_PACKET_DATA_H__ + +#include "fne/Defines.h" +#include "common/Clock.h" +#include "common/dmr/DMRDefines.h" +#include "common/dmr/data/DataHeader.h" +#include "network/FNENetwork.h" +#include "network/PeerNetwork.h" +#include "network/callhandler/TagDMRData.h" + +#include + +namespace network +{ + namespace callhandler + { + namespace packetdata + { + // --------------------------------------------------------------------------- + // Class Declaration + // --------------------------------------------------------------------------- + + /** + * @brief Implements the DMR packet data handler. + * @ingroup fne_callhandler + */ + class HOST_SW_API DMRPacketData { + public: + /** + * @brief Initializes a new instance of the DMRPacketData class. + * @param network Instance of the FNENetwork class. + * @param tag Instance of the TagDMRData class. + * @param debug Flag indicating whether network debug is enabled. + */ + DMRPacketData(FNENetwork* network, TagDMRData* tag, bool debug); + /** + * @brief Finalizes a instance of the P25PacketData class. + */ + ~DMRPacketData(); + + /** + * @brief Process a data frame from the network. + * @param data Network data buffer. + * @param len Length of data. + * @param peerId Peer ID. + * @param pktSeq RTP packet sequence. + * @param streamId Stream ID. + * @param external Flag indicating traffic is from an external peer. + * @returns bool True, if frame is processed, otherwise false. + */ + bool processFrame(const uint8_t* data, uint32_t len, uint32_t peerId, uint16_t pktSeq, uint32_t streamId, bool external = false); + + private: + FNENetwork* m_network; + TagDMRData *m_tag; + + /** + * @brief Represents the receive status of a call. + */ + class RxStatus { + public: + system_clock::hrc::hrc_t callStartTime; + uint32_t srcId; + uint32_t dstId; + uint8_t slotNo; + uint32_t streamId; + uint32_t peerId; + + dmr::data::DataHeader header; + uint8_t dataBlockCnt; + uint8_t frames; + + uint8_t* pduUserData; + uint32_t pduDataOffset; + + /** + * @brief Initializes a new instance of the RxStatus class + */ + RxStatus() : + srcId(0U), + dstId(0U), + slotNo(0U), + streamId(0U), + peerId(0U), + header(), + dataBlockCnt(0U), + pduUserData(nullptr), + pduDataOffset(0U) + { + pduUserData = new uint8_t[DMRDEF::MAX_PDU_COUNT * DMRDEF::DMR_PDU_UNCODED_LENGTH_BYTES + 2U]; + ::memset(pduUserData, 0x00U, DMRDEF::MAX_PDU_COUNT * DMRDEF::DMR_PDU_UNCODED_LENGTH_BYTES + 2U); + } + /** + * @brief Finalizes a instance of the RxStatus class + */ + ~RxStatus() + { + delete[] pduUserData; + } + }; + typedef std::pair StatusMapPair; + std::unordered_map m_status; + + bool m_debug; + + /** + * @brief Helper to dispatch PDU user data. + * @param peerId Peer ID. + * @param dmrData Instance of data::NetData DMR data container class. + * @param data Network data buffer. + * @param len Length of data. + * @param seqNo + * @param pktSeq RTP packet sequence. + * @param streamId Stream ID. + */ + void dispatch(uint32_t peerId, dmr::data::NetData& dmrData, const uint8_t* data, uint32_t len, uint8_t seqNo, uint16_t pktSeq, uint32_t streamId); + }; + } // namespace packetdata + } // namespace callhandler +} // namespace network + +#endif // __PACKETDATA__DMR_PACKET_DATA_H__ diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.cpp b/src/fne/network/callhandler/packetdata/P25PacketData.cpp index 42285442..b104e477 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.cpp +++ b/src/fne/network/callhandler/packetdata/P25PacketData.cpp @@ -8,7 +8,6 @@ * */ #include "fne/Defines.h" -#include "common/p25/lc/tsbk/TSBKFactory.h" #include "common/p25/Sync.h" #include "common/edac/CRC.h" #include "common/Clock.h" @@ -21,6 +20,7 @@ using namespace system_clock; using namespace network; +using namespace network::callhandler; using namespace network::callhandler::packetdata; using namespace p25; using namespace p25::defines; @@ -40,12 +40,14 @@ const uint8_t DATA_CALL_COLL_TIMEOUT = 60U; /* Initializes a new instance of the P25PacketData class. */ -P25PacketData::P25PacketData(FNENetwork* network, bool debug) : +P25PacketData::P25PacketData(FNENetwork* network, TagP25Data* tag, bool debug) : m_network(network), + m_tag(tag), m_status(), m_debug(debug) { assert(network != nullptr); + assert(tag != nullptr); } /* Finalizes a instance of the P25PacketData class. */ @@ -129,6 +131,29 @@ bool P25PacketData::processFrame(const uint8_t* data, uint32_t len, uint32_t pee RxStatus* status = m_status[peerId]; + // is the source ID a blacklisted ID? + lookups::RadioId rid = m_network->m_ridLookup->find(status->header.getLLId()); + if (!rid.radioDefault()) { + if (!rid.radioEnabled()) { + // report error event to InfluxDB + if (m_network->m_enableInfluxDB) { + influxdb::QueryBuilder() + .meas("call_error_event") + .tag("peerId", std::to_string(peerId)) + .tag("streamId", std::to_string(streamId)) + .tag("srcId", std::to_string(status->header.getLLId())) + .tag("dstId", std::to_string(status->header.getLLId())) + .field("message", INFLUXDB_ERRSTR_DISABLED_SRC_RID) + .timestamp(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + .request(m_network->m_influxServer); + } + + delete status; + m_status.erase(peerId); + return false; + } + } + // a PDU header only with no blocks to follow is usually a response header if (status->header.getBlocksToFollow() == 0U) { dispatch(peerId); diff --git a/src/fne/network/callhandler/packetdata/P25PacketData.h b/src/fne/network/callhandler/packetdata/P25PacketData.h index 207dd99b..fee1a368 100644 --- a/src/fne/network/callhandler/packetdata/P25PacketData.h +++ b/src/fne/network/callhandler/packetdata/P25PacketData.h @@ -23,6 +23,7 @@ #include "common/p25/data/DataHeader.h" #include "network/FNENetwork.h" #include "network/PeerNetwork.h" +#include "network/callhandler/TagP25Data.h" #include @@ -45,9 +46,10 @@ namespace network /** * @brief Initializes a new instance of the P25PacketData class. * @param network Instance of the FNENetwork class. + * @param tag Instance of the TagP25Data class. * @param debug Flag indicating whether network debug is enabled. */ - P25PacketData(FNENetwork* network, bool debug); + P25PacketData(FNENetwork* network, TagP25Data* tag, bool debug); /** * @brief Finalizes a instance of the P25PacketData class. */ @@ -67,6 +69,7 @@ namespace network private: FNENetwork* m_network; + TagP25Data *m_tag; /** * @brief Represents the receive status of a call. diff --git a/src/host/dmr/Control.cpp b/src/host/dmr/Control.cpp index ce3bdc58..c1533f70 100644 --- a/src/host/dmr/Control.cpp +++ b/src/host/dmr/Control.cpp @@ -630,7 +630,7 @@ void Control::processNetwork() return; } - data::Data data; + data::NetData data; // process network message header uint8_t seqNo = buffer[4U]; diff --git a/src/host/dmr/Control.h b/src/host/dmr/Control.h index fb358722..cfdc551d 100644 --- a/src/host/dmr/Control.h +++ b/src/host/dmr/Control.h @@ -26,7 +26,7 @@ #define __DMR_CONTROL_H__ #include "Defines.h" -#include "common/dmr/data/Data.h" +#include "common/dmr/data/NetData.h" #include "common/lookups/RSSIInterpolator.h" #include "common/lookups/IdenTableLookup.h" #include "common/lookups/RadioIdLookup.h" diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index 3ce59b19..ab74637a 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -107,11 +107,9 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz m_permittedDstId(0U), m_rfLC(nullptr), m_rfPrivacyLC(nullptr), - m_rfDataHeader(nullptr), m_rfSeqNo(0U), m_netLC(nullptr), m_netPrivacyLC(nullptr), - m_netDataHeader(nullptr), m_networkWatchdog(1000U, 0U, 1500U), m_rfTimeoutTimer(1000U, timeout), m_rfTGHang(1000U, tgHang), @@ -355,7 +353,7 @@ uint32_t Slot::getFrame(uint8_t* data) /* Process a data frame from the network. */ -void Slot::processNetwork(const data::Data& dmrData) +void Slot::processNetwork(const data::NetData& dmrData) { // don't process network frames if the RF modem isn't in a listening state if (m_rfState != RS_RF_LISTENING) { @@ -1183,7 +1181,7 @@ void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, FLCO::E flco, if (m_network == nullptr) return; - data::Data dmrData; + data::NetData dmrData; dmrData.setSlotNo(m_slotNo); dmrData.setDataType(dataType); dmrData.setSrcId(srcId); @@ -1251,7 +1249,6 @@ void Slot::writeEndRF(bool writeEnd) m_rfLC = nullptr; m_rfPrivacyLC = nullptr; - m_rfDataHeader = nullptr; } /* Helper to write network end of frame data. */ @@ -1309,7 +1306,6 @@ void Slot::writeEndNet(bool writeEnd) m_netLC = nullptr; m_netPrivacyLC = nullptr; - m_netDataHeader = nullptr; } /* Helper to write control channel packet data. */ diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index b1a64618..004b329b 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -148,7 +148,7 @@ namespace dmr * @brief Process a data frames from the network. * @param[in] data Instance of data::Data DMR data container class. */ - void processNetwork(const data::Data& data); + void processNetwork(const data::NetData& data); /** @} */ /** @name Data Clocking */ @@ -319,13 +319,11 @@ namespace dmr std::unique_ptr m_rfLC; std::unique_ptr m_rfPrivacyLC; - std::unique_ptr m_rfDataHeader; uint8_t m_rfSeqNo; std::unique_ptr m_netLC; std::unique_ptr m_netPrivacyLC; - std::unique_ptr m_netDataHeader; Timer m_networkWatchdog; Timer m_rfTimeoutTimer; diff --git a/src/host/dmr/packet/ControlSignaling.cpp b/src/host/dmr/packet/ControlSignaling.cpp index e9e46eb8..24118975 100644 --- a/src/host/dmr/packet/ControlSignaling.cpp +++ b/src/host/dmr/packet/ControlSignaling.cpp @@ -390,7 +390,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) /* Process a data frame from the network. */ -void ControlSignaling::processNetwork(const data::Data & dmrData) +void ControlSignaling::processNetwork(const data::NetData& dmrData) { DataType::E dataType = dmrData.getDataType(); @@ -570,7 +570,7 @@ void ControlSignaling::processNetwork(const data::Data & dmrData) case CSBKO::PRECCSBK: { if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, , CSBK, PRECCSBK (%s Preamble CSBK), toFollow = %u, srcId = %u, dstId = %u", + LogMessage(LOG_NET, "DMR Slot %u, CSBK, PRECCSBK (%s Preamble CSBK), toFollow = %u, srcId = %u, dstId = %u", m_slot->m_slotNo, csbk->getDataContent() ? "Data" : "CSBK", csbk->getCBF(), srcId, dstId); } } @@ -597,29 +597,7 @@ void ControlSignaling::processNetwork(const data::Data & dmrData) data[0U] = modem::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->addFrame(data, true); - } - } - else - m_slot->addFrame(data, true); + m_slot->addFrame(data, true); } } else { diff --git a/src/host/dmr/packet/ControlSignaling.h b/src/host/dmr/packet/ControlSignaling.h index f3c95441..cb8151f6 100644 --- a/src/host/dmr/packet/ControlSignaling.h +++ b/src/host/dmr/packet/ControlSignaling.h @@ -18,7 +18,7 @@ #define __DMR_PACKET_CONTROL_SIGNALING_H__ #include "Defines.h" -#include "common/dmr/data/Data.h" +#include "common/dmr/data/NetData.h" #include "common/dmr/data/EmbeddedData.h" #include "common/dmr/lc/LC.h" #include "common/dmr/lc/CSBK.h" @@ -62,10 +62,10 @@ namespace dmr bool process(uint8_t* data, uint32_t len); /** * @brief Process a data frame from the network. - * @param[in] data Instance of data::Data DMR data container class. + * @param[in] data Instance of data::NetData DMR data container class. * @returns bool True, if data frame is processed, otherwise false. */ - void processNetwork(const data::Data& dmrData); + void processNetwork(const data::NetData& dmrData); /** @} */ /** diff --git a/src/host/dmr/packet/Data.cpp b/src/host/dmr/packet/Data.cpp index 1d79233e..6827c3bb 100644 --- a/src/host/dmr/packet/Data.cpp +++ b/src/host/dmr/packet/Data.cpp @@ -15,6 +15,7 @@ #include "common/dmr/SlotType.h" #include "common/dmr/Sync.h" #include "common/edac/BPTC19696.h" +#include "common/edac/CRC.h" #include "common/edac/Trellis.h" #include "common/Log.h" #include "common/Utils.h" @@ -151,16 +152,14 @@ bool Data::process(uint8_t* data, uint32_t len) if (m_slot->m_rfState == RS_RF_DATA) return true; - data::DataHeader* dataHeader = new data::DataHeader(); - bool valid = dataHeader->decode(data + 2U); + m_rfDataHeader.reset(); + bool valid = m_rfDataHeader.decode(data + 2U); if (!valid) return false; - m_slot->m_rfDataHeader = std::unique_ptr(dataHeader); - - bool gi = dataHeader->getGI(); - uint32_t srcId = dataHeader->getSrcId(); - uint32_t dstId = dataHeader->getDstId(); + bool gi = m_rfDataHeader.getGI(); + uint32_t srcId = m_rfDataHeader.getSrcId(); + uint32_t dstId = m_rfDataHeader.getDstId(); CHECK_AUTHORITATIVE(dstId); CHECK_TRAFFIC_COLLISION(dstId); @@ -185,29 +184,31 @@ bool Data::process(uint8_t* data, uint32_t len) } } - m_slot->m_rfFrames = dataHeader->getBlocks(); + m_rfDataBlockCnt = 0U; + + m_slot->m_rfFrames = m_rfDataHeader.getBlocksToFollow(); m_slot->m_rfSeqNo = 0U; m_slot->m_rfLC = std::make_unique(gi ? FLCO::GROUP : FLCO::PRIVATE, srcId, dstId); if (m_verbose) { - LogMessage(LOG_RF, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", - m_slot->m_slotNo, dataHeader->getDPF(), dataHeader->getA(), dataHeader->getSAP(), dataHeader->getFullMesage(), dataHeader->getBlocks(), dataHeader->getPadCount(), dataHeader->getFSN(), - dstId, srcId, gi); + LogMessage(LOG_RF, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", + m_slot->m_slotNo, m_rfDataHeader.getDPF(), m_rfDataHeader.getA(), m_rfDataHeader.getSAP(), m_rfDataHeader.getFullMesage(), m_rfDataHeader.getBlocksToFollow(), m_rfDataHeader.getPadLength(), m_rfDataHeader.getPacketLength(), + m_rfDataHeader.getFSN(), dstId, srcId, gi); } // did we receive a response header? - if (dataHeader->getDPF() == DPF::RESPONSE) { + if (m_rfDataHeader.getDPF() == DPF::RESPONSE) { if (m_verbose) { LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, slot = %u, sap = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, dstId = %u, srcId = %u, group = %u", - m_slot->m_slotNo, dataHeader->getSAP(), dataHeader->getResponseClass(), dataHeader->getResponseType(), dataHeader->getResponseStatus(), + m_slot->m_slotNo, m_rfDataHeader.getSAP(), m_rfDataHeader.getResponseClass(), m_rfDataHeader.getResponseType(), m_rfDataHeader.getResponseStatus(), dstId, srcId, gi); - if (dataHeader->getResponseClass() == PDUResponseClass::ACK && dataHeader->getResponseType() == PDUResponseType::ACK) { + if (m_rfDataHeader.getResponseClass() == PDUResponseClass::ACK && m_rfDataHeader.getResponseType() == PDUResponseType::ACK) { LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP ACK, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); } else { - if (dataHeader->getResponseClass() == PDUResponseClass::NACK) { - switch (dataHeader->getResponseType()) { + if (m_rfDataHeader.getResponseClass() == PDUResponseClass::NACK) { + switch (m_rfDataHeader.getResponseType()) { case PDUResponseType::NACK_ILLEGAL: LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, illegal format, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); @@ -224,7 +225,7 @@ bool Data::process(uint8_t* data, uint32_t len) default: break; } - } else if (dataHeader->getResponseClass() == PDUResponseClass::ACK_RETRY) { + } else if (m_rfDataHeader.getResponseClass() == PDUResponseClass::ACK_RETRY) { LogMessage(LOG_RF, DMR_DT_DATA_HEADER " ISP, response, OSP ACK RETRY, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); } @@ -233,7 +234,7 @@ bool Data::process(uint8_t* data, uint32_t len) } // Regenerate the data header - dataHeader->encode(data + 2U); + m_rfDataHeader.encode(data + 2U); // Regenerate the Slot Type slotType.encode(data + 2U); @@ -259,7 +260,7 @@ bool Data::process(uint8_t* data, uint32_t len) ::ActivityLog("DMR", true, "Slot %u RF data header from %u to %s%u, %u blocks", m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId, m_slot->m_rfFrames); - ::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * PDU_UNCODED_LENGTH_BYTES + 2U); + ::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U); m_pduDataOffset = 0U; if (m_slot->m_rfFrames == 0U) { @@ -273,48 +274,43 @@ bool Data::process(uint8_t* data, uint32_t len) if (m_slot->m_rfState != RS_RF_DATA || m_slot->m_rfFrames == 0U) return false; - edac::BPTC19696 bptc; - edac::Trellis trellis; + data::DataBlock dataBlock; + dataBlock.setDataType(dataType); - // decode the rate 1/2 payload - if (dataType == DataType::RATE_12_DATA) { - // decode the BPTC (196,96) FEC - uint8_t payload[PDU_UNCONFIRMED_LENGTH_BYTES]; - bptc.decode(data + 2U, payload); + bool ret = dataBlock.decode(data + 2U, m_rfDataHeader); + if (ret) { + uint32_t len = dataBlock.getData(m_pduUserData + m_pduDataOffset); + m_pduDataOffset += len; - // store payload - ::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_UNCONFIRMED_LENGTH_BYTES); - m_pduDataOffset += PDU_UNCONFIRMED_LENGTH_BYTES; + m_slot->m_rfFrames--; + if (m_slot->m_rfFrames == 0U) + dataBlock.setLastBlock(true); - // encode the BPTC (196,96) FEC - bptc.encode(payload, data + 2U); - } - else if (dataType == DataType::RATE_34_DATA) { - // decode the Trellis 3/4 rate FEC - uint8_t payload[PDU_CONFIRMED_LENGTH_BYTES]; - bool ret = trellis.decode34(data + 2U, payload, true); - if (ret) { - // store payload - ::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_CONFIRMED_LENGTH_BYTES); - - // encode the Trellis 3/4 rate FEC - trellis.encode34(payload, data + 2U, true); - } - else { - LogWarning(LOG_RF, "DMR Slot %u, RATE_34_DATA, unfixable RF rate 3/4 data", m_slot->m_slotNo); - Utils::dump(1U, "Unfixable PDU Data", data + 2U, DMR_FRAME_LENGTH_BYTES); + if (m_verbose) { + if (dataType == DataType::RATE_34_DATA) { + LogMessage(LOG_RF, DMR_DT_RATE_34_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_rfDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); + } else if (dataType == DataType::RATE_12_DATA) { + LogMessage(LOG_RF, DMR_DT_RATE_12_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_rfDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); + } + else { + LogMessage(LOG_RF, DMR_DT_RATE_1_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_rfDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); + } } - m_pduDataOffset += PDU_CONFIRMED_LENGTH_BYTES; + dataBlock.encode(data + 2U); + m_rfDataBlockCnt++; } - else if (dataType == DataType::RATE_1_DATA) { - // store payload - ::memcpy(m_pduUserData + m_pduDataOffset, data + 2U, PDU_UNCODED_LENGTH_BYTES); - m_pduDataOffset += PDU_UNCODED_LENGTH_BYTES; - } + if (m_rfDataHeader.getBlocksToFollow() > 0U && m_slot->m_rfFrames == 0U) { + bool crcRet = edac::CRC::checkCRC32(m_pduUserData, m_pduDataOffset); + if (!crcRet) { + LogWarning(LOG_RF, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", m_rfDataHeader.getBlocksToFollow(), m_pduDataOffset); + } - m_slot->m_rfFrames--; + if (m_dumpDataPacket) { + Utils::dump(1U, "PDU Packet", m_pduUserData, m_pduDataOffset); + } + } data[0U] = m_slot->m_rfFrames == 0U ? modem::TAG_EOT : modem::TAG_DATA; data[1U] = 0x00U; @@ -332,26 +328,10 @@ bool Data::process(uint8_t* data, uint32_t len) } if (m_slot->m_rfFrames == 0U) { - if (m_dumpDataPacket) { - Utils::dump(1U, "PDU Packet", m_pduUserData, m_pduDataOffset); - } - LogMessage(LOG_RF, "DMR Slot %u, RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo); m_slot->writeEndRF(); } - if (m_verbose) { - if (dataType == DataType::RATE_12_DATA) { - LogMessage(LOG_RF, DMR_DT_RATE_12_DATA ", block = %u", m_slot->m_rfFrames + 1); - } - else if (dataType == DataType::RATE_34_DATA) { - LogMessage(LOG_RF, DMR_DT_RATE_34_DATA ", block = %u", m_slot->m_rfFrames + 1); - } - else { - LogMessage(LOG_RF, DMR_DT_RATE_1_DATA ", block = %u", m_slot->m_rfFrames + 1); - } - } - return true; } @@ -360,9 +340,9 @@ bool Data::process(uint8_t* data, uint32_t len) /* Process a data frame from the network. */ -void Data::processNetwork(const data::Data& dmrData) +void Data::processNetwork(const data::NetData& dmrData) { - uint8_t dataType = dmrData.getDataType(); + DataType::E dataType = dmrData.getDataType(); uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U]; dmrData.getData(data + 2U); @@ -424,18 +404,16 @@ void Data::processNetwork(const data::Data& dmrData) if (m_slot->m_netState == RS_NET_DATA) return; - data::DataHeader* dataHeader = new data::DataHeader(); - bool valid = dataHeader->decode(data + 2U); + m_netDataHeader.reset(); + bool valid = m_netDataHeader.decode(data + 2U); if (!valid) { LogError(LOG_NET, "DMR Slot %u, DataType::DATA_HEADER, unable to decode the network data header", m_slot->m_slotNo); return; } - m_slot->m_netDataHeader = std::unique_ptr(dataHeader); - - bool gi = dataHeader->getGI(); - uint32_t srcId = dataHeader->getSrcId(); - uint32_t dstId = dataHeader->getDstId(); + bool gi = m_netDataHeader.getGI(); + uint32_t srcId = m_netDataHeader.getSrcId(); + uint32_t dstId = m_netDataHeader.getDstId(); CHECK_NET_AUTHORITATIVE(dstId); CHECK_TG_HANG(dstId); @@ -444,22 +422,24 @@ void Data::processNetwork(const data::Data& dmrData) m_slot->m_tsccPayloadActRetry.stop(); } - m_slot->m_netFrames = dataHeader->getBlocks(); + m_netDataBlockCnt = 0U; + + m_slot->m_netFrames = m_netDataHeader.getBlocksToFollow(); m_slot->m_netLC = std::make_unique(gi ? FLCO::GROUP : FLCO::PRIVATE, srcId, dstId); // did we receive a response header? - if (dataHeader->getDPF() == DPF::RESPONSE) { + if (m_netDataHeader.getDPF() == DPF::RESPONSE) { if (m_verbose) { LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, slot = %u, sap = $%02X, rspClass = $%02X, rspType = $%02X, rspStatus = $%02X, dstId = %u, srcId = %u, group = %u", - m_slot->m_slotNo, dataHeader->getSAP(), dataHeader->getResponseClass(), dataHeader->getResponseType(), dataHeader->getResponseStatus(), + m_slot->m_slotNo, m_netDataHeader.getSAP(), m_netDataHeader.getResponseClass(), m_netDataHeader.getResponseType(), m_netDataHeader.getResponseStatus(), dstId, srcId, gi); - if (dataHeader->getResponseClass() == PDUResponseClass::ACK && dataHeader->getResponseType() == PDUResponseType::ACK) { + if (m_netDataHeader.getResponseClass() == PDUResponseClass::ACK && m_netDataHeader.getResponseType() == PDUResponseType::ACK) { LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP ACK, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); } else { - if (dataHeader->getResponseClass() == PDUResponseClass::NACK) { - switch (dataHeader->getResponseType()) { + if (m_netDataHeader.getResponseClass() == PDUResponseClass::NACK) { + switch (m_netDataHeader.getResponseType()) { case PDUResponseType::NACK_ILLEGAL: LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP NACK, illegal format, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); @@ -476,7 +456,7 @@ void Data::processNetwork(const data::Data& dmrData) default: break; } - } else if (dataHeader->getResponseClass() == PDUResponseClass::ACK_RETRY) { + } else if (m_netDataHeader.getResponseClass() == PDUResponseClass::ACK_RETRY) { LogMessage(LOG_NET, DMR_DT_DATA_HEADER " ISP, response, OSP ACK RETRY, slot = %u, dstId = %u, srcId = %u, group = %u", m_slot->m_slotNo, dstId, srcId, gi); } @@ -485,7 +465,7 @@ void Data::processNetwork(const data::Data& dmrData) } // Regenerate the data header - dataHeader->encode(data + 2U); + m_netDataHeader.encode(data + 2U); // Regenerate the Slot Type SlotType slotType; @@ -512,15 +492,15 @@ void Data::processNetwork(const data::Data& dmrData) m_slot->setShortLC(m_slot->m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::PRIVATE, Slot::SLCO_ACT_TYPE::DATA); if (m_verbose) { - LogMessage(LOG_NET, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padCount = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", - m_slot->m_slotNo, dataHeader->getDPF(), dataHeader->getA(), dataHeader->getSAP(), dataHeader->getFullMesage(), dataHeader->getBlocks(), dataHeader->getPadCount(), dataHeader->getFSN(), - dstId, srcId, gi); + LogMessage(LOG_NET, DMR_DT_DATA_HEADER ", slot = %u, dpf = $%02X, ack = %u, sap = $%02X, fullMessage = %u, blocksToFollow = %u, padLength = %u, packetLength = %u, seqNo = %u, dstId = %u, srcId = %u, group = %u", + m_slot->m_slotNo, m_netDataHeader.getDPF(), m_netDataHeader.getA(), m_netDataHeader.getSAP(), m_netDataHeader.getFullMesage(), m_netDataHeader.getBlocksToFollow(), m_netDataHeader.getPadLength(), m_netDataHeader.getPacketLength(), + m_netDataHeader.getFSN(), dstId, srcId, gi); } ::ActivityLog("DMR", false, "Slot %u network data header from %u to %s%u, %u blocks", m_slot->m_slotNo, srcId, gi ? "TG " : "", dstId, m_slot->m_netFrames); - ::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * PDU_UNCODED_LENGTH_BYTES + 2U); + ::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U); m_pduDataOffset = 0U; if (m_slot->m_netFrames == 0U) { @@ -534,47 +514,43 @@ void Data::processNetwork(const data::Data& dmrData) return; } - // regenerate the rate 1/2 payload - if (dataType == DataType::RATE_12_DATA) { - // decode the BPTC (196,96) FEC - edac::BPTC19696 bptc; - uint8_t payload[PDU_UNCONFIRMED_LENGTH_BYTES]; - bptc.decode(data + 2U, payload); + data::DataBlock dataBlock; + dataBlock.setDataType(dataType); - // store payload - ::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_UNCONFIRMED_LENGTH_BYTES); - m_pduDataOffset += PDU_UNCONFIRMED_LENGTH_BYTES; + bool ret = dataBlock.decode(data + 2U, m_netDataHeader); + if (ret) { + uint32_t len = dataBlock.getData(m_pduUserData + m_pduDataOffset); + m_pduDataOffset += len; - // encode the BPTC (196,96) FEC - bptc.encode(payload, data + 2U); - } - else if (dataType == DataType::RATE_34_DATA) { - // decode the Trellis 3/4 rate FEC - edac::Trellis trellis; - uint8_t payload[PDU_CONFIRMED_LENGTH_BYTES]; - bool ret = trellis.decode34(data + 2U, payload, true); - if (ret) { - // store payload - ::memcpy(m_pduUserData + m_pduDataOffset, payload, PDU_CONFIRMED_LENGTH_BYTES); - - // encode the Trellis 3/4 rate FEC - trellis.encode34(payload, data + 2U, true); - } - else { - LogWarning(LOG_NET, "DMR Slot %u, RATE_34_DATA, unfixable network rate 3/4 data", m_slot->m_slotNo); - Utils::dump(1U, "Data", data + 2U, DMR_FRAME_LENGTH_BYTES); + m_slot->m_netFrames--; + if (m_slot->m_netFrames == 0U) + dataBlock.setLastBlock(true); + + if (m_verbose) { + if (dataType == DataType::RATE_34_DATA) { + LogMessage(LOG_NET, DMR_DT_RATE_34_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_netDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); + } else if (dataType == DataType::RATE_12_DATA) { + LogMessage(LOG_NET, DMR_DT_RATE_12_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_netDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); + } + else { + LogMessage(LOG_NET, DMR_DT_RATE_1_DATA ", ISP, block %u, dataType = $%02X, dpf = $%02X", m_netDataBlockCnt, dataBlock.getDataType(), dataBlock.getFormat()); + } } - m_pduDataOffset += PDU_CONFIRMED_LENGTH_BYTES; + dataBlock.encode(data + 2U); + m_netDataBlockCnt++; } - else if (dataType == DataType::RATE_1_DATA) { - // store payload - ::memcpy(m_pduUserData + m_pduDataOffset, data + 2U, PDU_UNCODED_LENGTH_BYTES); - m_pduDataOffset += PDU_UNCODED_LENGTH_BYTES; - } + if (m_netDataHeader.getBlocksToFollow() > 0U && m_slot->m_netFrames == 0U) { + bool crcRet = edac::CRC::checkCRC32(m_pduUserData, m_pduDataOffset); + if (!crcRet) { + LogWarning(LOG_NET, P25_PDU_STR ", failed CRC-32 check, blocks %u, len %u", m_netDataHeader.getBlocksToFollow(), m_pduDataOffset); + } - m_slot->m_netFrames--; + if (m_dumpDataPacket) { + Utils::dump(1U, "PDU Packet", m_pduUserData, m_pduDataOffset); + } + } if (m_repeatDataPacket) { // regenerate the Slot Type @@ -590,25 +566,9 @@ void Data::processNetwork(const data::Data& dmrData) data[1U] = 0x00U; m_slot->addFrame(data, true); - - if (m_verbose) { - if (dataType == DataType::RATE_12_DATA) { - LogMessage(LOG_RF, DMR_DT_RATE_12_DATA ", block = %u", m_slot->m_netFrames + 1); - } - else if (dataType == DataType::RATE_34_DATA) { - LogMessage(LOG_RF, DMR_DT_RATE_34_DATA ", block = %u", m_slot->m_netFrames + 1); - } - else { - LogMessage(LOG_RF, DMR_DT_RATE_1_DATA ", block = %u", m_slot->m_netFrames + 1); - } - } } if (m_slot->m_netFrames == 0U) { - if (m_dumpDataPacket) { - Utils::dump(1U, "PDU Packet", m_pduUserData, m_pduDataOffset); - } - LogMessage(LOG_NET, "DMR Slot %u, RATE_12/34_DATA, ended data transmission", m_slot->m_slotNo); m_slot->writeEndNet(); } @@ -627,6 +587,10 @@ void Data::processNetwork(const data::Data& dmrData) Data::Data(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool repeatDataPacket, bool debug, bool verbose) : m_slot(slot), + m_rfDataHeader(), + m_rfDataBlockCnt(0U), + m_netDataHeader(), + m_netDataBlockCnt(0U), m_pduUserData(nullptr), m_pduDataOffset(0U), m_lastRejectId(0U), @@ -635,8 +599,8 @@ Data::Data(Slot* slot, network::BaseNetwork* network, bool dumpDataPacket, bool m_verbose(verbose), m_debug(debug) { - m_pduUserData = new uint8_t[MAX_PDU_COUNT * PDU_UNCODED_LENGTH_BYTES + 2U]; - ::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * PDU_UNCODED_LENGTH_BYTES + 2U); + m_pduUserData = new uint8_t[MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U]; + ::memset(m_pduUserData, 0x00U, MAX_PDU_COUNT * DMR_PDU_UNCODED_LENGTH_BYTES + 2U); } /* Finalizes a instance of the Data class. */ @@ -709,7 +673,7 @@ void Data::writeRF_PDU_Ack_Response(uint8_t rspClass, uint8_t rspType, uint8_t r rspHeader.setResponseClass(rspClass); rspHeader.setResponseType(rspType); rspHeader.setResponseStatus(rspStatus); - rspHeader.setBlocks(1U); + rspHeader.setBlocksToFollow(1U); rspHeader.encode(data + 2U); writeRF_PDU(DataType::DATA_HEADER, data); @@ -717,8 +681,8 @@ void Data::writeRF_PDU_Ack_Response(uint8_t rspClass, uint8_t rspType, uint8_t r ::memset(data + 2U, 0x00U, DMR_FRAME_LENGTH_BYTES); // decode the BPTC (196,96) FEC - uint8_t payload[PDU_UNCONFIRMED_LENGTH_BYTES]; - ::memset(payload, 0x00U, PDU_UNCONFIRMED_LENGTH_BYTES); + uint8_t payload[DMR_PDU_UNCONFIRMED_LENGTH_BYTES]; + ::memset(payload, 0x00U, DMR_PDU_UNCONFIRMED_LENGTH_BYTES); // encode the BPTC (196,96) FEC bptc.encode(payload, data + 2U); diff --git a/src/host/dmr/packet/Data.h b/src/host/dmr/packet/Data.h index cbf7df87..954101ec 100644 --- a/src/host/dmr/packet/Data.h +++ b/src/host/dmr/packet/Data.h @@ -18,8 +18,9 @@ #define __DMR_PACKET_DATA_H__ #include "Defines.h" -#include "common/dmr/data/Data.h" +#include "common/dmr/data/NetData.h" #include "common/dmr/data/DataHeader.h" +#include "common/dmr/data/DataBlock.h" #include "common/dmr/data/EmbeddedData.h" #include "common/dmr/lc/LC.h" #include "common/edac/AMBEFEC.h" @@ -63,10 +64,10 @@ namespace dmr bool process(uint8_t* data, uint32_t len); /** * @brief Process a data frame from the network. - * @param[in] data Instance of data::Data DMR data container class. + * @param[in] data Instance of data::NetData DMR data container class. * @returns bool True, if data frame is processed, otherwise false. */ - void processNetwork(const data::Data& dmrData); + void processNetwork(const data::NetData& dmrData); /** @} */ private: @@ -75,6 +76,12 @@ namespace dmr friend class dmr::Slot; Slot* m_slot; + data::DataHeader m_rfDataHeader; + uint8_t m_rfDataBlockCnt; + + data::DataHeader m_netDataHeader; + uint8_t m_netDataBlockCnt; + uint8_t* m_pduUserData; uint32_t m_pduDataOffset; uint32_t m_lastRejectId; diff --git a/src/host/dmr/packet/Voice.cpp b/src/host/dmr/packet/Voice.cpp index 930f7364..66cd2106 100644 --- a/src/host/dmr/packet/Voice.cpp +++ b/src/host/dmr/packet/Voice.cpp @@ -632,7 +632,7 @@ bool Voice::process(uint8_t* data, uint32_t len) /* Process a voice frame from the network. */ -void Voice::processNetwork(const data::Data& dmrData) +void Voice::processNetwork(const data::NetData& dmrData) { uint8_t dataType = dmrData.getDataType(); diff --git a/src/host/dmr/packet/Voice.h b/src/host/dmr/packet/Voice.h index 8316a132..8265f194 100644 --- a/src/host/dmr/packet/Voice.h +++ b/src/host/dmr/packet/Voice.h @@ -18,7 +18,7 @@ #define __DMR_PACKET_VOICE_H__ #include "Defines.h" -#include "common/dmr/data/Data.h" +#include "common/dmr/data/NetData.h" #include "common/dmr/data/EmbeddedData.h" #include "common/dmr/lc/LC.h" #include "common/dmr/lc/PrivacyLC.h" @@ -58,10 +58,10 @@ namespace dmr bool process(uint8_t* data, uint32_t len); /** * @brief Process a data frame from the network. - * @param[in] data Instance of data::Data DMR data container class. + * @param[in] data Instance of data::NetData DMR data container class. * @returns bool True, if data frame is processed, otherwise false. */ - void processNetwork(const data::Data& dmrData); + void processNetwork(const data::NetData& dmrData); /** @} */ private: