/* * Remote Laboratory Logic Analyzer 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-2019 Timothy Pearson * Raptor Engineering * http://www.raptorengineeringinc.com */ #include /* perror() */ #include /* atoi() */ #include #include #include /* read() */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "logic_analyzer_server.h" #include "bbb-gpmc-init.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) { } }; void gpmc_clear_channel_traces(); /* The LogicAnalyzerSocket 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. */ LogicAnalyzerSocket::LogicAnalyzerSocket(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(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); } LogicAnalyzerSocket::~LogicAnalyzerSocket() { 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 LogicAnalyzerSocket::close() { if (state() == TQSocket::Connected) { TDEKerberosServerSocket::close(); connectionClosedHandler(); TQTimer::singleShot(0, parent(), SLOT(remoteConnectionClosed())); } } void LogicAnalyzerSocket::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 LogicAnalyzerSocket::initiateKerberosHandshake() { setUsingKerberos(true); m_kerberosInitTimer->start(100, TRUE); } void LogicAnalyzerSocket::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 (setupGPMC() != 0) { if (enableDebug) { printf("[DEBUG] Connection from %s closed due to GPMC initialization failure\n\r", m_remoteHost.ascii()); fflush(stdout); } close(); return; } TQDataStream ds(this); ds.setPrintableData(true); ds << TQString("OK"); 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 LogicAnalyzerSocket::setupGPMC() { int ret; ret = setup_gpmc_bbb(); if (ret == 0) { // Verify attached uLab hardware model and version unsigned char model = read_gpmc(0x00); unsigned char version = read_gpmc(0x01); if ((model != 0x42) || (version < 1)) { printf("A compatible uLab hardware debug interface was not detected! Please verify your configuration.\n"); return -1; } printf("[DEBUG] Detected a compatible uLab hardware debug interface (model number 0x%02x, firmware version 0x%02x)\n", model, version); gpmc_clear_channel_traces(); } return 0; } int gpmc_channel_count() { return 64; } int gpmc_sample_count() { return 2048; } double gpmc_timestep() { return ((read_gpmc_uint16_t(0x0e/2))*1e-9); } int gpmc_get_running() { return read_gpmc(0x0d) & 0x1; } void gpmc_set_running(bool running) { if (running) { write_gpmc(0x0d, read_gpmc(0x0d) | 0x1); } else { write_gpmc(0x0d, read_gpmc(0x0d) & ~0x1); } } int gpmc_get_channel_name(TQString &name, unsigned int traceNumber) { int offset; char rawName[32]; memcpy_from_gpmc(rawName, 0x800 + (traceNumber * 32), 32); for (offset=0; offset<32; offset++) { if (rawName[offset] != 0) { break; } } name = TQString(rawName + offset); name.replace("<", "<"); name.replace(">", ">"); return 0; } void gpmc_clear_channel_traces() { unsigned int i; int traceLength; traceLength = gpmc_sample_count(); for (i=0; i> traceNumber); positionData[i] = position; position = position + timestep; } return traceLength; } void LogicAnalyzerSocket::commandLoop() { 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 == "LOGICANALYZER")) { // requesting logic analyzer access ds << TQString("ACK"); writeEndOfFrame(); } else if ((instrumentCommand == "RESET")) { // requesting reset // Nothing to reset... ds << TQString("ACK"); writeEndOfFrame(); } else if ((instrumentCommand == "GETLOGICTRACES")) { // Want all channel traces ds << TQString("ACK"); int i; int channels = gpmc_channel_count(); for (i=0; i= 0) { ds << TQString("ACK"); ds << divisions; writeEndOfFrame(); } else { ds << TQString("NCK"); writeEndOfFrame(); } } else if (instrumentCommand == "GETTRACESAMPLECOUNT") { // Want to get number of samples in a trace int i; int channels = gpmc_channel_count(); TQ_INT32 samples = gpmc_sample_count(); ds << TQString("ACK"); for (i=0; i 0) { ds << TQString("ACK"); ds << channels; writeEndOfFrame(); } else { ds << TQString("NCK"); writeEndOfFrame(); } } else if (instrumentCommand == "GETCHANNELNAME") { // Want to get channel name TQString name; int i; int channels = gpmc_channel_count(); ds << TQString("ACK"); for (i=0; i> value; gpmc_set_running(value); ds << TQString("ACK"); 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; } } 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 LogicAnalyzerSocket::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 LogicAnalyzerServer class handles new connections to the server. For every client that connects, it creates a new LogicAnalyzerSocket -- that instance is now responsible for the communication with that client. */ LogicAnalyzerServer::LogicAnalyzerServer(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); } LogicAnalyzerServer::~LogicAnalyzerServer() { // } void LogicAnalyzerServer::newConnection(int socket) { LogicAnalyzerSocket *s = new LogicAnalyzerSocket(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 LogicAnalyzerServer::remoteConnectionClosed() { m_numberOfConnections--; }