From 4afaf6400df09ee755999743bc153bc99247c908 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Sat, 8 Oct 2022 17:18:32 -0400 Subject: [PATCH] implement proper DMR NACK CSBK encoding; implement preliminary DMR grant support for individual and talkgroup calls; --- dmr/DMRDefines.h | 3 + dmr/acl/AccessControl.cpp | 10 +- dmr/lc/CSBK.cpp | 29 ++- dmr/packet/ControlSignaling.cpp | 415 ++++++++++++++++++++++++++++++-- dmr/packet/ControlSignaling.h | 8 +- 5 files changed, 438 insertions(+), 27 deletions(-) diff --git a/dmr/DMRDefines.h b/dmr/DMRDefines.h index 6737ed5b..bfac1675 100644 --- a/dmr/DMRDefines.h +++ b/dmr/DMRDefines.h @@ -243,6 +243,7 @@ namespace dmr const uint8_t TS_DENY_RSN_REG_REFUSED = 0x2AU; const uint8_t TS_DENY_RSN_REG_DENIED = 0x2BU; const uint8_t TS_DENY_RSN_MS_NOT_REG = 0x2DU; + const uint8_t TS_DENY_RSN_TGT_BUSY = 0x2EU; const uint8_t TS_DENY_RSN_TGT_GROUP_NOT_VALID = 0x2FU; const uint8_t MS_DENY_RSN_UNSUPPORTED_SVC = 0x00U; @@ -296,6 +297,8 @@ namespace dmr const uint8_t CSBKO_PV_GRANT = 0x30U; // PV_GRANT - Private Voice Channel Grant const uint8_t CSBKO_TV_GRANT = 0x31U; // TV_GRANT - Talkgroup Voice Channel Grant const uint8_t CSBKO_BTV_GRANT = 0x32U; // BTV_GRANT - Broadcast Talkgroup Voice Channel Grant + const uint8_t CSBKO_PD_GRANT = 0x33U; // PD_GRANT - Private Data Channel Grant + const uint8_t CSBKO_TD_GRANT = 0x34U; // TD_GRANT - Talkgroup Data Channel Grant const uint8_t CSBKO_BSDWNACT = 0x38U; // BS DWN ACT - BS Outbound Activation const uint8_t CSBKO_PRECCSBK = 0x3DU; // PRE CSBK - Preamble CSBK diff --git a/dmr/acl/AccessControl.cpp b/dmr/acl/AccessControl.cpp index e4e3ee35..d6e9758e 100644 --- a/dmr/acl/AccessControl.cpp +++ b/dmr/acl/AccessControl.cpp @@ -106,8 +106,12 @@ bool AccessControl::validateTGId(uint32_t slotNo, uint32_t id) if (tid.tgSlot() == 0) return true; // TG Slot of 0 for the talkgroup entry means both - if (tid.tgSlot() != slotNo) - return false; + if (slotNo != 0) { + if (tid.tgSlot() != slotNo) + return false; - return true; + return true; + } else { + return true; + } } diff --git a/dmr/lc/CSBK.cpp b/dmr/lc/CSBK.cpp index db257321..691ea11b 100644 --- a/dmr/lc/CSBK.cpp +++ b/dmr/lc/CSBK.cpp @@ -244,7 +244,8 @@ void CSBK::encode(uint8_t* data) csbkValue = (m_GI ? 0x40U : 0x00U) + // Source Type (m_siteData.siteId() & 0x3FU); // Net + Site LSB } - csbkValue = (csbkValue << 7) + (m_reason & 0xFFU); // Reason Code + csbkValue = (csbkValue << 7) + (m_response & 0x7FU); // Response Information + csbkValue = (csbkValue << 8) + (m_reason & 0xFFU); // Reason Code csbkValue = (csbkValue << 25) + m_dstId; // Target Radio Address csbkValue = (csbkValue << 24) + m_srcId; // Source Radio Address break; @@ -270,7 +271,7 @@ void CSBK::encode(uint8_t* data) csbkValue = (csbkValue << 12) + (m_logicalCh1 & 0xFFFU); // Logical Physical Channel 1 csbkValue = (csbkValue << 1) + (m_slotNo & 0x3U); // Logical Slot Number csbkValue = (csbkValue << 1) + 0U; // Reserved - csbkValue = (csbkValue << 1) + 0U; // Emergency + csbkValue = (csbkValue << 1) + ((m_emergency) ? 1U : 0U); // Emergency csbkValue = (csbkValue << 1) + ((m_siteOffsetTiming) ? 1U : 0U); // Site Timing: Aligned or Offset csbkValue = (csbkValue << 24) + m_dstId; // Talkgroup ID csbkValue = (csbkValue << 24) + m_srcId; // Source Radio Address @@ -282,7 +283,29 @@ void CSBK::encode(uint8_t* data) csbkValue = (csbkValue << 12) + (m_logicalCh1 & 0xFFFU); // Logical Physical Channel 1 csbkValue = (csbkValue << 1) + (m_slotNo & 0x3U); // Logical Slot Number csbkValue = (csbkValue << 1) + 0U; // Late Entry - csbkValue = (csbkValue << 1) + 0U; // Emergency + csbkValue = (csbkValue << 1) + ((m_emergency) ? 1U : 0U); // Emergency + csbkValue = (csbkValue << 1) + ((m_siteOffsetTiming) ? 1U : 0U); // Site Timing: Aligned or Offset + csbkValue = (csbkValue << 24) + m_dstId; // Talkgroup ID + csbkValue = (csbkValue << 24) + m_srcId; // Source Radio Address + break; + + case CSBKO_PD_GRANT: + csbkValue = 0U; + csbkValue = (csbkValue << 12) + (m_logicalCh1 & 0xFFFU); // Logical Physical Channel 1 + csbkValue = (csbkValue << 1) + (m_slotNo & 0x3U); // Logical Slot Number + csbkValue = (csbkValue << 1) + 0U; // High Rate Flag - Always Single Slot Data + csbkValue = (csbkValue << 1) + ((m_emergency) ? 1U : 0U); // Emergency + csbkValue = (csbkValue << 1) + ((m_siteOffsetTiming) ? 1U : 0U); // Site Timing: Aligned or Offset + csbkValue = (csbkValue << 24) + m_dstId; // Talkgroup ID + csbkValue = (csbkValue << 24) + m_srcId; // Source Radio Address + break; + + case CSBKO_TD_GRANT: + csbkValue = 0U; + csbkValue = (csbkValue << 12) + (m_logicalCh1 & 0xFFFU); // Logical Physical Channel 1 + csbkValue = (csbkValue << 1) + (m_slotNo & 0x3U); // Logical Slot Number + csbkValue = (csbkValue << 1) + 0U; // High Rate Flag - Always Single Slot Data + csbkValue = (csbkValue << 1) + ((m_emergency) ? 1U : 0U); // Emergency csbkValue = (csbkValue << 1) + ((m_siteOffsetTiming) ? 1U : 0U); // Site Timing: Aligned or Offset csbkValue = (csbkValue << 24) + m_dstId; // Talkgroup ID csbkValue = (csbkValue << 24) + m_srcId; // Source Radio Address diff --git a/dmr/packet/ControlSignaling.cpp b/dmr/packet/ControlSignaling.cpp index b837933f..3a3db023 100644 --- a/dmr/packet/ControlSignaling.cpp +++ b/dmr/packet/ControlSignaling.cpp @@ -70,42 +70,48 @@ using namespace dmr::packet; #define IS_SUPPORT_CONTROL_CHECK(_PCKT_STR, _PCKT, _SRCID) \ if (!m_slot->m_dmr->getTSCCSlot()->m_enableTSCC) { \ LogWarning(LOG_RF, "DMR Slot %u, " _PCKT_STR " denial, unsupported service, srcId = %u", m_slot->m_slotNo, _SRCID); \ - writeRF_CSBK_ACK_RSP(TS_DENY_RSN_SYS_UNSUPPORTED_SVC, _PCKT); \ + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_SYS_UNSUPPORTED_SVC, 0U); \ return false; \ } // Validate the source RID. -#define VALID_SRCID(_PCKT_STR, _PCKT, _SRCID, _RSN) \ +#define VALID_SRCID(_PCKT_STR, _PCKT, _SRCID) \ if (!acl::AccessControl::validateSrcId(_SRCID)) { \ LogWarning(LOG_RF, "DMR Slot %u, " _PCKT_STR " denial, RID rejection, srcId = %u", m_slot->m_slotNo, _SRCID); \ - writeRF_CSBK_ACK_RSP(_RSN, _PCKT); \ + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_PERM_USER_REFUSED, 0U); \ return false; \ } // Validate the target RID. -#define VALID_DSTID(_PCKT_STR, _PCKT, _DSTID, _RSN) \ +#define VALID_DSTID(_PCKT_STR, _PCKT, _DSTID) \ if (!acl::AccessControl::validateSrcId(_DSTID)) { \ LogWarning(LOG_RF, "DMR Slot %u, " _PCKT_STR " denial, RID rejection, dstId = %u", m_slot->m_slotNo, _DSTID); \ - writeRF_CSBK_ACK_RSP(_RSN, _PCKT); \ + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_TEMP_USER_REFUSED, 0U); \ return false; \ } // Validate the talkgroup ID. -#define VALID_TGID(_PCKT_STR, _PCKT, _DSTID, _RSN) \ - if (!acl::AccessControl::validateTGId(_DSTID)) { \ +#define VALID_TGID(_PCKT_STR, _PCKT, _DSTID) \ + if (!acl::AccessControl::validateTGId(0U, _DSTID)) { \ LogWarning(LOG_RF, "DMR Slot %u, " _PCKT_STR " denial, TGID rejection, dstId = %u", m_slot->m_slotNo, _DSTID); \ - writeRF_CSBK_ACK_RSP(_RSN, _PCKT); \ + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_TGT_GROUP_NOT_VALID, 0U); \ return false; \ } // Verify the source RID is registered. -#define VERIFY_SRCID_REG(_PCKT_STR, _PCKT, _SRCID, _RSN) \ +#define VERIFY_SRCID_REG(_PCKT_STR, _PCKT, _SRCID) \ if (!m_slot->m_affiliations->isUnitReg(_SRCID) && m_slot->m_verifyReg) { \ LogWarning(LOG_RF, "DMR Slot %u, " _PCKT_STR " denial, RID not registered, srcId = %u", m_slot->m_slotNo, _SRCID); \ - writeRF_CSBK_ACK_RSP(_RSN, _PCKT); \ + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_PERM_USER_REFUSED, 0U); \ return false; \ } +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +const uint32_t GRANT_TIMER_TIMEOUT = 15U; + // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -189,24 +195,66 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) handled = true; if (m_verbose) { - LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), emerg = %u, prio = %u, serviceKind = $%02X, serviceOptions = $%02X, serviceExtra = $%02X, srcId = %u, dstId = %u", - m_slot->m_slotNo, csbk.getEmergency(), csbk.getPriority(), csbk.getServiceKind(), csbk.getServiceOptions(), csbk.getServiceExtra(), csbk.getSrcId(), csbk.getDstId()); + LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), serviceKind = $%02X, serviceOptions = $%02X, serviceExtra = $%02X, srcId = %u, dstId = %u", + m_slot->m_slotNo, csbk.getServiceKind(), csbk.getServiceOptions(), csbk.getServiceExtra(), csbk.getSrcId(), csbk.getDstId()); } switch (csbk.getServiceKind()) { case SVC_KIND_IND_VOICE_CALL: - // TODO TODO TODO + // make sure control data is supported + IS_SUPPORT_CONTROL_CHECK("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_VOICE_CALL (Individual Voice Call)", SVC_KIND_IND_VOICE_CALL, srcId); + + // validate the source RID + VALID_SRCID("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_VOICE_CALL (Individual Voice Call)", SVC_KIND_IND_VOICE_CALL, srcId); + + // validate the target RID + VALID_DSTID("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_VOICE_CALL (Individual Voice Call)", SVC_KIND_IND_VOICE_CALL, dstId); + + // verify the source RID is registered + VERIFY_SRCID_REG("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_VOICE_CALL (Individual Voice Call)", SVC_KIND_IND_VOICE_CALL, srcId); + + writeRF_CSBK_Grant(srcId, dstId, csbk.getServiceOptions(), false); break; case SVC_KIND_GRP_VOICE_CALL: - // TODO TODO TODO + // make sure control data is supported + IS_SUPPORT_CONTROL_CHECK("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_VOICE_CALL (Group Voice Call)", SVC_KIND_GRP_VOICE_CALL, srcId); + + // validate the source RID + VALID_SRCID("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_VOICE_CALL (Group Voice Call)", SVC_KIND_GRP_VOICE_CALL, srcId); + + // validate the talkgroup ID + VALID_TGID("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_VOICE_CALL (Group Voice Call)", SVC_KIND_GRP_VOICE_CALL, dstId); + + writeRF_CSBK_Grant(srcId, dstId, csbk.getServiceOptions(), true); break; case SVC_KIND_IND_DATA_CALL: case SVC_KIND_IND_UDT_DATA_CALL: - // TODO TODO TODO + // make sure control data is supported + IS_SUPPORT_CONTROL_CHECK("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_VOICE_CALL (Individual Voice Call)", SVC_KIND_IND_VOICE_CALL, srcId); + + // validate the source RID + VALID_SRCID("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_VOICE_CALL (Individual Voice Call)", SVC_KIND_IND_VOICE_CALL, srcId); + + // validate the target RID + VALID_DSTID("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_VOICE_CALL (Individual Voice Call)", SVC_KIND_IND_VOICE_CALL, dstId); + + // verify the source RID is registered + VERIFY_SRCID_REG("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_VOICE_CALL (Individual Voice Call)", SVC_KIND_IND_VOICE_CALL, srcId); + + writeRF_CSBK_Data_Grant(srcId, dstId, csbk.getServiceOptions(), false); break; case SVC_KIND_GRP_DATA_CALL: case SVC_KIND_GRP_UDT_DATA_CALL: - // TODO TODO TODO + // make sure control data is supported + IS_SUPPORT_CONTROL_CHECK("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_VOICE_CALL (Group Voice Call)", SVC_KIND_GRP_VOICE_CALL, srcId); + + // validate the source RID + VALID_SRCID("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_VOICE_CALL (Group Voice Call)", SVC_KIND_GRP_VOICE_CALL, srcId); + + // validate the talkgroup ID + VALID_TGID("DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_VOICE_CALL (Group Voice Call)", SVC_KIND_GRP_VOICE_CALL, dstId); + + writeRF_CSBK_Data_Grant(srcId, dstId, csbk.getServiceOptions(), true); break; case SVC_KIND_REG_SVC: // make sure control data is supported @@ -360,8 +408,8 @@ void ControlSignaling::processNetwork(const data::Data & dmrData) ::ActivityLog("DMR", false, "Slot %u call alert request from %u to %u", m_slot->m_slotNo, srcId, dstId); } else { if (m_verbose) { - LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), emerg = %u, prio = %u, serviceKind = $%02X, serviceOptions = $%02X, serviceExtra = $%02X, srcId = %u, dstId = %u", - m_slot->m_slotNo, csbk.getEmergency(), csbk.getPriority(), csbk.getServiceKind(), csbk.getServiceOptions(), csbk.getServiceExtra(), csbk.getSrcId(), csbk.getDstId()); + LogMessage(LOG_NET, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), serviceKind = $%02X, serviceOptions = $%02X, serviceExtra = $%02X, srcId = %u, dstId = %u", + m_slot->m_slotNo, csbk.getServiceKind(), csbk.getServiceOptions(), csbk.getServiceExtra(), csbk.getSrcId(), csbk.getDstId()); } } break; @@ -618,19 +666,346 @@ void ControlSignaling::writeRF_CSBK(lc::CSBK csbk, bool clearBeforeWrite) /// Helper to write a deny packet. /// /// -/// -void ControlSignaling::writeRF_CSBK_ACK_RSP(uint8_t reason, uint8_t service) +/// +void ControlSignaling::writeRF_CSBK_ACK_RSP(uint8_t reason, uint8_t responseInfo) { lc::CSBK csbk = lc::CSBK(m_slot->m_siteData, m_slot->m_idenEntry, m_slot->m_dumpCSBKData); csbk.setVerbose(m_dumpCSBKData); csbk.setCSBKO(CSBKO_ACK_RSP); csbk.setFID(FID_ETSI); + csbk.setResponse(responseInfo); csbk.setReason(reason); writeRF_CSBK(csbk); } +/// +/// Helper to write a deny packet. +/// +/// +/// +void ControlSignaling::writeRF_CSBK_NACK_RSP(uint8_t reason, uint8_t service) +{ + lc::CSBK csbk = lc::CSBK(m_slot->m_siteData, m_slot->m_idenEntry, m_slot->m_dumpCSBKData); + csbk.setVerbose(m_dumpCSBKData); + csbk.setCSBKO(CSBKO_NACK_RSP); + csbk.setFID(FID_ETSI); + + csbk.setServiceKind(service); + csbk.setReason(reason); + + writeRF_CSBK(csbk); +} + +/// +/// Helper to write a grant packet. +/// +/// +/// +/// +/// +/// +/// +/// +/// +bool ControlSignaling::writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp, bool skip, bool net, bool skipNetCheck) +{ + Slot *m_tscc = m_slot->m_dmr->getTSCCSlot(); + + bool emergency = ((serviceOptions & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag + bool privacy = ((serviceOptions & 0xFFU) & 0x40U) == 0x40U; // Privacy Flag + bool broadcast = ((serviceOptions & 0xFFU) & 0x10U) == 0x10U; // Broadcast Flag + uint8_t priority = ((serviceOptions & 0xFFU) & 0x03U); // Priority + + lc::CSBK csbk = lc::CSBK(m_slot->m_siteData, m_slot->m_idenEntry, m_slot->m_dumpCSBKData); + csbk.setVerbose(m_dumpCSBKData); + csbk.setFID(FID_ETSI); + + if (dstId == DMR_WUID_ALL) { + return true; // do not generate grant packets for $FFFF (All Call) TGID + } + + // do we have a network connection and are we handling grants at the network? + if (m_tscc->m_network != NULL) { + if (m_tscc->m_network->isHandlingChGrants() && m_tscc->m_siteData.netActive() && !skipNetCheck) { + return m_tscc->m_network->writeGrantReq(grp, srcId, dstId); + } + } + + // are we skipping checking? + if (!skip) { + if (m_slot->m_rfState != RS_RF_LISTENING && m_slot->m_rfState != RS_RF_DATA) { + if (!net) { + LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId); + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); + + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId); + m_slot->m_rfState = RS_RF_REJECTED; + } + + return false; + } + + if (m_slot->m_netState != RS_NET_IDLE && dstId == m_slot->m_netLastDstId) { + if (!net) { + LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_VOICE_CALL (Voice Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId); + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); + + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId); + m_slot->m_rfState = RS_RF_REJECTED; + } + + return false; + } + + // don't transmit grants if the destination ID's don't match and the network TG hang timer is running + if (m_slot->m_rfLastDstId != 0U) { + if (m_slot->m_rfLastDstId != dstId && (m_slot->m_rfTGHang.isRunning() && !m_slot->m_rfTGHang.hasExpired())) { + if (!net) { + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); + m_slot->m_rfState = RS_RF_REJECTED; + } + + return false; + } + } + + if (!m_tscc->m_affiliations->isGranted(dstId)) { + if (!m_tscc->m_affiliations->isRFChAvailable()) { + if (grp) { + if (!net) { + LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_VOICE_CALL (Group Voice Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId); + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_SYS_BUSY, (grp) ? 1U : 0U); + + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", m_tscc->m_slotNo, srcId, dstId); + m_slot->m_rfState = RS_RF_REJECTED; + } + + return false; + } + else { + if (!net) { + LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_VOICE_CALL (Individual Voice Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId); + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_SYS_BUSY, (grp) ? 1U : 0U); + + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", m_tscc->m_slotNo, srcId, dstId); + m_slot->m_rfState = RS_RF_REJECTED; + } + + return false; + } + } + else { + if (m_tscc->m_affiliations->grantCh(dstId, GRANT_TIMER_TIMEOUT)) { + uint32_t chNo = m_tscc->m_affiliations->getGrantedCh(dstId); + csbk.setLogicalCh1(chNo); + csbk.setSlotNo(0U); + + //m_tscc->m_siteData.setChCnt(m_tscc->m_affiliations->getRFChCnt() + m_tscc->m_affiliations->getGrantedRFChCnt()); + } + } + } + else { + uint32_t chNo = m_tscc->m_affiliations->getGrantedCh(dstId); + csbk.setLogicalCh1(chNo); + csbk.setSlotNo(0U); + + m_tscc->m_affiliations->touchGrant(dstId); + } + } + + if (grp) { + if (!net) { + ::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); + } + + if (m_verbose) { + LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_VOICE_CALL (Group Voice Call), emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", + m_tscc->m_slotNo, emergency, privacy, broadcast, priority, csbk.getLogicalCh1(), csbk.getSlotNo(), srcId, dstId); + } + + // transmit group grant + csbk.setCSBKO(CSBKO_TV_GRANT); + if (broadcast) + csbk.setCSBKO(CSBKO_BTV_GRANT); + csbk.setSrcId(srcId); + csbk.setDstId(dstId); + csbk.setEmergency(emergency); + + writeRF_CSBK(csbk); + } + else { + if (!net) { + ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); + } + + if (m_verbose) { + LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_VOICE_CALL (Individual Voice Call), emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", + m_tscc->m_slotNo, emergency, privacy, broadcast, priority, csbk.getLogicalCh1(), csbk.getSlotNo(), srcId, dstId); + } + + // transmit private grant + csbk.setCSBKO(CSBKO_PV_GRANT); + csbk.setSrcId(srcId); + csbk.setDstId(dstId); + csbk.setEmergency(emergency); + + writeRF_CSBK(csbk); + } + + return true; +} + +/// +/// Helper to write a data grant packet. +/// +/// +/// +/// +/// +/// +/// +/// +bool ControlSignaling::writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp, bool skip, bool net) +{ + Slot *m_tscc = m_slot->m_dmr->getTSCCSlot(); + + bool emergency = ((serviceOptions & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag + bool privacy = ((serviceOptions & 0xFFU) & 0x40U) == 0x40U; // Privacy Flag + bool broadcast = ((serviceOptions & 0xFFU) & 0x10U) == 0x10U; // Broadcast Flag + uint8_t priority = ((serviceOptions & 0xFFU) & 0x03U); // Priority + + lc::CSBK csbk = lc::CSBK(m_slot->m_siteData, m_slot->m_idenEntry, m_slot->m_dumpCSBKData); + csbk.setVerbose(m_dumpCSBKData); + csbk.setFID(FID_ETSI); + + if (dstId == DMR_WUID_ALL) { + return true; // do not generate grant packets for $FFFF (All Call) TGID + } + + // are we skipping checking? + if (!skip) { + if (m_slot->m_rfState != RS_RF_LISTENING && m_slot->m_rfState != RS_RF_DATA) { + if (!net) { + LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_DATA_CALL (Data Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId); + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); + + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId); + m_slot->m_rfState = RS_RF_REJECTED; + } + + return false; + } + + if (m_slot->m_netState != RS_NET_IDLE && dstId == m_slot->m_netLastDstId) { + if (!net) { + LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_DATA_CALL (Data Call) denied, traffic in progress, dstId = %u", m_tscc->m_slotNo, dstId); + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); + + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u denied", m_tscc->m_slotNo, srcId, dstId); + m_slot->m_rfState = RS_RF_REJECTED; + } + + return false; + } + + // don't transmit grants if the destination ID's don't match and the network TG hang timer is running + if (m_slot->m_rfLastDstId != 0U) { + if (m_slot->m_rfLastDstId != dstId && (m_slot->m_rfTGHang.isRunning() && !m_slot->m_rfTGHang.hasExpired())) { + if (!net) { + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_TGT_BUSY, (grp) ? 1U : 0U); + m_slot->m_rfState = RS_RF_REJECTED; + } + + return false; + } + } + + if (!m_tscc->m_affiliations->isGranted(dstId)) { + if (!m_tscc->m_affiliations->isRFChAvailable()) { + if (grp) { + if (!net) { + LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_DATA_CALL (Group Data Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId); + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_SYS_BUSY, (grp) ? 1U : 0U); + + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", m_tscc->m_slotNo, srcId, dstId); + m_slot->m_rfState = RS_RF_REJECTED; + } + + return false; + } + else { + if (!net) { + LogWarning(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_DATA_CALL (Individual Data Call) queued, no channels available, dstId = %u", m_tscc->m_slotNo, dstId); + writeRF_CSBK_ACK_RSP(TS_DENY_RSN_SYS_BUSY, (grp) ? 1U : 0U); + + ::ActivityLog("DMR", true, "Slot %u group grant request %u to TG %u queued", m_tscc->m_slotNo, srcId, dstId); + m_slot->m_rfState = RS_RF_REJECTED; + } + + return false; + } + } + else { + if (m_tscc->m_affiliations->grantCh(dstId, GRANT_TIMER_TIMEOUT)) { + uint32_t chNo = m_tscc->m_affiliations->getGrantedCh(dstId); + csbk.setLogicalCh1(chNo); + csbk.setSlotNo(0U); + + //m_tscc->m_siteData.setChCnt(m_tscc->m_affiliations->getRFChCnt() + m_tscc->m_affiliations->getGrantedRFChCnt()); + } + } + } + else { + uint32_t chNo = m_tscc->m_affiliations->getGrantedCh(dstId); + csbk.setLogicalCh1(chNo); + csbk.setSlotNo(0U); + + m_tscc->m_affiliations->touchGrant(dstId); + } + } + + if (grp) { + if (!net) { + ::ActivityLog("DMR", true, "Slot %u group grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); + } + + if (m_verbose) { + LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_GRP_DATA_CALL (Group Data Call), emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", + m_tscc->m_slotNo, emergency, privacy, broadcast, priority, csbk.getLogicalCh1(), csbk.getSlotNo(), srcId, dstId); + } + + // transmit group grant + csbk.setCSBKO(CSBKO_TD_GRANT); + csbk.setSrcId(srcId); + csbk.setDstId(dstId); + csbk.setEmergency(emergency); + + writeRF_CSBK(csbk); + } + else { + if (!net) { + ::ActivityLog("DMR", true, "Slot %u individual grant request from %u to TG %u", m_tscc->m_slotNo, srcId, dstId); + } + + if (m_verbose) { + LogMessage((net) ? LOG_NET : LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_RAND (Random Access), SVC_KIND_IND_DATA_CALL (Individual Data Call), emerg = %u, privacy = %u, broadcast = %u, prio = %u, chNo = %u, slot = %u, srcId = %u, dstId = %u", + m_tscc->m_slotNo, emergency, privacy, broadcast, priority, csbk.getLogicalCh1(), csbk.getSlotNo(), srcId, dstId); + } + + // transmit private grant + csbk.setCSBKO(CSBKO_PD_GRANT); + csbk.setSrcId(srcId); + csbk.setDstId(dstId); + csbk.setEmergency(emergency); + + writeRF_CSBK(csbk); + } + + return true; +} + /// /// Helper to write a unit registration response packet. /// diff --git a/dmr/packet/ControlSignaling.h b/dmr/packet/ControlSignaling.h index 1847342f..a7dc4968 100644 --- a/dmr/packet/ControlSignaling.h +++ b/dmr/packet/ControlSignaling.h @@ -91,7 +91,13 @@ namespace dmr void writeRF_CSBK(lc::CSBK csbk, bool clearBeforeWrite = false); /// Helper to write a ACK RSP packet. - void writeRF_CSBK_ACK_RSP(uint8_t reason, uint8_t service); + void writeRF_CSBK_ACK_RSP(uint8_t reason, uint8_t responseInfo); + /// Helper to write a NACK RSP packet. + void writeRF_CSBK_NACK_RSP(uint8_t reason, uint8_t service); + /// Helper to write a grant packet. + bool writeRF_CSBK_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp, bool skip = false, bool net = false, bool skipNetCheck = false); + /// Helper to write a data grant packet. + bool writeRF_CSBK_Data_Grant(uint32_t srcId, uint32_t dstId, uint8_t serviceOptions, bool grp, bool skip = false, bool net = false); /// Helper to write a unit registration response packet. void writeRF_CSBK_U_Reg_Rsp(uint32_t srcId, uint8_t serviceOptions);