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);