/*
* 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 <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 "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 < LogicAnalyzerServer * > ( 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 < traceLength ; i + + ) {
write_gpmc_uint64_t ( 0x800 + i , 0x0 ) ;
}
}
int gpmc_get_channel_traces ( TQDoubleArray & traceData , TQDoubleArray & positionData , unsigned int traceNumber ) {
unsigned int i ;
int traceLength ;
double timestep ;
double position ;
traceLength = gpmc_sample_count ( ) ;
timestep = gpmc_timestep ( ) ;
traceData . resize ( traceLength ) ;
positionData . resize ( traceLength ) ;
position = 0 ;
for ( i = 0 ; i < traceLength ; i + + ) {
traceData [ i ] = ( ( read_gpmc_uint64_t ( 0x800 + i ) & ( ( uint64_t ) 1 < < traceNumber ) ) > > 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 < channels ; i + + ) {
TQDoubleArray traceData ;
TQDoubleArray positionData ;
gpmc_get_channel_traces ( traceData , positionData , i ) ;
ds < < traceData ;
ds < < positionData ;
}
writeEndOfFrame ( ) ;
}
else if ( instrumentCommand = = " GETHORIZONTALDIVCOUNT " ) { // Want the number of horizontal divisions available
// One horizontal division per sample
TQ_INT16 divisions = gpmc_sample_count ( ) ;
if ( divisions > = 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 < channels ; i + + ) {
ds < < samples ;
}
writeEndOfFrame ( ) ;
}
else if ( instrumentCommand = = " GETNUMBEROFCHANNELS " ) { // Want the number of channels available
TQ_INT32 channels = gpmc_channel_count ( ) ;
if ( channels > 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 < channels ; i + + ) {
gpmc_get_channel_name ( name , i ) ;
ds < < name ;
}
writeEndOfFrame ( ) ;
}
else if ( instrumentCommand = = " GETCHANNELACTIVE " ) { // Want to get channel activity
TQ_INT16 state ;
int i ;
int channels = gpmc_channel_count ( ) ;
ds < < TQString ( " ACK " ) ;
for ( i = 0 ; i < channels ; i + + ) {
// All channels are always active
state = 1 ;
ds < < state ;
}
writeEndOfFrame ( ) ;
}
else if ( instrumentCommand = = " GETSECONDSSDIV " ) { // Want to get seconds per division
double secondsdiv ;
int i ;
int channels = gpmc_channel_count ( ) ;
ds < < TQString ( " ACK " ) ;
for ( i = 0 ; i < channels ; i + + ) {
secondsdiv = gpmc_timestep ( ) ;
ds < < secondsdiv ;
}
writeEndOfFrame ( ) ;
}
else if ( instrumentCommand = = " GETRUNNING " ) { // Want to get run status
TQ_INT16 running = gpmc_get_running ( ) ;
ds < < TQString ( " ACK " ) ;
ds < < running ;
writeEndOfFrame ( ) ;
}
else if ( instrumentCommand = = " SETRUNNING " ) { // Want to change run status
TQ_INT16 value ;
ds > > 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 - - ;
}