diff --git a/DVMHost.vcxproj b/DVMHost.vcxproj
index 11a50ad2..20fbdd41 100644
--- a/DVMHost.vcxproj
+++ b/DVMHost.vcxproj
@@ -202,6 +202,7 @@
+
@@ -281,6 +282,7 @@
+
diff --git a/DVMHost.vcxproj.filters b/DVMHost.vcxproj.filters
index 5917e50d..3bf2aa99 100644
--- a/DVMHost.vcxproj.filters
+++ b/DVMHost.vcxproj.filters
@@ -353,6 +353,9 @@
Header Files\modem\port
+
+ Header Files\modem\port
+
Header Files\modem\port
@@ -571,6 +574,9 @@
Source Files\modem\port
+
+ Source Files\modem\port
+
Source Files\modem\port
diff --git a/Makefile b/Makefile
index 2fd40293..8b19eeaa 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ rpi-armSTRIP= /opt/tools/arm-bcm2708/arm-linux-gnueabihf/bin/arm-linux-gnueabihf
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -I.
EXTFLAGS=
-LIBS = -lpthread
+LIBS = -lpthread -lutil
LDFLAGS = -g
BIN = dvmhost
@@ -69,6 +69,7 @@ OBJECTS = \
modem/port/ISerialPort.o \
modem/port/ModemNullPort.o \
modem/port/UARTPort.o \
+ modem/port/PseudoPTYPort.o \
modem/port/UDPPort.o \
modem/Modem.o \
network/UDPSocket.o \
@@ -98,3 +99,31 @@ strip:
clean:
$(RM) $(BIN) $(OBJECTS) *.o *.d *.bak *~
+install: all
+ @mkdir -p /opt/dvm
+ install -m 755 $(BIN) /opt/dvm/bin/
+
+install-config-files:
+ @mkdir -p /opt/dvm
+ @cp -n config.example.yml /opt/dvm/config.yml
+ @cp -n iden_table.example.dat /opt/dvm/iden_table.dat
+ @cp -n rid_acl.example.dat /opt/dvm/rid_acl.dat
+ @cp -n tg_acl.example.dat /opt/dvm/tg_acl.dat
+ @sed -i 's/filePath: ./filePath: \/opt\/dvm\/log\//' /opt/dvm/config.yml
+ @sed -i 's/activityFilePath: ./activityFilePath: \/opt\/dvm\/log\//' /opt/dvm/config.yml
+ @sed -i 's/file: iden_table.dat/file: \/opt\/dvm\/iden_table.dat/' /opt/dvm/config.yml
+ @sed -i 's/file: rid_acl.dat/file: \/opt\/dvm\/rid_acl.dat/' /opt/dvm/config.yml
+ @sed -i 's/file: tg_acl.dat/file: \/opt\/dvm\/tg_acl.dat/' /opt/dvm/config.yml
+
+install-service: install install-config-files
+ @useradd --user-group -M --system dvmhost --shell /bin/false || true
+ @usermod --groups dialout --append dvmhost || true
+ @mkdir /opt/dvm/log || true
+ @chown dvmhost:dvmhost /opt/dvm/log
+ @cp ./linux/dvmhost.service /lib/systemd/system/
+ @systemctl enable dvmhost.service
+
+uninstall-service:
+ @systemctl stop dvmhost.service || true
+ @systemctl disable dvmhost.service || true
+ @rm -f /lib/systemd/system/dvmhost.service || true
diff --git a/linux/dvmhost.service b/linux/dvmhost.service
new file mode 100644
index 00000000..2963d010
--- /dev/null
+++ b/linux/dvmhost.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=DVMProject Host Radio Service
+After=syslog.target network.target
+
+[Service]
+User=mmdvm
+Type=forking
+ExecStart=/opt/dvm/bin/dvmhost -c /opt/dvm/config.yml
+Restart=on-abnormal
+
+[Install]
+WantedBy=multi-user.target
\ No newline at end of file
diff --git a/modem/port/PseudoPTYPort.cpp b/modem/port/PseudoPTYPort.cpp
new file mode 100644
index 00000000..8bc00f6f
--- /dev/null
+++ b/modem/port/PseudoPTYPort.cpp
@@ -0,0 +1,117 @@
+/**
+* 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) 2020,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/PseudoPTYPort.h"
+#include "Log.h"
+
+#include
+#include
+#include
+
+using namespace modem::port;
+
+#if !defined(_WIN32) && !defined(_WIN64)
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#if defined(__linux__)
+ #include
+#else
+ #include
+#endif
+
+// ---------------------------------------------------------------------------
+// Public Class Members
+// ---------------------------------------------------------------------------
+///
+/// Initializes a new instance of the PseudoPTYPort class.
+///
+/// Serial port device.
+/// Serial port speed.
+///
+PseudoPTYPort::PseudoPTYPort(const std::string& symlink, SERIAL_SPEED speed, bool assertRTS) : UARTPort(speed, assertRTS),
+ m_symlink(symlink)
+{
+ /* stub */
+}
+
+///
+/// Finalizes a instance of the PseudoPTYPort class.
+///
+PseudoPTYPort::~PseudoPTYPort()
+{
+ /* stub */
+}
+
+///
+/// Opens a connection to the serial port.
+///
+/// True, if connection is opened, otherwise false.
+bool PseudoPTYPort::open()
+{
+ assert(m_fd == -1);
+
+ int slavefd;
+ char slave[300];
+ int result = ::openpty(&m_fd, &slavefd, slave, NULL, NULL);
+ if (result < 0) {
+ ::LogError(LOG_HOST, "Cannot open the pseudo tty - errno : %d", errno);
+ return false;
+ }
+
+ // remove any previous stale symlink
+ ::unlink(m_symlink.c_str());
+
+ int ret = ::symlink(slave, m_symlink.c_str());
+ if (ret != 0) {
+ ::LogError(LOG_HOST, "Cannot make symlink to %s with %s", slave, m_symlink.c_str());
+ close();
+ return false;
+ }
+
+ ::LogMessage(LOG_HOST, "Made symbolic link from %s to %s", slave, m_symlink.c_str());
+ m_device = std::string(::ttyname(m_fd));
+ return setTermios();
+}
+
+///
+/// Closes the connection to the serial port.
+///
+void PseudoPTYPort::close()
+{
+ UARTPort::close();
+ ::unlink(m_symlink.c_str());
+}
+
+#endif
diff --git a/modem/port/PseudoPTYPort.h b/modem/port/PseudoPTYPort.h
new file mode 100644
index 00000000..ddf127e4
--- /dev/null
+++ b/modem/port/PseudoPTYPort.h
@@ -0,0 +1,72 @@
+/**
+* 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) 2020,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(__PSEUDO_PTY_PORT_H__)
+#define __PSEUDO_PTY_PORT_H__
+
+#if !defined(_WIN32) && !defined(_WIN64)
+
+#include "Defines.h"
+#include "modem/port/UARTPort.h"
+
+#include
+
+namespace modem
+{
+ namespace port
+ {
+ // ---------------------------------------------------------------------------
+ // Class Declaration
+ // This class implements low-level routines to communicate via a Linux
+ // PTY serial port.
+ // ---------------------------------------------------------------------------
+
+ class HOST_SW_API PseudoPTYPort : public UARTPort
+ {
+ public:
+ /// Initializes a new instance of the PseudoPTYPort class.
+ PseudoPTYPort(const std::string& symlink, SERIAL_SPEED speed, bool assertRTS = false);
+ /// Finalizes a instance of the PseudoPTYPort class.
+ virtual ~PseudoPTYPort();
+
+ /// Opens a connection to the serial port.
+ virtual bool open();
+
+ /// Closes the connection to the serial port.
+ virtual void close();
+
+ protected:
+ std::string m_symlink;
+ }; // class HOST_SW_API PseudoPTYPort : public UARTPort
+ } // namespace port
+} // namespace Modem
+
+#endif
+
+#endif // __PSEUDO_PTY_PORT_H__
diff --git a/modem/port/UARTPort.cpp b/modem/port/UARTPort.cpp
index 6e39bfcf..ce2465aa 100644
--- a/modem/port/UARTPort.cpp
+++ b/modem/port/UARTPort.cpp
@@ -258,7 +258,7 @@ void UARTPort::close()
/// Serial port device.
/// Serial port speed.
///
-UARTPort::UARTPort(const std::string& device, SERIAL_SPEED speed, bool assertRTS) :
+UARTPort::UARTPort(const std::string& device, SERIAL_SPEED speed, bool assertRTS) :
m_isOpen(false),
m_device(device),
m_speed(speed),
@@ -302,101 +302,7 @@ bool UARTPort::open()
return false;
}
- 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
-
- m_isOpen = true;
- return true;
+ return setTermios();
}
///
@@ -515,7 +421,7 @@ void UARTPort::close()
///
int UARTPort::setNonblock(bool nonblock)
{
- int flag = ::fcntl(m_fd, F_GETFD, 0);
+ int flag = ::fcntl(m_fd, F_GETFL, 0);
if (nonblock)
flag |= O_NONBLOCK;
@@ -530,6 +436,18 @@ int UARTPort::setNonblock(bool nonblock)
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
+UARTPort::UARTPort(SERIAL_SPEED speed, bool assertRTS) :
+ m_isOpen(false),
+ m_speed(speed),
+ m_assertRTS(assertRTS),
+#if defined(_WIN32) || defined(_WIN64)
+ m_handle(INVALID_HANDLE_VALUE)
+#else
+ m_fd(-1)
+#endif
+{
+ /* stub */
+}
#if defined(_WIN32) || defined(_WIN64)
///
@@ -596,4 +514,107 @@ bool UARTPort::canWrite()
return true;
#endif
}
+
+///
+///
+///
+///
+bool UARTPort::setTermios()
+{
+ 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
+
+ m_isOpen = true;
+ return true;
+}
#endif
diff --git a/modem/port/UARTPort.h b/modem/port/UARTPort.h
index c8c4f266..81159902 100644
--- a/modem/port/UARTPort.h
+++ b/modem/port/UARTPort.h
@@ -94,6 +94,9 @@ namespace modem
#endif
protected:
+ /// Initializes a new instance of the UARTPort class.
+ UARTPort(SERIAL_SPEED speed, bool assertRTS = false);
+
bool m_isOpen;
std::string m_device;
@@ -111,6 +114,9 @@ namespace modem
#else
///
bool canWrite();
+
+ ///
+ bool setTermios();
#endif
}; // class HOST_SW_API UARTPort : public ISerialPort, public IModemPort
} // namespace port