upstream: Refactor modem; support remote modem support via UDP;

pull/1/head
Bryan Biedenkapp 5 years ago
parent a0ef0aaf63
commit 63713f0f8e

@ -195,8 +195,11 @@
<ClInclude Include="lookups\RSSIInterpolator.h" />
<ClInclude Include="lookups\TalkgroupIdLookup.h" />
<ClInclude Include="modem\Modem.h" />
<ClInclude Include="modem\NullModem.h" />
<ClInclude Include="modem\SerialController.h" />
<ClInclude Include="modem\port\IModemPort.h" />
<ClInclude Include="modem\port\ISerialPort.h" />
<ClInclude Include="modem\port\ModemNullPort.h" />
<ClInclude Include="modem\port\UARTPort.h" />
<ClInclude Include="modem\port\UDPPort.h" />
<ClInclude Include="network\BaseNetwork.h" />
<ClInclude Include="network\Network.h" />
<ClInclude Include="network\RemoteControl.h" />
@ -267,8 +270,11 @@
<ClCompile Include="lookups\RSSIInterpolator.cpp" />
<ClCompile Include="lookups\TalkgroupIdLookup.cpp" />
<ClCompile Include="modem\Modem.cpp" />
<ClCompile Include="modem\NullModem.cpp" />
<ClCompile Include="modem\SerialController.cpp" />
<ClCompile Include="modem\port\IModemPort.cpp" />
<ClCompile Include="modem\port\ISerialPort.cpp" />
<ClCompile Include="modem\port\ModemNullPort.cpp" />
<ClCompile Include="modem\port\UARTPort.cpp" />
<ClCompile Include="modem\port\UDPPort.cpp" />
<ClCompile Include="network\BaseNetwork.cpp" />
<ClCompile Include="network\Network.cpp" />
<ClCompile Include="network\RemoteControl.cpp" />

@ -111,6 +111,12 @@
<Filter Include="Source Files\host\calibrate">
<UniqueIdentifier>{3a6ae793-a482-46fe-9fd3-93476ccb591d}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\modem\port">
<UniqueIdentifier>{25f65018-5057-418b-ae56-b6ed06395099}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\modem\port">
<UniqueIdentifier>{bd26735c-6610-4bfe-b1c4-27da1cb18e29}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Defines.h">
@ -170,9 +176,6 @@
<ClInclude Include="modem\Modem.h">
<Filter>Header Files\modem</Filter>
</ClInclude>
<ClInclude Include="modem\SerialController.h">
<Filter>Header Files\modem</Filter>
</ClInclude>
<ClInclude Include="network\Network.h">
<Filter>Header Files\network</Filter>
</ClInclude>
@ -182,9 +185,6 @@
<ClInclude Include="edac\RS634717.h">
<Filter>Header Files\edac</Filter>
</ClInclude>
<ClInclude Include="modem\NullModem.h">
<Filter>Header Files\modem</Filter>
</ClInclude>
<ClInclude Include="network\RemoteControl.h">
<Filter>Header Files\network</Filter>
</ClInclude>
@ -335,6 +335,21 @@
<ClInclude Include="dmr\ControlPacket.h">
<Filter>Header Files\dmr</Filter>
</ClInclude>
<ClInclude Include="modem\port\IModemPort.h">
<Filter>Header Files\modem\port</Filter>
</ClInclude>
<ClInclude Include="modem\port\ISerialPort.h">
<Filter>Header Files\modem\port</Filter>
</ClInclude>
<ClInclude Include="modem\port\UARTPort.h">
<Filter>Header Files\modem\port</Filter>
</ClInclude>
<ClInclude Include="modem\port\ModemNullPort.h">
<Filter>Header Files\modem\port</Filter>
</ClInclude>
<ClInclude Include="modem\port\UDPPort.h">
<Filter>Header Files\modem\port</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Log.cpp">
@ -394,15 +409,9 @@
<ClCompile Include="modem\Modem.cpp">
<Filter>Source Files\modem</Filter>
</ClCompile>
<ClCompile Include="modem\SerialController.cpp">
<Filter>Source Files\modem</Filter>
</ClCompile>
<ClCompile Include="edac\RS634717.cpp">
<Filter>Source Files\edac</Filter>
</ClCompile>
<ClCompile Include="modem\NullModem.cpp">
<Filter>Source Files\modem</Filter>
</ClCompile>
<ClCompile Include="network\RemoteControl.cpp">
<Filter>Source Files\network</Filter>
</ClCompile>
@ -538,6 +547,21 @@
<ClCompile Include="dmr\ControlPacket.cpp">
<Filter>Source Files\dmr</Filter>
</ClCompile>
<ClCompile Include="modem\port\UARTPort.cpp">
<Filter>Source Files\modem\port</Filter>
</ClCompile>
<ClCompile Include="modem\port\ModemNullPort.cpp">
<Filter>Source Files\modem\port</Filter>
</ClCompile>
<ClCompile Include="modem\port\UDPPort.cpp">
<Filter>Source Files\modem\port</Filter>
</ClCompile>
<ClCompile Include="modem\port\IModemPort.cpp">
<Filter>Source Files\modem\port</Filter>
</ClCompile>
<ClCompile Include="modem\port\ISerialPort.cpp">
<Filter>Source Files\modem\port</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="Makefile" />

@ -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;

@ -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

@ -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

@ -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

@ -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

@ -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 <cstdio>
#include <cstdarg>
#include <algorithm>
#include <vector>
#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()
/// </summary>
bool Host::readParams()
{
yaml::Node modemConf = m_conf["system"]["modem"];
yaml::Node modemProtocol = modemConf["protocol"];
std::string portType = modemProtocol["type"].as<std::string>("null");
yaml::Node udpProtocol = modemProtocol["udp"];
std::string udpMode = udpProtocol["mode"].as<std::string>("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<bool>(false);
m_p25Enabled = protocolConf["p25"]["enable"].as<bool>(false);
@ -1100,6 +1169,7 @@ bool Host::readParams()
LogInfo(" DMR: %s", m_dmrEnabled ? "enabled" : "disabled");
LogInfo(" P25: %s", m_p25Enabled ? "enabled" : "disabled");
LogInfo(" Duplex: %s", m_duplex ? "yes" : "no");
if (!udpMasterMode) {
LogInfo(" Timeout: %us", m_timeout);
LogInfo(" RF Mode Hang: %us", m_rfModeHang);
LogInfo(" RF Talkgroup Hang: %us", m_rfTalkgroupHang);
@ -1170,7 +1240,7 @@ bool Host::readParams()
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"];
yaml::Node & voiceChList = rfssConfig["voiceChNo"];
for (size_t i = 0; i < voiceChList.size(); i++) {
uint32_t chNo = (uint32_t)::strtoul(voiceChList[i].as<std::string>("1").c_str(), NULL, 16);
m_voiceChNo.push_back(chNo);
@ -1247,6 +1317,10 @@ bool Host::readParams()
LogInfo(" P25 Network Id: $%05X", m_p25NetId);
LogInfo(" P25 System Id: $%03X", m_p25SysId);
LogInfo(" P25 RFSS Id: $%02X", m_p25RfssId);
}
else {
LogInfo(" Modem Remote Control: yes");
}
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<std::string>();
yaml::Node modemProtocol = modemConf["protocol"];
std::string portType = modemProtocol["type"].as<std::string>("null");
yaml::Node uartProtocol = modemProtocol["uart"];
std::string uartPort = uartProtocol["port"].as<std::string>();
uint32_t uartSpeed = uartProtocol["speed"].as<uint32_t>(115200);
yaml::Node udpProtocol = modemProtocol["udp"];
std::string udpMode = udpProtocol["mode"].as<std::string>("master");
std::string udpAddress = udpProtocol["address"].as<std::string>();
uint32_t udpPort = udpProtocol["port"].as<uint32_t>(REMOTE_MODEM_PORT);
bool rxInvert = modemConf["rxInvert"].as<bool>(false);
bool txInvert = modemConf["txInvert"].as<bool>(false);
bool pttInvert = modemConf["pttInvert"].as<bool>(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;

@ -68,6 +68,7 @@ private:
yaml::Node m_conf;
modem::Modem* m_modem;
bool m_modemRemote;
network::Network* m_network;
uint8_t m_mode;

@ -42,6 +42,7 @@
using namespace modem;
#include <cstdio>
#include <algorithm>
#if !defined(_WIN32) && !defined(_WIN64)
#include <unistd.h>
@ -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) :
/// </summary>
HostCal::~HostCal()
{
/* stub */
delete m_serial;
}
/// <summary>
@ -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<std::string>();
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<std::string>("null");
yaml::Node uartProtocol = modemProtocol["uart"];
std::string uartPort = uartProtocol["port"].as<std::string>();
uint32_t uartSpeed = uartProtocol["speed"].as<uint32_t>(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()
/// <returns>Zero if no data was read, otherwise returns length of data read.</returns>
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;

@ -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;

@ -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;
/// <summary>
/// Initializes a new instance of the Modem class.
/// </summary>
/// <param name="port">Serial port the modem DSP is connected to.</param>
/// <param name="port">Port the air interface modem is connected to.</param>
/// <param name="duplex">Flag indicating the modem is operating in duplex mode.</param>
/// <param name="rxInvert">Flag indicating the Rx polarity should be inverted.</param>
/// <param name="txInvert">Flag indicating the Tx polarity should be inverted.</param>
@ -70,11 +69,12 @@ using namespace modem;
/// <param name="p25CorrCount">P25 Correlation Countdown.</param>
/// <param name="disableOFlowReset">Flag indicating whether the ADC/DAC overflow reset logic is disabled.</param>
/// <param name="packetPlayoutTime">Length of time in MS between packets to send to modem.</param>
/// <param name="trace">Flag indicating whether modem DSP trace is enabled.</param>
/// <param name="debug">Flag indicating whether modem DSP debug is enabled.</param>
Modem::Modem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, bool dcBlocker,
/// <param name="trace">Flag indicating whether air interface modem trace is enabled.</param>
/// <param name="debug">Flag indicating whether air interface modem debug is enabled.</param>
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,
/// </summary>
Modem::~Modem()
{
delete m_port;
if (m_remotePort != NULL) {
delete m_remotePort;
}
delete[] m_buffer;
}
/// <summary>
/// Sets the modem DSP RF DC offset parameters.
/// Sets the RF DC offset parameters.
/// </summary>
/// <param name="txDCOffset"></param>
/// <param name="rxDCOffset"></param>
@ -149,7 +155,7 @@ void Modem::setDCOffsetParams(int txDCOffset, int rxDCOffset)
}
/// <summary>
/// Sets the modem DSP enabled modes.
/// Sets the enabled modes.
/// </summary>
/// <param name="dmrEnabled"></param>
/// <param name="p25Enabled"></param>
@ -160,7 +166,7 @@ void Modem::setModeParams(bool dmrEnabled, bool p25Enabled)
}
/// <summary>
/// Sets the modem DSP RF deviation levels.
/// Sets the RF deviation levels.
/// </summary>
/// <param name="rxLevel"></param>
/// <param name="cwIdTXLevel"></param>
@ -175,7 +181,7 @@ void Modem::setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float
}
/// <summary>
/// Sets the modem DSP Symbol adjustment levels
/// Sets the symbol adjustment levels
/// </summary>
/// <param name="dmrSymLevel3Adj"></param>
/// <param name="dmrSymLevel1Adj"></param>
@ -209,7 +215,7 @@ void Modem::setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25Sym
}
/// <summary>
/// Sets the modem DSP DMR color code.
/// Sets the DMR color code.
/// </summary>
/// <param name="colorCode"></param>
void Modem::setDMRColorCode(uint32_t colorCode)
@ -220,7 +226,7 @@ void Modem::setDMRColorCode(uint32_t colorCode)
}
/// <summary>
/// Sets the modem DSP P25 NAC.
/// Sets the P25 NAC.
/// </summary>
/// <param name="nac"></param>
void Modem::setP25NAC(uint32_t nac)
@ -231,7 +237,7 @@ void Modem::setP25NAC(uint32_t nac)
}
/// <summary>
/// Sets the modem DSP RF receive deviation levels.
/// Sets the RF receive deviation levels.
/// </summary>
/// <param name="rxLevel"></param>
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)
}
/// <summary>
/// Opens connection to the modem DSP.
/// Sets the slave port for remote operation.
/// </summary>
void Modem::setSlavePort(port::IModemPort* slavePort)
{
assert(slavePort != NULL);
m_remotePort = slavePort;
}
/// <summary>
/// Opens connection to the air interface modem.
/// </summary>
/// <returns>True, if connection to modem is established, otherwise false.</returns>
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;
}
@ -352,6 +385,14 @@ void Modem::clock(uint32_t ms)
else if (type == RTM_ERROR) {
// Nothing to do
}
else {
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]) {
@ -453,6 +494,8 @@ void Modem::clock(uint32_t ms)
// if (m_trace)
// Utils::dump(1U, "Get Status", m_buffer, m_length);
m_modemState = (DVM_STATE)m_buffer[4U];
m_tx = (m_buffer[5U] & 0x01U) == 0x01U;
bool adcOverflow = (m_buffer[5U] & 0x02U) == 0x02U;
@ -469,7 +512,8 @@ void Modem::clock(uint32_t ms)
LogError(LOG_MODEM, "ADC overflow count > %u, resetting modem", MAX_ADC_OVERFLOW);
forceModemReset = true;
}
} else {
}
else {
m_adcOverFlowCount = 0U;
}
}
@ -503,7 +547,8 @@ void Modem::clock(uint32_t ms)
LogError(LOG_MODEM, "DAC overflow count > %u, resetting modem", MAX_DAC_OVERFLOW);
forceModemReset = true;
}
} else {
}
else {
m_dacOverFlowCount = 0U;
}
}
@ -545,6 +590,7 @@ void Modem::clock(uint32_t ms)
break;
}
}
}
// force a modem reset because of a error condition
if (forceModemReset) {
@ -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)
}
/// <summary>
/// Closes connection to the modem DSP.
/// Closes connection to the air interface modem.
/// </summary>
void Modem::close()
{
LogDebug(LOG_MODEM, "Closing the modem");
m_serial.close();
m_port->close();
if (m_remotePort != NULL) {
m_remotePort->close();
}
}
/// <summary>
@ -715,43 +795,43 @@ bool Modem::hasP25Space() const
}
/// <summary>
/// Flag indicating whether or not the modem DSP is transmitting.
/// Flag indicating whether or not the air interface modem is transmitting.
/// </summary>
/// <returns>True, if modem DSP is transmitting, otherwise false.</returns>
/// <returns>True, if air interface modem is transmitting, otherwise false.</returns>
bool Modem::hasTX() const
{
return m_tx;
}
/// <summary>
/// Flag indicating whether or not the modem DSP has carrier detect.
/// Flag indicating whether or not the air interface modem has carrier detect.
/// </summary>
/// <returns>True, if modem DSP has carrier detect, otherwise false.</returns>
/// <returns>True, if air interface modem has carrier detect, otherwise false.</returns>
bool Modem::hasCD() const
{
return m_cd;
}
/// <summary>
/// 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.
/// </summary>
/// <returns>True, if modem DSP is currently locked out, otherwise false.</returns>
/// <returns>True, if air interface modem is currently locked out, otherwise false.</returns>
bool Modem::hasLockout() const
{
return m_lockout;
}
/// <summary>
/// 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.
/// </summary>
/// <returns>True, if the modem DSP is current in an error condition, otherwise false.</returns>
/// <returns>True, if the air interface modem is current in an error condition, otherwise false.</returns>
bool Modem::hasError() const
{
return m_error;
}
/// <summary>
/// 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.
/// </summary>
void Modem::clearDMRData1()
{
@ -761,7 +841,7 @@ void Modem::clearDMRData1()
}
/// <summary>
/// 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.
/// </summary>
void Modem::clearDMRData2()
{
@ -771,7 +851,7 @@ void Modem::clearDMRData2()
}
/// <summary>
/// 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.
/// </summary>
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);
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="data">Data to write to ring buffer.</param>
/// <param name="length">Length of data to write.</param>
@ -815,7 +895,7 @@ void Modem::injectDMRData1(const uint8_t* data, uint32_t length)
}
/// <summary>
/// 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.
/// </summary>
/// <param name="data">Data to write to ring buffer.</param>
/// <param name="length">Length of data to write.</param>
@ -839,7 +919,7 @@ void Modem::injectDMRData2(const uint8_t* data, uint32_t length)
}
/// <summary>
/// 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.
/// </summary>
/// <param name="data">Data to write to ring buffer.</param>
/// <param name="length">Length of data to write.</param>
@ -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;
}
/// <summary>
/// Writes a DMR short LC to the modem DSP.
/// Writes a DMR short LC to the air interface modem.
/// </summary>
/// <param name="lc"></param>
/// <returns>True, if DMR LC is written, otherwise false.</returns>
@ -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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="slotNo">DMR slot to write abort for.</param>
/// <returns>True, if DMR abort is written, otherwise false.</returns>
@ -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;
}
/// <summary>
/// Sets the current operating mode for the modem DSP.
/// Sets the current operating mode for the air interface modem.
/// </summary>
/// <param name="state"></param>
/// <returns></returns>
@ -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;
}
/// <summary>
@ -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);
}
/// <summary>
/// Helper to create an instance of the Modem class.
/// </summary>
/// <param name="port">Serial port the modem DSP is connected to.</param>
/// <param name="duplex">Flag indicating the modem is operating in duplex mode.</param>
/// <param name="rxInvert">Flag indicating the Rx polarity should be inverted.</param>
/// <param name="txInvert">Flag indicating the Tx polarity should be inverted.</param>
/// <param name="pttInvert">Flag indicating the PTT polarity should be inverted.</param>
/// <param name="dcBlocker">Flag indicating whether the DSP DC-level blocking should be enabled.</param>
/// <param name="cosLockout">Flag indicating whether the COS signal should be used to lockout the modem.</param>
/// <param name="fdmaPreamble">Count of FDMA preambles to transmit before data. (P25/DMR DMO)</param>
/// <param name="dmrRxDelay">Compensate for delay in receiver audio chain in ms. Usually DSP based.</param>
/// <param name="p25CorrCount">P25 Correlation Countdown.</param>
/// <param name="packetPlayoutTime">Length of time in MS between packets to send to modem.</param>
/// <param name="disableOFlowReset">Flag indicating whether the ADC/DAC overflow reset logic is disabled.</param>
/// <param name="trace">Flag indicating whether modem DSP trace is enabled.</param>
/// <param name="debug">Flag indicating whether modem DSP debug is enabled.</param>
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
// ---------------------------------------------------------------------------
/// <summary>
/// Retrieve the modem DSP version.
/// Retrieve the air interface modem version.
/// </summary>
/// <returns></returns>
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()
}
/// <summary>
/// Retrieve the current status from the modem DSP.
/// Retrieve the current status from the air interface modem.
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// Write configuration to the modem DSP.
/// Write configuration to the air interface modem.
/// </summary>
/// <returns></returns>
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()
}
/// <summary>
/// Write symbol level adjustments to the modem DSP.
/// Write symbol level adjustments to the air interface modem.
/// </summary>
/// <returns>True, if level adjustments are written, otherwise false.</returns>
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;

@ -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:
/// <summary>Initializes a new instance of the Modem class.</summary>
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);
/// <summary>Finalizes a instance of the Modem class.</summary>
virtual ~Modem();
/// <summary>Sets the modem DSP RF DC offset parameters.</summary>
virtual void setDCOffsetParams(int txDCOffset, int rxDCOffset);
/// <summary>Sets the modem DSP enabled modes.</summary>
virtual void setModeParams(bool dmrEnabled, bool p25Enabled);
/// <summary>Sets the modem DSP RF deviation levels.</summary>
virtual void setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float p25TXLevel);
/// <summary>Sets the modem DSP Symbol adjustment levels.</summary>
virtual void setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25SymLevel3Adj, int p25SymLevel1Adj);
/// <summary>Sets the modem DSP DMR color code.</summary>
virtual void setDMRColorCode(uint32_t colorCode);
/// <summary>Sets the modem DSP P25 NAC.</summary>
virtual void setP25NAC(uint32_t nac);
/// <summary>Sets the modem DSP RF receive deviation levels.</summary>
virtual void setRXLevel(float rxLevel);
/// <summary>Opens connection to the modem DSP.</summary>
virtual bool open();
~Modem();
/// <summary>Sets the RF DC offset parameters.</summary>
void setDCOffsetParams(int txDCOffset, int rxDCOffset);
/// <summary>Sets the enabled modes.</summary>
void setModeParams(bool dmrEnabled, bool p25Enabled);
/// <summary>Sets the RF deviation levels.</summary>
void setLevels(float rxLevel, float cwIdTXLevel, float dmrTXLevel, float p25TXLevel);
/// <summary>Sets the symbol adjustment levels.</summary>
void setSymbolAdjust(int dmrSymLevel3Adj, int dmrSymLevel1Adj, int p25SymLevel3Adj, int p25SymLevel1Adj);
/// <summary>Sets the DMR color code.</summary>
void setDMRColorCode(uint32_t colorCode);
/// <summary>Sets the P25 NAC.</summary>
void setP25NAC(uint32_t nac);
/// <summary>Sets the RF receive deviation levels.</summary>
void setRXLevel(float rxLevel);
/// <summary>Sets the slave port for remote operation.</summary>
void setSlavePort(port::IModemPort* slavePort);
/// <summary>Opens connection to the air interface modem.</summary>
bool open();
/// <summary>Updates the modem by the passed number of milliseconds.</summary>
virtual void clock(uint32_t ms);
void clock(uint32_t ms);
/// <summary>Closes connection to the modem DSP.</summary>
virtual void close();
/// <summary>Closes connection to the air interface modem.</summary>
void close();
/// <summary>Reads DMR Slot 1 frame data from the DMR Slot 1 ring buffer.</summary>
virtual uint32_t readDMRData1(uint8_t* data);
uint32_t readDMRData1(uint8_t* data);
/// <summary>Reads DMR Slot 2 frame data from the DMR Slot 1 ring buffer.</summary>
virtual uint32_t readDMRData2(uint8_t* data);
uint32_t readDMRData2(uint8_t* data);
/// <summary>Reads P25 frame data from the P25 ring buffer.</summary>
virtual uint32_t readP25Data(uint8_t* data);
uint32_t readP25Data(uint8_t* data);
/// <summary>Helper to test if the DMR Slot 1 ring buffer has free space.</summary>
virtual bool hasDMRSpace1() const;
bool hasDMRSpace1() const;
/// <summary>Helper to test if the DMR Slot 2 ring buffer has free space.</summary>
virtual bool hasDMRSpace2() const;
bool hasDMRSpace2() const;
/// <summary>Helper to test if the P25 ring buffer has free space.</summary>
virtual bool hasP25Space() const;
bool hasP25Space() const;
/// <summary>Flag indicating whether or not the modem DSP is transmitting.</summary>
virtual bool hasTX() const;
/// <summary>Flag indicating whether or not the modem DSP has carrier detect.</summary>
virtual bool hasCD() const;
/// <summary>Flag indicating whether or not the air interface modem is transmitting.</summary>
bool hasTX() const;
/// <summary>Flag indicating whether or not the air interface modem has carrier detect.</summary>
bool hasCD() const;
/// <summary>Flag indicating whether or not the modem DSP is currently locked out.</summary>
virtual bool hasLockout() const;
/// <summary>Flag indicating whether or not the modem DSP is currently in an error condition.</summary>
virtual bool hasError() const;
/// <summary>Flag indicating whether or not the air interface modem is currently locked out.</summary>
bool hasLockout() const;
/// <summary>Flag indicating whether or not the air interface modem is currently in an error condition.</summary>
bool hasError() const;
/// <summary>Clears any buffered DMR Slot 1 frame data to be sent to the modem DSP.</summary>
/// <summary>Clears any buffered DMR Slot 1 frame data to be sent to the air interface modem.</summary>
void clearDMRData1();
/// <summary>Clears any buffered DMR Slot 2 frame data to be sent to the modem DSP.</summary>
/// <summary>Clears any buffered DMR Slot 2 frame data to be sent to the air interface modem.</summary>
void clearDMRData2();
/// <summary>Clears any buffered P25 frame data to be sent to the modem DSP.</summary>
/// <summary>Clears any buffered P25 frame data to be sent to the air interface modem.</summary>
void clearP25Data();
/// <summary>Internal helper to inject DMR Slot 1 frame data as if it came from the modem DSP.</summary>
/// <summary>Internal helper to inject DMR Slot 1 frame data as if it came from the air interface modem.</summary>
void injectDMRData1(const uint8_t* data, uint32_t length);
/// <summary>Internal helper to inject DMR Slot 2 frame data as if it came from the modem DSP.</summary>
/// <summary>Internal helper to inject DMR Slot 2 frame data as if it came from the air interface modem.</summary>
void injectDMRData2(const uint8_t* data, uint32_t length);
/// <summary>Internal helper to inject P25 frame data as if it came from the modem DSP.</summary>
/// <summary>Internal helper to inject P25 frame data as if it came from the air interface modem.</summary>
void injectP25Data(const uint8_t* data, uint32_t length);
/// <summary>Writes DMR Slot 1 frame data to the DMR Slot 1 ring buffer.</summary>
virtual bool writeDMRData1(const uint8_t* data, uint32_t length);
bool writeDMRData1(const uint8_t* data, uint32_t length);
/// <summary>Writes DMR Slot 2 frame data to the DMR Slot 2 ring buffer.</summary>
virtual bool writeDMRData2(const uint8_t* data, uint32_t length);
bool writeDMRData2(const uint8_t* data, uint32_t length);
/// <summary>Writes P25 frame data to the P25 ring buffer.</summary>
virtual bool writeP25Data(const uint8_t* data, uint32_t length);
bool writeP25Data(const uint8_t* data, uint32_t length);
/// <summary>Triggers the start of DMR transmit.</summary>
virtual bool writeDMRStart(bool tx);
/// <summary>Writes a DMR short LC to the modem DSP.</summary>
virtual bool writeDMRShortLC(const uint8_t* lc);
/// <summary>Writes a DMR abort message for the given slot to the modem DSP.</summary>
virtual bool writeDMRAbort(uint32_t slotNo);
bool writeDMRStart(bool tx);
/// <summary>Writes a DMR short LC to the air interface modem.</summary>
bool writeDMRShortLC(const uint8_t* lc);
/// <summary>Writes a DMR abort message for the given slot to the air interface modem.</summary>
bool writeDMRAbort(uint32_t slotNo);
/// <summary>Sets the current operating mode for the modem DSP.</summary>
virtual bool setMode(DVM_STATE state);
/// <summary>Gets the current operating mode for the air interface modem.</summary>
DVM_STATE getMode() const;
/// <summary>Sets the current operating mode for the air interface modem.</summary>
bool setMode(DVM_STATE state);
/// <summary>Transmits the given string as CW morse.</summary>
virtual bool sendCWId(const std::string& callsign);
/// <summary>Helper to create an instance of the Modem class.</summary>
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;
/// <summary>Retrieve the modem DSP version.</summary>
/// <summary>Retrieve the air interface modem version.</summary>
bool getFirmwareVersion();
/// <summary>Retrieve the current status from the modem DSP.</summary>
/// <summary>Retrieve the current status from the air interface modem.</summary>
bool getStatus();
/// <summary>Write configuration to the modem DSP.</summary>
/// <summary>Write configuration to the air interface modem.</summary>
bool writeConfig();
/// <summary>Write symbol level adjustments to the modem DSP.</summary>
/// <summary>Write symbol level adjustments to the air interface modem.</summary>
bool writeSymbolAdjust();
/// <summary></summary>

@ -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
// ---------------------------------------------------------------------------
/// <summary>
/// Finalizes a instance of the IModemPort class.
/// </summary>
IModemPort::~IModemPort()
{
/* stub */
}

@ -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:
/// <summary>Finalizes a instance of the IModemPort class.</summary>
virtual ~IModemPort() = 0;
/// <summary>Opens a connection to the port.</summary>
virtual bool open() = 0;
/// <summary>Reads data from the port.</summary>
virtual int read(uint8_t* buffer, uint32_t length) = 0;
/// <summary>Writes data to the port.</summary>
virtual int write(const uint8_t* buffer, uint32_t length) = 0;
/// <summary>Closes the connection to the port.</summary>
virtual void close() = 0;
};
} // namespace port
} // namespace modem
#endif // __I_MODEM_PORT_H__

@ -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
// ---------------------------------------------------------------------------
/// <summary>
/// Finalizes a instance of the ISerialPort class.
/// </summary>
ISerialPort::~ISerialPort()
{
/* stub */
}

@ -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:
/// <summary>Finalizes a instance of the ISerialPort class.</summary>
virtual ~ISerialPort() = 0;
/// <summary>Opens a connection to the port.</summary>
virtual bool open() = 0;
/// <summary>Reads data from the port.</summary>
virtual int read(uint8_t* buffer, uint32_t length) = 0;
/// <summary>Writes data to the port.</summary>
virtual int write(const uint8_t* buffer, uint32_t length) = 0;
/// <summary>Closes the connection to the port.</summary>
virtual void close() = 0;
};
} // namespace port
} // namespace modem
#endif // __I_SERIAL_PORT_H__

@ -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 <cstdio>
#include <cassert>
const char* HARDWARE = "Null Modem Controller";
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the ModemNullPort class.
/// </summary>
ModemNullPort::ModemNullPort() :
m_buffer(200U, "Null Controller Buffer")
{
/* stub */
}
/// <summary>
/// Finalizes a instance of the ModemNullPort class.
/// </summary>
ModemNullPort::~ModemNullPort()
{
}
/// <summary>
/// Opens a connection to the port.
/// </summary>
/// <returns>True, if connection is opened, otherwise false.</returns>
bool ModemNullPort::open()
{
return true;
}
/// <summary>
/// Reads data from the port.
/// </summary>
/// <param name="buffer">Buffer to read data from the serial port to.</param>
/// <param name="length">Length of data to read from the serial port.</param>
/// <returns>Actual length of data read from serial port.</returns>
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);
}
/// <summary>
/// Writes data to the serial port.
/// </summary>
/// <param name="buffer">Buffer containing data to write to serial port.</param>
/// <param name="length">Length of data to write to serial port.</param>
/// <returns>Actual length of data written to the serial port.</returns>
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);
}
/// <summary>
/// Closes the connection to the port.
/// </summary>
void ModemNullPort::close()
{
/* stub */
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
///
/// </summary>
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);
}
/// <summary>
///
/// </summary>
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);
}
/// <summary>
///
/// </summary>
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);
}

@ -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:
/// <summary>Initializes a new instance of the ModemNullPort class.</summary>
ModemNullPort();
/// <summary>Finalizes a instance of the ModemNullPort class.</summary>
virtual ~ModemNullPort();
/// <summary>Opens a connection to the port.</summary>
virtual bool open();
/// <summary>Reads data from the port.</summary>
virtual int read(uint8_t* buffer, uint32_t length);
/// <summary>Writes data to the port.</summary>
virtual int write(const uint8_t* buffer, uint32_t length);
/// <summary>Closes the connection to the port.</summary>
virtual void close();
private:
RingBuffer<unsigned char> m_buffer;
/// <summary></summary>
void getVersion();
/// <summary></summary>
void getStatus();
/// <summary></summary>
void writeAck(uint8_t type);
};
} // namespace port
} // namespace modem
#endif // __MODEM_NULL_PORT_H__

@ -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 <cstring>
#include <cassert>
#include <sys/types.h>
#if defined(_WIN32) || defined(_WIN64)
#include <setupapi.h>
#include <winioctl.h>
#else
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <cerrno>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#endif
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
#if defined(_WIN32) || defined(_WIN64)
/// <summary>
/// Initializes a new instance of the UARTPort class.
/// </summary>
/// <param name="device">Serial port device.</param>
/// <param name="speed">Serial port speed.</param>
/// <param name="assertRTS"></param>
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());
}
/// <summary>
/// Finalizes a instance of the UARTPort class.
/// </summary>
UARTPort::~UARTPort()
{
/* stub */
}
/// <summary>
/// Opens a connection to the serial port.
/// </summary>
/// <returns>True, if connection is opened, otherwise false.</returns>
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;
}
/// <summary>
/// Reads data from the serial port.
/// </summary>
/// <param name="buffer">Buffer to read data from the serial port to.</param>
/// <param name="length">Length of data to read from the serial port.</param>
/// <returns>Actual length of data read from serial port.</returns>
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);
}
/// <summary>
/// Writes data to the serial port.
/// </summary>
/// <param name="buffer">Buffer containing data to write to serial port.</param>
/// <param name="length">Length of data to write to serial port.</param>
/// <returns>Actual length of data written to the serial port.</returns>
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);
}
/// <summary>
/// Closes the connection to the serial port.
/// </summary>
void UARTPort::close()
{
assert(m_handle != INVALID_HANDLE_VALUE);
::CloseHandle(m_handle);
m_handle = INVALID_HANDLE_VALUE;
}
#else
/// <summary>
/// Initializes a new instance of the UARTPort class.
/// </summary>
/// <param name="device">Serial port device.</param>
/// <param name="speed">Serial port speed.</param>
/// <param name="assertRTS"></param>
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());
}
/// <summary>
/// Finalizes a instance of the UARTPort class.
/// </summary>
UARTPort::~UARTPort()
{
}
/// <summary>
/// Opens a connection to the serial port.
/// </summary>
/// <returns>True, if connection is opened, otherwise false.</returns>
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;
}
/// <summary>
/// Reads data from the serial port.
/// </summary>
/// <param name="buffer">Buffer to read data from the serial port to.</param>
/// <param name="length">Length of data to read from the serial port.</param>
/// <returns>Actual length of data read from serial port.</returns>
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;
}
/// <summary>
/// Writes data to the serial port.
/// </summary>
/// <param name="buffer">Buffer containing data to write to serial port.</param>
/// <param name="length">Length of data to write to serial port.</param>
/// <returns>Actual length of data written to the serial port.</returns>
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;
}
/// <summary>
/// Closes the connection to the serial port.
/// </summary>
void UARTPort::close()
{
assert(m_fd != -1);
::close(m_fd);
m_fd = -1;
}
#if defined(__APPLE__)
/// <summary>
///
/// </summary>
/// <returns></returns>
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)
/// <summary>
/// Initializes a new instance of the UARTPort class.
/// </summary>
/// <param name="speed">Serial port speed.</param>
/// <param name="assertRTS"></param>
UARTPort::UARTPort(SERIAL_SPEED speed, bool assertRTS) :
m_device(),
m_speed(speed),
m_assertRTS(assertRTS),
m_handle(INVALID_HANDLE_VALUE)
{
/* stub */
}
/// <summary>
///
/// </summary>
/// <param name="buffer"></param>
/// <param name="length"></param>
/// <returns></returns>
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
/// <summary>
/// Initializes a new instance of the UARTPort class.
/// </summary>
/// <param name="speed">Serial port speed.</param>
/// <param name="assertRTS"></param>
UARTPort::UARTPort(SERIAL_SPEED speed, bool assertRTS) :
m_device(),
m_speed(speed),
m_assertRTS(assertRTS),
m_fd(-1)
{
/* stub */
}
/// <summary>
///
/// </summary>
/// <returns></returns>
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
}
/// <summary>
///
/// </summary>
/// <returns></returns>
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

@ -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 <string>
#if defined(_WIN32) || defined(_WIN64)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#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:
/// <summary>Initializes a new instance of the UARTPort class.</summary>
UARTPort(const std::string& device, SERIAL_SPEED speed, bool assertRTS = false);
/// <summary>Finalizes a instance of the UARTPort class.</summary>
virtual ~UARTPort();
/// <summary>Opens a connection to the serial port.</summary>
virtual bool open();
/// <summary>Reads data from the serial port.</summary>
virtual int read(uint8_t* buffer, uint32_t length);
/// <summary>Writes data to the serial port.</summary>
virtual int write(const uint8_t* buffer, uint32_t length);
/// <summary>Closes the connection to the serial port.</summary>
virtual void close();
#if defined(__APPLE__)
/// <summary></summary>
virtual int setNonblock(bool nonblock);
#endif
protected:
/// <summary>Initializes a new instance of the UARTPort class.</summary>
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)
/// <summary></summary>
int readNonblock(uint8_t * buffer, uint32_t length);
#else
/// <summary></summary>
bool canWrite();
/// <summary></summary>
bool setRaw();
#endif
}; // class HOST_SW_API UARTPort : public ISerialPort, public IModemPort
} // namespace port
} // namespace Modem
#endif // __UART_PORT_H__

@ -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 <cstring>
#include <cassert>
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint32_t BUFFER_LENGTH = 2000U;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the UDPPort class.
/// </summary>
/// <param name="address">Hostname/IP address to connect to.</param>
/// <param name="modemPort">Port number.</param>
/// <param name="master"></param>
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());
}
}
/// <summary>
/// Finalizes a instance of the UDPPort class.
/// </summary>
UDPPort::~UDPPort()
{
/* stub */
}
/// <summary>
/// Opens a connection to the port.
/// </summary>
/// <returns>True, if connection is opened, otherwise false.</returns>
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);
}
/// <summary>
/// Reads data from the port.
/// </summary>
/// <param name="buffer">Buffer to read data from the port to.</param>
/// <param name="length">Length of data to read from the port.</param>
/// <returns>Actual length of data read from serial port.</returns>
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);
}
/// <summary>
/// Writes data to the port.
/// </summary>
/// <param name="buffer">Buffer containing data to write to port.</param>
/// <param name="length">Length of data to write to port.</param>
/// <returns>Actual length of data written to the port.</returns>
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;
}
/// <summary>
/// Closes the connection to the port.
/// </summary>
void UDPPort::close()
{
m_socket.close();
}

@ -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 <string>
namespace modem
{
namespace port
{
// ---------------------------------------------------------------------------
// Class Declaration
// This class implements low-level routines to communicate over UDP.
// ---------------------------------------------------------------------------
class HOST_SW_API UDPPort : public IModemPort {
public:
/// <summary>Initializes a new instance of the UDPPort class.</summary>
UDPPort(const std::string& modemAddress, uint32_t modemPort);
/// <summary>Finalizes a instance of the UDPPort class.</summary>
virtual ~UDPPort();
/// <summary>Opens a connection to the serial port.</summary>
virtual bool open();
/// <summary>Reads data from the serial port.</summary>
virtual int read(uint8_t* buffer, uint32_t length);
/// <summary>Writes data to the serial port.</summary>
virtual int write(const uint8_t* buffer, uint32_t length);
/// <summary>Closes the connection to the serial port.</summary>
virtual void close();
protected:
network::UDPSocket m_socket;
sockaddr_storage m_addr;
uint32_t m_addrLen;
RingBuffer<uint8_t> m_buffer;
};
} // namespace port
} // namespace Modem
#endif // __UDP_PORT_H__

@ -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';

@ -49,7 +49,7 @@ using namespace network;
/// </summary>
/// <param name="address">Hostname/IP address to connect to.</param>
/// <param name="port">Port number.</param>
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.
/// </summary>
/// <param name="port">Port number.</param>
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)
/// </summary>
/// <param name="af"></param>
/// <returns>True, if UDP socket is opened, otherwise false.</returns>
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)
/// <param name="address"></param>
/// <param name="port"></param>
/// <returns>True, if UDP socket is opened, otherwise false.</returns>
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:
/// <param name="address">IP address to read data from.</param>
/// <param name="addrLen"></param>
/// <returns>Actual length of data read from remote UDP socket.</returns>
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
/// <param name="address">IP address to write data to.</param>
/// <param name="addrLen"></param>
/// <returns>Actual length of data written to remote UDP socket.</returns>
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.
/// </summary>
/// <param name="index"></param>
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()
/// <param name="addr">Socket address structure.</param>
/// <param name="addrLen"></param>
/// <returns>Zero if no error during lookup, otherwise error.</returns>
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
/// <param name="addrLen"></param>
/// <param name="hints"></param>
/// <returns>Zero if no error during lookup, otherwise error.</returns>
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
}
}
/// <summary>
///
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
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;
}
/// <summary>
///
/// </summary>

@ -69,28 +69,28 @@ namespace network
class HOST_SW_API UDPSocket {
public:
/// <summary>Initializes a new instance of the UDPSocket class.</summary>
UDPSocket(const std::string& address, unsigned int port = 0U);
UDPSocket(const std::string& address, uint32_t port = 0U);
/// <summary>Initializes a new instance of the UDPSocket class.</summary>
UDPSocket(unsigned int port = 0U);
UDPSocket(uint32_t port = 0U);
/// <summary>Initializes a new instance of the UDPSocket class.</summary>
~UDPSocket();
/// <summary>Opens UDP socket connection.</summary>
bool open(unsigned int af = AF_UNSPEC);
bool open(uint32_t af = AF_UNSPEC);
/// <summary>Opens UDP socket connection.</summary>
bool open(const sockaddr_storage& address);
/// <summary>Opens UDP socket connection.</summary>
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);
/// <summary>Read data from the UDP socket.</summary>
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);
/// <summary>Write data to the UDP socket.</summary>
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);
/// <summary>Closes the UDP socket connection.</summary>
void close();
/// <summary>Closes the UDP socket connection.</summary>
void close(const unsigned int index);
void close(const uint32_t index);
/// <summary></summary>
static void startup();
@ -98,24 +98,28 @@ namespace network
static void shutdown();
/// <summary>Helper to lookup a hostname and resolve it to an IP address.</summary>
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);
/// <summary>Helper to lookup a hostname and resolve it to an IP address.</summary>
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);
/// <summary></summary>
static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT);
/// <summary></summary>
static std::string address(const sockaddr_storage& addr);
/// <summary></summary>
static bool isNone(const sockaddr_storage& addr);
private:
std::string m_address_save;
unsigned short m_port_save;
uint16_t m_port_save;
std::string m_address[UDP_SOCKET_MAX];
unsigned short m_port[UDP_SOCKET_MAX];
unsigned int m_af[UDP_SOCKET_MAX];
uint16_t m_port[UDP_SOCKET_MAX];
uint32_t m_af[UDP_SOCKET_MAX];
int m_fd[UDP_SOCKET_MAX];
unsigned int m_counter;
uint32_t m_counter;
};
} // namespace network

Loading…
Cancel
Save

Powered by TurnKey Linux.