From 63713f0f8ee6dc0fb738868acc87353ddf97b60a Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Tue, 13 Apr 2021 01:48:13 +0000 Subject: [PATCH] upstream: Refactor modem; support remote modem support via UDP; --- DVMHost.vcxproj | 14 +- DVMHost.vcxproj.filters | 48 ++- Defines.h | 8 + Makefile | 9 +- Makefile.arm | 9 +- Makefile.rpi-arm | 9 +- config.yml | 10 +- host/Host.cpp | 432 +++++++++++++++++-------- host/Host.h | 1 + host/calibrate/HostCal.cpp | 100 ++++-- host/calibrate/HostCal.h | 5 +- modem/Modem.cpp | 471 +++++++++++++++------------ modem/Modem.h | 134 ++++---- modem/port/IModemPort.cpp | 43 +++ modem/port/IModemPort.h | 63 ++++ modem/port/ISerialPort.cpp | 43 +++ modem/port/ISerialPort.h | 63 ++++ modem/port/ModemNullPort.cpp | 197 ++++++++++++ modem/port/ModemNullPort.h | 79 +++++ modem/port/UARTPort.cpp | 605 +++++++++++++++++++++++++++++++++++ modem/port/UARTPort.h | 122 +++++++ modem/port/UDPPort.cpp | 161 ++++++++++ modem/port/UDPPort.h | 79 +++++ network/RemoteControl.cpp | 2 +- network/UDPSocket.cpp | 56 +++- network/UDPSocket.h | 36 ++- 26 files changed, 2313 insertions(+), 486 deletions(-) create mode 100644 modem/port/IModemPort.cpp create mode 100644 modem/port/IModemPort.h create mode 100644 modem/port/ISerialPort.cpp create mode 100644 modem/port/ISerialPort.h create mode 100644 modem/port/ModemNullPort.cpp create mode 100644 modem/port/ModemNullPort.h create mode 100644 modem/port/UARTPort.cpp create mode 100644 modem/port/UARTPort.h create mode 100644 modem/port/UDPPort.cpp create mode 100644 modem/port/UDPPort.h diff --git a/DVMHost.vcxproj b/DVMHost.vcxproj index 90ee1af0..106a03d4 100644 --- a/DVMHost.vcxproj +++ b/DVMHost.vcxproj @@ -195,8 +195,11 @@ - - + + + + + @@ -267,8 +270,11 @@ - - + + + + + diff --git a/DVMHost.vcxproj.filters b/DVMHost.vcxproj.filters index 4c9d2516..f7e151e2 100644 --- a/DVMHost.vcxproj.filters +++ b/DVMHost.vcxproj.filters @@ -111,6 +111,12 @@ {3a6ae793-a482-46fe-9fd3-93476ccb591d} + + {25f65018-5057-418b-ae56-b6ed06395099} + + + {bd26735c-6610-4bfe-b1c4-27da1cb18e29} + @@ -170,9 +176,6 @@ Header Files\modem - - Header Files\modem - Header Files\network @@ -182,9 +185,6 @@ Header Files\edac - - Header Files\modem - Header Files\network @@ -335,6 +335,21 @@ Header Files\dmr + + Header Files\modem\port + + + Header Files\modem\port + + + Header Files\modem\port + + + Header Files\modem\port + + + Header Files\modem\port + @@ -394,15 +409,9 @@ Source Files\modem - - Source Files\modem - Source Files\edac - - Source Files\modem - Source Files\network @@ -538,6 +547,21 @@ Source Files\dmr + + Source Files\modem\port + + + Source Files\modem\port + + + Source Files\modem\port + + + Source Files\modem\port + + + Source Files\modem\port + diff --git a/Defines.h b/Defines.h index 9939eca5..04f45862 100644 --- a/Defines.h +++ b/Defines.h @@ -115,6 +115,14 @@ typedef unsigned long long ulong64_t; #define __forceinline __attribute__((always_inline)) #endif +#define NULL_PORT "null" +#define UART_PORT "uart" +#define UDP_PORT "udp" + +#define UDP_MODE_MASTER "master" +#define UDP_MODE_PEER "peer" + +const uint32_t REMOTE_MODEM_PORT = 3334; const uint32_t TRAFFIC_DEFAULT_PORT = 62031; const uint32_t RCON_DEFAULT_PORT = 9990; diff --git a/Makefile b/Makefile index 3f108828..15ec92b5 100644 --- a/Makefile +++ b/Makefile @@ -54,9 +54,12 @@ OBJECTS = \ p25/TrunkPacket.o \ p25/P25Utils.o \ p25/VoicePacket.o \ - modem/SerialController.o \ + modem/port/IModemPort.o \ + modem/port/ISerialPort.o \ + modem/port/ModemNullPort.o \ + modem/port/UARTPort.o \ + modem/port/UDPPort.o \ modem/Modem.o \ - modem/NullModem.o \ network/UDPSocket.o \ network/RemoteControl.o \ network/BaseNetwork.o \ @@ -82,4 +85,4 @@ dvmhost: $(OBJECTS) $(CXX) $(CFLAGS) -c -o $@ $< clean: - $(RM) dvmhost *.o *.d *.bak *~ edac/*.o dmr/*.o dmr/acl/*.o dmr/data/*.o dmr/edac/*.o dmr/lc/*.o p25/*.o p25/acl/*.o p25/data/*.o p25/edac/*.o p25/lc/*.o lookups/*.o modem/*.o network/*.o yaml/*.o host/*.o host/calibrate/*.o + $(RM) dvmhost *.o *.d *.bak *~ edac/*.o dmr/*.o dmr/acl/*.o dmr/data/*.o dmr/edac/*.o dmr/lc/*.o p25/*.o p25/acl/*.o p25/data/*.o p25/edac/*.o p25/lc/*.o lookups/*.o modem/*.o modem/port/*.o network/*.o yaml/*.o host/*.o host/calibrate/*.o diff --git a/Makefile.arm b/Makefile.arm index bbe2443a..853a6fbc 100644 --- a/Makefile.arm +++ b/Makefile.arm @@ -54,9 +54,12 @@ OBJECTS = \ p25/TrunkPacket.o \ p25/P25Utils.o \ p25/VoicePacket.o \ - modem/SerialController.o \ + modem/port/IModemPort.o \ + modem/port/ISerialPort.o \ + modem/port/ModemNullPort.o \ + modem/port/UARTPort.o \ + modem/port/UDPPort.o \ modem/Modem.o \ - modem/NullModem.o \ network/UDPSocket.o \ network/RemoteControl.o \ network/BaseNetwork.o \ @@ -82,4 +85,4 @@ dvmhost: $(OBJECTS) $(CXX) $(CFLAGS) -c -o $@ $< clean: - $(RM) dvmhost *.o *.d *.bak *~ edac/*.o dmr/*.o dmr/acl/*.o dmr/data/*.o dmr/edac/*.o dmr/lc/*.o p25/*.o p25/acl/*.o p25/data/*.o p25/edac/*.o p25/lc/*.o lookups/*.o modem/*.o network/*.o yaml/*.o host/*.o host/calibrate/*.o + $(RM) dvmhost *.o *.d *.bak *~ edac/*.o dmr/*.o dmr/acl/*.o dmr/data/*.o dmr/edac/*.o dmr/lc/*.o p25/*.o p25/acl/*.o p25/data/*.o p25/edac/*.o p25/lc/*.o lookups/*.o modem/*.o modem/port/*.o network/*.o yaml/*.o host/*.o host/calibrate/*.o diff --git a/Makefile.rpi-arm b/Makefile.rpi-arm index c7b705ff..66719fc6 100644 --- a/Makefile.rpi-arm +++ b/Makefile.rpi-arm @@ -54,9 +54,12 @@ OBJECTS = \ p25/TrunkPacket.o \ p25/P25Utils.o \ p25/VoicePacket.o \ - modem/SerialController.o \ + modem/port/IModemPort.o \ + modem/port/ISerialPort.o \ + modem/port/ModemNullPort.o \ + modem/port/UARTPort.o \ + modem/port/UDPPort.o \ modem/Modem.o \ - modem/NullModem.o \ network/UDPSocket.o \ network/RemoteControl.o \ network/BaseNetwork.o \ @@ -82,4 +85,4 @@ dvmhost: $(OBJECTS) $(CXX) $(CFLAGS) -c -o $@ $< clean: - $(RM) dvmhost *.o *.d *.bak *~ edac/*.o dmr/*.o dmr/acl/*.o dmr/data/*.o dmr/edac/*.o dmr/lc/*.o p25/*.o p25/acl/*.o p25/data/*.o p25/edac/*.o p25/lc/*.o lookups/*.o modem/*.o network/*.o yaml/*.o host/*.o host/calibrate/*.o + $(RM) dvmhost *.o *.d *.bak *~ edac/*.o dmr/*.o dmr/acl/*.o dmr/data/*.o dmr/edac/*.o dmr/lc/*.o p25/*.o p25/acl/*.o p25/data/*.o p25/edac/*.o p25/lc/*.o lookups/*.o modem/*.o modem/port/*.o network/*.o yaml/*.o host/*.o host/calibrate/*.o diff --git a/config.yml b/config.yml index 0015ca29..b63606a3 100644 --- a/config.yml +++ b/config.yml @@ -103,7 +103,15 @@ system: rfssId: 1 siteId: 1 modem: - port: null + protocol: + type: "null" # Valid values are "null", "uart", and "udp" + uart: + port: /dev/ttyUSB0 + speed: 115200 + udp: + mode: master # Valid values are "master", and "peer" + address: 0.0.0.0 + port: 3334 rxInvert: false txInvert: false pttInvert: false diff --git a/host/Host.cpp b/host/Host.cpp index 369d9d46..0d01d5ac 100644 --- a/host/Host.cpp +++ b/host/Host.cpp @@ -31,7 +31,9 @@ #include "Defines.h" #include "dmr/Control.h" #include "p25/Control.h" -#include "modem/SerialController.h" +#include "modem/port/ModemNullPort.h" +#include "modem/port/UARTPort.h" +#include "modem/port/UDPPort.h" #include "network/UDPSocket.h" #include "lookups/RSSIInterpolator.h" #include "host/Host.h" @@ -47,6 +49,7 @@ using namespace lookups; #include #include +#include #include #if !defined(_WIN32) && !defined(_WIN64) @@ -68,6 +71,7 @@ Host::Host(const std::string& confFile) : m_confFile(confFile), m_conf(), m_modem(NULL), + m_modemRemote(false), m_network(NULL), m_mode(STATE_IDLE), m_modeTimer(1000U), @@ -195,6 +199,57 @@ int Host::run() if (!ret) return EXIT_FAILURE; + // is the modem slaved to a remote DVM host? + if (m_modemRemote) { + ::LogInfoEx(LOG_HOST, "Host is up and running in remote modem mode"); + + StopWatch stopWatch; + stopWatch.start(); + + bool killed = false; + + // main execution loop + while (!killed) { + if (m_modem->hasLockout() && m_mode != HOST_STATE_LOCKOUT) + setMode(HOST_STATE_LOCKOUT); + else if (!m_modem->hasLockout() && m_mode == HOST_STATE_LOCKOUT) + setMode(STATE_IDLE); + + if (m_modem->hasError() && m_mode != HOST_STATE_ERROR) + setMode(HOST_STATE_ERROR); + else if (!m_modem->hasError() && m_mode == HOST_STATE_ERROR) + setMode(STATE_IDLE); + + uint32_t ms = stopWatch.elapsed(); + if (ms > 1U) + m_modem->clock(ms); + + // ------------------------------------------------------ + // -- Modem, DMR, P25 and Network Clocking -- + // ------------------------------------------------------ + + ms = stopWatch.elapsed(); + stopWatch.start(); + + m_modem->clock(ms); + + if (g_killed) { + if (!m_modem->hasTX()) { + killed = true; + } + } + + m_modeTimer.clock(ms); + + if (ms < 2U) + Thread::sleep(1U); + } + + setMode(HOST_STATE_QUIT); + + return EXIT_SUCCESS; + } + yaml::Node systemConf = m_conf["system"]; // try to load radio IDs table @@ -1076,6 +1131,20 @@ int Host::run() /// bool Host::readParams() { + yaml::Node modemConf = m_conf["system"]["modem"]; + + yaml::Node modemProtocol = modemConf["protocol"]; + std::string portType = modemProtocol["type"].as("null"); + + yaml::Node udpProtocol = modemProtocol["udp"]; + std::string udpMode = udpProtocol["mode"].as("master"); + + bool udpMasterMode = false; + std::transform(portType.begin(), portType.end(), portType.begin(), ::tolower); + if (portType == UDP_PORT && udpMode == UDP_MODE_MASTER) { + udpMasterMode = true; + } + yaml::Node protocolConf = m_conf["protocols"]; m_dmrEnabled = protocolConf["dmr"]["enable"].as(false); m_p25Enabled = protocolConf["p25"]["enable"].as(false); @@ -1100,154 +1169,159 @@ bool Host::readParams() LogInfo(" DMR: %s", m_dmrEnabled ? "enabled" : "disabled"); LogInfo(" P25: %s", m_p25Enabled ? "enabled" : "disabled"); LogInfo(" Duplex: %s", m_duplex ? "yes" : "no"); - LogInfo(" Timeout: %us", m_timeout); - LogInfo(" RF Mode Hang: %us", m_rfModeHang); - LogInfo(" RF Talkgroup Hang: %us", m_rfTalkgroupHang); - LogInfo(" Net Mode Hang: %us", m_netModeHang); - LogInfo(" Identity: %s", m_identity.c_str()); - LogInfo(" Fixed Mode: %s", m_fixedMode ? "yes" : "no"); - LogInfo(" Lock Filename: %s", g_lockFile.c_str()); - - yaml::Node systemInfo = systemConf["info"]; - m_latitude = systemInfo["latitude"].as(0.0F); - m_longitude = systemInfo["longitude"].as(0.0F); - m_height = systemInfo["height"].as(0); - m_power = systemInfo["power"].as(0U); - m_location = systemInfo["location"].as(); - - LogInfo("System Info Parameters"); - LogInfo(" Latitude: %fdeg N", m_latitude); - LogInfo(" Longitude: %fdeg E", m_longitude); - LogInfo(" Height: %um", m_height); - LogInfo(" Power: %uW", m_power); - LogInfo(" Location: \"%s\"", m_location.c_str()); - - // try to load bandplan identity table - std::string idenLookupFile = systemConf["iden_table"]["file"].as(); - uint32_t idenReloadTime = systemConf["iden_table"]["time"].as(0U); - - if (idenLookupFile.length() <= 0U) { - ::LogError(LOG_HOST, "No bandplan identity table? This must be defined!"); - return false; - } + if (!udpMasterMode) { + LogInfo(" Timeout: %us", m_timeout); + LogInfo(" RF Mode Hang: %us", m_rfModeHang); + LogInfo(" RF Talkgroup Hang: %us", m_rfTalkgroupHang); + LogInfo(" Net Mode Hang: %us", m_netModeHang); + LogInfo(" Identity: %s", m_identity.c_str()); + LogInfo(" Fixed Mode: %s", m_fixedMode ? "yes" : "no"); + LogInfo(" Lock Filename: %s", g_lockFile.c_str()); + + yaml::Node systemInfo = systemConf["info"]; + m_latitude = systemInfo["latitude"].as(0.0F); + m_longitude = systemInfo["longitude"].as(0.0F); + m_height = systemInfo["height"].as(0); + m_power = systemInfo["power"].as(0U); + m_location = systemInfo["location"].as(); + + LogInfo("System Info Parameters"); + LogInfo(" Latitude: %fdeg N", m_latitude); + LogInfo(" Longitude: %fdeg E", m_longitude); + LogInfo(" Height: %um", m_height); + LogInfo(" Power: %uW", m_power); + LogInfo(" Location: \"%s\"", m_location.c_str()); + + // try to load bandplan identity table + std::string idenLookupFile = systemConf["iden_table"]["file"].as(); + uint32_t idenReloadTime = systemConf["iden_table"]["time"].as(0U); + + if (idenLookupFile.length() <= 0U) { + ::LogError(LOG_HOST, "No bandplan identity table? This must be defined!"); + return false; + } - LogInfo("Iden Table Lookups"); - LogInfo(" File: %s", idenLookupFile.length() > 0U ? idenLookupFile.c_str() : "None"); - if (idenReloadTime > 0U) - LogInfo(" Reload: %u mins", idenReloadTime); + LogInfo("Iden Table Lookups"); + LogInfo(" File: %s", idenLookupFile.length() > 0U ? idenLookupFile.c_str() : "None"); + if (idenReloadTime > 0U) + LogInfo(" Reload: %u mins", idenReloadTime); - m_idenTable = new IdenTableLookup(idenLookupFile, idenReloadTime); - m_idenTable->read(); + m_idenTable = new IdenTableLookup(idenLookupFile, idenReloadTime); + m_idenTable->read(); - yaml::Node rfssConfig = systemConf["config"]; - m_channelId = (uint8_t)rfssConfig["channelId"].as(0U); - if (m_channelId > 15U) { // clamp to 15 - m_channelId = 15U; - } + yaml::Node rfssConfig = systemConf["config"]; + m_channelId = (uint8_t)rfssConfig["channelId"].as(0U); + if (m_channelId > 15U) { // clamp to 15 + m_channelId = 15U; + } - IdenTable entry = m_idenTable->find(m_channelId); - if (entry.baseFrequency() == 0U) { - ::LogError(LOG_HOST, "Channel Id %u has an invalid base frequency.", m_channelId); - return false; - } + IdenTable entry = m_idenTable->find(m_channelId); + if (entry.baseFrequency() == 0U) { + ::LogError(LOG_HOST, "Channel Id %u has an invalid base frequency.", m_channelId); + return false; + } - if (entry.txOffsetMhz() == 0U) { - ::LogError(LOG_HOST, "Channel Id %u has an invalid Tx offset.", m_channelId); - return false; - } + if (entry.txOffsetMhz() == 0U) { + ::LogError(LOG_HOST, "Channel Id %u has an invalid Tx offset.", m_channelId); + return false; + } - uint32_t calcSpace = (uint32_t)(entry.chSpaceKhz() / 0.125); - float calcTxOffset = entry.txOffsetMhz() * 1000000; + uint32_t calcSpace = (uint32_t)(entry.chSpaceKhz() / 0.125); + float calcTxOffset = entry.txOffsetMhz() * 1000000; - m_channelNo = (uint32_t)::strtoul(rfssConfig["channelNo"].as("1").c_str(), NULL, 16); - if (m_channelNo == 0U) { // clamp to 1 - m_channelNo = 1U; - } - if (m_channelNo > 4095U) { // clamp to 4095 - m_channelNo = 4095U; - } + m_channelNo = (uint32_t)::strtoul(rfssConfig["channelNo"].as("1").c_str(), NULL, 16); + if (m_channelNo == 0U) { // clamp to 1 + m_channelNo = 1U; + } + if (m_channelNo > 4095U) { // clamp to 4095 + m_channelNo = 4095U; + } - m_rxFrequency = (uint32_t)((entry.baseFrequency() + ((calcSpace * 125) * m_channelNo)) + calcTxOffset); - m_txFrequency = (uint32_t)((entry.baseFrequency() + ((calcSpace * 125) * m_channelNo))); + m_rxFrequency = (uint32_t)((entry.baseFrequency() + ((calcSpace * 125) * m_channelNo)) + calcTxOffset); + m_txFrequency = (uint32_t)((entry.baseFrequency() + ((calcSpace * 125) * m_channelNo))); - yaml::Node& voiceChList = rfssConfig["voiceChNo"]; - for (size_t i = 0; i < voiceChList.size(); i++) { - uint32_t chNo = (uint32_t)::strtoul(voiceChList[i].as("1").c_str(), NULL, 16); - m_voiceChNo.push_back(chNo); - } + yaml::Node & voiceChList = rfssConfig["voiceChNo"]; + for (size_t i = 0; i < voiceChList.size(); i++) { + uint32_t chNo = (uint32_t)::strtoul(voiceChList[i].as("1").c_str(), NULL, 16); + m_voiceChNo.push_back(chNo); + } - std::string strVoiceChNo = ""; - for (auto it = m_voiceChNo.begin(); it != m_voiceChNo.end(); ++it) { - int decVal = ::atoi(std::to_string(*it).c_str()); - char hexStr[8]; + std::string strVoiceChNo = ""; + for (auto it = m_voiceChNo.begin(); it != m_voiceChNo.end(); ++it) { + int decVal = ::atoi(std::to_string(*it).c_str()); + char hexStr[8]; - ::sprintf(hexStr, "$%04X", decVal); + ::sprintf(hexStr, "$%04X", decVal); - strVoiceChNo.append(std::string(hexStr)); - strVoiceChNo.append(","); - } - strVoiceChNo.erase(strVoiceChNo.find_last_of(",")); + strVoiceChNo.append(std::string(hexStr)); + strVoiceChNo.append(","); + } + strVoiceChNo.erase(strVoiceChNo.find_last_of(",")); - m_siteId = (uint8_t)::strtoul(rfssConfig["siteId"].as("1").c_str(), NULL, 16); - if (m_siteId == 0U) { // clamp to 1 - m_siteId = 1U; - } - if (m_siteId > 0xFEU) { // clamp to $FE - m_siteId = 0xFEU; - } + m_siteId = (uint8_t)::strtoul(rfssConfig["siteId"].as("1").c_str(), NULL, 16); + if (m_siteId == 0U) { // clamp to 1 + m_siteId = 1U; + } + if (m_siteId > 0xFEU) { // clamp to $FE + m_siteId = 0xFEU; + } - m_dmrColorCode = rfssConfig["colorCode"].as(2U); - m_dmrNetId = (uint32_t)::strtoul(rfssConfig["dmrNetId"].as("1").c_str(), NULL, 16); - if (m_dmrNetId == 0U) { // clamp to 1 - m_dmrNetId = 1U; - } - if (m_dmrNetId > 0x1FFU) { // clamp to $1FF - m_dmrNetId = 0x1FFU; - } + m_dmrColorCode = rfssConfig["colorCode"].as(2U); + m_dmrNetId = (uint32_t)::strtoul(rfssConfig["dmrNetId"].as("1").c_str(), NULL, 16); + if (m_dmrNetId == 0U) { // clamp to 1 + m_dmrNetId = 1U; + } + if (m_dmrNetId > 0x1FFU) { // clamp to $1FF + m_dmrNetId = 0x1FFU; + } - m_p25NAC = (uint32_t)::strtoul(rfssConfig["nac"].as("293").c_str(), NULL, 16); - m_p25PatchSuperGroup = (uint32_t)::strtoul(rfssConfig["pSuperGroup"].as("FFFF").c_str(), NULL, 16); - m_p25NetId = (uint32_t)::strtoul(rfssConfig["netId"].as("BB800").c_str(), NULL, 16); - if (m_p25NetId == 0U) { // clamp to 1 - m_p25NetId = 1U; - } - if (m_p25NetId > 0xFFFFEU) { // clamp to $FFFFE - m_p25NetId = 0xFFFFEU; - } - m_p25SysId = (uint32_t)::strtoul(rfssConfig["sysId"].as("001").c_str(), NULL, 16); - if (m_p25SysId == 0U) { // clamp to 1 - m_p25SysId = 1U; - } - if (m_p25SysId > 0xFFEU) { // clamp to $FFE - m_p25SysId = 0xFFEU; - } - m_p25RfssId = (uint8_t)::strtoul(rfssConfig["rfssId"].as("1").c_str(), NULL, 16); - if (m_p25RfssId == 0U) { // clamp to 1 - m_p25RfssId = 1U; + m_p25NAC = (uint32_t)::strtoul(rfssConfig["nac"].as("293").c_str(), NULL, 16); + m_p25PatchSuperGroup = (uint32_t)::strtoul(rfssConfig["pSuperGroup"].as("FFFF").c_str(), NULL, 16); + m_p25NetId = (uint32_t)::strtoul(rfssConfig["netId"].as("BB800").c_str(), NULL, 16); + if (m_p25NetId == 0U) { // clamp to 1 + m_p25NetId = 1U; + } + if (m_p25NetId > 0xFFFFEU) { // clamp to $FFFFE + m_p25NetId = 0xFFFFEU; + } + m_p25SysId = (uint32_t)::strtoul(rfssConfig["sysId"].as("001").c_str(), NULL, 16); + if (m_p25SysId == 0U) { // clamp to 1 + m_p25SysId = 1U; + } + if (m_p25SysId > 0xFFEU) { // clamp to $FFE + m_p25SysId = 0xFFEU; + } + m_p25RfssId = (uint8_t)::strtoul(rfssConfig["rfssId"].as("1").c_str(), NULL, 16); + if (m_p25RfssId == 0U) { // clamp to 1 + m_p25RfssId = 1U; + } + if (m_p25RfssId > 0xFEU) { // clamp to $FE + m_p25RfssId = 0xFEU; + } + + LogInfo("System Config Parameters"); + LogInfo(" RX Frequency: %uHz", m_rxFrequency); + LogInfo(" TX Frequency: %uHz", m_txFrequency); + LogInfo(" Base Frequency: %uHz", entry.baseFrequency()); + LogInfo(" TX Offset: %fMHz", entry.txOffsetMhz()); + LogInfo(" Bandwidth: %fKHz", entry.chBandwidthKhz()); + LogInfo(" Channel Spacing: %fKHz", entry.chSpaceKhz()); + LogInfo(" Channel Id: %u", m_channelId); + LogInfo(" Channel No.: $%04X", m_channelNo); + LogInfo(" Voice Channel No(s).: %s", strVoiceChNo.c_str()); + LogInfo(" Site Id: $%02X", m_siteId); + LogInfo(" DMR Color Code: %u", m_dmrColorCode); + LogInfo(" DMR Network Id: $%05X", m_dmrNetId); + LogInfo(" P25 NAC: $%03X", m_p25NAC); + LogInfo(" P25 Patch Super Group: $%04X", m_p25PatchSuperGroup); + LogInfo(" P25 Network Id: $%05X", m_p25NetId); + LogInfo(" P25 System Id: $%03X", m_p25SysId); + LogInfo(" P25 RFSS Id: $%02X", m_p25RfssId); } - if (m_p25RfssId > 0xFEU) { // clamp to $FE - m_p25RfssId = 0xFEU; + else { + LogInfo(" Modem Remote Control: yes"); } - LogInfo("System Config Parameters"); - LogInfo(" RX Frequency: %uHz", m_rxFrequency); - LogInfo(" TX Frequency: %uHz", m_txFrequency); - LogInfo(" Base Frequency: %uHz", entry.baseFrequency()); - LogInfo(" TX Offset: %fMHz", entry.txOffsetMhz()); - LogInfo(" Bandwidth: %fKHz", entry.chBandwidthKhz()); - LogInfo(" Channel Spacing: %fKHz", entry.chSpaceKhz()); - LogInfo(" Channel Id: %u", m_channelId); - LogInfo(" Channel No.: $%04X", m_channelNo); - LogInfo(" Voice Channel No(s).: %s", strVoiceChNo.c_str()); - LogInfo(" Site Id: $%02X", m_siteId); - LogInfo(" DMR Color Code: %u", m_dmrColorCode); - LogInfo(" DMR Network Id: $%05X", m_dmrNetId); - LogInfo(" P25 NAC: $%03X", m_p25NAC); - LogInfo(" P25 Patch Super Group: $%04X", m_p25PatchSuperGroup); - LogInfo(" P25 Network Id: $%05X", m_p25NetId); - LogInfo(" P25 System Id: $%03X", m_p25SysId); - LogInfo(" P25 RFSS Id: $%02X", m_p25RfssId); - return true; } @@ -1257,7 +1331,19 @@ bool Host::readParams() bool Host::createModem() { yaml::Node modemConf = m_conf["system"]["modem"]; - std::string port = modemConf["port"].as(); + + yaml::Node modemProtocol = modemConf["protocol"]; + std::string portType = modemProtocol["type"].as("null"); + + yaml::Node uartProtocol = modemProtocol["uart"]; + std::string uartPort = uartProtocol["port"].as(); + uint32_t uartSpeed = uartProtocol["speed"].as(115200); + + yaml::Node udpProtocol = modemProtocol["udp"]; + std::string udpMode = udpProtocol["mode"].as("master"); + std::string udpAddress = udpProtocol["address"].as(); + uint32_t udpPort = udpProtocol["port"].as(REMOTE_MODEM_PORT); + bool rxInvert = modemConf["rxInvert"].as(false); bool txInvert = modemConf["txInvert"].as(false); bool pttInvert = modemConf["pttInvert"].as(false); @@ -1289,7 +1375,81 @@ bool Host::createModem() packetPlayoutTime = 1U; LogInfo("Modem Parameters"); - LogInfo(" Port: %s", port.c_str()); + LogInfo(" Port Type: %s", portType.c_str()); + + port::IModemPort* modemPort = NULL; + std::transform(portType.begin(), portType.end(), portType.begin(), ::tolower); + if (portType == NULL_PORT) { + modemPort = new port::ModemNullPort(); + } + else if (portType == UART_PORT || portType == UDP_PORT) { + port::SERIAL_SPEED serialSpeed = port::SERIAL_115200; + switch (uartSpeed) { + case 1200: + serialSpeed = port::SERIAL_1200; + break; + case 2400: + serialSpeed = port::SERIAL_2400; + break; + case 4800: + serialSpeed = port::SERIAL_4800; + break; + case 9600: + serialSpeed = port::SERIAL_9600; + break; + case 19200: + serialSpeed = port::SERIAL_19200; + break; + case 38400: + serialSpeed = port::SERIAL_38400; + break; + case 76800: + serialSpeed = port::SERIAL_76800; + break; + case 230400: + serialSpeed = port::SERIAL_230400; + break; + case 460800: + serialSpeed = port::SERIAL_460800; + break; + default: + LogWarning(LOG_HOST, "Unsupported serial speed %u, defaulting to %u", uartSpeed, port::SERIAL_115200); + uartSpeed = 115200; + case 115200: + break; + } + + modemPort = new port::UARTPort(uartPort, serialSpeed, true); + LogInfo(" UART Port: %s", uartPort.c_str()); + LogInfo(" UART Speed: %u", uartSpeed); + } + else { + LogError(LOG_HOST, "Invalid protocol port type, %s!", portType.c_str()); + return false; + } + + port::IModemPort* slavePort = NULL; + if (portType == UDP_PORT) { + std::transform(udpMode.begin(), udpMode.end(), udpMode.begin(), ::tolower); + if (udpMode == UDP_MODE_MASTER) { + slavePort = new port::UDPPort(udpAddress, udpPort); + m_modemRemote = true; + } + else if (udpMode == UDP_MODE_PEER) { + delete modemPort; + modemPort = new port::UDPPort(udpAddress, udpPort); + m_modemRemote = false; + } + else { + LogError(LOG_HOST, "Invalid UDP mode, %s!", udpMode.c_str()); + return false; + } + + LogInfo(" UDP Mode: %s", udpMode.c_str()); + LogInfo(" UDP Address: %s", udpAddress.c_str()); + LogInfo(" UDP Port: %u", udpPort); + } + LogInfo(" RX Invert: %s", rxInvert ? "yes" : "no"); LogInfo(" TX Invert: %s", txInvert ? "yes" : "no"); LogInfo(" PTT Invert: %s", pttInvert ? "yes" : "no"); @@ -1311,7 +1471,7 @@ bool Host::createModem() LogInfo(" Debug: yes"); } - m_modem = Modem::createModem(port, m_duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, fdmaPreamble, dmrRxDelay, p25CorrCount, packetPlayoutTime, disableOFlowReset, trace, debug); + m_modem = new Modem(modemPort, m_duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, fdmaPreamble, dmrRxDelay, p25CorrCount, packetPlayoutTime, disableOFlowReset, trace, debug); m_modem->setModeParams(m_dmrEnabled, m_p25Enabled); m_modem->setLevels(rxLevel, cwIdTXLevel, dmrTXLevel, p25TXLevel); m_modem->setSymbolAdjust(dmrSymLevel3Adj, dmrSymLevel1Adj, p25SymLevel3Adj, p25SymLevel1Adj); @@ -1319,6 +1479,10 @@ bool Host::createModem() m_modem->setDMRColorCode(m_dmrColorCode); m_modem->setP25NAC(m_p25NAC); + if (m_modemRemote) { + m_modem->setSlavePort(slavePort); + } + bool ret = m_modem->open(); if (!ret) { delete m_modem; diff --git a/host/Host.h b/host/Host.h index afec92ac..2b86afa0 100644 --- a/host/Host.h +++ b/host/Host.h @@ -68,6 +68,7 @@ private: yaml::Node m_conf; modem::Modem* m_modem; + bool m_modemRemote; network::Network* m_network; uint8_t m_mode; diff --git a/host/calibrate/HostCal.cpp b/host/calibrate/HostCal.cpp index 1be6a40d..33a0b092 100644 --- a/host/calibrate/HostCal.cpp +++ b/host/calibrate/HostCal.cpp @@ -42,6 +42,7 @@ using namespace modem; #include +#include #if !defined(_WIN32) && !defined(_WIN64) #include @@ -130,8 +131,7 @@ unsigned char LDU2_1K[] = { HostCal::HostCal(const std::string& confFile) : m_confFile(confFile), m_conf(), - m_port(), - m_serial(), + m_serial(NULL), m_console(), m_fec(), m_transmit(false), @@ -174,7 +174,7 @@ HostCal::HostCal(const std::string& confFile) : /// HostCal::~HostCal() { - /* stub */ + delete m_serial; } /// @@ -188,10 +188,6 @@ int HostCal::run() ::fatal("cannot read the configuration file, %s\n", m_confFile.c_str()); } - yaml::Node modemConf = m_conf["system"]["modem"]; - m_port = modemConf["port"].as(); - m_serial = CSerialController(m_port, SERIAL_115200); - // initialize system logging ret = ::LogInitialise("", "", 0U, 2U); if (!ret) { @@ -199,16 +195,76 @@ int HostCal::run() return 1; } - getHostVersion(); - ::LogInfo(">> Modem Calibration"); + yaml::Node modemConf = m_conf["system"]["modem"]; + + yaml::Node modemProtocol = modemConf["protocol"]; + std::string portType = modemProtocol["type"].as("null"); + + yaml::Node uartProtocol = modemProtocol["uart"]; + std::string uartPort = uartProtocol["port"].as(); + uint32_t uartSpeed = uartProtocol["speed"].as(115200); - if (m_port == NULL_MODEM) { + std::transform(portType.begin(), portType.end(), portType.begin(), ::tolower); + if (portType == NULL_PORT) { ::LogError(LOG_HOST, "Calibration mode is unsupported with the null modem!"); return 2; } + else if (portType == UART_PORT) { + port::SERIAL_SPEED serialSpeed = port::SERIAL_115200; + switch (uartSpeed) { + case 1200: + serialSpeed = port::SERIAL_1200; + break; + case 2400: + serialSpeed = port::SERIAL_2400; + break; + case 4800: + serialSpeed = port::SERIAL_4800; + break; + case 9600: + serialSpeed = port::SERIAL_9600; + break; + case 19200: + serialSpeed = port::SERIAL_19200; + break; + case 38400: + serialSpeed = port::SERIAL_38400; + break; + case 76800: + serialSpeed = port::SERIAL_76800; + break; + case 230400: + serialSpeed = port::SERIAL_230400; + break; + case 460800: + serialSpeed = port::SERIAL_460800; + break; + default: + LogWarning(LOG_HOST, "Unsupported serial speed %u, defaulting to %u", uartSpeed, port::SERIAL_115200); + uartSpeed = 115200; + case 115200: + break; + } + + m_serial = new port::UARTPort(uartPort, serialSpeed, true); + LogInfo(" UART Port: %s", uartPort.c_str()); + LogInfo(" UART Speed: %u", uartSpeed); + } + else if (portType == UDP_PORT) { + ::LogError(LOG_HOST, "Calibration mode is unsupported with a remote modem!"); + return 2; + } + + if (m_serial == NULL) { + ::LogError(LOG_HOST, "Invalid modem port type, %s!", portType.c_str()); + return 2; + } + + getHostVersion(); + ::LogInfo(">> Modem Calibration"); // open serial connection to modem DSP and initialize - ret = m_serial.open(); + ret = m_serial->open(); if (!ret) { ::LogError(LOG_CAL, "Failed to open serial device"); return 1; @@ -217,14 +273,14 @@ int HostCal::run() ret = initModem(); if (!ret) { ::LogError(LOG_CAL, "Modem is unresponsive"); - m_serial.close(); + m_serial->close(); return 1; } // open terminal console ret = m_console.open(); if (!ret) { - m_serial.close(); + m_serial->close(); return 1; } @@ -621,7 +677,7 @@ int HostCal::run() if (m_transmit) setTransmit(); - m_serial.close(); + m_serial->close(); m_console.close(); return 0; } @@ -890,7 +946,7 @@ bool HostCal::setTransmit() buffer[2U] = CMD_CAL_DATA; buffer[3U] = m_transmit ? 0x01U : 0x00U; - int ret = m_serial.write(buffer, 4U); + int ret = m_serial->write(buffer, 4U); if (ret <= 0) return false; @@ -950,7 +1006,7 @@ bool HostCal::initModem() /// Zero if no data was read, otherwise returns length of data read. int HostCal::readModem(uint8_t *buffer, uint32_t length) { - int n = m_serial.read(buffer + 0U, 1U); + int n = m_serial->read(buffer + 0U, 1U); if (n <= 0) return n; @@ -959,7 +1015,7 @@ int HostCal::readModem(uint8_t *buffer, uint32_t length) n = 0; for (uint32_t i = 0U; i < 20U && n == 0; i++) { - n = m_serial.read(buffer + 1U, 1U); + n = m_serial->read(buffer + 1U, 1U); if (n < 0) return n; if (n == 0) @@ -973,7 +1029,7 @@ int HostCal::readModem(uint8_t *buffer, uint32_t length) uint32_t offset = 2U; for (uint32_t i = 0U; i < 20U && offset < len; i++) { - n = m_serial.read(buffer + offset, len - offset); + n = m_serial->read(buffer + offset, len - offset); if (n < 0) return n; if (n == 0) @@ -1520,7 +1576,7 @@ bool HostCal::getFirmwareVersion() buffer[1U] = 3U; buffer[2U] = CMD_GET_VERSION; - ret = m_serial.write(buffer, 3U); + ret = m_serial->write(buffer, 3U); if (ret <= 0) return false; @@ -1654,7 +1710,7 @@ bool HostCal::writeConfig(uint8_t modeOverride) buffer[14U] = (uint8_t)m_p25CorrCount; - int ret = m_serial.write(buffer, 17U); + int ret = m_serial->write(buffer, 17U); if (ret <= 0) return false; @@ -1699,7 +1755,7 @@ bool HostCal::writeSymbolAdjust() m_conf["system"]["modem"]["p25SymLvl1Adj"] = __INT_STR(m_p25SymLevel1Adj); buffer[6U] = (uint8_t)(m_p25SymLevel1Adj + 128); - int ret = m_serial.write(buffer, 7U); + int ret = m_serial->write(buffer, 7U); if (ret <= 0) return false; @@ -1795,7 +1851,7 @@ void HostCal::printStatus() buffer[1U] = 4U; buffer[2U] = CMD_GET_STATUS; - int ret = m_serial.write(buffer, 4U); + int ret = m_serial->write(buffer, 4U); if (ret <= 0) return; diff --git a/host/calibrate/HostCal.h b/host/calibrate/HostCal.h index 4b3618ae..0b863a0e 100644 --- a/host/calibrate/HostCal.h +++ b/host/calibrate/HostCal.h @@ -35,7 +35,7 @@ #include "Defines.h" #include "edac/AMBEFEC.h" #include "modem/Modem.h" -#include "modem/SerialController.h" +#include "modem/port/UARTPort.h" #include "host/calibrate/Console.h" #include "host/Host.h" #include "yaml/Yaml.h" @@ -61,8 +61,7 @@ private: const std::string& m_confFile; yaml::Node m_conf; - std::string m_port; - modem::CSerialController m_serial; + modem::port::UARTPort* m_serial; Console m_console; edac::AMBEFEC m_fec; diff --git a/modem/Modem.cpp b/modem/Modem.cpp index 370cd343..688b3e51 100644 --- a/modem/Modem.cpp +++ b/modem/Modem.cpp @@ -11,7 +11,7 @@ // Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) // /* -* Copyright (C) 2011-2017 by Jonathan Naylor G4KLX +* Copyright (C) 2011-2021 by Jonathan Naylor G4KLX * Copyright (C) 2017-2020 by Bryan Biedenkapp N2PLL * * This program is free software; you can redistribute it and/or modify @@ -32,7 +32,6 @@ #include "dmr/DMRDefines.h" #include "p25/P25Defines.h" #include "modem/Modem.h" -#include "modem/NullModem.h" #include "Log.h" #include "Thread.h" #include "Utils.h" @@ -58,7 +57,7 @@ using namespace modem; /// /// Initializes a new instance of the Modem class. /// -/// Serial port the modem DSP is connected to. +/// Port the air interface modem is connected to. /// Flag indicating the modem is operating in duplex mode. /// Flag indicating the Rx polarity should be inverted. /// Flag indicating the Tx polarity should be inverted. @@ -70,11 +69,12 @@ using namespace modem; /// P25 Correlation Countdown. /// Flag indicating whether the ADC/DAC overflow reset logic is disabled. /// Length of time in MS between packets to send to modem. -/// Flag indicating whether modem DSP trace is enabled. -/// Flag indicating whether modem DSP debug is enabled. -Modem::Modem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker, +/// Flag indicating whether air interface modem trace is enabled. +/// Flag indicating whether air interface modem debug is enabled. +Modem::Modem(port::IModemPort* port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker, bool cosLockout, uint8_t fdmaPreamble, uint8_t dmrRxDelay, uint8_t p25CorrCount, uint8_t packetPlayoutTime, bool disableOFlowReset, bool trace, bool debug) : m_port(port), + m_remotePort(NULL), m_dmrColorCode(0U), m_p25NAC(0x293U), m_duplex(duplex), @@ -103,7 +103,7 @@ Modem::Modem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, m_p25SymLevel1Adj(0), m_adcOverFlowCount(0U), m_dacOverFlowCount(0U), - m_serial(port, SERIAL_115200, true), + m_modemState(STATE_IDLE), m_buffer(NULL), m_length(0U), m_offset(0U), @@ -124,7 +124,7 @@ Modem::Modem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, m_lockout(false), m_error(false) { - assert(!port.empty()); + assert(port != NULL); m_buffer = new uint8_t[BUFFER_LENGTH]; } @@ -134,11 +134,17 @@ Modem::Modem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, /// Modem::~Modem() { + delete m_port; + + if (m_remotePort != NULL) { + delete m_remotePort; + } + delete[] m_buffer; } /// -/// Sets the modem DSP RF DC offset parameters. +/// Sets the RF DC offset parameters. /// /// /// @@ -149,7 +155,7 @@ void Modem::setDCOffsetParams(int txDCOffset, int rxDCOffset) } /// -/// Sets the modem DSP enabled modes. +/// Sets the enabled modes. /// /// /// @@ -160,7 +166,7 @@ void Modem::setModeParams(bool dmrEnabled, bool p25Enabled) } /// -/// Sets the modem DSP RF deviation levels. +/// Sets the RF deviation levels. /// /// /// @@ -175,7 +181,7 @@ void Modem::setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float } /// -/// Sets the modem DSP Symbol adjustment levels +/// Sets the symbol adjustment levels /// /// /// @@ -209,7 +215,7 @@ void Modem::setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25Sym } /// -/// Sets the modem DSP DMR color code. +/// Sets the DMR color code. /// /// void Modem::setDMRColorCode(uint32_t colorCode) @@ -220,7 +226,7 @@ void Modem::setDMRColorCode(uint32_t colorCode) } /// -/// Sets the modem DSP P25 NAC. +/// Sets the P25 NAC. /// /// void Modem::setP25NAC(uint32_t nac) @@ -231,7 +237,7 @@ void Modem::setP25NAC(uint32_t nac) } /// -/// Sets the modem DSP RF receive deviation levels. +/// Sets the RF receive deviation levels. /// /// void Modem::setRXLevel(float rxLevel) @@ -248,7 +254,7 @@ void Modem::setRXLevel(float rxLevel) // Utils::dump(1U, "Written", buffer, 16U); - int ret = m_serial.write(buffer, 16U); + int ret = m_port->write(buffer, 16U); if (ret != 16) return; @@ -275,20 +281,30 @@ void Modem::setRXLevel(float rxLevel) } /// -/// Opens connection to the modem DSP. +/// Sets the slave port for remote operation. +/// +void Modem::setSlavePort(port::IModemPort* slavePort) +{ + assert(slavePort != NULL); + + m_remotePort = slavePort; +} + +/// +/// Opens connection to the air interface modem. /// /// True, if connection to modem is established, otherwise false. bool Modem::open() { - LogMessage(LOG_MODEM, "Initializing HW modem"); + LogMessage(LOG_MODEM, "Initializing modem"); - bool ret = m_serial.open(); + bool ret = m_port->open(); if (!ret) return false; ret = getFirmwareVersion(); if (!ret) { - m_serial.close(); + m_port->close(); return false; } else { @@ -297,10 +313,27 @@ bool Modem::open() m_inactivityTimer.stop(); } + if (m_remotePort != NULL) { + ret = m_remotePort->open(); + if (!ret) + return false; + + m_statusTimer.start(); + + m_error = false; + m_offset = 0U; + + LogMessage(LOG_MODEM, "Modem Ready [Remote Mode]"); + + m_playoutTimer.start(); + + return true; + } + ret = writeConfig(); if (!ret) { LogError(LOG_MODEM, "Modem is unresponsive"); - m_serial.close(); + m_port->close(); return false; } @@ -311,7 +344,7 @@ bool Modem::open() m_error = false; m_offset = 0U; - LogMessage(LOG_MODEM, "Modem Ready"); + LogMessage(LOG_MODEM, "Modem Ready [Direct Mode]"); return true; } @@ -353,175 +386,187 @@ void Modem::clock(uint32_t ms) // Nothing to do } else { - // type == RTM_OK - switch (m_buffer[2U]) { + if (m_remotePort != NULL) { + if (m_trace) + Utils::dump(1U, "TX Remote Data", m_buffer, m_length); + + // send entire modem packet over the slave port + m_remotePort->write(m_buffer, m_length); + } + else { + // type == RTM_OK + switch (m_buffer[2U]) { /** Digital Mobile Radio */ case CMD_DMR_DATA1: - { - if (m_trace) - Utils::dump(1U, "RX DMR Data 1", m_buffer, m_length); + { + if (m_trace) + Utils::dump(1U, "RX DMR Data 1", m_buffer, m_length); - uint8_t data = m_length - 2U; - m_rxDMRData1.addData(&data, 1U); + uint8_t data = m_length - 2U; + m_rxDMRData1.addData(&data, 1U); - if (m_buffer[3U] == (dmr::DMR_SYNC_DATA | dmr::DT_TERMINATOR_WITH_LC)) - data = TAG_EOT; - else - data = TAG_DATA; - m_rxDMRData1.addData(&data, 1U); + if (m_buffer[3U] == (dmr::DMR_SYNC_DATA | dmr::DT_TERMINATOR_WITH_LC)) + data = TAG_EOT; + else + data = TAG_DATA; + m_rxDMRData1.addData(&data, 1U); - m_rxDMRData1.addData(m_buffer + 3U, m_length - 3U); - } - break; + m_rxDMRData1.addData(m_buffer + 3U, m_length - 3U); + } + break; case CMD_DMR_DATA2: - { - if (m_trace) - Utils::dump(1U, "RX DMR Data 2", m_buffer, m_length); + { + if (m_trace) + Utils::dump(1U, "RX DMR Data 2", m_buffer, m_length); - uint8_t data = m_length - 2U; - m_rxDMRData2.addData(&data, 1U); + uint8_t data = m_length - 2U; + m_rxDMRData2.addData(&data, 1U); - if (m_buffer[3U] == (dmr::DMR_SYNC_DATA | dmr::DT_TERMINATOR_WITH_LC)) - data = TAG_EOT; - else - data = TAG_DATA; - m_rxDMRData2.addData(&data, 1U); + if (m_buffer[3U] == (dmr::DMR_SYNC_DATA | dmr::DT_TERMINATOR_WITH_LC)) + data = TAG_EOT; + else + data = TAG_DATA; + m_rxDMRData2.addData(&data, 1U); - m_rxDMRData2.addData(m_buffer + 3U, m_length - 3U); - } - break; + m_rxDMRData2.addData(m_buffer + 3U, m_length - 3U); + } + break; case CMD_DMR_LOST1: - { - if (m_trace) - Utils::dump(1U, "RX DMR Lost 1", m_buffer, m_length); + { + if (m_trace) + Utils::dump(1U, "RX DMR Lost 1", m_buffer, m_length); - uint8_t data = 1U; - m_rxDMRData1.addData(&data, 1U); + uint8_t data = 1U; + m_rxDMRData1.addData(&data, 1U); - data = TAG_LOST; - m_rxDMRData1.addData(&data, 1U); - } - break; + data = TAG_LOST; + m_rxDMRData1.addData(&data, 1U); + } + break; case CMD_DMR_LOST2: - { - if (m_trace) - Utils::dump(1U, "RX DMR Lost 2", m_buffer, m_length); + { + if (m_trace) + Utils::dump(1U, "RX DMR Lost 2", m_buffer, m_length); - uint8_t data = 1U; - m_rxDMRData2.addData(&data, 1U); + uint8_t data = 1U; + m_rxDMRData2.addData(&data, 1U); - data = TAG_LOST; - m_rxDMRData2.addData(&data, 1U); - } - break; + data = TAG_LOST; + m_rxDMRData2.addData(&data, 1U); + } + break; /** Project 25 */ case CMD_P25_DATA: - { - if (m_trace) - Utils::dump(1U, "RX P25 Data", m_buffer, m_length); + { + if (m_trace) + Utils::dump(1U, "RX P25 Data", m_buffer, m_length); - uint8_t data = m_length - 2U; - m_rxP25Data.addData(&data, 1U); + uint8_t data = m_length - 2U; + m_rxP25Data.addData(&data, 1U); - data = TAG_DATA; - m_rxP25Data.addData(&data, 1U); + data = TAG_DATA; + m_rxP25Data.addData(&data, 1U); - m_rxP25Data.addData(m_buffer + 3U, m_length - 3U); - } - break; + m_rxP25Data.addData(m_buffer + 3U, m_length - 3U); + } + break; case CMD_P25_LOST: - { - if (m_trace) - Utils::dump(1U, "RX P25 Lost", m_buffer, m_length); + { + if (m_trace) + Utils::dump(1U, "RX P25 Lost", m_buffer, m_length); - uint8_t data = 1U; - m_rxP25Data.addData(&data, 1U); + uint8_t data = 1U; + m_rxP25Data.addData(&data, 1U); - data = TAG_LOST; - m_rxP25Data.addData(&data, 1U); - } - break; + data = TAG_LOST; + m_rxP25Data.addData(&data, 1U); + } + break; /** General */ case CMD_GET_STATUS: - { - // if (m_trace) - // Utils::dump(1U, "Get Status", m_buffer, m_length); + { + // if (m_trace) + // Utils::dump(1U, "Get Status", m_buffer, m_length); - m_tx = (m_buffer[5U] & 0x01U) == 0x01U; + m_modemState = (DVM_STATE)m_buffer[4U]; - bool adcOverflow = (m_buffer[5U] & 0x02U) == 0x02U; - if (adcOverflow) { - //LogError(LOG_MODEM, "ADC levels have overflowed"); - m_adcOverFlowCount++; + m_tx = (m_buffer[5U] & 0x01U) == 0x01U; - if (m_adcOverFlowCount >= MAX_ADC_OVERFLOW / 2U) { - LogWarning(LOG_MODEM, "ADC overflow count > %u!", MAX_ADC_OVERFLOW / 2U); - } + bool adcOverflow = (m_buffer[5U] & 0x02U) == 0x02U; + if (adcOverflow) { + //LogError(LOG_MODEM, "ADC levels have overflowed"); + m_adcOverFlowCount++; + + if (m_adcOverFlowCount >= MAX_ADC_OVERFLOW / 2U) { + LogWarning(LOG_MODEM, "ADC overflow count > %u!", MAX_ADC_OVERFLOW / 2U); + } - if (!m_disableOFlowReset) { - if (m_adcOverFlowCount > MAX_ADC_OVERFLOW) { - LogError(LOG_MODEM, "ADC overflow count > %u, resetting modem", MAX_ADC_OVERFLOW); - forceModemReset = true; - } - } else { - m_adcOverFlowCount = 0U; + if (!m_disableOFlowReset) { + if (m_adcOverFlowCount > MAX_ADC_OVERFLOW) { + LogError(LOG_MODEM, "ADC overflow count > %u, resetting modem", MAX_ADC_OVERFLOW); + forceModemReset = true; } } else { - if (m_adcOverFlowCount != 0U) { - m_adcOverFlowCount--; - } + m_adcOverFlowCount = 0U; + } + } + else { + if (m_adcOverFlowCount != 0U) { + m_adcOverFlowCount--; } + } - bool rxOverflow = (m_buffer[5U] & 0x04U) == 0x04U; - if (rxOverflow) - LogError(LOG_MODEM, "RX buffer has overflowed"); + bool rxOverflow = (m_buffer[5U] & 0x04U) == 0x04U; + if (rxOverflow) + LogError(LOG_MODEM, "RX buffer has overflowed"); - bool txOverflow = (m_buffer[5U] & 0x08U) == 0x08U; - if (txOverflow) - LogError(LOG_MODEM, "TX buffer has overflowed"); + bool txOverflow = (m_buffer[5U] & 0x08U) == 0x08U; + if (txOverflow) + LogError(LOG_MODEM, "TX buffer has overflowed"); - m_lockout = (m_buffer[5U] & 0x10U) == 0x10U; + m_lockout = (m_buffer[5U] & 0x10U) == 0x10U; - bool dacOverflow = (m_buffer[5U] & 0x20U) == 0x20U; - if (dacOverflow) { - //LogError(LOG_MODEM, "DAC levels have overflowed"); - m_dacOverFlowCount++; + bool dacOverflow = (m_buffer[5U] & 0x20U) == 0x20U; + if (dacOverflow) { + //LogError(LOG_MODEM, "DAC levels have overflowed"); + m_dacOverFlowCount++; - if (m_dacOverFlowCount > MAX_DAC_OVERFLOW / 2U) { - LogWarning(LOG_MODEM, "DAC overflow count > %u!", MAX_DAC_OVERFLOW / 2U); - } + if (m_dacOverFlowCount > MAX_DAC_OVERFLOW / 2U) { + LogWarning(LOG_MODEM, "DAC overflow count > %u!", MAX_DAC_OVERFLOW / 2U); + } - if (!m_disableOFlowReset) { - if (m_dacOverFlowCount > MAX_DAC_OVERFLOW) { - LogError(LOG_MODEM, "DAC overflow count > %u, resetting modem", MAX_DAC_OVERFLOW); - forceModemReset = true; - } - } else { - m_dacOverFlowCount = 0U; + if (!m_disableOFlowReset) { + if (m_dacOverFlowCount > MAX_DAC_OVERFLOW) { + LogError(LOG_MODEM, "DAC overflow count > %u, resetting modem", MAX_DAC_OVERFLOW); + forceModemReset = true; } } else { - if (m_dacOverFlowCount != 0U) { - m_dacOverFlowCount--; - } + m_dacOverFlowCount = 0U; + } + } + else { + if (m_dacOverFlowCount != 0U) { + m_dacOverFlowCount--; } + } - m_cd = (m_buffer[5U] & 0x40U) == 0x40U; + m_cd = (m_buffer[5U] & 0x40U) == 0x40U; - m_dmrSpace1 = m_buffer[7U]; - m_dmrSpace2 = m_buffer[8U]; - m_p25Space = m_buffer[10U]; + m_dmrSpace1 = m_buffer[7U]; + m_dmrSpace2 = m_buffer[8U]; + m_p25Space = m_buffer[10U]; - m_inactivityTimer.start(); - } - break; + m_inactivityTimer.start(); + } + break; case CMD_GET_VERSION: case CMD_ACK: @@ -543,6 +588,7 @@ void Modem::clock(uint32_t ms) LogWarning(LOG_MODEM, "Unknown message, type = %02X", m_buffer[2U]); Utils::dump("Buffer dump", m_buffer, m_length); break; + } } } @@ -565,6 +611,34 @@ void Modem::clock(uint32_t ms) if (!m_playoutTimer.hasExpired()) return; + // read any data from the slave port for the air interface + if (m_remotePort != NULL) { + uint8_t buffer[BUFFER_LENGTH]; + ::memset(buffer, 0x00U, BUFFER_LENGTH); + + uint32_t ret = m_remotePort->read(buffer, BUFFER_LENGTH); + if (ret > 0) { + if (m_trace) + Utils::dump(1U, "RX Remote Data", (uint8_t*)buffer, ret); + + if (ret < 3U) { + LogError(LOG_MODEM, "Illegal length of remote data must be >3 bytes"); + Utils::dump("Buffer dump", buffer, ret); + return; + } + + uint8_t len = buffer[1U]; + int ret = m_port->write(buffer, len); + if (ret != int(len)) + LogError(LOG_MODEM, "Error writing remote data data"); + } + + m_playoutTimer.start(); + + return; + } + + // write DMR slot 1 data to air interface if (m_dmrSpace1 > 1U && !m_txDMRData1.isEmpty()) { uint8_t len = 0U; m_txDMRData1.getData(&len, 1U); @@ -573,7 +647,7 @@ void Modem::clock(uint32_t ms) if (m_trace) Utils::dump(1U, "TX DMR Data 1", m_buffer, len); - int ret = m_serial.write(m_buffer, len); + int ret = m_port->write(m_buffer, len); if (ret != int(len)) LogError(LOG_MODEM, "Error writing DMR slot 1 data"); @@ -582,6 +656,7 @@ void Modem::clock(uint32_t ms) m_dmrSpace1--; } + // write DMR slot 2 data to air interface if (m_dmrSpace2 > 1U && !m_txDMRData2.isEmpty()) { uint8_t len = 0U; m_txDMRData2.getData(&len, 1U); @@ -590,7 +665,7 @@ void Modem::clock(uint32_t ms) if (m_trace) Utils::dump(1U, "TX DMR Data 2", m_buffer, len); - int ret = m_serial.write(m_buffer, len); + int ret = m_port->write(m_buffer, len); if (ret != int(len)) LogError(LOG_MODEM, "Error writing DMR slot 2 data"); @@ -599,6 +674,7 @@ void Modem::clock(uint32_t ms) m_dmrSpace2--; } + // write P25 data to air interface if (m_p25Space > 1U && !m_txP25Data.isEmpty()) { uint8_t len = 0U; m_txP25Data.getData(&len, 1U); @@ -608,7 +684,7 @@ void Modem::clock(uint32_t ms) Utils::dump(1U, "TX P25 Data", m_buffer, len); } - int ret = m_serial.write(m_buffer, len); + int ret = m_port->write(m_buffer, len); if (ret != int(len)) LogError(LOG_MODEM, "Error writing P25 data"); @@ -619,12 +695,16 @@ void Modem::clock(uint32_t ms) } /// -/// Closes connection to the modem DSP. +/// Closes connection to the air interface modem. /// void Modem::close() { LogDebug(LOG_MODEM, "Closing the modem"); - m_serial.close(); + m_port->close(); + + if (m_remotePort != NULL) { + m_remotePort->close(); + } } /// @@ -715,43 +795,43 @@ bool Modem::hasP25Space() const } /// -/// Flag indicating whether or not the modem DSP is transmitting. +/// Flag indicating whether or not the air interface modem is transmitting. /// -/// True, if modem DSP is transmitting, otherwise false. +/// True, if air interface modem is transmitting, otherwise false. bool Modem::hasTX() const { return m_tx; } /// -/// Flag indicating whether or not the modem DSP has carrier detect. +/// Flag indicating whether or not the air interface modem has carrier detect. /// -/// True, if modem DSP has carrier detect, otherwise false. +/// True, if air interface modem has carrier detect, otherwise false. bool Modem::hasCD() const { return m_cd; } /// -/// Flag indicating whether or not the modem DSP is currently locked out. +/// Flag indicating whether or not the air interface modem is currently locked out. /// -/// True, if modem DSP is currently locked out, otherwise false. +/// True, if air interface modem is currently locked out, otherwise false. bool Modem::hasLockout() const { return m_lockout; } /// -/// Flag indicating whether or not the modem DSP is currently in an error condition. +/// Flag indicating whether or not the air interface modem is currently in an error condition. /// -/// True, if the modem DSP is current in an error condition, otherwise false. +/// True, if the air interface modem is current in an error condition, otherwise false. bool Modem::hasError() const { return m_error; } /// -/// Clears any buffered DMR Slot 1 frame data to be sent to the modem DSP. +/// Clears any buffered DMR Slot 1 frame data to be sent to the air interface modem. /// void Modem::clearDMRData1() { @@ -761,7 +841,7 @@ void Modem::clearDMRData1() } /// -/// Clears any buffered DMR Slot 2 frame data to be sent to the modem DSP. +/// Clears any buffered DMR Slot 2 frame data to be sent to the air interface modem. /// void Modem::clearDMRData2() { @@ -771,7 +851,7 @@ void Modem::clearDMRData2() } /// -/// Clears any buffered P25 frame data to be sent to the modem DSP. +/// Clears any buffered P25 frame data to be sent to the air interface modem. /// void Modem::clearP25Data() { @@ -786,12 +866,12 @@ void Modem::clearP25Data() // Utils::dump(1U, "Written", buffer, 3U); - m_serial.write(buffer, 3U); + m_port->write(buffer, 3U); } } /// -/// Internal helper to inject DMR Slot 1 frame data as if it came from the modem DSP. +/// Internal helper to inject DMR Slot 1 frame data as if it came from the air interface modem. /// /// Data to write to ring buffer. /// Length of data to write. @@ -815,7 +895,7 @@ void Modem::injectDMRData1(const uint8_t* data, uint32_t length) } /// -/// Internal helper to inject DMR Slot 2 frame data as if it came from the modem DSP. +/// Internal helper to inject DMR Slot 2 frame data as if it came from the air interface modem. /// /// Data to write to ring buffer. /// Length of data to write. @@ -839,7 +919,7 @@ void Modem::injectDMRData2(const uint8_t* data, uint32_t length) } /// -/// Internal helper to inject P25 frame data as if it came from the modem DSP. +/// Internal helper to inject P25 frame data as if it came from the air interface modem. /// /// Data to write to ring buffer. /// Length of data to write. @@ -970,11 +1050,11 @@ bool Modem::writeDMRStart(bool tx) // Utils::dump(1U, "Written", buffer, 4U); - return m_serial.write(buffer, 4U) == 4; + return m_port->write(buffer, 4U) == 4; } /// -/// Writes a DMR short LC to the modem DSP. +/// Writes a DMR short LC to the air interface modem. /// /// /// True, if DMR LC is written, otherwise false. @@ -999,11 +1079,11 @@ bool Modem::writeDMRShortLC(const uint8_t* lc) // Utils::dump(1U, "Written", buffer, 12U); - return m_serial.write(buffer, 12U) == 12; + return m_port->write(buffer, 12U) == 12; } /// -/// Writes a DMR abort message for the given slot to the modem DSP. +/// Writes a DMR abort message for the given slot to the air interface modem. /// /// DMR slot to write abort for. /// True, if DMR abort is written, otherwise false. @@ -1023,11 +1103,11 @@ bool Modem::writeDMRAbort(uint32_t slotNo) // Utils::dump(1U, "Written", buffer, 4U); - return m_serial.write(buffer, 4U) == 4; + return m_port->write(buffer, 4U) == 4; } /// -/// Sets the current operating mode for the modem DSP. +/// Sets the current operating mode for the air interface modem. /// /// /// @@ -1042,7 +1122,7 @@ bool Modem::setMode(DVM_STATE state) // Utils::dump(1U, "Written", buffer, 4U); - return m_serial.write(buffer, 4U) == 4; + return m_port->write(buffer, 4U) == 4; } /// @@ -1071,42 +1151,14 @@ bool Modem::sendCWId(const std::string& callsign) Utils::dump(1U, "CW ID Data", buffer, length + 3U); } - return m_serial.write(buffer, length + 3U) == int(length + 3U); -} - -/// -/// Helper to create an instance of the Modem class. -/// -/// Serial port the modem DSP is connected to. -/// Flag indicating the modem is operating in duplex mode. -/// Flag indicating the Rx polarity should be inverted. -/// Flag indicating the Tx polarity should be inverted. -/// Flag indicating the PTT polarity should be inverted. -/// Flag indicating whether the DSP DC-level blocking should be enabled. -/// Flag indicating whether the COS signal should be used to lockout the modem. -/// Count of FDMA preambles to transmit before data. (P25/DMR DMO) -/// Compensate for delay in receiver audio chain in ms. Usually DSP based. -/// P25 Correlation Countdown. -/// Length of time in MS between packets to send to modem. -/// Flag indicating whether the ADC/DAC overflow reset logic is disabled. -/// Flag indicating whether modem DSP trace is enabled. -/// Flag indicating whether modem DSP debug is enabled. -Modem* Modem::createModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker, - bool cosLockout, uint8_t fdmaPreamble, uint8_t dmrRxDelay, uint8_t p25CorrCount, uint8_t packetPlayoutTime, bool disableOFlowReset, bool trace, bool debug) -{ - if (port == NULL_MODEM) { - return new NullModem(port, duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, fdmaPreamble, dmrRxDelay, p25CorrCount, packetPlayoutTime, disableOFlowReset, trace, debug); - } - else { - return new Modem(port, duplex, rxInvert, txInvert, pttInvert, dcBlocker, cosLockout, fdmaPreamble, dmrRxDelay, p25CorrCount, packetPlayoutTime, disableOFlowReset, trace, debug); - } + return m_port->write(buffer, length + 3U) == int(length + 3U); } // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- /// -/// Retrieve the modem DSP version. +/// Retrieve the air interface modem version. /// /// bool Modem::getFirmwareVersion() @@ -1122,7 +1174,7 @@ bool Modem::getFirmwareVersion() // Utils::dump(1U, "Written", buffer, 3U); - int ret = m_serial.write(buffer, 3U); + int ret = m_port->write(buffer, 3U); if (ret != 3) return false; @@ -1145,6 +1197,9 @@ bool Modem::getFirmwareVersion() case 2U: LogMessage(LOG_MODEM, "ST-Micro ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[5U], m_buffer[6U], m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U]); break; + case 15U: + LogMessage(LOG_MODEM, "Null Modem, UDID: N/A"); + break; default: LogMessage(LOG_MODEM, "Unknown CPU type: %u", m_buffer[4U]); break; @@ -1167,7 +1222,7 @@ bool Modem::getFirmwareVersion() } /// -/// Retrieve the current status from the modem DSP. +/// Retrieve the current status from the air interface modem. /// /// bool Modem::getStatus() @@ -1180,11 +1235,11 @@ bool Modem::getStatus() // Utils::dump(1U, "Written", buffer, 3U); - return m_serial.write(buffer, 3U) == 3; + return m_port->write(buffer, 3U) == 3; } /// -/// Write configuration to the modem DSP. +/// Write configuration to the air interface modem. /// /// bool Modem::writeConfig() @@ -1248,7 +1303,7 @@ bool Modem::writeConfig() // Utils::dump(1U, "Written", buffer, 17U); - int ret = m_serial.write(buffer, 17U); + int ret = m_port->write(buffer, 17U); if (ret != 17) return false; @@ -1280,7 +1335,7 @@ bool Modem::writeConfig() } /// -/// Write symbol level adjustments to the modem DSP. +/// Write symbol level adjustments to the air interface modem. /// /// True, if level adjustments are written, otherwise false. bool Modem::writeSymbolAdjust() @@ -1297,7 +1352,7 @@ bool Modem::writeSymbolAdjust() buffer[5U] = (uint8_t)(m_p25SymLevel3Adj + 128); buffer[6U] = (uint8_t)(m_p25SymLevel1Adj + 128); - int ret = m_serial.write(buffer, 7U); + int ret = m_port->write(buffer, 7U); if (ret <= 0) return false; @@ -1368,7 +1423,7 @@ RESP_TYPE_DVM Modem::getResponse() { if (m_offset == 0U) { // Get the start of the frame or nothing at all - int ret = m_serial.read(m_buffer + 0U, 1U); + int ret = m_port->read(m_buffer + 0U, 1U); if (ret < 0) { LogError(LOG_MODEM, "Error reading from the modem, ret = %d", ret); return RTM_ERROR; @@ -1385,7 +1440,7 @@ RESP_TYPE_DVM Modem::getResponse() if (m_offset == 1U) { // Get the length of the frame - int ret = m_serial.read(m_buffer + 1U, 1U); + int ret = m_port->read(m_buffer + 1U, 1U); if (ret < 0) { LogError(LOG_MODEM, "Error reading from the modem, ret = %d", ret); m_offset = 0U; @@ -1407,7 +1462,7 @@ RESP_TYPE_DVM Modem::getResponse() if (m_offset == 2U) { // Get the frame type - int ret = m_serial.read(m_buffer + 2U, 1U); + int ret = m_port->read(m_buffer + 2U, 1U); if (ret < 0) { LogError(LOG_MODEM, "Error reading from the modem, ret = %d", ret); m_offset = 0U; @@ -1423,7 +1478,7 @@ RESP_TYPE_DVM Modem::getResponse() if (m_offset >= 3U) { // Use later two byte length field if (m_length == 0U) { - int ret = m_serial.read(m_buffer + 3U, 2U); + int ret = m_port->read(m_buffer + 3U, 2U); if (ret < 0) { LogError(LOG_MODEM, "Error reading from the modem, ret = %d", ret); m_offset = 0U; @@ -1438,7 +1493,7 @@ RESP_TYPE_DVM Modem::getResponse() } while (m_offset < m_length) { - int ret = m_serial.read(m_buffer + m_offset, m_length - m_offset); + int ret = m_port->read(m_buffer + m_offset, m_length - m_offset); if (ret < 0) { LogError(LOG_MODEM, "Error reading from the modem, ret = %d", ret); m_offset = 0U; diff --git a/modem/Modem.h b/modem/Modem.h index 861c97d7..94046ffb 100644 --- a/modem/Modem.h +++ b/modem/Modem.h @@ -11,7 +11,7 @@ // Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) // /* -* Copyright (C) 2011-2017 by Jonathan Naylor G4KLX +* Copyright (C) 2011-2021 by Jonathan Naylor G4KLX * Copyright (C) 2017-2018 by Bryan Biedenkapp N2PLL * * This program is free software; you can redistribute it and/or modify @@ -32,7 +32,7 @@ #define __MODEM_H__ #include "Defines.h" -#include "modem/SerialController.h" +#include "modem/port/IModemPort.h" #include "RingBuffer.h" #include "Timer.h" @@ -166,99 +166,100 @@ namespace modem class HOST_SW_API Modem { public: /// Initializes a new instance of the Modem class. - Modem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker, + Modem(port::IModemPort* port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker, bool cosLockout, uint8_t fdmaPreamble, uint8_t dmrRxDelay, uint8_t p25CorrCount, uint8_t packetPlayoutTime, bool disableOFlowReset, bool trace, bool debug); /// Finalizes a instance of the Modem class. - virtual ~Modem(); - - /// Sets the modem DSP RF DC offset parameters. - virtual void setDCOffsetParams(int txDCOffset, int rxDCOffset); - /// Sets the modem DSP enabled modes. - virtual void setModeParams(bool dmrEnabled, bool p25Enabled); - /// Sets the modem DSP RF deviation levels. - virtual void setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float p25TXLevel); - /// Sets the modem DSP Symbol adjustment levels. - virtual void setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25SymLevel3Adj, int p25SymLevel1Adj); - /// Sets the modem DSP DMR color code. - virtual void setDMRColorCode(uint32_t colorCode); - /// Sets the modem DSP P25 NAC. - virtual void setP25NAC(uint32_t nac); - /// Sets the modem DSP RF receive deviation levels. - virtual void setRXLevel(float rxLevel); - - /// Opens connection to the modem DSP. - virtual bool open(); + ~Modem(); + + /// Sets the RF DC offset parameters. + void setDCOffsetParams(int txDCOffset, int rxDCOffset); + /// Sets the enabled modes. + void setModeParams(bool dmrEnabled, bool p25Enabled); + /// Sets the RF deviation levels. + void setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float p25TXLevel); + /// Sets the symbol adjustment levels. + void setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25SymLevel3Adj, int p25SymLevel1Adj); + /// Sets the DMR color code. + void setDMRColorCode(uint32_t colorCode); + /// Sets the P25 NAC. + void setP25NAC(uint32_t nac); + /// Sets the RF receive deviation levels. + void setRXLevel(float rxLevel); + /// Sets the slave port for remote operation. + void setSlavePort(port::IModemPort* slavePort); + + /// Opens connection to the air interface modem. + bool open(); /// Updates the modem by the passed number of milliseconds. - virtual void clock(uint32_t ms); + void clock(uint32_t ms); - /// Closes connection to the modem DSP. - virtual void close(); + /// Closes connection to the air interface modem. + void close(); /// Reads DMR Slot 1 frame data from the DMR Slot 1 ring buffer. - virtual uint32_t readDMRData1(uint8_t* data); + uint32_t readDMRData1(uint8_t* data); /// Reads DMR Slot 2 frame data from the DMR Slot 1 ring buffer. - virtual uint32_t readDMRData2(uint8_t* data); + uint32_t readDMRData2(uint8_t* data); /// Reads P25 frame data from the P25 ring buffer. - virtual uint32_t readP25Data(uint8_t* data); + uint32_t readP25Data(uint8_t* data); /// Helper to test if the DMR Slot 1 ring buffer has free space. - virtual bool hasDMRSpace1() const; + bool hasDMRSpace1() const; /// Helper to test if the DMR Slot 2 ring buffer has free space. - virtual bool hasDMRSpace2() const; + bool hasDMRSpace2() const; /// Helper to test if the P25 ring buffer has free space. - virtual bool hasP25Space() const; + bool hasP25Space() const; - /// Flag indicating whether or not the modem DSP is transmitting. - virtual bool hasTX() const; - /// Flag indicating whether or not the modem DSP has carrier detect. - virtual bool hasCD() const; + /// Flag indicating whether or not the air interface modem is transmitting. + bool hasTX() const; + /// Flag indicating whether or not the air interface modem has carrier detect. + bool hasCD() const; - /// Flag indicating whether or not the modem DSP is currently locked out. - virtual bool hasLockout() const; - /// Flag indicating whether or not the modem DSP is currently in an error condition. - virtual bool hasError() const; + /// Flag indicating whether or not the air interface modem is currently locked out. + bool hasLockout() const; + /// Flag indicating whether or not the air interface modem is currently in an error condition. + bool hasError() const; - /// Clears any buffered DMR Slot 1 frame data to be sent to the modem DSP. + /// Clears any buffered DMR Slot 1 frame data to be sent to the air interface modem. void clearDMRData1(); - /// Clears any buffered DMR Slot 2 frame data to be sent to the modem DSP. + /// Clears any buffered DMR Slot 2 frame data to be sent to the air interface modem. void clearDMRData2(); - /// Clears any buffered P25 frame data to be sent to the modem DSP. + /// Clears any buffered P25 frame data to be sent to the air interface modem. void clearP25Data(); - /// Internal helper to inject DMR Slot 1 frame data as if it came from the modem DSP. + /// Internal helper to inject DMR Slot 1 frame data as if it came from the air interface modem. void injectDMRData1(const uint8_t* data, uint32_t length); - /// Internal helper to inject DMR Slot 2 frame data as if it came from the modem DSP. + /// Internal helper to inject DMR Slot 2 frame data as if it came from the air interface modem. void injectDMRData2(const uint8_t* data, uint32_t length); - /// Internal helper to inject P25 frame data as if it came from the modem DSP. + /// Internal helper to inject P25 frame data as if it came from the air interface modem. void injectP25Data(const uint8_t* data, uint32_t length); /// Writes DMR Slot 1 frame data to the DMR Slot 1 ring buffer. - virtual bool writeDMRData1(const uint8_t* data, uint32_t length); + bool writeDMRData1(const uint8_t* data, uint32_t length); /// Writes DMR Slot 2 frame data to the DMR Slot 2 ring buffer. - virtual bool writeDMRData2(const uint8_t* data, uint32_t length); + bool writeDMRData2(const uint8_t* data, uint32_t length); /// Writes P25 frame data to the P25 ring buffer. - virtual bool writeP25Data(const uint8_t* data, uint32_t length); + bool writeP25Data(const uint8_t* data, uint32_t length); /// Triggers the start of DMR transmit. - virtual bool writeDMRStart(bool tx); - /// Writes a DMR short LC to the modem DSP. - virtual bool writeDMRShortLC(const uint8_t* lc); - /// Writes a DMR abort message for the given slot to the modem DSP. - virtual bool writeDMRAbort(uint32_t slotNo); + bool writeDMRStart(bool tx); + /// Writes a DMR short LC to the air interface modem. + bool writeDMRShortLC(const uint8_t* lc); + /// Writes a DMR abort message for the given slot to the air interface modem. + bool writeDMRAbort(uint32_t slotNo); - /// Sets the current operating mode for the modem DSP. - virtual bool setMode(DVM_STATE state); + /// Gets the current operating mode for the air interface modem. + DVM_STATE getMode() const; + /// Sets the current operating mode for the air interface modem. + bool setMode(DVM_STATE state); /// Transmits the given string as CW morse. - virtual bool sendCWId(const std::string& callsign); - - /// Helper to create an instance of the Modem class. - static Modem* createModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker, - bool cosLockout, uint8_t fdmaPreamble, uint8_t dmrRxDelay, uint8_t p25CorrCount, uint8_t packetPlayoutTime, bool disableOFlowReset, bool trace, bool debug); + bool sendCWId(const std::string& callsign); private: - std::string m_port; + port::IModemPort* m_port; + port::IModemPort* m_remotePort; uint32_t m_dmrColorCode; uint32_t m_p25NAC; @@ -298,7 +299,8 @@ namespace modem uint32_t m_adcOverFlowCount; uint32_t m_dacOverFlowCount; - CSerialController m_serial; + DVM_STATE m_modemState; + uint8_t* m_buffer; uint32_t m_length; uint32_t m_offset; @@ -323,13 +325,13 @@ namespace modem bool m_lockout; bool m_error; - /// Retrieve the modem DSP version. + /// Retrieve the air interface modem version. bool getFirmwareVersion(); - /// Retrieve the current status from the modem DSP. + /// Retrieve the current status from the air interface modem. bool getStatus(); - /// Write configuration to the modem DSP. + /// Write configuration to the air interface modem. bool writeConfig(); - /// Write symbol level adjustments to the modem DSP. + /// Write symbol level adjustments to the air interface modem. bool writeSymbolAdjust(); /// diff --git a/modem/port/IModemPort.cpp b/modem/port/IModemPort.cpp new file mode 100644 index 00000000..624fddce --- /dev/null +++ b/modem/port/IModemPort.cpp @@ -0,0 +1,43 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2016,2021 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include "modem/port/IModemPort.h" + +using namespace modem::port; + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- +/// +/// Finalizes a instance of the IModemPort class. +/// +IModemPort::~IModemPort() +{ + /* stub */ +} diff --git a/modem/port/IModemPort.h b/modem/port/IModemPort.h new file mode 100644 index 00000000..e4b8bfa7 --- /dev/null +++ b/modem/port/IModemPort.h @@ -0,0 +1,63 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2016,2021 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#if !defined(__I_MODEM_PORT_H__) +#define __I_MODEM_PORT_H__ + +#include "Defines.h" + +namespace modem +{ + namespace port + { + // --------------------------------------------------------------------------- + // Class Declaration + // Defines a "port" the modem is connected to. + // --------------------------------------------------------------------------- + + class HOST_SW_API IModemPort { + public: + /// Finalizes a instance of the IModemPort class. + virtual ~IModemPort() = 0; + + /// Opens a connection to the port. + virtual bool open() = 0; + + /// Reads data from the port. + virtual int read(uint8_t* buffer, uint32_t length) = 0; + /// Writes data to the port. + virtual int write(const uint8_t* buffer, uint32_t length) = 0; + + /// Closes the connection to the port. + virtual void close() = 0; + }; + } // namespace port +} // namespace modem + +#endif // __I_MODEM_PORT_H__ diff --git a/modem/port/ISerialPort.cpp b/modem/port/ISerialPort.cpp new file mode 100644 index 00000000..a5afee5d --- /dev/null +++ b/modem/port/ISerialPort.cpp @@ -0,0 +1,43 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2016 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include "modem/port/ISerialPort.h" + +using namespace modem::port; + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- +/// +/// Finalizes a instance of the ISerialPort class. +/// +ISerialPort::~ISerialPort() +{ + /* stub */ +} diff --git a/modem/port/ISerialPort.h b/modem/port/ISerialPort.h new file mode 100644 index 00000000..d5d65cab --- /dev/null +++ b/modem/port/ISerialPort.h @@ -0,0 +1,63 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2016,2021 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#if !defined(__I_SERIAL_PORT_H__) +#define __I_SERIAL_PORT_H__ + +#include "Defines.h" + +namespace modem +{ + namespace port + { + // --------------------------------------------------------------------------- + // Class Declaration + // Defines a serial port. + // --------------------------------------------------------------------------- + + class HOST_SW_API ISerialPort { + public: + /// Finalizes a instance of the ISerialPort class. + virtual ~ISerialPort() = 0; + + /// Opens a connection to the port. + virtual bool open() = 0; + + /// Reads data from the port. + virtual int read(uint8_t* buffer, uint32_t length) = 0; + /// Writes data to the port. + virtual int write(const uint8_t* buffer, uint32_t length) = 0; + + /// Closes the connection to the port. + virtual void close() = 0; + }; + } // namespace port +} // namespace modem + +#endif // __I_SERIAL_PORT_H__ diff --git a/modem/port/ModemNullPort.cpp b/modem/port/ModemNullPort.cpp new file mode 100644 index 00000000..cced6df2 --- /dev/null +++ b/modem/port/ModemNullPort.cpp @@ -0,0 +1,197 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2021 by Jonathan Naylor G4KLX +* Copyright (C) 2021 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "modem/port/ModemNullPort.h" +#include "modem/Modem.h" + +using namespace modem::port; +using namespace modem; + +#include +#include + +const char* HARDWARE = "Null Modem Controller"; + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- +/// +/// Initializes a new instance of the ModemNullPort class. +/// +ModemNullPort::ModemNullPort() : + m_buffer(200U, "Null Controller Buffer") +{ + /* stub */ +} + +/// +/// Finalizes a instance of the ModemNullPort class. +/// +ModemNullPort::~ModemNullPort() +{ +} + +/// +/// Opens a connection to the port. +/// +/// True, if connection is opened, otherwise false. +bool ModemNullPort::open() +{ + return true; +} + +/// +/// Reads data from the port. +/// +/// Buffer to read data from the serial port to. +/// Length of data to read from the serial port. +/// Actual length of data read from serial port. +int ModemNullPort::read(uint8_t* buffer, uint32_t length) +{ + uint32_t dataSize = m_buffer.dataSize(); + if (dataSize == 0U) + return 0; + + if (length > dataSize) + length = dataSize; + + m_buffer.getData(buffer, length); + + return int(length); +} + +/// +/// Writes data to the serial port. +/// +/// Buffer containing data to write to serial port. +/// Length of data to write to serial port. +/// Actual length of data written to the serial port. +int ModemNullPort::write(const uint8_t* buffer, uint32_t length) +{ + switch (buffer[2U]) { + case CMD_GET_VERSION: + getVersion(); + break; + case CMD_GET_STATUS: + getStatus(); + break; + case CMD_SET_CONFIG: + case CMD_SET_MODE: + writeAck(buffer[2U]); + break; + default: + break; + } + + return int(length); +} + +/// +/// Closes the connection to the port. +/// +void ModemNullPort::close() +{ + /* stub */ +} + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- +/// +/// +/// +void ModemNullPort::getVersion() +{ + unsigned char reply[200U]; + + reply[0U] = DVM_FRAME_START; + reply[1U] = 0U; + reply[2U] = CMD_GET_VERSION; + + reply[3U] = PROTOCOL_VERSION; + reply[4U] = 15U; + + // Reserve 16 bytes for the UDID + ::memset(reply + 5U, 0x00U, 16U); + + uint8_t count = 21U; + for (uint8_t i = 0U; HARDWARE[i] != 0x00U; i++, count++) + reply[count] = HARDWARE[i]; + + reply[1U] = count; + + m_buffer.addData(reply, count); +} + +/// +/// +/// +void ModemNullPort::getStatus() +{ + unsigned char reply[15U]; + + // Send all sorts of interesting internal values + reply[0U] = DVM_FRAME_START; + reply[1U] = 11U; + reply[2U] = CMD_GET_STATUS; + + reply[3U] = 0U; + + reply[4U] = 0x00U; + + reply[5U] = 0x00U; + + reply[6U] = 20U; + + reply[7U] = 20U; + reply[8U] = 20U; + + reply[9U] = 0U; + + reply[10U] = 20U; + + m_buffer.addData(reply, 11U); +} + +/// +/// +/// +void ModemNullPort::writeAck(uint8_t type) +{ + unsigned char reply[4U]; + + reply[0U] = DVM_FRAME_START; + reply[1U] = 4U; + reply[2U] = CMD_ACK; + reply[3U] = type; + + m_buffer.addData(reply, 4U); +} diff --git a/modem/port/ModemNullPort.h b/modem/port/ModemNullPort.h new file mode 100644 index 00000000..987b4254 --- /dev/null +++ b/modem/port/ModemNullPort.h @@ -0,0 +1,79 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2021 by Jonathan Naylor G4KLX +* Copyright (C) 2021 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#if !defined(__MODEM_NULL_PORT_H__) +#define __MODEM_NULL_PORT_H__ + +#include "Defines.h" +#include "modem/port/IModemPort.h" +#include "RingBuffer.h" + +namespace modem +{ + namespace port + { + // --------------------------------------------------------------------------- + // Class Declaration + // This class implements low-level routines that represent a "null" + // modem port. + // --------------------------------------------------------------------------- + + class HOST_SW_API ModemNullPort : public IModemPort { + public: + /// Initializes a new instance of the ModemNullPort class. + ModemNullPort(); + /// Finalizes a instance of the ModemNullPort class. + virtual ~ModemNullPort(); + + /// Opens a connection to the port. + virtual bool open(); + + /// Reads data from the port. + virtual int read(uint8_t* buffer, uint32_t length); + /// Writes data to the port. + virtual int write(const uint8_t* buffer, uint32_t length); + + /// Closes the connection to the port. + virtual void close(); + + private: + RingBuffer m_buffer; + + /// + void getVersion(); + /// + void getStatus(); + /// + void writeAck(uint8_t type); + }; + } // namespace port +} // namespace modem + +#endif // __MODEM_NULL_PORT_H__ diff --git a/modem/port/UARTPort.cpp b/modem/port/UARTPort.cpp new file mode 100644 index 00000000..9233d03d --- /dev/null +++ b/modem/port/UARTPort.cpp @@ -0,0 +1,605 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2002-2004,2007-2011,2013,2014-2017,2019,2020,2021 by Jonathan Naylor G4KLX +* Copyright (C) 1999-2001 by Thomas Sailor HB9JNX +* Copyright (C) 2020-2021 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include "Defines.h" +#include "modem/port/UARTPort.h" +#include "Log.h" + +using namespace modem::port; + +#include +#include + +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- + +#if defined(_WIN32) || defined(_WIN64) +/// +/// Initializes a new instance of the UARTPort class. +/// +/// Serial port device. +/// Serial port speed. +/// +UARTPort::UARTPort(const std::string& device, SERIAL_SPEED speed, bool assertRTS) : + m_device(device), + m_speed(speed), + m_assertRTS(assertRTS), + m_handle(INVALID_HANDLE_VALUE) +{ + assert(!device.empty()); +} + +/// +/// Finalizes a instance of the UARTPort class. +/// +UARTPort::~UARTPort() +{ + /* stub */ +} + +/// +/// Opens a connection to the serial port. +/// +/// True, if connection is opened, otherwise false. +bool UARTPort::open() +{ + assert(m_handle == INVALID_HANDLE_VALUE); + + DWORD errCode; + + // Convert "\\.\COM10" to "COM10" + std::string baseName = m_device.substr(4U); + + m_handle = ::CreateFileA(m_device.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (m_handle == INVALID_HANDLE_VALUE) { + ::LogError(LOG_HOST, "Cannot open device - %s, err=%04lx", m_device.c_str(), ::GetLastError()); + return false; + } + + DCB dcb; + if (::GetCommState(m_handle, &dcb) == 0) { + ::LogError(LOG_HOST, "Cannot get the attributes for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + dcb.BaudRate = DWORD(m_speed); + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.fParity = FALSE; + dcb.StopBits = ONESTOPBIT; + dcb.fInX = FALSE; + dcb.fOutX = FALSE; + dcb.fOutxCtsFlow = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fDsrSensitivity = FALSE; + dcb.fDtrControl = DTR_CONTROL_DISABLE; + dcb.fRtsControl = RTS_CONTROL_DISABLE; + + if (::SetCommState(m_handle, &dcb) == 0) { + ::LogError(LOG_HOST, "Cannot set the attributes for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + COMMTIMEOUTS timeouts; + if (!::GetCommTimeouts(m_handle, &timeouts)) { + ::LogError(LOG_HOST, "Cannot get the timeouts for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = 0UL; + timeouts.ReadTotalTimeoutConstant = 0UL; + + if (!::SetCommTimeouts(m_handle, &timeouts)) { + ::LogError(LOG_HOST, "Cannot set the timeouts for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + if (::EscapeCommFunction(m_handle, CLRDTR) == 0) { + ::LogError(LOG_HOST, "Cannot clear DTR for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + if (::EscapeCommFunction(m_handle, m_assertRTS ? SETRTS : CLRRTS) == 0) { + ::LogError(LOG_HOST, "Cannot set/clear RTS for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + ::ClearCommError(m_handle, &errCode, NULL); + ::CloseHandle(m_handle); + return false; + } + + ::ClearCommError(m_handle, &errCode, NULL); + + return true; +} + +/// +/// Reads data from the serial port. +/// +/// Buffer to read data from the serial port to. +/// Length of data to read from the serial port. +/// Actual length of data read from serial port. +int UARTPort::read(uint8_t* buffer, uint32_t length) +{ + assert(m_handle != INVALID_HANDLE_VALUE); + assert(buffer != NULL); + + uint32_t ptr = 0U; + + while (ptr < length) { + int ret = readNonblock(buffer + ptr, length - ptr); + if (ret < 0) { + return ret; + } + else if (ret == 0) { + if (ptr == 0U) + return 0; + } + else { + ptr += ret; + } + } + + return int(length); +} + +/// +/// Writes data to the serial port. +/// +/// Buffer containing data to write to serial port. +/// Length of data to write to serial port. +/// Actual length of data written to the serial port. +int UARTPort::write(const uint8_t* buffer, uint32_t length) +{ + assert(m_handle != INVALID_HANDLE_VALUE); + assert(buffer != NULL); + + if (length == 0U) + return 0; + + uint32_t ptr = 0U; + + while (ptr < length) { + DWORD bytes = 0UL; + BOOL ret = ::WriteFile(m_handle, buffer + ptr, length - ptr, &bytes, NULL); + if (!ret) { + ::LogError(LOG_HOST, "Error from WriteFile for %s: %04lx", m_device.c_str(), ::GetLastError()); + return -1; + } + + ptr += bytes; + } + + return int(length); +} + +/// +/// Closes the connection to the serial port. +/// +void UARTPort::close() +{ + assert(m_handle != INVALID_HANDLE_VALUE); + + ::CloseHandle(m_handle); + m_handle = INVALID_HANDLE_VALUE; +} + +#else + +/// +/// Initializes a new instance of the UARTPort class. +/// +/// Serial port device. +/// Serial port speed. +/// +UARTPort::UARTPort(const std::string& device, SERIAL_SPEED speed, bool assertRTS) : + m_device(device), + m_speed(speed), + m_assertRTS(assertRTS), + m_fd(-1) +{ + assert(!device.empty()); +} + +/// +/// Finalizes a instance of the UARTPort class. +/// +UARTPort::~UARTPort() +{ +} + +/// +/// Opens a connection to the serial port. +/// +/// True, if connection is opened, otherwise false. +bool UARTPort::open() +{ + assert(m_fd == -1); + +#if defined(__APPLE__) + m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); /*open in block mode under OSX*/ +#else + m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0); +#endif + if (m_fd < 0) { + ::LogError(LOG_HOST, "Cannot open device - %s", m_device.c_str()); + return false; + } + + if (::isatty(m_fd)) + return setRaw(); + + return true; +} + +/// +/// Reads data from the serial port. +/// +/// Buffer to read data from the serial port to. +/// Length of data to read from the serial port. +/// Actual length of data read from serial port. +int UARTPort::read(uint8_t* buffer, uint32_t length) +{ + assert(buffer != NULL); + assert(m_fd != -1); + + if (length == 0U) + return 0; + + uint32_t offset = 0U; + + while (offset < length) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(m_fd, &fds); + int n; + if (offset == 0U) { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + n = ::select(m_fd + 1, &fds, NULL, NULL, &tv); + if (n == 0) + return 0; + } + else { + n = ::select(m_fd + 1, &fds, NULL, NULL, NULL); + } + + if (n < 0) { + ::LogError(LOG_HOST, "Error from select(), errno=%d", errno); + return -1; + } + + if (n > 0) { + ssize_t len = ::read(m_fd, buffer + offset, length - offset); + if (len < 0) { + if (errno != EAGAIN) { + ::LogError(LOG_HOST, "Error from read(), errno=%d", errno); + return -1; + } + } + + if (len > 0) + offset += len; + } + } + + return length; +} + +/// +/// Writes data to the serial port. +/// +/// Buffer containing data to write to serial port. +/// Length of data to write to serial port. +/// Actual length of data written to the serial port. +int UARTPort::write(const uint8_t* buffer, uint32_t length) +{ + assert(buffer != NULL); + assert(m_fd != -1); + + if (length == 0U) + return 0; + + uint32_t ptr = 0U; + while (ptr < length) { + ssize_t n = 0U; + if (canWrite()) + n = ::write(m_fd, buffer + ptr, length - ptr); + if (n < 0) { + if (errno != EAGAIN) { + ::LogError(LOG_HOST, "Error returned from write(), errno=%d", errno); + return -1; + } + } + + if (n > 0) + ptr += n; + } + + return length; +} + +/// +/// Closes the connection to the serial port. +/// +void UARTPort::close() +{ + assert(m_fd != -1); + + ::close(m_fd); + m_fd = -1; +} + +#if defined(__APPLE__) +/// +/// +/// +/// +int UARTPort::setNonblock(bool nonblock) +{ + int flag = ::fcntl(m_fd, F_GETFD, 0); + + if (nonblock) + flag |= O_NONBLOCK; + else + flag &= ~O_NONBLOCK; + + return ::fcntl(m_fd, F_SETFL, flag); +} +#endif +#endif + +// --------------------------------------------------------------------------- +// Private Class Members +// --------------------------------------------------------------------------- + +#if defined(_WIN32) || defined(_WIN64) +/// +/// Initializes a new instance of the UARTPort class. +/// +/// Serial port speed. +/// +UARTPort::UARTPort(SERIAL_SPEED speed, bool assertRTS) : + m_device(), + m_speed(speed), + m_assertRTS(assertRTS), + m_handle(INVALID_HANDLE_VALUE) +{ + /* stub */ +} + +/// +/// +/// +/// +/// +/// +int UARTPort::readNonblock(uint8_t* buffer, uint32_t length) +{ + assert(m_handle != INVALID_HANDLE_VALUE); + assert(buffer != NULL); + + if (length == 0U) + return 0; + + DWORD errors; + COMSTAT status; + if (::ClearCommError(m_handle, &errors, &status) == 0) { + ::LogError(LOG_HOST, "Error from ClearCommError for %s, err=%04lx", m_device.c_str(), ::GetLastError()); + return -1; + } + + if (status.cbInQue == 0UL) + return 0; + + DWORD readLength = status.cbInQue; + if (length < readLength) + readLength = length; + + DWORD bytes = 0UL; + BOOL ret = ::ReadFile(m_handle, buffer, readLength, &bytes, NULL); + if (!ret) { + ::LogError(LOG_HOST, "Error from ReadFile for %s: %04lx", m_device.c_str(), ::GetLastError()); + return -1; + } + + return int(bytes); +} + +#else + +/// +/// Initializes a new instance of the UARTPort class. +/// +/// Serial port speed. +/// +UARTPort::UARTPort(SERIAL_SPEED speed, bool assertRTS) : + m_device(), + m_speed(speed), + m_assertRTS(assertRTS), + m_fd(-1) +{ + /* stub */ +} + +/// +/// +/// +/// +bool UARTPort::canWrite() +{ +#if defined(__APPLE__) + fd_set wset; + FD_ZERO(&wset); + FD_SET(m_fd, &wset); + + struct timeval timeo; + timeo.tv_sec = 0; + timeo.tv_usec = 0; + + int rc = ::select(m_fd + 1, NULL, &wset, NULL, &timeo); + if (rc > 0 && FD_ISSET(m_fd, &wset)) + return true; + + return false; +#else + return true; +#endif +} + +/// +/// +/// +/// +bool UARTPort::setRaw() +{ + termios termios; + if (::tcgetattr(m_fd, &termios) < 0) { + ::LogError(LOG_HOST, "Cannot get the attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + + termios.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK); + termios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL); + termios.c_iflag &= ~(IXON | IXOFF | IXANY); + termios.c_oflag &= ~(OPOST); + termios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS); + termios.c_cflag |= (CS8 | CLOCAL | CREAD); + termios.c_lflag &= ~(ISIG | ICANON | IEXTEN); + termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); +#if defined(__APPLE__) + termios.c_cc[VMIN] = 1; + termios.c_cc[VTIME] = 1; +#else + termios.c_cc[VMIN] = 0; + termios.c_cc[VTIME] = 10; +#endif + + switch (m_speed) { + case SERIAL_1200: + ::cfsetospeed(&termios, B1200); + ::cfsetispeed(&termios, B1200); + break; + case SERIAL_2400: + ::cfsetospeed(&termios, B2400); + ::cfsetispeed(&termios, B2400); + break; + case SERIAL_4800: + ::cfsetospeed(&termios, B4800); + ::cfsetispeed(&termios, B4800); + break; + case SERIAL_9600: + ::cfsetospeed(&termios, B9600); + ::cfsetispeed(&termios, B9600); + break; + case SERIAL_19200: + ::cfsetospeed(&termios, B19200); + ::cfsetispeed(&termios, B19200); + break; + case SERIAL_38400: + ::cfsetospeed(&termios, B38400); + ::cfsetispeed(&termios, B38400); + break; + case SERIAL_115200: + ::cfsetospeed(&termios, B115200); + ::cfsetispeed(&termios, B115200); + break; + case SERIAL_230400: + ::cfsetospeed(&termios, B230400); + ::cfsetispeed(&termios, B230400); + break; + case SERIAL_460800: + ::cfsetospeed(&termios, B460800); + ::cfsetispeed(&termios, B460800); + break; + default: + ::LogError(LOG_HOST, "Unsupported serial port speed - %u", m_speed); + ::close(m_fd); + return false; + } + + if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) { + ::LogError(LOG_HOST, "Cannot set the attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + + if (m_assertRTS) { + uint32_t y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) { + ::LogError(LOG_HOST, "Cannot get the control attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + + y |= TIOCM_RTS; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) { + ::LogError(LOG_HOST, "Cannot set the control attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + } + +#if defined(__APPLE__) + setNonblock(false); +#endif + + return true; +} +#endif diff --git a/modem/port/UARTPort.h b/modem/port/UARTPort.h new file mode 100644 index 00000000..999ea2da --- /dev/null +++ b/modem/port/UARTPort.h @@ -0,0 +1,122 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2002-2004,2007-2009,2011-2013,2015-2017,2020,2021 by Jonathan Naylor G4KLX +* Copyright (C) 1999-2001 by Thomas Sailor HB9JNX +* Copyright (C) 2020-2021 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#if !defined(__UART_PORT_H__) +#define __UART_PORT_H__ + +#include "Defines.h" +#include "modem/port/IModemPort.h" +#include "modem/port/ISerialPort.h" + +#include + +#if defined(_WIN32) || defined(_WIN64) +#define WIN32_LEAN_AND_MEAN +#include +#endif + +namespace modem +{ + namespace port + { + // --------------------------------------------------------------------------- + // Constants + // --------------------------------------------------------------------------- + + enum SERIAL_SPEED { + SERIAL_1200 = 1200, + SERIAL_2400 = 2400, + SERIAL_4800 = 4800, + SERIAL_9600 = 9600, + SERIAL_19200 = 19200, + SERIAL_38400 = 38400, + SERIAL_76800 = 76800, + SERIAL_115200 = 115200, + SERIAL_230400 = 230400, + SERIAL_460800 = 460800 + }; + + // --------------------------------------------------------------------------- + // Class Declaration + // This class implements low-level routines to communicate over a RS232 + // serial port. + // --------------------------------------------------------------------------- + + class HOST_SW_API UARTPort : public ISerialPort, public IModemPort { + public: + /// Initializes a new instance of the UARTPort class. + UARTPort(const std::string& device, SERIAL_SPEED speed, bool assertRTS = false); + /// Finalizes a instance of the UARTPort class. + virtual ~UARTPort(); + + /// Opens a connection to the serial port. + virtual bool open(); + + /// Reads data from the serial port. + virtual int read(uint8_t* buffer, uint32_t length); + /// Writes data to the serial port. + virtual int write(const uint8_t* buffer, uint32_t length); + + /// Closes the connection to the serial port. + virtual void close(); + +#if defined(__APPLE__) + /// + virtual int setNonblock(bool nonblock); +#endif + + protected: + /// Initializes a new instance of the UARTPort class. + UARTPort(SERIAL_SPEED speed, bool assertRTS = false); + + std::string m_device; + SERIAL_SPEED m_speed; + bool m_assertRTS; +#if defined(_WIN32) || defined(_WIN64) + HANDLE m_handle; +#else + int m_fd; +#endif + +#if defined(_WIN32) || defined(_WIN64) + /// + int readNonblock(uint8_t * buffer, uint32_t length); +#else + /// + bool canWrite(); + /// + bool setRaw(); +#endif + }; // class HOST_SW_API UARTPort : public ISerialPort, public IModemPort + } // namespace port +} // namespace Modem + +#endif // __UART_PORT_H__ diff --git a/modem/port/UDPPort.cpp b/modem/port/UDPPort.cpp new file mode 100644 index 00000000..1010271e --- /dev/null +++ b/modem/port/UDPPort.cpp @@ -0,0 +1,161 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2021 by Jonathan Naylor G4KLX +* Copyright (C) 2021 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#include "Defines.h" +#include "modem/port/UDPPort.h" +#include "Log.h" +#include "Utils.h" + +using namespace modem::port; +using namespace network; + +#include +#include + +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +const uint32_t BUFFER_LENGTH = 2000U; + +// --------------------------------------------------------------------------- +// Public Class Members +// --------------------------------------------------------------------------- +/// +/// Initializes a new instance of the UDPPort class. +/// +/// Hostname/IP address to connect to. +/// Port number. +/// +UDPPort::UDPPort(const std::string& address, uint32_t modemPort) : + m_socket(modemPort), + m_addr(), + m_addrLen(0U), + m_buffer(2000U, "UDP Port Ring Buffer") +{ + assert(!address.empty()); + assert(modemPort > 0U); + + if (UDPSocket::lookup(address, modemPort, m_addr, m_addrLen) != 0) + m_addrLen = 0U; + + if (m_addrLen > 0U) { + std::string addrStr = UDPSocket::address(m_addr); + LogWarning(LOG_HOST, "SECURITY: Remote modem expects IP address; %s for remote modem control", addrStr.c_str()); + } +} + +/// +/// Finalizes a instance of the UDPPort class. +/// +UDPPort::~UDPPort() +{ + /* stub */ +} + +/// +/// Opens a connection to the port. +/// +/// True, if connection is opened, otherwise false. +bool UDPPort::open() +{ + if (m_addrLen == 0U) { + LogError(LOG_NET, "Unable to resolve the address of the modem"); + return false; + } + + return m_socket.open(m_addr); +} + +/// +/// Reads data from the port. +/// +/// Buffer to read data from the port to. +/// Length of data to read from the port. +/// Actual length of data read from serial port. +int UDPPort::read(uint8_t* buffer, uint32_t length) +{ + assert(buffer != NULL); + assert(length > 0U); + + uint8_t data[BUFFER_LENGTH]; + ::memset(data, 0x00U, BUFFER_LENGTH); + + sockaddr_storage addr; + uint32_t addrLen; + int ret = m_socket.read(data, BUFFER_LENGTH, addr, addrLen); + + // An error occurred on the socket + if (ret < 0) + return ret; + + // Add new data to the ring buffer + if (ret > 0) { + if (UDPSocket::match(addr, m_addr)) { + m_buffer.addData(data, ret); + } + else { + std::string addrStr = UDPSocket::address(addr); + LogWarning(LOG_HOST, "SECURITY: Remote modem mode encountered invalid IP address; %s", addrStr.c_str()); + } + } + + // Get required data from the ring buffer + uint32_t avail = m_buffer.dataSize(); + if (avail < length) + length = avail; + + if (length > 0U) + m_buffer.getData(buffer, length); + + return int(length); +} + +/// +/// Writes data to the port. +/// +/// Buffer containing data to write to port. +/// Length of data to write to port. +/// Actual length of data written to the port. +int UDPPort::write(const uint8_t* buffer, uint32_t length) +{ + assert(buffer != NULL); + assert(length > 0U); + + return m_socket.write(buffer, length, m_addr, m_addrLen) ? int(length) : -1; +} + +/// +/// Closes the connection to the port. +/// +void UDPPort::close() +{ + m_socket.close(); +} diff --git a/modem/port/UDPPort.h b/modem/port/UDPPort.h new file mode 100644 index 00000000..5772f50b --- /dev/null +++ b/modem/port/UDPPort.h @@ -0,0 +1,79 @@ +/** +* Digital Voice Modem - Host Software +* GPLv2 Open Source. Use is subject to license terms. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* @package DVM / Host Software +* +*/ +// +// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost) +// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) +// +/* +* Copyright (C) 2021 by Jonathan Naylor G4KLX +* Copyright (C) 2021 by Bryan Biedenkapp N2PLL +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +#if !defined(__UDP_PORT_H__) +#define __UDP_PORT_H__ + +#include "Defines.h" +#include "modem/port/IModemPort.h" +#include "RingBuffer.h" +#include "network/UDPSocket.h" + +#include + +namespace modem +{ + namespace port + { + // --------------------------------------------------------------------------- + // Class Declaration + // This class implements low-level routines to communicate over UDP. + // --------------------------------------------------------------------------- + + class HOST_SW_API UDPPort : public IModemPort { + public: + /// Initializes a new instance of the UDPPort class. + UDPPort(const std::string& modemAddress, uint32_t modemPort); + /// Finalizes a instance of the UDPPort class. + virtual ~UDPPort(); + + /// Opens a connection to the serial port. + virtual bool open(); + + /// Reads data from the serial port. + virtual int read(uint8_t* buffer, uint32_t length); + /// Writes data to the serial port. + virtual int write(const uint8_t* buffer, uint32_t length); + + /// Closes the connection to the serial port. + virtual void close(); + + protected: + network::UDPSocket m_socket; + + sockaddr_storage m_addr; + uint32_t m_addrLen; + + RingBuffer m_buffer; + }; + } // namespace port +} // namespace Modem + +#endif // __UDP_PORT_H__ diff --git a/network/RemoteControl.cpp b/network/RemoteControl.cpp index d5326fd6..d9bca173 100644 --- a/network/RemoteControl.cpp +++ b/network/RemoteControl.cpp @@ -166,7 +166,7 @@ void RemoteControl::process(Host* host, dmr::Control* dmr, p25::Control* p25) uint8_t buffer[RC_BUFFER_LENGTH]; sockaddr_storage address; - unsigned int addrLen; + uint32_t addrLen; uint32_t ret = m_socket.read((uint8_t*)buffer, RC_BUFFER_LENGTH, address, addrLen); if (ret > 0U) { buffer[ret] = '\0'; diff --git a/network/UDPSocket.cpp b/network/UDPSocket.cpp index dcaa501a..f098b1a1 100644 --- a/network/UDPSocket.cpp +++ b/network/UDPSocket.cpp @@ -49,7 +49,7 @@ using namespace network; /// /// Hostname/IP address to connect to. /// Port number. -UDPSocket::UDPSocket(const std::string& address, unsigned int port) : +UDPSocket::UDPSocket(const std::string& address, uint32_t port) : m_address_save(address), m_port_save(port), m_counter(0U) @@ -66,7 +66,7 @@ UDPSocket::UDPSocket(const std::string& address, unsigned int port) : /// Initializes a new instance of the UDPSocket class. /// /// Port number. -UDPSocket::UDPSocket(unsigned int port) : +UDPSocket::UDPSocket(uint32_t port) : m_address_save(), m_port_save(port), m_counter(0U) @@ -102,7 +102,7 @@ bool UDPSocket::open(const sockaddr_storage& address) /// /// /// True, if UDP socket is opened, otherwise false. -bool UDPSocket::open(unsigned int af) +bool UDPSocket::open(uint32_t af) { return open(0, af, m_address_save, m_port_save); } @@ -115,10 +115,10 @@ bool UDPSocket::open(unsigned int af) /// /// /// True, if UDP socket is opened, otherwise false. -bool UDPSocket::open(const unsigned int index, const unsigned int af, const std::string& address, const unsigned int port) +bool UDPSocket::open(const uint32_t index, const uint32_t af, const std::string& address, const uint32_t port) { sockaddr_storage addr; - unsigned int addrlen; + uint32_t addrlen; struct addrinfo hints; ::memset(&hints, 0, sizeof(hints)); @@ -183,7 +183,7 @@ bool UDPSocket::open(const unsigned int index, const unsigned int af, const std: /// IP address to read data from. /// /// Actual length of data read from remote UDP socket. -int UDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int& addrLen) +int UDPSocket::read(uint8_t* buffer, uint32_t length, sockaddr_storage& address, uint32_t& addrLen) { assert(buffer != NULL); assert(length > 0U); @@ -267,7 +267,7 @@ int UDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storage /// IP address to write data to. /// /// Actual length of data written to remote UDP socket. -bool UDPSocket::write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int addrLen) +bool UDPSocket::write(const uint8_t* buffer, uint32_t length, const sockaddr_storage& address, uint32_t addrLen) { assert(buffer != NULL); assert(length > 0U); @@ -318,7 +318,7 @@ void UDPSocket::close() /// Closes the UDP socket connection. /// /// -void UDPSocket::close(const unsigned int index) +void UDPSocket::close(const uint32_t index) { if ((index < UDP_SOCKET_MAX) && (m_fd[index] >= 0)) { #if defined(_WIN32) || defined(_WIN64) @@ -362,7 +362,7 @@ void UDPSocket::shutdown() /// Socket address structure. /// /// Zero if no error during lookup, otherwise error. -int UDPSocket::lookup(const std::string& hostname, unsigned int port, sockaddr_storage& addr, unsigned int& addrLen) +int UDPSocket::lookup(const std::string& hostname, uint32_t port, sockaddr_storage& addr, uint32_t& addrLen) { struct addrinfo hints; ::memset(&hints, 0, sizeof(hints)); @@ -379,7 +379,7 @@ int UDPSocket::lookup(const std::string& hostname, unsigned int port, sockaddr_s /// /// /// Zero if no error during lookup, otherwise error. -int UDPSocket::lookup(const std::string& hostname, unsigned int port, sockaddr_storage& addr, unsigned int& addrLen, struct addrinfo& hints) +int UDPSocket::lookup(const std::string& hostname, uint32_t port, sockaddr_storage& addr, uint32_t& addrLen, struct addrinfo& hints) { std::string portstr = std::to_string(port); struct addrinfo* res; @@ -454,6 +454,42 @@ bool UDPSocket::match(const sockaddr_storage& addr1, const sockaddr_storage& add } } +/// +/// +/// +/// +/// +std::string UDPSocket::address(const sockaddr_storage& addr) +{ + std::string address = std::string(); + char str[INET_ADDRSTRLEN]; + + + + switch (addr.ss_family) { + case AF_INET: + { + struct sockaddr_in* in; + in = (struct sockaddr_in*) & addr; + inet_ntop(AF_INET, &(in->sin_addr), str, INET_ADDRSTRLEN); + address = std::string(str); + } + break; + case AF_INET6: + { + struct sockaddr_in6* in6; + in6 = (struct sockaddr_in6*) & addr; + inet_ntop(AF_INET6, &(in6->sin6_addr), str, INET_ADDRSTRLEN); + address = std::string(str); + } + break; + default: + break; + } + + return address; +} + /// /// /// diff --git a/network/UDPSocket.h b/network/UDPSocket.h index 980a7f64..16ef0246 100644 --- a/network/UDPSocket.h +++ b/network/UDPSocket.h @@ -69,28 +69,28 @@ namespace network class HOST_SW_API UDPSocket { public: /// Initializes a new instance of the UDPSocket class. - UDPSocket(const std::string& address, unsigned int port = 0U); + UDPSocket(const std::string& address, uint32_t port = 0U); /// Initializes a new instance of the UDPSocket class. - UDPSocket(unsigned int port = 0U); + UDPSocket(uint32_t port = 0U); /// Initializes a new instance of the UDPSocket class. ~UDPSocket(); /// Opens UDP socket connection. - bool open(unsigned int af = AF_UNSPEC); + bool open(uint32_t af = AF_UNSPEC); /// Opens UDP socket connection. bool open(const sockaddr_storage& address); /// Opens UDP socket connection. - bool open(const unsigned int index, const unsigned int af, const std::string& address, const unsigned int port); + bool open(const uint32_t index, const uint32_t af, const std::string& address, const uint32_t port); /// Read data from the UDP socket. - int read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int& addrLen); + int read(uint8_t* buffer, uint32_t length, sockaddr_storage& address, uint32_t& addrLen); /// Write data to the UDP socket. - bool write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int addrLen); + bool write(const uint8_t* buffer, uint32_t length, const sockaddr_storage& address, uint32_t addrLen); /// Closes the UDP socket connection. void close(); /// Closes the UDP socket connection. - void close(const unsigned int index); + void close(const uint32_t index); /// static void startup(); @@ -98,24 +98,28 @@ namespace network static void shutdown(); /// Helper to lookup a hostname and resolve it to an IP address. - static int lookup(const std::string& hostName, unsigned int port, sockaddr_storage& address, unsigned int& addrLen); + static int lookup(const std::string& hostName, uint32_t port, sockaddr_storage& address, uint32_t& addrLen); /// Helper to lookup a hostname and resolve it to an IP address. - static int lookup(const std::string& hostName, unsigned int port, sockaddr_storage& address, unsigned int& addrLen, struct addrinfo& hints); + static int lookup(const std::string& hostName, uint32_t port, sockaddr_storage& address, uint32_t& addrLen, struct addrinfo& hints); /// static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT); + /// + static std::string address(const sockaddr_storage& addr); /// static bool isNone(const sockaddr_storage& addr); private: - std::string m_address_save; - unsigned short m_port_save; - std::string m_address[UDP_SOCKET_MAX]; - unsigned short m_port[UDP_SOCKET_MAX]; - unsigned int m_af[UDP_SOCKET_MAX]; - int m_fd[UDP_SOCKET_MAX]; - unsigned int m_counter; + std::string m_address_save; + uint16_t m_port_save; + std::string m_address[UDP_SOCKET_MAX]; + uint16_t m_port[UDP_SOCKET_MAX]; + + uint32_t m_af[UDP_SOCKET_MAX]; + int m_fd[UDP_SOCKET_MAX]; + + uint32_t m_counter; }; } // namespace network