diff --git a/configs/config.example.yml b/configs/config.example.yml index 0ac084d6..6a72c049 100644 --- a/configs/config.example.yml +++ b/configs/config.example.yml @@ -129,8 +129,14 @@ protocols: # Flag indicating whether or not the source ID validation before granting disabled. disableGrantSourceIdCheck: false + # Flag indicating whether or not network calls will generate a channel grant. + # (This applies only in conventional operations where channel granting is utilized and RF-only talkgroup + # steering is required.) + disableNetworkGrant: false # Flag indicating whether or not a TGID will be tested for affiliations before being granted. ignoreAffiliationCheck: false + # Flag indicating the host should send a network grant demand for conventional traffic. + convNetGrantDemand: false # Flag indicating whether or not received RF embedded LC data only should be transmitted. embeddedLCOnly: false # Flag indicating whether talker alias data should be dumped to the log. diff --git a/src/bridge/HostBridge.cpp b/src/bridge/HostBridge.cpp index ef52fb79..d2bf6786 100644 --- a/src/bridge/HostBridge.cpp +++ b/src/bridge/HostBridge.cpp @@ -1687,6 +1687,11 @@ void HostBridge::encodeDMRAudioFrame(uint8_t* pcm, uint32_t forcedSrcId, uint32_ dmrData.setSrcId(srcId); dmrData.setDstId(dstId); dmrData.setFLCO(FLCO::GROUP); + if (m_grantDemand) { + dmrData.setControl(0x80U); // DMR remote grant demand flag + } else { + dmrData.setControl(0U); + } dmrData.setN(m_dmrN); dmrData.setSeqNo(m_dmrSeqNo); dmrData.setBER(0U); diff --git a/src/common/dmr/data/NetData.cpp b/src/common/dmr/data/NetData.cpp index 6e07b65a..fe2d33d9 100644 --- a/src/common/dmr/data/NetData.cpp +++ b/src/common/dmr/data/NetData.cpp @@ -30,6 +30,7 @@ NetData::NetData(const NetData& data) : m_srcId(data.m_srcId), m_dstId(data.m_dstId), m_flco(data.m_flco), + m_control(data.m_control), m_n(data.m_n), m_seqNo(data.m_seqNo), m_dataType(data.m_dataType), @@ -48,6 +49,7 @@ NetData::NetData() : m_srcId(0U), m_dstId(0U), m_flco(FLCO::GROUP), + m_control(0U), m_n(0U), m_seqNo(0U), m_dataType(DataType::IDLE), @@ -76,6 +78,7 @@ NetData& NetData::operator=(const NetData& data) m_srcId = data.m_srcId; m_dstId = data.m_dstId; m_flco = data.m_flco; + m_control = data.m_control; m_dataType = data.m_dataType; m_seqNo = data.m_seqNo; m_n = data.m_n; diff --git a/src/common/dmr/data/NetData.h b/src/common/dmr/data/NetData.h index b089ca23..8c753e7d 100644 --- a/src/common/dmr/data/NetData.h +++ b/src/common/dmr/data/NetData.h @@ -85,6 +85,11 @@ namespace dmr */ __PROPERTY(defines::FLCO::E, flco, FLCO); + /** + * @brief + */ + __PROPERTY(uint8_t, control, Control); + /** * @brief */ diff --git a/src/common/network/BaseNetwork.cpp b/src/common/network/BaseNetwork.cpp index cdfa4e16..c34f4a76 100644 --- a/src/common/network/BaseNetwork.cpp +++ b/src/common/network/BaseNetwork.cpp @@ -742,7 +742,7 @@ UInt8Array BaseNetwork::createDMR_Message(uint32_t& length, const uint32_t strea uint32_t slotNo = data.getSlotNo(); - buffer[14U] = 0U; // Control Bits + buffer[14U] = data.getControl(); // Control Bits // Individual slot disabling if (slotNo == 1U && !m_slot1) { diff --git a/src/host/dmr/Control.cpp b/src/host/dmr/Control.cpp index a0565223..1e16f7c5 100644 --- a/src/host/dmr/Control.cpp +++ b/src/host/dmr/Control.cpp @@ -176,6 +176,18 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa m_slot1->m_ignoreAffiliationCheck = ignoreAffiliationCheck; m_slot2->m_ignoreAffiliationCheck = ignoreAffiliationCheck; + /* + ** Network Grant Disables + */ + bool disableNetworkGrant = dmrProtocol["disableNetworkGrant"].as(false); + m_slot1->m_disableNetworkGrant = disableNetworkGrant; + m_slot2->m_disableNetworkGrant = disableNetworkGrant; + + bool convNetGrantDemand = dmrProtocol["convNetGrantDemand"].as(false); + m_slot1->m_convNetGrantDemand = convNetGrantDemand; + m_slot2->m_convNetGrantDemand = convNetGrantDemand; + + if (printOptions) { if (enableTSCC) { LogInfo(" TSCC Slot: %u", m_tsccSlotNo); @@ -185,6 +197,9 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa LogInfo(" TSCC Disable Grant Source ID Check: yes"); } } + if (disableNetworkGrant) { + LogInfo(" Disable Network Grants: yes"); + } LogInfo(" Ignore Affiliation Check: %s", ignoreAffiliationCheck ? "yes" : "no"); LogInfo(" Notify Control: %s", notifyCC ? "yes" : "no"); @@ -192,6 +207,7 @@ void Control::setOptions(yaml::Node& conf, bool supervisor, ::lookups::VoiceChDa LogInfo(" Frame Loss Threshold: %u", frameLossThreshold); LogInfo(" Verify Registration: %s", Slot::m_verifyReg ? "yes" : "no"); + LogInfo(" Conventional Network Grant Demand: %s", convNetGrantDemand ? "yes" : "no"); } } @@ -660,6 +676,8 @@ void Control::processNetwork() uint32_t srcId = __GET_UINT16(buffer, 5U); uint32_t dstId = __GET_UINT16(buffer, 8U); + uint8_t controlByte = buffer[14U]; + FLCO::E flco = (buffer[15U] & 0x40U) == 0x40U ? FLCO::PRIVATE : FLCO::GROUP; uint32_t slotNo = (buffer[15U] & 0x80U) == 0x80U ? 2U : 1U; @@ -691,6 +709,8 @@ void Control::processNetwork() data.setDstId(dstId); data.setFLCO(flco); + data.setControl(controlByte); + bool dataSync = (buffer[15U] & 0x20U) == 0x20U; bool voiceSync = (buffer[15U] & 0x10U) == 0x10U; diff --git a/src/host/dmr/Slot.cpp b/src/host/dmr/Slot.cpp index 4cbe363d..249d355a 100644 --- a/src/host/dmr/Slot.cpp +++ b/src/host/dmr/Slot.cpp @@ -152,6 +152,8 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz m_enableTSCC(false), m_dedicatedTSCC(false), m_ignoreAffiliationCheck(false), + m_disableNetworkGrant(false), + m_convNetGrantDemand(false), m_tsccPayloadDstId(0U), m_tsccPayloadSrcId(0U), m_tsccPayloadGroup(false), @@ -426,9 +428,54 @@ void Slot::processNetwork(const data::NetData& dmrData) DataType::E dataType = dmrData.getDataType(); // ignore non-CSBK data destined for the TSCC slot - if (m_enableTSCC && m_dedicatedTSCC && m_slotNo == m_dmr->m_tsccSlotNo && - dataType != DataType::CSBK) { - return; + if (m_enableTSCC && m_dedicatedTSCC && m_slotNo == m_dmr->m_tsccSlotNo) { + switch (dataType) + { + case DataType::CSBK: + break; + case DataType::VOICE_LC_HEADER: + case DataType::DATA_HEADER: + { + bool grantDemand = (dmrData.getControl() & 0x80U) == 0x80U; + bool unitToUnit = (dmrData.getControl() & 0x01U) == 0x01U; + + if (grantDemand) { + if (m_disableNetworkGrant) { + return; + } + + // if we're non-dedicated control, and if we're not in a listening or idle state, ignore any grant + // demands + if (!m_dedicatedTSCC && (m_rfState != RS_RF_LISTENING || m_netState != RS_NET_IDLE)) { + return; + } + + // validate source RID + if (!acl::AccessControl::validateSrcId(dmrData.getSrcId())) { + return; + } + + // validate the target ID, if the target is a talkgroup + if (!acl::AccessControl::validateTGId(dmrData.getSlotNo(), dmrData.getDstId())) { + return; + } + + if (m_verbose) { + LogMessage(LOG_NET, "DMR Slot %u, remote grant demand, srcId = %u, dstId = %u, unitToUnit = %u", + m_slotNo, dmrData.getSrcId(), dmrData.getDstId(), unitToUnit); + } + + // perform grant response logic + if (dataType == DataType::VOICE_LC_HEADER) + m_control->writeRF_CSBK_Grant(dmrData.getSrcId(), dmrData.getDstId(), 4U, !unitToUnit, true); + if (dataType == DataType::DATA_HEADER) + m_control->writeRF_CSBK_Data_Grant(dmrData.getSrcId(), dmrData.getDstId(), 4U, !unitToUnit, true); + } + } + return; + default: + return; + } } switch (dataType) @@ -1203,18 +1250,18 @@ void Slot::notifyCC_TouchGrant(uint32_t dstId) /* Write data frame to the network. */ -void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, uint8_t errors, bool noSequence) +void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, uint8_t control, uint8_t errors, bool noSequence) { assert(data != nullptr); assert(m_rfLC != nullptr); - writeNetwork(data, dataType, m_rfLC->getFLCO(), m_rfLC->getSrcId(), m_rfLC->getDstId(), errors); + writeNetwork(data, dataType, m_rfLC->getFLCO(), m_rfLC->getSrcId(), m_rfLC->getDstId(), control, errors); } /* Write data frame to the network. */ void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, FLCO::E flco, uint32_t srcId, - uint32_t dstId, uint8_t errors, bool noSequence) + uint32_t dstId, uint8_t control, uint8_t errors, bool noSequence) { assert(data != nullptr); @@ -1230,6 +1277,7 @@ void Slot::writeNetwork(const uint8_t* data, DataType::E dataType, FLCO::E flco, dmrData.setSrcId(srcId); dmrData.setDstId(dstId); dmrData.setFLCO(flco); + dmrData.setControl(control); dmrData.setN(m_voice->m_rfN); dmrData.setSeqNo(m_rfSeqNo); dmrData.setBER(errors); diff --git a/src/host/dmr/Slot.h b/src/host/dmr/Slot.h index 185de43f..6f6ff11b 100644 --- a/src/host/dmr/Slot.h +++ b/src/host/dmr/Slot.h @@ -384,6 +384,8 @@ namespace dmr bool m_enableTSCC; bool m_dedicatedTSCC; bool m_ignoreAffiliationCheck; + bool m_disableNetworkGrant; + bool m_convNetGrantDemand; uint32_t m_tsccPayloadDstId; uint32_t m_tsccPayloadSrcId; @@ -488,10 +490,11 @@ namespace dmr * @brief Write data frame to the network. * @param[in] data Buffer containing frame data to write to the network. * @param dataType DMR Data Type for this frame. + * @param control Control Byte. * @param errors Number of bit errors detected for this frame. * @param noSequence Flag indicating this frame carries no sequence number. */ - void writeNetwork(const uint8_t* data, defines::DataType::E dataType, uint8_t errors = 0U, bool noSequence = false); + void writeNetwork(const uint8_t* data, defines::DataType::E dataType, uint8_t control, uint8_t errors = 0U, bool noSequence = false); /** * @brief Write data frame to the network. * @param[in] data Buffer containing frame data to write to the network. @@ -499,11 +502,12 @@ namespace dmr * @param flco Full-Link Control Opcode. * @param srcId Source Radio ID. * @param dstId Destination ID. + * @param control Control Byte. * @param errors Number of bit errors detected for this frame. * @param noSequence Flag indicating this frame carries no sequence number. */ void writeNetwork(const uint8_t* data, defines::DataType::E dataType, defines::FLCO::E flco, uint32_t srcId, - uint32_t dstId, uint8_t errors = 0U, bool noSequence = false); + uint32_t dstId, uint8_t control, uint8_t errors = 0U, bool noSequence = false); /** * @brief Helper to write RF end of frame data. diff --git a/src/host/dmr/packet/ControlSignaling.cpp b/src/host/dmr/packet/ControlSignaling.cpp index 304d345f..d3d5665d 100644 --- a/src/host/dmr/packet/ControlSignaling.cpp +++ b/src/host/dmr/packet/ControlSignaling.cpp @@ -379,7 +379,7 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) if (m_slot->m_duplex) m_slot->addFrame(data); - m_slot->writeNetwork(data, DataType::CSBK, gi ? FLCO::GROUP : FLCO::PRIVATE, srcId, dstId, 0U, true); + m_slot->writeNetwork(data, DataType::CSBK, gi ? FLCO::GROUP : FLCO::PRIVATE, srcId, dstId, 0U, 0U, true); } return true; @@ -765,7 +765,7 @@ void ControlSignaling::writeNet_CSBK(lc::CSBK* csbk) if (m_slot->m_duplex) m_slot->addFrame(data); - m_slot->writeNetwork(data, DataType::CSBK, csbk->getGI() ? FLCO::GROUP : FLCO::PRIVATE, csbk->getSrcId(), csbk->getDstId(), 0U, true); + m_slot->writeNetwork(data, DataType::CSBK, csbk->getGI() ? FLCO::GROUP : FLCO::PRIVATE, csbk->getSrcId(), csbk->getDstId(), 0U, 0U, true); } /* diff --git a/src/host/dmr/packet/Data.cpp b/src/host/dmr/packet/Data.cpp index cc00837b..72bf7118 100644 --- a/src/host/dmr/packet/Data.cpp +++ b/src/host/dmr/packet/Data.cpp @@ -105,7 +105,7 @@ bool Data::process(uint8_t* data, uint32_t len) data[0U] = modem::TAG_EOT; data[1U] = 0x00U; - m_slot->writeNetwork(data, DataType::TERMINATOR_WITH_LC); + m_slot->writeNetwork(data, DataType::TERMINATOR_WITH_LC, 0U); if (m_slot->m_duplex) { for (uint32_t i = 0U; i < m_slot->m_hangCount; i++) @@ -250,7 +250,11 @@ bool Data::process(uint8_t* data, uint32_t len) if (m_slot->m_duplex && m_repeatDataPacket) m_slot->addFrame(data); - m_slot->writeNetwork(data, DataType::DATA_HEADER); + uint8_t controlByte = 0U; + if (m_slot->m_convNetGrantDemand) + controlByte |= 0x80U; // Grant Demand Flag + + m_slot->writeNetwork(data, DataType::DATA_HEADER, controlByte); m_slot->m_rfState = RS_RF_DATA; m_slot->m_rfLastDstId = dstId; @@ -323,7 +327,7 @@ bool Data::process(uint8_t* data, uint32_t len) // convert the Data Sync to be from the BS or MS as needed Sync::addDMRDataSync(data + 2U, m_slot->m_duplex); - m_slot->writeNetwork(data, dataType); + m_slot->writeNetwork(data, dataType, 0U); if (m_slot->m_duplex && m_repeatDataPacket) { m_slot->addFrame(data); diff --git a/src/host/dmr/packet/Voice.cpp b/src/host/dmr/packet/Voice.cpp index 224014b9..ba25180a 100644 --- a/src/host/dmr/packet/Voice.cpp +++ b/src/host/dmr/packet/Voice.cpp @@ -199,7 +199,11 @@ bool Voice::process(uint8_t* data, uint32_t len) m_slot->addFrame(data); } - m_slot->writeNetwork(data, DataType::VOICE_LC_HEADER); + uint8_t controlByte = 0U; + if (m_slot->m_convNetGrantDemand) + controlByte |= 0x80U; // Grant Demand Flag + + m_slot->writeNetwork(data, DataType::VOICE_LC_HEADER, controlByte); m_slot->m_rfState = RS_RF_AUDIO; m_slot->m_rfLastDstId = dstId; @@ -246,7 +250,7 @@ bool Voice::process(uint8_t* data, uint32_t len) if (m_slot->m_duplex) m_slot->addFrame(data); - m_slot->writeNetwork(data, DataType::VOICE_PI_HEADER); + m_slot->writeNetwork(data, DataType::VOICE_PI_HEADER, 0U); if (m_verbose) { LogMessage(LOG_RF, DMR_DT_VOICE_PI_HEADER ", slot = %u, algId = %u, kId = %u, dstId = %u", m_slot->m_slotNo, @@ -304,7 +308,7 @@ bool Voice::process(uint8_t* data, uint32_t len) if (m_slot->m_duplex) m_slot->addFrame(data); - m_slot->writeNetwork(data, DataType::VOICE_SYNC, errors); + m_slot->writeNetwork(data, DataType::VOICE_SYNC, 0U, errors); return true; } @@ -458,7 +462,7 @@ bool Voice::process(uint8_t* data, uint32_t len) data[0U] = modem::TAG_DATA; data[1U] = 0x00U; - m_slot->writeNetwork(data, DataType::VOICE, errors); + m_slot->writeNetwork(data, DataType::VOICE, 0U, errors); if (m_embeddedLCOnly) { // Only send the previously received LC @@ -561,7 +565,11 @@ bool Voice::process(uint8_t* data, uint32_t len) m_slot->addFrame(start); } - m_slot->writeNetwork(start, DataType::VOICE_LC_HEADER); + uint8_t controlByte = 0U; + if (m_slot->m_convNetGrantDemand) + controlByte |= 0x80U; // Grant Demand Flag + + m_slot->writeNetwork(start, DataType::VOICE_LC_HEADER, controlByte); m_rfN = data[1U] & 0x0FU; @@ -610,7 +618,7 @@ bool Voice::process(uint8_t* data, uint32_t len) if (m_slot->m_duplex) m_slot->addFrame(data); - m_slot->writeNetwork(data, DataType::VOICE, errors); + m_slot->writeNetwork(data, DataType::VOICE, 0U, errors); m_slot->m_rfState = RS_RF_AUDIO; @@ -645,6 +653,8 @@ void Voice::processNetwork(const data::NetData& dmrData) if (m_slot->m_netState == RS_NET_AUDIO) return; + + lc::FullLC fullLC; std::unique_ptr lc = fullLC.decode(data + 2U, DataType::VOICE_LC_HEADER); if (lc == nullptr) {