From 8a400ea44c04585d1cf827ebffe42bec9cdb2ab5 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 21 Mar 2023 12:11:18 -0400 Subject: [PATCH] correct REST API bindings; implement P25 raw TSBK API; --- network/RESTAPI.cpp | 87 +++++++++++++++++++++++++++++-- network/RESTAPI.h | 2 + network/RESTDefines.h | 6 ++- p25/lc/tsbk/TSBKFactory.h | 2 + p25/lc/tsbk/TSBK_RAW.cpp | 107 ++++++++++++++++++++++++++++++++++++++ p25/lc/tsbk/TSBK_RAW.h | 65 +++++++++++++++++++++++ p25/packet/Trunk.cpp | 16 ++++++ p25/packet/Trunk.h | 2 + 8 files changed, 280 insertions(+), 7 deletions(-) create mode 100644 p25/lc/tsbk/TSBK_RAW.cpp create mode 100644 p25/lc/tsbk/TSBK_RAW.h diff --git a/network/RESTAPI.cpp b/network/RESTAPI.cpp index b626f07f..9c72f58f 100644 --- a/network/RESTAPI.cpp +++ b/network/RESTAPI.cpp @@ -271,10 +271,10 @@ void RESTAPI::initializeEndpoints() m_dispatcher.match(GET_VOICE_CH).get(REST_API_BIND(RESTAPI::restAPI_GetVoiceCh, this)); m_dispatcher.match(PUT_MDM_MODE).put(REST_API_BIND(RESTAPI::restAPI_PutModemMode, this)); - m_dispatcher.match(PUT_MDM_KILL).get(REST_API_BIND(RESTAPI::restAPI_PutModemKill, this)); + m_dispatcher.match(PUT_MDM_KILL).put(REST_API_BIND(RESTAPI::restAPI_PutModemKill, this)); - m_dispatcher.match(PUT_PERMIT_TG).get(REST_API_BIND(RESTAPI::restAPI_PutPermitTG, this)); - m_dispatcher.match(PUT_GRANT_TG).get(REST_API_BIND(RESTAPI::restAPI_PutGrantTG, this)); + m_dispatcher.match(PUT_PERMIT_TG).put(REST_API_BIND(RESTAPI::restAPI_PutPermitTG, this)); + m_dispatcher.match(PUT_GRANT_TG).put(REST_API_BIND(RESTAPI::restAPI_PutGrantTG, this)); m_dispatcher.match(GET_RELEASE_GRNTS).get(REST_API_BIND(RESTAPI::restAPI_GetReleaseGrants, this)); m_dispatcher.match(GET_RELEASE_AFFS).get(REST_API_BIND(RESTAPI::restAPI_GetReleaseAffs, this)); @@ -288,7 +288,7 @@ void RESTAPI::initializeEndpoints() m_dispatcher.match(GET_DMR_BEACON).get(REST_API_BIND(RESTAPI::restAPI_GetDMRBeacon, this)); m_dispatcher.match(GET_DMR_DEBUG, true).get(REST_API_BIND(RESTAPI::restAPI_GetDMRDebug, this)); m_dispatcher.match(GET_DMR_DUMP_CSBK, true).get(REST_API_BIND(RESTAPI::restAPI_GetDMRDumpCSBK, this)); - m_dispatcher.match(PUT_DMR_RID).get(REST_API_BIND(RESTAPI::restAPI_PutDMRRID, this)); + m_dispatcher.match(PUT_DMR_RID).put(REST_API_BIND(RESTAPI::restAPI_PutDMRRID, this)); m_dispatcher.match(GET_DMR_CC_DEDICATED).get(REST_API_BIND(RESTAPI::restAPI_GetDMRCCEnable, this)); m_dispatcher.match(GET_DMR_CC_BCAST).get(REST_API_BIND(RESTAPI::restAPI_GetDMRCCBroadcast, this)); @@ -299,9 +299,10 @@ void RESTAPI::initializeEndpoints() m_dispatcher.match(GET_P25_CC).get(REST_API_BIND(RESTAPI::restAPI_GetP25CC, this)); m_dispatcher.match(GET_P25_DEBUG, true).get(REST_API_BIND(RESTAPI::restAPI_GetP25Debug, this)); m_dispatcher.match(GET_P25_DUMP_TSBK, true).get(REST_API_BIND(RESTAPI::restAPI_GetP25DumpTSBK, this)); - m_dispatcher.match(PUT_P25_RID).get(REST_API_BIND(RESTAPI::restAPI_PutP25RID, this)); + m_dispatcher.match(PUT_P25_RID).put(REST_API_BIND(RESTAPI::restAPI_PutP25RID, this)); m_dispatcher.match(GET_P25_CC_DEDICATED).get(REST_API_BIND(RESTAPI::restAPI_GetP25CCEnable, this)); m_dispatcher.match(GET_P25_CC_BCAST).get(REST_API_BIND(RESTAPI::restAPI_GetP25CCBroadcast, this)); + m_dispatcher.match(PUT_P25_RAW_TSBK).put(REST_API_BIND(RESTAPI::restAPI_PutP25RawTSBK, this)); /* ** Next Generation Digital Narrowband @@ -1569,6 +1570,22 @@ void RESTAPI::restAPI_PutP25RID(const HTTPPayload& request, HTTPPayload& reply, else if (::strtolower(command) == RID_CMD_UREG) { m_p25->trunk()->writeRF_TSDU_U_Reg_Cmd(dstId); } + else if (::strtolower(command) == RID_CMD_EMERG) { + // validate source ID is a integer within the JSON blob + if (!req["srcId"].is()) { + errorPayload(reply, "source ID was not valid"); + return; + } + + uint32_t srcId = req["srcId"].get(); + + if (srcId == 0U) { + errorPayload(reply, "source ID was not valid"); + return; + } + + m_p25->trunk()->writeRF_TSDU_Emerg_Alrm(srcId, dstId); + } else { errorPayload(reply, "invalid command"); return; @@ -1664,6 +1681,66 @@ void RESTAPI::restAPI_GetP25CCBroadcast(const HTTPPayload& request, HTTPPayload& #endif // defined(ENABLE_P25) } +/// +/// +/// +/// +/// +/// +void RESTAPI::restAPI_PutP25RawTSBK(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +{ + if (!validateAuth(request, reply)) { + return; + } + + json::object req = json::object(); + if (!parseRequestBody(request, reply, req)) { + return; + } +#if defined(ENABLE_P25) + if (m_p25 == nullptr) { + errorPayload(reply, "P25 mode is not enabled", HTTPPayload::SERVICE_UNAVAILABLE); + return; + } + + // validate state is a string within the JSON blob + if (!req["tsbk"].is()) { + errorPayload(reply, "tsbk was not valid"); + return; + } + + std::string tsbkBytes = req["tsbk"].get(); + + if (tsbkBytes.size() != 24) { + errorPayload(reply, "TSBK must be 24 characters in length"); + return; + } + + if (!(tsbkBytes.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos)) { + errorPayload(reply, "TSBK contains invalid characters"); + return; + } + + const char* tsbkPtr = tsbkBytes.c_str(); + uint8_t* tsbk = new uint8_t[p25::P25_TSBK_LENGTH_BYTES]; + ::memset(tsbk, 0x00U, p25::P25_TSBK_LENGTH_BYTES); + + for (uint8_t i = 0; i < p25::P25_TSBK_LENGTH_BYTES; i++) { + char t[4] = {tsbkPtr[0], tsbkPtr[1], 0}; + tsbk[i] = (uint8_t)::strtoul(t, NULL, 16); + tsbkPtr += 2 * sizeof(char); + } + + if (m_debug) { + Utils::dump("Raw TSBK", tsbk, p25::P25_TSBK_LENGTH_BYTES); + } + + m_p25->trunk()->writeRF_TSDU_Raw(tsbk); +#else + errorPayload(reply, "P25 operations are unavailable", HTTPPayload::SERVICE_UNAVAILABLE); +#endif // defined(ENABLE_P25) +} + /* ** Next Generation Digital Narrowband */ diff --git a/network/RESTAPI.h b/network/RESTAPI.h index 62c64d44..93ff7a26 100644 --- a/network/RESTAPI.h +++ b/network/RESTAPI.h @@ -169,6 +169,8 @@ private: void restAPI_GetP25CCEnable(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); /// void restAPI_GetP25CCBroadcast(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + /// + void restAPI_PutP25RawTSBK(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); /* ** Next Generation Digital Narrowband diff --git a/network/RESTDefines.h b/network/RESTDefines.h index b85c5434..7b213cda 100644 --- a/network/RESTDefines.h +++ b/network/RESTDefines.h @@ -54,6 +54,7 @@ #define RID_CMD_UNINHIBIT "uninhibit" #define RID_CMD_GAQ "group-aff-req" #define RID_CMD_UREG "unit-reg" +#define RID_CMD_EMERG "emerg" #define PUT_MDM_KILL "/mdm/kill" @@ -77,8 +78,8 @@ #define GET_DMR_CC_BCAST "/dmr/cc-broadcast" #define GET_P25_CC "/p25/cc" -//#define GET_P25_CC_FALLBACK_BASE "/p25/cc-fallback/" -//#define GET_P25_CC_FALLBACK GET_P25_CC_FALLBACK_BASE"(\\d+)" +#define GET_P25_CC_FALLBACK_BASE "/p25/cc-fallback/" +#define GET_P25_CC_FALLBACK GET_P25_CC_FALLBACK_BASE"(\\d+)" #define GET_P25_DEBUG_BASE "/p25/debug/(\\d+)/(\\d+)" #define GET_P25_DEBUG GET_P25_DEBUG_BASE"(\\d+)/(\\d+)" #define GET_P25_DUMP_TSBK_BASE "/p25/dump-tsbk/(\\d+)" @@ -86,6 +87,7 @@ #define PUT_P25_RID "/p25/rid" #define GET_P25_CC_DEDICATED "/p25/cc-enable" #define GET_P25_CC_BCAST "/p25/cc-broadcast" +#define PUT_P25_RAW_TSBK "/p25/raw-tsbk" #define GET_NXDN_CC "/nxdn/cc" #define GET_NXDN_DEBUG_BASE "/nxdn/debug/(\\d+)/(\\d+)" diff --git a/p25/lc/tsbk/TSBKFactory.h b/p25/lc/tsbk/TSBKFactory.h index c88ed2cf..8c9d53d7 100644 --- a/p25/lc/tsbk/TSBKFactory.h +++ b/p25/lc/tsbk/TSBKFactory.h @@ -94,6 +94,8 @@ #include "p25/lc/tsbk/MBT_OSP_NET_STS_BCAST.h" #include "p25/lc/tsbk/MBT_OSP_RFSS_STS_BCAST.h" +#include "p25/lc/tsbk/TSBK_RAW.h" + namespace p25 { namespace lc diff --git a/p25/lc/tsbk/TSBK_RAW.cpp b/p25/lc/tsbk/TSBK_RAW.cpp new file mode 100644 index 00000000..600398d3 --- /dev/null +++ b/p25/lc/tsbk/TSBK_RAW.cpp @@ -0,0 +1,107 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +/* +* Copyright (C) 2023 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include "Defines.h" +#include "p25/lc/tsbk/TSBK_RAW.h" +#include "Log.h" +#include "Utils.h" + +using namespace p25::lc::tsbk; +using namespace p25::lc; +using namespace p25; + +#include +#include + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +/// +/// Initializes a new instance of the TSBK_RAW class. +/// +TSBK_RAW::TSBK_RAW() : TSBK(), + m_tsbk(nullptr) +{ + m_lco = TSBK_IOSP_ACK_RSP; +} + +/// +/// Finalizes a new instance of the TSBK_RAW class. +/// +TSBK_RAW::~TSBK_RAW() +{ + if (m_tsbk != nullptr) { + delete m_tsbk; + } +} + +/// +/// Decode a trunking signalling block. +/// +/// +/// +/// True, if TSBK was decoded, otherwise false. +bool TSBK_RAW::decode(const uint8_t* data, bool rawTSBK) +{ + assert(data != NULL); + + /* stub */ + + return true; +} + +/// +/// Encode a trunking signalling block. +/// +/// +/// +/// +void TSBK_RAW::encode(uint8_t* data, bool rawTSBK, bool noTrellis) +{ + assert(data != NULL); + + /* stub */ + + TSBK::encode(data, m_tsbk, rawTSBK, noTrellis); +} + +/// +/// Sets the TSBK to encode. +/// +/// +void TSBK_RAW::setTSBK(const uint8_t* tsbk) +{ + assert(tsbk != NULL); + + m_lco = tsbk[0U] & 0x3F; // LCO + m_lastBlock = (tsbk[0U] & 0x80U) == 0x80U; // Last Block Marker + m_mfId = tsbk[1U]; // Mfg Id. + + m_tsbk = new uint8_t[P25_TSBK_LENGTH_BYTES]; + ::memset(m_tsbk, 0x00U, P25_TSBK_LENGTH_BYTES); + + ::memcpy(m_tsbk, tsbk, P25_TSBK_LENGTH_BYTES); +} diff --git a/p25/lc/tsbk/TSBK_RAW.h b/p25/lc/tsbk/TSBK_RAW.h new file mode 100644 index 00000000..bacea93c --- /dev/null +++ b/p25/lc/tsbk/TSBK_RAW.h @@ -0,0 +1,65 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +/* +* Copyright (C) 2023 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#if !defined(__P25_LC_TSBK__TSBK_RAW_H__) +#define __P25_LC_TSBK__TSBK_RAW_H__ + +#include "Defines.h" +#include "p25/lc/TSBK.h" + +namespace p25 +{ + namespace lc + { + namespace tsbk + { + // --------------------------------------------------------------------------- + // Class Declaration + // Implements a mechanism to generate raw TSBK data from bytes. + // --------------------------------------------------------------------------- + + class HOST_SW_API TSBK_RAW : public TSBK { + public: + /// Initializes a new instance of the TSBK_RAW class. + TSBK_RAW(); + /// Finalizes a instance of the TSBK_RAW class. + ~TSBK_RAW(); + + /// Decode a trunking signalling block. + virtual bool decode(const uint8_t* data, bool rawTSBK = false); + /// Encode a trunking signalling block. + virtual void encode(uint8_t* data, bool rawTSBK = false, bool noTrellis = false); + + /// Sets the TSBK to encode. + void setTSBK(const uint8_t* tsbk); + + private: + uint8_t* m_tsbk; + }; + } // namespace tsbk + } // namespace lc +} // namespace p25 + +#endif // __P25_LC_TSBK__TSBK_RAW_H__ diff --git a/p25/packet/Trunk.cpp b/p25/packet/Trunk.cpp index 1033ca4b..aec2f6d5 100644 --- a/p25/packet/Trunk.cpp +++ b/p25/packet/Trunk.cpp @@ -1128,6 +1128,22 @@ void Trunk::writeRF_TSDU_Emerg_Alrm(uint32_t srcId, uint32_t dstId) writeRF_TSDU_SBF(isp.get(), true); } +/// +/// Helper to write a raw TSBK. +/// +/// +void Trunk::writeRF_TSDU_Raw(const uint8_t* tsbk) +{ + if (tsbk == nullptr) { + return; + } + + std::unique_ptr osp = new_unique(TSBK_RAW); + osp->setTSBK(tsbk); + + writeRF_TSDU_SBF(osp.get(), true); +} + /// /// Helper to change the conventional fallback state. /// diff --git a/p25/packet/Trunk.h b/p25/packet/Trunk.h index cefb0eec..70860ad5 100644 --- a/p25/packet/Trunk.h +++ b/p25/packet/Trunk.h @@ -88,6 +88,8 @@ namespace p25 void writeRF_TSDU_U_Reg_Cmd(uint32_t dstId); /// Helper to write a emergency alarm packet. void writeRF_TSDU_Emerg_Alrm(uint32_t srcId, uint32_t dstId); + /// Helper to write a emergency alarm packet. + void writeRF_TSDU_Raw(const uint8_t* tsbk); /// Helper to change the conventional fallback state. void setConvFallback(bool fallback);