diff --git a/dmr/Control.cpp b/dmr/Control.cpp index 155e0a39..928aed7a 100644 --- a/dmr/Control.cpp +++ b/dmr/Control.cpp @@ -110,12 +110,15 @@ Control::~Control() /// Helper to set DMR configuration options. /// /// Instance of the ConfigINI class. -/// -/// -/// -/// +/// Voice Channel Number list. +/// Voice Channel data map. +/// DMR Network ID. +/// DMR Site ID. +/// Channel ID. +/// Channel Number. /// -void Control::setOptions(yaml::Node& conf, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions) +void Control::setOptions(yaml::Node& conf, const std::vector voiceChNo, const std::unordered_map voiceChData, + uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions) { yaml::Node systemConf = conf["system"]; yaml::Node dmrProtocol = conf["protocols"]["dmr"]; @@ -139,7 +142,7 @@ void Control::setOptions(yaml::Node& conf, uint32_t netId, uint8_t siteId, uint8 dedicatedTSCC = false; } - Slot::setSiteData(netId, siteId, channelId, channelNo, dedicatedTSCC); + Slot::setSiteData(voiceChNo, voiceChData, netId, siteId, channelId, channelNo, dedicatedTSCC); Slot::setAlohaConfig(nRandWait, backOff); m_tsccSlotNo = (uint8_t)control["slot"].as(0U); @@ -328,9 +331,20 @@ void Control::clock() /// Permits a TGID on a non-authoritative host. /// /// -void Control::permittedTG(uint32_t dstId) +/// +void Control::permittedTG(uint32_t dstId, uint8_t slot) { - // TODO TODO + switch (slot) { + case 1U: + m_slot1->permittedTG(dstId); + break; + case 2U: + m_slot2->permittedTG(dstId); + break; + default: + LogError(LOG_NET, "DMR, invalid slot, slotNo = %u", slot); + break; + } } /// diff --git a/dmr/Control.h b/dmr/Control.h index 369434fe..4a662c18 100644 --- a/dmr/Control.h +++ b/dmr/Control.h @@ -41,6 +41,7 @@ #include "lookups/IdenTableLookup.h" #include "lookups/RadioIdLookup.h" #include "lookups/TalkgroupIdLookup.h" +#include "lookups/AffiliationLookup.h" #include "yaml/Yaml.h" namespace dmr @@ -68,7 +69,8 @@ namespace dmr ~Control(); /// Helper to set DMR configuration options. - void setOptions(yaml::Node& conf, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions); + void setOptions(yaml::Node& conf, const std::vector voiceChNo, const std::unordered_map voiceChData, + uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions); /// Gets a flag indicating whether the DMR control channel is running. bool getCCRunning() { return m_ccRunning; } @@ -91,7 +93,7 @@ namespace dmr void clock(); /// Permits a TGID on a non-authoritative host. - void permittedTG(uint32_t dstId); + void permittedTG(uint32_t dstId, uint8_t slot); /// Helper to return the slot carrying the TSCC. Slot* getTSCCSlot() const; diff --git a/dmr/Slot.cpp b/dmr/Slot.cpp index 5af66d1e..ddcbce84 100644 --- a/dmr/Slot.cpp +++ b/dmr/Slot.cpp @@ -125,6 +125,7 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz m_rfLastDstId(0U), m_netState(RS_NET_IDLE), m_netLastDstId(0U), + m_permittedDstId(0U), m_rfLC(nullptr), m_rfPrivacyLC(nullptr), m_rfDataHeader(nullptr), @@ -538,6 +539,19 @@ void Slot::clock() } } +/// +/// Permits a TGID on a non-authoritative host. +/// +/// +void Slot::permittedTG(uint32_t dstId) +{ + if (!m_authoritative) { + return; + } + + m_permittedDstId = dstId; +} + /// /// Helper to change the debug and verbose state. /// @@ -645,12 +659,15 @@ void Slot::init(Control* dmr, bool authoritative, uint32_t colorCode, SiteData s /// /// Sets local configured site data. /// +/// Voice Channel Number list. +/// Voice Channel data map. /// DMR Network ID. /// DMR Site ID. /// Channel ID. /// Channel Number. /// -void Slot::setSiteData(uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReg) +void Slot::setSiteData(const std::vector voiceChNo, const std::unordered_map voiceChData, + uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReg) { m_siteData = SiteData(SITE_MODEL_SMALL, netId, siteId, 3U, requireReg); m_channelNo = channelNo; @@ -664,6 +681,13 @@ void Slot::setSiteData(uint32_t netId, uint8_t siteId, uint8_t channelId, uint32 } } + std::vector availCh = voiceChNo; + for (auto it = availCh.begin(); it != availCh.end(); ++it) { + m_affiliations->addRFCh(*it); + } + + m_affiliations->setRFChData(voiceChData); + lc::CSBK::setSiteData(m_siteData); } diff --git a/dmr/Slot.h b/dmr/Slot.h index 7cd6a696..8354a1c6 100644 --- a/dmr/Slot.h +++ b/dmr/Slot.h @@ -94,6 +94,9 @@ namespace dmr /// Updates the slot processor. void clock(); + /// Permits a TGID on a non-authoritative host. + void permittedTG(uint32_t dstId); + /// Gets instance of the ControlSignaling class. packet::ControlSignaling* control() { return m_control; } @@ -110,7 +113,8 @@ namespace dmr network::BaseNetwork* network, bool duplex, lookups::RadioIdLookup* ridLookup, lookups::TalkgroupIdLookup* tidLookup, lookups::IdenTableLookup* idenTable, lookups::RSSIInterpolator* rssiMapper, uint32_t jitter, bool verbose); /// Sets local configured site data. - static void setSiteData(uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReq); + static void setSiteData(const std::vector voiceChNo, const std::unordered_map voiceChData, + uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool requireReq); /// Sets TSCC Aloha configuration. static void setAlohaConfig(uint8_t nRandWait, uint8_t backOff); @@ -132,6 +136,8 @@ namespace dmr RPT_NET_STATE m_netState; uint32_t m_netLastDstId; + uint32_t m_permittedDstId; + std::unique_ptr m_rfLC; std::unique_ptr m_rfPrivacyLC; std::unique_ptr m_rfDataHeader; diff --git a/dmr/packet/ControlSignaling.cpp b/dmr/packet/ControlSignaling.cpp index 32ecee4f..851db1bc 100644 --- a/dmr/packet/ControlSignaling.cpp +++ b/dmr/packet/ControlSignaling.cpp @@ -215,7 +215,11 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) // 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, isp->getServiceOptions(), false); + if (m_slot->m_authoritative) { + writeRF_CSBK_Grant(srcId, dstId, isp->getServiceOptions(), false); + } else { + m_slot->m_network->writeGrantReq(modem::DVM_STATE::STATE_DMR, srcId, dstId, m_slot->m_slotNo, true); + } break; case SVC_KIND_GRP_VOICE_CALL: // make sure control data is supported @@ -227,7 +231,11 @@ bool ControlSignaling::process(uint8_t* data, uint32_t len) // 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, srcId, dstId); - writeRF_CSBK_Grant(srcId, dstId, isp->getServiceOptions(), true); + if (m_slot->m_authoritative) { + writeRF_CSBK_Grant(srcId, dstId, isp->getServiceOptions(), true); + } else { + m_slot->m_network->writeGrantReq(modem::DVM_STATE::STATE_DMR, srcId, dstId, m_slot->m_slotNo, false); + } break; case SVC_KIND_IND_DATA_CALL: case SVC_KIND_IND_UDT_DATA_CALL: diff --git a/host/Host.cpp b/host/Host.cpp index 737fc6d0..ed054ea8 100644 --- a/host/Host.cpp +++ b/host/Host.cpp @@ -113,6 +113,8 @@ Host::Host(const std::string& confFile) : m_txFrequency(0U), m_channelId(0U), m_channelNo(0U), + m_voiceChNo(), + m_voiceChData(), m_idenTable(nullptr), m_ridLookup(nullptr), m_tidLookup(nullptr), @@ -431,7 +433,7 @@ int Host::run() dmr = std::unique_ptr(new dmr::Control(m_authoritative, m_dmrColorCode, callHang, m_dmrQueueSizeBytes, embeddedLCOnly, dumpTAData, m_timeout, m_rfTalkgroupHang, m_modem, m_network, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, jitter, dmrDumpDataPacket, dmrRepeatDataPacket, dmrDumpCsbkData, dmrDebug, dmrVerbose)); - dmr->setOptions(m_conf, m_dmrNetId, m_siteId, m_channelId, m_channelNo, true); + dmr->setOptions(m_conf, m_voiceChNo, m_voiceChData, m_dmrNetId, m_siteId, m_channelId, m_channelNo, true); if (dmrCtrlChannel) { dmr->setCCRunning(true); @@ -504,7 +506,7 @@ int Host::run() p25 = std::unique_ptr(new p25::Control(m_authoritative, m_p25NAC, callHang, m_p25QueueSizeBytes, m_modem, m_network, m_timeout, m_rfTalkgroupHang, m_duplex, m_ridLookup, m_tidLookup, m_idenTable, rssi, p25DumpDataPacket, p25RepeatDataPacket, p25DumpTsbkData, p25Debug, p25Verbose)); - p25->setOptions(m_conf, m_cwCallsign, m_voiceChNo, m_p25PatchSuperGroup, m_p25NetId, m_p25SysId, m_p25RfssId, + p25->setOptions(m_conf, m_cwCallsign, m_voiceChNo, m_voiceChData, m_p25PatchSuperGroup, m_p25NetId, m_p25SysId, m_p25RfssId, m_siteId, m_channelId, m_channelNo, true); if (p25CtrlChannel) { @@ -1847,8 +1849,8 @@ bool Host::readParams() ::LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X RCON Adddress %s:%u", m_channelId, chNo, rconAddress.c_str(), rconPort); - // TODO handle storing RCON data for voice channels - + VoiceChData data = VoiceChData(chNo, rconAddress, rconPort, rconPassword); + m_voiceChData[chNo] = data; m_voiceChNo.push_back(chNo); } @@ -1921,6 +1923,10 @@ bool Host::readParams() LogInfo(" P25 Network Id: $%05X", m_p25NetId); LogInfo(" P25 System Id: $%03X", m_p25SysId); LogInfo(" P25 RFSS Id: $%02X", m_p25RfssId); + + if (!m_authoritative) { + LogWarning(LOG_HOST, "Host is non-authoritative, this requires RCON to \"permit-tg\" for VCs and \"grant-tg\" for CCs!"); + } } else { LogInfo(" Modem Remote Control: yes"); diff --git a/host/Host.h b/host/Host.h index fd511f0a..6d925b09 100644 --- a/host/Host.h +++ b/host/Host.h @@ -36,12 +36,14 @@ #include "network/RemoteControl.h" #include "modem/Modem.h" #include "Timer.h" +#include "lookups/AffiliationLookup.h" #include "lookups/IdenTableLookup.h" #include "lookups/RadioIdLookup.h" #include "lookups/TalkgroupIdLookup.h" #include "yaml/Yaml.h" #include +#include // --------------------------------------------------------------------------- // Class Prototypes @@ -64,6 +66,11 @@ public: /// Executes the main modem host processing loop. int run(); + /// Gets the voice channel number list. + std::vector getVoiceChNo() const { return m_voiceChNo; } + /// Gets the voice channel data. + std::unordered_map getVoiceChData() const { return m_voiceChData; } + private: const std::string& m_confFile; yaml::Node m_conf; @@ -107,7 +114,9 @@ private: uint32_t m_txFrequency; uint8_t m_channelId; uint32_t m_channelNo; + std::vector m_voiceChNo; + std::unordered_map m_voiceChData; lookups::IdenTableLookup* m_idenTable; lookups::RadioIdLookup* m_ridLookup; diff --git a/lookups/AffiliationLookup.cpp b/lookups/AffiliationLookup.cpp index d619a893..ae2dd09e 100644 --- a/lookups/AffiliationLookup.cpp +++ b/lookups/AffiliationLookup.cpp @@ -44,6 +44,7 @@ using namespace lookups; /// Flag indicating whether verbose logging is enabled. AffiliationLookup::AffiliationLookup(const char* name, bool verbose) : m_rfChTable(), + m_rfChDataTable(), m_rfGrantChCnt(0U), m_unitRegTable(), m_grpAffTable(), @@ -401,6 +402,27 @@ uint32_t AffiliationLookup::getGrantedCh(uint32_t dstId) return 0U; } +/// +/// Helper to get RF channel data. +/// +/// +/// +VoiceChData AffiliationLookup::getRFChData(uint32_t chNo) const +{ + if (chNo == 0U) { + return VoiceChData(); + } + + VoiceChData data; + try { + data = m_rfChDataTable.at(chNo); + } catch (...) { + data = VoiceChData(); + } + + return VoiceChData(); +} + /// /// Updates the processor by the passed number of milliseconds. /// diff --git a/lookups/AffiliationLookup.h b/lookups/AffiliationLookup.h index e1151de6..ecf0abbd 100644 --- a/lookups/AffiliationLookup.h +++ b/lookups/AffiliationLookup.h @@ -36,6 +36,65 @@ namespace lookups { + // --------------------------------------------------------------------------- + // Class Declaration + // Represents voice channel data. + // --------------------------------------------------------------------------- + + class HOST_SW_API VoiceChData { + public: + /// Initializes a new instance of the VoiceChData class. + VoiceChData() : + m_chNo(0U), + m_address(), + m_port(), + m_password() + { + /* stub */ + } + /// Initializes a new instance of the VoiceChData class. + /// Voice Channel Number. + /// RCON Address. + /// RCON Port. + /// RCON Password. + VoiceChData(uint32_t chNo, std::string address, uint16_t port, std::string password) : + m_chNo(chNo), + m_address(address), + m_port(port), + m_password(password) + { + /* stub */ + } + + /// Equals operator. + /// + /// + VoiceChData & operator=(const VoiceChData & data) + { + if (this != &data) { + m_chNo = data.m_chNo; + m_address = data.m_address; + m_port = data.m_port; + m_password = data.m_password; + } + + return *this; + } + + /// Helper to determine if the channel is valid. + bool isValidCh() { return m_chNo != 0U; } + + public: + /// Voice Channel Number. + __READONLY_PROPERTY_PLAIN(uint32_t, chNo, chNo); + /// RCON Address. + __READONLY_PROPERTY_PLAIN(std::string, address, address); + /// RCON Port. + __READONLY_PROPERTY_PLAIN(uint16_t, port, port); + /// RCON Password. + __READONLY_PROPERTY_PLAIN(std::string, password, password); + }; + // --------------------------------------------------------------------------- // Class Declaration // Implements a lookup table class that contains subscriber registration @@ -90,6 +149,11 @@ namespace lookups /// Helper to get the channel granted for the given destination ID. virtual uint32_t getGrantedCh(uint32_t dstId); + /// Helper to set RF channel data. + void setRFChData(const std::unordered_map chData) { m_rfChDataTable = chData; } + /// Helper to get RF channel data. + VoiceChData getRFChData(uint32_t chNo) const; + /// Helper to add a RF channel. void addRFCh(uint32_t chNo) { m_rfChTable.push_back(chNo); } /// Helper to remove a RF channel. @@ -106,6 +170,7 @@ namespace lookups protected: std::vector m_rfChTable; + std::unordered_map m_rfChDataTable; uint8_t m_rfGrantChCnt; std::vector m_unitRegTable; diff --git a/network/BaseNetwork.cpp b/network/BaseNetwork.cpp index e373e40f..50f4268c 100644 --- a/network/BaseNetwork.cpp +++ b/network/BaseNetwork.cpp @@ -466,6 +466,38 @@ bool BaseNetwork::writeNXDN(const nxdn::lc::RTCH& lc, const uint8_t* data, const return writeNXDN(m_id, m_nxdnStreamId, lc, data, len); } +/// +/// Writes grant request to the network. +/// +/// +/// +/// +/// +/// +/// +bool BaseNetwork::writeGrantReq(const uint8_t mode, const uint32_t srcId, const uint32_t dstId, const uint8_t slot, const bool unitToUnit) +{ + if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING) + return false; + + uint8_t buffer[DATA_PACKET_LENGTH]; + ::memset(buffer, 0x00U, DATA_PACKET_LENGTH); + + ::memcpy(buffer + 0U, TAG_REPEATER_GRANT, 7U); + __SET_UINT32(m_id, buffer, 7U); + + __SET_UINT32(srcId, buffer, 11U); // Source Address + __SET_UINT32(dstId, buffer, 15U); // Destination Address + buffer[19U] = slot; // Slot Number + + if (unitToUnit) + buffer[19U] |= 0x80U; + + buffer[20U] = mode; // DVM Mode State + + return write((uint8_t*)buffer, 21U); +} + /// /// Writes the local activity log to the network. /// diff --git a/network/BaseNetwork.h b/network/BaseNetwork.h index a2905969..4c79e800 100644 --- a/network/BaseNetwork.h +++ b/network/BaseNetwork.h @@ -73,8 +73,6 @@ #define TAG_MASTER_CLOSING "MSTCL" #define TAG_MASTER_PONG "MSTPONG" -#define TAG_MASTER_GRANT "MSTGRNT" - #define TAG_REPEATER_LOGIN "RPTL" #define TAG_REPEATER_AUTH "RPTK" #define TAG_REPEATER_CONFIG "RPTC" @@ -83,6 +81,8 @@ #define TAG_REPEATER_CLOSING "RPTCL" #define TAG_REPEATER_PING "RPTPING" +#define TAG_REPEATER_GRANT "RPTGRNT" + #define TAG_TRANSFER_ACT_LOG "TRNSLOG" #define TAG_TRANSFER_DIAG_LOG "TRNSDIAG" @@ -156,6 +156,9 @@ namespace network /// Writes NXDN frame data to the network. virtual bool writeNXDN(const nxdn::lc::RTCH& lc, const uint8_t* data, const uint32_t len); + /// Writes a grant request to the network. + virtual bool writeGrantReq(const uint8_t mode, const uint32_t srcId, const uint32_t dstId, const uint8_t slot, const bool unitToUnit); + /// Writes the local activity log to the network. virtual bool writeActLog(const char* message); diff --git a/network/RemoteControl.cpp b/network/RemoteControl.cpp index 48875ad0..214b5a47 100644 --- a/network/RemoteControl.cpp +++ b/network/RemoteControl.cpp @@ -56,14 +56,18 @@ using namespace modem; // Constants // --------------------------------------------------------------------------- -#define BAD_CMD_STR "Bad or invalid remote command." -#define INVALID_AUTH_STR "Invalid authentication" -#define INVALID_OPT_STR "Invalid command arguments: " -#define CMD_FAILED_STR "Remote command failed: " +#define RCON_CMD_OK "ACK" +#define RCON_CMD_NACK "NACK: " + +#define BAD_CMD_STR "NACK: Bad or invalid remote command" +#define NO_DATA_CMD_STR "NACK: No data" +#define INVALID_AUTH_STR "NACK: Invalid authentication" +#define INVALID_OPT_STR "NACK: Invalid command arguments, " #define RCD_GET_VERSION "version" #define RCD_GET_HELP "help" #define RCD_GET_STATUS "status" +#define RCD_GET_VOICE_CH "voice-ch" #define RCD_MODE "mdm-mode" #define RCD_MODE_OPT_IDLE "idle" @@ -76,6 +80,7 @@ using namespace modem; #define RCD_FORCE_KILL "mdm-force-kill" #define RCD_PERMIT_TG "permit-tg" +#define RCD_GRANT_TG "grant-tg" #define RCD_RID_WLIST "rid-whitelist" #define RCD_RID_BLIST "rid-blacklist" @@ -211,7 +216,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx args.clear(); uint8_t buffer[RC_BUFFER_LENGTH]; - std::string reply = "OK"; + std::string reply = RCON_CMD_OK; sockaddr_storage address; uint32_t addrLen; @@ -245,7 +250,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx uint8_t hash[32U]; ::memset(hash, 0x00U, 32U); if (::memcmp(m_passwordHash, buffer + 2U, 32U) != 0) { - LogError(LOG_RCON, CMD_FAILED_STR INVALID_AUTH_STR " from %s", UDPSocket::address(address).c_str()); + LogError(LOG_RCON, INVALID_AUTH_STR " from %s", UDPSocket::address(address).c_str()); reply = INVALID_AUTH_STR; writeResponse(reply, address, addrLen); return; @@ -294,6 +299,9 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx else if (rcom == RCD_GET_STATUS) { reply = rcdGetStatus(host, dmr, p25, nxdn); } + else if (rcom == RCD_GET_VOICE_CH) { + reply = rcdGetVoiceCh(host, dmr, p25, nxdn); + } else if (rcom == RCD_MODE && argCnt >= 1U) { reply = rcdMode(args, host, dmr, p25, nxdn); } @@ -307,6 +315,9 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx else if (rcom == RCD_PERMIT_TG && argCnt >= 1U) { reply = rcdPermitTG(args, host, dmr, p25, nxdn); } + else if (rcom == RCD_GRANT_TG && argCnt >= 1U) { + reply = rcdPermitTG(args, host, dmr, p25, nxdn); + } else if (rcom == RCD_RID_WLIST && argCnt >= 1U) { uint32_t srcId = getArgUInt32(args, 0U); if (srcId != 0U) { @@ -334,12 +345,12 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx g_fireDMRBeacon = true; } else { - reply = CMD_FAILED_STR "DMR beacons is not enabled!"; + reply = RCON_CMD_NACK "DMR beacons is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -351,12 +362,12 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx g_fireP25Control = true; } else { - reply = CMD_FAILED_STR "P25 control data is not enabled!"; + reply = RCON_CMD_NACK "P25 control data is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -367,12 +378,12 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx p25->trunk()->setConvFallback((fallback == 1U) ? true : false); } else { - reply = CMD_FAILED_STR "P25 control data is not enabled!"; + reply = RCON_CMD_NACK "P25 control data is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -397,7 +408,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -420,7 +431,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -443,7 +454,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -466,7 +477,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -487,7 +498,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx p25->trunk()->setLastMFId(m_p25MFId); } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -505,7 +516,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -523,7 +534,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -541,7 +552,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -559,7 +570,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -577,7 +588,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -595,7 +606,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -604,7 +615,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx p25->affiliations().releaseGrant(0, true); } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -620,7 +631,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -630,7 +641,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx if (dmr != nullptr) { if (host->m_dmrTSCCData) { if (p25 != nullptr) { - reply = CMD_FAILED_STR "Can't enable DMR control channel while P25 is enabled!"; + reply = RCON_CMD_NACK "Can't enable DMR control channel while P25 is enabled!"; LogError(LOG_RCON, reply.c_str()); } else { @@ -640,12 +651,12 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "DMR control data is not enabled!"; + reply = RCON_CMD_NACK "DMR control data is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -656,7 +667,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx LogInfoEx(LOG_RCON, reply.c_str()); } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -666,7 +677,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx if (p25 != nullptr) { if (host->m_p25CCData) { if (dmr != nullptr) { - reply = CMD_FAILED_STR "Can't enable P25 control channel while DMR is enabled!"; + reply = RCON_CMD_NACK "Can't enable P25 control channel while DMR is enabled!"; LogError(LOG_RCON, reply.c_str()); } else { @@ -680,12 +691,12 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx } } else { - reply = CMD_FAILED_STR "P25 control data is not enabled!"; + reply = RCON_CMD_NACK "P25 control data is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -707,12 +718,12 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx LogInfoEx(LOG_RCON, reply.c_str()); } else { - reply = CMD_FAILED_STR "P25 control data is not enabled!"; + reply = RCON_CMD_NACK "P25 control data is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -726,7 +737,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx reply = string_format("dmr->debug = %u, dmr->verbose = %u", debug, verbose); } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -737,7 +748,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx dmr->setDebugVerbose((debug == 1U) ? true : false, (verbose == 1U) ? true : false); } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -749,7 +760,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx reply = string_format("dmr->dumpCsbkData = %u", csbkDump); } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -759,7 +770,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx dmr->setCSBKVerbose((verbose == 1U) ? true : false); } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -774,7 +785,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx reply = string_format("p25->debug = %u, p25->verbose = %u", debug, verbose); } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -785,7 +796,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx p25->setDebugVerbose((debug == 1U) ? true : false, (verbose == 1U) ? true : false); } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -797,7 +808,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx reply = string_format("p25->dumpTsbkData = %u", tsbkDump); } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -807,7 +818,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx p25->trunk()->setTSBKVerbose((verbose == 1U) ? true : false); } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -822,7 +833,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx reply = string_format("nxdn->debug = %u, nxdn->verbose = %u", debug, verbose); } else { - reply = CMD_FAILED_STR "NXDN mode is not enabled!"; + reply = RCON_CMD_NACK "NXDN mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -833,7 +844,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx nxdn->setDebugVerbose((debug == 1U) ? true : false, (verbose == 1U) ? true : false); } else { - reply = CMD_FAILED_STR "NXDN mode is not enabled!"; + reply = RCON_CMD_NACK "NXDN mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -845,7 +856,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx reply = string_format("nxdn->dumpRcchData = %u", rcchDump); } else { - reply = CMD_FAILED_STR "NXDN mode is not enabled!"; + reply = RCON_CMD_NACK "NXDN mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -855,7 +866,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25, nx nxdn->setRCCHVerbose((verbose == 1U) ? true : false); } else { - reply = CMD_FAILED_STR "NXDN mode is not enabled!"; + reply = RCON_CMD_NACK "NXDN mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -997,12 +1008,14 @@ std::string RemoteControl::displayHelp() reply += " version Display current version of host\r\n"; reply += " help Displays RCON help\r\n"; reply += " status Display current settings and operation mode\r\n"; + reply += " voice-ch Retrieves the list of configured voice channels\r\n"; reply += "\r\n"; reply += " mdm-mode Set current mode of host (idle, lockout, dmr, p25, nxdn)\r\n"; reply += " mdm-kill Causes the host to quit\r\n"; reply += " mdm-force-kill Causes the host to quit immediately\r\n"; reply += "\r\n"; reply += " permit-tg Causes the host to permit the specified destination ID if non-authoritative\r\n"; + reply += " grant-tg Causes the host to grant the specified destination ID if non-authoritative\r\n"; reply += "\r\n"; reply += " rid-whitelist Whitelists the specified RID in the host ACL tables\r\n"; reply += " rid-blacklist Blacklists the specified RID in the host ACL tables\r\n"; @@ -1078,6 +1091,7 @@ std::string RemoteControl::rcdGetStatus(Host* host, dmr::Control* dmr, p25::Cont reply += string_format("Host State: %u, DMR: %u, P25: %u, NXDN: %u, Port Type: %s, Modem Port: %s, Port Speed: %u, Proto Ver: %u", host->m_state, dmr != nullptr, p25 != nullptr, nxdn != nullptr, type.c_str(), modemPort.c_str(), portSpeed, host->m_modem->getVersion()); + reply += string_format("\r\nDMR CC: %u, P25 CC: %u, NXDN CC: %u", host->m_dmrCtrlChannel, host->m_p25CtrlChannel, host->m_nxdnCtrlChannel); } { @@ -1135,6 +1149,33 @@ std::string RemoteControl::rcdGetStatus(Host* host, dmr::Control* dmr, p25::Cont return reply; } +/// +/// +/// +/// Instance of the Host class. +/// Instance of the DMR Control class. +/// Instance of the P25 Control class. +/// Instance of the NXDN Control class. +/// +std::string RemoteControl::rcdGetVoiceCh(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn) +{ + std::string reply = ""; + + if (host->m_voiceChData.size() > 0) { + for (auto it = host->m_voiceChData.begin(); it != host->m_voiceChData.end(); ++it) { + uint32_t chNo = it->first; + lookups::VoiceChData data = it->second; + + reply += string_format("\r\n%u, %s:%u", chNo, data.address().c_str(), data.port()); + } + } + else { + reply = NO_DATA_CMD_STR; + } + + return reply; +} + /// /// /// @@ -1170,7 +1211,7 @@ std::string RemoteControl::rcdMode(std::vector args, Host* host, dm LogInfoEx(LOG_RCON, reply.c_str()); } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -1184,7 +1225,7 @@ std::string RemoteControl::rcdMode(std::vector args, Host* host, dm LogInfoEx(LOG_RCON, reply.c_str()); } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -1198,7 +1239,7 @@ std::string RemoteControl::rcdMode(std::vector args, Host* host, dm LogInfoEx(LOG_RCON, reply.c_str()); } else { - reply = CMD_FAILED_STR "NXDN mode is not enabled!"; + reply = RCON_CMD_NACK "NXDN mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } } @@ -1224,78 +1265,192 @@ std::string RemoteControl::rcdPermitTG(std::vector args, Host* host { std::string reply = ""; if (!host->m_authoritative) { - DVM_STATE state = (DVM_STATE)getArgInt32(args, 0U); - uint32_t dstId = getArgInt32(args, 1U); - if (dstId == 0U) { - reply = string_format(INVALID_OPT_STR "illegal TGID permitted (%u)", dstId); + reply = RCON_CMD_NACK "Host is authoritative, cannot permit TG!"; + LogError(LOG_RCON, reply.c_str()); + return reply; + } + + DVM_STATE state = (DVM_STATE)getArgInt32(args, 0U); + uint32_t dstId = getArgInt32(args, 1U); + if (dstId == 0U) { + reply = string_format(INVALID_OPT_STR "illegal TGID permitted (%u)", dstId); + LogError(LOG_RCON, reply.c_str()); + return reply; + } + + switch (state) { + case STATE_DMR: +#if defined(ENABLE_DMR) + { + uint8_t slot = getArgInt8(args, 2U); + if (slot == 0U) { + reply = INVALID_OPT_STR "illegal slot"; LogError(LOG_RCON, reply.c_str()); + return reply; + } + + if (dmr != nullptr) { + dmr->permittedTG(dstId, slot); } else { - switch (state) { - case STATE_DMR: -#if defined(ENABLE_DMR) - { - if (dmr != nullptr) { - dmr->permittedTG(dstId); - } - else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; - LogError(LOG_RCON, reply.c_str()); - } - } + reply = RCON_CMD_NACK "DMR mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + } #else - { - reply = INVALID_OPT_STR "invalid mode!"; - LogError(LOG_RCON, reply.c_str()); - } + { + reply = INVALID_OPT_STR "invalid mode!"; + LogError(LOG_RCON, reply.c_str()); + } #endif // defined(ENABLE_DMR) - break; - case STATE_P25: + break; + case STATE_P25: #if defined(ENABLE_P25) - { - if (p25 != nullptr) { - p25->permittedTG(dstId); - } - else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; - LogError(LOG_RCON, reply.c_str()); - } - } + { + if (p25 != nullptr) { + p25->permittedTG(dstId); + } + else { + reply = RCON_CMD_NACK "P25 mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + } #else - { - reply = INVALID_OPT_STR "invalid mode!"; - LogError(LOG_RCON, reply.c_str()); - } + { + reply = INVALID_OPT_STR "invalid mode!"; + LogError(LOG_RCON, reply.c_str()); + } #endif // defined(ENABLE_P25) - break; - case STATE_NXDN: + break; + case STATE_NXDN: #if defined(ENABLE_NXDN) - { - if (nxdn != nullptr) { - nxdn->permittedTG(dstId); - } - else { - reply = CMD_FAILED_STR "NXDN mode is not enabled!"; - LogError(LOG_RCON, reply.c_str()); - } - } + { + if (nxdn != nullptr) { + nxdn->permittedTG(dstId); + } + else { + reply = RCON_CMD_NACK "NXDN mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + } #else - { - reply = INVALID_OPT_STR "invalid mode!"; - LogError(LOG_RCON, reply.c_str()); - } + { + reply = INVALID_OPT_STR "invalid mode!"; + LogError(LOG_RCON, reply.c_str()); + } #endif // defined(ENABLE_NXDN) - break; - default: - reply = INVALID_OPT_STR "invalid mode!"; - LogError(LOG_RCON, reply.c_str()); - break; - } + break; + default: + reply = INVALID_OPT_STR "invalid mode!"; + LogError(LOG_RCON, reply.c_str()); + break; + } + + return reply; +} + +/// +/// +/// +/// +/// Instance of the Host class. +/// Instance of the DMR Control class. +/// Instance of the P25 Control class. +/// Instance of the NXDN Control class. +/// +std::string RemoteControl::rcdGrantTG(std::vector args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn) +{ + std::string reply = ""; + if (!host->m_authoritative && (host->m_dmrCtrlChannel || host->m_p25CtrlChannel || host->m_nxdnCtrlChannel)) { + reply = RCON_CMD_NACK "Host is authoritative, cannot grant TG!"; + LogError(LOG_RCON, reply.c_str()); + return reply; + } + + DVM_STATE state = (DVM_STATE)getArgInt32(args, 0U); + uint32_t dstId = getArgInt32(args, 1U); + uint8_t unitToUnit = getArgInt32(args, 2U); + if (unitToUnit > 1U) { + reply = string_format(INVALID_OPT_STR "illegal TGID granted (%u)", dstId); + LogError(LOG_RCON, reply.c_str()); + return reply; + } + + if (dstId == 0U) { + reply = string_format(INVALID_OPT_STR "illegal TGID granted (%u)", dstId); + LogError(LOG_RCON, reply.c_str()); + return reply; + } + + switch (state) { + case STATE_DMR: +#if defined(ENABLE_DMR) + { + uint8_t slot = getArgInt8(args, 2U); + if (slot == 0U) { + reply = INVALID_OPT_STR "illegal slot"; + LogError(LOG_RCON, reply.c_str()); + return reply; + } + + if (dmr != nullptr) { + // TODO TODO + //dmr->grantTG(dstId, slot, unitToUnit == 1U); + } + else { + reply = RCON_CMD_NACK "DMR mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); } } - else { - reply = CMD_FAILED_STR "Host is authoritative, cannot permit TG!"; +#else + { + reply = INVALID_OPT_STR "invalid mode!"; + LogError(LOG_RCON, reply.c_str()); + } +#endif // defined(ENABLE_DMR) + break; + case STATE_P25: +#if defined(ENABLE_P25) + { + if (p25 != nullptr) { + // TODO TODO + //p25->grantTG(dstId, unitToUnit == 1U); + } + else { + reply = RCON_CMD_NACK "P25 mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + } +#else + { + reply = INVALID_OPT_STR "invalid mode!"; + LogError(LOG_RCON, reply.c_str()); + } +#endif // defined(ENABLE_P25) + break; + case STATE_NXDN: +#if defined(ENABLE_NXDN) + { + if (nxdn != nullptr) { + // TODO TODO + //nxdn->grantTG(dstId, unitToUnit == 1U); + } + else { + reply = RCON_CMD_NACK "NXDN mode is not enabled!"; + LogError(LOG_RCON, reply.c_str()); + } + } +#else + { + reply = INVALID_OPT_STR "invalid mode!"; + LogError(LOG_RCON, reply.c_str()); + } +#endif // defined(ENABLE_NXDN) + break; + default: + reply = INVALID_OPT_STR "invalid mode!"; LogError(LOG_RCON, reply.c_str()); + break; } return reply; @@ -1355,17 +1510,17 @@ std::string RemoteControl::rcdDMRModemInj(std::vector args, Host* h host->m_modem->injectDMRData2(buffer, fileSize); } else { - reply = CMD_FAILED_STR "invalid DMR slot!"; + reply = RCON_CMD_NACK "invalid DMR slot!"; LogError(LOG_RCON, reply.c_str()); } } else { - reply = CMD_FAILED_STR "DMR data has too many errors!"; + reply = RCON_CMD_NACK "DMR data has too many errors!"; LogError(LOG_RCON, reply.c_str()); } } else { - reply = CMD_FAILED_STR "DMR failed to open DMR data!"; + reply = RCON_CMD_NACK "DMR failed to open DMR data!"; LogError(LOG_RCON, reply.c_str()); } @@ -1377,7 +1532,7 @@ std::string RemoteControl::rcdDMRModemInj(std::vector args, Host* h } } else { - reply = CMD_FAILED_STR "DMR mode is not enabled!"; + reply = RCON_CMD_NACK "DMR mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } @@ -1427,17 +1582,17 @@ std::string RemoteControl::rcdP25ModemInj(std::vector args, Host* h host->m_modem->injectP25Data(buffer, fileSize); } else { - reply = CMD_FAILED_STR "P25 data did not contain a valid NID!"; + reply = RCON_CMD_NACK "P25 data did not contain a valid NID!"; LogError(LOG_RCON, reply.c_str()); } } else { - reply = CMD_FAILED_STR "P25 data has too many errors!"; + reply = RCON_CMD_NACK "P25 data has too many errors!"; LogError(LOG_RCON, reply.c_str()); } } else { - reply = CMD_FAILED_STR "P25 failed to open P25 data!"; + reply = RCON_CMD_NACK "P25 failed to open P25 data!"; LogError(LOG_RCON, reply.c_str()); } @@ -1449,7 +1604,7 @@ std::string RemoteControl::rcdP25ModemInj(std::vector args, Host* h } } else { - reply = CMD_FAILED_STR "P25 mode is not enabled!"; + reply = RCON_CMD_NACK "P25 mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } @@ -1497,12 +1652,12 @@ std::string RemoteControl::rcdNXDNModemInj(std::vector args, Host* host->m_modem->injectNXDNData(buffer, fileSize); } else { - reply = CMD_FAILED_STR "NXDN data has too many errors!"; + reply = RCON_CMD_NACK "NXDN data has too many errors!"; LogError(LOG_RCON, reply.c_str()); } } else { - reply = CMD_FAILED_STR "NXDN failed to open NXDN data!"; + reply = RCON_CMD_NACK "NXDN failed to open NXDN data!"; LogError(LOG_RCON, reply.c_str()); } @@ -1514,7 +1669,7 @@ std::string RemoteControl::rcdNXDNModemInj(std::vector args, Host* } } else { - reply = CMD_FAILED_STR "NXDN mode is not enabled!"; + reply = RCON_CMD_NACK "NXDN mode is not enabled!"; LogError(LOG_RCON, reply.c_str()); } diff --git a/network/RemoteControl.h b/network/RemoteControl.h index 524bc1d6..2f7f885a 100644 --- a/network/RemoteControl.h +++ b/network/RemoteControl.h @@ -92,9 +92,14 @@ private: /// std::string rcdGetStatus(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn); /// + std::string rcdGetVoiceCh(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn); + /// std::string rcdMode(std::vector args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn); + /// std::string rcdPermitTG(std::vector args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn); + /// + std::string rcdGrantTG(std::vector args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn); /// std::string rcdDMRModemInj(std::vector args, Host* host, dmr::Control* dmr); diff --git a/nxdn/Control.cpp b/nxdn/Control.cpp index 3b6d7058..34ea38e8 100644 --- a/nxdn/Control.cpp +++ b/nxdn/Control.cpp @@ -105,6 +105,7 @@ Control::Control(bool authoritative, uint32_t ran, uint32_t callHang, uint32_t q m_rfLastLICH(), m_rfLC(), m_netLC(), + m_permittedDstId(0U), m_rfMask(0U), m_netMask(0U), m_idenTable(idenTable), @@ -203,13 +204,15 @@ void Control::reset() /// /// Instance of the yaml::Node class. /// -/// -/// -/// -/// +/// Voice Channel Number list. +/// Voice Channel data map. +/// NXDN Location ID. +/// Channel ID. +/// Channel Number. /// -void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo, - uint16_t locId, uint8_t channelId, uint32_t channelNo, bool printOptions) +void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo, + const std::unordered_map voiceChData, uint16_t locId, + uint8_t channelId, uint32_t channelNo, bool printOptions) { yaml::Node systemConf = conf["system"]; yaml::Node nxdnProtocol = conf["protocols"]["nxdn"]; @@ -249,6 +252,13 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s m_siteData = SiteData(locId, channelId, channelNo, serviceClass, false); m_siteData.setCallsign(cwCallsign); + std::vector availCh = voiceChNo; + for (auto it = availCh.begin(); it != availCh.end(); ++it) { + m_affiliations.addRFCh(*it); + } + + m_affiliations.setRFChData(voiceChData); + lc::RCCH::setSiteData(m_siteData); lc::RCCH::setCallsign(cwCallsign); @@ -261,11 +271,6 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s } } - std::vector availCh = voiceChNo; - for (auto it = availCh.begin(); it != availCh.end(); ++it) { - m_affiliations.addRFCh(*it); - } - if (printOptions) { LogInfo(" Silence Threshold: %u (%.1f%%)", m_voice->m_silenceThreshold, float(m_voice->m_silenceThreshold) / 12.33F); @@ -591,7 +596,11 @@ void Control::clock(uint32_t ms) /// void Control::permittedTG(uint32_t dstId) { - // TODO TODO + if (!m_authoritative) { + return; + } + + m_permittedDstId = dstId; } /// diff --git a/nxdn/Control.h b/nxdn/Control.h index 214cb3aa..b3ded7c3 100644 --- a/nxdn/Control.h +++ b/nxdn/Control.h @@ -83,8 +83,9 @@ namespace nxdn void reset(); /// Helper to set NXDN configuration options. - void setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo, - uint16_t locId, uint8_t channelId, uint32_t channelNo, bool printOptions); + void setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo, + const std::unordered_map voiceChData, uint16_t locId, + uint8_t channelId, uint32_t channelNo, bool printOptions); /// Gets a flag indicating whether the NXDN control channel is running. bool getCCRunning() { return m_ccRunning; } @@ -145,6 +146,8 @@ namespace nxdn lc::RTCH m_rfLC; lc::RTCH m_netLC; + uint32_t m_permittedDstId; + uint8_t m_rfMask; uint8_t m_netMask; diff --git a/nxdn/packet/Trunk.cpp b/nxdn/packet/Trunk.cpp index 7c82bd16..51118aac 100644 --- a/nxdn/packet/Trunk.cpp +++ b/nxdn/packet/Trunk.cpp @@ -181,7 +181,11 @@ bool Trunk::process(uint8_t fct, uint8_t option, uint8_t* data, uint32_t len) (rcch->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag (rcch->getPriority() & 0x07U); // Priority - writeRF_Message_Grant(srcId, dstId, serviceOptions, true); + if (m_nxdn->m_authoritative) { + writeRF_Message_Grant(srcId, dstId, serviceOptions, true); + } else { + m_network->writeGrantReq(modem::DVM_STATE::STATE_NXDN, srcId, dstId, 0U, false); + } } break; case RCCH_MESSAGE_TYPE_REG: diff --git a/p25/Control.cpp b/p25/Control.cpp index 85506d68..c9d7807b 100644 --- a/p25/Control.cpp +++ b/p25/Control.cpp @@ -115,6 +115,7 @@ Control::Control(bool authoritative, uint32_t nac, uint32_t callHang, uint32_t q m_rfLastDstId(0U), m_netState(RS_NET_IDLE), m_netLastDstId(0U), + m_permittedDstId(0U), m_tailOnIdle(false), m_ccRunning(false), m_ccPrevRunning(false), @@ -208,18 +209,19 @@ void Control::reset() /// /// Instance of the yaml::Node class. /// -/// +/// Voice Channel Number list. +/// Voice Channel data map. /// -/// -/// -/// -/// -/// -/// +/// P25 Network ID. +/// P25 System ID. +/// P25 RFSS ID. +/// P25 Site ID. +/// Channel ID. +/// Channel Number. /// -void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo, - uint32_t pSuperGroup, uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint8_t channelId, - uint32_t channelNo, bool printOptions) +void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo, + const std::unordered_map voiceChData, uint32_t pSuperGroup, uint32_t netId, + uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions) { yaml::Node systemConf = conf["system"]; yaml::Node p25Protocol = conf["protocols"]["p25"]; @@ -309,6 +311,8 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s m_affiliations.addRFCh(*it); } + m_affiliations.setRFChData(voiceChData); + uint32_t ccBcstInterval = p25Protocol["control"]["interval"].as(300U); m_trunk->m_adjSiteUpdateInterval += ccBcstInterval; @@ -760,7 +764,11 @@ void Control::clock(uint32_t ms) /// void Control::permittedTG(uint32_t dstId) { - // TODO TODO + if (!m_authoritative) { + return; + } + + m_permittedDstId = dstId; } /// diff --git a/p25/Control.h b/p25/Control.h index b19b2da2..944cef30 100644 --- a/p25/Control.h +++ b/p25/Control.h @@ -50,6 +50,8 @@ #include "yaml/Yaml.h" #include +#include +#include namespace p25 { @@ -83,9 +85,9 @@ namespace p25 void reset(); /// Helper to set P25 configuration options. - void setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo, - uint32_t pSuperGroup, uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId, - uint8_t channelId, uint32_t channelNo, bool printOptions); + void setOptions(yaml::Node& conf, const std::string cwCallsign, const std::vector voiceChNo, + const std::unordered_map voiceChData, uint32_t pSuperGroup, uint32_t netId, + uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions); /// Gets a flag indicating whether the P25 control channel is running. bool getCCRunning() { return m_ccRunning; } @@ -178,6 +180,8 @@ namespace p25 RPT_NET_STATE m_netState; uint32_t m_netLastDstId; + uint32_t m_permittedDstId; + bool m_tailOnIdle; bool m_ccRunning; bool m_ccPrevRunning; diff --git a/p25/packet/Trunk.cpp b/p25/packet/Trunk.cpp index 6187dad1..88069c4b 100644 --- a/p25/packet/Trunk.cpp +++ b/p25/packet/Trunk.cpp @@ -219,11 +219,15 @@ bool Trunk::process(uint8_t* data, uint32_t len, lc::TSBK* preDecodedTSBK) LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_IOSP_GRP_VCH (Group Voice Channel Request), srcId = %u, dstId = %u", srcId, dstId); } - uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag - (tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag - (tsbk->getPriority() & 0x07U); // Priority + if (m_p25->m_authoritative) { + uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag + (tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag + (tsbk->getPriority() & 0x07U); // Priority - writeRF_TSDU_Grant(srcId, dstId, serviceOptions, true); + writeRF_TSDU_Grant(srcId, dstId, serviceOptions, true); + } else { + m_network->writeGrantReq(modem::DVM_STATE::STATE_P25, srcId, dstId, 0U, false); + } } break; case TSBK_IOSP_UU_VCH: @@ -248,11 +252,15 @@ bool Trunk::process(uint8_t* data, uint32_t len, lc::TSBK* preDecodedTSBK) writeRF_TSDU_UU_Ans_Req(srcId, dstId); } else { - uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag - (tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag - (tsbk->getPriority() & 0x07U); // Priority + if (m_p25->m_authoritative) { + uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag + (tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag + (tsbk->getPriority() & 0x07U); // Priority - writeRF_TSDU_Grant(srcId, dstId, serviceOptions, false); + writeRF_TSDU_Grant(srcId, dstId, serviceOptions, false); + } else { + m_network->writeGrantReq(modem::DVM_STATE::STATE_P25, srcId, dstId, 0U, true); + } } } break; @@ -278,11 +286,15 @@ bool Trunk::process(uint8_t* data, uint32_t len, lc::TSBK* preDecodedTSBK) writeRF_TSDU_ACK_FNE(dstId, TSBK_IOSP_UU_ANS, false, true); } - uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag - (tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag - (tsbk->getPriority() & 0x07U); // Priority + if (m_p25->m_authoritative) { + uint8_t serviceOptions = (tsbk->getEmergency() ? 0x80U : 0x00U) + // Emergency Flag + (tsbk->getEncrypted() ? 0x40U : 0x00U) + // Encrypted Flag + (tsbk->getPriority() & 0x07U); // Priority - writeRF_TSDU_Grant(srcId, dstId, serviceOptions, false); + writeRF_TSDU_Grant(srcId, dstId, serviceOptions, false); + } else { + m_network->writeGrantReq(modem::DVM_STATE::STATE_P25, srcId, dstId, 0U, true); + } } else if (iosp->getResponse() == P25_ANS_RSP_DENY) { writeRF_TSDU_Deny(P25_WUID_FNE, srcId, P25_DENY_RSN_TGT_UNIT_REFUSED, TSBK_IOSP_UU_ANS);