From ec1cf8c87e8f78c2831de1f00e7c5bbe34615ad8 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 22 Jul 2024 11:46:43 -0400 Subject: [PATCH] drop dvmdfsi as a standalone application (this functionality has been entirely rolled into dvmhost); update README.md; --- CMakeLists.txt | 28 +- README.md | 35 +- configs/dfsi-config.example.yml | 105 -- src/CMakeLists.txt | 8 - src/dfsi/ActivityLog.cpp | 150 -- src/dfsi/ActivityLog.h | 45 - src/dfsi/CMakeLists.txt | 23 - src/dfsi/Defines.h | 45 - src/dfsi/Dfsi.cpp | 418 ------ src/dfsi/Dfsi.h | 94 -- src/dfsi/DfsiMain.cpp | 230 --- src/dfsi/DfsiMain.h | 60 - src/dfsi/network/CallData.cpp | 99 -- src/dfsi/network/CallData.h | 153 -- src/dfsi/network/DfsiPeerNetwork.cpp | 296 ---- src/dfsi/network/DfsiPeerNetwork.h | 118 -- src/dfsi/network/SerialService.cpp | 2039 -------------------------- src/dfsi/network/SerialService.h | 264 ---- 18 files changed, 19 insertions(+), 4191 deletions(-) delete mode 100644 configs/dfsi-config.example.yml delete mode 100644 src/dfsi/ActivityLog.cpp delete mode 100644 src/dfsi/ActivityLog.h delete mode 100644 src/dfsi/CMakeLists.txt delete mode 100644 src/dfsi/Defines.h delete mode 100644 src/dfsi/Dfsi.cpp delete mode 100644 src/dfsi/Dfsi.h delete mode 100644 src/dfsi/DfsiMain.cpp delete mode 100644 src/dfsi/DfsiMain.h delete mode 100644 src/dfsi/network/CallData.cpp delete mode 100644 src/dfsi/network/CallData.h delete mode 100644 src/dfsi/network/DfsiPeerNetwork.cpp delete mode 100644 src/dfsi/network/DfsiPeerNetwork.h delete mode 100644 src/dfsi/network/SerialService.cpp delete mode 100644 src/dfsi/network/SerialService.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 190a2f8b..d4b25e5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,7 +227,6 @@ endif (ENABLE_TESTS) install(TARGETS dvmhost DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) install(TARGETS dvmcmd DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) install(TARGETS dvmfne DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) -install(TARGETS dvmdfsi DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR)) install(TARGETS dvmmon DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR)) @@ -249,14 +248,12 @@ if (NOT TARGET strip) COMMAND arm-linux-gnueabihf-strip -s dvmhost COMMAND arm-linux-gnueabihf-strip -s dvmfne COMMAND arm-linux-gnueabihf-strip -s dvmcmd - COMMAND arm-linux-gnueabihf-strip -s dvmmon - COMMAND arm-linux-gnueabihf-strip -s dvmdfsi) + COMMAND arm-linux-gnueabihf-strip -s dvmmon) else() add_custom_target(strip COMMAND arm-linux-gnueabihf-strip -s dvmhost COMMAND arm-linux-gnueabihf-strip -s dvmfne - COMMAND arm-linux-gnueabihf-strip -s dvmcmd - COMMAND arm-linux-gnueabihf-strip -s dvmdfsi) + COMMAND arm-linux-gnueabihf-strip -s dvmcmd) endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR)) elseif (CROSS_COMPILE_AARCH64) if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR)) @@ -264,28 +261,24 @@ if (NOT TARGET strip) COMMAND aarch64-linux-gnu-strip -s dvmhost COMMAND aarch64-linux-gnu-strip -s dvmfne COMMAND aarch64-linux-gnu-strip -s dvmcmd - COMMAND aarch64-linux-gnu-strip -s dvmmon - COMMAND aarch64-linux-gnu-strip -s dvmdfsi) + COMMAND aarch64-linux-gnu-strip -s dvmmon) else() add_custom_target(strip COMMAND aarch64-linux-gnu-strip -s dvmhost COMMAND aarch64-linux-gnu-strip -s dvmfne - COMMAND aarch64-linux-gnu-strip -s dvmcmd - COMMAND aarch64-linux-gnu-strip -s dvmdfsi) + COMMAND aarch64-linux-gnu-strip -s dvmcmd) endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR)) elseif (CROSS_COMPILE_RPI_ARM) if (NOT WITH_RPI_ARM_TOOLS) add_custom_target(strip COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmhost COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmfne - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmcmd - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmdfsi) + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/_deps/rpitools-src/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmcmd) else() add_custom_target(strip COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmhost COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmfne - COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmcmd - COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmdfsi) + COMMAND ${RPI_ARM_TOOLS}/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf-strip -s dvmcmd) endif () else() if (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR)) @@ -293,14 +286,12 @@ if (NOT TARGET strip) COMMAND strip -s dvmhost COMMAND strip -s dvmfne COMMAND strip -s dvmcmd - COMMAND strip -s dvmmon - COMMAND strip -s dvmdfsi) + COMMAND strip -s dvmmon) else() add_custom_target(strip COMMAND strip -s dvmhost COMMAND strip -s dvmfne - COMMAND strip -s dvmcmd - COMMAND strip -s dvmdfsi) + COMMAND strip -s dvmcmd) endif (ENABLE_TUI_SUPPORT AND (NOT DISABLE_MONITOR)) endif (CROSS_COMPILE_ARM) endif (NOT TARGET strip) @@ -326,7 +317,6 @@ if (NOT TARGET tarball) COMMAND cp -v dvmcmd ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin COMMAND cp -v dvmmon ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin COMMAND cp -v dvmfne ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin - COMMAND cp -v dvmdfsi ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin COMMAND cp ../tools/*.sh ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm COMMAND chmod +x ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/*.sh COMMAND cp -v ../configs/*.yml ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm @@ -355,7 +345,6 @@ if (NOT TARGET tarball) COMMAND cp -v dvmhost ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin COMMAND cp -v dvmcmd ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin COMMAND cp -v dvmfne ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin - COMMAND cp -v dvmdfsi ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/bin COMMAND cp ../tools/*.sh ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm COMMAND chmod +x ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm/*.sh COMMAND cp -v ../configs/*.yml ${CMAKE_INSTALL_PREFIX_TARBALL}/dvm @@ -394,7 +383,6 @@ add_custom_target(old_install COMMAND install -m 755 dvmcmd ${CMAKE_LEGACY_INSTALL_PREFIX}/bin COMMAND install -m 755 dvmmon ${CMAKE_LEGACY_INSTALL_PREFIX}/bin COMMAND install -m 755 dvmfne ${CMAKE_LEGACY_INSTALL_PREFIX}/bin - COMMAND install -m 755 dvmdfsi ${CMAKE_LEGACY_INSTALL_PREFIX}/bin COMMAND install -m 644 ../configs/config.example.yml ${CMAKE_LEGACY_INSTALL_PREFIX}/config.example.yml COMMAND install -m 644 ../configs/fne-config.example.yml ${CMAKE_LEGACY_INSTALL_PREFIX}/fne-config.example.yml COMMAND install -m 644 ../configs/monitor-config.example.yml ${CMAKE_LEGACY_INSTALL_PREFIX}/monitor-config.example.yml diff --git a/README.md b/README.md index 84db4414..636791ec 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # Digital Voice Modem Host -The DVM Host (dvmhost) software suite, provides the a set of applications that, act as a primary host computer implementation of a mixed-mode DMR, P25 and/or NXDN or dedicated-mode DMR, P25 or NXDN repeater system that talks to the actual modem hardware, a TIA/V.24 standard interface (dvmdfsi) allowing communications to commercial P25 hardware, and the networking core (dvmfne) that provides a centralized network service that interconnects various DVM endpoint applications allowing networked communications. +The DVM Host (dvmhost) software suite, provides the a set of applications that, act as a primary host computer implementation of a mixed-mode DMR, P25 and/or NXDN or dedicated-mode DMR, P25 or NXDN repeater system that talks to the actual air modem hardware, a TIA/V.24 standard interface mode allowing communications to commercial P25 hardware using the V.24 DFSI modem hardware or UDP, and the networking core (dvmfne) that provides a centralized network service that interconnects various DVM endpoint applications allowing networked communications. Please feel free to reach out to us for help, comments or otherwise, on our Discord: https://discord.gg/3pBe8xgrEz This project generates a few executables: - `dvmhost` host software that connects to the DVM modems (both repeater and hotspot) and is the primary data processing application for digital modes. [See configuration](#dvmhost-configuration) to configure and calibrate. -- `dvmdfsi` TIA/V.24 standard interface application that connects to a V.24 interface board or UDP to allow for P25 DFSI communications with commercial P25 hardware. - `dvmfne` a network "core", this provides a central server for `dvmhost` instances to connect to and be networked with, allowing relay of traffic and other data between `dvmhost` instances and other `dvmfne` instances. [See configuration](#dvmfne-configuration) to configure. - `dvmcmd` a simple command-line utility to send remote control commands to a `dvmhost` or `dvmfne` instance with REST API configured. - `dvmmon` a TUI utility that allows semi-realtime console-based monitoring of `dvmhost` instances (this tool is only available when project wide TUI support is enabled!). @@ -80,9 +79,12 @@ It should also be important to read and review the [calibration notes](#calibrat ### Initial Setup Steps -The following setups assume the host is compiled with the setup TUI mode (if availble). It is possible to setup the modem without the setup TUI, and requires manually modifying `config.yml` and the `iden_table.dat` files. +The following setups assume the host is compiled with the setup TUI mode (if available) [NOTE: Steps 3 - 5 only apply to the air interface modem.]. It is possible to setup the modem without the setup TUI, and requires manually modifying `config.yml` and the `iden_table.dat` files. -1. Create/Edit `config.yml` and ensure the settings for the modem are correct, find the "modem" section in "system". Check that the uart protocol has the appropriate UART port and port speed set (the config.yml defaults to /dev/ttyUSB0 and 115200). +1. Create/Edit `config.yml` and ensure the settings for the modem are correct, find the "modem" section in "system". Check that the uart settings have the appropriate UART port and port speed set (the config.yml defaults to /dev/ttyUSB0 and 115200). + 1.1. If using the air modem interface, ensure the the modem protocol mode is set to "air". + 1.2. If using the V.24 DFSI modem interface, ensure the modem protocol mode is set to "dfsi". + 1.2.1. The V.24 DFSI modem has multiple firmware revisions, it is required to use firmware version 2.0 or greater for use with dvmhost. 2. Start `dvmhost` as follows: `/path/to/dvmhost -c /path/to/config.yml --setup`. This will start the dvmhost setup TUI mode. 3. Using the TUI user interface, use the "Setup" menu to set default parameters. 3.1. The "Logging & Data Configuration" submenu allows you to alter the various logging file paths and levels, as well as paths to data files (such as the `iden_table.dat` file). @@ -92,7 +94,7 @@ The following setups assume the host is compiled with the setup TUI mode (if ava 4. After altering settings, use the "File" menu, "Save Settings" menu option to save the desired configuration. 5. Quit setup mode (some settings changes require a restart of the software to be effective) using, "File" menu, "Quit". -### Transmit Calibration (using setup TUI, if available) +### (Air Interface) Transmit Calibration (using setup TUI, if available) 1. Start `dvmhost` as follows: `/path/to/dvmhost -c /path/to/config.yml --setup`. This will start the dvmhost setup TUI mode. The best way to calibrate the DVM is to use a radio from which you can receive and transmit the appropriate test patterns (for example using ASTRO25 Tuner and an XTS radio to use the "Bit Error Rate" functions under Performance Testing). 2. Depending on which protocol you are calibration with, use the "Calibrate" menu, and select the appropriate mode using the "Operational Mode" submenu. (For example, select [Tx] DMR BS 1031 Hz Test Pattern for DMR or [Tx] P25 1011 Hz Test Pattern (NAC293 ID1 TG1) for P25.) @@ -105,7 +107,7 @@ The following setups assume the host is compiled with the setup TUI mode (if ava 9. After altering settings, use the "File" menu, "Save Settings" menu option to save the desired configuration. 10. Quit setup mode, if done doing calibration, using, "File" menu, "Quit". -### Transmit Calibration (using old calibration CLI) +### (Air Interface) Transmit Calibration (using old calibration CLI) 1. Start `dvmhost` as follows: `/path/to/dvmhost -c /path/to/config.yml --cal`. This will start the dvmhost calibration mode. The best way to calibrate the DVM is to use a radio from which you can receive and transmit the appropriate test patterns (for example using ASTRO25 Tuner and an XTS radio to use the "Bit Error Rate" functions under Performance Testing). 2. Depending on which protocol you are calibration with, enter DMR BS 1031 Hz Test Pattern (M) or P25 1011 Hz Test Pattern (NAC293 ID1 TG1) (P). @@ -116,7 +118,7 @@ The following setups assume the host is compiled with the setup TUI mode (if ava 7. Stop Tx (press spacebar to toggle Tx). 8. Save the configuration using "s" and quit calibration mode with "q". -### Receive Calibration (using setup TUI, if available) +### (Air Interface) Receive Calibration (using setup TUI, if available) 1. Start `dvmhost` as follows: `/path/to/dvmhost -c /path/to/config.yml --setup`. This will start the dvmhost setup TUI mode. The best way to calibrate the DVM is to use a radio from which you can receive and transmit the appropriate test patterns (for example using ASTRO25 Tuner and an XTS radio to use the "Transmitter Test Pattern" functions under Performance Testing). 2. Depending on which protocol you are calibration with, use the "Calibrate" menu, and select the appropriate mode using the "Operational Mode" submenu. (For example, select [Rx] DMR BS 1031 Hz Test Pattern for DMR or [Rx] P25 1011 Hz Test Pattern (NAC293 ID1 TG1) for P25.) @@ -127,7 +129,7 @@ The following setups assume the host is compiled with the setup TUI mode (if ava 8. After altering settings, use the "File" menu, "Save Settings" menu option to save the desired configuration. 9. Quit setup mode, if done doing calibration, using, "File" menu, "Quit". -### Receive Calibration (using old calibration CLI) +### (Air Interface) Receive Calibration (using old calibration CLI) 1. Start `dvmhost` as follows: `/path/to/dvmhost -c /path/to/config.yml --cal`. This will start the dvmhost calibration mode. The best way to calibrate the DVM is to use a radio from which you can receive and transmit the appropriate test patterns (for example using ASTRO25 Tuner and an XTS radio to use the "Transmitter Test Pattern" functions under Performance Testing). 2. Depending on which protocol you are calibration with, enter DMR BS 1031 Hz Test Pattern (M) or P25 1011 Hz Test Pattern (P). @@ -137,7 +139,7 @@ The following setups assume the host is compiled with the setup TUI mode (if ava 6. While observing the BER via the calibration console, adjust the RX potentiometer(s) for the lowest received BER. If necessary also adjust the software RXLevel for some fine tuning with the "R" (increase) and "r" (decrease). 7. Save the configuration using "s" and quit calibration mode with "q". -### Calibration Notes +### (Air Interface) Calibration Notes - If you have access to appropriate RF test equipment (or equivilant equipment) that is capable of monitor the overall transmitted *analog* FM deviation; if is important to adjust both the modem and the connected radios so that the overall transmitted *analog* FM deviation be between 2.75khz and 2.83khz (a center average of 2.80khz *analog* FM deviation is best). - When using a repeater/modem board attached to an appropriate FM repeater/radio, it *may* be necessary to "de-tune" the repeater/radio slightly, most commercial grade equipment operating within a 12.5khz channel may impose a strict 2.5khz (and no greater) maximum *analog* FM deviation, this is well below what is required for good digital operation. It may be necessary using whatever tuning/alignment tools to "de-tune" or adjust the equipments alignment to allow for a wider *analog* FM deviation, as close to 2.80khz as possible. @@ -176,21 +178,6 @@ usage: ./dvmhost [-vhdf][--syslog][--setup][-c ][--remote [- -- stop handling options ``` -### dvmdfsi Command Line Parameters -``` -usage: ./dvmdfsi [-vhf][--syslog][-c ] - - -v show version information - -h show this screen - -f foreground mode - - --syslog force logging to syslog - - -c specifies the configuration file to use - - -- stop handling options -``` - ### dvmfne Command Line Parameters ``` diff --git a/configs/dfsi-config.example.yml b/configs/dfsi-config.example.yml deleted file mode 100644 index c8c2418c..00000000 --- a/configs/dfsi-config.example.yml +++ /dev/null @@ -1,105 +0,0 @@ -# -# Digital Voice Modem - DFSI Software Configuration -# -# @package DVM / DFSI Software -# - -# Flag indicating whether the host will run as a background or foreground task. -daemon: false - -# -# Network Configuration -# -network: - # Textual Name - identity: DFSI - # Network Peer ID - peerId: 9000123 - # Hostname/IP address of FNE master to connect to. - address: 127.0.0.1 - # Port number to connect to. - port: 62031 - # FNE access password. - password: RPT1234 - # Additional debug logging - debug: false - # Encrypted endpoint networking - encrypted: false - # Pre-shared key for encrypted networking (AES-256 32-byte hex keystring) - presharedKey: "000102030405060708090A0B0C0D0E0F000102030405060708090A0B0C0D0E0F" - -# -# DFSI Configuration -# -dfsi: - # Mode - # 1 - UDP DFSI to DVM FNE (TODO: Implement this) - # 2 - Serial DFSI to DVM FNE (Using DVM-V24 adapter board) - # 3 - Serial DFSI to UDP DFSI (TODO: Implement this) - mode: 2 - - # Flag enabling "the" Manufacturer standard of RTP. - theManufacturer: false - - # 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 - # Flag disabling control connection establishment. - noConnectionEstablishment: false - # Local DFSI RTP Port number. - localRtpPort: 27500 - # Remote RFSS DFSI Hostname/IP address of FNE master to connect to. - remoteDfsiAddress: 127.0.0.2 - # Remote DFSI Control Port number to connect to. - remoteControlPort: 27000 - # Remote DFSI RTP Port number to connect to. - remoteRtpPort: 27500 - - serial: - # Modem port type. - portType: "uart" # Valid values are "null", and "uart" - # Serial configuration for serial DFSI - port: "/dev/ttyACM0" - # - baudrate: 115200 - # RT/RT flag enabled (0x02) or disabled (0x04) - rtrt: true - # Use the DIU source flag (0x00) instead of the quantar source flag (0x02) - diu: true - # Jitter buffer length in ms - jitter: 200 - # Debug logging - debug: true - # 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: -# 1 - Debug -# 2 - Message -# 3 - Informational -# 4 - Warning -# 5 - Error -# 6 - Fatal -# -log: - # Console display logging level (used when in foreground). - displayLevel: 1 - # File logging level. - fileLevel: 1 - # Flag indicating file logs should be sent to syslog instead of a file. - useSyslog: false - # Full path for the directory to store the log files. - filePath: . - # Full path for the directory to store the activity log files. - activityFilePath: . - # Log filename prefix. - fileRoot: dvmdfsi diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1ea18be7..82807df8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -81,11 +81,3 @@ include(src/remote/CMakeLists.txt) add_executable(dvmcmd ${common_INCLUDE} ${dvmcmd_SRC}) target_link_libraries(dvmcmd PRIVATE common ${OPENSSL_LIBRARIES} asio::asio Threads::Threads) target_include_directories(dvmcmd PRIVATE ${OPENSSL_INCLUDE_DIR} src src/remote) - -# -## dvmdfsi -# -include(src/dfsi/CMakeLists.txt) -add_executable(dvmdfsi ${common_INCLUDE} ${dvmdfsi_SRC}) -target_link_libraries(dvmdfsi PRIVATE common ${OPENSSL_LIBRARIES} asio::asio Threads::Threads) -target_include_directories(dvmdfsi PRIVATE ${OPENSSL_INCLUDE_DIR} src src/host src/dfsi) \ No newline at end of file diff --git a/src/dfsi/ActivityLog.cpp b/src/dfsi/ActivityLog.cpp deleted file mode 100644 index 79bf2617..00000000 --- a/src/dfsi/ActivityLog.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * - */ -#include "ActivityLog.h" -#include "common/network/BaseNetwork.h" -#include "common/Log.h" // for CurrentLogFileLevel() and LogGetNetwork() - -#include - -#if defined(CATCH2_TEST_COMPILATION) -#include -#endif - -#include -#include -#include -#include -#include -#include - -// --------------------------------------------------------------------------- -// Constants -// --------------------------------------------------------------------------- - -#define EOL "\r\n" - -const uint32_t ACT_LOG_BUFFER_LEN = 501U; - -// --------------------------------------------------------------------------- -// Global Variables -// --------------------------------------------------------------------------- - -static std::string m_actFilePath; -static std::string m_actFileRoot; - -static FILE* m_actFpLog = nullptr; - -static struct tm m_actTm; - -// --------------------------------------------------------------------------- -// Global Functions -// --------------------------------------------------------------------------- - -/* Helper to open the activity log file, file handle. */ - -static bool ActivityLogOpen() -{ - if (CurrentLogFileLevel() == 0U) - return true; - - time_t now; - ::time(&now); - - struct tm* tm = ::localtime(&now); - - if (tm->tm_mday == m_actTm.tm_mday && tm->tm_mon == m_actTm.tm_mon && tm->tm_year == m_actTm.tm_year) { - if (m_actFpLog != nullptr) - return true; - } - else { - if (m_actFpLog != nullptr) - ::fclose(m_actFpLog); - } - - char filename[200U]; - ::sprintf(filename, "%s/%s-%04d-%02d-%02d.activity.log", LogGetFilePath().c_str(), LogGetFileRoot().c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); - - m_actFpLog = ::fopen(filename, "a+t"); - m_actTm = *tm; - - return m_actFpLog != nullptr; -} - -/* Initializes the activity log. */ - -bool ActivityLogInitialise(const std::string& filePath, const std::string& fileRoot) -{ -#if defined(CATCH2_TEST_COMPILATION) - return true; -#endif - m_actFilePath = filePath; - m_actFileRoot = fileRoot; - - return ::ActivityLogOpen(); -} - -/* Finalizes the activity log. */ - -void ActivityLogFinalise() -{ -#if defined(CATCH2_TEST_COMPILATION) - return; -#endif - if (m_actFpLog != nullptr) - ::fclose(m_actFpLog); -} - -/* Writes a new entry to the activity log. */ - -void ActivityLog(const char* msg, ...) -{ -#if defined(CATCH2_TEST_COMPILATION) - return; -#endif - assert(msg != nullptr); - - char buffer[ACT_LOG_BUFFER_LEN]; - struct timeval now; - ::gettimeofday(&now, NULL); - - struct tm* tm = ::gmtime(&now.tv_sec); - - ::sprintf(buffer, "A: %04d-%02d-%02d %02d:%02d:%02d.%03lu ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000U); - - va_list vl, vl_len; - va_start(vl, msg); - va_copy(vl_len, vl); - - size_t len = ::vsnprintf(nullptr, 0U, msg, vl_len); - ::vsnprintf(buffer + ::strlen(buffer), len + 1U, msg, vl); - - va_end(vl_len); - va_end(vl); - - bool ret = ::ActivityLogOpen(); - if (!ret) - return; - - if (LogGetNetwork() != nullptr) { - network::BaseNetwork* network = (network::BaseNetwork*)LogGetNetwork();; - network->writeActLog(buffer); - } - - if (CurrentLogFileLevel() == 0U) - return; - - ::fprintf(m_actFpLog, "%s\n", buffer); - ::fflush(m_actFpLog); - - if (2U >= g_logDisplayLevel && g_logDisplayLevel != 0U) { - ::fprintf(stdout, "%s" EOL, buffer); - ::fflush(stdout); - } -} \ No newline at end of file diff --git a/src/dfsi/ActivityLog.h b/src/dfsi/ActivityLog.h deleted file mode 100644 index 164bc511..00000000 --- a/src/dfsi/ActivityLog.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * - */ -/** - * @file ActivityLog.h - * @ingroup dfsi - * @file ActivityLog.cpp - * @ingroup dfsi - */ -#if !defined(__ACTIVITY_LOG_H__) -#define __ACTIVITY_LOG_H__ - -#include "Defines.h" - -#include - -// --------------------------------------------------------------------------- -// Global Functions -// --------------------------------------------------------------------------- - -/** - * @brief Initializes the activity log. - * @param filePath File path for the log file. - * @param fileRoot Root name for log file. - */ -extern HOST_SW_API bool ActivityLogInitialise(const std::string& filePath, const std::string& fileRoot); -/** - * @brief Finalizes the activity log. - */ -extern HOST_SW_API void ActivityLogFinalise(); -/** - * @brief Writes a new entry to the activity log. - * @param msg String format. - * - * This is a variable argument function. - */ -extern HOST_SW_API void ActivityLog(const char* msg, ...); - -#endif // __ACTIVITY_LOG_H__ diff --git a/src/dfsi/CMakeLists.txt b/src/dfsi/CMakeLists.txt deleted file mode 100644 index 8330ff76..00000000 --- a/src/dfsi/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -#/* -# * Digital Voice Modem - Modem Host Software -# * GPLv2 Open Source. Use is subject to license terms. -# * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# * -# * Copyright (C) 2024 Patrick McDonnell, W3AXL -# * -# */ -file(GLOB dvmdfsi_SRC - "src/host/modem/port/*.h" - "src/host/modem/port/*.cpp" - "src/host/network/Network.h" - "src/host/network/Network.cpp" - - # Network Core - "src/dfsi/network/*.h" - "src/dfsi/network/*.cpp" - - # Core - "src/dfsi/*.h" - "src/dfsi/*.cpp" -) \ No newline at end of file diff --git a/src/dfsi/Defines.h b/src/dfsi/Defines.h deleted file mode 100644 index 89418203..00000000 --- a/src/dfsi/Defines.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * - */ -/** - * @defgroup dfsi DFSI V.24/UDP Software (dvmdfsi) - * @brief Digital Voice Modem - DFSI V.24/UDP Software - * @details TIA/V.24 standard interface application that connects to a V.24 interface board or UDP to allow for P25 DFSI communications with commercial P25 hardware. - * @ingroup dfsi - * - * @file Defines.h - * @ingroup dfsi - */ -#if !defined(__DEFINES_H__) -#define __DEFINES_H__ - -#include "common/Defines.h" - -// --------------------------------------------------------------------------- -// Constants -// --------------------------------------------------------------------------- - -#undef __PROG_NAME__ -#define __PROG_NAME__ "Digital Voice Modem (DVM) DFSI V.24/UDP Peer" -#undef __EXE_NAME__ -#define __EXE_NAME__ "dvmdfsi" - -#undef __NETVER__ -#define __NETVER__ "DFSI" VERSION_MAJOR VERSION_REV VERSION_MINOR - -#undef DEFAULT_CONF_FILE -#define DEFAULT_CONF_FILE "dfsi-config.yml" -#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 deleted file mode 100644 index f52a44a3..00000000 --- a/src/dfsi/Dfsi.cpp +++ /dev/null @@ -1,418 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL - * - */ -#include "Defines.h" -#include "common/dmr/DMRDefines.h" -#include "common/p25/P25Utils.h" -#include "common/network/udp/Socket.h" -#include "common/Log.h" -#include "common/StopWatch.h" -#include "common/Thread.h" -#include "common/Utils.h" -#include "host/ActivityLog.h" -#include "Dfsi.h" -#include "DfsiMain.h" - -using namespace network; -using namespace lookups; - -#include -#include -#include -#include - -#include -#include -#include - -// --------------------------------------------------------------------------- -// Constants -// --------------------------------------------------------------------------- - -#define IDLE_WARMUP_MS 5U - -// --------------------------------------------------------------------------- -// Public Class Members -// --------------------------------------------------------------------------- - -/* Initializes a new instance of the HostTest class. */ - -Dfsi::Dfsi(const std::string& confFile) : - m_confFile(confFile), - m_conf(), - m_network(nullptr), - m_ridLookup(nullptr), - m_tidLookup(nullptr), - m_pingTime(5U), - m_maxMissedPings(5U), - m_updateLookupTime(10U), - m_debug(false), - m_repeatTraffic(true), - m_serial(nullptr) -{ - /* stub */ -} - -/* Finalizes a instance of the HostTest class. */ - -Dfsi::~Dfsi() = default; - -/* Executes the main FNE processing loop. */ - -int Dfsi::run() -{ - bool ret = false; - - // Try and parse the config yaml - try { - ret = yaml::Parse(m_conf, m_confFile.c_str()); - if (!ret) { - ::fatal("cannot read the configuration file, %s\n", m_confFile.c_str()); - } - } - catch (yaml::OperationException const& e) { - ::fatal("cannot read the configuration file - %s (%s)", m_confFile.c_str(), e.message()); - } - - // Check if we should run as a daemn or not - bool m_daemon = m_conf["daemon"].as(false); - if (m_daemon && g_foreground) - m_daemon = false; - - // initialize system logging - yaml::Node logConf = m_conf["log"]; - ret = ::LogInitialise(logConf["filePath"].as(), logConf["fileRoot"].as(), - logConf["fileLevel"].as(0U), logConf["displayLevel"].as(0U), false, logConf["useSyslog"].as(false)); - if (!ret) { - ::fatal("unable to open the log file\n"); - } - - // Init activity logging - ret = ::ActivityLogInitialise(logConf["activityFilePath"].as(), logConf["fileRoot"].as()); - if (!ret) { - ::fatal("unable to open the activity log file\n"); - } - - // handle POSIX process forking - if (m_daemon) { - // create new process - pid_t pid = ::fork(); - if (pid == -1) { - ::fprintf(stderr, "%s: Couldn't fork() , exiting\n", g_progExe.c_str()); - ::LogFinalise(); - return EXIT_FAILURE; - } - else if (pid != 0) { - ::LogFinalise(); - exit(EXIT_SUCCESS); - } - - // create new session and process group - if (::setsid() == -1) { - ::fprintf(stderr, "%s: Couldn't setsid(), exiting\n", g_progExe.c_str()); - ::LogFinalise(); - return EXIT_FAILURE; - } - - // set the working directory to the root directory - if (::chdir("/") == -1) { - ::fprintf(stderr, "%s: Couldn't cd /, exiting\n", g_progExe.c_str()); - ::LogFinalise(); - return EXIT_FAILURE; - } - - ::close(STDIN_FILENO); - ::close(STDOUT_FILENO); - ::close(STDERR_FILENO); - } - - ::LogInfo(__BANNER__ "\r\n" __PROG_NAME__ " " __VER__ " (built " __BUILD__ ")\r\n" \ - "Copyright (c) 2024 Patrick McDonnell, W3AXL and DVMProject (https://github.com/dvmproject) Authors.\r\n" \ - ">> DFSI Network Peer\r\n"); - - // read base parameters from configuration - ret = readParams(); - 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) - return EXIT_FAILURE; - - std::string dfsiModeStr = "Unknown"; - - 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; - } - - 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(); - - /* - ** Main execution loop - */ - - struct utsname utsinfo; - ::memset(&utsinfo, 0, sizeof(utsinfo)); - ::uname(&utsinfo); - - ::LogInfoEx(LOG_HOST, "[ OK ] DFSI is up and running on %s %s %s", utsinfo.sysname, utsinfo.release, utsinfo.machine); - - // Get serial board info if connected - if (m_serial) - { - m_serial->getBoardInfo(); - } - - // main execution loop - while (!g_killed) { - uint32_t ms = stopWatch.elapsed(); - - ms = stopWatch.elapsed(); - stopWatch.start(); - - // ------------------------------------------------------ - // -- Network RX Clocking -- - // ------------------------------------------------------ - - if (m_network != nullptr) - m_network->clock(ms); - - uint32_t length = 0U; - bool netReadRet = false; - - UInt8Array p25Buffer = m_network->readP25(netReadRet, length); - if (netReadRet) { - // Send the data to the serial handler if serial is up - if (m_serial != nullptr) - m_serial->processP25FromNet(std::move(p25Buffer), length); - } - - // ------------------------------------------------------ - // -- Network TX Clocking -- - // ------------------------------------------------------ - - // 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(); - } - - // ------------------------------------------------------ - // -- Serial Clocking -- - // ------------------------------------------------------ - - if (m_serial != nullptr) { - m_serial->clock(ms); - } - - // Timekeeping - if (ms < 2U) - Thread::sleep(1U); - } - - ::LogSetNetwork(nullptr); - if (m_network != nullptr) { - m_network->close(); - delete m_network; - } - - if (m_serial != nullptr) { - m_serial->close(); - delete m_serial; - } - - if (m_tidLookup != nullptr) { - m_tidLookup->stop(); - delete m_tidLookup; - } - if (m_ridLookup != nullptr) { - m_ridLookup->stop(); - delete m_ridLookup; - } - - return EXIT_SUCCESS; -} - -// --------------------------------------------------------------------------- -// Private Class Members -// --------------------------------------------------------------------------- - -/* Reads basic configuration parameters from the YAML configuration file. */ - -bool Dfsi::readParams() -{ - // No basic config params right now - - return true; -} - -/* Initializes peer network connectivity. */ - -bool Dfsi::createPeerNetwork() -{ - yaml::Node networkConf = m_conf["network"]; - std::string password = networkConf["password"].as(); - - std::string address = networkConf["address"].as(); - uint16_t port = networkConf["port"].as(); - uint32_t id = networkConf["peerId"].as(); - - bool encrypted = networkConf["encrypted"].as(false); - std::string key = networkConf["presharedKey"].as(); - uint8_t presharedKey[AES_WRAPPED_PCKT_KEY_LEN]; - if (!key.empty()) { - if (key.size() == 32) { - // bryanb: shhhhhhh....dirty nasty hacks - key = key.append(key); // since the key is 32 characters (16 hex pairs), double it on itself for 64 characters (32 hex pairs) - LogWarning(LOG_HOST, "Half-length network preshared encryption key detected, doubling key on itself."); - } - - if (key.size() == 64) { - if ((key.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos)) { - const char* keyPtr = key.c_str(); - ::memset(presharedKey, 0x00U, AES_WRAPPED_PCKT_KEY_LEN); - - for (uint8_t i = 0; i < AES_WRAPPED_PCKT_KEY_LEN; i++) { - char t[4] = {keyPtr[0], keyPtr[1], 0}; - presharedKey[i] = (uint8_t)::strtoul(t, NULL, 16); - keyPtr += 2 * sizeof(char); - } - } - else { - LogWarning(LOG_HOST, "Invalid characters in the network preshared encryption key. Encryption disabled."); - encrypted = false; - } - } - else { - LogWarning(LOG_HOST, "Invalid network preshared encryption key length, key should be 32 hex pairs, or 64 characters. Encryption disabled."); - encrypted = false; - } - } - - std::string identity = networkConf["identity"].as(); - - 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(" Encrypted: %s", encrypted ? "yes" : "no"); - - if (id > 999999999U) { - ::LogError(LOG_HOST, "Network Peer ID cannot be greater then 999999999."); - return false; - } - - // initialize networking - m_network = new DfsiPeerNetwork(address, port, 0U, id, password, true, netDebug, false, true, false, true, true, true, true, true, false); - m_network->setMetadata(identity, 0U, 0U, 0.0F, 0.0F, 0, 0, 0, 0.0F, 0.0F, 0, ""); - m_network->setLookups(m_ridLookup, m_tidLookup); - - ::LogSetNetwork(m_network); - - if (encrypted) { - m_network->setPresharedKey(presharedKey); - } - - m_network->enable(true); - bool ret = m_network->open(); - if (!ret) { - delete m_network; - m_network = nullptr; - LogError(LOG_HOST, "failed to initialize traffic networking for PEER %u", id); - return false; - } - - ::LogSetNetwork(m_network); - - return true; -} - -/* Initializes serial V.24 network. */ - -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 portType = serial_conf["portType"].as("null"); - 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 Type: %s", portType.c_str()); - 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(portType, 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 deleted file mode 100644 index 71d0d2f2..00000000 --- a/src/dfsi/Dfsi.h +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * - */ -/** - * @file Dfsi.h - * @ingroup dfsi - * @file Dfsi.cpp - * @ingroup dfsi - */ -#if !defined(__DFSI_H__) -#define __DFSI_H__ - -#include "Defines.h" -#include "common/lookups/RadioIdLookup.h" -#include "common/lookups/TalkgroupRulesLookup.h" -#include "common/yaml/Yaml.h" -#include "common/Timer.h" -#include "network/DfsiPeerNetwork.h" -#include "network/SerialService.h" - -#include -#include -#include - -// --------------------------------------------------------------------------- -// Class Declaration -// --------------------------------------------------------------------------- - -/** - * @brief This class implements the core service logic. - * @ingroup dfsi - */ -class HOST_SW_API Dfsi { -public: - /** - * @brief Initializes a new instance of the HostTest class. - * @param confFile Full-path to the configuration file. - */ - Dfsi(const std::string& confFile); - /** - * @brief Finalizes a instance of the HostTest class. - */ - ~Dfsi(); - - /** - * @brief Executes the main host processing loop. - * @returns int Zero if successful, otherwise error occurred. - */ - int run(); - -private: - const std::string& m_confFile; - yaml::Node m_conf; - - network::DfsiPeerNetwork* m_network; - - lookups::RadioIdLookup* m_ridLookup; - lookups::TalkgroupRulesLookup* m_tidLookup; - - uint32_t m_pingTime; - uint32_t m_maxMissedPings; - - uint32_t m_updateLookupTime; - - bool m_debug; - - bool m_repeatTraffic; - - network::SerialService* m_serial; - - /** - * @brief Reads basic configuration parameters from the INI. - * @returns bool True, if configuration was read successfully, otherwise false. - */ - bool readParams(); - /** - * @brief Initializes peer network connectivity. - * @returns bool True, if network connectivity was initialized, otherwise false. - */ - bool createPeerNetwork(); - /** - * @brief Initializes serial V.24 network. - * @returns bool True, if serial connectivity was initialized, otherwise false. - */ - bool createSerialNetwork(uint32_t p25BufferSize, uint16_t callTimeout); -}; - -#endif // __DFSI_H__ \ No newline at end of file diff --git a/src/dfsi/DfsiMain.cpp b/src/dfsi/DfsiMain.cpp deleted file mode 100644 index 2f038355..00000000 --- a/src/dfsi/DfsiMain.cpp +++ /dev/null @@ -1,230 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL - * - */ -#include "Defines.h" -#include "common/Log.h" -#include "dfsi/ActivityLog.h" -#include "DfsiMain.h" -#include "Dfsi.h" - -using namespace network; -using namespace lookups; - -#include -#include -#include - -#include - -// --------------------------------------------------------------------------- -// Macros -// --------------------------------------------------------------------------- - -#define IS(s) (::strcmp(argv[i], s) == 0) - -// --------------------------------------------------------------------------- -// Global Variables -// --------------------------------------------------------------------------- - -int g_signal = 0; -std::string g_progExe = std::string(__EXE_NAME__); -std::string g_iniFile = std::string(DEFAULT_CONF_FILE); -std::string g_lockFile = std::string(DEFAULT_LOCK_FILE); - -std::string g_masterAddress = std::string("127.0.0.1"); -uint16_t g_masterPort = 63031; -uint32_t g_peerId = 9000999; - -bool g_foreground = false; -bool g_killed = false; -bool g_hideMessages = false; - -uint8_t* g_gitHashBytes = nullptr; - -// --------------------------------------------------------------------------- -// Global Functions -// --------------------------------------------------------------------------- - -#if !defined(CATCH2_TEST_COMPILATION) -/* Internal signal handler. */ - -static void sigHandler(int signum) -{ - g_signal = signum; - g_killed = true; -} -#endif - -/* Helper to print a fatal error message and exit. */ - -void fatal(const char* msg, ...) -{ - char buffer[400U]; - ::memset(buffer, 0x20U, 400U); - - va_list vl; - va_start(vl, msg); - - ::vsprintf(buffer, msg, vl); - - va_end(vl); - - ::fprintf(stderr, "%s: %s\n", g_progExe.c_str(), buffer); - exit(EXIT_FAILURE); -} - -/* Helper to pring usage the command line arguments. (And optionally an error.) */ - -void usage(const char* message, const char* arg) -{ - ::fprintf(stdout, __PROG_NAME__ " %s (built %s)\r\n", __VER__, __BUILD__); - ::fprintf(stdout, "Copyright (c) 2024 Patrick McDonnell, W3AXL and DVMProject (https://github.com/dvmproject) Authors.\n\n"); - if (message != nullptr) { - ::fprintf(stderr, "%s: ", g_progExe.c_str()); - ::fprintf(stderr, message, arg); - ::fprintf(stderr, "\n\n"); - } - - ::fprintf(stdout, - "usage: %s [-vhf]" - "[--syslog]" - "[-c ]" - "\n\n" - " -v show version information\n" - " -h show this screen\n" - " -f foreground mode\n" - "\n" - " --syslog force logging to syslog\n" - "\n" - " -c specifies the configuration file to use\n" - "\n" - " -- stop handling options\n", - g_progExe.c_str()); - exit(EXIT_FAILURE); -} - -/* Helper to validate the command line arguments. */ - -int checkArgs(int argc, char* argv[]) -{ - int i, p = 0; - - // iterate through arguments - for (i = 1; i <= argc; i++) - { - if (argv[i] == nullptr) { - break; - } - - if (*argv[i] != '-') { - continue; - } - else if (IS("--")) { - ++p; - break; - } - else if (IS("-f")) { - g_foreground = true; - } - else if (IS("--syslog")) { - g_useSyslog = true; - } - else if (IS("-c")) { - if (argc-- <= 0) - usage("error: %s", "must specify the configuration file to use"); - g_iniFile = std::string(argv[++i]); - - if (g_iniFile.empty()) - usage("error: %s", "configuration file cannot be blank!"); - - p += 2; - } - else if (IS("-v")) { - ::fprintf(stdout, __PROG_NAME__ " %s (built %s)\r\n", __VER__, __BUILD__); - ::fprintf(stdout, "Copyright (c) 2024 Patrick McDonnell, W3AXL and DVMProject (https://github.com/dvmproject) Authors.\n"); - ::fprintf(stdout, "Portions Copyright (c) 2015-2021 by Jonathan Naylor, G4KLX and others\n\n"); - if (argc == 2) - exit(EXIT_SUCCESS); - } - else if (IS("-h")) { - usage(nullptr, nullptr); - if (argc == 2) - exit(EXIT_SUCCESS); - } - else { - usage("unrecognized option `%s'", argv[i]); - } - } - - if (p < 0 || p > argc) { - p = 0; - } - - return ++p; -} - -// --------------------------------------------------------------------------- -// Program Entry Point -// --------------------------------------------------------------------------- -#if !defined(CATCH2_TEST_COMPILATION) -int main(int argc, char** argv) -{ - g_gitHashBytes = new uint8_t[4U]; - ::memset(g_gitHashBytes, 0x00U, 4U); - - uint32_t hash = ::strtoul(__GIT_VER_HASH__, 0, 16); - __SET_UINT32(hash, g_gitHashBytes, 0U); - - if (argv[0] != nullptr && *argv[0] != 0) - g_progExe = std::string(argv[0]); - - if (argc > 1) { - // check arguments - int i = checkArgs(argc, argv); - if (i < argc) { - argc -= i; - argv += i; - } - else { - argc--; - argv++; - } - } - - ::signal(SIGINT, sigHandler); - ::signal(SIGTERM, sigHandler); - ::signal(SIGHUP, sigHandler); - - int ret = 0; - - do { - g_signal = 0; - g_killed = false; - - Dfsi *dfsi = new Dfsi(g_iniFile); - ret = dfsi->run(); - delete dfsi; - - if (g_signal == 2) - ::LogInfoEx(LOG_HOST, "[STOP] dvmdfsi:main SIGINT"); - - if (g_signal == 15) - ::LogInfoEx(LOG_HOST, "[STOP] dvmdfsi:main SIGTERM"); - - if (g_signal == 1) - ::LogInfoEx(LOG_HOST, "[RSTR] dvmdfsi:main SIGHUP"); - } while (g_signal == 1); - - ::LogFinalise(); - ::ActivityLogFinalise(); - - return ret; -} -#endif \ No newline at end of file diff --git a/src/dfsi/DfsiMain.h b/src/dfsi/DfsiMain.h deleted file mode 100644 index 92e8990f..00000000 --- a/src/dfsi/DfsiMain.h +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * - */ -/** - * @file DfsiMain.h - * @ingroup dfsi - * @file DfsiMain.cpp - * @ingroup dfsi - */ -#if !defined(__DFSI_MAIN_H__) -#define __DFSI_MAIN_H__ - -#include "Defines.h" - -#include - -// --------------------------------------------------------------------------- -// Externs -// --------------------------------------------------------------------------- - -/** @brief */ -extern int g_signal; -/** @brief */ -extern std::string g_progExe; -/** @brief */ -extern std::string g_iniFile; -/** @brief */ -extern std::string g_lockFile; - -/** @brief (Global) Flag indicating foreground operation. */ -extern bool g_foreground; -/** @brief (Global) Flag indicating the FNE should stop immediately. */ -extern bool g_killed; - -/** @brief */ -extern std::string g_masterAddress; -/** @brief */ -extern uint16_t g_masterPort; -/** @brief */ -extern uint32_t g_peerId; - -extern uint8_t* g_gitHashBytes; - -/** - * @brief Helper to trigger a fatal error message. This will cause the program to terminate - * immediately with an error message. - * - * @param msg String format. - * - * This is a variable argument function. - */ -extern HOST_SW_API void fatal(const char* msg, ...); - -#endif // __DFSI_MAIN_H__ \ No newline at end of file diff --git a/src/dfsi/network/CallData.cpp b/src/dfsi/network/CallData.cpp deleted file mode 100644 index bf4111d6..00000000 --- a/src/dfsi/network/CallData.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * - */ -#include "CallData.h" - -using namespace network; -using namespace modem; -using namespace p25; -using namespace p25::defines; -using namespace p25::dfsi::frames; -using namespace dfsi; - -// --------------------------------------------------------------------------- -// Public Class Members -// --------------------------------------------------------------------------- - -/* Initializes a new instance of the VoiceCallData class. */ - -VoiceCallData::VoiceCallData() : - srcId(0U), - dstId(0U), - lco(0U), - mfId(MFG_STANDARD), - serviceOptions(0U), - lsd1(0U), - lsd2(0U), - mi(), - algoId(ALGO_UNENCRYPT), - kId(0U), - VHDR1(), - VHDR2(), - netLDU1(), - netLDU2(), - seqNo(0U), - n(0U), - streamId(0U) -{ - mi = new uint8_t[MI_LENGTH_BYTES]; - VHDR1 = new uint8_t[MotVoiceHeader1::HCW_LENGTH]; - VHDR2 = new uint8_t[MotVoiceHeader2::HCW_LENGTH]; - netLDU1 = new uint8_t[9U * 25U]; - netLDU2 = new uint8_t[9U * 25U]; - - ::memset(netLDU1, 0x00U, 9U * 25U); - ::memset(netLDU2, 0x00U, 9U * 25U); -} - -/* Finalizes a instance of the VoiceCallData class. */ - -VoiceCallData::~VoiceCallData() -{ - delete[] mi; - delete[] VHDR1; - delete[] VHDR2; - delete[] netLDU1; - delete[] netLDU2; -} - -/* Reset call data to defaults. */ - -void VoiceCallData::resetCallData() -{ - srcId = 0U; - dstId = 0U; - lco = 0U; - mfId = MFG_STANDARD; - serviceOptions = 0U; - lsd1 = 0U; - lsd2 = 0U; - - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - algoId = ALGO_UNENCRYPT; - kId = 0U; - - ::memset(VHDR1, 0x00U, MotVoiceHeader1::HCW_LENGTH); - ::memset(VHDR2, 0x00U, MotVoiceHeader2::HCW_LENGTH); - - ::memset(netLDU1, 0x00U, 9U * 25U); - ::memset(netLDU2, 0x00U, 9U * 25U); - - n = 0U; - seqNo = 0U; - streamId = 0U; -} - -/* Generate a new stream ID for a call. */ - -void VoiceCallData::newStreamId() -{ - std::uniform_int_distribution dist(DVM_RAND_MIN, DVM_RAND_MAX); - streamId = dist(random); -} \ No newline at end of file diff --git a/src/dfsi/network/CallData.h b/src/dfsi/network/CallData.h deleted file mode 100644 index 443ad858..00000000 --- a/src/dfsi/network/CallData.h +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * - */ -/** - * @file CallData.h - * @ingroup dfsi_network - * @file CallData.cpp - * @ingroup dfsi_network - */ -#if !defined(__DFSI_CALL_DATA_H__) -#define __DFSI_CALL_DATA_H__ - -#include "Defines.h" -#include "common/edac/RS634717.h" -#include "common/network/RawFrameQueue.h" -#include "common/p25/data/LowSpeedData.h" -#include "common/p25/P25Defines.h" -#include "common/p25/dfsi/DFSIDefines.h" -#include "common/p25/dfsi/LC.h" -#include "common/p25/dfsi/frames/Frames.h" -#include "common/Log.h" -#include "common/Utils.h" -#include "common/yaml/Yaml.h" -#include "common/RingBuffer.h" -#include "network/DfsiPeerNetwork.h" -#include "host/modem/Modem.h" -#include "host/modem/port/IModemPort.h" -#include "host/modem/port/UARTPort.h" - -#include - -namespace network -{ - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief Represents an on-going call. - * @ingroup dfsi_network - */ - class HOST_SW_API VoiceCallData { - public: - /** - * @brief Initializes a new instance of the VoiceCallData class. - */ - VoiceCallData(); - /** - * @brief Initializes a new instance of the VoiceCallData class. - */ - ~VoiceCallData(); - - /** - * @brief Reset call data to defaults. - */ - void resetCallData(); - /** - * @brief Generate a new stream ID for a call. - */ - void newStreamId(); - - /** @name Call Data */ - /** - * @brief Source Radio ID. - */ - uint32_t srcId; - /** - * @brief Destination ID. - */ - uint32_t dstId; - - /** - * @brief Link Control Opcode. - */ - uint8_t lco; - /** - * @brief Manufacturer ID. - */ - uint8_t mfId; - /** - * @brief Call Service Options. - */ - uint8_t serviceOptions; - - /** - * @brief Low Speed Data 1. - */ - uint8_t lsd1; - /** - * @brief Low Speed Data 2. - */ - uint8_t lsd2; - - /** - * @brief Encryption Message Indicator. - */ - uint8_t* mi; - /** - * @brief Encryption Algorithm ID. - */ - uint8_t algoId; - /** - * @brief Encryption Key ID. - */ - uint32_t kId; - - /** - * @brief Voice Header 1. - */ - uint8_t* VHDR1; - /** - * @brief Voice Header 2. - */ - uint8_t* VHDR2; - - /** - * @brief FNE Network LDU1 Buffer. - */ - uint8_t* netLDU1; - /** - * @brief FNE Network LDU2 Buffer. - */ - uint8_t* netLDU2; - - /** - * @brief Sequence Number. - */ - uint32_t seqNo; - /** - * @brief - */ - uint8_t n; - - /** - * @brief Stream ID. - */ - uint32_t streamId; - /** @} */ - - private: - // Used for stream ID generation - std::mt19937 random; - - }; -} - -#endif // __DFSI_CALL_DATA_H__ \ No newline at end of file diff --git a/src/dfsi/network/DfsiPeerNetwork.cpp b/src/dfsi/network/DfsiPeerNetwork.cpp deleted file mode 100644 index 2332622f..00000000 --- a/src/dfsi/network/DfsiPeerNetwork.cpp +++ /dev/null @@ -1,296 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL - * - */ -#include "dfsi/Defines.h" -#include "common/network/json/json.h" -#include "common/p25/dfsi/DFSIDefines.h" -#include "common/p25/dfsi/LC.h" -#include "common/Utils.h" -#include "network/DfsiPeerNetwork.h" - -using namespace network; - -#include - -// --------------------------------------------------------------------------- -// Public Class Members -// --------------------------------------------------------------------------- - -/* Initializes a new instance of the PeerNetwork class. */ - -DfsiPeerNetwork::DfsiPeerNetwork(const std::string& address, uint16_t port, uint16_t localPort, uint32_t peerId, const std::string& password, - bool duplex, bool debug, bool dmr, bool p25, bool nxdn, bool slot1, bool slot2, bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup) : - Network(address, port, localPort, peerId, password, duplex, debug, dmr, p25, nxdn, slot1, slot2, allowActivityTransfer, allowDiagnosticTransfer, updateLookup, saveLookup) -{ - assert(!address.empty()); - assert(port > 0U); - assert(!password.empty()); -} - -/* Writes P25 LDU1 frame data to the network. */ - -bool DfsiPeerNetwork::writeP25LDU1(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data, P25DEF::FrameType::E frameType) -{ - if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING) - return false; - - bool resetSeq = false; - if (m_p25StreamId == 0U) { - resetSeq = true; - m_p25StreamId = createStreamId(); - } - - uint32_t messageLength = 0U; - UInt8Array message = createP25_LDU1Message_Raw(messageLength, control, lsd, data, frameType); - if (message == nullptr) { - return false; - } - - return writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq(resetSeq), m_p25StreamId); -} - -/* Writes P25 LDU2 frame data to the network. */ - -bool DfsiPeerNetwork::writeP25LDU2(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data) -{ - if (m_status != NET_STAT_RUNNING && m_status != NET_STAT_MST_RUNNING) - return false; - - bool resetSeq = false; - if (m_p25StreamId == 0U) { - resetSeq = true; - m_p25StreamId = createStreamId(); - } - - uint32_t messageLength = 0U; - UInt8Array message = createP25_LDU2Message_Raw(messageLength, control, lsd, data); - if (message == nullptr) { - return false; - } - - return writeMaster({ NET_FUNC::PROTOCOL, NET_SUBFUNC::PROTOCOL_SUBFUNC_P25 }, message.get(), messageLength, pktSeq(resetSeq), m_p25StreamId); -} - -// --------------------------------------------------------------------------- -// Protected Class Members -// --------------------------------------------------------------------------- - -/* Writes configuration to the network. */ - -bool DfsiPeerNetwork::writeConfig() -{ - if (m_loginStreamId == 0U) { - LogWarning(LOG_NET, "BUGBUG: tried to write network authorisation with no stream ID?"); - return false; - } - - const char* software = __NETVER__; - - json::object config = json::object(); - - // identity and frequency - config["identity"].set(m_identity); // Identity - config["rxFrequency"].set(m_rxFrequency); // Rx Frequency - config["txFrequency"].set(m_txFrequency); // Tx Frequency - - // system info - json::object sysInfo = json::object(); - sysInfo["latitude"].set(m_latitude); // Latitude - sysInfo["longitude"].set(m_longitude); // Longitude - - sysInfo["height"].set(m_height); // Height - sysInfo["location"].set(m_location); // Location - config["info"].set(sysInfo); - - // channel data - json::object channel = json::object(); - channel["txPower"].set(m_power); // Tx Power - channel["txOffsetMhz"].set(m_txOffsetMhz); // Tx Offset (Mhz) - channel["chBandwidthKhz"].set(m_chBandwidthKhz); // Ch. Bandwidth (khz) - channel["channelId"].set(m_channelId); // Channel ID - channel["channelNo"].set(m_channelNo); // Channel No - config["channel"].set(channel); - - // RCON - json::object rcon = json::object(); - rcon["password"].set(m_restApiPassword); // REST API Password - rcon["port"].set(m_restApiPort); // REST API Port - config["rcon"].set(rcon); - - config["software"].set(std::string(software)); // Software ID - - json::value v = json::value(config); - std::string json = v.serialize(); - - char buffer[json.length() + 8U]; - - ::memcpy(buffer + 0U, TAG_REPEATER_CONFIG, 4U); - ::sprintf(buffer + 8U, "%s", json.c_str()); - - if (m_debug) { - Utils::dump(1U, "Network Message, Configuration", (uint8_t*)buffer, json.length() + 8U); - } - - return writeMaster({ NET_FUNC::RPTC, NET_SUBFUNC::NOP }, (uint8_t*)buffer, json.length() + 8U, pktSeq(), m_loginStreamId); -} - -// --------------------------------------------------------------------------- -// Private Class Members -// --------------------------------------------------------------------------- - -/* Creates an P25 LDU1 frame message. */ - -UInt8Array DfsiPeerNetwork::createP25_LDU1Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, - const uint8_t* data, P25DEF::FrameType::E frameType) -{ - using namespace p25::dfsi::defines; - using namespace p25::defines; - assert(data != nullptr); - - p25::dfsi::LC dfsiLC = p25::dfsi::LC(control, lsd); - - uint8_t* buffer = new uint8_t[P25_LDU1_PACKET_LENGTH + PACKET_PAD]; - ::memset(buffer, 0x00U, P25_LDU1_PACKET_LENGTH + PACKET_PAD); - - // construct P25 message header - createP25_MessageHdr(buffer, DUID::LDU1, control, lsd, frameType); - - // pack DFSI data - uint32_t count = MSG_HDR_SIZE; - uint8_t imbe[RAW_IMBE_LENGTH_BYTES]; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE1); - ::memcpy(imbe, data + 10U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU1(buffer + 24U, imbe); - count += DFSI_LDU1_VOICE1_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE2); - ::memcpy(imbe, data + 26U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU1(buffer + 46U, imbe); - count += DFSI_LDU1_VOICE2_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE3); - ::memcpy(imbe, data + 55U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU1(buffer + 60U, imbe); - count += DFSI_LDU1_VOICE3_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE4); - ::memcpy(imbe, data + 80U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU1(buffer + 77U, imbe); - count += DFSI_LDU1_VOICE4_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE5); - ::memcpy(imbe, data + 105U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU1(buffer + 94U, imbe); - count += DFSI_LDU1_VOICE5_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE6); - ::memcpy(imbe, data + 130U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU1(buffer + 111U, imbe); - count += DFSI_LDU1_VOICE6_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE7); - ::memcpy(imbe, data + 155U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU1(buffer + 128U, imbe); - count += DFSI_LDU1_VOICE7_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE8); - ::memcpy(imbe, data + 180U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU1(buffer + 145U, imbe); - count += DFSI_LDU1_VOICE8_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE9); - ::memcpy(imbe, data + 204U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU1(buffer + 162U, imbe); - count += DFSI_LDU1_VOICE9_FRAME_LENGTH_BYTES; - - buffer[23U] = count; - - if (m_debug) - Utils::dump(1U, "Network Message, P25 LDU1", buffer, (P25_LDU1_PACKET_LENGTH + PACKET_PAD)); - - length = (P25_LDU1_PACKET_LENGTH + PACKET_PAD); - return UInt8Array(buffer); -} - -/* Creates an P25 LDU2 frame message. */ - -UInt8Array DfsiPeerNetwork::createP25_LDU2Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, - const uint8_t* data) -{ - using namespace p25::dfsi::defines; - using namespace p25::defines; - assert(data != nullptr); - - p25::dfsi::LC dfsiLC = p25::dfsi::LC(control, lsd); - - uint8_t* buffer = new uint8_t[P25_LDU2_PACKET_LENGTH + PACKET_PAD]; - ::memset(buffer, 0x00U, P25_LDU2_PACKET_LENGTH + PACKET_PAD); - - // construct P25 message header - createP25_MessageHdr(buffer, DUID::LDU2, control, lsd, FrameType::DATA_UNIT); - - // pack DFSI data - uint32_t count = MSG_HDR_SIZE; - uint8_t imbe[RAW_IMBE_LENGTH_BYTES]; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE10); - ::memcpy(imbe, data + 10U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU2(buffer + 24U, imbe); - count += DFSI_LDU2_VOICE10_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE11); - ::memcpy(imbe, data + 26U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU2(buffer + 46U, imbe); - count += DFSI_LDU2_VOICE11_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE12); - ::memcpy(imbe, data + 55U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU2(buffer + 60U, imbe); - count += DFSI_LDU2_VOICE12_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE13); - ::memcpy(imbe, data + 80U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU2(buffer + 77U, imbe); - count += DFSI_LDU2_VOICE13_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE14); - ::memcpy(imbe, data + 105U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU2(buffer + 94U, imbe); - count += DFSI_LDU2_VOICE14_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE15); - ::memcpy(imbe, data + 130U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU2(buffer + 111U, imbe); - count += DFSI_LDU2_VOICE15_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE16); - ::memcpy(imbe, data + 155U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU2(buffer + 128U, imbe); - count += DFSI_LDU2_VOICE16_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE17); - ::memcpy(imbe, data + 180U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU2(buffer + 145U, imbe); - count += DFSI_LDU2_VOICE17_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE18); - ::memcpy(imbe, data + 204U, RAW_IMBE_LENGTH_BYTES); - dfsiLC.encodeLDU2(buffer + 162U, imbe); - count += DFSI_LDU2_VOICE18_FRAME_LENGTH_BYTES; - - buffer[23U] = count; - - if (m_debug) - Utils::dump(1U, "Network Message, P25 LDU2", buffer, (P25_LDU2_PACKET_LENGTH + PACKET_PAD)); - - length = (P25_LDU2_PACKET_LENGTH + PACKET_PAD); - return UInt8Array(buffer); -} \ No newline at end of file diff --git a/src/dfsi/network/DfsiPeerNetwork.h b/src/dfsi/network/DfsiPeerNetwork.h deleted file mode 100644 index c24160fb..00000000 --- a/src/dfsi/network/DfsiPeerNetwork.h +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL - * - */ -/** - * @file DfsiPeerNetwork.h - * @ingroup dfsi_network - * @file DfsiPeerNetwork.cpp - * @ingroup dfsi_network - */ -#if !defined(__DFSI_PEER_NETWORK_H__) -#define __DFSI_PEER_NETWORK_H__ - -#include "Defines.h" -#include "host/network/Network.h" - -#include -#include - -namespace network -{ - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief Implements the core peer networking logic. - * @ingroup dfsi_network - */ - class HOST_SW_API DfsiPeerNetwork : public Network { - public: - /** - * @brief Initializes a new instance of the PeerNetwork class. - * @param address Network Hostname/IP address to connect to. - * @param port Network port number. - * @param local - * @param peerId Unique ID on the network. - * @param password Network authentication password. - * @param duplex Flag indicating full-duplex operation. - * @param debug Flag indicating whether network debug is enabled. - * @param dmr Flag indicating whether DMR is enabled. - * @param p25 Flag indicating whether P25 is enabled. - * @param nxdn Flag indicating whether NXDN is enabled. - * @param slot1 Flag indicating whether DMR slot 1 is enabled for network traffic. - * @param slot2 Flag indicating whether DMR slot 2 is enabled for network traffic. - * @param allowActivityTransfer Flag indicating that the system activity logs will be sent to the network. - * @param allowDiagnosticTransfer Flag indicating that the system diagnostic logs will be sent to the network. - * @param updateLookup Flag indicating that the system will accept radio ID and talkgroup ID lookups from the network. - */ - DfsiPeerNetwork(const std::string& address, uint16_t port, uint16_t localPort, uint32_t peerId, const std::string& password, - bool duplex, bool debug, bool dmr, bool p25, bool nxdn, bool slot1, bool slot2, bool allowActivityTransfer, bool allowDiagnosticTransfer, bool updateLookup, bool saveLookup); - - /** - * @brief Writes P25 LDU1 frame data to the network. - * @param[in] control Instance of p25::lc::LC containing link control data. - * @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data. - * @param[in] data Buffer containing P25 LDU1 data to send. - * @param[in] frameType DVM P25 frame type. - * @returns bool True, if message was sent, otherwise false. - */ - bool writeP25LDU1(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data, - P25DEF::FrameType::E frameType) override; - /** - * @brief Writes P25 LDU2 frame data to the network. - * @param[in] control Instance of p25::lc::LC containing link control data. - * @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data. - * @param[in] data Buffer containing P25 LDU2 data to send. - * @returns bool True, if message was sent, otherwise false. - */ - bool writeP25LDU2(const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, const uint8_t* data) override; - - protected: - /** - * @brief Writes configuration to the network. - * @returns bool True, if configuration was sent, otherwise false. - */ - bool writeConfig() override; - - private: - /** - * @brief Creates an P25 LDU1 frame message. - * - * The data packed into a P25 LDU1 frame message is near standard DFSI messaging, just instead of - * 9 individual frames, they are packed into a single message one right after another. - * - * @param[out] length Length of network message buffer. - * @param[in] control Instance of p25::lc::LC containing link control data. - * @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data. - * @param[in] data Buffer containing P25 LDU1 data to send. - * @param[in] frameType DVM P25 frame type. - * @returns UInt8Array Buffer containing the built network message. - */ - UInt8Array createP25_LDU1Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, - const uint8_t* data, P25DEF::FrameType::E frameType); - /** - * @brief Creates an P25 LDU2 frame message. - * - * The data packed into a P25 LDU2 frame message is near standard DFSI messaging, just instead of - * 9 individual frames, they are packed into a single message one right after another. - * - * @param[out] length Length of network message buffer. - * @param[in] control Instance of p25::lc::LC containing link control data. - * @param[in] lsd Instance of p25::data::LowSpeedData containing low speed data. - * @param[in] data Buffer containing P25 LDU2 data to send. - * @returns UInt8Array Buffer containing the built network message. - */ - UInt8Array createP25_LDU2Message_Raw(uint32_t& length, const p25::lc::LC& control, const p25::data::LowSpeedData& lsd, - const uint8_t* data); - }; -} // namespace network - -#endif // __DFSI_PEER_NETWORK_H__ \ No newline at end of file diff --git a/src/dfsi/network/SerialService.cpp b/src/dfsi/network/SerialService.cpp deleted file mode 100644 index cc1bba28..00000000 --- a/src/dfsi/network/SerialService.cpp +++ /dev/null @@ -1,2039 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * Copyright (C) 2024 Bryan Biedenkapp, N2PLL - * - */ -#include "common/p25/lc/tdulc/TDULCFactory.h" -#include "network/SerialService.h" -#include "dfsi/ActivityLog.h" - -using namespace network; -using namespace modem; -using namespace p25; -using namespace p25::defines; -using namespace p25::dfsi::defines; -using namespace p25::dfsi::frames; -using namespace dfsi; - -// --------------------------------------------------------------------------- -// Public Class Members -// --------------------------------------------------------------------------- - -/* Initializes a new instance of the SerialService class. */ - -SerialService::SerialService(std::string& portType, 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), - m_diu(diu), - m_port(), - m_jitter(jitter), - m_debug(debug), - m_trace(trace), - m_network(network), - m_lastIMBE(nullptr), - m_lastHeard(), - m_sequences(), - m_msgBuffer(nullptr), - m_msgState(RESP_START), - m_msgLength(0U), - m_msgOffset(0U), - m_msgType(CMD_GET_STATUS), - m_msgDoubleLength(false), - m_netFrames(0U), - m_netLost(0U), - m_rxP25Queue(p25RxQueueSize, "RX P25 Queue"), - m_txP25Queue(p25TxQueueSize, "TX P25 Queue"), - m_lastP25Tx(0U), - m_rs(), - m_rxP25LDUCounter(0U), - m_netCallInProgress(false), - m_lclCallInProgress(false), - m_callTimeout(callTimeout), - m_rxVoiceControl(nullptr), - m_rxVoiceLsd(nullptr), - m_rxVoiceCallData(nullptr), - m_rxLastLDU1(nullptr) -{ - assert(!portName.empty()); - assert(baudrate > 0U); - - // Setup serial - port::SERIAL_SPEED serialSpeed = port::SERIAL_115200; - - std::transform(portType.begin(), portType.end(), portType.begin(), ::tolower); - if (portType == NULL_PORT) { - m_port = new port::ModemNullPort(); - } - else { - m_port = new port::UARTPort(portName, serialSpeed, false); - } - - m_lastIMBE = new uint8_t[11U]; - ::memcpy(m_lastIMBE, NULL_IMBE, 11U); - - m_msgBuffer = new uint8_t[BUFFER_LENGTH]; -} - -/* Finalizes a instance of the SerialService class. */ - -SerialService::~SerialService() -{ - if (m_port != nullptr) { - delete m_port; - } - - // Delete our buffers - delete[] m_lastIMBE; - delete[] m_msgBuffer; - - // Delete our rx P25 objects - delete m_rxVoiceControl; - delete m_rxVoiceLsd; - delete m_rxVoiceCallData; - delete m_rxLastLDU1; -} - -/* Updates the timer by the passed number of milliseconds. */ - -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(); - - // Handle what we got - if (type == RTM_TIMEOUT) { - // Do nothing - } - else if (type == RTM_ERROR) { - // Also do nothing - } - else { - // Get cmd byte offset - uint8_t cmdOffset = 2U; - if (m_msgDoubleLength) - cmdOffset = 3U; - - // Get command type - switch (m_msgBuffer[2U]) { - - // P25 data is handled identically to the dvmhost modem - case CMD_P25_DATA: - { - // Get length - uint8_t length[2U]; - if (m_msgLength > 255U) - length[0U] = ((m_msgLength - cmdOffset) >> 8U) & 0xFFU; - else - length[0U] = 0x00U; - length[1U] = (m_msgLength - cmdOffset) & 0xFFU; - m_rxP25Queue.addData(length, 2U); - - // Add data tag to queue - uint8_t data = TAG_DATA; - m_rxP25Queue.addData(&data, 1U); - - // Add P25 data to buffer - m_rxP25Queue.addData(m_msgBuffer + (cmdOffset + 1U), m_msgLength - (cmdOffset + 1U)); - - if (m_debug && m_trace) { - LogDebug(LOG_SERIAL, "Got P25 data from V24 board (len: %u)", m_msgLength); - } - } - break; - - // P25 data lost is also handled, though the V24 board doesn't implement it (yet?) - case CMD_P25_LOST: - { - if (m_debug) - LogDebug(LOG_SERIAL, "Got P25 lost msg from V24 board"); - - if (m_msgDoubleLength) { - LogError(LOG_SERIAL, "CMD_P25_LOST got double length byte?"); - break; - } - - uint8_t data = 1U; - m_rxP25Queue.addData(&data, 1U); - - data = TAG_LOST; - m_rxP25Queue.addData(&data, 1U); - } - break; - - // Get version string - case CMD_GET_VERSION: - { - // Print proto info - LogMessage(LOG_DVMV24, "V24 board protocol: %02X, CPU: %02X", m_msgBuffer[3U], m_msgBuffer[4U]); - // Print CPU info - switch (m_msgBuffer[4U]) - { - case 2U: - LogMessage(LOG_DVMV24, "ST-Micro ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_msgBuffer[5U], m_msgBuffer[6U], m_msgBuffer[7U], m_msgBuffer[8U], m_msgBuffer[9U], m_msgBuffer[10U], m_msgBuffer[11U], m_msgBuffer[12U], m_msgBuffer[13U], m_msgBuffer[14U], m_msgBuffer[15U], m_msgBuffer[16U]); - break; - default: - LogWarning(LOG_DVMV24, "Unknown CPU type!"); - break; - } - } - break; - - // Handle debug messages - case CMD_DEBUG1: - case CMD_DEBUG2: - case CMD_DEBUG3: - case CMD_DEBUG4: - case CMD_DEBUG5: - case CMD_DEBUG_DUMP: - printDebug(m_msgBuffer, m_msgLength); - break; - - // Fallback if we get a message we have no clue how to handle - default: - { - LogError(LOG_SERIAL, "Unhandled command from V24 board: %02X", m_msgBuffer[2U]); - } - } - } - - // Write anything waiting to the serial port - int out = writeSerial(); - if (m_trace && out > 0) { - LogDebug(LOG_SERIAL, "Wrote %u-byte message to the serial V24 device", out); - } 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)) { - // Clear the flag - m_lclCallInProgress = false; - // Reset the call data - rxResetCallData(); - // Debug print - 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"); - } -} - -/* Opens connection to the serial interface. */ - -bool SerialService::open() -{ - LogInfoEx(LOG_SERIAL, "Opening port %s at %u baud", m_portName.c_str(), m_baudrate); - - bool ret = m_port->open(); - - if (!ret) { - LogError(LOG_SERIAL, "Failed to open port!"); - return false; - } - - m_msgState = RESP_START; - - return true; -} - -/* Closes connection to the serial interface. */ - -void SerialService::close() -{ - LogInfoEx(LOG_SERIAL, "Closing port"); - m_port->close(); -} - -/* Process P25 data from the peer network and send to writeP25Frame() */ - -void SerialService::processP25FromNet(UInt8Array p25Buffer, uint32_t length) -{ - // If there's a local call in progress, ignore the frames - if (m_lclCallInProgress) { - LogWarning(LOG_SERIAL, "Local call in progress, ignoring frames from network"); - 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; - - // We don't use these (yet?) - //bool grantDenial = (p25Buffer[14U] & 0x40U) == 0x40U; - //bool unitToUnit = (p25Buffer[14U] & 0x01U) == 0x01U; - - // Decode network header - DUID::E duid = (DUID::E)p25Buffer[22U]; - uint8_t mfid = p25Buffer[15U]; - - // Setup P25 data handlers - UInt8Array data; - uint8_t frameLength = p25Buffer[23U]; - - // Handle PDUs - if (duid == DUID::PDU) { - frameLength = length; - data = std::unique_ptr(new uint8_t[length]); - ::memset(data.get(), 0x00U, length); - ::memcpy(data.get(), p25Buffer.get(), length); - LogInfoEx(LOG_SERIAL, "Got P25 PDU, we don't handle these (yet)"); - } - // Handle everything else - else { - if (frameLength <= 24) { - data = std::unique_ptr(new uint8_t[frameLength]); - ::memset(data.get(), 0x00U, frameLength); - } - else { - data = std::unique_ptr(new uint8_t[frameLength]); - ::memset(data.get(), 0x00U, frameLength); - ::memcpy(data.get(), p25Buffer.get() + 24U, frameLength); - } - } - - // Get basic info - uint8_t lco = p25Buffer[4U]; - uint32_t srcId = __GET_UINT16(p25Buffer, 5U); - uint32_t dstId = __GET_UINT16(p25Buffer, 8U); - uint32_t sysId = (p25Buffer[11U] << 8) | (p25Buffer[12U] << 0); - uint32_t netId = __GET_UINT16(p25Buffer, 16U); - uint8_t lsd1 = p25Buffer[20U]; - uint8_t lsd2 = p25Buffer[21U]; - FrameType::E frameType = FrameType::DATA_UNIT; - - // Default any 0's - if (netId == 0U) { - netId = lc::LC::getSiteData().netId(); - } - - if (sysId == 0U) { - sysId = lc::LC::getSiteData().sysId(); - } - - if (m_debug) { - LogDebug(LOG_NET, "P25, duid = $%02X, lco = $%02X, MFId = $%02X, srcId = %u, dstId = %u, len = %u", duid, lco, mfid, srcId, dstId, length); - } - - lc::LC control; - data::LowSpeedData lsd; - - // is this a LDU1, is this the first of a call? - if (duid == DUID::LDU1) { - frameType = (FrameType::E)p25Buffer[180U]; - - if (m_debug) { - LogDebug(LOG_NET, "P25, frameType = $%02X", frameType); - } - - if (frameType == FrameType::HDU_VALID) { - uint8_t algId = p25Buffer[181U]; - uint32_t kid = (p25Buffer[182U] << 8) | (p25Buffer[183U] << 0); - - // copy MI data - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - - for (uint8_t i = 0; i < MI_LENGTH_BYTES; i++) { - mi[i] = p25Buffer[184U + i]; - } - - if (m_debug) { - LogDebug(LOG_NET, "P25, HDU algId = $%02X, kId = $%04X", algId, kid); - } - /*if (m_trace) { - Utils::dump(1U, "P25 HDU Network MI", mi, p25::P25_MI_LENGTH_BYTES); - }*/ - - control.setAlgId(algId); - control.setKId(kid); - control.setMI(mi); - } - } - - control.setLCO(lco); - control.setSrcId(srcId); - control.setDstId(dstId); - control.setMFId(mfid); - - control.setNetId(netId); - control.setSysId(sysId); - - lsd.setLSD1(lsd1); - lsd.setLSD2(lsd2); - - /*if (m_trace) { - Utils::dump(2U, "!!! *P25 Network Frame", data.get(), frameLength); - }*/ - - uint8_t* message = data.get(); - - //Utils::dump(2U, "!!! *P25 Network Frame", message, frameLength); - - // forward onto the specific processor for final processing and delivery - switch (duid) { - case DUID::LDU1: - { - if ((message[0U] == DFSIFrameType::LDU1_VOICE1) && (message[22U] == DFSIFrameType::LDU1_VOICE2) && - (message[36U] == DFSIFrameType::LDU1_VOICE3) && (message[53U] == DFSIFrameType::LDU1_VOICE4) && - (message[70U] == DFSIFrameType::LDU1_VOICE5) && (message[87U] == DFSIFrameType::LDU1_VOICE6) && - (message[104U] == DFSIFrameType::LDU1_VOICE7) && (message[121U] == DFSIFrameType::LDU1_VOICE8) && - (message[138U] == DFSIFrameType::LDU1_VOICE9)) { - uint32_t count = 0U; - dfsi::LC dfsiLC = dfsi::LC(control, lsd); - - uint8_t netLDU1[9U * 25U]; - ::memset(netLDU1, 0x00U, 9U * 25U); - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE1); - dfsiLC.decodeLDU1(message + count, netLDU1 + 10U); - count += DFSI_LDU1_VOICE1_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE2); - dfsiLC.decodeLDU1(message + count, netLDU1 + 26U); - count += DFSI_LDU1_VOICE2_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE3); - dfsiLC.decodeLDU1(message + count, netLDU1 + 55U); - count += DFSI_LDU1_VOICE3_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE4); - dfsiLC.decodeLDU1(message + count, netLDU1 + 80U); - count += DFSI_LDU1_VOICE4_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE5); - dfsiLC.decodeLDU1(message + count, netLDU1 + 105U); - count += DFSI_LDU1_VOICE5_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE6); - dfsiLC.decodeLDU1(message + count, netLDU1 + 130U); - count += DFSI_LDU1_VOICE6_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE7); - dfsiLC.decodeLDU1(message + count, netLDU1 + 155U); - count += DFSI_LDU1_VOICE7_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE8); - dfsiLC.decodeLDU1(message + count, netLDU1 + 180U); - count += DFSI_LDU1_VOICE8_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU1_VOICE9); - dfsiLC.decodeLDU1(message + count, netLDU1 + 204U); - count += DFSI_LDU1_VOICE9_FRAME_LENGTH_BYTES; - - control = lc::LC(*dfsiLC.control()); - - // Override the source/destination from the FNE RTP header (handles rewrites properly) - control.setSrcId(srcId); - control.setDstId(dstId); - - LogInfoEx(LOG_NET, P25_LDU1_STR " audio, mfId = $%02X, srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u", - control.getMFId(), control.getSrcId(), control.getDstId(), control.getGroup(), control.getEmergency(), control.getEncrypted(), control.getPriority()); - - //Utils::dump("P25 LDU1 from net", netLDU1, 9U * 25U); - - writeP25Frame(duid, dfsiLC, netLDU1); - } - } - break; - case DUID::LDU2: - { - if ((message[0U] == DFSIFrameType::LDU2_VOICE10) && (message[22U] == DFSIFrameType::LDU2_VOICE11) && - (message[36U] == DFSIFrameType::LDU2_VOICE12) && (message[53U] == DFSIFrameType::LDU2_VOICE13) && - (message[70U] == DFSIFrameType::LDU2_VOICE14) && (message[87U] == DFSIFrameType::LDU2_VOICE15) && - (message[104U] == DFSIFrameType::LDU2_VOICE16) && (message[121U] == DFSIFrameType::LDU2_VOICE17) && - (message[138U] == DFSIFrameType::LDU2_VOICE18)) { - uint32_t count = 0U; - dfsi::LC dfsiLC = dfsi::LC(control, lsd); - - uint8_t netLDU2[9U * 25U]; - ::memset(netLDU2, 0x00U, 9U * 25U); - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE10); - dfsiLC.decodeLDU2(message + count, netLDU2 + 10U); - count += DFSI_LDU2_VOICE10_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE11); - dfsiLC.decodeLDU2(message + count, netLDU2 + 26U); - count += DFSI_LDU2_VOICE11_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE12); - dfsiLC.decodeLDU2(message + count, netLDU2 + 55U); - count += DFSI_LDU2_VOICE12_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE13); - dfsiLC.decodeLDU2(message + count, netLDU2 + 80U); - count += DFSI_LDU2_VOICE13_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE14); - dfsiLC.decodeLDU2(message + count, netLDU2 + 105U); - count += DFSI_LDU2_VOICE14_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE15); - dfsiLC.decodeLDU2(message + count, netLDU2 + 130U); - count += DFSI_LDU2_VOICE15_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE16); - dfsiLC.decodeLDU2(message + count, netLDU2 + 155U); - count += DFSI_LDU2_VOICE16_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE17); - dfsiLC.decodeLDU2(message + count, netLDU2 + 180U); - count += DFSI_LDU2_VOICE17_FRAME_LENGTH_BYTES; - - dfsiLC.setFrameType(DFSIFrameType::LDU2_VOICE18); - dfsiLC.decodeLDU2(message + count, netLDU2 + 204U); - count += 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()); - - //Utils::dump("P25 LDU2 from net", netLDU2, 9U * 25U); - - writeP25Frame(duid, dfsiLC, netLDU2); - } - } - break; - - case DUID::TSDU: - //processTSDU(data.get(), frameLength, control, lsd); // We don't handle TSDUs right now - break; - - case DUID::TDU: - case DUID::TDULC: - { - if (duid == DUID::TDULC) { - std::unique_ptr tdulc = lc::tdulc::TDULCFactory::createTDULC(data.get()); - if (tdulc == nullptr) { - LogWarning(LOG_NET, P25_TDULC_STR ", undecodable TDULC"); - } - else { - if (tdulc->getLCO() != LCO::CALL_TERM) - break; - } - } - - // is this an TDU with a grant demand? - if (duid == DUID::TDU && grantDemand) { - break; // ignore grant demands - } - - // Log print - LogInfoEx(LOG_NET, P25_TDU_STR ", srcId = %u, dstId = %u", srcId, dstId); - - // End the call - endOfStream(); - - // Update our sequence number - m_sequences[dstId] = RTP_END_OF_CALL_SEQ; - } - break; - - default: - break; - } -} - -/* Retrieve and process a P25 frame from the rx P25 queue */ - -void SerialService::processP25ToNet() -{ - - // Buffer to store the retrieved P25 frame - uint8_t data[P25_PDU_FRAME_LENGTH_BYTES * 2U]; - - // Get a P25 frame from the RX queue - uint32_t len = readP25Frame(data); - - // If we didn't read anything, return - if (len <= 0U) { - return; - } - - // If there's already a call from the network in progress, ignore any additional frames comfing from the V24 interface - if (m_netCallInProgress) { - LogWarning(LOG_SERIAL, "Remote call in progress, ignoring frames from V24"); - return; - } - - //LogDebug(LOG_SERIAL, "processP25ToNet() got data (len: %u)", len); - - /*if (m_trace) { - Utils::dump(1U, "data: ", data, len); - }*/ - - // Create a new link control object if needed - if (m_rxVoiceControl == nullptr) { - m_rxVoiceControl = new lc::LC(); - } - - // Create a new lsd object if needed - if (m_rxVoiceLsd == nullptr) { - m_rxVoiceLsd = new data::LowSpeedData(); - } - - // Create a new call data object if needed - if (m_rxVoiceCallData == nullptr) { - m_rxVoiceCallData = new VoiceCallData(); - } - - // Create a new last LDU1 data object if needed - if (m_rxLastLDU1 == nullptr) { - m_rxLastLDU1 = new p25::lc::LC(); - } - - // Parse out the data - uint8_t tag = data[0U]; - - // Sanity check - if (tag != TAG_DATA) { - LogError(LOG_SERIAL, "Unexpected data tag in RX P25 frame buffer: 0x%02X", tag); - return; - } - - // Get the DFSI data (skip the 0x00 padded byte at the start) - uint8_t dfsiData[len - 2]; - ::memset(dfsiData, 0x00U, len - 2); - ::memcpy(dfsiData, data + 2U, len - 2); - - // Extract DFSI frame type - DFSIFrameType::E frameType = (DFSIFrameType::E)dfsiData[0U]; - - //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 - case DFSIFrameType::MOT_START_STOP: - { - // Decode the frame - MotStartOfStream start = MotStartOfStream(dfsiData); - // Handle start/stop - if (start.getStartStop() == StartStopFlag::START) { - // Flag we have a local call (i.e. from V24) in progress - m_lclCallInProgress = true; - // Reset the call data - rxResetCallData(); - // Generate a new random stream ID - m_rxVoiceCallData->newStreamId(); - // Log - LogInfoEx(LOG_SERIAL, "V24 CALL START [STREAM ID %u]", m_rxVoiceCallData->streamId); - } else { - if (m_lclCallInProgress) { - // Flag call over - m_lclCallInProgress = false; - // Log - LogInfoEx(LOG_SERIAL, "V24 CALL END"); - // Send the TDU (using call data which we hope has been filled earlier) - m_network->writeP25TDU(*m_rxVoiceControl, *m_rxVoiceLsd); - // Reset - rxResetCallData(); - } - } - } - break; - // VHDR 1 Frame - case DFSIFrameType::MOT_VHDR_1: - { - // Decode - MotVoiceHeader1 vhdr1 = MotVoiceHeader1(dfsiData); - - // Copy to call data VHDR1 - m_rxVoiceCallData->VHDR1 = new uint8_t[vhdr1.HCW_LENGTH]; - ::memcpy(m_rxVoiceCallData->VHDR1, vhdr1.header, vhdr1.HCW_LENGTH); - // Debug Log - if (m_debug) { - LogDebug(LOG_SERIAL, "V24 VHDR1 [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - } - break; - // VHDR 2 Frame - case DFSIFrameType::MOT_VHDR_2: - { - // Decode - MotVoiceHeader2 vhdr2 = MotVoiceHeader2(dfsiData); - // Copy to call data - ::memcpy(m_rxVoiceCallData->VHDR2, vhdr2.header, vhdr2.HCW_LENGTH); - // Debug Log - if (m_debug) { - LogDebug(LOG_SERIAL, "V24 VHDR2 [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - - // Buffer for raw VHDR data - uint8_t raw[DFSI_VHDR_RAW_LEN]; - // Get VHDR1 data - ::memcpy(raw, m_rxVoiceCallData->VHDR1, 8U); - ::memcpy(raw + 8U, m_rxVoiceCallData->VHDR1 + 9U, 8U); - ::memcpy(raw + 16U, m_rxVoiceCallData->VHDR1 + 18U, 2U); - // Get VHDR2 data - ::memcpy(raw + 18U, m_rxVoiceCallData->VHDR2, 8U); - ::memcpy(raw + 26U, m_rxVoiceCallData->VHDR2 + 9U, 8U); - ::memcpy(raw + 34U, m_rxVoiceCallData->VHDR2 + 18U, 2U); - - // Buffer for decoded VHDR data - uint8_t vhdr[DFSI_VHDR_LEN]; - - // Copy over the data, decoding hex with the weird bit stuffing nonsense - uint offset = 0U; - for (uint32_t i = 0; i < DFSI_VHDR_RAW_LEN; i++, offset += 6) - Utils::hex2Bin(raw[i], vhdr, offset); - - // Try to decode the RS data - try { - bool ret = m_rs.decode362017(vhdr); - if (!ret) { - LogError(LOG_SERIAL, "V24 traffic failed to decode RS (36,20,17) FEC [STREAM ID %u]", m_rxVoiceCallData->streamId); - } else { - // Start the call, if it's not been started already - if (!m_lclCallInProgress) - { - // Flag we have a local call (i.e. from V24) in progress - m_lclCallInProgress = true; - // Reset the call data - rxResetCallData(); - // Generate a new random stream ID - m_rxVoiceCallData->newStreamId(); - // Log - LogInfoEx(LOG_SERIAL, "V24 LATE CALL START (VHDR) [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - // Copy Message Indicator - ::memcpy(m_rxVoiceCallData->mi, vhdr, MI_LENGTH_BYTES); - // Get additional info - m_rxVoiceCallData->mfId = vhdr[9U]; - m_rxVoiceCallData->algoId = vhdr[10U]; - m_rxVoiceCallData->kId = __GET_UINT16B(vhdr, 11U); - m_rxVoiceCallData->dstId = __GET_UINT16B(vhdr, 13U); - // Update our last LDU dst ID - m_rxLastLDU1->setDstId(m_rxVoiceCallData->dstId); - // Log if we decoded succesfully - if (m_debug) { - LogDebug(LOG_SERIAL, "P25, HDU algId = $%02X, kId = $%04X, dstId = $%04X", m_rxVoiceCallData->algoId, m_rxVoiceCallData->kId, m_rxVoiceCallData->dstId); - } - } - } - catch (...) { - LogError(LOG_SERIAL, "V24 traffic got exception while trying to decode RS data for VHDR [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - } - break; - // VOICE1/10 create a start voice frame - case DFSIFrameType::LDU1_VOICE1: - { - // Decode - MotStartVoiceFrame svf = MotStartVoiceFrame(dfsiData); - // Copy - ::memcpy(m_rxVoiceCallData->netLDU1 + 10U, svf.fullRateVoice->imbeData, RAW_IMBE_LENGTH_BYTES); - // Increment our voice frame counter - m_rxVoiceCallData->n++; - } - break; - case DFSIFrameType::LDU2_VOICE10: - { - // Decode - MotStartVoiceFrame svf = MotStartVoiceFrame(dfsiData); - // Copy - ::memcpy(m_rxVoiceCallData->netLDU2 + 10U, svf.fullRateVoice->imbeData, RAW_IMBE_LENGTH_BYTES); - // Increment our voice frame counter - m_rxVoiceCallData->n++; - } - break; - // The remaining LDUs all create full rate voice frames so we do that here - default: - { - // Decode - MotFullRateVoice voice = MotFullRateVoice(dfsiData); - // Copy based on frame type - switch (frameType) { - // VOICE2 - case DFSIFrameType::LDU1_VOICE2: - { - ::memcpy(m_rxVoiceCallData->netLDU1 + 26U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - } - break; - // VOICE3 - case DFSIFrameType::LDU1_VOICE3: - { - ::memcpy(m_rxVoiceCallData->netLDU1 + 55U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - if (voice.additionalData != nullptr) { - m_rxVoiceCallData->lco = voice.additionalData[0U]; - m_rxVoiceCallData->mfId = voice.additionalData[1U]; - m_rxVoiceCallData->serviceOptions = voice.additionalData[2U]; - } else { - LogWarning(LOG_SERIAL, "V24 VC3 traffic missing metadata [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - } - break; - // VOICE4 - case DFSIFrameType::LDU1_VOICE4: - { - ::memcpy(m_rxVoiceCallData->netLDU1 + 80U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - if (voice.additionalData != nullptr) { - m_rxVoiceCallData->dstId = __GET_UINT16(voice.additionalData, 0U); - } else { - LogWarning(LOG_SERIAL, "V24 VC4 traffic missing metadata [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - } - break; - // VOICE5 - case DFSIFrameType::LDU1_VOICE5: - { - ::memcpy(m_rxVoiceCallData->netLDU1 + 105U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - if (voice.additionalData != nullptr) { - m_rxVoiceCallData->srcId = __GET_UINT16(voice.additionalData, 0U); - } else { - LogWarning(LOG_SERIAL, "V24 VC5 traffic missing metadata [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - } - break; - // VOICE6 - case DFSIFrameType::LDU1_VOICE6: - { - ::memcpy(m_rxVoiceCallData->netLDU1 + 130U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - } - break; - // VOICE7 - case DFSIFrameType::LDU1_VOICE7: - { - ::memcpy(m_rxVoiceCallData->netLDU1 + 155U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - } - break; - // VOICE8 - case DFSIFrameType::LDU1_VOICE8: - { - ::memcpy(m_rxVoiceCallData->netLDU1 + 180U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - } - break; - // VOICE9 - case DFSIFrameType::LDU1_VOICE9: - { - ::memcpy(m_rxVoiceCallData->netLDU1 + 204U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - if (voice.additionalData != nullptr) { - m_rxVoiceCallData->lsd1 = voice.additionalData[0U]; - m_rxVoiceCallData->lsd2 = voice.additionalData[1U]; - } else { - LogWarning(LOG_SERIAL, "V24 VC9 traffic missing metadata [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - } - break; - // VOICE11 - case DFSIFrameType::LDU2_VOICE11: - { - ::memcpy(m_rxVoiceCallData->netLDU2 + 26U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - } - break; - // VOICE12 - case DFSIFrameType::LDU2_VOICE12: - { - ::memcpy(m_rxVoiceCallData->netLDU2 + 55U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - if (voice.additionalData != nullptr) { - ::memcpy(m_rxVoiceCallData->mi, voice.additionalData, 3U); - } else { - LogWarning(LOG_SERIAL, "V24 VC12 traffic missing metadata [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - } - break; - // VOICE13 - case DFSIFrameType::LDU2_VOICE13: - { - ::memcpy(m_rxVoiceCallData->netLDU2 + 80U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - if (voice.additionalData != nullptr) { - ::memcpy(m_rxVoiceCallData->mi + 3U, voice.additionalData, 3U); - } else { - LogWarning(LOG_SERIAL, "V24 VC13 traffic missing metadata [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - } - break; - // VOICE14 - case DFSIFrameType::LDU2_VOICE14: - { - ::memcpy(m_rxVoiceCallData->netLDU2 + 105U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - if (voice.additionalData != nullptr) { - ::memcpy(m_rxVoiceCallData->mi + 6U, voice.additionalData, 3U); - } else { - LogWarning(LOG_SERIAL, "V24 VC14 traffic missing metadata [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - } - break; - // VOICE15 - case DFSIFrameType::LDU2_VOICE15: - { - ::memcpy(m_rxVoiceCallData->netLDU2 + 130U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - if (voice.additionalData != nullptr) { - m_rxVoiceCallData->algoId = voice.additionalData[0U]; - m_rxVoiceCallData->kId = __GET_UINT16B(voice.additionalData, 1U); - } else { - LogWarning(LOG_SERIAL, "V24 VC15 traffic missing metadata [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - } - break; - // VOICE16 - case DFSIFrameType::LDU2_VOICE16: - { - ::memcpy(m_rxVoiceCallData->netLDU2 + 155U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - } - break; - // VOICE17 - case DFSIFrameType::LDU2_VOICE17: - { - ::memcpy(m_rxVoiceCallData->netLDU2 + 180U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - } - break; - // VOICE18 - case DFSIFrameType::LDU2_VOICE18: - { - ::memcpy(m_rxVoiceCallData->netLDU2 + 204U, voice.imbeData, RAW_IMBE_LENGTH_BYTES); - if (voice.additionalData != nullptr) { - m_rxVoiceCallData->lsd1 = voice.additionalData[0U]; - m_rxVoiceCallData->lsd2 = voice.additionalData[1U]; - } else { - LogWarning(LOG_SERIAL, "V24 VC18 traffic missing metadata [STREAM ID %u]", m_rxVoiceCallData->streamId); - } - } - break; - - default: - break; - } - - // Increment our voice frame counter - m_rxVoiceCallData->n++; - } - break; - } - - // Get LC & LSD data if we're ready for either LDU1 or LDU2 (don't do this every frame to be more efficient) - - if (m_rxVoiceCallData->n == 9U || m_rxVoiceCallData->n == 18U) { - m_rxVoiceControl->setLCO(m_rxVoiceCallData->lco); - m_rxVoiceControl->setMFId(m_rxVoiceCallData->mfId); - - // Update src/dst as long as this is a standard MFID LDU1 - if (m_rxVoiceControl->isStandardMFId()) { - m_rxVoiceControl->setSrcId(m_rxVoiceCallData->srcId); - m_rxVoiceControl->setDstId(m_rxVoiceCallData->dstId); - } - - // Check if we need to overwrite the src ID due to a nonstandard/corrupt/invalid LDU1 - if (m_rxLastLDU1->getSrcId() != 0U) { - if (m_rxVoiceCallData->srcId != m_rxLastLDU1->getSrcId()) { - LogWarning(LOG_SERIAL, P25_LDU1_STR ", srcId = %u doesn't match last LDU1 srcId = %u, fixing", m_rxVoiceCallData->srcId, m_rxLastLDU1->getSrcId()); - m_rxVoiceControl->setSrcId(m_rxLastLDU1->getSrcId()); - } - } - - // Do the same for dst ID - if (m_rxLastLDU1->getDstId() != 0U) { - if (m_rxVoiceCallData->dstId != m_rxLastLDU1->getDstId()) { - LogWarning(LOG_SERIAL, P25_LDU1_STR ", dstId = %u doesn't match last LDU1 dstId = %u, fixing", m_rxVoiceCallData->dstId, m_rxLastLDU1->getDstId()); - m_rxVoiceControl->setDstId(m_rxLastLDU1->getDstId()); - } - } - - // Get service options - bool emergency = ((m_rxVoiceCallData->serviceOptions & 0xFFU) & 0x80U) == 0x80U; // Emergency Flag - bool encryption = ((m_rxVoiceCallData->serviceOptions & 0xFFU) & 0x40U) == 0x40U; // Encryption Flag - uint8_t priority = ((m_rxVoiceCallData->serviceOptions & 0xFFU) & 0x07U); // Priority - m_rxVoiceControl->setEmergency(emergency); - m_rxVoiceControl->setEncrypted(encryption); - m_rxVoiceControl->setPriority(priority); - - // Get more data - m_rxVoiceControl->setMI(m_rxVoiceCallData->mi); - m_rxVoiceControl->setAlgId(m_rxVoiceCallData->algoId); - m_rxVoiceControl->setKId(m_rxVoiceCallData->kId); - - // Get LSD - m_rxVoiceLsd->setLSD1(m_rxVoiceCallData->lsd1); - m_rxVoiceLsd->setLSD2(m_rxVoiceCallData->lsd2); - - // is this an LDU1 frame? - if (m_rxVoiceCallData->n == 9U) { - // is it a non-standard MFId for the LC? - if (!m_rxVoiceControl->isStandardMFId()) { - uint8_t rsBuffer[P25_LDU_LC_FEC_LENGTH_BYTES]; - ::memset(rsBuffer, 0x00U, P25_LDU_LC_FEC_LENGTH_BYTES); - - rsBuffer[0U] = m_rxVoiceCallData->lco; - rsBuffer[1U] = m_rxVoiceCallData->mfId; - rsBuffer[2U] = m_rxVoiceCallData->serviceOptions; - rsBuffer[3U] = (m_rxVoiceCallData->dstId >> 16) & 0xFFU; - rsBuffer[4U] = (m_rxVoiceCallData->dstId >> 8) & 0xFFU; - rsBuffer[5U] = (m_rxVoiceCallData->dstId >> 0) & 0xFFU; - rsBuffer[6U] = (m_rxVoiceCallData->srcId >> 16) & 0xFFU; - rsBuffer[7U] = (m_rxVoiceCallData->srcId >> 8) & 0xFFU; - rsBuffer[8U] = (m_rxVoiceCallData->srcId >> 0) & 0xFFU; - - m_rs.decode241213(rsBuffer); - - ulong64_t rsValue = 0U; - - // combine bytes into ulong64_t (8 byte) value - rsValue = rsBuffer[1U]; - rsValue = (rsValue << 8) + rsBuffer[2U]; - rsValue = (rsValue << 8) + rsBuffer[3U]; - rsValue = (rsValue << 8) + rsBuffer[4U]; - rsValue = (rsValue << 8) + rsBuffer[5U]; - rsValue = (rsValue << 8) + rsBuffer[6U]; - rsValue = (rsValue << 8) + rsBuffer[7U]; - rsValue = (rsValue << 8) + rsBuffer[8U]; - m_rxVoiceControl->setRS(rsValue); - - // Optional LC dump - if (m_debug) - LogDebug(LOG_SERIAL, "Got non-standard LC: MFID = $%02X, LC = $%llX", m_rxVoiceControl->getMFId(), rsValue); - } - } - } - - // Send LDU1 if ready - if (m_rxVoiceCallData->n == 9U) { - // Send (TODO: dynamically set HDU_VALID or DATA_VALID depending on start of call or not) - bool ret = m_network->writeP25LDU1(*m_rxVoiceControl, *m_rxVoiceLsd, m_rxVoiceCallData->netLDU1, FrameType::HDU_VALID); - // Update last - m_rxLastLDU1->setSrcId(m_rxVoiceControl->getSrcId()); - m_rxLastLDU1->setDstId(m_rxVoiceControl->getDstId()); - // Print - LogInfoEx(LOG_SERIAL, P25_LDU1_STR " audio, mfId = $%02X, srcId = %u, dstId = %u, group = %u, emerg = %u, encrypt = %u, prio = %u, lsd = $%02X%02X", - m_rxVoiceControl->getMFId(), m_rxVoiceControl->getSrcId(), m_rxVoiceControl->getDstId(), m_rxVoiceControl->getGroup(), - m_rxVoiceControl->getEmergency(), m_rxVoiceControl->getEncrypted(), m_rxVoiceControl->getPriority(), m_rxVoiceLsd->getLSD1(), m_rxVoiceLsd->getLSD2()); - // Optional Debug - if (ret) { - /*if (m_debug) - LogDebug(LOG_SERIAL, "V24 LDU1 [STREAM ID %u, SRC %u, DST %u]", m_rxVoiceCallData->streamId, m_rxVoiceCallData->srcId, m_rxVoiceCallData->dstId); - if (m_trace) - Utils::dump(1U, "LDU1 to net", m_rxVoiceCallData->netLDU1, 9U * 25U);*/ - } - else { - LogError(LOG_SERIAL, "V24 LDU1 failed to write to network"); - } - } - - // Send LDU2 if ready - if (m_rxVoiceCallData->n == 18U) { - // Send - bool ret = m_network->writeP25LDU2(*m_rxVoiceControl, *m_rxVoiceLsd, m_rxVoiceCallData->netLDU2); - // Print - LogInfoEx(LOG_SERIAL, P25_LDU2_STR " audio, algo = $%02X, kid = $%04X, lsd = $%02X%02X", m_rxVoiceCallData->algoId, m_rxVoiceCallData->kId, m_rxVoiceLsd->getLSD1(), m_rxVoiceLsd->getLSD2()); - // Optional Debug - if (ret) { - /*if (m_debug) - LogDebug(LOG_SERIAL, "V24 LDU2 [STREAM ID %u, SRC %u, DST %u]", m_rxVoiceCallData->streamId, m_rxVoiceCallData->srcId, m_rxVoiceCallData->dstId); - if (m_trace) - Utils::dump(1U, "LDU2 to net", m_rxVoiceCallData->netLDU2, 9U * 25U);*/ - } - else { - LogError(LOG_SERIAL, "V24 LDU2 failed to write to network"); - } - // Reset counter since we've sent both frames - m_rxVoiceCallData->n = 0; - } -} - -// --------------------------------------------------------------------------- -// Private Class Members -// --------------------------------------------------------------------------- - -/* Read a data message from the serial port */ - -RESP_TYPE_DVM SerialService::readSerial() -{ - // Flag for a 16-bit (i.e. 2-byte) length - m_msgDoubleLength = false; - - // If we're waiting for a message start byte, read a single byte - if (m_msgState == RESP_START) { - // Try and read a byte from the serial port - int ret = m_port->read(m_msgBuffer + 0U, 1U); - - // Catch read error - if (ret < 0) { - LogError(LOG_SERIAL, "Error reading from serial port, ret = %d", ret); - m_msgState = RESP_START; - return RTM_ERROR; - } - - // Handle no data - if (ret == 0) { - return RTM_TIMEOUT; - } - - // Handle anything other than a start flag while in RESP_START mode - if (m_msgBuffer[0U] != DVM_SHORT_FRAME_START && - m_msgBuffer[0U] != DVM_LONG_FRAME_START) { - ::memset(m_msgBuffer, 0x00U, BUFFER_LENGTH); - return RTM_ERROR; - } - - // Detect short vs long frame - if (m_msgBuffer[0U] == DVM_LONG_FRAME_START) { - m_msgDoubleLength = true; - } - - // Advance state machine - m_msgState = RESP_LENGTH1; - } - - // Check length byte (1/2) - if (m_msgState == RESP_LENGTH1) { - // Read length - int ret = m_port->read(m_msgBuffer + 1U, 1U); - - // Catch read error - if (ret < 0) { - LogError(LOG_SERIAL, "Error reading from serial port, ret = %d", ret); - m_msgState = RESP_START; - return RTM_ERROR; - } - - // Handle no data - if (ret == 0) { - return RTM_TIMEOUT; - } - - // Handle invalid length - if (m_msgBuffer[1U] >= 250U && !m_msgDoubleLength) { - LogError(LOG_SERIAL, "Invalid length received from the modem, len = %u", m_msgBuffer[1U]); - return RTM_ERROR; - } - - // Handle double-byte length - if (m_msgDoubleLength) { - m_msgState = RESP_LENGTH2; - m_msgLength = ( (m_msgBuffer[1U] & 0xFFU) << 8 ); - } else { - // Advnace to type byte if single-byte length - m_msgState = RESP_TYPE; - m_msgLength = m_msgBuffer[1U]; - } - - // Set proper data offset - m_msgOffset = 2U; - } - - // Check length byte (2/2) - if (m_msgState == RESP_LENGTH2) { - // Read - int ret = m_port->read(m_msgBuffer + 2U, 1U); - - // Catch read error - if (ret < 0) { - LogError(LOG_SERIAL, "Error reading from serial port, ret = %d", ret); - m_msgState = RESP_START; - return RTM_ERROR; - } - - // Handle no data - if (ret == 0) { - return RTM_TIMEOUT; - } - - // Calculate total length - m_msgLength = (m_msgLength + (m_msgBuffer[2U] & 0xFFU) ); - - // Advance state machine - m_msgState = RESP_TYPE; - - // Set flag - m_msgDoubleLength = true; - - // Set proper data offset - m_msgOffset = 3U; - } - - if (m_msgState == RESP_TYPE) { - // Read type - int ret = m_port->read(m_msgBuffer + m_msgOffset, 1U); - - // Catch read error - if (ret < 0) { - LogError(LOG_SERIAL, "Error reading from serial port, ret = %d", ret); - m_msgState = RESP_START; - return RTM_ERROR; - } - - // Handle no data - if (ret == 0) { - return RTM_TIMEOUT; - } - - // Get command type - m_msgType = (DVM_COMMANDS)m_msgBuffer[m_msgOffset]; - - // Move on - m_msgState = RESP_DATA; - m_msgOffset++; - } - - // Get the data - if (m_msgState == RESP_DATA) { - if (m_trace) { - LogDebug(LOG_SERIAL, "readSerial(), RESP_DATA, len = %u, offset = %u, type = %02X", m_msgLength, m_msgOffset, m_msgType); - } - - // Get the data based on the earlier length - while (m_msgOffset < m_msgLength) { - int ret = m_port->read(m_msgBuffer + m_msgOffset, m_msgLength - m_msgOffset); - - if (ret < 0) { - LogError(LOG_SERIAL, "Error reading from serial port, ret = %d", ret); - m_msgState = RESP_START; - return RTM_ERROR; - } - - if (ret == 0) - return RTM_TIMEOUT; - - if (ret > 0) - m_msgOffset += ret; - } - - if (m_debug && m_trace) - Utils::dump(1U, "Serial RX Data", m_msgBuffer, m_msgLength); - } - - m_msgState = RESP_START; - m_msgOffset = 0U; - - return RTM_OK; -} - -/* Called from clock thread, checks for an available P25 frame to write and sends it based on jitter timing requirements */ - -int SerialService::writeSerial() -{ - /** - * Serial TX ringbuffer format: - * - * | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 | 0x08 | 0x09 | 0x0A | 0x0B | 0x0C | ... | - * | Length | Tag | int64_t timestamp in ms | data | - */ - - // Check empty - if (m_txP25Queue.isEmpty()) - return 0U; - - // Get length - uint8_t length[2U]; - ::memset(length, 0x00U, 2U); - m_txP25Queue.peek(length, 2U); - - // Convert length byets to int - uint16_t len = 0U; - len = (length[0U] << 8) + length[1U]; - - // This ensures we never get in a situation where we have length & type bytes stuck in the queue by themselves - if (m_txP25Queue.dataSize() == 2U && len > m_txP25Queue.dataSize()) { - m_txP25Queue.get(length, 2U); // ensure we pop bytes off - return 0U; - } - - // Get current timestamp - int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - - // Peek the timestamp to see if we should wait - if (m_txP25Queue.dataSize() >= 11U) { - // Peek everything up to the timestamp - uint8_t lengthTagTs[11U]; - ::memset(lengthTagTs, 0x00U, 11U); - m_txP25Queue.peek(lengthTagTs, 11U); - // Get the timestamp - int64_t ts; - assert(sizeof ts == 8); - ::memcpy(&ts, lengthTagTs + 3U, 8U); - // If it's not time to send, return - if (ts > now) { - return 0U; - } - } - - // Check if we have enough data to get everything - len + 2U (length bytes) + 1U (tag) + 8U (timestamp) - if (m_txP25Queue.dataSize() >= len + 11U) { - // Get the length, tag and timestamp - uint8_t lengthTagTs[11U]; - m_txP25Queue.get(lengthTagTs, 11U); - - // Get the actual data - uint8_t buffer[len]; - m_txP25Queue.get(buffer, len); - - // Sanity check on data tag - uint8_t tag = lengthTagTs[2U]; - if (tag != TAG_DATA) { - LogError(LOG_SERIAL, "Got unexpected data tag from TX P25 ringbuffer! %02X", tag); - return 0U; - } - - // We already checked the timestamp above, so we just get the data and write it - return m_port->write(buffer, len); - } - - return 0U; -} - -/* Gets a frame of P25 data from the RX queue */ - -uint32_t SerialService::readP25Frame(uint8_t* data) -{ - - /** - * Serial RX ringbuffer format: - * - * | 0x01 | 0x02 | 0x03 | 0x04 | ... | - * | Length | Tag | data | - */ - - // sanity check - assert(data != nullptr); - - // Check empty - if (m_rxP25Queue.isEmpty()) - return 0U; - - // Get length bytes - uint8_t length[2U]; - ::memset(length, 0x00U, 2U); - m_rxP25Queue.peek(length, 2U); - - // Convert length bytes to a length int - uint16_t len = 0U; - len = (length[0] << 8) + length[1U]; - - // this ensures we never get in a situation where we have length stuck on the queue - if (m_rxP25Queue.dataSize() == 2U && len > m_rxP25Queue.dataSize()) { - m_rxP25Queue.get(length, 2U); // ensure we pop the length off - return 0U; - } - - if (m_rxP25Queue.dataSize() >= len) { - m_rxP25Queue.get(length, 2U); // ensure we pop the length off - m_rxP25Queue.get(data, len); - return len; - } - - return 0U; -} - -/* Break apart a P25 LDU and add to the TX queue, timed appropriately */ - -void SerialService::writeP25Frame(DUID::E duid, dfsi::LC& lc, uint8_t* ldu) -{ - // Sanity check - assert(ldu != nullptr); - - // Get now - int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - - // Break out the control components - lc::LC control = lc::LC(*lc.control()); - data::LowSpeedData lsd = data::LowSpeedData(*lc.lsd()); - - // Get the service options - uint8_t serviceOptions = - (control.getEmergency() ? 0x80U : 0x00U) + - (control.getEncrypted() ? 0x40U : 0x00U) + - (control.getPriority() & 0x07U); - - // Get the MI - uint8_t mi[MI_LENGTH_BYTES]; - control.getMI(mi); - - // Calculate reed-solomon encoding depending on DUID type - uint8_t rs[P25_LDU_LC_FEC_LENGTH_BYTES]; - ::memset(rs, 0x00U, P25_LDU_LC_FEC_LENGTH_BYTES); - switch(duid) { - case DUID::LDU1: - { - if (control.isStandardMFId()) { - rs[0U] = control.getLCO(); // LCO - rs[1U] = control.getMFId(); // MFId - rs[2U] = serviceOptions; // Service Options - uint32_t dstId = control.getDstId(); - rs[3U] = (dstId >> 16) & 0xFFU; // Target Address - rs[4U] = (dstId >> 8) & 0xFFU; - rs[5U] = (dstId >> 0) & 0xFFU; - uint32_t srcId = control.getSrcId(); - rs[6U] = (srcId >> 16) & 0xFFU; // Source Address - rs[7U] = (srcId >> 8) & 0xFFU; - rs[8U] = (srcId >> 0) & 0xFFU; - } else { - rs[0U] = control.getLCO(); // LCO - - // split ulong64_t (8 byte) value into bytes - rs[1U] = (uint8_t)((control.getRS() >> 56) & 0xFFU); - rs[2U] = (uint8_t)((control.getRS() >> 48) & 0xFFU); - rs[3U] = (uint8_t)((control.getRS() >> 40) & 0xFFU); - rs[4U] = (uint8_t)((control.getRS() >> 32) & 0xFFU); - rs[5U] = (uint8_t)((control.getRS() >> 24) & 0xFFU); - rs[6U] = (uint8_t)((control.getRS() >> 16) & 0xFFU); - rs[7U] = (uint8_t)((control.getRS() >> 8) & 0xFFU); - rs[8U] = (uint8_t)((control.getRS() >> 0) & 0xFFU); - } - - // encode RS (24,12,13) FEC - m_rs.encode241213(rs); - } - break; - case DUID::LDU2: - { - for (uint32_t i = 0; i < MI_LENGTH_BYTES; i++) - rs[i] = mi[i]; // Message Indicator - - rs[9U] = control.getAlgId(); // Algorithm ID - rs[10U] = (control.getKId() >> 8) & 0xFFU; // Key ID - rs[11U] = (control.getKId() >> 0) & 0xFFU; // ... - - // encode RS (24,16,9) FEC - m_rs.encode24169(rs); - } - break; - - default: - break; - } - - // 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 - { - auto entry = m_lastHeard.find(control.getDstId()); - if (entry != m_lastHeard.end()) { - lastHeard = m_lastHeard[control.getDstId()]; - } - } - - // Get the last sequence number - { - auto entry = m_sequences.find(control.getDstId()); - if (entry != m_sequences.end()) { - sequence = m_sequences[control.getDstId()]; - } - } - - // Check if we need to start a new data stream - if (duid == DUID::LDU1 && ((sequence == 0U) || (sequence == RTP_END_OF_CALL_SEQ))) { - // Start the new stream - startOfStream(lc); - // Update our call entry - m_sequences[control.getDstId()] = ++sequence; - - // Log - LogInfoEx(LOG_NET, "CALL START: %svoice call from %u to TG %u", (control.getAlgId() != ALGO_UNENCRYPT) ? "encrypted " : "", control.getSrcId(), control.getDstId()); - - ActivityLog("network %svoice transmission call from %u to TG %u", (control.getAlgId() != ALGO_UNENCRYPT) ? "encrypted " : "", control.getSrcId(), control.getDstId()); - } else { - // If this TGID isn't either lookup, consider it a late entry and start a new stream - if ( (m_sequences.find(control.getDstId()) == m_sequences.end()) || (m_lastHeard.find(control.getDstId()) == m_lastHeard.end()) ) { - // Start the stream, same as above - startOfStream(lc); - m_lastHeard[control.getDstId()] = now; - m_sequences[control.getDstId()] = ++sequence; - - LogInfoEx(LOG_NET, "LATE CALL START: %svoice call from %u to TG %u", (control.getAlgId() != ALGO_UNENCRYPT) ? "encrypted " : "", control.getSrcId(), control.getDstId()); - ActivityLog("network %svoice transmission late entry from %u to TG %u", (control.getAlgId() != ALGO_UNENCRYPT) ? "encrypted " : "", control.getSrcId(), control.getDstId()); - } - } - - // Check if we need to end the call - if ( (duid == DUID::TDU) || (duid == DUID::TDULC) ) { - // Stop - endOfStream(); - // Log - LogInfoEx(LOG_NET, "CALL END: %svoice call from %u to TG %u", (control.getAlgId() != ALGO_UNENCRYPT) ? "encrypted " : "", control.getSrcId(), control.getDstId()); - // Clear our counters - 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++) { - - // We use this buffer and fullrate voice object to slim down the code to a common sendFrame routine at the bottom - uint8_t* buffer = nullptr; - uint16_t bufferSize = 0; - MotFullRateVoice voice = MotFullRateVoice(); - - switch (n) { - case 0: // VOICE1/10 - { - // Set frametype - voice.setFrameType((duid == DUID::LDU1) ? DFSIFrameType::LDU1_VOICE1 : DFSIFrameType::LDU2_VOICE10); - - // Create the new frame objects - MotStartVoiceFrame svf = MotStartVoiceFrame(); - - // Set values appropriately - svf.startOfStream->setStartStop(StartStopFlag::START); - svf.startOfStream->setRT(m_rtrt ? RTFlag::ENABLED : RTFlag::DISABLED); - - // Set frame type - svf.fullRateVoice->setFrameType(voice.getFrameType()); - - // Set source flag & ICW flag - svf.fullRateVoice->setSource(m_diu ? SourceFlag::DIU : SourceFlag::QUANTAR); - svf.setICW(m_diu ? ICWFlag::DIU : ICWFlag::QUANTAR); - - // Copy data - ::memcpy(svf.fullRateVoice->imbeData, ldu + 10U, RAW_IMBE_LENGTH_BYTES); - - // Encode - buffer = new uint8_t[svf.LENGTH]; - ::memset(buffer, 0x00U, svf.LENGTH); - svf.encode(buffer); - bufferSize = svf.LENGTH; - } - break; - case 1: // VOICE2/11 - { - voice.setFrameType((duid == DUID::LDU1) ? DFSIFrameType::LDU1_VOICE2 : DFSIFrameType::LDU2_VOICE11); - // Set source flag - voice.setSource(m_diu ? SourceFlag::DIU : SourceFlag::QUANTAR); - ::memcpy(voice.imbeData, ldu + 26U, RAW_IMBE_LENGTH_BYTES); - } - break; - case 2: // VOICE3/12 - { - voice.setFrameType((duid == DUID::LDU1) ? DFSIFrameType::LDU1_VOICE3 : DFSIFrameType::LDU2_VOICE12); - ::memcpy(voice.imbeData, ldu + 55U, RAW_IMBE_LENGTH_BYTES); - - // Create the additional data array - voice.additionalData = new uint8_t[voice.ADDITIONAL_LENGTH]; - ::memset(voice.additionalData, 0x00U, voice.ADDITIONAL_LENGTH); - - // Copy additional data - if (voice.getFrameType() == DFSIFrameType::LDU1_VOICE3) { - voice.additionalData[0U] = control.getLCO(); // LCO - voice.additionalData[1U] = rs[1U]; // MFId - voice.additionalData[2U] = rs[2U]; // Service Options - } else { - voice.additionalData[0U] = mi[0U]; // MI - voice.additionalData[1U] = mi[1U]; - voice.additionalData[2U] = mi[2U]; - } - } - break; - case 3: // VOICE4/13 - { - voice.setFrameType((duid == DUID::LDU1) ? DFSIFrameType::LDU1_VOICE4 : DFSIFrameType::LDU2_VOICE13); - ::memcpy(voice.imbeData, ldu + 80U, RAW_IMBE_LENGTH_BYTES); - - // Create the additional data array - voice.additionalData = new uint8_t[voice.ADDITIONAL_LENGTH]; - ::memset(voice.additionalData, 0x00U, voice.ADDITIONAL_LENGTH); - - // We set the additional data based on LDU1/2 - switch (duid) { - case DUID::LDU1: - { - voice.additionalData[0U] = rs[3U]; // Destination ID - voice.additionalData[1U] = rs[4U]; - voice.additionalData[2U] = rs[5U]; - } - break; - case DUID::LDU2: - { - voice.additionalData[0U] = mi[3U]; // MI - voice.additionalData[1U] = mi[4U]; - voice.additionalData[2U] = mi[5U]; - } - break; - default: - break; - } - } - break; - case 4: // VOICE5/14 - { - voice.setFrameType((duid == DUID::LDU1) ? DFSIFrameType::LDU1_VOICE5 : DFSIFrameType::LDU2_VOICE14); - ::memcpy(voice.imbeData, ldu + 105U, RAW_IMBE_LENGTH_BYTES); - // Create the additional data array - voice.additionalData = new uint8_t[voice.ADDITIONAL_LENGTH]; - ::memset(voice.additionalData, 0x00U, voice.ADDITIONAL_LENGTH); - // Same as case 3 above - switch (duid) { - case DUID::LDU1: - { - voice.additionalData[0U] = rs[6U]; // Source ID - voice.additionalData[1U] = rs[7U]; - voice.additionalData[2U] = rs[8U]; - } - break; - case DUID::LDU2: - { - voice.additionalData[0U] = mi[6U]; // MI - voice.additionalData[1U] = mi[7U]; - voice.additionalData[2U] = mi[8U]; - } - break; - default: - break; - } - } - break; - case 5: // VOICE6/15 - { - voice.setFrameType((duid == DUID::LDU1) ? DFSIFrameType::LDU1_VOICE6 : DFSIFrameType::LDU2_VOICE15); - ::memcpy(voice.imbeData, ldu + 130U, RAW_IMBE_LENGTH_BYTES); - // Create the additional data array - voice.additionalData = new uint8_t[voice.ADDITIONAL_LENGTH]; - ::memset(voice.additionalData, 0x00U, voice.ADDITIONAL_LENGTH); - // Another switch on LDU1/2 - switch (duid) { - case DUID::LDU1: - { - // RS encoding - voice.additionalData[0U] = rs[9U]; - voice.additionalData[1U] = rs[10U]; - voice.additionalData[2U] = rs[11U]; - } - break; - case DUID::LDU2: - { - voice.additionalData[0U] = control.getAlgId(); // Algo ID - __SET_UINT16B(control.getKId(), voice.additionalData, 1U); // Key ID - } - break; - default: - break; - } - } - break; - case 6: // VOICE7/16 - { - voice.setFrameType((duid == DUID::LDU1) ? DFSIFrameType::LDU1_VOICE7 : DFSIFrameType::LDU2_VOICE16); - ::memcpy(voice.imbeData, ldu + 155U, RAW_IMBE_LENGTH_BYTES); - // Create the additional data array - voice.additionalData = new uint8_t[voice.ADDITIONAL_LENGTH]; - ::memset(voice.additionalData, 0x00U, voice.ADDITIONAL_LENGTH); - // RS data offsets are the same regardless of LDU - voice.additionalData[0U] = rs[12U]; - voice.additionalData[1U] = rs[13U]; - voice.additionalData[2U] = rs[14U]; - } - break; - case 7: // VOICE8/17 - { - voice.setFrameType((duid == DUID::LDU1) ? DFSIFrameType::LDU1_VOICE8 : DFSIFrameType::LDU2_VOICE17); - ::memcpy(voice.imbeData, ldu + 180U, RAW_IMBE_LENGTH_BYTES); - // Create the additional data array - voice.additionalData = new uint8_t[voice.ADDITIONAL_LENGTH]; - ::memset(voice.additionalData, 0x00U, voice.ADDITIONAL_LENGTH); - // RS data offsets are the same regardless of LDU - voice.additionalData[0U] = rs[15U]; - voice.additionalData[1U] = rs[16U]; - voice.additionalData[2U] = rs[17U]; - } - break; - case 8: // VOICE9/18 - { - voice.setFrameType((duid == DUID::LDU1) ? DFSIFrameType::LDU1_VOICE9 : DFSIFrameType::LDU2_VOICE18); - ::memcpy(voice.imbeData, ldu + 204U, RAW_IMBE_LENGTH_BYTES); - // Create the additional data array - voice.additionalData = new uint8_t[voice.ADDITIONAL_LENGTH]; - ::memset(voice.additionalData, 0x00U, voice.ADDITIONAL_LENGTH); - // Get low speed data bytes - voice.additionalData[0U] = lsd.getLSD1(); - voice.additionalData[1U] = lsd.getLSD2(); - } - break; - } - - // For n=0 (VHDR1/10) case we create the buffer in the switch, for all other frame types we do that here - if (n != 0) { - buffer = new uint8_t[voice.size()]; - ::memset(buffer, 0x00U, voice.size()); - voice.encode(buffer); - bufferSize = voice.size(); - } - - // Debug logging - if (m_trace) { - Utils::dump("Encoded V24 voice frame data", buffer, bufferSize); - } - - // Send if we have data (which we always should) - if (buffer != nullptr) { - addTxToQueue(buffer, bufferSize, SERIAL_TX_TYPE::IMBE); - } - - } -} - -/* Send a start of stream sequence (HDU, etc) to the connected serial V.24 device */ - -void SerialService::startOfStream(const LC& lc) -{ - // Flag that we have a network call in progress - m_netCallInProgress = true; - - // Create new start of stream - MotStartOfStream start = MotStartOfStream(); - start.setStartStop(StartStopFlag::START); - start.setRT(m_rtrt ? RTFlag::ENABLED : RTFlag::DISABLED); - - // Create buffer for bytes and encode - uint8_t buffer[start.LENGTH]; - ::memset(buffer, 0x00U, start.LENGTH); - start.encode(buffer); - - // Optional debug & trace - if (m_debug) - LogDebug(LOG_SERIAL, "encoded mot p25 start frame"); - if (m_trace) - Utils::dump(1U, "data", buffer, start.LENGTH); - - // Send start frame - addTxToQueue(buffer, start.LENGTH, network::SERIAL_TX_TYPE::NONIMBE); - - // Break out the control components - lc::LC control = lc::LC(*lc.control()); - data::LowSpeedData lsd = data::LowSpeedData(*lc.lsd()); - - // Init message indicator & get - uint8_t mi[MI_LENGTH_BYTES]; - ::memset(mi, 0x00U, MI_LENGTH_BYTES); - control.getMI(mi); - - // Init VHDR data array - uint8_t vhdr[DFSI_VHDR_LEN]; - ::memset(vhdr, 0x00U, DFSI_VHDR_LEN); - - // Copy MI to VHDR - ::memcpy(vhdr, mi, MI_LENGTH_BYTES); - - // Set values - vhdr[9U] = control.getMFId(); - vhdr[10U] = control.getAlgId(); - __SET_UINT16B(control.getKId(), vhdr, 11U); - __SET_UINT16B(control.getDstId(), vhdr, 13U); - - // Perform RS encoding - m_rs.encode362017(vhdr); - - // Convert the binary bytes to hex bytes (some kind of bit packing thing I don't understand) - uint8_t raw[DFSI_VHDR_RAW_LEN]; - uint32_t offset = 0; - for (uint8_t i = 0; i < DFSI_VHDR_RAW_LEN; i++, offset += 6) { - raw[i] = Utils::bin2Hex(vhdr, offset); - } - - // Prepare VHDR1 - MotVoiceHeader1 vhdr1 = MotVoiceHeader1(); - vhdr1.startOfStream->setStartStop(StartStopFlag::START); - vhdr1.startOfStream->setRT(m_rtrt ? RTFlag::ENABLED : RTFlag::DISABLED); - vhdr1.setICW(m_diu ? ICWFlag::DIU : ICWFlag::QUANTAR); - ::memcpy(vhdr1.header, raw, 8U); - ::memcpy(vhdr1.header + 9U, raw + 8U, 8U); - ::memcpy(vhdr1.header + 18U, raw + 16U, 2U); - - // Encode VHDR1 and send - uint8_t buffer1[vhdr1.LENGTH]; - ::memset(buffer1, 0x00U, vhdr1.LENGTH); - vhdr1.encode(buffer1); - - if (m_debug) - LogDebug(LOG_SERIAL, "encoded mot VHDR1 p25 frame"); - if (m_trace) - Utils::dump(1U, "data", buffer1, vhdr1.LENGTH); - - addTxToQueue(buffer1, vhdr1.LENGTH, SERIAL_TX_TYPE::NONIMBE); - - // Prepare VHDR2 - MotVoiceHeader2 vhdr2 = MotVoiceHeader2(); - ::memcpy(vhdr2.header, raw + 18U, 8U); - ::memcpy(vhdr2.header + 9U, raw + 26U, 8U); - ::memcpy(vhdr2.header + 18U, raw + 34U, 2U); - - // Encode VHDR2 and send - uint8_t buffer2[vhdr2.LENGTH]; - ::memset(buffer2, 0x00U, vhdr2.LENGTH); - vhdr2.encode(buffer2); - - if (m_debug) - LogDebug(LOG_SERIAL, "encoded mot VHDR2 p25 frame"); - if (m_trace) - Utils::dump(1U, "data", buffer2, vhdr2.LENGTH); - - addTxToQueue(buffer2, vhdr2.LENGTH, SERIAL_TX_TYPE::NONIMBE); -} - -/* Send an end of stream sequence (TDU, etc) to the connected serial V.24 device */ - -void SerialService::endOfStream() -{ - // Create the new end of stream (which looks like a start of stream with the stop flag) - MotStartOfStream end = MotStartOfStream(); - end.setStartStop(StartStopFlag::STOP); - - // Create buffer and encode - uint8_t buffer[end.LENGTH]; - ::memset(buffer, 0x00U, end.LENGTH); - end.encode(buffer); - - if (m_trace) { - LogDebug(LOG_SERIAL, "encoded mot p25 end frame"); - Utils::dump(1U, "data", buffer, end.LENGTH); - } - - // Send start frame - addTxToQueue(buffer, end.LENGTH, SERIAL_TX_TYPE::NONIMBE); - - // Our net call is done - m_netCallInProgress = false; -} - -/* Helper to add a V24 dataframe to the P25 TX queue with the proper timestamp and formatting */ - -void SerialService::addTxToQueue(uint8_t* data, uint16_t len, SERIAL_TX_TYPE msgType) -{ - // If the port isn't connected, just return - if (m_port == nullptr) - return; - - // Get current time in ms - uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - - // Timestamp for this message (in ms) - uint64_t msgTime = 0U; - - // If this is our first message, timestamp is just now + the jitter buffer offset in ms - if (m_lastP25Tx == 0U) { - msgTime = now + m_jitter; - } - // If we had a message before this, calculate the new timestamp dynamically - else { - // If the last message occurred longer than our jitter buffer delay, we restart the sequence and calculate the same as above - if ((int64_t)(now - m_lastP25Tx) > m_jitter) { - msgTime = now + m_jitter; - } - // Otherwise, we time out messages as required by the message type - else { - if (msgType == IMBE) { - // IMBEs must go out at 20ms intervals - msgTime = m_lastP25Tx + 20; - } else { - // Otherwise we don't care, we use 5ms since that's the theoretical minimum time a 9600 baud message can take - msgTime = m_lastP25Tx + 5; - } - } - } - - // Increment the length by 4 for the header bytes - len += 4U; - - // Convert 16-bit length to 2 bytes - uint8_t length[2U]; - if (len > 255U) - length[0U] = (len >> 8U) & 0xFFU; - else - length[0U] = 0x00U; - length[1U] = len & 0xFFU; - - m_txP25Queue.addData(length, 2U); - - // Add the data tag - uint8_t tag = TAG_DATA; - m_txP25Queue.addData(&tag, 1U); - - // Convert 64-bit timestamp to 8 bytes and add - uint8_t tsBytes[8U]; - assert(sizeof msgTime == 8U); - ::memcpy(tsBytes, &msgTime, 8U); - m_txP25Queue.addData(tsBytes, 8U); - - // Add the DVM start byte, length byte, CMD byte, and padding 0 - uint8_t header[4U]; - header[0U] = DVM_SHORT_FRAME_START; - header[1U] = len & 0xFFU; - header[2U] = CMD_P25_DATA; - header[3U] = 0x00U; - m_txP25Queue.addData(header, 4U); - - // Add the data - m_txP25Queue.addData(data, len - 4U); - - //Utils::dump(1U, "SERIAL TX DATA", data, len - 4U); - - // Update the last message time - m_lastP25Tx = msgTime; -} - -/* Helper to insert IMBE silence frames for missing audio. */ - -void SerialService::insertMissingAudio(uint8_t *data, uint32_t& lost) -{ - if (data[10U] == 0x00U) { - ::memcpy(data + 10U, m_lastIMBE, 11U); - lost++; - } - else { - ::memcpy(m_lastIMBE, data + 10U, 11U); - } - - if (data[26U] == 0x00U) { - ::memcpy(data + 26U, m_lastIMBE, 11U); - lost++; - } - else { - ::memcpy(m_lastIMBE, data + 26U, 11U); - } - - if (data[55U] == 0x00U) { - ::memcpy(data + 55U, m_lastIMBE, 11U); - lost++; - } - else { - ::memcpy(m_lastIMBE, data + 55U, 11U); - } - - if (data[80U] == 0x00U) { - ::memcpy(data + 80U, m_lastIMBE, 11U); - lost++; - } - else { - ::memcpy(m_lastIMBE, data + 80U, 11U); - } - - if (data[105U] == 0x00U) { - ::memcpy(data + 105U, m_lastIMBE, 11U); - lost++; - } - else { - ::memcpy(m_lastIMBE, data + 105U, 11U); - } - - if (data[130U] == 0x00U) { - ::memcpy(data + 130U, m_lastIMBE, 11U); - lost++; - } - else { - ::memcpy(m_lastIMBE, data + 130U, 11U); - } - - if (data[155U] == 0x00U) { - ::memcpy(data + 155U, m_lastIMBE, 11U); - lost++; - } - else { - ::memcpy(m_lastIMBE, data + 155U, 11U); - } - - if (data[180U] == 0x00U) { - ::memcpy(data + 180U, m_lastIMBE, 11U); - lost++; - } - else { - ::memcpy(m_lastIMBE, data + 180U, 11U); - } - - if (data[204U] == 0x00U) { - ::memcpy(data + 204U, m_lastIMBE, 11U); - lost++; - } - else { - ::memcpy(m_lastIMBE, data + 204U, 11U); - } -} - -/* Resets the V24 RX call data objects */ - -void SerialService::rxResetCallData() -{ - // Reset the RX call data object - m_rxVoiceCallData->resetCallData(); - // Reset our LDU TGID info - m_rxLastLDU1->setSrcId(0U); - m_rxLastLDU1->setDstId(0U); -} - -/* */ - -void SerialService::getBoardInfo() -{ - uint8_t buffer[3U]; - buffer[0U] = DVM_SHORT_FRAME_START; - buffer[1U] = 3U; - buffer[2U] = CMD_GET_VERSION; - - int ret = m_port->write(buffer, 3U); - - if (ret != 3U) - { - LogError(LOG_SERIAL, "Failed to send version query command to V24 board"); - } -} - -/* */ - -void SerialService::printDebug(const uint8_t* buffer, uint16_t len) -{ - if (m_msgDoubleLength && buffer[3U] == CMD_DEBUG_DUMP) { - uint8_t data[512U]; - ::memset(data, 0x00U, 512U); - ::memcpy(data, buffer, len); - - Utils::dump(1U, "V24 Debug Dump", data, len); - return; - } - else { - if (m_msgDoubleLength) { - LogError(LOG_SERIAL, "Invalid debug data received from the V24 board, len = %u", len); - return; - } - } - - // Handle the individual debug types - if (buffer[2U] == CMD_DEBUG1) { - LogDebug(LOG_DVMV24, "%.*s", len - 3U, buffer + 3U); - } - else if (buffer[2U] == CMD_DEBUG2) { - short val1 = (buffer[len - 2U] << 8) | buffer[len - 1U]; - LogDebug(LOG_DVMV24, "%.*s %X", len - 5U, buffer + 3U, val1); - } - else if (buffer[2U] == CMD_DEBUG3) { - short val1 = (buffer[len - 4U] << 8) | buffer[len - 3U]; - short val2 = (buffer[len - 2U] << 8) | buffer[len - 1U]; - LogDebug(LOG_DVMV24, "%.*s %X %X", len - 7U, buffer + 3U, val1, val2); - } - else if (buffer[2U] == CMD_DEBUG4) { - short val1 = (buffer[len - 6U] << 8) | buffer[len - 5U]; - short val2 = (buffer[len - 4U] << 8) | buffer[len - 3U]; - short val3 = (buffer[len - 2U] << 8) | buffer[len - 1U]; - LogDebug(LOG_DVMV24, "%.*s %X %X %X", len - 9U, buffer + 3U, val1, val2, val3); - } - else if (buffer[2U] == CMD_DEBUG5) { - short val1 = (buffer[len - 8U] << 8) | buffer[len - 7U]; - short val2 = (buffer[len - 6U] << 8) | buffer[len - 5U]; - short val3 = (buffer[len - 4U] << 8) | buffer[len - 3U]; - short val4 = (buffer[len - 2U] << 8) | buffer[len - 1U]; - LogDebug(LOG_DVMV24, "%.*s %X %X %X %X", len - 11U, buffer + 3U, val1, val2, val3, val4); - } - else if (buffer[2U] == CMD_DEBUG_DUMP) { - uint8_t data[255U]; - ::memset(data, 0x00U, 255U); - ::memcpy(data, buffer, len); - Utils::dump(1U, "DVMV24 Debug Dump", data, len); - } -} \ No newline at end of file diff --git a/src/dfsi/network/SerialService.h b/src/dfsi/network/SerialService.h deleted file mode 100644 index b81e842f..00000000 --- a/src/dfsi/network/SerialService.h +++ /dev/null @@ -1,264 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Digital Voice Modem - DFSI V.24/UDP Software - * GPLv2 Open Source. Use is subject to license terms. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * Copyright (C) 2024 Patrick McDonnell, W3AXL - * - */ -/** - * @defgroup dfsi_network DFSI Networking/Serial Communications - * @brief Implementation for the DFSI networking and serial communications. - * @ingroup dfsi - * - * @file SerialService.h - * @ingroup dfsi_network - * @file SerialService.cpp - * @ingroup dfsi_network - */ -#if !defined(__SERIAL_SERVICE_H__) -#define __SERIAL_SERVICE_H__ - -#include "Defines.h" -#include "common/edac/RS634717.h" -#include "common/network/RawFrameQueue.h" -#include "common/p25/data/LowSpeedData.h" -#include "common/p25/P25Defines.h" -#include "common/p25/dfsi/DFSIDefines.h" -#include "common/p25/dfsi/LC.h" -#include "common/p25/dfsi/frames/Frames.h" -#include "common/Log.h" -#include "common/Utils.h" -#include "common/yaml/Yaml.h" -#include "common/RingBuffer.h" -#include "network/DfsiPeerNetwork.h" -#include "network/CallData.h" -#include "host/modem/Modem.h" -#include "host/modem/port/IModemPort.h" -#include "host/modem/port/UARTPort.h" -#include "host/modem/port/ModemNullPort.h" - -#include -#include -#include -#include -#include -#include - -using namespace modem; -using namespace p25; -using namespace dfsi; - -namespace network -{ - /** - * @brief DFSI serial tx flags used to determine proper jitter handling of data in ringbuffer. - * @ingroup dfsi_network - */ - enum SERIAL_TX_TYPE { - NONIMBE, - IMBE - }; - - // --------------------------------------------------------------------------- - // Class Declaration - // --------------------------------------------------------------------------- - - /** - * @brief Implements the serial V.24 communications service. - * @ingroup dfsi_network - */ - class HOST_SW_API SerialService { - public: - /** - * @brief Initializes an instance of the SerialService class. - * @param portType Serial port type. - * @param portName Serial port device name. - * @param baudrate Baud rate. - * @param rtrt Flag indicating whether or not RT/RT is enabled. - * @param diu Flag indicating whether or not V.24 communications are to a DIU. - * @param jitter - * @param network Instance of the DfsiPeerNetwork class. - * @param p25TxQueueSize Ringbuffer size for the P25 transmit queue. - * @param p25RxQueueSize Ringbuffer size for the P24 receive queue. - * @param callTimeout - * @param debug Flag indicating whether verbose debug logging is enabled. - * @param trace Flag indicating whether verbose trace logging is enabled. - */ - SerialService(std::string& portType, 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); - /** - * @brief Finalizes an instance of the SerialService class. - */ - ~SerialService(); - - /** - * @brief Updates the serial interface by the passed number of milliseconds. - * @param ms Number of milliseconds. - */ - void clock(uint32_t ms); - - /** - * @brief Opens connection to the serial interface. - * @returns bool True, if serial interface has started, otherwise false. - */ - bool open(); - - /** - * @brief Closes connection to the serial interface. - */ - void close(); - - /** - * @brief Process data frames from the network. - * @param p25Buffer Buffer containing the network frame. - * @param length Length of the buffer. - */ - void processP25FromNet(UInt8Array p25Buffer, uint32_t length); - - /** - * @brief Process data frames from the V.24 serial interface. - * - * This function pieces together LDU1/LDU2 messages from individual DFSI frames received over the serial port. It's - * called multiple times before an LDU is sent, and each time adds more data pieces to the LDUs. - */ - void processP25ToNet(); - - /** - * @brief Send the CMD_GET_VERSION to the connected V24 board to get firmware/UDID info - */ - void getBoardInfo(); - - private: - std::string m_portName; - uint32_t m_baudrate; - bool m_rtrt; - bool m_diu; - - port::IModemPort* m_port; - uint16_t m_jitter; - - bool m_debug; - bool m_trace; - - DfsiPeerNetwork* m_network; - - uint8_t* m_lastIMBE; - - // Tables to keep track of calls - std::unordered_map m_lastHeard; - std::unordered_map m_sequences; - - uint8_t* m_msgBuffer; - RESP_STATE m_msgState; - uint16_t m_msgLength; - uint16_t m_msgOffset; - DVM_COMMANDS m_msgType; - bool m_msgDoubleLength; - - uint32_t m_netFrames; - uint32_t m_netLost; - - RingBuffer m_rxP25Queue; - RingBuffer m_txP25Queue; - - // Storage for V24 TX jitter buffer metering - uint64_t m_lastP25Tx; - - edac::RS634717 m_rs; - - // Counter for assembling a full LDU from individual frames in the RX queue - uint8_t m_rxP25LDUCounter; - - // "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; - - // Call Data object for current RX P25 call (VHDR, LDU, etc) - VoiceCallData* m_rxVoiceCallData; - - // The last LDU1 LC sent to the net, used to keep track of current call src/dst IDs, etc - p25::lc::LC* m_rxLastLDU1; - - // Functions called by clock() to read/write from/to the serial port - /** - * @brief Read a data message from the serial interface. - * @return RESP_TYPE_DVM - */ - RESP_TYPE_DVM readSerial(); - /** - * @brief Helper to write data from the P25 Tx queue to the serial interface. - * - * Very similar to the readP25Frame function. - * - * Note: the length encoded at the start does not include the length, tag, or timestamp bytes. - * - * @return int Actual number of bytes written to the serial interface. - */ - int writeSerial(); - - /** - * @brief Gets a frame of P25 data from the RX queue. - * @param data Buffer containing P25 data. - * @return uint32_t Size of the P25 data buffer, including leading data tag. - */ - uint32_t readP25Frame(uint8_t* data); - /** - * @brief Process a P25 LDU and add to the TX queue, timed appropriately. - * @param duid DUID. - * @param lc Instance of the dfsi::LC class. - * @param ldu Buffer containing LDU data. - */ - void writeP25Frame(P25DEF::DUID::E duid, dfsi::LC& lc, uint8_t* ldu); - - /** - * @brief Send a start of stream sequence (HDU, etc) to the connected serial V24 device. - * @param lc Instance of the p25::LC class. - */ - void startOfStream(const LC& lc); - /** - * @brief Send an end of stream sequence (TDU, etc) to the connected serial V24 device. - */ - void endOfStream(); - - /** - * @brief Helper to add a V.24 dataframe to the P25 Tx queue with the proper timestamp and formatting. - * @param data Buffer containing V.24 data frame to send. - * @param len Length of buffer. - * @param msgType Type of message to send (used for proper jitter clocking). - */ - void addTxToQueue(uint8_t* data, uint16_t length, SERIAL_TX_TYPE msgType); - - /** - * @brief Helper to insert IMBE silence frames for missing audio. - * @param data Buffer containing frame data. - * @param[out] lost Count of lost IMBE frames. - */ - void insertMissingAudio(uint8_t* data, uint32_t& lost); - - /** - * @brief Resets the current rx call data (stream ID, TGIDs, etc) - */ - void rxResetCallData(); - - /** - * @brief - * @param buffer - * @param length - */ - void printDebug(const uint8_t* buffer, uint16_t length); - }; -} // namespace network - -#endif // __SERIAL_SERVICE_H__ \ No newline at end of file