split unit registration, group affiliation and group grant handling into its own AffiliationLookup class; implement some more NXDN ISP/OSP trunking messages; reorganize how header includes were being done slightly;

pull/12/head
Bryan Biedenkapp 4 years ago
parent b64b5b12ba
commit 905b49736a

@ -59,6 +59,7 @@ HOST_OBJECTS = \
dmr/Slot.o \ dmr/Slot.o \
dmr/SlotType.o \ dmr/SlotType.o \
dmr/Sync.o \ dmr/Sync.o \
lookups/AffiliationLookup.o \
lookups/IdenTableLookup.o \ lookups/IdenTableLookup.o \
lookups/RadioIdLookup.o \ lookups/RadioIdLookup.o \
lookups/RSSIInterpolator.o \ lookups/RSSIInterpolator.o \
@ -90,6 +91,7 @@ HOST_OBJECTS = \
p25/lc/LC.o \ p25/lc/LC.o \
p25/lc/TDULC.o \ p25/lc/TDULC.o \
p25/lc/TSBK.o \ p25/lc/TSBK.o \
p25/lookups/P25AffiliationLookup.o \
p25/packet/Data.o \ p25/packet/Data.o \
p25/packet/Trunk.o \ p25/packet/Trunk.o \
p25/packet/Voice.o \ p25/packet/Voice.o \

@ -32,6 +32,7 @@
#include "dmr/lc/ShortLC.h" #include "dmr/lc/ShortLC.h"
#include "dmr/lc/FullLC.h" #include "dmr/lc/FullLC.h"
#include "dmr/lc/CSBK.h" #include "dmr/lc/CSBK.h"
#include "dmr/Slot.h"
#include "dmr/SlotType.h" #include "dmr/SlotType.h"
#include "dmr/Sync.h" #include "dmr/Sync.h"
#include "edac/BPTC19696.h" #include "edac/BPTC19696.h"

@ -35,7 +35,6 @@
#include "dmr/data/Data.h" #include "dmr/data/Data.h"
#include "dmr/data/EmbeddedData.h" #include "dmr/data/EmbeddedData.h"
#include "dmr/lc/LC.h" #include "dmr/lc/LC.h"
#include "dmr/Slot.h"
#include "modem/Modem.h" #include "modem/Modem.h"
#include "network/BaseNetwork.h" #include "network/BaseNetwork.h"
#include "RingBuffer.h" #include "RingBuffer.h"

@ -31,6 +31,7 @@
#include "dmr/lc/ShortLC.h" #include "dmr/lc/ShortLC.h"
#include "dmr/lc/FullLC.h" #include "dmr/lc/FullLC.h"
#include "dmr/lc/CSBK.h" #include "dmr/lc/CSBK.h"
#include "dmr/Slot.h"
#include "dmr/SlotType.h" #include "dmr/SlotType.h"
#include "dmr/Sync.h" #include "dmr/Sync.h"
#include "edac/BPTC19696.h" #include "edac/BPTC19696.h"

@ -36,7 +36,6 @@
#include "dmr/data/DataHeader.h" #include "dmr/data/DataHeader.h"
#include "dmr/data/EmbeddedData.h" #include "dmr/data/EmbeddedData.h"
#include "dmr/lc/LC.h" #include "dmr/lc/LC.h"
#include "dmr/Slot.h"
#include "edac/AMBEFEC.h" #include "edac/AMBEFEC.h"
#include "modem/Modem.h" #include "modem/Modem.h"
#include "network/BaseNetwork.h" #include "network/BaseNetwork.h"

@ -24,6 +24,7 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include "Defines.h" #include "Defines.h"
#include "dmr/packet/Data.h"
#include "dmr/packet/Voice.h" #include "dmr/packet/Voice.h"
#include "dmr/acl/AccessControl.h" #include "dmr/acl/AccessControl.h"
#include "dmr/data/DataHeader.h" #include "dmr/data/DataHeader.h"
@ -32,6 +33,7 @@
#include "dmr/lc/CSBK.h" #include "dmr/lc/CSBK.h"
#include "dmr/lc/ShortLC.h" #include "dmr/lc/ShortLC.h"
#include "dmr/lc/FullLC.h" #include "dmr/lc/FullLC.h"
#include "dmr/Slot.h"
#include "dmr/SlotType.h" #include "dmr/SlotType.h"
#include "dmr/Sync.h" #include "dmr/Sync.h"
#include "edac/BPTC19696.h" #include "edac/BPTC19696.h"

@ -36,7 +36,6 @@
#include "dmr/data/EmbeddedData.h" #include "dmr/data/EmbeddedData.h"
#include "dmr/lc/LC.h" #include "dmr/lc/LC.h"
#include "dmr/lc/PrivacyLC.h" #include "dmr/lc/PrivacyLC.h"
#include "dmr/Slot.h"
#include "edac/AMBEFEC.h" #include "edac/AMBEFEC.h"
#include "network/BaseNetwork.h" #include "network/BaseNetwork.h"
#include "lookups/RadioIdLookup.h" #include "lookups/RadioIdLookup.h"

@ -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 <cassert>
#include <cstdio>
#include <cstring>
#include <ctime>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the AffiliationLookup class.
/// </summary>
/// <param name="name">Name of lookup table.</param>
/// <param name="verbose">Flag indicating whether verbose logging is enabled.</param>
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();
}
/// <summary>
/// Finalizes a instance of the AffiliationLookup class.
/// </summary>
AffiliationLookup::~AffiliationLookup()
{
/* stub */
}
/// <summary>
/// Helper to group affiliate a source ID.
/// </summary>
/// <param name="srcId"></param>
void AffiliationLookup::unitReg(uint32_t srcId)
{
m_unitRegTable.push_back(srcId);
}
/// <summary>
/// Helper to group unaffiliate a source ID.
/// </summary>
/// <param name="srcId"></param>
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;
}
/// <summary>
/// Helper to determine if the source ID has unit registered.
/// </summary>
/// <param name="srcId"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Helper to group affiliate a source ID.
/// </summary>
/// <param name="srcId"></param>
/// <param name="dstId"></param>
void AffiliationLookup::groupAff(uint32_t srcId, uint32_t dstId)
{
// update dynamic affiliation table
m_grpAffTable[srcId] = dstId;
}
/// <summary>
/// Helper to group unaffiliate a source ID.
/// </summary>
/// <param name="srcId"></param>
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;
}
}
/// <summary>
/// Helper to determine if the source ID has affiliated to the group destination ID.
/// </summary>
/// <param name="srcId"></param>
/// <param name="dstId"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Helper to release group affiliations.
/// </summary>
/// <param name="dstId"></param>
/// <param name="releaseAll"></param>
std::vector<uint32_t> AffiliationLookup::clearGroupAff(uint32_t dstId, bool releaseAll)
{
std::vector<uint32_t> srcToRel = std::vector<uint32_t>();
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;
}
/// <summary>
/// Helper to grant a channel.
/// </summary>
/// <param name="dstId"></param>
/// <param name="grantTimeout"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Helper to start the destination ID grant timer.
/// </summary>
/// <param name="dstId"></param>
/// <returns></returns>
void AffiliationLookup::touchGrant(uint32_t dstId)
{
if (dstId == 0U) {
return;
}
if (isGranted(dstId)) {
m_grantTimers[dstId].start();
}
}
/// <summary>
/// Helper to release the channel grant for the destination ID.
/// </summary>
/// <param name="dstId"></param>
/// <param name="releaseAll"></param>
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<uint32_t> gntsToRel = std::vector<uint32_t>();
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;
}
/// <summary>
/// Helper to determine if the channel number is busy.
/// </summary>
/// <param name="chNo"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Helper to determine if the destination ID is already granted.
/// </summary>
/// <param name="dstId"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Helper to get the channel granted for the given destination ID.
/// </summary>
/// <param name="dstId"></param>
/// <returns></returns>
uint32_t AffiliationLookup::getGrantedCh(uint32_t dstId)
{
if (dstId == 0U) {
return 0U;
}
if (isGranted(dstId)) {
return m_grantChTable[dstId];
}
return 0U;
}
/// <summary>
/// Updates the processor by the passed number of milliseconds.
/// </summary>
/// <param name="ms"></param>
void AffiliationLookup::clock(uint32_t ms)
{
// clock all the grant timers
std::vector<uint32_t> gntsToRel = std::vector<uint32_t>();
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);
}
}

@ -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 <cstdio>
#include <unordered_map>
#include <algorithm>
#include <vector>
namespace lookups
{
// ---------------------------------------------------------------------------
// Class Declaration
// Implements a lookup table class that contains subscriber registration
// and group affiliation information.
// ---------------------------------------------------------------------------
class HOST_SW_API AffiliationLookup {
public:
/// <summary>Initializes a new instance of the AffiliationLookup class.</summary>
AffiliationLookup(const char* name, bool verbose);
/// <summary>Finalizes a instance of the AffiliationLookup class.</summary>
virtual ~AffiliationLookup();
/// <summary>Gets the count of unit registrations.</summary>
uint8_t unitRegSize() const { return m_unitRegTable.size(); }
/// <summary>Gets the unit registration table.</summary>
std::vector<uint32_t> unitRegTable() const { return m_unitRegTable; }
/// <summary>Helper to register a source ID.</summary>
virtual void unitReg(uint32_t srcId);
/// <summary>Helper to deregister a source ID.</summary>
virtual bool unitDereg(uint32_t srcId);
/// <summary>Helper to determine if the source ID has unit registered.</summary>
virtual bool isUnitReg(uint32_t srcId) const;
/// <summary>Gets the count of affiliations.</summary>
uint8_t grpAffSize() const { return m_grpAffTable.size(); }
/// <summary>Gets the group affiliation table.</summary>
std::unordered_map<uint32_t, uint32_t> grpAffTable() const { return m_grpAffTable; }
/// <summary>Helper to group affiliate a source ID.</summary>
virtual void groupAff(uint32_t srcId, uint32_t dstId);
/// <summary>Helper to group unaffiliate a source ID.</summary>
virtual bool groupUnaff(uint32_t srcId);
/// <summary>Helper to determine if the source ID has affiliated to the group destination ID.</summary>
virtual bool isGroupAff(uint32_t srcId, uint32_t dstId) const;
/// <summary>Helper to release group affiliations.</summary>
virtual std::vector<uint32_t> clearGroupAff(uint32_t dstId, bool releaseAll);
/// <summary>Gets the count of grants.</summary>
uint8_t grantSize() const { return m_grantChTable.size(); }
/// <summary>Gets the grant table.</summary>
std::unordered_map<uint32_t, uint32_t> grantTable() const { return m_grantChTable; }
/// <summary>Helper to grant a channel.</summary>
virtual bool grantCh(uint32_t dstId, uint32_t grantTimeout);
/// <summary>Helper to start the destination ID grant timer.</summary>
virtual void touchGrant(uint32_t dstId);
/// <summary>Helper to release the channel grant for the destination ID.</summary>
virtual bool releaseGrant(uint32_t dstId, bool releaseAll);
/// <summary>Helper to determine if the channel number is busy.</summary>
virtual bool isChBusy(uint32_t chNo) const;
/// <summary>Helper to determine if the destination ID is already granted.</summary>
virtual bool isGranted(uint32_t dstId) const;
/// <summary>Helper to get the channel granted for the given destination ID.</summary>
virtual uint32_t getGrantedCh(uint32_t dstId);
/// <summary>Helper to add a RF channel.</summary>
void addRFCh(uint32_t chNo) { m_rfChTable.push_back(chNo); }
/// <summary>Helper to remove a RF channel.</summary>
void removeRFCh(uint32_t chNo) { m_rfChTable.push_back(chNo); }
/// <summary>Gets the count of RF channels.</summary>
uint8_t getRFChCnt() const { return m_rfChTable.size(); }
/// <summary>Helper to determine if there are any RF channels available..</summary>
bool isRFChAvailable() const { return !m_rfChTable.empty(); }
/// <summary>Gets the count of granted RF channels.</summary>
uint8_t getGrantedRFChCnt() const { return m_rfGrantChCnt; }
/// <summary>Updates the processor by the passed number of milliseconds.</summary>
void clock(uint32_t ms);
protected:
std::vector<uint32_t> m_rfChTable;
uint8_t m_rfGrantChCnt;
std::vector<uint32_t> m_unitRegTable;
std::unordered_map<uint32_t, uint32_t> m_grpAffTable;
std::unordered_map<uint32_t, uint32_t> m_grantChTable;
std::unordered_map<uint32_t, Timer> m_grantTimers;
const char *m_name;
bool m_verbose;
};
} // namespace lookups
#endif // __AFFILIATION_LOOKUP_H__

@ -30,6 +30,10 @@
*/ */
#include "Defines.h" #include "Defines.h"
#include "edac/SHA256.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 "RemoteControl.h"
#include "HostMain.h" #include "HostMain.h"
#include "Log.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) { else if (rcom == RCD_P25_RELEASE_GRANTS_CMD) {
// Command is in the form of: "p25-rel-grnts" // Command is in the form of: "p25-rel-grnts"
if (p25 != NULL) { if (p25 != NULL) {
p25->trunk()->releaseDstIdGrant(0, true); p25->affiliations().releaseGrant(0, true);
} }
else { else {
LogError(LOG_RCON, CMD_FAILED_STR "P25 mode is not enabled!"); 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); uint32_t grp = getArgUInt32(args, 0U);
if (grp == 0) { if (grp == 0) {
p25->trunk()->clearGrpAff(0, true); p25->affiliations().clearGroupAff(0, true);
} }
else { else {
p25->trunk()->clearGrpAff(grp, false); p25->affiliations().clearGroupAff(grp, false);
} }
} }
else { else {

@ -33,10 +33,6 @@
#include "Defines.h" #include "Defines.h"
#include "network/UDPSocket.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/RadioIdLookup.h"
#include "lookups/TalkgroupIdLookup.h" #include "lookups/TalkgroupIdLookup.h"
@ -65,7 +61,7 @@ public:
~RemoteControl(); ~RemoteControl();
/// <summary>Sets the instances of the Radio ID and Talkgroup ID lookup tables.</summary> /// <summary>Sets the instances of the Radio ID and Talkgroup ID lookup tables.</summary>
void setLookups(lookups::RadioIdLookup* ridLookup, lookups::TalkgroupIdLookup* tidLookup); void setLookups(::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupIdLookup* tidLookup);
/// <summary>Process remote network command data.</summary> /// <summary>Process remote network command data.</summary>
void process(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn); void process(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
@ -84,8 +80,8 @@ private:
uint8_t* m_passwordHash; uint8_t* m_passwordHash;
bool m_debug; bool m_debug;
lookups::RadioIdLookup* m_ridLookup; ::lookups::RadioIdLookup* m_ridLookup;
lookups::TalkgroupIdLookup* m_tidLookup; ::lookups::TalkgroupIdLookup* m_tidLookup;
/// <summary></summary> /// <summary></summary>
std::string getArgString(std::vector<std::string> args, uint32_t n) const; std::string getArgString(std::vector<std::string> args, uint32_t n) const;

@ -239,6 +239,9 @@ namespace nxdn
const uint8_t RTCH_MESSAGE_TYPE_SDCALL_RESP = 0x3BU; // SDCALL_RESP - Short Data Call Response const uint8_t RTCH_MESSAGE_TYPE_SDCALL_RESP = 0x3BU; // SDCALL_RESP - Short Data Call Response
// Control Channel Message Types // 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_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 = 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) const uint8_t RCCH_MESSAGE_TYPE_REG_C = 0x22U; // REG_C - Registration Clear Request (ISP) / Registration Clear Response (OSP)

@ -158,6 +158,15 @@ void RCCH::reset()
m_version = 0U; m_version = 0U;
m_causeRsp = NXDN_CAUSE_MM_NORMAL_1; 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;
} }
/// <summary> /// <summary>
@ -224,6 +233,14 @@ RCCH::RCCH(SiteData siteData) :
m_regOption(0U), m_regOption(0U),
m_version(0U), m_version(0U),
m_causeRsp(NXDN_CAUSE_MM_NORMAL_1), 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_siteData(siteData),
m_siteIdenEntry(), m_siteIdenEntry(),
m_data(NULL) m_data(NULL)
@ -251,6 +268,25 @@ bool RCCH::decodeLC(const uint8_t* data)
// message type opcodes // message type opcodes
switch (m_messageType) { 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: case MESSAGE_TYPE_IDLE:
break; break;
case RCCH_MESSAGE_TYPE_REG: case RCCH_MESSAGE_TYPE_REG:
@ -289,6 +325,58 @@ void RCCH::encodeLC(uint8_t* data)
// message type opcodes // message type opcodes
switch (m_messageType) { 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: case MESSAGE_TYPE_IDLE:
break; break;
case MESSAGE_TYPE_DST_ID_INFO: case MESSAGE_TYPE_DST_ID_INFO:

@ -98,6 +98,28 @@ namespace nxdn
/// <summary>Cause Response.</summary> /// <summary>Cause Response.</summary>
__PROPERTY(uint8_t, causeRsp, CauseResponse); __PROPERTY(uint8_t, causeRsp, CauseResponse);
/// <summary>Voice channel number.</summary>
__PROPERTY(uint32_t, grpVchNo, GrpVchNo);
/** Call Data */
/// <summary>Call Type</summary>
__PROPERTY(uint8_t, callType, CallType);
/** Common Call Options */
/// <summary>Flag indicating the emergency bits are set.</summary>
__PROPERTY(bool, emergency, Emergency);
/// <summary>Flag indicating that encryption is enabled.</summary>
__PROPERTY(bool, encrypted, Encrypted);
/// <summary>Flag indicating priority paging.</summary>
__PROPERTY(bool, priority, Priority);
/// <summary>Flag indicating a group/talkgroup operation.</summary>
__PROPERTY(bool, group, Group);
/// <summary>Flag indicating a half/full duplex operation.</summary>
__PROPERTY(bool, duplex, Duplex);
/// <summary>Transmission mode.</summary>
__PROPERTY(uint8_t, transmissionMode, TransmissionMode);
/** Local Site data */ /** Local Site data */
/// <summary>Local Site Data.</summary> /// <summary>Local Site Data.</summary>
__PROPERTY_PLAIN(SiteData, siteData, siteData); __PROPERTY_PLAIN(SiteData, siteData, siteData);

@ -44,6 +44,8 @@ namespace nxdn
// Class Prototypes // Class Prototypes
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
namespace packet { class HOST_SW_API Data; }
namespace packet { class HOST_SW_API Voice; }
class HOST_SW_API Control; class HOST_SW_API Control;
namespace packet namespace packet
@ -69,6 +71,8 @@ namespace nxdn
void clock(uint32_t ms); void clock(uint32_t ms);
protected: protected:
friend class nxdn::packet::Data;
friend class nxdn::packet::Voice;
friend class nxdn::Control; friend class nxdn::Control;
Control* m_nxdn; Control* m_nxdn;

@ -82,8 +82,8 @@ const uint32_t MAX_PREAMBLE_TDU_CNT = 64U;
/// <param name="debug">Flag indicating whether P25 debug is enabled.</param> /// <param name="debug">Flag indicating whether P25 debug is enabled.</param>
/// <param name="verbose">Flag indicating whether P25 verbose logging is enabled.</param> /// <param name="verbose">Flag indicating whether P25 verbose logging is enabled.</param>
Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::BaseNetwork* network, 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, uint32_t timeout, uint32_t tgHang, bool duplex, ::lookups::RadioIdLookup* ridLookup,
lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper, ::lookups::TalkgroupIdLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper,
bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose) : bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose) :
m_voice(NULL), m_voice(NULL),
m_data(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_idenTable(idenTable),
m_ridLookup(ridLookup), m_ridLookup(ridLookup),
m_tidLookup(tidLookup), m_tidLookup(tidLookup),
m_affiliations(this, verbose),
m_idenEntry(), m_idenEntry(),
m_queue(queueSize, "P25 Frame"), m_queue(queueSize, "P25 Frame"),
m_rfState(RS_RF_LISTENING), 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 = SiteData(netId, sysId, rfssId, siteId, 0U, channelId, channelNo, serviceClass);
m_siteData.setCallsign(cwCallsign); m_siteData.setCallsign(cwCallsign);
std::vector<lookups::IdenTable> entries = m_idenTable->list(); std::vector<::lookups::IdenTable> entries = m_idenTable->list();
for (auto it = entries.begin(); it != entries.end(); ++it) { for (auto it = entries.begin(); it != entries.end(); ++it) {
lookups::IdenTable entry = *it; ::lookups::IdenTable entry = *it;
if (entry.channelId() == channelId) { if (entry.channelId() == channelId) {
m_idenEntry = entry; m_idenEntry = entry;
break; break;
@ -291,11 +292,10 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s
} }
std::vector<uint32_t> availCh = voiceChNo; std::vector<uint32_t> availCh = voiceChNo;
m_trunk->m_voiceChCnt = (uint8_t)availCh.size();
m_siteData.setChCnt((uint8_t)availCh.size()); m_siteData.setChCnt((uint8_t)availCh.size());
for (auto it = availCh.begin(); it != availCh.end(); ++it) { 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<uint32_t>(300U); uint32_t ccBcstInterval = p25Protocol["control"]["interval"].as<uint32_t>(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)); 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) { if (m_control) {
m_trunk->releaseDstIdGrant(m_voice->m_rfLC.getDstId(), false); m_affiliations.releaseGrant(m_voice->m_rfLC.getDstId(), false);
} }
writeRF_TDU(false); writeRF_TDU(false);
@ -509,7 +509,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len)
if (!m_dedicatedControl) if (!m_dedicatedControl)
ret = m_voice->process(data, len); ret = m_voice->process(data, len);
else { 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); ret = m_voice->process(data, len);
} }
} }
@ -524,7 +524,7 @@ bool Control::processFrame(uint8_t* data, uint32_t len)
if (!m_dedicatedControl) if (!m_dedicatedControl)
ret = m_data->process(data, len); ret = m_data->process(data, len);
else { 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); ret = m_data->process(data, len);
} }
} }
@ -686,7 +686,7 @@ void Control::clock(uint32_t ms)
m_networkWatchdog.stop(); m_networkWatchdog.stop();
if (m_control) { if (m_control) {
m_trunk->releaseDstIdGrant(m_voice->m_netLC.getDstId(), false); m_affiliations.releaseGrant(m_voice->m_netLC.getDstId(), false);
} }
if (m_dedicatedControl) { if (m_dedicatedControl) {

@ -32,17 +32,18 @@
#define __P25_CONTROL_H__ #define __P25_CONTROL_H__
#include "Defines.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/NID.h"
#include "p25/SiteData.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/BaseNetwork.h"
#include "network/RemoteControl.h" #include "network/RemoteControl.h"
#include "lookups/RSSIInterpolator.h" #include "lookups/RSSIInterpolator.h"
#include "lookups/IdenTableLookup.h" #include "lookups/IdenTableLookup.h"
#include "lookups/RadioIdLookup.h" #include "lookups/RadioIdLookup.h"
#include "lookups/TalkgroupIdLookup.h" #include "lookups/TalkgroupIdLookup.h"
#include "p25/lookups/P25AffiliationLookup.h"
#include "modem/Modem.h" #include "modem/Modem.h"
#include "RingBuffer.h" #include "RingBuffer.h"
#include "Timer.h" #include "Timer.h"
@ -61,6 +62,7 @@ namespace p25
namespace packet { class HOST_SW_API Data; } namespace packet { class HOST_SW_API Data; }
namespace packet { class HOST_SW_API Trunk; } namespace packet { class HOST_SW_API Trunk; }
namespace dfsi { namespace packet { class HOST_SW_API DFSITrunk; } } namespace dfsi { namespace packet { class HOST_SW_API DFSITrunk; } }
namespace lookups { class HOST_SW_API P25AffiliationLookup; }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Class Declaration // Class Declaration
@ -71,8 +73,8 @@ namespace p25
public: public:
/// <summary>Initializes a new instance of the Control class.</summary> /// <summary>Initializes a new instance of the Control class.</summary>
Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Modem* modem, network::BaseNetwork* network, 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, uint32_t timeout, uint32_t tgHang, bool duplex, ::lookups::RadioIdLookup* ridLookup,
lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper, ::lookups::TalkgroupIdLookup* tidLookup, ::lookups::IdenTableLookup* idenTable, ::lookups::RSSIInterpolator* rssiMapper,
bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose); bool dumpPDUData, bool repeatPDU, bool dumpTSBKData, bool debug, bool verbose);
/// <summary>Finalizes a instance of the Control class.</summary> /// <summary>Finalizes a instance of the Control class.</summary>
~Control(); ~Control();
@ -112,6 +114,8 @@ namespace p25
NID nid() { return m_nid; } NID nid() { return m_nid; }
/// <summary>Gets instance of the Trunk class.</summary> /// <summary>Gets instance of the Trunk class.</summary>
packet::Trunk* trunk() { return m_trunk; } packet::Trunk* trunk() { return m_trunk; }
/// <summary>Gets instance of the P25AffiliationLookup class.</summary>
lookups::P25AffiliationLookup affiliations() { return m_affiliations; }
/// <summary>Flag indicating whether the processor or is busy or not.</summary> /// <summary>Flag indicating whether the processor or is busy or not.</summary>
bool isBusy() const; bool isBusy() const;
@ -128,6 +132,7 @@ namespace p25
friend class packet::Trunk; friend class packet::Trunk;
friend class dfsi::packet::DFSITrunk; friend class dfsi::packet::DFSITrunk;
packet::Trunk* m_trunk; packet::Trunk* m_trunk;
friend class lookups::P25AffiliationLookup;
uint32_t m_nac; uint32_t m_nac;
uint32_t m_txNAC; uint32_t m_txNAC;
@ -147,11 +152,12 @@ namespace p25
bool m_ackTSBKRequests; bool m_ackTSBKRequests;
bool m_disableNetworkHDU; bool m_disableNetworkHDU;
lookups::IdenTableLookup* m_idenTable; ::lookups::IdenTableLookup* m_idenTable;
lookups::RadioIdLookup* m_ridLookup; ::lookups::RadioIdLookup* m_ridLookup;
lookups::TalkgroupIdLookup* m_tidLookup; ::lookups::TalkgroupIdLookup* m_tidLookup;
lookups::P25AffiliationLookup m_affiliations;
lookups::IdenTable m_idenEntry; ::lookups::IdenTable m_idenEntry;
RingBuffer<uint8_t> m_queue; RingBuffer<uint8_t> m_queue;
@ -182,7 +188,7 @@ namespace p25
SiteData m_siteData; SiteData m_siteData;
lookups::RSSIInterpolator* m_rssiMapper; ::lookups::RSSIInterpolator* m_rssiMapper;
uint8_t m_rssi; uint8_t m_rssi;
uint8_t m_maxRSSI; uint8_t m_maxRSSI;
uint8_t m_minRSSI; uint8_t m_minRSSI;

@ -26,6 +26,7 @@
#include "Defines.h" #include "Defines.h"
#include "p25/P25Defines.h" #include "p25/P25Defines.h"
#include "p25/dfsi/DFSIDefines.h" #include "p25/dfsi/DFSIDefines.h"
#include "p25/packet/Trunk.h"
#include "p25/dfsi/packet/DFSITrunk.h" #include "p25/dfsi/packet/DFSITrunk.h"
#include "p25/P25Utils.h" #include "p25/P25Utils.h"
#include "p25/Sync.h" #include "p25/Sync.h"

@ -28,7 +28,6 @@
#include "Defines.h" #include "Defines.h"
#include "p25/dfsi/LC.h" #include "p25/dfsi/LC.h"
#include "p25/packet/Trunk.h"
#include "p25/Control.h" #include "p25/Control.h"
#include "network/BaseNetwork.h" #include "network/BaseNetwork.h"

@ -31,6 +31,8 @@
#include "p25/P25Defines.h" #include "p25/P25Defines.h"
#include "p25/acl/AccessControl.h" #include "p25/acl/AccessControl.h"
#include "p25/dfsi/DFSIDefines.h" #include "p25/dfsi/DFSIDefines.h"
#include "p25/packet/Trunk.h"
#include "p25/packet/Voice.h"
#include "p25/dfsi/packet/DFSIVoice.h" #include "p25/dfsi/packet/DFSIVoice.h"
#include "p25/P25Utils.h" #include "p25/P25Utils.h"
#include "p25/Sync.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 // verify the source RID is affiliated to the group TGID; only if control data
// is supported // is supported
if (group && m_p25->m_control) { 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) { 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); 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); 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 (m_p25->m_control) {
// if the group wasn't granted out -- explicitly grant the group // 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) { if (m_p25->m_legacyGroupGrnt) {
// are we auto-registering legacy radios to groups? // are we auto-registering legacy radios to groups?
if (m_p25->m_legacyGroupReg && group) { 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)) { if (!m_p25->m_trunk->writeRF_TSDU_Grp_Aff_Rsp(srcId, dstId)) {
::memset(m_dfsiLDU1, 0x00U, 9U * 25U); ::memset(m_dfsiLDU1, 0x00U, 9U * 25U);
return false; return false;
@ -414,7 +416,7 @@ bool DFSIVoice::process(uint8_t* data, uint32_t len)
} }
if (m_p25->m_control) { 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? // 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) { else if (frameType == P25_DFSI_START_STOP) {
if (m_rfDFSILC.getType() == P25_DFSI_TYPE_VOICE && m_rfDFSILC.getStartStop() == P25_DFSI_STOP_FLAG) { if (m_rfDFSILC.getType() == P25_DFSI_TYPE_VOICE && m_rfDFSILC.getStartStop() == P25_DFSI_STOP_FLAG) {
if (m_p25->m_control) { 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]; 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) { 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) { if (m_p25->m_netState != RS_NET_IDLE) {
@ -831,7 +833,7 @@ DFSIVoice::~DFSIVoice()
void DFSIVoice::writeNet_TDU() void DFSIVoice::writeNet_TDU()
{ {
if (m_p25->m_control) { 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); m_trunk->writeRF_DSFI_Stop(P25_DFSI_TYPE_VOICE);
@ -928,7 +930,7 @@ void DFSIVoice::writeNet_LDU1()
} }
if (m_p25->m_control) { 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 // set network and RF link control states

@ -33,7 +33,6 @@
#include "Defines.h" #include "Defines.h"
#include "p25/dfsi/LC.h" #include "p25/dfsi/LC.h"
#include "p25/dfsi/packet/DFSITrunk.h" #include "p25/dfsi/packet/DFSITrunk.h"
#include "p25/packet/Trunk.h"
#include "p25/Control.h" #include "p25/Control.h"
#include "network/BaseNetwork.h" #include "network/BaseNetwork.h"

@ -36,7 +36,6 @@
#include "p25/lc/TDULC.h" #include "p25/lc/TDULC.h"
#include "p25/SiteData.h" #include "p25/SiteData.h"
#include "edac/RS634717.h" #include "edac/RS634717.h"
#include "lookups/IdenTableLookup.h"
#include <string> #include <string>

@ -58,7 +58,7 @@ TDULC::TDULC(const TDULC& data) : TDULC()
/// </summary> /// </summary>
/// <param name="siteData"></param> /// <param name="siteData"></param>
/// <param name="entry"></param> /// <param name="entry"></param>
TDULC::TDULC(SiteData siteData, lookups::IdenTable entry) : TDULC(siteData) TDULC::TDULC(SiteData siteData, ::lookups::IdenTable entry) : TDULC(siteData)
{ {
m_siteIdenEntry = entry; m_siteIdenEntry = entry;
m_grpVchNo = m_siteData.channelNo(); m_grpVchNo = m_siteData.channelNo();
@ -70,7 +70,7 @@ TDULC::TDULC(SiteData siteData, lookups::IdenTable entry) : TDULC(siteData)
/// <param name="siteData"></param> /// <param name="siteData"></param>
/// <param name="entry"></param> /// <param name="entry"></param>
/// <param name="verbose"></param> /// <param name="verbose"></param>
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_verbose = verbose;
m_siteIdenEntry = entry; m_siteIdenEntry = entry;

@ -57,9 +57,9 @@ namespace p25
/// <summary>Initializes a copy instance of the TDULC class.</summary> /// <summary>Initializes a copy instance of the TDULC class.</summary>
TDULC(const TDULC& data); TDULC(const TDULC& data);
/// <summary>Initializes a new instance of the TDULC class.</summary> /// <summary>Initializes a new instance of the TDULC class.</summary>
TDULC(SiteData siteData, lookups::IdenTable entry); TDULC(SiteData siteData, ::lookups::IdenTable entry);
/// <summary>Initializes a new instance of the TDULC class.</summary> /// <summary>Initializes a new instance of the TDULC class.</summary>
TDULC(SiteData siteData, lookups::IdenTable entry, bool verbose); TDULC(SiteData siteData, ::lookups::IdenTable entry, bool verbose);
/// <summary>Initializes a new instance of the TDULC class.</summary> /// <summary>Initializes a new instance of the TDULC class.</summary>
TDULC(LC* lc); TDULC(LC* lc);
/// <summary>Finalizes a instance of the TDULC class.</summary> /// <summary>Finalizes a instance of the TDULC class.</summary>
@ -123,7 +123,7 @@ namespace p25
/// <summary>Local Site Data.</summary> /// <summary>Local Site Data.</summary>
__PROPERTY_PLAIN(SiteData, siteData, siteData); __PROPERTY_PLAIN(SiteData, siteData, siteData);
/// <summary>Local Site Identity Entry.</summary> /// <summary>Local Site Identity Entry.</summary>
__PROPERTY_PLAIN(lookups::IdenTable, siteIdenEntry, siteIdenEntry); __PROPERTY_PLAIN(::lookups::IdenTable, siteIdenEntry, siteIdenEntry);
private: private:
/// <summary>Initializes a new instance of the TDULC class.</summary> /// <summary>Initializes a new instance of the TDULC class.</summary>

@ -58,7 +58,7 @@ TSBK::TSBK(const TSBK& data) : TSBK()
/// </summary> /// </summary>
/// <param name="siteData"></param> /// <param name="siteData"></param>
/// <param name="entry"></param> /// <param name="entry"></param>
TSBK::TSBK(SiteData siteData, lookups::IdenTable entry) : TSBK(siteData) TSBK::TSBK(SiteData siteData, ::lookups::IdenTable entry) : TSBK(siteData)
{ {
m_siteIdenEntry = entry; m_siteIdenEntry = entry;
} }
@ -69,7 +69,7 @@ TSBK::TSBK(SiteData siteData, lookups::IdenTable entry) : TSBK(siteData)
/// <param name="siteData"></param> /// <param name="siteData"></param>
/// <param name="entry"></param> /// <param name="entry"></param>
/// <param name="verbose"></param> /// <param name="verbose"></param>
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; m_verbose = verbose;
} }
@ -80,7 +80,7 @@ TSBK::TSBK(SiteData siteData, lookups::IdenTable entry, bool verbose) : TSBK(sit
/// <param name="siteData"></param> /// <param name="siteData"></param>
/// <param name="entry"></param> /// <param name="entry"></param>
/// <param name="verbose"></param> /// <param name="verbose"></param>
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_warnCRC = warnCRC;
m_siteIdenEntry = entry; m_siteIdenEntry = entry;

@ -68,11 +68,11 @@ namespace p25
/// <summary>Initializes a copy instance of the TSBK class.</summary> /// <summary>Initializes a copy instance of the TSBK class.</summary>
TSBK(const TSBK& data); TSBK(const TSBK& data);
/// <summary>Initializes a new instance of the TSBK class.</summary> /// <summary>Initializes a new instance of the TSBK class.</summary>
TSBK(SiteData siteData, lookups::IdenTable entry); TSBK(SiteData siteData, ::lookups::IdenTable entry);
/// <summary>Initializes a new instance of the TSBK class.</summary> /// <summary>Initializes a new instance of the TSBK class.</summary>
TSBK(SiteData siteData, lookups::IdenTable entry, bool verbose); TSBK(SiteData siteData, ::lookups::IdenTable entry, bool verbose);
/// <summary>Initializes a new instance of the TSBK class.</summary> /// <summary>Initializes a new instance of the TSBK class.</summary>
TSBK(SiteData siteData, lookups::IdenTable entry, bool verbose, bool warnCRC); TSBK(SiteData siteData, ::lookups::IdenTable entry, bool verbose, bool warnCRC);
/// <summary>Initializes a new instance of the TSBK class.</summary> /// <summary>Initializes a new instance of the TSBK class.</summary>
TSBK(LC* lc); TSBK(LC* lc);
/// <summary>Finalizes a instance of the TSBK class.</summary> /// <summary>Finalizes a instance of the TSBK class.</summary>
@ -205,7 +205,7 @@ namespace p25
/// <summary>Local Site Data.</summary> /// <summary>Local Site Data.</summary>
__PROPERTY_PLAIN(SiteData, siteData, siteData); __PROPERTY_PLAIN(SiteData, siteData, siteData);
/// <summary>Local Site Identity Entry.</summary> /// <summary>Local Site Identity Entry.</summary>
__PROPERTY_PLAIN(lookups::IdenTable, siteIdenEntry, siteIdenEntry); __PROPERTY_PLAIN(::lookups::IdenTable, siteIdenEntry, siteIdenEntry);
private: private:
/// <summary>Initializes a new instance of the TSBK class.</summary> /// <summary>Initializes a new instance of the TSBK class.</summary>

@ -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 <cassert>
#include <cstdio>
#include <cstring>
#include <ctime>
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the P25AffiliationLookup class.
/// </summary>
/// <param name="name">Name of lookup table.</param>
/// <param name="verbose">Flag indicating whether verbose logging is enabled.</param>
P25AffiliationLookup::P25AffiliationLookup(Control* p25, bool verbose) : ::lookups::AffiliationLookup("P25 Aff", verbose),
m_p25(p25)
{
/* stub */
}
/// <summary>
/// Finalizes a instance of the P25AffiliationLookup class.
/// </summary>
P25AffiliationLookup::~P25AffiliationLookup()
{
/* stub */
}
/// <summary>
/// Helper to release the channel grant for the destination ID.
/// </summary>
/// <param name="dstId"></param>
/// <param name="releaseAll"></param>
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;
}
/// <summary>
/// Helper to release group affiliations.
/// </summary>
/// <param name="dstId"></param>
/// <param name="releaseAll"></param>
std::vector<uint32_t> P25AffiliationLookup::clearGroupAff(uint32_t dstId, bool releaseAll)
{
std::vector<uint32_t> 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;
}

@ -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:
/// <summary>Initializes a new instance of the P25AffiliationLookup class.</summary>
P25AffiliationLookup(Control* p25, bool verbose);
/// <summary>Finalizes a instance of the P25AffiliationLookup class.</summary>
virtual ~P25AffiliationLookup();
/// <summary>Helper to release the channel grant for the destination ID.</summary>
virtual bool releaseGrant(uint32_t dstId, bool releaseAll);
/// <summary>Helper to release group affiliations.</summary>
virtual std::vector<uint32_t> clearGroupAff(uint32_t dstId, bool releaseAll);
protected:
Control* m_p25;
};
} // namespace lookups
} // namespace p25
#endif // __P25_AFFILIATION_LOOKUP_H__

@ -31,6 +31,7 @@
#include "Defines.h" #include "Defines.h"
#include "p25/P25Defines.h" #include "p25/P25Defines.h"
#include "p25/packet/Data.h" #include "p25/packet/Data.h"
#include "p25/packet/Trunk.h"
#include "p25/acl/AccessControl.h" #include "p25/acl/AccessControl.h"
#include "p25/P25Utils.h" #include "p25/P25Utils.h"
#include "p25/Sync.h" #include "p25/Sync.h"

@ -25,8 +25,10 @@
*/ */
#include "Defines.h" #include "Defines.h"
#include "p25/P25Defines.h" #include "p25/P25Defines.h"
#include "p25/packet/Voice.h"
#include "p25/packet/Trunk.h" #include "p25/packet/Trunk.h"
#include "p25/acl/AccessControl.h" #include "p25/acl/AccessControl.h"
#include "p25/lookups/P25AffiliationLookup.h"
#include "p25/P25Utils.h" #include "p25/P25Utils.h"
#include "p25/Sync.h" #include "p25/Sync.h"
#include "edac/CRC.h" #include "edac/CRC.h"
@ -85,7 +87,7 @@ using namespace p25::packet;
// Verify the source RID is registered. // Verify the source RID is registered.
#define VERIFY_SRCID_REG(_PCKT_STR, _PCKT, _SRCID) \ #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); \ 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_Deny(P25_DENY_RSN_REQ_UNIT_NOT_AUTH, _PCKT); \
writeRF_TSDU_U_Reg_Cmd(_SRCID); \ writeRF_TSDU_U_Reg_Cmd(_SRCID); \
@ -95,7 +97,7 @@ using namespace p25::packet;
// Verify the source RID is affiliated. // Verify the source RID is affiliated.
#define VERIFY_SRCID_AFF(_PCKT_STR, _PCKT, _SRCID, _DSTID) \ #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); \ 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_Deny(P25_DENY_RSN_REQ_UNIT_NOT_AUTH, _PCKT); \
writeRF_TSDU_U_Reg_Cmd(_SRCID); \ 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? // is the specified channel granted?
if (isChBusy(chNo) && hasDstIdGranted(dstId)) { if (m_p25->m_affiliations.isChBusy(chNo) && m_p25->m_affiliations.isGranted(dstId)) {
releaseDstIdGrant(dstId, false); m_p25->m_affiliations.releaseGrant(dstId, false);
} }
} }
break; 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 // 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.setSrcId(srcId);
m_rfTSBK.setDstId(dstId); 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 // 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.setSrcId(srcId);
m_rfTSBK.setDstId(dstId); m_rfTSBK.setDstId(dstId);
@ -865,194 +867,6 @@ void Trunk::writeAdjSSNetwork()
} }
} }
/// <summary>
/// Helper to determine if the source ID has affiliated to the group destination ID.
/// </summary>
/// <param name="srcId"></param>
/// <param name="dstId"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Helper to determine if the source ID has unit registered.
/// </summary>
/// <param name="srcId"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Helper to determine if the channel number is busy.
/// </summary>
/// <param name="chNo"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Helper to determine if the destination ID is already granted.
/// </summary>
/// <param name="dstId"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Helper to start the destination ID grant timer.
/// </summary>
/// <param name="dstId"></param>
/// <returns></returns>
void Trunk::touchDstIdGrant(uint32_t dstId)
{
if (dstId == 0U) {
return;
}
if (hasDstIdGranted(dstId)) {
m_grantTimers[dstId].start();
}
}
/// <summary>
/// Helper to release the channel grant for the destination ID.
/// </summary>
/// <param name="dstId"></param>
/// <param name="releaseAll"></param>
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<uint32_t> gntsToRel = std::vector<uint32_t>();
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();
}
}
/// <summary>
/// Helper to release group affiliations.
/// </summary>
/// <param name="dstId"></param>
/// <param name="releaseAll"></param>
void Trunk::clearGrpAff(uint32_t dstId, bool releaseAll)
{
if (dstId == 0U && !releaseAll) {
return;
}
std::vector<uint32_t> srcToRel = std::vector<uint32_t>();
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);
}
}
/// <summary> /// <summary>
/// Updates the processor by the passed number of milliseconds. /// Updates the processor by the passed number of milliseconds.
/// </summary> /// </summary>
@ -1079,20 +893,7 @@ void Trunk::clock(uint32_t ms)
} }
// clock all the grant timers // clock all the grant timers
std::vector<uint32_t> gntsToRel = std::vector<uint32_t>(); m_p25->m_affiliations.clock(ms);
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);
}
// clock adjacent site and SCCB update timers // clock adjacent site and SCCB update timers
m_adjSiteUpdateTimer.clock(ms); m_adjSiteUpdateTimer.clock(ms);
@ -1340,25 +1141,18 @@ Trunk::Trunk(Control* p25, network::BaseNetwork* network, bool dumpTSBKData, boo
m_patchSuperGroup(0xFFFFU), m_patchSuperGroup(0xFFFFU),
m_verifyAff(false), m_verifyAff(false),
m_verifyReg(false), m_verifyReg(false),
m_rfTSBK(SiteData(), lookups::IdenTable()), m_rfTSBK(SiteData(), ::lookups::IdenTable()),
m_netTSBK(SiteData(), lookups::IdenTable()), m_netTSBK(SiteData(), ::lookups::IdenTable()),
m_rfMBF(NULL), m_rfMBF(NULL),
m_mbfCnt(0U), m_mbfCnt(0U),
m_mbfIdenCnt(0U), m_mbfIdenCnt(0U),
m_mbfAdjSSCnt(0U), m_mbfAdjSSCnt(0U),
m_mbfSCCBCnt(0U), m_mbfSCCBCnt(0U),
m_mbfGrpGrntCnt(0U), m_mbfGrpGrntCnt(0U),
m_voiceChTable(),
m_adjSiteTable(), m_adjSiteTable(),
m_adjSiteUpdateCnt(), m_adjSiteUpdateCnt(),
m_sccbTable(), m_sccbTable(),
m_sccbUpdateCnt(), m_sccbUpdateCnt(),
m_unitRegTable(),
m_grpAffTable(),
m_grantChTable(),
m_grantTimers(),
m_voiceChCnt(1U),
m_voiceGrantChCnt(0U),
m_noStatusAck(false), m_noStatusAck(false),
m_noMessageAck(true), m_noMessageAck(true),
m_unitToUnitAvailCheck(true), m_unitToUnitAvailCheck(true),
@ -1376,20 +1170,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]; 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); ::memset(m_rfMBF, 0x00U, P25_MAX_PDU_COUNT * P25_LDU_FRAME_LENGTH_BYTES + 2U);
m_voiceChTable.clear();
m_adjSiteTable.clear(); m_adjSiteTable.clear();
m_adjSiteUpdateCnt.clear(); m_adjSiteUpdateCnt.clear();
m_sccbTable.clear(); m_sccbTable.clear();
m_sccbUpdateCnt.clear(); m_sccbUpdateCnt.clear();
m_unitRegTable.clear();
m_grpAffTable.clear();
m_grantChTable.clear();
m_grantTimers.clear();
m_adjSiteUpdateInterval = ADJ_SITE_TIMER_TIMEOUT; m_adjSiteUpdateInterval = ADJ_SITE_TIMER_TIMEOUT;
m_adjSiteUpdateTimer.setTimeout(m_adjSiteUpdateInterval); m_adjSiteUpdateTimer.setTimeout(m_adjSiteUpdateInterval);
m_adjSiteUpdateTimer.start(); m_adjSiteUpdateTimer.start();
@ -1489,7 +1275,7 @@ void Trunk::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS)
break; break;
/** update data */ /** update data */
case 4: case 4:
if (m_grantChTable.size() > 0) { if (m_p25->m_affiliations.grantSize() > 0) {
queueRF_TSBK_Ctrl(TSBK_OSP_GRP_VCH_GRANT_UPD); queueRF_TSBK_Ctrl(TSBK_OSP_GRP_VCH_GRANT_UPD);
} }
break; 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 // pad MBF if we have 2 queued TSDUs
if (m_mbfCnt == 2U) { if (m_mbfCnt == 2U) {
std::vector<lookups::IdenTable> entries = m_p25->m_idenTable->list(); std::vector<::lookups::IdenTable> entries = m_p25->m_idenTable->list();
if (entries.size() > 1U) { if (entries.size() > 1U) {
queueRF_TSBK_Ctrl(TSBK_OSP_IDEN_UP); queueRF_TSBK_Ctrl(TSBK_OSP_IDEN_UP);
} }
@ -1848,8 +1634,8 @@ void Trunk::queueRF_TSBK_Ctrl(uint8_t lco)
switch (lco) { switch (lco) {
case TSBK_OSP_GRP_VCH_GRANT_UPD: case TSBK_OSP_GRP_VCH_GRANT_UPD:
// write group voice grant update // write group voice grant update
if (m_grantChTable.size() > 0) { if (m_p25->m_affiliations.grantSize() > 0) {
if (m_mbfGrpGrntCnt >= m_grantChTable.size()) if (m_mbfGrpGrntCnt >= m_p25->m_affiliations.grantSize())
m_mbfGrpGrntCnt = 0U; m_mbfGrpGrntCnt = 0U;
if (m_debug) { if (m_debug) {
@ -1858,7 +1644,9 @@ void Trunk::queueRF_TSBK_Ctrl(uint8_t lco)
bool noData = false; bool noData = false;
uint8_t i = 0U; uint8_t i = 0U;
for (auto it = m_grantChTable.begin(); it != m_grantChTable.end(); ++it) { std::unordered_map<uint32_t, uint32_t> grantTable = m_p25->m_affiliations.grantTable();
for (auto it = grantTable.begin(); it != grantTable.end(); ++it)
{
// no good very bad way of skipping entries... // no good very bad way of skipping entries...
if (i != m_mbfGrpGrntCnt) { if (i != m_mbfGrpGrntCnt) {
i++; 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)"); LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_OSP_IDEN_UP (Identity Update)");
} }
std::vector<lookups::IdenTable> entries = m_p25->m_idenTable->list(); std::vector<::lookups::IdenTable> entries = m_p25->m_idenTable->list();
if (m_mbfIdenCnt >= entries.size()) if (m_mbfIdenCnt >= entries.size())
m_mbfIdenCnt = 0U; m_mbfIdenCnt = 0U;
@ -1911,7 +1699,7 @@ void Trunk::queueRF_TSBK_Ctrl(uint8_t lco)
continue; continue;
} }
else { else {
lookups::IdenTable entry = *it; ::lookups::IdenTable entry = *it;
// LogDebug(LOG_P25, "baseFrequency = %uHz, txOffsetMhz = %fMHz, chBandwidthKhz = %fKHz, chSpaceKhz = %fKHz", // LogDebug(LOG_P25, "baseFrequency = %uHz, txOffsetMhz = %fMHz, chBandwidthKhz = %fKHz, chSpaceKhz = %fKHz",
// entry.baseFrequency(), entry.txOffsetMhz(), entry.chBandwidthKhz(), entry.chSpaceKhz()); // 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_p25->m_affiliations.isGranted(m_rfTSBK.getDstId())) {
if (m_voiceChTable.empty()) { if (!m_p25->m_affiliations.isRFChAvailable()) {
if (grp) { if (grp) {
if (!net) { 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()); 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 { else {
uint32_t chNo = m_voiceChTable.at(0); if (m_p25->m_affiliations.grantCh(m_rfTSBK.getDstId(), GRANT_TIMER_TIMEOUT)) {
auto it = std::find(m_voiceChTable.begin(), m_voiceChTable.end(), chNo); uint32_t chNo = m_p25->m_affiliations.getGrantedCh(m_rfTSBK.getDstId());
m_voiceChTable.erase(it); m_rfTSBK.setGrpVchNo(chNo);
m_rfTSBK.setDataChnNo(chNo);
m_grantChTable[m_rfTSBK.getDstId()] = chNo; m_p25->m_siteData.setChCnt(m_p25->m_affiliations.getRFChCnt() + m_p25->m_affiliations.getGrantedRFChCnt());
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);
} }
} }
else { 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.setGrpVchNo(chNo);
m_rfTSBK.setDataChnNo(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; return false;
} }
if (!hasDstIdGranted(m_rfTSBK.getSrcId())) { if (!m_p25->m_affiliations.isGranted(m_rfTSBK.getSrcId())) {
if (m_voiceChTable.empty()) { if (!m_p25->m_affiliations.isRFChAvailable()) {
if (!net) { 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()); 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); 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; return false;
} }
else { else {
uint32_t chNo = m_voiceChTable.at(0); if (m_p25->m_affiliations.grantCh(m_rfTSBK.getSrcId(), GRANT_TIMER_TIMEOUT)) {
auto it = std::find(m_voiceChTable.begin(), m_voiceChTable.end(), chNo); uint32_t chNo = m_p25->m_affiliations.getGrantedCh(m_rfTSBK.getSrcId());
m_voiceChTable.erase(it); m_rfTSBK.setGrpVchNo(chNo);
m_rfTSBK.setDataChnNo(chNo);
m_grantChTable[m_rfTSBK.getSrcId()] = chNo; m_p25->m_siteData.setChCnt(m_p25->m_affiliations.getRFChCnt() + m_p25->m_affiliations.getGrantedRFChCnt());
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);
} }
} }
else { 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.setGrpVchNo(chNo);
m_rfTSBK.setDataChnNo(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 // 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); 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); ::ActivityLog("P25", true, "group affiliation request from %u to %s %u denied", srcId, "TG ", dstId);
m_rfTSBK.setResponse(P25_RSP_REFUSED); 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; ret = true;
// update dynamic affiliation table // update dynamic affiliation table
m_grpAffTable[srcId] = dstId; m_p25->m_affiliations.groupAff(srcId, dstId);
} }
writeRF_TSDU_SBF(false); 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); ::ActivityLog("P25", true, "unit registration request from %u", srcId);
// update dynamic unit registration table // update dynamic unit registration table
if (!hasSrcIdUnitReg(srcId)) { if (!m_p25->m_affiliations.isUnitReg(srcId)) {
m_unitRegTable.push_back(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 // remove dynamic unit registration table entry
if (std::find(m_unitRegTable.begin(), m_unitRegTable.end(), srcId) != m_unitRegTable.end()) { dereged = m_p25->m_affiliations.unitDereg(srcId);
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
}
if (dereged) { if (dereged) {
::ActivityLog("P25", true, "unit deregistration request from %u", srcId); ::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 // 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); 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); ::ActivityLog("P25", true, "location registration request from %u denied", srcId);
writeRF_TSDU_U_Reg_Cmd(srcId); writeRF_TSDU_U_Reg_Cmd(srcId);

@ -50,6 +50,7 @@ namespace p25
namespace packet { class HOST_SW_API Voice; } namespace packet { class HOST_SW_API Voice; }
namespace dfsi { namespace packet { class HOST_SW_API DFSIVoice; } } namespace dfsi { namespace packet { class HOST_SW_API DFSIVoice; } }
namespace packet { class HOST_SW_API Data; } namespace packet { class HOST_SW_API Data; }
namespace lookups { class HOST_SW_API P25AffiliationLookup; }
class HOST_SW_API Control; class HOST_SW_API Control;
namespace packet namespace packet
@ -77,22 +78,6 @@ namespace p25
/// <summary>Helper to write P25 adjacent site information to the network.</summary> /// <summary>Helper to write P25 adjacent site information to the network.</summary>
void writeAdjSSNetwork(); void writeAdjSSNetwork();
/// <summary>Helper to determine if the source ID has affiliated to the group destination ID.</summary>
bool hasSrcIdGrpAff(uint32_t srcId, uint32_t dstId) const;
/// <summary>Helper to determine if the source ID has unit registered.</summary>
bool hasSrcIdUnitReg(uint32_t srcId) const;
/// <summary>Helper to determine if the channel number is busy.</summary>
bool isChBusy(uint32_t chNo) const;
/// <summary>Helper to determine if the destination ID is already granted.</summary>
bool hasDstIdGranted(uint32_t dstId) const;
/// <summary>Helper to start the destination ID grant timer.</summary>
void touchDstIdGrant(uint32_t dstId);
/// <summary>Helper to release the channel grant for the destination ID.</summary>
void releaseDstIdGrant(uint32_t dstId, bool releaseAll);
/// <summary>Helper to release group affiliations.</summary>
void clearGrpAff(uint32_t dstId, bool releaseAll);
/// <summary>Updates the processor by the passed number of milliseconds.</summary> /// <summary>Updates the processor by the passed number of milliseconds.</summary>
void clock(uint32_t ms); void clock(uint32_t ms);
@ -124,6 +109,7 @@ namespace p25
friend class packet::Data; friend class packet::Data;
friend class p25::Control; friend class p25::Control;
Control* m_p25; Control* m_p25;
friend class lookups::P25AffiliationLookup;
network::BaseNetwork* m_network; network::BaseNetwork* m_network;
@ -142,23 +128,12 @@ namespace p25
uint8_t m_mbfSCCBCnt; uint8_t m_mbfSCCBCnt;
uint8_t m_mbfGrpGrntCnt; uint8_t m_mbfGrpGrntCnt;
std::vector<uint32_t> m_voiceChTable;
std::unordered_map<uint8_t, SiteData> m_adjSiteTable; std::unordered_map<uint8_t, SiteData> m_adjSiteTable;
std::unordered_map<uint8_t, uint8_t> m_adjSiteUpdateCnt; std::unordered_map<uint8_t, uint8_t> m_adjSiteUpdateCnt;
std::unordered_map<uint8_t, SiteData> m_sccbTable; std::unordered_map<uint8_t, SiteData> m_sccbTable;
std::unordered_map<uint8_t, uint8_t> m_sccbUpdateCnt; std::unordered_map<uint8_t, uint8_t> m_sccbUpdateCnt;
std::vector<uint32_t> m_unitRegTable;
std::unordered_map<uint32_t, uint32_t> m_grpAffTable;
std::unordered_map<uint32_t, uint32_t> m_grantChTable;
std::unordered_map<uint32_t, Timer> m_grantTimers;
uint8_t m_voiceChCnt;
uint8_t m_voiceGrantChCnt;
bool m_noStatusAck; bool m_noStatusAck;
bool m_noMessageAck; bool m_noMessageAck;
bool m_unitToUnitAvailCheck; bool m_unitToUnitAvailCheck;

@ -31,6 +31,7 @@
#include "Defines.h" #include "Defines.h"
#include "p25/P25Defines.h" #include "p25/P25Defines.h"
#include "p25/packet/Voice.h" #include "p25/packet/Voice.h"
#include "p25/packet/Trunk.h"
#include "p25/acl/AccessControl.h" #include "p25/acl/AccessControl.h"
#include "p25/dfsi/DFSIDefines.h" #include "p25/dfsi/DFSIDefines.h"
#include "p25/P25Utils.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 // verify the source RID is affiliated to the group TGID; only if control data
// is supported // is supported
if (group && m_p25->m_control) { 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) { 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); 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); 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 (m_p25->m_control) {
// if the group wasn't granted out -- explicitly grant the group // 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) { if (m_p25->m_legacyGroupGrnt) {
// are we auto-registering legacy radios to groups? // are we auto-registering legacy radios to groups?
if (m_p25->m_legacyGroupReg && group) { 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)) { if (!m_p25->m_trunk->writeRF_TSDU_Grp_Aff_Rsp(srcId, dstId)) {
return false; return false;
} }
@ -471,7 +472,7 @@ bool Voice::process(uint8_t* data, uint32_t len)
alreadyDecoded = false; alreadyDecoded = false;
if (m_p25->m_control) { 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? // 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) { else if (duid == P25_DUID_TDU || duid == P25_DUID_TDULC) {
if (m_p25->m_control) { 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) { 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) { 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) { if (m_p25->m_netState != RS_NET_IDLE) {
@ -967,7 +968,7 @@ void Voice::writeRF_EndOfVoice()
void Voice::writeNet_TDU() void Voice::writeNet_TDU()
{ {
if (m_p25->m_control) { 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]; uint8_t buffer[P25_TDU_FRAME_LENGTH_BYTES + 2U];
@ -1096,7 +1097,7 @@ void Voice::writeNet_LDU1()
} }
if (m_p25->m_control) { 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 // set network and RF link control states

Loading…
Cancel
Save

Powered by TurnKey Linux.