From ba8067c55cba0d13ccfd632f9a21ec6c1d0d7882 Mon Sep 17 00:00:00 2001 From: W3AXL <29879554+W3AXL@users.noreply.github.com> Date: Tue, 18 Jun 2024 23:45:41 -0400 Subject: [PATCH] fixed deletes on invalid pointers, cleaned up serial initialization, added timeouts for call flags, other small QoL tweaks --- configs/dfsi-config.example.yml | 5 +- src/dfsi/Defines.h | 4 + src/dfsi/Dfsi.cpp | 123 +++++++++++++++++-------- src/dfsi/Dfsi.h | 2 + src/dfsi/frames/MotFullRateVoice.cpp | 4 + src/dfsi/frames/MotStartVoiceFrame.cpp | 4 + src/dfsi/frames/MotVoiceHeader1.cpp | 4 + src/dfsi/frames/MotVoiceHeader2.cpp | 3 + src/dfsi/network/SerialService.cpp | 44 +++++++-- src/dfsi/network/SerialService.h | 12 ++- 10 files changed, 156 insertions(+), 49 deletions(-) diff --git a/configs/dfsi-config.example.yml b/configs/dfsi-config.example.yml index 235a8f2b..c2a286e5 100644 --- a/configs/dfsi-config.example.yml +++ b/configs/dfsi-config.example.yml @@ -44,7 +44,7 @@ dfsi: # P25 buffer size in bytes (don't change this unless you have a good reason to) # Default of 3060 is 10 full LDU1/2s worth of data p25BufferSize: 3060 - + udp: # Time in seconds between heartbets to DFSI peers. heartbeat: 5 @@ -74,6 +74,9 @@ dfsi: # Trace logging (prints lots of data) trace: false + # Timer which will reset local/remote call flags if frames aren't received longer than this time in ms + callTimeout: 200 + # # Logging Configuration # Logging Levels: diff --git a/src/dfsi/Defines.h b/src/dfsi/Defines.h index f71d410f..2a0f5334 100644 --- a/src/dfsi/Defines.h +++ b/src/dfsi/Defines.h @@ -33,4 +33,8 @@ #undef DEFAULT_LOCK_FILE #define DEFAULT_LOCK_FILE "/tmp/dvmdfsi.lock" +#define DFSI_MODE_UDP_FNE 1 +#define DFSI_MODE_V24_FNE 2 +#define DFSI_MODE_UDP_V24 3 + #endif // __DEFINES_H__ \ No newline at end of file diff --git a/src/dfsi/Dfsi.cpp b/src/dfsi/Dfsi.cpp index b89fcea4..5936a1fc 100644 --- a/src/dfsi/Dfsi.cpp +++ b/src/dfsi/Dfsi.cpp @@ -150,6 +150,12 @@ int Dfsi::run() if (!ret) return EXIT_FAILURE; + // Read DFSI config + yaml::Node dfsi_conf = m_conf["dfsi"]; + uint16_t dfsiMode = dfsi_conf["mode"].as(); + uint32_t p25BufferSize = dfsi_conf["p25BufferSize"].as(); + uint16_t callTimeout = dfsi_conf["callTimeout"].as(); + // initialize peer networking ret = createPeerNetwork(); if (!ret) @@ -157,36 +163,43 @@ int Dfsi::run() ::LogInfoEx(LOG_HOST, "DFSI peer network is up and running"); - // Read DFSI config - yaml::Node dfsi_conf = m_conf["dfsi"]; - uint32_t p25BufferSize = dfsi_conf["p25BufferSize"].as(); - - // Read serial config - yaml::Node serial_conf = dfsi_conf["serial"]; - std::string port = serial_conf["port"].as(); - uint32_t baudrate = serial_conf["baudrate"].as(); - bool rtrt = serial_conf["rtrt"].as(); - bool diu = serial_conf["diu"].as(); - uint16_t jitter = serial_conf["jitter"].as(); - bool serial_debug = serial_conf["debug"].as(); - bool serial_trace = serial_conf["trace"].as(); + std::string dfsiModeStr = "Unknown"; - LogInfo("Serial Parameters"); - LogInfo(" Port: %s", port.c_str()); - LogInfo(" Baudrate: %u", baudrate); - LogInfo(" RT/RT: %s", rtrt ? "Enabled" : "Disabled"); - LogInfo(" DIU Flag: %s", diu ? "Enabled" : "Disabled"); - LogInfo(" Jitter Size: %u ms", jitter); - LogInfo(" Debug: %s", serial_debug ? "Enabled" : "Disabled"); - LogInfo(" Trace: %s", serial_trace ? "Enabled" : "Disabled"); - - // Create serial service - m_serial = new SerialService(port, baudrate, rtrt, diu, jitter, m_network, p25BufferSize, p25BufferSize, serial_debug, serial_trace); + switch (dfsiMode) { + case DFSI_MODE_UDP_FNE: + { + dfsiModeStr = "UDP DFSI to FNE"; + LogError(LOG_HOST, "UDP DFSI mode not yet supported, sorry!"); + return EXIT_FAILURE; + } + break; + case DFSI_MODE_V24_FNE: + { + dfsiModeStr = "V24 DFSI to FNE"; + ret = createSerialNetwork(p25BufferSize, callTimeout); + if (!ret) + return EXIT_FAILURE; + } + break; + case DFSI_MODE_UDP_V24: + { + dfsiModeStr = "UDP DFSI to V24 DFSI"; + LogError(LOG_HOST, "UDP to V24 mode not yet supported, sorry!"); + return EXIT_FAILURE; + } + break; + default: + { + LogError(LOG_HOST, "Invalid DFSI mode specified: %d", dfsiMode); + return EXIT_FAILURE; + } + break; + } - // Open serial - ret = m_serial->open(); - if (!ret) - return EXIT_FAILURE; + LogInfo("DFSI Parameters"); + LogInfo(" Mode: %u (%s)", dfsiMode, dfsiModeStr.c_str()); + LogInfo(" P25 Buffer Size: %u bytes", p25BufferSize); + LogInfo(" Call Timeout: %u ms", callTimeout); StopWatch stopWatch; stopWatch.start(); @@ -223,11 +236,12 @@ int Dfsi::run() if (!g_hideMessages) LogMessage(LOG_NET, "P25, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u", duid, lco, MFId, srcId, dstId, length); - // Send the data to the serial handler - m_serial->processP25FromNet(std::move(p25Buffer), length); + // Send the data to the serial handler if serial is up + if (m_serial != nullptr) + m_serial->processP25FromNet(std::move(p25Buffer), length); } - // We keep DMR & NXDN in so nothing breaks, even though DFSI doesn't do DMR or NXDNS + // We keep DMR & NXDN in so nothing breaks, even though DFSI doesn't do DMR or NXDN UInt8Array dmrBuffer = m_network->readDMR(netReadRet, length); if (netReadRet) { uint8_t seqNo = dmrBuffer[4U]; @@ -258,7 +272,7 @@ int Dfsi::run() // -- Network TX Clocking -- // ------------------------------------------------------ - // Processes data in the serial rx P25 buffer and sends it to the network buffer for sending + // Processes data in the serial rx P25 buffer and sends it to the network buffer for sending, if serial is up if (m_serial != nullptr) { m_serial->processP25ToNet(); } @@ -364,10 +378,10 @@ bool Dfsi::createPeerNetwork() bool netDebug = networkConf["debug"].as(); LogInfo("Network Parameters"); - LogInfo(" Identity: %s", identity.c_str()); - LogInfo(" Peer ID: %u", id); - LogInfo(" Address: %s", address.c_str()); - LogInfo(" Port: %u", port); + LogInfo(" Identity: %s", identity.c_str()); + LogInfo(" Peer ID: %u", id); + LogInfo(" Address: %s", address.c_str()); + LogInfo(" Port: %u", port); LogInfo(" Encrypted: %s", encrypted ? "yes" : "no"); if (id > 999999999U) { @@ -397,5 +411,42 @@ bool Dfsi::createPeerNetwork() ::LogSetNetwork(m_network); + return true; +} + +bool Dfsi::createSerialNetwork(uint32_t p25BufferSize, uint16_t callTimeout) +{ + // Read serial config + yaml::Node dfsi_conf = m_conf["dfsi"]; + yaml::Node serial_conf = dfsi_conf["serial"]; + std::string port = serial_conf["port"].as(); + uint32_t baudrate = serial_conf["baudrate"].as(); + bool rtrt = serial_conf["rtrt"].as(); + bool diu = serial_conf["diu"].as(); + uint16_t jitter = serial_conf["jitter"].as(); + bool serial_debug = serial_conf["debug"].as(); + bool serial_trace = serial_conf["trace"].as(); + + LogInfo("Serial Parameters"); + LogInfo(" Port: %s", port.c_str()); + LogInfo(" Baudrate: %u", baudrate); + LogInfo(" RT/RT: %s", rtrt ? "Enabled" : "Disabled"); + LogInfo(" DIU Flag: %s", diu ? "Enabled" : "Disabled"); + LogInfo(" Jitter Size: %u ms", jitter); + LogInfo(" Debug: %s", serial_debug ? "Enabled" : "Disabled"); + LogInfo(" Trace: %s", serial_trace ? "Enabled" : "Disabled"); + + // Create serial service + m_serial = new SerialService(port, baudrate, rtrt, diu, jitter, m_network, p25BufferSize, p25BufferSize, callTimeout, serial_debug, serial_trace); + + // Open serial + bool ret = m_serial->open(); + if (!ret) { + delete m_serial; + m_serial = nullptr; + LogError(LOG_HOST, "failed to initialied serial V24 interface"); + return false; + } + return true; } \ No newline at end of file diff --git a/src/dfsi/Dfsi.h b/src/dfsi/Dfsi.h index f2f16046..9ad74115 100644 --- a/src/dfsi/Dfsi.h +++ b/src/dfsi/Dfsi.h @@ -65,6 +65,8 @@ private: bool readParams(); /// Initializes peer network connectivity. bool createPeerNetwork(); + /// Initializes serial V24 network. + bool createSerialNetwork(uint32_t p25BufferSize, uint16_t callTimeout); }; #endif // __DFSI_H__ \ No newline at end of file diff --git a/src/dfsi/frames/MotFullRateVoice.cpp b/src/dfsi/frames/MotFullRateVoice.cpp index 17a31352..a1e14da6 100644 --- a/src/dfsi/frames/MotFullRateVoice.cpp +++ b/src/dfsi/frames/MotFullRateVoice.cpp @@ -47,6 +47,10 @@ MotFullRateVoice::MotFullRateVoice() : /// MotFullRateVoice::MotFullRateVoice(uint8_t* data) { + // set our pointers to null since it doesn't get initialized otherwise + imbeData = nullptr; + additionalData = nullptr; + // decode decode(data); } diff --git a/src/dfsi/frames/MotStartVoiceFrame.cpp b/src/dfsi/frames/MotStartVoiceFrame.cpp index 2df6fce6..66482883 100644 --- a/src/dfsi/frames/MotStartVoiceFrame.cpp +++ b/src/dfsi/frames/MotStartVoiceFrame.cpp @@ -50,6 +50,10 @@ MotStartVoiceFrame::MotStartVoiceFrame() : /// MotStartVoiceFrame::MotStartVoiceFrame(uint8_t* data) { + // set our pointers to null since we don't initialize them anywhere else + startOfStream = nullptr; + fullRateVoice = nullptr; + // decode decode(data); } diff --git a/src/dfsi/frames/MotVoiceHeader1.cpp b/src/dfsi/frames/MotVoiceHeader1.cpp index 16c3cda8..e8315b6b 100644 --- a/src/dfsi/frames/MotVoiceHeader1.cpp +++ b/src/dfsi/frames/MotVoiceHeader1.cpp @@ -51,6 +51,10 @@ MotVoiceHeader1::MotVoiceHeader1() : /// MotVoiceHeader1::MotVoiceHeader1(uint8_t* data) { + // set our pointers to null since we haven't initialized them yet + startOfStream = nullptr; + header = nullptr; + // decode decode(data); } diff --git a/src/dfsi/frames/MotVoiceHeader2.cpp b/src/dfsi/frames/MotVoiceHeader2.cpp index 3bc7f805..fed1e1fc 100644 --- a/src/dfsi/frames/MotVoiceHeader2.cpp +++ b/src/dfsi/frames/MotVoiceHeader2.cpp @@ -45,6 +45,9 @@ MotVoiceHeader2::MotVoiceHeader2() : /// MotVoiceHeader2::MotVoiceHeader2(uint8_t* data) { + // set pointer to null since it hasn't been initialized yet + header = nullptr; + // decode decode(data); } diff --git a/src/dfsi/network/SerialService.cpp b/src/dfsi/network/SerialService.cpp index 0008dd61..a41b711a 100644 --- a/src/dfsi/network/SerialService.cpp +++ b/src/dfsi/network/SerialService.cpp @@ -23,7 +23,7 @@ using namespace modem; using namespace p25; using namespace dfsi; -SerialService::SerialService(const std::string& portName, uint32_t baudrate, bool rtrt, bool diu, uint16_t jitter, DfsiPeerNetwork* network, uint32_t p25TxQueueSize, uint32_t p25RxQueueSize, bool debug, bool trace) : +SerialService::SerialService(const std::string& portName, uint32_t baudrate, bool rtrt, bool diu, uint16_t jitter, DfsiPeerNetwork* network, uint32_t p25TxQueueSize, uint32_t p25RxQueueSize, uint16_t callTimeout, bool debug, bool trace) : m_portName(portName), m_baudrate(baudrate), m_rtrt(rtrt), @@ -51,6 +51,7 @@ SerialService::SerialService(const std::string& portName, uint32_t baudrate, boo m_rxP25LDUCounter(0U), m_netCallInProgress(false), m_lclCallInProgress(false), + m_callTimeout(callTimeout), m_rxVoiceControl(nullptr), m_rxVoiceLsd(nullptr), m_rxVoiceCallData(nullptr) @@ -87,6 +88,9 @@ SerialService::~SerialService() void SerialService::clock(uint32_t ms) { + // Get now + uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + // Get data from serial port RESP_TYPE_DVM type = readSerial(); @@ -125,7 +129,7 @@ void SerialService::clock(uint32_t ms) // Add P25 data to buffer m_rxP25Queue.addData(m_msgBuffer + (cmdOffset + 1U), m_msgLength - (cmdOffset + 1U)); - if (m_debug) { + if (m_debug && m_trace) { LogDebug(LOG_SERIAL, "Got P25 data from V24 board (len: %u)", m_msgLength); } } @@ -175,6 +179,18 @@ void SerialService::clock(uint32_t ms) } else if (out < 0) { LogError(LOG_SERIAL, "Failed to write to serial port!"); } + + // Clear a call in progress flag if we're longer than our timeout value + if (m_lclCallInProgress && (now - m_lastLclFrame > m_callTimeout)) { + m_lclCallInProgress = false; + if (m_debug) + LogDebug(LOG_SERIAL, "Local call activity timeout"); + } + if (m_netCallInProgress && (now - m_lastNetFrame > m_callTimeout)) { + m_netCallInProgress = false; + if (m_debug) + LogDebug(LOG_SERIAL, "Net call activity timeout"); + } } bool SerialService::open() @@ -210,6 +226,9 @@ void SerialService::processP25FromNet(UInt8Array p25Buffer, uint32_t length) return; } + // Update our last frame time + m_lastNetFrame = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + // Decode grant info bool grantDemand = (p25Buffer[14U] & 0x80U) == 0x80U; @@ -440,7 +459,7 @@ void SerialService::processP25FromNet(UInt8Array p25Buffer, uint32_t length) count += dfsi::P25_DFSI_LDU2_VOICE18_FRAME_LENGTH_BYTES; control = lc::LC(*dfsiLC.control()); - LogInfoEx(LOG_NET, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", control.getAlgId(), control.getKId()); + LogInfoEx(LOG_SERIAL, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X", control.getAlgId(), control.getKId()); //Utils::dump("P25 LDU2 from net", netLDU2, 9U * 25U); @@ -550,6 +569,9 @@ void SerialService::processP25ToNet() //LogDebug(LOG_SERIAL, "Handling DFSI frameType 0x%02X", frameType); + // Update our last frame time + m_lastLclFrame = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + // Switch based on DFSI frame type switch (frameType) { // Start/Stop Frame @@ -1248,19 +1270,19 @@ void SerialService::writeP25Frame(uint8_t duid, dfsi::LC& lc, uint8_t* ldu) break; } - // Placeholder for last call timestamp retrieved below (not yet used) - //int64_t lastHeard = 0U; + // Placeholder for last call timestamp retrieved below (Not used yet) + int64_t lastHeard = 0U; // Placeholder for sequence number retrieved below uint32_t sequence = 0U; - // Get the last heard value (not used currently, TODO: use this for covnentional TGID timeout purposes) - /*{ + // Get the last heard value + { auto entry = m_lastHeard.find(control.getDstId()); if (entry != m_lastHeard.end()) { lastHeard = m_lastHeard[control.getDstId()]; } - }*/ + } // Get the last sequence number { @@ -1274,8 +1296,7 @@ void SerialService::writeP25Frame(uint8_t duid, dfsi::LC& lc, uint8_t* ldu) if (duid == P25_DUID_LDU1 && ((sequence == 0U) || (sequence == RTP_END_OF_CALL_SEQ))) { // Start the new stream startOfStream(lc); - // Update our call entries - m_lastHeard[control.getDstId()] = now; + // Update our call entry m_sequences[control.getDstId()] = ++sequence; // Log @@ -1305,6 +1326,9 @@ void SerialService::writeP25Frame(uint8_t duid, dfsi::LC& lc, uint8_t* ldu) m_sequences[control.getDstId()] = RTP_END_OF_CALL_SEQ; } + // Update our last heard value (updated to now every time a new frame arrives) + m_lastHeard[control.getDstId()] = now; + // Break out the 9 individual P25 packets for (int n = 0; n < 9; n++) { diff --git a/src/dfsi/network/SerialService.h b/src/dfsi/network/SerialService.h index f70b2f75..1cb9e6df 100644 --- a/src/dfsi/network/SerialService.h +++ b/src/dfsi/network/SerialService.h @@ -62,7 +62,7 @@ namespace network class HOST_SW_API SerialService { public: - SerialService(const std::string& portName, uint32_t baudrate, bool rtrt, bool diu, uint16_t jitter, DfsiPeerNetwork* network, uint32_t p25TxQueueSize, uint32_t p25RxQueueSize, bool debug, bool trace); + SerialService(const std::string& portName, uint32_t baudrate, bool rtrt, bool diu, uint16_t jitter, DfsiPeerNetwork* network, uint32_t p25TxQueueSize, uint32_t p25RxQueueSize, uint16_t callTimeout, bool debug, bool trace); ~SerialService(); @@ -111,6 +111,7 @@ namespace network RingBuffer m_rxP25Queue; RingBuffer m_txP25Queue; + // Storage for V24 TX jitter buffer metering uint64_t m_lastP25Tx; edac::RS634717 m_rs; @@ -118,10 +119,17 @@ namespace network // Counter for assembling a full LDU from individual frames in the RX queue uint8_t m_rxP25LDUCounter; - // Flags to indicate if calls to/from the FNE are already in progress + // "Mutex" flags to indicate if calls to/from the FNE are already in progress bool m_netCallInProgress; bool m_lclCallInProgress; + // Time in ms to wait before considering a call in progress as "over" in case we miss the TDUs + uint16_t m_callTimeout; + + // Storage for handling local/net call timeouts (for callInProgress mutexes) + uint64_t m_lastNetFrame; + uint64_t m_lastLclFrame; + // Control and LSD objects for current RX P25 data being built to send to the net lc::LC* m_rxVoiceControl; data::LowSpeedData* m_rxVoiceLsd;