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