diff --git a/config.yml b/config.yml index 0a39839c..85838372 100644 --- a/config.yml +++ b/config.yml @@ -47,6 +47,7 @@ protocols: voiceOnControl: false inhibitIllegal: false legacyGroupGrnt: true + legacyGroupReg: false verifyAff: false verifyReg: false dumpDataPacket: false diff --git a/network/RemoteControl.cpp b/network/RemoteControl.cpp index 44463417..0a7fd1bc 100644 --- a/network/RemoteControl.cpp +++ b/network/RemoteControl.cpp @@ -83,6 +83,7 @@ using namespace modem; #define RCD_P25_PATCH_CMD "p25-patch" #define RCD_P25_RELEASE_GRANTS "p25-rel-grnts" +#define RCD_P25_RELEASE_AFFS "p25-rel-affs" const uint32_t RC_BUFFER_LENGTH = 100U; @@ -572,6 +573,22 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25) LogError(LOG_RCON, CMD_FAILED_STR "P25 mode is not enabled!"); } } + else if (rcom == RCD_P25_RELEASE_AFFS) { + // Command is in the form of: "p25-rel-affs " + if (p25 != NULL) { + uint32_t grp = getArgUInt32(args, 0U); + + if (grp == 0) { + p25->trunk()->clearGrpAff(0, true); + } + else { + p25->trunk()->clearGrpAff(grp, false); + } + } + else { + LogError(LOG_RCON, CMD_FAILED_STR "P25 mode is not enabled!"); + } + } else { args.clear(); LogError(LOG_RCON, BAD_CMD_STR " (\"%s\")", rcom.c_str()); diff --git a/p25/Control.cpp b/p25/Control.cpp index d795eddc..a9b95b28 100644 --- a/p25/Control.cpp +++ b/p25/Control.cpp @@ -89,6 +89,7 @@ Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Mod m_network(network), m_inhibitIllegal(false), m_legacyGroupGrnt(true), + m_legacyGroupReg(false), m_duplex(duplex), m_control(false), m_continuousControl(false), @@ -112,6 +113,7 @@ Control::Control(uint32_t nac, uint32_t callHang, uint32_t queueSize, modem::Mod m_hangCount(3U * 8U), m_preambleCount(0U), m_ccFrameCnt(0U), + m_ccSeq(0U), m_nid(nac), m_rssiMapper(rssiMapper), m_rssi(0U), @@ -181,6 +183,7 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s m_inhibitIllegal = p25Protocol["inhibitIllegal"].as(false); m_legacyGroupGrnt = p25Protocol["legacyGroupGrnt"].as(true); + m_legacyGroupReg = p25Protocol["legacyGroupReg"].as(false); m_trunk->m_verifyAff = p25Protocol["verifyAff"].as(false); m_trunk->m_verifyReg = p25Protocol["verifyReg"].as(false); @@ -226,6 +229,7 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s LogInfo(" Inhibit Illegal: %s", m_inhibitIllegal ? "yes" : "no"); LogInfo(" Legacy Group Grant: %s", m_legacyGroupGrnt ? "yes" : "no"); + LogInfo(" Legacy Group Registration: %s", m_legacyGroupReg ? "yes" : "no"); LogInfo(" Verify Affiliation: %s", m_trunk->m_verifyAff ? "yes" : "no"); LogInfo(" Verify Registration: %s", m_trunk->m_verifyReg ? "yes" : "no"); @@ -455,13 +459,18 @@ bool Control::writeControlRF() return false; } + if (m_ccSeq == 5U) { + m_ccSeq = 0U; + } + if (m_ccFrameCnt == 254U) { m_ccFrameCnt = 0U; } if (m_netState == RS_NET_IDLE && m_rfState == RS_RF_LISTENING) { - m_trunk->writeRF_ControlData(m_ccFrameCnt, true); + m_trunk->writeRF_ControlData(m_ccFrameCnt, m_ccSeq, true); m_ccFrameCnt++; + m_ccSeq++; return true; } diff --git a/p25/Control.h b/p25/Control.h index c4004185..137b5190 100644 --- a/p25/Control.h +++ b/p25/Control.h @@ -122,6 +122,7 @@ namespace p25 bool m_inhibitIllegal; bool m_legacyGroupGrnt; + bool m_legacyGroupReg; bool m_duplex; bool m_control; @@ -151,7 +152,9 @@ namespace p25 uint32_t m_hangCount; uint32_t m_preambleCount; + uint8_t m_ccFrameCnt; + uint8_t m_ccSeq; NID m_nid; diff --git a/p25/TrunkPacket.cpp b/p25/TrunkPacket.cpp index 76aa35b5..e0758112 100644 --- a/p25/TrunkPacket.cpp +++ b/p25/TrunkPacket.cpp @@ -339,6 +339,8 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_GRP_VCH (Group Voice Channel Request), srcId = %u, dstId = %u", srcId, dstId); } + ::ActivityLog("P25", true, "received group grant request from %u to TG %u", srcId, dstId); + writeRF_TSDU_Grant(true, false); break; case TSBK_IOSP_UU_VCH: @@ -358,6 +360,8 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_UU_VCH (Unit-to-Unit Voice Channel Request), srcId = %u, dstId = %u", srcId, dstId); } + ::ActivityLog("P25", true, "received unit-to-unit grant request from %u to %u", srcId, dstId); + writeRF_TSDU_UU_Ans_Req(srcId, dstId); break; case TSBK_IOSP_UU_ANS: @@ -375,6 +379,8 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) m_rfTSBK.getResponse(), srcId, dstId); } + writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_UU_ANS, true); + if (m_rfTSBK.getResponse() == P25_ANS_RSP_PROCEED) { writeRF_TSDU_Grant(false, false); } @@ -397,6 +403,8 @@ bool TrunkPacket::process(uint8_t* data, uint32_t len) m_rfTSBK.getResponse(), srcId); } + writeRF_TSDU_ACK_FNE(srcId, TSBK_IOSP_TELE_INT_ANS, true); + if (m_rfTSBK.getResponse() == P25_ANS_RSP_PROCEED) { //writeRF_TSDU_Grant(false); writeRF_TSDU_Deny(P25_DENY_RSN_SYS_UNSUPPORTED_SVC, TSBK_IOSP_TELE_INT_ANS); @@ -677,6 +685,19 @@ bool TrunkPacket::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, d resetStatusCommand(m_netTSBK); switch (m_netTSBK.getLCO()) { + case TSBK_IOSP_UU_ANS: + if (m_netTSBK.getResponse() > 0U) { + if (m_verbose) { + LogMessage(LOG_NET, P25_TSDU_STR ", TSBK_IOSP_UU_ANS (Unit-to-Unit Answer Response), response = $%02X, srcId = %u, dstId = %u", + m_netTSBK.getResponse(), srcId, dstId); + } + } + else { + if (m_verbose) { + LogMessage(LOG_NET, P25_TSDU_STR ", TSBK_IOSP_UU_ANS (Unit-to-Unit Answer Request), srcId = %u, dstId = %u", srcId, dstId); + } + } + break; case TSBK_IOSP_STS_UPDT: // validate the source RID VALID_SRCID_NET("TSBK_IOSP_STS_UPDT (Status Update)", srcId); @@ -751,14 +772,14 @@ bool TrunkPacket::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, d break; case TSBK_OSP_DENY_RSP: if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", TSBK_OSP_DENY_RSP (Deny Response), reason = %u, srcId = %u, dstId = %u", - m_netTSBK.getResponse(), m_netTSBK.getSrcId(), m_netTSBK.getDstId()); + LogMessage(LOG_NET, P25_TSDU_STR ", TSBK_OSP_DENY_RSP (Deny Response), AIV = %u, reason = $%02X, srcId = %u, dstId = %u", + m_netTSBK.getAIV(), m_netTSBK.getResponse(), m_netTSBK.getSrcId(), m_netTSBK.getDstId()); } break; case TSBK_OSP_QUE_RSP: if (m_verbose) { - LogMessage(LOG_NET, P25_TSDU_STR ", TSBK_OSP_QUE_RSP (Queue Response), reason = %u, srcId = %u, dstId = %u", - m_netTSBK.getResponse(), m_netTSBK.getSrcId(), m_netTSBK.getDstId()); + LogMessage(LOG_NET, P25_TSDU_STR ", TSBK_OSP_QUE_RSP (Queue Response), AIV = %u, reason = $%02X, srcId = %u, dstId = %u", + m_netTSBK.getAIV(), m_netTSBK.getResponse(), m_netTSBK.getSrcId(), m_netTSBK.getDstId()); } break; default: @@ -959,6 +980,42 @@ void TrunkPacket::releaseDstIdGrant(uint32_t dstId, bool releaseAll) } } +/// +/// Helper to release group affiliations. +/// +/// +/// +void TrunkPacket::clearGrpAff(uint32_t dstId, bool releaseAll) +{ + if (dstId == 0U && !releaseAll) { + return; + } + + std::vector srcToRel = std::vector(); + if (dstId == 0U && releaseAll) { + LogWarning(LOG_RF, "P25, releasing all group affiliations"); + for (auto it = m_grpAffTable.begin(); it != m_grpAffTable.end(); ++it) { + uint32_t srcId = it->first; + srcToRel.push_back(srcId); + } + } + else { + LogWarning(LOG_RF, "P25, releasing group affiliations, dstId = %u", dstId); + for (auto it = m_grpAffTable.begin(); it != m_grpAffTable.end(); ++it) { + uint32_t srcId = it->first; + uint32_t grpId = it->second; + if (grpId == dstId) { + srcToRel.push_back(srcId); + } + } + } + + // release affiliations + for (auto it = srcToRel.begin(); it != srcToRel.end(); ++it) { + writeRF_TSDU_U_Dereg_Ack(*it); + } +} + /// /// /// @@ -1258,124 +1315,91 @@ void TrunkPacket::writeNetworkRF(const uint8_t* data, bool autoReset) /// Helper to write control channel packet data. /// /// +/// /// -void TrunkPacket::writeRF_ControlData(uint8_t frameCnt, bool adjSS) +void TrunkPacket::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS) { + uint8_t i = 0U, seqCnt = 0U; + if (!m_p25->m_control) { return; } - m_rfTSBK.reset(); - - bool alt = (frameCnt % 2) > 0U; - if (m_debug) { - LogDebug(LOG_P25, "writeRF_ControlData, mbfCnt = %u, frameCnt = %u, alt = %u, adjSS = %u", m_mbfCnt, frameCnt, alt, adjSS); - } - - queueRF_TSBK_Ctrl_MBF(TSBK_OSP_IDEN_UP); - - if (alt) { - // write rfss-net-rfss bcast - queueRF_TSBK_Ctrl_MBF(TSBK_OSP_RFSS_STS_BCAST); - queueRF_TSBK_Ctrl_MBF(TSBK_OSP_NET_STS_BCAST); - queueRF_TSBK_Ctrl_MBF(TSBK_OSP_RFSS_STS_BCAST); - } - else { - // write net-rfss-net bcast - queueRF_TSBK_Ctrl_MBF(TSBK_OSP_NET_STS_BCAST); - queueRF_TSBK_Ctrl_MBF(TSBK_OSP_RFSS_STS_BCAST); - queueRF_TSBK_Ctrl_MBF(TSBK_OSP_NET_STS_BCAST); + // loop to generate 6 control sequences + if (frameCnt == 255U) { + seqCnt = 6U; } - queueRF_TSBK_Ctrl_MBF(TSBK_OSP_SNDCP_CH_ANN); - - // LogDebug(LOG_P25, "writeRF_ControlData, before adjSS, mbfCnt = %u", m_mbfCnt); - - // write ADJSS - if (adjSS && m_adjSiteTable.size() > 0) { - if (m_mbfAdjSSCnt >= m_adjSiteTable.size()) - m_mbfAdjSSCnt = 0U; - - uint8_t i = 0U; - for (auto it = m_adjSiteTable.begin(); it != m_adjSiteTable.end(); ++it) { - // no good very bad way of skipping entries... - if (i != m_mbfAdjSSCnt) { - i++; - continue; - } - else { - m_rfTSBK.reset(); - - SiteData site = it->second; - - uint8_t cfva = P25_CFVA_CONV | P25_CFVA_NETWORK; - if (m_adjSiteUpdateCnt[site.siteId()] == 0U) { - cfva |= P25_CFVA_FAILURE; - } - else { - cfva |= P25_CFVA_VALID; - } - - // transmit adjacent site broadcast - m_rfTSBK.setLCO(TSBK_OSP_ADJ_STS_BCAST); - m_rfTSBK.setAdjSiteCFVA(cfva); - m_rfTSBK.setAdjSiteSysId(site.sysId()); - m_rfTSBK.setAdjSiteRFSSId(site.rfssId()); - m_rfTSBK.setAdjSiteId(site.siteId()); - m_rfTSBK.setAdjSiteChnId(site.channelId()); - m_rfTSBK.setAdjSiteChnNo(site.channelNo()); + do + { + m_rfTSBK.reset(); - m_rfTSBK.setLastBlock(true); // always set last block - writeRF_TSDU_MBF(); + if (m_debug) { + LogDebug(LOG_P25, "writeRF_ControlData, mbfCnt = %u, frameCnt = %u, seq = %u, adjSS = %u", m_mbfCnt, frameCnt, n, adjSS); + } - m_mbfAdjSSCnt++; - break; + switch (n) + { + case 0: + queueRF_TSBK_Ctrl_MBF(TSBK_OSP_IDEN_UP); + break; + case 1: + queueRF_TSBK_Ctrl_MBF(TSBK_OSP_RFSS_STS_BCAST); + break; + case 2: + queueRF_TSBK_Ctrl_MBF(TSBK_OSP_NET_STS_BCAST); + break; + case 3: + queueRF_TSBK_Ctrl_MBF(TSBK_OSP_SNDCP_CH_ANN); + break; + case 4: + // write ADJSS + if (adjSS) { + queueRF_TSBK_Ctrl_MBF(TSBK_OSP_ADJ_STS_BCAST); } + break; } - } - - // LogDebug(LOG_P25, "writeRF_ControlData, after adjSS, mbfCnt = %u", m_mbfCnt); + + if (seqCnt > 0U) + n++; + i++; + } while (i <= seqCnt); // should we insert the BSI bursts? - bool bsi = (frameCnt % 127) == 0U; - if (bsi) { + bool bsi = (frameCnt % 64U) == 0U; + if (bsi || frameCnt == 255U) { queueRF_TSBK_Ctrl_MBF(TSBK_OSP_MOT_CC_BSI); - - m_rfTSBK.reset(); - - // transmit CC BSI burst - m_rfTSBK.setLCO(TSBK_OSP_MOT_CC_BSI); - m_rfTSBK.setMFId(P25_MFG_MOT); - - m_rfTSBK.setLastBlock(true); // always set last block - writeRF_TSDU_MBF(); } - // pad MBF if we have 1 queued TSDUs - if (m_mbfCnt == 1U) { - queueRF_TSBK_Ctrl_MBF(TSBK_OSP_RFSS_STS_BCAST); - queueRF_TSBK_Ctrl_MBF(TSBK_OSP_NET_STS_BCAST); - if (m_debug) { - LogDebug(LOG_P25, "writeRF_ControlData, have 1 pad 2, mbfCnt = %u", m_mbfCnt); - } - } - - // pad MBF if we have 2 queued TSDUs - if (m_mbfCnt == 2U) { - std::vector entries = m_p25->m_idenTable->list(); - if (entries.size() > 1U) { - queueRF_TSBK_Ctrl_MBF(TSBK_OSP_IDEN_UP); - } - else { + // add padding after the 4th sequence + if (seqCnt > 4U) { + // pad MBF if we have 1 queued TSDUs + if (m_mbfCnt == 1U) { queueRF_TSBK_Ctrl_MBF(TSBK_OSP_RFSS_STS_BCAST); + queueRF_TSBK_Ctrl_MBF(TSBK_OSP_NET_STS_BCAST); + if (m_debug) { + LogDebug(LOG_P25, "writeRF_ControlData, have 1 pad 2, mbfCnt = %u", m_mbfCnt); + } } - if (m_debug) { - LogDebug(LOG_P25, "writeRF_ControlData, have 2 pad 1, mbfCnt = %u", m_mbfCnt); + + // pad MBF if we have 2 queued TSDUs + if (m_mbfCnt == 2U) { + std::vector entries = m_p25->m_idenTable->list(); + if (entries.size() > 1U) { + queueRF_TSBK_Ctrl_MBF(TSBK_OSP_IDEN_UP); + } + else { + queueRF_TSBK_Ctrl_MBF(TSBK_OSP_RFSS_STS_BCAST); + } + + if (m_debug) { + LogDebug(LOG_P25, "writeRF_ControlData, have 2 pad 1, mbfCnt = %u", m_mbfCnt); + } } - } - // reset MBF count - m_mbfCnt = 0U; + // reset MBF count + m_mbfCnt = 0U; + } } /// @@ -1669,6 +1693,10 @@ void TrunkPacket::queueRF_TSBK_Ctrl_MBF(uint8_t lco) switch (lco) { case TSBK_OSP_IDEN_UP: { + if (m_debug) { + LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_OSP_IDEN_UP (Identity Update)"); + } + std::vector entries = m_p25->m_idenTable->list(); if (m_mbfIdenCnt >= entries.size()) m_mbfIdenCnt = 0U; @@ -1723,6 +1751,52 @@ void TrunkPacket::queueRF_TSBK_Ctrl_MBF(uint8_t lco) // transmit rfss status burst m_rfTSBK.setLCO(TSBK_OSP_RFSS_STS_BCAST); break; + case TSBK_OSP_ADJ_STS_BCAST: + // write ADJSS + if (m_adjSiteTable.size() > 0) { + if (m_mbfAdjSSCnt >= m_adjSiteTable.size()) + m_mbfAdjSSCnt = 0U; + + if (m_debug) { + LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_OSP_ADJ_STS_BCAST (Adjacent Site Broadcast)"); + } + + uint8_t i = 0U; + for (auto it = m_adjSiteTable.begin(); it != m_adjSiteTable.end(); ++it) { + // no good very bad way of skipping entries... + if (i != m_mbfAdjSSCnt) { + i++; + continue; + } + else { + SiteData site = it->second; + + uint8_t cfva = P25_CFVA_CONV | P25_CFVA_NETWORK; + if (m_adjSiteUpdateCnt[site.siteId()] == 0U) { + cfva |= P25_CFVA_FAILURE; + } + else { + cfva |= P25_CFVA_VALID; + } + + // transmit adjacent site broadcast + m_rfTSBK.setLCO(TSBK_OSP_ADJ_STS_BCAST); + m_rfTSBK.setAdjSiteCFVA(cfva); + m_rfTSBK.setAdjSiteSysId(site.sysId()); + m_rfTSBK.setAdjSiteRFSSId(site.rfssId()); + m_rfTSBK.setAdjSiteId(site.siteId()); + m_rfTSBK.setAdjSiteChnId(site.channelId()); + m_rfTSBK.setAdjSiteChnNo(site.channelNo()); + + m_mbfAdjSSCnt++; + break; + } + } + } + else { + return; // don't create anything + } + break; case TSBK_OSP_SNDCP_CH_ANN: if (m_debug) { LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_OSP_SNDCP_CH_ANN (SNDCP Channel Announcement)"); @@ -1880,7 +1954,7 @@ void TrunkPacket::writeRF_TSDU_UU_Ans_Req(uint32_t srcId, uint32_t dstId) m_rfTSBK.setSrcId(srcId); m_rfTSBK.setDstId(dstId); m_rfTSBK.setVendorSkip(true); - writeRF_TSDU_SBF(true); + writeRF_TSDU_SBF(false); m_rfTSBK.setLCO(lco); m_rfTSBK.setVendorSkip(false); @@ -1929,7 +2003,7 @@ void TrunkPacket::writeRF_TSDU_Deny(uint8_t reason, uint8_t service) m_rfTSBK.setLCO(TSBK_OSP_DENY_RSP); m_rfTSBK.setService(service); m_rfTSBK.setResponse(reason); - writeRF_TSDU_SBF(true); + writeRF_TSDU_SBF(false); m_rfTSBK.setLCO(lco); } @@ -1939,8 +2013,10 @@ void TrunkPacket::writeRF_TSDU_Deny(uint8_t reason, uint8_t service) /// /// /// -void TrunkPacket::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId) +bool TrunkPacket::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId) { + bool ret = false; + m_rfTSBK.setLCO(TSBK_IOSP_GRP_AFF); m_rfTSBK.setResponse(P25_RSP_ACCEPT); m_rfTSBK.setPatchSuperGroupId(m_patchSuperGroup); @@ -1972,12 +2048,14 @@ void TrunkPacket::writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId) } ::ActivityLog("P25", true, "received group affiliation request from %u to %s %u", srcId, "TG ", dstId); + ret = true; // update dynamic affiliation table m_grpAffTable[srcId] = dstId; } writeRF_TSDU_SBF(false); + return ret; } /// @@ -2059,6 +2137,7 @@ void TrunkPacket::writeRF_TSDU_U_Dereg_Ack(uint32_t srcId) } m_rfTSBK.setSrcId(P25_WUID_SYS); + m_rfTSBK.setDstId(srcId); writeRF_TSDU_SBF(false); } @@ -2080,7 +2159,7 @@ void TrunkPacket::writeRF_TSDU_Queue(uint8_t reason, uint8_t service) m_rfTSBK.setLCO(TSBK_OSP_QUE_RSP); m_rfTSBK.setService(service); m_rfTSBK.setResponse(reason); - writeRF_TSDU_SBF(true); + writeRF_TSDU_SBF(false); m_rfTSBK.setLCO(lco); } diff --git a/p25/TrunkPacket.h b/p25/TrunkPacket.h index ddd511e4..267c8910 100644 --- a/p25/TrunkPacket.h +++ b/p25/TrunkPacket.h @@ -100,6 +100,8 @@ namespace p25 void touchDstIdGrant(uint32_t dstId); /// Helper to release the channel grant for the destination ID. void releaseDstIdGrant(uint32_t dstId, bool releaseAll); + /// Helper to release group affiliations. + void clearGrpAff(uint32_t dstId, bool releaseAll); /// void resetStatusCommand(); @@ -191,7 +193,7 @@ namespace p25 void writeNetworkRF(const uint8_t* data, bool autoReset); /// Helper to write control channel packet data. - void writeRF_ControlData(uint8_t frameCnt, bool adjSS); + void writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS); /// Helper to write a P25 TDU w/ link control packet. void writeRF_TDULC(uint8_t duid, bool noNetwork); @@ -217,7 +219,7 @@ namespace p25 /// Helper to write a deny packet. void writeRF_TSDU_Deny(uint8_t reason, uint8_t service); /// Helper to write a group affiliation response packet. - void writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId); + bool writeRF_TSDU_Grp_Aff_Rsp(uint32_t srcId, uint32_t dstId); /// Helper to write a unit registration response packet. void writeRF_TSDU_U_Reg_Rsp(uint32_t srcId); /// Helper to write a unit de-registration acknowledge packet. diff --git a/p25/VoicePacket.cpp b/p25/VoicePacket.cpp index 1bbde575..58b71430 100644 --- a/p25/VoicePacket.cpp +++ b/p25/VoicePacket.cpp @@ -129,7 +129,7 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) m_p25->writeRF_Preamble(); if (!m_p25->m_ccRunning) { - m_p25->m_trunk->writeRF_ControlData(127U, false); + m_p25->m_trunk->writeRF_ControlData(255U, 0U, false); } } @@ -235,6 +235,16 @@ bool VoicePacket::process(uint8_t* data, uint32_t len) // if the group wasn't granted out -- explicitly grant the group if (!m_p25->m_trunk->hasDstIdGranted(dstId)) { if (m_p25->m_legacyGroupGrnt) { + // are we auto-registering legacy radios to groups? + if (m_p25->m_legacyGroupReg && m_rfLC.getGroup()) { + if (!m_p25->m_trunk->hasSrcIdGrpAff(srcId, dstId)) { + m_p25->m_trunk->m_skipSBFPreamble = true; // HACK: force an SBF to skip generating preambles + if (!m_p25->m_trunk->writeRF_TSDU_Grp_Aff_Rsp(srcId, dstId)) { + return false; + } + } + } + m_p25->m_trunk->m_skipSBFPreamble = true; // HACK: force an SBF to skip generating preambles if (!m_p25->m_trunk->writeRF_TSDU_Grant(m_rfLC.getGroup(), false)) { return false; @@ -693,7 +703,7 @@ bool VoicePacket::processNetwork(uint8_t* data, uint32_t len, lc::LC& control, d m_p25->m_trunk->resetNet(); if (!m_p25->m_ccRunning) { - m_p25->m_trunk->writeRF_ControlData(127U, false); + m_p25->m_trunk->writeRF_ControlData(255U, 0U, false); } writeNet_HDU(control, lsd); @@ -740,7 +750,7 @@ bool VoicePacket::writeEndRF() writeRF_EndOfVoice(); if (!m_p25->m_ccRunning) { - m_p25->m_trunk->writeRF_ControlData(127U, false); + m_p25->m_trunk->writeRF_ControlData(255U, 0U, false); m_p25->writeControlEndRF(); } diff --git a/p25/lc/TSBK.cpp b/p25/lc/TSBK.cpp index b5722106..08badb8f 100644 --- a/p25/lc/TSBK.cpp +++ b/p25/lc/TSBK.cpp @@ -156,6 +156,8 @@ bool TSBK::decode(const uint8_t* data) case TSBK_IOSP_U_REG: case TSBK_ISP_CAN_SRV_REQ: case TSBK_ISP_GRP_AFF_Q_RSP: + case TSBK_OSP_DENY_RSP: + case TSBK_OSP_QUE_RSP: case TSBK_ISP_U_DEREG_REQ: case TSBK_OSP_U_DEREG_ACK: case TSBK_ISP_LOC_REG_REQ: @@ -252,6 +254,14 @@ bool TSBK::decode(const uint8_t* data) m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFU); // Talkgroup Address m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address break; + case TSBK_OSP_DENY_RSP: + case TSBK_OSP_QUE_RSP: + m_aivFlag = (((tsbkValue >> 56) & 0xFFU) & 0x80U) == 0x80U; // Additional Info. Flag + m_service = (uint8_t)((tsbkValue >> 56) & 0x3FU); // Service Type + m_response = (uint8_t)((tsbkValue >> 48) & 0xFFU); // Reason + m_dstId = (uint32_t)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address + m_srcId = (uint32_t)(tsbkValue & 0xFFFFFFU); // Source Radio Address + break; case TSBK_ISP_U_DEREG_REQ: case TSBK_OSP_U_DEREG_ACK: m_netId = (uint32_t)((tsbkValue >> 36) & 0xFFFFFU); // Network ID