diff --git a/Makefile b/Makefile index ef207f19..48f22a05 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,7 @@ HOST_OBJECTS = \ dmr/Slot.o \ dmr/SlotType.o \ dmr/Sync.o \ + lookups/AffiliationLookup.o \ lookups/IdenTableLookup.o \ lookups/RadioIdLookup.o \ lookups/RSSIInterpolator.o \ @@ -90,6 +91,7 @@ HOST_OBJECTS = \ p25/lc/LC.o \ p25/lc/TDULC.o \ p25/lc/TSBK.o \ + p25/lookups/P25AffiliationLookup.o \ p25/packet/Data.o \ p25/packet/Trunk.o \ p25/packet/Voice.o \ diff --git a/dmr/packet/ControlSignaling.cpp b/dmr/packet/ControlSignaling.cpp index 3b311a78..272bb198 100644 --- a/dmr/packet/ControlSignaling.cpp +++ b/dmr/packet/ControlSignaling.cpp @@ -32,6 +32,7 @@ #include "dmr/lc/ShortLC.h" #include "dmr/lc/FullLC.h" #include "dmr/lc/CSBK.h" +#include "dmr/Slot.h" #include "dmr/SlotType.h" #include "dmr/Sync.h" #include "edac/BPTC19696.h" diff --git a/dmr/packet/ControlSignaling.h b/dmr/packet/ControlSignaling.h index 8a788179..3b2f7a0b 100644 --- a/dmr/packet/ControlSignaling.h +++ b/dmr/packet/ControlSignaling.h @@ -35,7 +35,6 @@ #include "dmr/data/Data.h" #include "dmr/data/EmbeddedData.h" #include "dmr/lc/LC.h" -#include "dmr/Slot.h" #include "modem/Modem.h" #include "network/BaseNetwork.h" #include "RingBuffer.h" diff --git a/dmr/packet/Data.cpp b/dmr/packet/Data.cpp index c0f9264b..bc8b9a28 100644 --- a/dmr/packet/Data.cpp +++ b/dmr/packet/Data.cpp @@ -31,6 +31,7 @@ #include "dmr/lc/ShortLC.h" #include "dmr/lc/FullLC.h" #include "dmr/lc/CSBK.h" +#include "dmr/Slot.h" #include "dmr/SlotType.h" #include "dmr/Sync.h" #include "edac/BPTC19696.h" diff --git a/dmr/packet/Data.h b/dmr/packet/Data.h index aed7660b..9acfe2fe 100644 --- a/dmr/packet/Data.h +++ b/dmr/packet/Data.h @@ -36,7 +36,6 @@ #include "dmr/data/DataHeader.h" #include "dmr/data/EmbeddedData.h" #include "dmr/lc/LC.h" -#include "dmr/Slot.h" #include "edac/AMBEFEC.h" #include "modem/Modem.h" #include "network/BaseNetwork.h" diff --git a/dmr/packet/Voice.cpp b/dmr/packet/Voice.cpp index cafac909..47e24fef 100644 --- a/dmr/packet/Voice.cpp +++ b/dmr/packet/Voice.cpp @@ -24,6 +24,7 @@ * GNU General Public License for more details. */ #include "Defines.h" +#include "dmr/packet/Data.h" #include "dmr/packet/Voice.h" #include "dmr/acl/AccessControl.h" #include "dmr/data/DataHeader.h" @@ -32,6 +33,7 @@ #include "dmr/lc/CSBK.h" #include "dmr/lc/ShortLC.h" #include "dmr/lc/FullLC.h" +#include "dmr/Slot.h" #include "dmr/SlotType.h" #include "dmr/Sync.h" #include "edac/BPTC19696.h" diff --git a/dmr/packet/Voice.h b/dmr/packet/Voice.h index 3fc98244..aee4827a 100644 --- a/dmr/packet/Voice.h +++ b/dmr/packet/Voice.h @@ -36,7 +36,6 @@ #include "dmr/data/EmbeddedData.h" #include "dmr/lc/LC.h" #include "dmr/lc/PrivacyLC.h" -#include "dmr/Slot.h" #include "edac/AMBEFEC.h" #include "network/BaseNetwork.h" #include "lookups/RadioIdLookup.h" diff --git a/lookups/AffiliationLookup.cpp b/lookups/AffiliationLookup.cpp new file mode 100644 index 00000000..f4651fd5 --- /dev/null +++ b/lookups/AffiliationLookup.cpp @@ -0,0 +1,384 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +/* +* Copyright (C) 2022 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include "lookups/AffiliationLookup.h" +#include "Log.h" + +using namespace lookups; + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/// +/// Initializes a new instance of the AffiliationLookup class. +/// +/// Name of lookup table. +/// Flag indicating whether verbose logging is enabled. +AffiliationLookup::AffiliationLookup(const char* name, bool verbose) : + m_rfChTable(), + m_rfGrantChCnt(0U), + m_unitRegTable(), + m_grpAffTable(), + m_grantChTable(), + m_grantTimers(), + m_name(name), + m_verbose(verbose) +{ + m_rfChTable.clear(); + + m_unitRegTable.clear(); + m_grpAffTable.clear(); + + m_grantChTable.clear(); + m_grantTimers.clear(); +} + +/// +/// Finalizes a instance of the AffiliationLookup class. +/// +AffiliationLookup::~AffiliationLookup() +{ + /* stub */ +} + +/// +/// Helper to group affiliate a source ID. +/// +/// +void AffiliationLookup::unitReg(uint32_t srcId) +{ + m_unitRegTable.push_back(srcId); +} + +/// +/// Helper to group unaffiliate a source ID. +/// +/// +bool AffiliationLookup::unitDereg(uint32_t srcId) +{ + bool ret = false; + + groupUnaff(srcId); + + // remove dynamic unit registration table entry + if (std::find(m_unitRegTable.begin(), m_unitRegTable.end(), srcId) != m_unitRegTable.end()) { + auto it = std::find(m_unitRegTable.begin(), m_unitRegTable.end(), srcId); + m_unitRegTable.erase(it); + ret = true; + } + + return ret; +} + +/// +/// Helper to determine if the source ID has unit registered. +/// +/// +/// +bool AffiliationLookup::isUnitReg(uint32_t srcId) const +{ + // lookup dynamic unit registration table entry + if (std::find(m_unitRegTable.begin(), m_unitRegTable.end(), srcId) != m_unitRegTable.end()) { + return true; + } + else { + return false; + } +} + +/// +/// Helper to group affiliate a source ID. +/// +/// +/// +void AffiliationLookup::groupAff(uint32_t srcId, uint32_t dstId) +{ + // update dynamic affiliation table + m_grpAffTable[srcId] = dstId; +} + +/// +/// Helper to group unaffiliate a source ID. +/// +/// +bool AffiliationLookup::groupUnaff(uint32_t srcId) +{ + // remove dynamic affiliation table entry + try { + m_grpAffTable.at(srcId); + m_grpAffTable.erase(srcId); + return true; + } + catch (...) { + return false; + } +} + +/// +/// Helper to determine if the source ID has affiliated to the group destination ID. +/// +/// +/// +/// +bool AffiliationLookup::isGroupAff(uint32_t srcId, uint32_t dstId) const +{ + // lookup dynamic affiliation table entry + try { + uint32_t tblDstId = m_grpAffTable.at(srcId); + if (tblDstId == dstId) { + return true; + } + else { + return false; + } + } catch (...) { + return false; + } +} + +/// +/// Helper to release group affiliations. +/// +/// +/// +std::vector AffiliationLookup::clearGroupAff(uint32_t dstId, bool releaseAll) +{ + std::vector srcToRel = std::vector(); + if (dstId == 0U && !releaseAll) { + return srcToRel; + } + + if (dstId == 0U && releaseAll) { + LogWarning(LOG_HOST, "%s, releasing all group affiliations", m_name); + for (auto it = m_grpAffTable.begin(); it != m_grpAffTable.end(); ++it) { + uint32_t srcId = it->first; + srcToRel.push_back(srcId); + } + } + else { + LogWarning(LOG_HOST, "%s, releasing group affiliations, dstId = %u", m_name, dstId); + for (auto it = m_grpAffTable.begin(); it != m_grpAffTable.end(); ++it) { + uint32_t srcId = it->first; + uint32_t grpId = it->second; + if (grpId == dstId) { + srcToRel.push_back(srcId); + } + } + } + + return srcToRel; +} + +/// +/// Helper to grant a channel. +/// +/// +/// +/// +bool AffiliationLookup::grantCh(uint32_t dstId, uint32_t grantTimeout) +{ + if (dstId == 0U) { + return false; + } + + if (!isRFChAvailable()) { + return false; + } + + uint32_t chNo = m_rfChTable.at(0); + auto it = std::find(m_rfChTable.begin(), m_rfChTable.end(), chNo); + m_rfChTable.erase(it); + + m_grantChTable[dstId] = chNo; + m_rfGrantChCnt++; + + m_grantTimers[dstId] = Timer(1000U, grantTimeout); + m_grantTimers[dstId].start(); + + return true; +} + +/// +/// Helper to start the destination ID grant timer. +/// +/// +/// +void AffiliationLookup::touchGrant(uint32_t dstId) +{ + if (dstId == 0U) { + return; + } + + if (isGranted(dstId)) { + m_grantTimers[dstId].start(); + } +} + +/// +/// Helper to release the channel grant for the destination ID. +/// +/// +/// +bool AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) +{ + if (dstId == 0U && !releaseAll) { + return false; + } + + // are we trying to release all grants? + if (dstId == 0U && releaseAll) { + LogWarning(LOG_HOST, "%s, force releasing all channel grants", m_name); + + std::vector gntsToRel = std::vector(); + for (auto it = m_grantChTable.begin(); it != m_grantChTable.end(); ++it) { + uint32_t dstId = it->first; + gntsToRel.push_back(dstId); + } + + // release grants + for (auto it = gntsToRel.begin(); it != gntsToRel.end(); ++it) { + releaseGrant(*it, false); + } + + return true; + } + + if (isGranted(dstId)) { + uint32_t chNo = m_grantChTable.at(dstId); + + if (m_verbose) { + LogMessage(LOG_HOST, "%s, releasing channel grant, chNo = %u, dstId = %u", + m_name, chNo, dstId); + } + + m_grantChTable[dstId] = 0U; + m_rfChTable.push_back(chNo); + + if (m_rfGrantChCnt > 0U) { + m_rfGrantChCnt--; + } + else { + m_rfGrantChCnt = 0U; + } + + m_grantTimers[dstId].stop(); + return true; + } + + return false; +} + +/// +/// Helper to determine if the channel number is busy. +/// +/// +/// +bool AffiliationLookup::isChBusy(uint32_t chNo) const +{ + if (chNo == 0U) { + return false; + } + + // lookup dynamic channel grant table entry + for (auto it = m_grantChTable.begin(); it != m_grantChTable.end(); ++it) { + if (it->second == chNo) { + return true; + } + } + + return false; +} + +/// +/// Helper to determine if the destination ID is already granted. +/// +/// +/// +bool AffiliationLookup::isGranted(uint32_t dstId) const +{ + if (dstId == 0U) { + return false; + } + + // lookup dynamic channel grant table entry + try { + uint32_t chNo = m_grantChTable.at(dstId); + if (chNo != 0U) { + return true; + } + else { + return false; + } + } catch (...) { + return false; + } +} + +/// +/// Helper to get the channel granted for the given destination ID. +/// +/// +/// +uint32_t AffiliationLookup::getGrantedCh(uint32_t dstId) +{ + if (dstId == 0U) { + return 0U; + } + + if (isGranted(dstId)) { + return m_grantChTable[dstId]; + } + + return 0U; +} + +/// +/// Updates the processor by the passed number of milliseconds. +/// +/// +void AffiliationLookup::clock(uint32_t ms) +{ + // clock all the grant timers + std::vector gntsToRel = std::vector(); + for (auto it = m_grantChTable.begin(); it != m_grantChTable.end(); ++it) { + uint32_t dstId = it->first; + + m_grantTimers[dstId].clock(ms); + if (m_grantTimers[dstId].isRunning() && m_grantTimers[dstId].hasExpired()) { + gntsToRel.push_back(dstId); + } + } + + // release grants that have timed out + for (auto it = gntsToRel.begin(); it != gntsToRel.end(); ++it) { + releaseGrant(*it, false); + } +} diff --git a/lookups/AffiliationLookup.h b/lookups/AffiliationLookup.h new file mode 100644 index 00000000..e1151de6 --- /dev/null +++ b/lookups/AffiliationLookup.h @@ -0,0 +1,123 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +/* +* Copyright (C) 2022 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#if !defined(__AFFILIATION_LOOKUP_H__) +#define __AFFILIATION_LOOKUP_H__ + +#include "Defines.h" +#include "Timer.h" + +#include +#include +#include +#include + +namespace lookups +{ + // --------------------------------------------------------------------------- + // Class Declaration + // Implements a lookup table class that contains subscriber registration + // and group affiliation information. + // --------------------------------------------------------------------------- + + class HOST_SW_API AffiliationLookup { + public: + /// Initializes a new instance of the AffiliationLookup class. + AffiliationLookup(const char* name, bool verbose); + /// Finalizes a instance of the AffiliationLookup class. + virtual ~AffiliationLookup(); + + /// Gets the count of unit registrations. + uint8_t unitRegSize() const { return m_unitRegTable.size(); } + /// Gets the unit registration table. + std::vector unitRegTable() const { return m_unitRegTable; } + /// Helper to register a source ID. + virtual void unitReg(uint32_t srcId); + /// Helper to deregister a source ID. + virtual bool unitDereg(uint32_t srcId); + /// Helper to determine if the source ID has unit registered. + virtual bool isUnitReg(uint32_t srcId) const; + + /// Gets the count of affiliations. + uint8_t grpAffSize() const { return m_grpAffTable.size(); } + /// Gets the group affiliation table. + std::unordered_map grpAffTable() const { return m_grpAffTable; } + /// Helper to group affiliate a source ID. + virtual void groupAff(uint32_t srcId, uint32_t dstId); + /// Helper to group unaffiliate a source ID. + virtual bool groupUnaff(uint32_t srcId); + /// Helper to determine if the source ID has affiliated to the group destination ID. + virtual bool isGroupAff(uint32_t srcId, uint32_t dstId) const; + /// Helper to release group affiliations. + virtual std::vector clearGroupAff(uint32_t dstId, bool releaseAll); + + /// Gets the count of grants. + uint8_t grantSize() const { return m_grantChTable.size(); } + /// Gets the grant table. + std::unordered_map grantTable() const { return m_grantChTable; } + /// Helper to grant a channel. + virtual bool grantCh(uint32_t dstId, uint32_t grantTimeout); + /// Helper to start the destination ID grant timer. + virtual void touchGrant(uint32_t dstId); + /// Helper to release the channel grant for the destination ID. + virtual bool releaseGrant(uint32_t dstId, bool releaseAll); + /// Helper to determine if the channel number is busy. + virtual bool isChBusy(uint32_t chNo) const; + /// Helper to determine if the destination ID is already granted. + virtual bool isGranted(uint32_t dstId) const; + /// Helper to get the channel granted for the given destination ID. + virtual uint32_t getGrantedCh(uint32_t dstId); + + /// Helper to add a RF channel. + void addRFCh(uint32_t chNo) { m_rfChTable.push_back(chNo); } + /// Helper to remove a RF channel. + void removeRFCh(uint32_t chNo) { m_rfChTable.push_back(chNo); } + /// Gets the count of RF channels. + uint8_t getRFChCnt() const { return m_rfChTable.size(); } + /// Helper to determine if there are any RF channels available.. + bool isRFChAvailable() const { return !m_rfChTable.empty(); } + /// Gets the count of granted RF channels. + uint8_t getGrantedRFChCnt() const { return m_rfGrantChCnt; } + + /// Updates the processor by the passed number of milliseconds. + void clock(uint32_t ms); + + protected: + std::vector m_rfChTable; + uint8_t m_rfGrantChCnt; + + std::vector m_unitRegTable; + std::unordered_map m_grpAffTable; + + std::unordered_map m_grantChTable; + std::unordered_map m_grantTimers; + + const char *m_name; + + bool m_verbose; + }; +} // namespace lookups + +#endif // __AFFILIATION_LOOKUP_H__ diff --git a/network/RemoteControl.cpp b/network/RemoteControl.cpp index c43136fa..30583f5b 100644 --- a/network/RemoteControl.cpp +++ b/network/RemoteControl.cpp @@ -30,6 +30,10 @@ */ #include "Defines.h" #include "edac/SHA256.h" +#include "dmr/Control.h" +#include "p25/Control.h" +#include "nxdn/Control.h" +#include "host/Host.h" #include "RemoteControl.h" #include "HostMain.h" #include "Log.h" @@ -755,7 +759,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx else if (rcom == RCD_P25_RELEASE_GRANTS_CMD) { // Command is in the form of: "p25-rel-grnts" if (p25 != NULL) { - p25->trunk()->releaseDstIdGrant(0, true); + p25->affiliations().releaseGrant(0, true); } else { LogError(LOG_RCON, CMD_FAILED_STR "P25 mode is not enabled!"); @@ -767,10 +771,10 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx uint32_t grp = getArgUInt32(args, 0U); if (grp == 0) { - p25->trunk()->clearGrpAff(0, true); + p25->affiliations().clearGroupAff(0, true); } else { - p25->trunk()->clearGrpAff(grp, false); + p25->affiliations().clearGroupAff(grp, false); } } else { diff --git a/network/RemoteControl.h b/network/RemoteControl.h index ab9fc6d7..74fae630 100644 --- a/network/RemoteControl.h +++ b/network/RemoteControl.h @@ -33,10 +33,6 @@ #include "Defines.h" #include "network/UDPSocket.h" -#include "dmr/Control.h" -#include "p25/Control.h" -#include "nxdn/Control.h" -#include "host/Host.h" #include "lookups/RadioIdLookup.h" #include "lookups/TalkgroupIdLookup.h" @@ -65,7 +61,7 @@ public: ~RemoteControl(); /// Sets the instances of the Radio ID and Talkgroup ID lookup tables. - void setLookups(lookups::RadioIdLookup* ridLookup, lookups::TalkgroupIdLookup* tidLookup); + void setLookups(::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupIdLookup* tidLookup); /// Process remote network command data. void process(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn); @@ -84,8 +80,8 @@ private: uint8_t* m_passwordHash; bool m_debug; - lookups::RadioIdLookup* m_ridLookup; - lookups::TalkgroupIdLookup* m_tidLookup; + ::lookups::RadioIdLookup* m_ridLookup; + ::lookups::TalkgroupIdLookup* m_tidLookup; /// std::string getArgString(std::vector args, uint32_t n) const; diff --git a/nxdn/NXDNDefines.h b/nxdn/NXDNDefines.h index 78c2027e..11069682 100644 --- a/nxdn/NXDNDefines.h +++ b/nxdn/NXDNDefines.h @@ -239,6 +239,9 @@ namespace nxdn const uint8_t RTCH_MESSAGE_TYPE_SDCALL_RESP = 0x3BU; // SDCALL_RESP - Short Data Call Response // Control Channel Message Types + const uint8_t RCCH_MESSAGE_TYPE_VCALL_CONN = 0x03U; // VCALL_CONN - Voice Call Connection Request (ISP) / Voice Call Connection Response (OSP) + const uint8_t RCCH_MESSAGE_TYPE_VCALL_ASSGN = 0x04U; // VCALL_ASSGN - Voice Call Assignment + const uint8_t RCCH_MESSAGE_TYPE_DCALL_ASSGN = 0x14U; // DCALL_ASSGN - Data Call Assignment const uint8_t RCCH_MESSAGE_TYPE_SITE_INFO = 0x18U; // SITE_INFO - Site Information const uint8_t RCCH_MESSAGE_TYPE_REG = 0x20U; // REG - Registration Request (ISP) / Registration Response (OSP) const uint8_t RCCH_MESSAGE_TYPE_REG_C = 0x22U; // REG_C - Registration Clear Request (ISP) / Registration Clear Response (OSP) diff --git a/nxdn/lc/RCCH.cpp b/nxdn/lc/RCCH.cpp index e155b998..d71127f4 100644 --- a/nxdn/lc/RCCH.cpp +++ b/nxdn/lc/RCCH.cpp @@ -158,6 +158,15 @@ void RCCH::reset() m_version = 0U; m_causeRsp = NXDN_CAUSE_MM_NORMAL_1; + + m_grpVchNo = 0U; + + m_emergency = false; + m_encrypted = false; + m_priority = false; + m_group = true; + m_duplex = false; + m_transmissionMode = TRANSMISSION_MODE_4800; } /// @@ -224,6 +233,14 @@ RCCH::RCCH(SiteData siteData) : m_regOption(0U), m_version(0U), m_causeRsp(NXDN_CAUSE_MM_NORMAL_1), + m_grpVchNo(0U), + m_callType(CALL_TYPE_UNSPECIFIED), + m_emergency(false), + m_encrypted(false), + m_priority(false), + m_group(true), + m_duplex(false), + m_transmissionMode(TRANSMISSION_MODE_4800), m_siteData(siteData), m_siteIdenEntry(), m_data(NULL) @@ -251,6 +268,25 @@ bool RCCH::decodeLC(const uint8_t* data) // message type opcodes switch (m_messageType) { + case RTCH_MESSAGE_TYPE_VCALL: + case RCCH_MESSAGE_TYPE_VCALL_CONN: + m_callType = (data[2U] >> 5) & 0x07U; // Call Type + m_emergency = (data[1U] & 0x80U) == 0x80U; // Emergency Flag + m_priority = (data[1U] & 0x20U) == 0x20U; // Priority Flag + m_duplex = (data[2U] & 0x10U) == 0x10U; // Half/Full Duplex Flag + m_transmissionMode = (data[2U] & 0x07U); // Transmission Mode + m_srcId = (uint16_t)((data[3U] << 8) | data[4U]) & 0xFFFFU; // Source Radio Address + m_dstId = (uint16_t)((data[5U] << 8) | data[6U]) & 0xFFFFU; // Target Radio Address + break; + case RTCH_MESSAGE_TYPE_DCALL_HDR: + m_callType = (data[2U] >> 5) & 0x07U; // Call Type + m_emergency = (data[1U] & 0x80U) == 0x80U; // Emergency Flag + m_priority = (data[1U] & 0x20U) == 0x20U; // Priority Flag + m_duplex = (data[2U] & 0x10U) == 0x10U; // Half/Full Duplex Flag + m_transmissionMode = (data[2U] & 0x07U); // Transmission Mode + m_srcId = (uint16_t)((data[3U] << 8) | data[4U]) & 0xFFFFU; // Source Radio Address + m_dstId = (uint16_t)((data[5U] << 8) | data[6U]) & 0xFFFFU; // Target Radio Address + break; case MESSAGE_TYPE_IDLE: break; case RCCH_MESSAGE_TYPE_REG: @@ -289,6 +325,58 @@ void RCCH::encodeLC(uint8_t* data) // message type opcodes switch (m_messageType) { + case RTCH_MESSAGE_TYPE_VCALL: + case RCCH_MESSAGE_TYPE_VCALL_CONN: + m_data[1U] = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag + (m_priority ? 0x20U : 0x00U); // Priority Flag + m_data[2U] = ((m_callType & 0x07U) << 5) + // Call Type + (m_duplex ? 0x10U : 0x00U) + // Half/Full Duplex Flag + (m_transmissionMode & 0x07U); // Transmission Mode + + m_data[3U] = (m_srcId >> 8U) & 0xFFU; // Source Radio Address + m_data[4U] = (m_srcId >> 0U) & 0xFFU; // ... + m_data[5U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address + m_data[6U] = (m_dstId >> 0U) & 0xFFU; // ... + + m_data[7U] = m_causeRsp; // Cause (VD) + m_data[9U] = (m_siteData.locId() >> 8) & 0xFFU; // Location ID + m_data[10U] = (m_siteData.locId() >> 0) & 0xFFU; // ... + break; + case RCCH_MESSAGE_TYPE_VCALL_ASSGN: + case RCCH_MESSAGE_TYPE_DCALL_ASSGN: + m_data[1U] = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag + (m_priority ? 0x20U : 0x00U); // Priority Flag + m_data[2U] = ((m_callType & 0x07U) << 5) + // Call Type + (m_duplex ? 0x10U : 0x00U) + // Half/Full Duplex Flag + (m_transmissionMode & 0x07U); // Transmission Mode + + m_data[3U] = (m_srcId >> 8U) & 0xFFU; // Source Radio Address + m_data[4U] = (m_srcId >> 0U) & 0xFFU; // ... + m_data[5U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address + m_data[6U] = (m_dstId >> 0U) & 0xFFU; // ... + + m_data[7U] = (m_grpVchNo >> 10) & 0x03U; // Channel + m_data[8U] = (m_grpVchNo & 0xFFU); // ... + + m_data[10U] = (m_siteData.locId() >> 8) & 0xFFU; // Location ID + m_data[11U] = (m_siteData.locId() >> 0) & 0xFFU; // ... + break; + case RTCH_MESSAGE_TYPE_DCALL_HDR: + m_data[1U] = (m_emergency ? 0x80U : 0x00U) + // Emergency Flag + (m_priority ? 0x20U : 0x00U); // Priority Flag + m_data[2U] = ((m_callType & 0x07U) << 5) + // Call Type + (m_duplex ? 0x10U : 0x00U) + // Half/Full Duplex Flag + (m_transmissionMode & 0x07U); // Transmission Mode + + m_data[3U] = (m_srcId >> 8U) & 0xFFU; // Source Radio Address + m_data[4U] = (m_srcId >> 0U) & 0xFFU; // ... + m_data[5U] = (m_dstId >> 8U) & 0xFFU; // Target Radio Address + m_data[6U] = (m_dstId >> 0U) & 0xFFU; // ... + + m_data[7U] = m_causeRsp; // Cause (VD) + m_data[9U] = (m_siteData.locId() >> 8) & 0xFFU; // Location ID + m_data[10U] = (m_siteData.locId() >> 0) & 0xFFU; // ... + break; case MESSAGE_TYPE_IDLE: break; case MESSAGE_TYPE_DST_ID_INFO: diff --git a/nxdn/lc/RCCH.h b/nxdn/lc/RCCH.h index 1d865e6d..0a4b3bf0 100644 --- a/nxdn/lc/RCCH.h +++ b/nxdn/lc/RCCH.h @@ -98,6 +98,28 @@ namespace nxdn /// Cause Response. __PROPERTY(uint8_t, causeRsp, CauseResponse); + /// Voice channel number. + __PROPERTY(uint32_t, grpVchNo, GrpVchNo); + + /** Call Data */ + /// Call Type + __PROPERTY(uint8_t, callType, CallType); + + /** Common Call Options */ + /// Flag indicating the emergency bits are set. + __PROPERTY(bool, emergency, Emergency); + /// Flag indicating that encryption is enabled. + __PROPERTY(bool, encrypted, Encrypted); + /// Flag indicating priority paging. + __PROPERTY(bool, priority, Priority); + /// Flag indicating a group/talkgroup operation. + __PROPERTY(bool, group, Group); + /// Flag indicating a half/full duplex operation. + __PROPERTY(bool, duplex, Duplex); + + /// Transmission mode. + __PROPERTY(uint8_t, transmissionMode, TransmissionMode); + /** Local Site data */ /// Local Site Data. __PROPERTY_PLAIN(SiteData, siteData, siteData); diff --git a/nxdn/packet/Trunk.h b/nxdn/packet/Trunk.h index db2da67a..6387366f 100644 --- a/nxdn/packet/Trunk.h +++ b/nxdn/packet/Trunk.h @@ -44,6 +44,8 @@ namespace nxdn // Class Prototypes // --------------------------------------------------------------------------- + namespace packet { class HOST_SW_API Data; } + namespace packet { class HOST_SW_API Voice; } class HOST_SW_API Control; namespace packet @@ -69,6 +71,8 @@ namespace nxdn void clock(uint32_t ms); protected: + friend class nxdn::packet::Data; + friend class nxdn::packet::Voice; friend class nxdn::Control; Control* m_nxdn; diff --git a/p25/Control.cpp b/p25/Control.cpp index 995beb74..6057b800 100644 --- a/p25/Control.cpp +++ b/p25/Control.cpp @@ -82,8 +82,8 @@ const uint32_t MAX_PREAMBLE_TDU_CNT = 64U; /// Flag indicating whether P25 debug is enabled. /// Flag indicating whether P25 verbose logging is enabled. Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::BaseNetwork* network, - uint32_t timeout, uint32_t tgHang, bool duplex, lookups::RadioIdLookup* ridLookup, - lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper, + uint32_t timeout, uint32_t tgHang, bool duplex, ::lookups::RadioIdLookup* ridLookup, + ::lookups::TalkgroupIdLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper, bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose) : m_voice(NULL), m_data(NULL), @@ -105,6 +105,7 @@ Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Mod m_idenTable(idenTable), m_ridLookup(ridLookup), m_tidLookup(tidLookup), + m_affiliations(this, verbose), m_idenEntry(), m_queue(queueSize, "P25 Frame"), m_rfState(RS_RF_LISTENING), @@ -281,9 +282,9 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s m_siteData = SiteData(netId, sysId, rfssId, siteId, 0U, channelId, channelNo, serviceClass); m_siteData.setCallsign(cwCallsign); - std::vector entries = m_idenTable->list(); + std::vector<::lookups::IdenTable> entries = m_idenTable->list(); for (auto it = entries.begin(); it != entries.end(); ++it) { - lookups::IdenTable entry = *it; + ::lookups::IdenTable entry = *it; if (entry.channelId() == channelId) { m_idenEntry = entry; break; @@ -291,11 +292,10 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s } std::vector availCh = voiceChNo; - m_trunk->m_voiceChCnt = (uint8_t)availCh.size(); m_siteData.setChCnt((uint8_t)availCh.size()); for (auto it = availCh.begin(); it != availCh.end(); ++it) { - m_trunk->m_voiceChTable.push_back(*it); + m_affiliations.addRFCh(*it); } uint32_t ccBcstInterval = p25Protocol["control"]["interval"].as(300U); @@ -382,7 +382,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) m_voice->m_rfFrames, m_voice->m_rfBits, m_voice->m_rfUndecodableLC, m_voice->m_rfErrs, float(m_voice->m_rfErrs * 100U) / float(m_voice->m_rfBits)); if (m_control) { - m_trunk->releaseDstIdGrant(m_voice->m_rfLC.getDstId(), false); + m_affiliations.releaseGrant(m_voice->m_rfLC.getDstId(), false); } writeRF_TDU(false); @@ -509,7 +509,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) if (!m_dedicatedControl) ret = m_voice->process(data, len); else { - if (m_voiceOnControl && m_trunk->isChBusy(m_siteData.channelNo())) { + if (m_voiceOnControl && m_affiliations.isChBusy(m_siteData.channelNo())) { ret = m_voice->process(data, len); } } @@ -524,7 +524,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len) if (!m_dedicatedControl) ret = m_data->process(data, len); else { - if (m_voiceOnControl && m_trunk->isChBusy(m_siteData.channelNo())) { + if (m_voiceOnControl && m_affiliations.isChBusy(m_siteData.channelNo())) { ret = m_data->process(data, len); } } @@ -686,7 +686,7 @@ void Control::clock(uint32_t ms) m_networkWatchdog.stop(); if (m_control) { - m_trunk->releaseDstIdGrant(m_voice->m_netLC.getDstId(), false); + m_affiliations.releaseGrant(m_voice->m_netLC.getDstId(), false); } if (m_dedicatedControl) { diff --git a/p25/Control.h b/p25/Control.h index 8a7fd6e3..583340d1 100644 --- a/p25/Control.h +++ b/p25/Control.h @@ -32,17 +32,18 @@ #define __P25_CONTROL_H__ #include "Defines.h" -#include "p25/packet/Trunk.h" -#include "p25/packet/Data.h" -#include "p25/packet/Voice.h" #include "p25/NID.h" #include "p25/SiteData.h" +#include "p25/packet/Data.h" +#include "p25/packet/Voice.h" +#include "p25/packet/Trunk.h" #include "network/BaseNetwork.h" #include "network/RemoteControl.h" #include "lookups/RSSIInterpolator.h" #include "lookups/IdenTableLookup.h" #include "lookups/RadioIdLookup.h" #include "lookups/TalkgroupIdLookup.h" +#include "p25/lookups/P25AffiliationLookup.h" #include "modem/Modem.h" #include "RingBuffer.h" #include "Timer.h" @@ -61,6 +62,7 @@ namespace p25 namespace packet { class HOST_SW_API Data; } namespace packet { class HOST_SW_API Trunk; } namespace dfsi { namespace packet { class HOST_SW_API DFSITrunk; } } + namespace lookups { class HOST_SW_API P25AffiliationLookup; } // --------------------------------------------------------------------------- // Class Declaration @@ -71,8 +73,8 @@ namespace p25 public: /// Initializes a new instance of the Control class. Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::BaseNetwork* network, - uint32_t timeout, uint32_t tgHang, bool duplex, lookups::RadioIdLookup* ridLookup, - lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper, + uint32_t timeout, uint32_t tgHang, bool duplex, ::lookups::RadioIdLookup* ridLookup, + ::lookups::TalkgroupIdLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper, bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose); /// Finalizes a instance of the Control class. ~Control(); @@ -112,6 +114,8 @@ namespace p25 NID nid() { return m_nid; } /// Gets instance of the Trunk class. packet::Trunk* trunk() { return m_trunk; } + /// Gets instance of the P25AffiliationLookup class. + lookups::P25AffiliationLookup affiliations() { return m_affiliations; } /// Flag indicating whether the processor or is busy or not. bool isBusy() const; @@ -128,6 +132,7 @@ namespace p25 friend class packet::Trunk; friend class dfsi::packet::DFSITrunk; packet::Trunk* m_trunk; + friend class lookups::P25AffiliationLookup; uint32_t m_nac; uint32_t m_txNAC; @@ -147,11 +152,12 @@ namespace p25 bool m_ackTSBKRequests; bool m_disableNetworkHDU; - lookups::IdenTableLookup* m_idenTable; - lookups::RadioIdLookup* m_ridLookup; - lookups::TalkgroupIdLookup* m_tidLookup; + ::lookups::IdenTableLookup* m_idenTable; + ::lookups::RadioIdLookup* m_ridLookup; + ::lookups::TalkgroupIdLookup* m_tidLookup; + lookups::P25AffiliationLookup m_affiliations; - lookups::IdenTable m_idenEntry; + ::lookups::IdenTable m_idenEntry; RingBuffer m_queue; @@ -182,7 +188,7 @@ namespace p25 SiteData m_siteData; - lookups::RSSIInterpolator* m_rssiMapper; + ::lookups::RSSIInterpolator* m_rssiMapper; uint8_t m_rssi; uint8_t m_maxRSSI; uint8_t m_minRSSI; diff --git a/p25/dfsi/packet/DFSITrunk.cpp b/p25/dfsi/packet/DFSITrunk.cpp index 868f06aa..049f78ec 100644 --- a/p25/dfsi/packet/DFSITrunk.cpp +++ b/p25/dfsi/packet/DFSITrunk.cpp @@ -26,6 +26,7 @@ #include "Defines.h" #include "p25/P25Defines.h" #include "p25/dfsi/DFSIDefines.h" +#include "p25/packet/Trunk.h" #include "p25/dfsi/packet/DFSITrunk.h" #include "p25/P25Utils.h" #include "p25/Sync.h" diff --git a/p25/dfsi/packet/DFSITrunk.h b/p25/dfsi/packet/DFSITrunk.h index ec249f10..11d3031d 100644 --- a/p25/dfsi/packet/DFSITrunk.h +++ b/p25/dfsi/packet/DFSITrunk.h @@ -28,7 +28,6 @@ #include "Defines.h" #include "p25/dfsi/LC.h" -#include "p25/packet/Trunk.h" #include "p25/Control.h" #include "network/BaseNetwork.h" diff --git a/p25/dfsi/packet/DFSIVoice.cpp b/p25/dfsi/packet/DFSIVoice.cpp index e9b8e7a0..313e7dbb 100644 --- a/p25/dfsi/packet/DFSIVoice.cpp +++ b/p25/dfsi/packet/DFSIVoice.cpp @@ -31,6 +31,8 @@ #include "p25/P25Defines.h" #include "p25/acl/AccessControl.h" #include "p25/dfsi/DFSIDefines.h" +#include "p25/packet/Trunk.h" +#include "p25/packet/Voice.h" #include "p25/dfsi/packet/DFSIVoice.h" #include "p25/P25Utils.h" #include "p25/Sync.h" @@ -295,7 +297,7 @@ bool DFSIVoice::process(uint8_t* data, uint32_t len) // verify the source RID is affiliated to the group TGID; only if control data // is supported if (group && m_p25->m_control) { - if (!m_p25->m_trunk->hasSrcIdGrpAff(srcId, dstId) && m_p25->m_trunk->m_verifyAff) { + if (!m_p25->m_affiliations.isGroupAff(srcId, dstId) && m_p25->m_trunk->m_verifyAff) { if (m_lastRejectId == 0 || m_lastRejectId != srcId) { LogWarning(LOG_RF, P25_HDU_STR " denial, RID not affiliated to TGID, srcId = %u, dstId = %u", srcId, dstId); m_p25->m_trunk->writeRF_TSDU_Deny(P25_DENY_RSN_REQ_UNIT_NOT_AUTH, TSBK_IOSP_GRP_VCH); @@ -321,11 +323,11 @@ bool DFSIVoice::process(uint8_t* data, uint32_t len) if (m_p25->m_control) { // if the group wasn't granted out -- explicitly grant the group - if (!m_p25->m_trunk->hasDstIdGranted(dstId)) { + if (!m_p25->m_affiliations.isGranted(dstId)) { if (m_p25->m_legacyGroupGrnt) { // are we auto-registering legacy radios to groups? if (m_p25->m_legacyGroupReg && group) { - if (!m_p25->m_trunk->hasSrcIdGrpAff(srcId, dstId)) { + if (!m_p25->m_affiliations.isGroupAff(srcId, dstId)) { if (!m_p25->m_trunk->writeRF_TSDU_Grp_Aff_Rsp(srcId, dstId)) { ::memset(m_dfsiLDU1, 0x00U, 9U * 25U); return false; @@ -414,7 +416,7 @@ bool DFSIVoice::process(uint8_t* data, uint32_t len) } if (m_p25->m_control) { - m_p25->m_trunk->touchDstIdGrant(m_rfLC.getDstId()); + m_p25->m_affiliations.touchGrant(m_rfLC.getDstId()); } // single-channel trunking or voice on control support? @@ -569,7 +571,7 @@ bool DFSIVoice::process(uint8_t* data, uint32_t len) else if (frameType == P25_DFSI_START_STOP) { if (m_rfDFSILC.getType() == P25_DFSI_TYPE_VOICE && m_rfDFSILC.getStartStop() == P25_DFSI_STOP_FLAG) { if (m_p25->m_control) { - m_p25->m_trunk->releaseDstIdGrant(m_rfLC.getDstId(), false); + m_p25->m_affiliations.releaseGrant(m_rfLC.getDstId(), false); } uint8_t data[P25_TDU_FRAME_LENGTH_BYTES + 2U]; @@ -772,7 +774,7 @@ bool DFSIVoice::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, dat } if (m_p25->m_control) { - m_p25->m_trunk->releaseDstIdGrant(m_netLC.getDstId(), false); + m_p25->m_affiliations.releaseGrant(m_netLC.getDstId(), false); } if (m_p25->m_netState != RS_NET_IDLE) { @@ -831,7 +833,7 @@ DFSIVoice::~DFSIVoice() void DFSIVoice::writeNet_TDU() { if (m_p25->m_control) { - m_p25->m_trunk->releaseDstIdGrant(m_netLC.getDstId(), false); + m_p25->m_affiliations.releaseGrant(m_netLC.getDstId(), false); } m_trunk->writeRF_DSFI_Stop(P25_DFSI_TYPE_VOICE); @@ -928,7 +930,7 @@ void DFSIVoice::writeNet_LDU1() } if (m_p25->m_control) { - m_p25->m_trunk->touchDstIdGrant(m_rfLC.getDstId()); + m_p25->m_affiliations.touchGrant(m_rfLC.getDstId()); } // set network and RF link control states diff --git a/p25/dfsi/packet/DFSIVoice.h b/p25/dfsi/packet/DFSIVoice.h index 3223b97e..f501a253 100644 --- a/p25/dfsi/packet/DFSIVoice.h +++ b/p25/dfsi/packet/DFSIVoice.h @@ -33,7 +33,6 @@ #include "Defines.h" #include "p25/dfsi/LC.h" #include "p25/dfsi/packet/DFSITrunk.h" -#include "p25/packet/Trunk.h" #include "p25/Control.h" #include "network/BaseNetwork.h" diff --git a/p25/lc/LC.h b/p25/lc/LC.h index 08612fc8..d08f6e0e 100644 --- a/p25/lc/LC.h +++ b/p25/lc/LC.h @@ -36,7 +36,6 @@ #include "p25/lc/TDULC.h" #include "p25/SiteData.h" #include "edac/RS634717.h" -#include "lookups/IdenTableLookup.h" #include diff --git a/p25/lc/TDULC.cpp b/p25/lc/TDULC.cpp index 34ad3293..ec13b161 100644 --- a/p25/lc/TDULC.cpp +++ b/p25/lc/TDULC.cpp @@ -58,7 +58,7 @@ TDULC::TDULC(const TDULC& data) : TDULC() /// /// /// -TDULC::TDULC(SiteData siteData, lookups::IdenTable entry) : TDULC(siteData) +TDULC::TDULC(SiteData siteData, ::lookups::IdenTable entry) : TDULC(siteData) { m_siteIdenEntry = entry; m_grpVchNo = m_siteData.channelNo(); @@ -70,7 +70,7 @@ TDULC::TDULC(SiteData siteData, lookups::IdenTable entry) : TDULC(siteData) /// /// /// -TDULC::TDULC(SiteData siteData, lookups::IdenTable entry, bool verbose) : TDULC(siteData) +TDULC::TDULC(SiteData siteData, ::lookups::IdenTable entry, bool verbose) : TDULC(siteData) { m_verbose = verbose; m_siteIdenEntry = entry; diff --git a/p25/lc/TDULC.h b/p25/lc/TDULC.h index fa3e4b64..97734b68 100644 --- a/p25/lc/TDULC.h +++ b/p25/lc/TDULC.h @@ -57,9 +57,9 @@ namespace p25 /// Initializes a copy instance of the TDULC class. TDULC(const TDULC& data); /// Initializes a new instance of the TDULC class. - TDULC(SiteData siteData, lookups::IdenTable entry); + TDULC(SiteData siteData, ::lookups::IdenTable entry); /// Initializes a new instance of the TDULC class. - TDULC(SiteData siteData, lookups::IdenTable entry, bool verbose); + TDULC(SiteData siteData, ::lookups::IdenTable entry, bool verbose); /// Initializes a new instance of the TDULC class. TDULC(LC* lc); /// Finalizes a instance of the TDULC class. @@ -123,7 +123,7 @@ namespace p25 /// Local Site Data. __PROPERTY_PLAIN(SiteData, siteData, siteData); /// Local Site Identity Entry. - __PROPERTY_PLAIN(lookups::IdenTable, siteIdenEntry, siteIdenEntry); + __PROPERTY_PLAIN(::lookups::IdenTable, siteIdenEntry, siteIdenEntry); private: /// Initializes a new instance of the TDULC class. diff --git a/p25/lc/TSBK.cpp b/p25/lc/TSBK.cpp index b4745a75..a29617ab 100644 --- a/p25/lc/TSBK.cpp +++ b/p25/lc/TSBK.cpp @@ -58,7 +58,7 @@ TSBK::TSBK(const TSBK& data) : TSBK() /// /// /// -TSBK::TSBK(SiteData siteData, lookups::IdenTable entry) : TSBK(siteData) +TSBK::TSBK(SiteData siteData, ::lookups::IdenTable entry) : TSBK(siteData) { m_siteIdenEntry = entry; } @@ -69,7 +69,7 @@ TSBK::TSBK(SiteData siteData, lookups::IdenTable entry) : TSBK(siteData) /// /// /// -TSBK::TSBK(SiteData siteData, lookups::IdenTable entry, bool verbose) : TSBK(siteData, entry, verbose, false) +TSBK::TSBK(SiteData siteData, ::lookups::IdenTable entry, bool verbose) : TSBK(siteData, entry, verbose, false) { m_verbose = verbose; } @@ -80,7 +80,7 @@ TSBK::TSBK(SiteData siteData, lookups::IdenTable entry, bool verbose) : TSBK(sit /// /// /// -TSBK::TSBK(SiteData siteData, lookups::IdenTable entry, bool verbose, bool warnCRC) : TSBK(siteData) +TSBK::TSBK(SiteData siteData, ::lookups::IdenTable entry, bool verbose, bool warnCRC) : TSBK(siteData) { m_warnCRC = warnCRC; m_siteIdenEntry = entry; diff --git a/p25/lc/TSBK.h b/p25/lc/TSBK.h index 56cc9453..6e1afbb4 100644 --- a/p25/lc/TSBK.h +++ b/p25/lc/TSBK.h @@ -68,11 +68,11 @@ namespace p25 /// Initializes a copy instance of the TSBK class. TSBK(const TSBK& data); /// Initializes a new instance of the TSBK class. - TSBK(SiteData siteData, lookups::IdenTable entry); + TSBK(SiteData siteData, ::lookups::IdenTable entry); /// Initializes a new instance of the TSBK class. - TSBK(SiteData siteData, lookups::IdenTable entry, bool verbose); + TSBK(SiteData siteData, ::lookups::IdenTable entry, bool verbose); /// Initializes a new instance of the TSBK class. - TSBK(SiteData siteData, lookups::IdenTable entry, bool verbose, bool warnCRC); + TSBK(SiteData siteData, ::lookups::IdenTable entry, bool verbose, bool warnCRC); /// Initializes a new instance of the TSBK class. TSBK(LC* lc); /// Finalizes a instance of the TSBK class. @@ -205,7 +205,7 @@ namespace p25 /// Local Site Data. __PROPERTY_PLAIN(SiteData, siteData, siteData); /// Local Site Identity Entry. - __PROPERTY_PLAIN(lookups::IdenTable, siteIdenEntry, siteIdenEntry); + __PROPERTY_PLAIN(::lookups::IdenTable, siteIdenEntry, siteIdenEntry); private: /// Initializes a new instance of the TSBK class. diff --git a/p25/lookups/P25AffiliationLookup.cpp b/p25/lookups/P25AffiliationLookup.cpp new file mode 100644 index 00000000..5eb3f78f --- /dev/null +++ b/p25/lookups/P25AffiliationLookup.cpp @@ -0,0 +1,97 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +/* +* Copyright (C) 2022 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include "p25/lookups/P25AffiliationLookup.h" +#include "p25/packet/Trunk.h" +#include "p25/Control.h" +#include "Log.h" + +using namespace p25::lookups; + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/// +/// Initializes a new instance of the P25AffiliationLookup class. +/// +/// Name of lookup table. +/// Flag indicating whether verbose logging is enabled. +P25AffiliationLookup::P25AffiliationLookup(Control* p25, bool verbose) : ::lookups::AffiliationLookup("P25 Aff", verbose), + m_p25(p25) +{ + /* stub */ +} + +/// +/// Finalizes a instance of the P25AffiliationLookup class. +/// +P25AffiliationLookup::~P25AffiliationLookup() +{ + /* stub */ +} + +/// +/// Helper to release the channel grant for the destination ID. +/// +/// +/// +bool P25AffiliationLookup::releaseGrant(uint32_t dstId, bool releaseAll) +{ + bool ret = ::lookups::AffiliationLookup::releaseGrant(dstId, releaseAll); + if (ret) { + if (m_rfGrantChCnt > 0U) { + m_p25->m_siteData.setChCnt(getRFChCnt() + m_rfGrantChCnt); + } + else { + m_p25->m_siteData.setChCnt(getRFChCnt()); + } + } + + return ret; +} + +/// +/// Helper to release group affiliations. +/// +/// +/// +std::vector P25AffiliationLookup::clearGroupAff(uint32_t dstId, bool releaseAll) +{ + std::vector srcToRel = ::lookups::AffiliationLookup::clearGroupAff(dstId, releaseAll); + if (srcToRel.size() > 0U) { + // release affiliations + for (auto it = srcToRel.begin(); it != srcToRel.end(); ++it) { + m_p25->m_trunk->writeRF_TSDU_U_Dereg_Ack(*it); + } + } + + return srcToRel; +} diff --git a/p25/lookups/P25AffiliationLookup.h b/p25/lookups/P25AffiliationLookup.h new file mode 100644 index 00000000..03fc9673 --- /dev/null +++ b/p25/lookups/P25AffiliationLookup.h @@ -0,0 +1,66 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +/* +* Copyright (C) 2022 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#if !defined(__P25_AFFILIATION_LOOKUP_H__) +#define __P25_AFFILIATION_LOOKUP_H__ + +#include "Defines.h" +#include "lookups/AffiliationLookup.h" + +namespace p25 +{ + // --------------------------------------------------------------------------- + // Class Prototypes + // --------------------------------------------------------------------------- + + class HOST_SW_API Control; + + namespace lookups + { + // --------------------------------------------------------------------------- + // Class Declaration + // Implements a lookup table class that contains subscriber registration + // and group affiliation information. + // --------------------------------------------------------------------------- + + class HOST_SW_API P25AffiliationLookup : public ::lookups::AffiliationLookup { + public: + /// Initializes a new instance of the P25AffiliationLookup class. + P25AffiliationLookup(Control* p25, bool verbose); + /// Finalizes a instance of the P25AffiliationLookup class. + virtual ~P25AffiliationLookup(); + + /// Helper to release the channel grant for the destination ID. + virtual bool releaseGrant(uint32_t dstId, bool releaseAll); + /// Helper to release group affiliations. + virtual std::vector clearGroupAff(uint32_t dstId, bool releaseAll); + + protected: + Control* m_p25; + }; + } // namespace lookups +} // namespace p25 + +#endif // __P25_AFFILIATION_LOOKUP_H__ diff --git a/p25/packet/Data.cpp b/p25/packet/Data.cpp index 82660ae2..c0e95efa 100644 --- a/p25/packet/Data.cpp +++ b/p25/packet/Data.cpp @@ -31,6 +31,7 @@ #include "Defines.h" #include "p25/P25Defines.h" #include "p25/packet/Data.h" +#include "p25/packet/Trunk.h" #include "p25/acl/AccessControl.h" #include "p25/P25Utils.h" #include "p25/Sync.h" diff --git a/p25/packet/Trunk.cpp b/p25/packet/Trunk.cpp index 3fd8fbb8..a2244263 100644 --- a/p25/packet/Trunk.cpp +++ b/p25/packet/Trunk.cpp @@ -25,8 +25,10 @@ */ #include "Defines.h" #include "p25/P25Defines.h" +#include "p25/packet/Voice.h" #include "p25/packet/Trunk.h" #include "p25/acl/AccessControl.h" +#include "p25/lookups/P25AffiliationLookup.h" #include "p25/P25Utils.h" #include "p25/Sync.h" #include "edac/CRC.h" @@ -85,7 +87,7 @@ using namespace p25::packet; // Verify the source RID is registered. #define VERIFY_SRCID_REG(_PCKT_STR, _PCKT, _SRCID) \ - if (!hasSrcIdUnitReg(_SRCID) && m_verifyReg) { \ + if (!m_p25->m_affiliations.isUnitReg(_SRCID) && m_verifyReg) { \ LogWarning(LOG_RF, P25_TSDU_STR ", " _PCKT_STR " denial, RID not registered, srcId = %u", _SRCID); \ writeRF_TSDU_Deny(P25_DENY_RSN_REQ_UNIT_NOT_AUTH, _PCKT); \ writeRF_TSDU_U_Reg_Cmd(_SRCID); \ @@ -95,7 +97,7 @@ using namespace p25::packet; // Verify the source RID is affiliated. #define VERIFY_SRCID_AFF(_PCKT_STR, _PCKT, _SRCID, _DSTID) \ - if (!hasSrcIdGrpAff(_SRCID, _DSTID) && m_verifyAff) { \ + if (!m_p25->m_affiliations.isGroupAff(_SRCID, _DSTID) && m_verifyAff) { \ LogWarning(LOG_RF, P25_TSDU_STR ", " _PCKT_STR " denial, RID not affiliated to TGID, srcId = %u, dstId = %u", _SRCID, _DSTID); \ writeRF_TSDU_Deny(P25_DENY_RSN_REQ_UNIT_NOT_AUTH, _PCKT); \ writeRF_TSDU_U_Reg_Cmd(_SRCID); \ @@ -617,8 +619,8 @@ bool Trunk::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L } // is the specified channel granted? - if (isChBusy(chNo) && hasDstIdGranted(dstId)) { - releaseDstIdGrant(dstId, false); + if (m_p25->m_affiliations.isChBusy(chNo) && m_p25->m_affiliations.isGranted(dstId)) { + m_p25->m_affiliations.releaseGrant(dstId, false); } } break; @@ -641,7 +643,7 @@ bool Trunk::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L } // workaround for single channel dedicated sites to pass network traffic on a lone VC - if (m_p25->m_dedicatedControl && !m_p25->m_voiceOnControl && m_voiceChTable.size() == 1U) { + if (m_p25->m_dedicatedControl && !m_p25->m_voiceOnControl && m_p25->m_affiliations.getRFChCnt() == 1U) { m_rfTSBK.setSrcId(srcId); m_rfTSBK.setDstId(dstId); @@ -660,7 +662,7 @@ bool Trunk::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L } // workaround for single channel dedicated sites to pass network traffic on a lone VC - if (m_p25->m_dedicatedControl && !m_p25->m_voiceOnControl && m_voiceChTable.size() == 1U) { + if (m_p25->m_dedicatedControl && !m_p25->m_voiceOnControl && m_p25->m_affiliations.getRFChCnt() == 1U) { m_rfTSBK.setSrcId(srcId); m_rfTSBK.setDstId(dstId); @@ -865,194 +867,6 @@ void Trunk::writeAdjSSNetwork() } } -/// -/// Helper to determine if the source ID has affiliated to the group destination ID. -/// -/// -/// -/// -bool Trunk::hasSrcIdGrpAff(uint32_t srcId, uint32_t dstId) const -{ - // lookup dynamic affiliation table entry - try { - uint32_t tblDstId = m_grpAffTable.at(srcId); - if (tblDstId == dstId) { - return true; - } - else { - return false; - } - } catch (...) { - return false; - } -} - -/// -/// Helper to determine if the source ID has unit registered. -/// -/// -/// -bool Trunk::hasSrcIdUnitReg(uint32_t srcId) const -{ - // lookup dynamic unit registration table entry - if (std::find(m_unitRegTable.begin(), m_unitRegTable.end(), srcId) != m_unitRegTable.end()) { - return true; - } - else { - return false; - } -} - -/// -/// Helper to determine if the channel number is busy. -/// -/// -/// -bool Trunk::isChBusy(uint32_t chNo) const -{ - if (chNo == 0U) { - return false; - } - - // lookup dynamic channel grant table entry - for (auto it = m_grantChTable.begin(); it != m_grantChTable.end(); ++it) { - if (it->second == chNo) { - return true; - } - } - - return false; -} - -/// -/// Helper to determine if the destination ID is already granted. -/// -/// -/// -bool Trunk::hasDstIdGranted(uint32_t dstId) const -{ - if (dstId == 0U) { - return false; - } - - // lookup dynamic channel grant table entry - try { - uint32_t chNo = m_grantChTable.at(dstId); - if (chNo != 0U) { - return true; - } - else { - return false; - } - } catch (...) { - return false; - } -} - -/// -/// Helper to start the destination ID grant timer. -/// -/// -/// -void Trunk::touchDstIdGrant(uint32_t dstId) -{ - if (dstId == 0U) { - return; - } - - if (hasDstIdGranted(dstId)) { - m_grantTimers[dstId].start(); - } -} - -/// -/// Helper to release the channel grant for the destination ID. -/// -/// -/// -void Trunk::releaseDstIdGrant(uint32_t dstId, bool releaseAll) -{ - if (dstId == 0U && !releaseAll) { - return; - } - - if (dstId == 0U && releaseAll) { - LogWarning(LOG_RF, "P25, force releasing all channel grants"); - - std::vector gntsToRel = std::vector(); - for (auto it = m_grantChTable.begin(); it != m_grantChTable.end(); ++it) { - uint32_t dstId = it->first; - gntsToRel.push_back(dstId); - } - - // release grants - for (auto it = gntsToRel.begin(); it != gntsToRel.end(); ++it) { - releaseDstIdGrant(*it, false); - } - - return; - } - - if (hasDstIdGranted(dstId)) { - uint32_t chNo = m_grantChTable.at(dstId); - - if (m_verbose) { - LogMessage(LOG_RF, "P25, releasing channel grant, chNo = %u, dstId = %u", - chNo, dstId); - } - - m_grantChTable[dstId] = 0U; - m_voiceChTable.push_back(chNo); - - if (m_voiceGrantChCnt > 0U) { - m_voiceGrantChCnt--; - m_p25->m_siteData.setChCnt(m_voiceChCnt + m_voiceGrantChCnt); - } - else { - m_voiceGrantChCnt = 0U; - m_p25->m_siteData.setChCnt(m_voiceChCnt); - } - - m_grantTimers[dstId].stop(); - } -} - -/// -/// Helper to release group affiliations. -/// -/// -/// -void Trunk::clearGrpAff(uint32_t dstId, bool releaseAll) -{ - if (dstId == 0U && !releaseAll) { - return; - } - - std::vector srcToRel = std::vector(); - if (dstId == 0U && releaseAll) { - LogWarning(LOG_RF, "P25, releasing all group affiliations"); - for (auto it = m_grpAffTable.begin(); it != m_grpAffTable.end(); ++it) { - uint32_t srcId = it->first; - srcToRel.push_back(srcId); - } - } - else { - LogWarning(LOG_RF, "P25, releasing group affiliations, dstId = %u", dstId); - for (auto it = m_grpAffTable.begin(); it != m_grpAffTable.end(); ++it) { - uint32_t srcId = it->first; - uint32_t grpId = it->second; - if (grpId == dstId) { - srcToRel.push_back(srcId); - } - } - } - - // release affiliations - for (auto it = srcToRel.begin(); it != srcToRel.end(); ++it) { - writeRF_TSDU_U_Dereg_Ack(*it); - } -} - /// /// Updates the processor by the passed number of milliseconds. /// @@ -1079,20 +893,7 @@ void Trunk::clock(uint32_t ms) } // clock all the grant timers - std::vector gntsToRel = std::vector(); - for (auto it = m_grantChTable.begin(); it != m_grantChTable.end(); ++it) { - uint32_t dstId = it->first; - - m_grantTimers[dstId].clock(ms); - if (m_grantTimers[dstId].isRunning() && m_grantTimers[dstId].hasExpired()) { - gntsToRel.push_back(dstId); - } - } - - // release grants that have timed out - for (auto it = gntsToRel.begin(); it != gntsToRel.end(); ++it) { - releaseDstIdGrant(*it, false); - } + m_p25->m_affiliations.clock(ms); // clock adjacent site and SCCB update timers m_adjSiteUpdateTimer.clock(ms); @@ -1340,25 +1141,18 @@ Trunk::Trunk(Control* p25, network::BaseNetwork* network, bool dumpTSBKData, boo m_patchSuperGroup(0xFFFFU), m_verifyAff(false), m_verifyReg(false), - m_rfTSBK(SiteData(), lookups::IdenTable()), - m_netTSBK(SiteData(), lookups::IdenTable()), + m_rfTSBK(SiteData(), ::lookups::IdenTable()), + m_netTSBK(SiteData(), ::lookups::IdenTable()), m_rfMBF(NULL), m_mbfCnt(0U), m_mbfIdenCnt(0U), m_mbfAdjSSCnt(0U), m_mbfSCCBCnt(0U), m_mbfGrpGrntCnt(0U), - m_voiceChTable(), m_adjSiteTable(), m_adjSiteUpdateCnt(), m_sccbTable(), m_sccbUpdateCnt(), - m_unitRegTable(), - m_grpAffTable(), - m_grantChTable(), - m_grantTimers(), - m_voiceChCnt(1U), - m_voiceGrantChCnt(0U), m_noStatusAck(false), m_noMessageAck(true), m_unitToUnitAvailCheck(true), @@ -1375,20 +1169,12 @@ Trunk::Trunk(Control* p25, network::BaseNetwork* network, bool dumpTSBKData, boo { m_rfMBF = new uint8_t[P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U]; ::memset(m_rfMBF, 0x00U, P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U); - - m_voiceChTable.clear(); m_adjSiteTable.clear(); m_adjSiteUpdateCnt.clear(); m_sccbTable.clear(); m_sccbUpdateCnt.clear(); - - m_unitRegTable.clear(); - m_grpAffTable.clear(); - - m_grantChTable.clear(); - m_grantTimers.clear(); m_adjSiteUpdateInterval = ADJ_SITE_TIMER_TIMEOUT; m_adjSiteUpdateTimer.setTimeout(m_adjSiteUpdateInterval); @@ -1489,7 +1275,7 @@ void Trunk::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS) break; /** update data */ case 4: - if (m_grantChTable.size() > 0) { + if (m_p25->m_affiliations.grantSize() > 0) { queueRF_TSBK_Ctrl(TSBK_OSP_GRP_VCH_GRANT_UPD); } break; @@ -1542,7 +1328,7 @@ void Trunk::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS) // pad MBF if we have 2 queued TSDUs if (m_mbfCnt == 2U) { - std::vector entries = m_p25->m_idenTable->list(); + std::vector<::lookups::IdenTable> entries = m_p25->m_idenTable->list(); if (entries.size() > 1U) { queueRF_TSBK_Ctrl(TSBK_OSP_IDEN_UP); } @@ -1848,8 +1634,8 @@ void Trunk::queueRF_TSBK_Ctrl(uint8_t lco) switch (lco) { case TSBK_OSP_GRP_VCH_GRANT_UPD: // write group voice grant update - if (m_grantChTable.size() > 0) { - if (m_mbfGrpGrntCnt >= m_grantChTable.size()) + if (m_p25->m_affiliations.grantSize() > 0) { + if (m_mbfGrpGrntCnt >= m_p25->m_affiliations.grantSize()) m_mbfGrpGrntCnt = 0U; if (m_debug) { @@ -1858,7 +1644,9 @@ void Trunk::queueRF_TSBK_Ctrl(uint8_t lco) bool noData = false; uint8_t i = 0U; - for (auto it = m_grantChTable.begin(); it != m_grantChTable.end(); ++it) { + std::unordered_map grantTable = m_p25->m_affiliations.grantTable(); + for (auto it = grantTable.begin(); it != grantTable.end(); ++it) + { // no good very bad way of skipping entries... if (i != m_mbfGrpGrntCnt) { i++; @@ -1899,7 +1687,7 @@ void Trunk::queueRF_TSBK_Ctrl(uint8_t lco) LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_OSP_IDEN_UP (Identity Update)"); } - std::vector entries = m_p25->m_idenTable->list(); + std::vector<::lookups::IdenTable> entries = m_p25->m_idenTable->list(); if (m_mbfIdenCnt >= entries.size()) m_mbfIdenCnt = 0U; @@ -1911,7 +1699,7 @@ void Trunk::queueRF_TSBK_Ctrl(uint8_t lco) continue; } else { - lookups::IdenTable entry = *it; + ::lookups::IdenTable entry = *it; // LogDebug(LOG_P25, "baseFrequency = %uHz, txOffsetMhz = %fMHz, chBandwidthKhz = %fKHz, chSpaceKhz = %fKHz", // entry.baseFrequency(), entry.txOffsetMhz(), entry.chBandwidthKhz(), entry.chSpaceKhz()); @@ -2151,8 +1939,8 @@ bool Trunk::writeRF_TSDU_Grant(bool grp, bool skip, bool net, bool skipNetCheck) } } - if (!hasDstIdGranted(m_rfTSBK.getDstId())) { - if (m_voiceChTable.empty()) { + if (!m_p25->m_affiliations.isGranted(m_rfTSBK.getDstId())) { + if (!m_p25->m_affiliations.isRFChAvailable()) { if (grp) { if (!net) { LogWarning(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_GRP_VCH (Group Voice Channel Request) queued, no channels available, dstId = %u", m_rfTSBK.getDstId()); @@ -2179,27 +1967,20 @@ bool Trunk::writeRF_TSDU_Grant(bool grp, bool skip, bool net, bool skipNetCheck) } } else { - uint32_t chNo = m_voiceChTable.at(0); - auto it = std::find(m_voiceChTable.begin(), m_voiceChTable.end(), chNo); - m_voiceChTable.erase(it); - - m_grantChTable[m_rfTSBK.getDstId()] = chNo; - m_rfTSBK.setGrpVchNo(chNo); - m_rfTSBK.setDataChnNo(chNo); - - m_grantTimers[m_rfTSBK.getDstId()] = Timer(1000U, GRANT_TIMER_TIMEOUT); - m_grantTimers[m_rfTSBK.getDstId()].start(); - - m_voiceGrantChCnt++; - m_p25->m_siteData.setChCnt(m_voiceChCnt + m_voiceGrantChCnt); + if (m_p25->m_affiliations.grantCh(m_rfTSBK.getDstId(), GRANT_TIMER_TIMEOUT)) { + uint32_t chNo = m_p25->m_affiliations.getGrantedCh(m_rfTSBK.getDstId()); + m_rfTSBK.setGrpVchNo(chNo); + m_rfTSBK.setDataChnNo(chNo); + m_p25->m_siteData.setChCnt(m_p25->m_affiliations.getRFChCnt() + m_p25->m_affiliations.getGrantedRFChCnt()); + } } } else { - uint32_t chNo = m_grantChTable[m_rfTSBK.getDstId()]; + uint32_t chNo = m_p25->m_affiliations.getGrantedCh(m_rfTSBK.getDstId()); m_rfTSBK.setGrpVchNo(chNo); m_rfTSBK.setDataChnNo(chNo); - m_grantTimers[m_rfTSBK.getDstId()].start(); + m_p25->m_affiliations.touchGrant(m_rfTSBK.getDstId()); } } @@ -2267,8 +2048,8 @@ bool Trunk::writeRF_TSDU_SNDCP_Grant(bool skip, bool net) return false; } - if (!hasDstIdGranted(m_rfTSBK.getSrcId())) { - if (m_voiceChTable.empty()) { + if (!m_p25->m_affiliations.isGranted(m_rfTSBK.getSrcId())) { + if (!m_p25->m_affiliations.isRFChAvailable()) { if (!net) { LogWarning(LOG_RF, P25_TSDU_STR ", TSBK_ISP_SNDCP_CH_REQ (SNDCP Data Channel Request) queued, no channels available, srcId = %u", m_rfTSBK.getSrcId()); writeRF_TSDU_Queue(P25_QUE_RSN_CHN_RESOURCE_NOT_AVAIL, TSBK_ISP_SNDCP_CH_REQ); @@ -2281,27 +2062,20 @@ bool Trunk::writeRF_TSDU_SNDCP_Grant(bool skip, bool net) return false; } else { - uint32_t chNo = m_voiceChTable.at(0); - auto it = std::find(m_voiceChTable.begin(), m_voiceChTable.end(), chNo); - m_voiceChTable.erase(it); - - m_grantChTable[m_rfTSBK.getSrcId()] = chNo; - m_rfTSBK.setGrpVchNo(chNo); - m_rfTSBK.setDataChnNo(chNo); - - m_grantTimers[m_rfTSBK.getSrcId()] = Timer(1000U, GRANT_TIMER_TIMEOUT); - m_grantTimers[m_rfTSBK.getSrcId()].start(); - - m_voiceGrantChCnt++; - m_p25->m_siteData.setChCnt(m_voiceChCnt + m_voiceGrantChCnt); + if (m_p25->m_affiliations.grantCh(m_rfTSBK.getSrcId(), GRANT_TIMER_TIMEOUT)) { + uint32_t chNo = m_p25->m_affiliations.getGrantedCh(m_rfTSBK.getSrcId()); + m_rfTSBK.setGrpVchNo(chNo); + m_rfTSBK.setDataChnNo(chNo); + m_p25->m_siteData.setChCnt(m_p25->m_affiliations.getRFChCnt() + m_p25->m_affiliations.getGrantedRFChCnt()); + } } } else { - uint32_t chNo = m_grantChTable[m_rfTSBK.getSrcId()]; + uint32_t chNo = m_p25->m_affiliations.getGrantedCh(m_rfTSBK.getSrcId()); m_rfTSBK.setGrpVchNo(chNo); m_rfTSBK.setDataChnNo(chNo); - m_grantTimers[m_rfTSBK.getSrcId()].start(); + m_p25->m_affiliations.touchGrant(m_rfTSBK.getSrcId()); } } @@ -2426,7 +2200,7 @@ bool Trunk::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId) } // validate the source RID is registered - if (!hasSrcIdUnitReg(srcId) && m_verifyReg) { + if (!m_p25->m_affiliations.isUnitReg(srcId) && m_verifyReg) { LogWarning(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_GRP_AFF (Group Affiliation Response) denial, RID not registered, srcId = %u", srcId); ::ActivityLog("P25", true, "group affiliation request from %u to %s %u denied", srcId, "TG ", dstId); m_rfTSBK.setResponse(P25_RSP_REFUSED); @@ -2456,7 +2230,7 @@ bool Trunk::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId) ret = true; // update dynamic affiliation table - m_grpAffTable[srcId] = dstId; + m_p25->m_affiliations.groupAff(srcId, dstId); } writeRF_TSDU_SBF(false); @@ -2495,8 +2269,8 @@ void Trunk::writeRF_TSDU_U_Reg_Rsp(uint32_t srcId) ::ActivityLog("P25", true, "unit registration request from %u", srcId); // update dynamic unit registration table - if (!hasSrcIdUnitReg(srcId)) { - m_unitRegTable.push_back(srcId); + if (!m_p25->m_affiliations.isUnitReg(srcId)) { + m_p25->m_affiliations.unitReg(srcId); } } @@ -2527,21 +2301,7 @@ void Trunk::writeRF_TSDU_U_Dereg_Ack(uint32_t srcId) } // remove dynamic unit registration table entry - if (std::find(m_unitRegTable.begin(), m_unitRegTable.end(), srcId) != m_unitRegTable.end()) { - auto it = std::find(m_unitRegTable.begin(), m_unitRegTable.end(), srcId); - m_unitRegTable.erase(it); - dereged = true; - } - - // remove dynamic affiliation table entry - try { - m_grpAffTable.at(srcId); - m_grpAffTable.erase(srcId); - dereged = true; - } - catch (...) { - // stub - } + dereged = m_p25->m_affiliations.unitDereg(srcId); if (dereged) { ::ActivityLog("P25", true, "unit deregistration request from %u", srcId); @@ -2604,7 +2364,7 @@ bool Trunk::writeRF_TSDU_Loc_Reg_Rsp(uint32_t srcId, uint32_t dstId) } // validate the source RID is registered - if (!hasSrcIdUnitReg(srcId)) { + if (!m_p25->m_affiliations.isUnitReg(srcId)) { LogWarning(LOG_RF, P25_TSDU_STR ", TSBK_OSP_LOC_REG_RSP (Location Registration Response) denial, RID not registered, srcId = %u", srcId); ::ActivityLog("P25", true, "location registration request from %u denied", srcId); writeRF_TSDU_U_Reg_Cmd(srcId); diff --git a/p25/packet/Trunk.h b/p25/packet/Trunk.h index aac1870a..904177c7 100644 --- a/p25/packet/Trunk.h +++ b/p25/packet/Trunk.h @@ -50,6 +50,7 @@ namespace p25 namespace packet { class HOST_SW_API Voice; } namespace dfsi { namespace packet { class HOST_SW_API DFSIVoice; } } namespace packet { class HOST_SW_API Data; } + namespace lookups { class HOST_SW_API P25AffiliationLookup; } class HOST_SW_API Control; namespace packet @@ -77,22 +78,6 @@ namespace p25 /// Helper to write P25 adjacent site information to the network. void writeAdjSSNetwork(); - /// Helper to determine if the source ID has affiliated to the group destination ID. - bool hasSrcIdGrpAff(uint32_t srcId, uint32_t dstId) const; - /// Helper to determine if the source ID has unit registered. - bool hasSrcIdUnitReg(uint32_t srcId) const; - - /// Helper to determine if the channel number is busy. - bool isChBusy(uint32_t chNo) const; - /// Helper to determine if the destination ID is already granted. - bool hasDstIdGranted(uint32_t dstId) const; - /// Helper to start the destination ID grant timer. - void touchDstIdGrant(uint32_t dstId); - /// Helper to release the channel grant for the destination ID. - void releaseDstIdGrant(uint32_t dstId, bool releaseAll); - /// Helper to release group affiliations. - void clearGrpAff(uint32_t dstId, bool releaseAll); - /// Updates the processor by the passed number of milliseconds. void clock(uint32_t ms); @@ -124,6 +109,7 @@ namespace p25 friend class packet::Data; friend class p25::Control; Control* m_p25; + friend class lookups::P25AffiliationLookup; network::BaseNetwork* m_network; @@ -142,23 +128,12 @@ namespace p25 uint8_t m_mbfSCCBCnt; uint8_t m_mbfGrpGrntCnt; - std::vector m_voiceChTable; - std::unordered_map m_adjSiteTable; std::unordered_map m_adjSiteUpdateCnt; std::unordered_map m_sccbTable; std::unordered_map m_sccbUpdateCnt; - std::vector m_unitRegTable; - std::unordered_map m_grpAffTable; - - std::unordered_map m_grantChTable; - std::unordered_map m_grantTimers; - - uint8_t m_voiceChCnt; - uint8_t m_voiceGrantChCnt; - bool m_noStatusAck; bool m_noMessageAck; bool m_unitToUnitAvailCheck; diff --git a/p25/packet/Voice.cpp b/p25/packet/Voice.cpp index b6b835f7..14955144 100644 --- a/p25/packet/Voice.cpp +++ b/p25/packet/Voice.cpp @@ -31,6 +31,7 @@ #include "Defines.h" #include "p25/P25Defines.h" #include "p25/packet/Voice.h" +#include "p25/packet/Trunk.h" #include "p25/acl/AccessControl.h" #include "p25/dfsi/DFSIDefines.h" #include "p25/P25Utils.h" @@ -316,7 +317,7 @@ bool Voice::process(uint8_t* data, uint32_t len) // verify the source RID is affiliated to the group TGID; only if control data // is supported if (group && m_p25->m_control) { - if (!m_p25->m_trunk->hasSrcIdGrpAff(srcId, dstId) && m_p25->m_trunk->m_verifyAff) { + if (!m_p25->m_affiliations.isGroupAff(srcId, dstId) && m_p25->m_trunk->m_verifyAff) { if (m_lastRejectId == 0 || m_lastRejectId != srcId) { LogWarning(LOG_RF, P25_HDU_STR " denial, RID not affiliated to TGID, srcId = %u, dstId = %u", srcId, dstId); m_p25->m_trunk->writeRF_TSDU_Deny(P25_DENY_RSN_REQ_UNIT_NOT_AUTH, TSBK_IOSP_GRP_VCH); @@ -341,11 +342,11 @@ bool Voice::process(uint8_t* data, uint32_t len) if (m_p25->m_control) { // if the group wasn't granted out -- explicitly grant the group - if (!m_p25->m_trunk->hasDstIdGranted(dstId)) { + if (!m_p25->m_affiliations.isGranted(dstId)) { if (m_p25->m_legacyGroupGrnt) { // are we auto-registering legacy radios to groups? if (m_p25->m_legacyGroupReg && group) { - if (!m_p25->m_trunk->hasSrcIdGrpAff(srcId, dstId)) { + if (!m_p25->m_affiliations.isGroupAff(srcId, dstId)) { if (!m_p25->m_trunk->writeRF_TSDU_Grp_Aff_Rsp(srcId, dstId)) { return false; } @@ -471,7 +472,7 @@ bool Voice::process(uint8_t* data, uint32_t len) alreadyDecoded = false; if (m_p25->m_control) { - m_p25->m_trunk->touchDstIdGrant(m_rfLC.getDstId()); + m_p25->m_affiliations.touchGrant(m_rfLC.getDstId()); } // single-channel trunking or voice on control support? @@ -627,7 +628,7 @@ bool Voice::process(uint8_t* data, uint32_t len) } else if (duid == P25_DUID_TDU || duid == P25_DUID_TDULC) { if (m_p25->m_control) { - m_p25->m_trunk->releaseDstIdGrant(m_rfLC.getDstId(), false); + m_p25->m_affiliations.releaseGrant(m_rfLC.getDstId(), false); } if (duid == P25_DUID_TDU) { @@ -829,7 +830,7 @@ bool Voice::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, data::L } if (m_p25->m_control) { - m_p25->m_trunk->releaseDstIdGrant(m_netLC.getDstId(), false); + m_p25->m_affiliations.releaseGrant(m_netLC.getDstId(), false); } if (m_p25->m_netState != RS_NET_IDLE) { @@ -967,7 +968,7 @@ void Voice::writeRF_EndOfVoice() void Voice::writeNet_TDU() { if (m_p25->m_control) { - m_p25->m_trunk->releaseDstIdGrant(m_netLC.getDstId(), false); + m_p25->m_affiliations.releaseGrant(m_netLC.getDstId(), false); } uint8_t buffer[P25_TDU_FRAME_LENGTH_BYTES + 2U]; @@ -1096,7 +1097,7 @@ void Voice::writeNet_LDU1() } if (m_p25->m_control) { - m_p25->m_trunk->touchDstIdGrant(m_rfLC.getDstId()); + m_p25->m_affiliations.touchGrant(m_rfLC.getDstId()); } // set network and RF link control states