From d588982b72ec17ce4c592455357dab9e1566f29f Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 10 Dec 2021 13:10:50 -0500 Subject: [PATCH] implement an internal DVM MFId so we can abuse TSDUs to transmit call termination across the network with ease; properly implement GRP_VCH_GRANT_UPD; --- p25/Control.cpp | 2 +- p25/P25Defines.h | 1 + p25/TrunkPacket.cpp | 126 ++++++++++++++++++++++++++++++++++++++++---- p25/TrunkPacket.h | 4 ++ p25/lc/TSBK.cpp | 49 +++++++++++++++++ p25/lc/TSBK.h | 2 + 6 files changed, 174 insertions(+), 10 deletions(-) diff --git a/p25/Control.cpp b/p25/Control.cpp index b6913e5f..721b3ac5 100644 --- a/p25/Control.cpp +++ b/p25/Control.cpp @@ -514,7 +514,7 @@ bool Control::writeControlRF() return false; } - const uint8_t maxSeq = 7U; + const uint8_t maxSeq = 8U; if (m_ccSeq == maxSeq) { m_ccSeq = 0U; } diff --git a/p25/P25Defines.h b/p25/P25Defines.h index 0a0bb982..08d97b9b 100644 --- a/p25/P25Defines.h +++ b/p25/P25Defines.h @@ -112,6 +112,7 @@ namespace p25 const uint8_t P25_MFG_STANDARD = 0x00U; const uint8_t P25_MFG_MOT = 0x90U; + const uint8_t P25_MFG_DVM = 0xFEU; // internal P25 MFId used for internal signalling (shouldn't be air transmitted!) const uint8_t P25_MOT_CALLSIGN_LENGTH_BYTES = 8U; diff --git a/p25/TrunkPacket.cpp b/p25/TrunkPacket.cpp index d4a3492e..d3eaf08f 100644 --- a/p25/TrunkPacket.cpp +++ b/p25/TrunkPacket.cpp @@ -562,19 +562,47 @@ bool TrunkPacket::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, d uint32_t srcId = m_netTSBK.getSrcId(); uint32_t dstId = m_netTSBK.getDstId(); + // handle internal DVM TSDUs + if (m_netTSBK.getMFId() == P25_MFG_DVM) { + switch (m_netTSBK.getLCO()) { + case LC_CALL_TERM: + if (m_p25->m_dedicatedControl) { + uint32_t chNo = m_netTSBK.getGrpVchNo(); + + if (m_verbose) { + LogMessage(LOG_NET, P25_TSDU_STR ", LC_CALL_TERM (Call Termination), chNo = %u, srcId = %u, dstId = %u", chNo, srcId, dstId); + } + + // is the specified channel granted? + if (isChBusy(chNo) && hasDstIdGranted(dstId)) { + releaseDstIdGrant(dstId, false); + } + } + break; + default: + LogError(LOG_NET, P25_TSDU_STR ", unhandled LCO, mfId = $%02X, lco = $%02X", m_netTSBK.getMFId(), m_netTSBK.getLCO()); + return false; + } + + writeNet_TSDU(); + + return true; + } + + // handle standard P25 reference opcodes switch (m_netTSBK.getLCO()) { case TSBK_IOSP_GRP_VCH: if (m_verbose) { LogMessage(LOG_NET, P25_TSDU_STR ", TSBK_IOSP_GRP_VCH (Group Voice Channel Grant), emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u", m_netTSBK.getEmergency(), m_netTSBK.getEncrypted(), m_netTSBK.getPriority(), m_netTSBK.getGrpVchNo(), srcId, dstId); } - break; + return true; // don't allow this to write to the air case TSBK_IOSP_UU_VCH: if (m_verbose) { LogMessage(LOG_NET, P25_TSDU_STR ", TSBK_IOSP_UU_VCH (Unit-to-Unit Voice Channel Grant), emerg = %u, encrypt = %u, prio = %u, chNo = %u, srcId = %u, dstId = %u", m_netTSBK.getEmergency(), m_netTSBK.getEncrypted(), m_netTSBK.getPriority(), m_netTSBK.getGrpVchNo(), srcId, dstId); } - break; + return true; // don't allow this to write to the air case TSBK_IOSP_UU_ANS: if (m_netTSBK.getResponse() > 0U) { if (m_verbose) { @@ -654,13 +682,13 @@ bool TrunkPacket::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, d break; case TSBK_IOSP_GRP_AFF: // ignore a network group affiliation command - break; + return true; // don't allow this to write to the air case TSBK_OSP_U_DEREG_ACK: // ignore a network user deregistration command - break; + return true; // don't allow this to write to the air case TSBK_OSP_LOC_REG_RSP: // ignore a network location registration command - break; + return true; // don't allow this to write to the air case TSBK_OSP_DENY_RSP: if (m_verbose) { LogMessage(LOG_NET, P25_TSDU_STR ", TSBK_OSP_DENY_RSP (Deny Response), AIV = %u, reason = $%02X, srcId = %u, dstId = %u", @@ -1136,6 +1164,7 @@ TrunkPacket::TrunkPacket(Control* p25, network::BaseNetwork* network, bool dumpT m_mbfIdenCnt(0U), m_mbfAdjSSCnt(0U), m_mbfSCCBCnt(0U), + m_mbfGrpGrntCnt(0U), m_voiceChTable(), m_adjSiteTable(), m_adjSiteUpdateCnt(), @@ -1250,11 +1279,17 @@ void TrunkPacket::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS) else queueRF_TSBK_Ctrl(TSBK_OSP_NET_STS_BCAST); break; - /** extra data */ + /** update data */ case 4: - queueRF_TSBK_Ctrl(TSBK_OSP_SNDCP_CH_ANN); + if (m_grantChTable.size() > 0) { + queueRF_TSBK_Ctrl(TSBK_OSP_GRP_VCH_GRANT_UPD); + } break; + /** extra data */ case 5: + queueRF_TSBK_Ctrl(TSBK_OSP_SNDCP_CH_ANN); + break; + case 6: // write ADJSS if (adjSS && m_adjSiteTable.size() > 0) { queueRF_TSBK_Ctrl(TSBK_OSP_ADJ_STS_BCAST); @@ -1262,12 +1297,14 @@ void TrunkPacket::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS) } else { forcePad = true; } - case 6: + break; + case 7: // write SCCB if (adjSS && m_sccbTable.size() > 0) { queueRF_TSBK_Ctrl(TSBK_OSP_SCCB_EXP); break; } + break; } // should we insert the BSI bursts? @@ -1278,7 +1315,7 @@ void TrunkPacket::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS) // add padding after the last sequence or if forced; and only // if we're doing multiblock frames (MBF) - if ((n == 6U || forcePad) && m_ctrlTSDUMBF) + if ((n >= 4U || forcePad) && m_ctrlTSDUMBF) { // pad MBF if we have 1 queued TSDUs if (m_mbfCnt == 1U) { @@ -1389,6 +1426,10 @@ void TrunkPacket::writeRF_TDULC_ChanRelease(bool grp, uint32_t srcId, uint32_t d lc.setLCO(LC_CALL_TERM); writeRF_TDULC(lc, true); + + if (m_p25->m_control) { + writeNet_TSDU_Call_Term(srcId, dstId); + } } /// @@ -1568,6 +1609,53 @@ void TrunkPacket::queueRF_TSBK_Ctrl(uint8_t lco) resetRF(); 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()) + m_mbfGrpGrntCnt = 0U; + + if (m_debug) { + LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_OSP_GRP_VCH_GRANT_UPD (Group Voice Channel Grant Update)"); + } + + bool noData = false; + uint8_t i = 0U; + for (auto it = m_grantChTable.begin(); it != m_grantChTable.end(); ++it) { + // no good very bad way of skipping entries... + if (i != m_mbfGrpGrntCnt) { + i++; + continue; + } + else { + uint32_t dstId = it->first; + uint32_t chNo = it->second; + + if (chNo == 0U) { + noData = true; + m_mbfGrpGrntCnt++; + break; + } + else { + // transmit group voice grant update + m_rfTSBK.setLCO(TSBK_OSP_GRP_VCH_GRANT_UPD); + m_rfTSBK.setDstId(dstId); + m_rfTSBK.setGrpVchNo(chNo); + + m_mbfGrpGrntCnt++; + break; + } + } + } + + if (noData) { + return; // don't create anything + } + } + else { + return; // don't create anything + } + break; case TSBK_OSP_IDEN_UP: { if (m_debug) { @@ -2186,6 +2274,26 @@ bool TrunkPacket::writeRF_TSDU_Loc_Reg_Rsp(uint32_t srcId, uint32_t dstId) return ret; } +/// +/// Helper to write a call termination packet. +/// +/// +/// +bool TrunkPacket::writeNet_TSDU_Call_Term(uint32_t srcId, uint32_t dstId) +{ + bool ret = false; + + m_rfTSBK.setLCO(LC_CALL_TERM); + m_rfTSBK.setMFId(P25_MFG_DVM); + m_rfTSBK.setGrpVchId(m_p25->m_siteData.channelId()); + m_rfTSBK.setGrpVchNo(m_p25->m_siteData.channelNo()); + m_rfTSBK.setDstId(dstId); + m_rfTSBK.setSrcId(srcId); + + writeRF_TSDU_SBF(false); // the problem with this is the vendor code going over the air! + return ret; +} + /// /// Helper to write a network TSDU from the RF data queue. /// diff --git a/p25/TrunkPacket.h b/p25/TrunkPacket.h index 595a7696..f94e9052 100644 --- a/p25/TrunkPacket.h +++ b/p25/TrunkPacket.h @@ -129,6 +129,7 @@ namespace p25 uint8_t m_mbfIdenCnt; uint8_t m_mbfAdjSSCnt; uint8_t m_mbfSCCBCnt; + uint8_t m_mbfGrpGrntCnt; std::vector m_voiceChTable; @@ -203,6 +204,9 @@ namespace p25 /// Helper to write a location registration response packet. bool writeRF_TSDU_Loc_Reg_Rsp(uint32_t srcId, uint32_t dstId); + /// Helper to write a call termination packet. + bool writeNet_TSDU_Call_Term(uint32_t srcId, uint32_t dstId); + /// Helper to write a network TSDU from the RF data queue. void writeNet_TSDU_From_RF(uint8_t* data); diff --git a/p25/lc/TSBK.cpp b/p25/lc/TSBK.cpp index 6b450f1f..66af680f 100644 --- a/p25/lc/TSBK.cpp +++ b/p25/lc/TSBK.cpp @@ -259,12 +259,36 @@ bool TSBK::decode(const uint8_t* data) } } + // internal P25 vendor opcodes + if (m_mfId == P25_MFG_DVM) { + switch (m_lco) { + case LC_CALL_TERM: + m_grpVchId = ((tsbkValue >> 52) & 0x0FU); // Channel ID + m_grpVchNo = ((tsbkValue >> 40) & 0xFFFU); // Channel Number + m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFU); // Target Radio Address + m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address + break; + default: + m_mfId = P25_MFG_STANDARD; + break; + } + + if (m_mfId == P25_MFG_DVM) { + return true; + } + else { + m_mfId = tsbk[1U]; + } + } + // standard P25 reference opcodes switch (m_lco) { case TSBK_IOSP_GRP_VCH: m_emergency = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag m_encrypted = (((tsbkValue >> 56) & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag m_priority = (((tsbkValue >> 56) & 0xFFU) & 0x07U); // Priority + m_grpVchId = ((tsbkValue >> 52) & 0x0FU); // Channel ID + m_grpVchNo = ((tsbkValue >> 40) & 0xFFFU); // Channel Number m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; @@ -272,6 +296,8 @@ bool TSBK::decode(const uint8_t* data) m_emergency = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag m_encrypted = (((tsbkValue >> 56) & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag m_priority = (((tsbkValue >> 56) & 0xFFU) & 0x07U); // Priority + m_grpVchId = ((tsbkValue >> 52) & 0x0FU); // Channel ID + m_grpVchNo = ((tsbkValue >> 40) & 0xFFFU); // Channel Number m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; @@ -484,6 +510,13 @@ void TSBK::encode(uint8_t * data, bool singleBlock) tsbkValue = (tsbkValue << 24) + m_dstId; // Source ID tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; + case TSBK_OSP_GRP_VCH_GRANT_UPD: + tsbkValue = 0U; + tsbkValue = m_siteData.channelId(); // Channel ID + tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number + tsbkValue = (tsbkValue << 16) + m_dstId; // Talkgroup Address + tsbkValue = (tsbkValue << 32) + 0; + break; case TSBK_OSP_DENY_RSP: case TSBK_OSP_QUE_RSP: { @@ -795,6 +828,21 @@ void TSBK::encode(uint8_t * data, bool singleBlock) break; } } + + // internal P25 vendor opcodes + if (m_mfId == P25_MFG_DVM) { + switch (m_lco) { + case LC_CALL_TERM: + tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID + tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number + tsbkValue = (tsbkValue << 16) + m_dstId; // Talkgroup Address + tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address + break; + default: + LogError(LOG_P25, "unknown TSBK LCO value, mfId = $%02X, lco = $%02X", m_mfId, m_lco); + break; + } + } } // split rs value into bytes @@ -879,6 +927,7 @@ TSBK::TSBK(SiteData siteData) : m_response(P25_RSP_ACCEPT), m_netId(P25_WACN_STD_DEFAULT), m_sysId(P25_SID_STD_DEFAULT), + m_grpVchId(0U), m_grpVchNo(0U), m_messageValue(0U), m_statusValue(0U), diff --git a/p25/lc/TSBK.h b/p25/lc/TSBK.h index 68d95fdc..bce7fce5 100644 --- a/p25/lc/TSBK.h +++ b/p25/lc/TSBK.h @@ -115,6 +115,8 @@ namespace p25 /// Configured system ID. __READONLY_PROPERTY(uint32_t, sysId, SysId); + /// Voice channel ID. + __PROPERTY(uint32_t, grpVchId, GrpVchId); /// Voice channel number. __PROPERTY(uint32_t, grpVchNo, GrpVchNo);