summaryrefslogtreecommitdiffstats
path: root/servers/serial_server_lin/src
diff options
context:
space:
mode:
authorTimothy Pearson <kb9vqf@pearsoncomputing.net>2014-01-13 05:08:51 -0600
committerTimothy Pearson <kb9vqf@pearsoncomputing.net>2014-01-13 05:08:51 -0600
commitc912b0f1d3994cde07d472e2c53f0375518a74df (patch)
treeb2def2445d01a2f8151c54c855c5db252b0d2e74 /servers/serial_server_lin/src
parent06f302400e33d8f1b8175a08ccd6630b7ae4ee40 (diff)
downloadulab-c912b0f1d3994cde07d472e2c53f0375518a74df.tar.gz
ulab-c912b0f1d3994cde07d472e2c53f0375518a74df.zip
Add serial console server
Diffstat (limited to 'servers/serial_server_lin/src')
-rw-r--r--servers/serial_server_lin/src/Makefile.am11
-rw-r--r--servers/serial_server_lin/src/main.cpp64
-rw-r--r--servers/serial_server_lin/src/serial_server.cpp353
-rw-r--r--servers/serial_server_lin/src/serial_server.h99
4 files changed, 527 insertions, 0 deletions
diff --git a/servers/serial_server_lin/src/Makefile.am b/servers/serial_server_lin/src/Makefile.am
new file mode 100644
index 0000000..643df79
--- /dev/null
+++ b/servers/serial_server_lin/src/Makefile.am
@@ -0,0 +1,11 @@
+INCLUDES= $(all_includes) $(KDE_INCLUDES)/tde -I/usr/include/sasl
+KDE_CXXFLAGS = $(USE_EXCEPTIONS)
+
+bin_PROGRAMS = ulab_serialserver
+
+ulab_serialserver_SOURCES = main.cpp serial_server.cpp
+
+ulab_serialserver_METASOURCES = AUTO
+ulab_serialserver_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_QT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor -ltdekrbsocket -ltqtrla
+
+KDE_OPTIONS = nofinal
diff --git a/servers/serial_server_lin/src/main.cpp b/servers/serial_server_lin/src/main.cpp
new file mode 100644
index 0000000..22810a0
--- /dev/null
+++ b/servers/serial_server_lin/src/main.cpp
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright (C) 2014 by Timothy Pearson *
+ * kb9vqf@pearsoncomputing.net *
+ * *
+ * 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., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <tqdatetime.h>
+#include <tqfile.h>
+#include <tqdir.h>
+
+#include <tdeapplication.h>
+#include <tdestartupinfo.h>
+#include <tdecmdlineargs.h>
+#include <tdeaboutdata.h>
+#include <ksimpleconfig.h>
+
+#include "serial_server.h"
+
+static const char description[] = I18N_NOOP("uLab Serial Server");
+
+static const char version[] = "v0.0.1";
+
+int main(int argc, char *argv[])
+{
+ TDEAboutData aboutData( "ulab_serial_server", I18N_NOOP("uLab Serial Server"),
+ version, description, TDEAboutData::License_GPL,
+ "(c) 2014, Timothy Pearson");
+ aboutData.addAuthor("Timothy Pearson",0, "kb9vqf@pearsoncomputing.net");
+ TDECmdLineArgs::init( argc, argv, &aboutData );
+ TDEApplication::disableAutoDcopRegistration();
+
+ TDEApplication app(false, false);
+
+ TDEStartupInfo::appStarted();
+
+ KSimpleConfig config(TQDir::currentDirPath() + "/ulab_serialserver.conf", false);
+ config.setGroup("Server");
+ SerialServer serialsvr(0, config.readNumEntry("port", 4018), &config);
+ return app.exec();
+
+}
diff --git a/servers/serial_server_lin/src/serial_server.cpp b/servers/serial_server_lin/src/serial_server.cpp
new file mode 100644
index 0000000..384409e
--- /dev/null
+++ b/servers/serial_server_lin/src/serial_server.cpp
@@ -0,0 +1,353 @@
+/*
+ * Remote Laboratory Serial Server
+ *
+ * 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 3 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (c) 2014 Timothy Pearson
+ * Raptor Engineering
+ * http://www.raptorengineeringinc.com
+ */
+
+#include <stdio.h> /* perror() */
+#include <stdlib.h> /* atoi() */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h> /* read() */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/signal.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <math.h>
+
+#include <tqtimer.h>
+
+#include <tdelocale.h>
+
+#include "serial_server.h"
+
+#define FLUSH_IN 0
+#define FLUSH_OUT 1
+#define FLUSH_BOTH 2
+
+#define ABORT_SOCKET(s) s->close(); \
+ s->disconnect(); \
+ delete s; \
+ s = NULL;
+
+/* exception handling */
+struct exit_exception {
+ int c;
+ exit_exception(int c):c(c) { }
+};
+
+/*
+ The SerialSocket class provides a socket that is connected with a client.
+ For every client that connects to the server, the server creates a new
+ instance of this class.
+*/
+SerialSocket::SerialSocket(int sock, TQObject *parent, const char *name) :
+ TDEKerberosServerSocket(parent, name), m_criticalSection(0), m_pollInterval(10), enableDebug(false), m_loopTimer(NULL), m_config(static_cast<SerialServer*>(parent)->m_config) {
+
+ // Read settings
+ m_config->setGroup("Tuning");
+ m_pollInterval = m_config->readNumEntry("pollInterval", m_pollInterval);
+ enableDebug = m_config->readBoolEntry("enableDebug", enableDebug);
+
+ // Initialize timers
+ m_kerberosInitTimer = new TQTimer();
+ connect(m_kerberosInitTimer, SIGNAL(timeout()), this, SLOT(finishKerberosHandshake()));
+
+ setServiceName("ulab");
+
+ line = 0;
+ connect(this, SIGNAL(connectionClosed()), SLOT(connectionClosedHandler()));
+ connect(this, SIGNAL(connectionClosed()), parent, SLOT(remoteConnectionClosed()));
+ setSocket(sock);
+}
+
+SerialSocket::~SerialSocket() {
+ if (m_kerberosInitTimer) {
+ m_kerberosInitTimer->stop();
+ delete m_kerberosInitTimer;
+ m_kerberosInitTimer = NULL;
+ }
+ if (m_loopTimer) {
+ m_loopTimer->stop();
+ delete m_loopTimer;
+ m_loopTimer = NULL;
+ }
+}
+
+void SerialSocket::close() {
+ if (state() == TQSocket::Connected) {
+ TDEKerberosServerSocket::close();
+ connectionClosedHandler();
+ TQTimer::singleShot(0, parent(), SLOT(remoteConnectionClosed()));
+ }
+}
+
+void SerialSocket::connectionClosedHandler() {
+ if (enableDebug) {
+ printf("[DEBUG] Connection from %s closed\n\r", m_remoteHost.ascii()); fflush(stdout);
+ }
+
+ if (m_criticalSection > 0) {
+ throw exit_exception(-1);
+ }
+}
+
+void SerialSocket::initiateKerberosHandshake() {
+ setUsingKerberos(true);
+ m_kerberosInitTimer->start(100, TRUE);
+}
+
+void SerialSocket::finishKerberosHandshake() {
+ if (kerberosStatus() == TDEKerberosServerSocket::KerberosInitializing) {
+ m_kerberosInitTimer->start(100, TRUE);
+ return;
+ }
+ if (kerberosStatus() == TDEKerberosServerSocket::KerberosInUse) {
+ m_config->setGroup("Security");
+ TQString masterUser = m_config->readEntry("masteruser");
+ TQString masterRealm = m_config->readEntry("masterrealm");
+ if (masterRealm == "") {
+ masterRealm = "(NULL)";
+ }
+ if ((m_authenticatedUserName != masterUser) || (m_authenticatedRealmName != masterRealm)) {
+ if (enableDebug) {
+ printf("[DEBUG] Connection from %s closed due to authentication failure (attempted connection as user %s@%s)\n\r", m_remoteHost.ascii(), m_authenticatedUserName.ascii(), m_authenticatedRealmName.ascii()); fflush(stdout);
+ }
+ close();
+ return;
+ }
+ if (setupSerial() != 0) {
+ if (enableDebug) {
+ printf("[DEBUG] Connection from %s closed due to serial port initialization failure\n\r", m_remoteHost.ascii()); fflush(stdout);
+ }
+ close();
+ return;
+ }
+
+ TQDataStream ds(this);
+ ds.setPrintableData(true);
+ ds << TQString("OK");
+ writeEndOfFrame();
+
+ // Inform user of baudrate
+ TQString infoString;
+ infoString = TQString("Serial connection established with a baudrate of %1\rReady for data").arg(m_baudRate);
+ TQByteArray data;
+ data.duplicate(infoString.ascii(), strlen(infoString.ascii()));
+ ds << TQString("DATA");
+ ds << data;
+ writeEndOfFrame();
+
+ enterCommandLoop();
+ return;
+ }
+ else {
+ if (enableDebug) {
+ printf("[DEBUG] Connection from %s closed due to Kerberos failure\n\r", m_remoteHost.ascii()); fflush(stdout);
+ }
+ close();
+ return;
+ }
+}
+
+int SerialSocket::setupSerial() {
+ struct termios oldtio, newtio;
+
+ m_config->setGroup("Serial");
+ TQString serialDevice = m_config->readEntry("serialdevice", "/dev/ttyS0");
+ m_baudRate = m_config->readEntry("baudrate", "9600");
+
+ m_fd_tty = ::open(serialDevice.ascii(), O_RDWR | O_NOCTTY | O_NONBLOCK | O_APPEND);
+ if (m_fd_tty < 0) {
+ printf("[FAIL] Unable to open serial device %s\n\r", serialDevice.ascii()); fflush(stdout);
+ return 1;
+ }
+
+ tcgetattr(m_fd_tty, &oldtio); // Save current port settings
+
+ long serialBaud;
+ if (m_baudRate == "1200") {
+ serialBaud = B1200;
+ }
+ else if (m_baudRate == "9600") {
+ serialBaud = B9600;
+ }
+ else if (m_baudRate == "19200") {
+ serialBaud = B19200;
+ }
+ else if (m_baudRate == "115200") {
+ serialBaud = B115200;
+ }
+ else {
+ printf("[WARNING] Invalid baudrate %s specified, selecting 9600 instead\n\r", m_baudRate.ascii()); fflush(stdout);
+ serialBaud = B9600;
+ }
+
+ bzero(&newtio, sizeof(newtio));
+ newtio.c_cflag = serialBaud | CS8 | CLOCAL | CREAD;
+ newtio.c_iflag = IGNPAR;
+ newtio.c_oflag = 0;
+
+ // Set input mode (non-canonical, no echo,...)
+ newtio.c_lflag = 0;
+
+ newtio.c_cc[VTIME] = 0; // Inter-character timer unused
+ newtio.c_cc[VMIN] = 0; // Blocking read unused
+
+ tcflush(m_fd_tty, TCIFLUSH);
+ tcsetattr(m_fd_tty, TCSANOW, &newtio);
+
+ return 0;
+}
+
+void SerialSocket::commandLoop() {
+ int cc;
+ int ret;
+ char buffer[1024];
+ bool transferred_data;
+ TQString instrumentCommand;
+
+ m_criticalSection++;
+ try {
+ transferred_data = false;
+ if (state() == TQSocket::Connected) {
+ if (canReadFrame()) {
+ TQDataStream ds(this);
+ ds.setPrintableData(true);
+ ds >> instrumentCommand;
+
+ if (instrumentCommand != "") {
+ if ((instrumentCommand == "SEND")) { // Send data to serial port
+ TQByteArray data;
+ ds >> data;
+ ret = write(m_fd_tty, data.data(), data.size());
+ while ((ret < 0) && (errno == EAGAIN)) {
+ usleep(1000);
+ ret = write(m_fd_tty, data.data(), data.size());
+ }
+ ioctl(m_fd_tty, TCFLSH, FLUSH_OUT);
+ if (ret >= 0) {
+ ds << TQString("ACK");
+ if (enableDebug) {
+ printf("[DEBUG] Wrote %d bytes to the serial port\n\r", ret); fflush(stdout);
+ }
+ }
+ else {
+ ds << TQString("NCK");
+ }
+ writeEndOfFrame();
+ }
+ else if ((instrumentCommand == "PING")) {
+ ds << TQString("PONG");
+ writeEndOfFrame();
+ }
+ else {
+ printf("[WARNING] Received unknown command %s from host %s\n\r", instrumentCommand.ascii(), m_remoteHost.ascii()); fflush(stdout);
+ ds << TQString("NCK");
+ writeEndOfFrame();
+ }
+ }
+
+ transferred_data = true;
+ }
+ cc = read(m_fd_tty, buffer, 1024);
+ if (cc > 0) {
+ TQDataStream ds(this);
+ ds.setPrintableData(true);
+ ds >> instrumentCommand;
+
+ TQByteArray data;
+ data.duplicate(buffer, cc);
+ ds << TQString("DATA");
+ ds << data;
+ writeEndOfFrame();
+
+ transferred_data = true;
+ if (enableDebug) {
+ printf("[DEBUG] Got %d bytes from the serial port\n\r", cc); fflush(stdout);
+ }
+ }
+ }
+ m_criticalSection--;
+ if (transferred_data) {
+ if (m_loopTimer) m_loopTimer->start(0, TRUE);
+ }
+ else {
+ if (m_loopTimer) m_loopTimer->start(m_pollInterval, TRUE);
+ }
+ return;
+ }
+ catch (...) {
+ m_criticalSection--;
+ return;
+ }
+}
+
+int SerialSocket::enterCommandLoop() {
+ if (!m_loopTimer) {
+ m_loopTimer = new TQTimer();
+ connect(m_loopTimer, SIGNAL(timeout()), this, SLOT(commandLoop()));
+ }
+ if (m_loopTimer) m_loopTimer->start(0, TRUE);
+ return 0;
+}
+
+/*
+ The SerialServer class handles new connections to the server. For every
+ client that connects, it creates a new SerialSocket -- that instance is now
+ responsible for the communication with that client.
+*/
+SerialServer::SerialServer(TQObject* parent, int port, KSimpleConfig* config) :
+ TQServerSocket( port, 1, parent ), m_config(config), m_numberOfConnections(0) {
+
+ if ( !ok() ) {
+ printf("[ERROR] Failed to bind to port %d\n\r", port);
+ exit(1);
+ }
+
+ printf("[INFO] Server started on port %d\n\r", port); fflush(stdout);
+}
+
+SerialServer::~SerialServer() {
+ //
+}
+
+void SerialServer::newConnection(int socket) {
+ SerialSocket *s = new SerialSocket(socket, this);
+ s->m_remoteHost = s->peerAddress().toString();
+ printf("[DEBUG] New connection from %s\n\r", s->m_remoteHost.ascii()); fflush(stdout);
+ if (m_numberOfConnections > 0) {
+ printf("[DEBUG] Connection from %s closed due to multiple access attempt\n\r", s->m_remoteHost.ascii()); fflush(stdout);
+ ABORT_SOCKET(s)
+ return;
+ }
+ connect(s, SIGNAL(connectionClosed()), s, SLOT(deleteLater()));
+ s->initiateKerberosHandshake();
+ emit newConnect(s);
+}
+
+void SerialServer::remoteConnectionClosed() {
+ m_numberOfConnections--;
+}
diff --git a/servers/serial_server_lin/src/serial_server.h b/servers/serial_server_lin/src/serial_server.h
new file mode 100644
index 0000000..0c4a49a
--- /dev/null
+++ b/servers/serial_server_lin/src/serial_server.h
@@ -0,0 +1,99 @@
+/*
+ * Remote Laboratory Serial Server
+ *
+ * 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 3 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (c) 2012-2013 Timothy Pearson
+ * Raptor Engineering
+ * http://www.raptorengineeringinc.com
+ */
+
+#include <tqsocket.h>
+#include <tqserversocket.h>
+#include <tqapplication.h>
+#include <tqvbox.h>
+#include <tqtextview.h>
+#include <tqlabel.h>
+#include <tqpushbutton.h>
+#include <tqtextstream.h>
+
+#include <ksimpleconfig.h>
+
+#include <tdekrbserversocket.h>
+
+#include <tqtrla.h>
+
+#define MAGIC_NUMBER 1
+#define PROTOCOL_VERSION 1
+
+class SerialSocket : public TDEKerberosServerSocket
+{
+ Q_OBJECT
+
+ public:
+ SerialSocket(int sock, TQObject *parent=0, const char *name=0);
+ ~SerialSocket();
+
+ public:
+ void close();
+ void initiateKerberosHandshake();
+ int enterCommandLoop();
+
+ private slots:
+ void finishKerberosHandshake();
+ void connectionClosedHandler();
+ int setupSerial();
+ void commandLoop();
+
+ private:
+ int line;
+ int m_criticalSection;
+ int m_pollInterval;
+ bool enableDebug;
+ TQString m_remoteHost;
+ TQString m_baudRate;
+ int m_fd_tty;
+
+ TQTimer* m_kerberosInitTimer;
+ TQTimer* m_loopTimer;
+
+ KSimpleConfig* m_config;
+
+ friend class SerialServer;
+};
+
+class SerialServer : public TQServerSocket
+{
+ Q_OBJECT
+
+ public:
+ SerialServer(TQObject* parent=0, int port=0, KSimpleConfig* config=0);
+ ~SerialServer();
+
+ void newConnection(int socket);
+
+ private slots:
+ void remoteConnectionClosed();
+
+ signals:
+ void newConnect(SerialSocket*);
+
+ private:
+ KSimpleConfig* m_config;
+ int m_numberOfConnections;
+
+ friend class SerialSocket;
+
+}; \ No newline at end of file