/*
* Remote Laboratory FPGA 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 <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 "fpga_conn.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 ) { }
} ;
/*
The FPGASocket 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 .
*/
FPGASocket : : FPGASocket ( 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 < FPGAServer * > ( 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 ) ;
}
FPGASocket : : ~ FPGASocket ( ) {
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 FPGASocket : : close ( ) {
if ( state ( ) = = TQSocket : : Connected ) {
TDEKerberosServerSocket : : close ( ) ;
connectionClosedHandler ( ) ;
TQTimer : : singleShot ( 0 , parent ( ) , SLOT ( remoteConnectionClosed ( ) ) ) ;
}
}
void FPGASocket : : connectionClosedHandler ( ) {
if ( enableDebug ) {
printf ( " [DEBUG] Connection from %s closed \n \r " , m_remoteHost . ascii ( ) ) ; fflush ( stdout ) ;
}
if ( m_interfaceType = = " gpmc " ) {
// Reset user device
write_gpmc ( 0x0c , read_gpmc ( 0x0c ) | 0x03 ) ;
usleep ( 100 ) ;
write_gpmc ( 0x0c , read_gpmc ( 0x0c ) & ~ 0x03 ) ;
}
if ( m_criticalSection > 0 ) {
throw exit_exception ( - 1 ) ;
}
}
void FPGASocket : : initiateKerberosHandshake ( ) {
setUsingKerberos ( true ) ;
m_kerberosInitTimer - > start ( 100 , TRUE ) ;
}
void FPGASocket : : 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 ;
}
m_config - > setGroup ( " FPGA " ) ;
m_interfaceType = m_config - > readEntry ( " interface " , " serial " ) ;
if ( m_interfaceType = = " serial " ) {
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 ;
}
}
else if ( m_interfaceType = = " gpmc " ) {
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 ;
}
}
else {
if ( enableDebug ) {
printf ( " [DEBUG] Connection from %s closed due to incorrect interface type specification in configuration file \n \r " , m_remoteHost . ascii ( ) ) ; fflush ( stdout ) ;
}
close ( ) ;
return ;
}
// If enabled, reset user FPGA to known good state
m_config - > setGroup ( " Programming " ) ;
TQString resetScript = m_config - > readEntry ( " userresetscript " ) ;
if ( resetScript ! = " " ) {
system ( resetScript . ascii ( ) ) ;
}
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 FPGASocket : : setupSerial ( ) {
struct termios oldtio , newtio ;
m_config - > setGroup ( " FPGA " ) ;
TQString serialDevice = m_config - > readEntry ( " serialdevice " , " /dev/ttyS0 " ) ;
TQString desiredBaudRate = 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 ( desiredBaudRate = = " 1200 " ) {
serialBaud = B1200 ;
}
else if ( desiredBaudRate = = " 9600 " ) {
serialBaud = B9600 ;
}
else if ( desiredBaudRate = = " 19200 " ) {
serialBaud = B19200 ;
}
else if ( desiredBaudRate = = " 115200 " ) {
serialBaud = B115200 ;
}
else {
printf ( " [WARNING] Invalid baudrate %s specified, selecting 9600 instead \n \r " , desiredBaudRate . 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 ;
}
int FPGASocket : : setupGPMC ( ) {
int i ;
int ret ;
m_stateTXRequested = false ;
m_stateImageRXRequested = false ;
m_stateImageTXRequested = false ;
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 ) ;
// Reset user device
write_gpmc ( 0x0c , read_gpmc ( 0x0c ) | 0x03 ) ;
usleep ( 100 ) ;
write_gpmc ( 0x0c , read_gpmc ( 0x0c ) & ~ 0x03 ) ;
// Clear out DSP and LCD RAM
unsigned char dsp_ram_bits = read_gpmc ( 0x0b ) ;
unsigned int dsp_ram_offset = ( 1 < < dsp_ram_bits ) ;
unsigned int dsp_ram_size = ( 1 < < dsp_ram_bits ) ;
for ( i = 0 ; i < dsp_ram_size ; i + + ) {
write_gpmc ( dsp_ram_offset + i , 0x00 ) ;
}
for ( i = 0 ; i < 32 ; i + + ) {
write_gpmc ( 0x20 + i , 0x00 ) ;
}
}
return 0 ;
}
void FPGASocket : : commandLoop ( ) {
int cc ;
int ret ;
char buffer [ 1024 ] ;
bool transferred_data ;
m_criticalSection + + ;
try {
transferred_data = false ;
if ( state ( ) = = TQSocket : : Connected ) {
if ( m_interfaceType = = " serial " ) {
cc = read ( m_fd_tty , buffer , 1024 ) ;
if ( cc > 0 ) {
writeBlock ( buffer , cc ) ;
flush ( ) ;
transferred_data = true ;
if ( enableDebug ) {
printf ( " [DEBUG] Got %d bytes from the serial port \n \r " , cc ) ; fflush ( stdout ) ;
}
}
if ( canReadData ( ) ) {
cc = readBlock ( buffer , 1024 ) ;
if ( cc > 0 ) {
ret = write ( m_fd_tty , buffer , cc ) ;
// HACK
// This works around a buffer overflow on FTDI serial devices
// It may not be sufficient for baudrates less than 115200!
if ( cc > 128 ) {
usleep ( 100000 ) ;
}
while ( ( ret < 0 ) & & ( errno = = EAGAIN ) ) {
usleep ( 1000 ) ;
ret = write ( m_fd_tty , buffer , cc ) ;
}
if ( ret < 0 ) {
// ERROR
printf ( " [ERROR] Failed to transmit data to serial port (%s, code %d)! Continuing, but data was likely lost \n \r " , strerror ( errno ) , errno ) ; fflush ( stdout ) ;
}
ioctl ( m_fd_tty , TCFLSH , FLUSH_OUT ) ;
transferred_data = true ;
if ( enableDebug ) {
printf ( " [DEBUG] Got %d bytes from the network interface \n \r " , cc ) ; fflush ( stdout ) ;
}
}
}
}
else if ( m_interfaceType = = " gpmc " ) {
if ( m_stateImageTXRequested ) {
if ( read_gpmc ( 0x0a ) & 0x02 ) {
m_stateImageTXRequested = false ;
// Transmit image back to client
unsigned char dsp_ram_bits = read_gpmc ( 0x0b ) ;
unsigned int dsp_ram_size = ( 1 < < dsp_ram_bits ) ;
unsigned int dsp_ram_offset = ( 1 < < dsp_ram_bits ) ;
TQByteArray dataToSend ( dsp_ram_size ) ;
memcpy_from_gpmc ( dataToSend . data ( ) , dsp_ram_offset , dsp_ram_size ) ;
int offset = 0 ;
while ( offset < = dsp_ram_size ) {
writeBlock ( dataToSend . data ( ) + offset , 1024 ) ;
writeBufferedData ( ) ;
offset = offset + 1024 ;
}
}
}
else if ( m_stateTXRequested ) {
m_stateTXRequested = false ;
char data [ 42 ] ;
// Read state data from memory map and assemble a reply
memcpy_from_gpmc ( data + 0 , 0x20 , 0x20 ) ; // LCD display
data [ 32 ] = 1 ; // Input mode (locked to Remote)
data [ 33 ] = read_gpmc ( 0x0b ) ; // Number of address bits of DSP RAM
data [ 34 ] = read_gpmc ( 0x02 ) ; // 4-bit LEDs
data [ 35 ] = read_gpmc ( 0x03 ) ; // 8-bit LEDs
data [ 36 ] = read_gpmc ( 0x04 ) ; // 16-bit LEDs (upper byte)
data [ 37 ] = read_gpmc ( 0x05 ) ; // 16-bit LEDs (lower byte)
memcpy_from_gpmc ( data + 38 , 0x06 , 0x04 ) ; // 7-segment LED display
writeBlock ( data , 42 ) ;
writeBufferedData ( ) ;
}
if ( canReadData ( ) ) {
int read_offset = 0 ;
cc = readBlock ( buffer , 1024 ) ;
if ( cc > 0 ) {
if ( m_stateImageRXRequested ) {
unsigned char dsp_ram_bits = read_gpmc ( 0x0b ) ;
unsigned int dsp_ram_offset = ( 1 < < dsp_ram_bits ) ;
unsigned int dsp_ram_size = ( 1 < < dsp_ram_bits ) ;
memcpy_to_gpmc ( buffer , ( dsp_ram_offset + m_stateImageRXCounter ) , cc ) ;
m_stateImageRXCounter = m_stateImageRXCounter + cc ;
if ( m_stateImageRXCounter > = dsp_ram_size ) {
m_stateImageRXRequested = false ;
m_stateImageTXRequested = true ;
// Start user processing
write_gpmc ( 0x0a , read_gpmc ( 0x0a ) | 0x01 ) ;
}
}
else {
// Parse and write state data to the memory map
while ( read_offset < cc ) {
if ( buffer [ read_offset + 0 ] = = ' M ' ) {
// Receive image data and store in FPGA memory
m_stateImageRXRequested = true ;
m_stateImageTXRequested = false ;
m_stateImageRXCounter = 0 ;
read_offset = read_offset + 2 ;
}
else if ( buffer [ read_offset + 0 ] = = ' L ' ) {
m_stateTXRequested = true ;
read_offset = read_offset + 2 ;
}
else if ( buffer [ read_offset + 0 ] = = ' I ' ) {
write_gpmc ( 0x02 , buffer [ read_offset + 2 ] ) ;
read_offset = read_offset + 4 ;
}
else if ( buffer [ read_offset + 0 ] = = ' B ' ) {
write_gpmc ( 0x03 , buffer [ read_offset + 2 ] ) ;
read_offset = read_offset + 4 ;
}
else if ( buffer [ read_offset + 0 ] = = ' C ' ) {
write_gpmc ( 0x04 , buffer [ read_offset + 2 ] ) ;
write_gpmc ( 0x05 , buffer [ read_offset + 4 ] ) ;
read_offset = read_offset + 6 ;
}
else if ( buffer [ read_offset + 0 ] = = ' R ' ) {
write_gpmc ( 0x0c , read_gpmc ( 0x0c ) | 0x01 ) ;
usleep ( 100 ) ;
write_gpmc ( 0x0c , read_gpmc ( 0x0c ) & ~ 0x01 ) ;
read_offset = read_offset + 2 ;
}
else {
printf ( " [WARNING] Received invalid command '%c' from client! Dazed and confused, but continuing... \n " , buffer [ read_offset + 0 ] ) ;
read_offset = read_offset + 2 ;
}
}
if ( m_stateImageTXRequested ) {
m_stateImageTXRequested = false ;
}
}
transferred_data = true ;
if ( enableDebug ) {
printf ( " [DEBUG] Got %d bytes from the network interface \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 FPGASocket : : 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 FPGAServer class handles new connections to the server . For every
client that connects , it creates a new FPGASocket - - that instance is now
responsible for the communication with that client .
*/
FPGAServer : : FPGAServer ( 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 ) ;
}
// If enabled, setup control FPGA
m_config - > setGroup ( " Programming " ) ;
TQString setupControlFPGAScript = m_config - > readEntry ( " controlstartupscript " ) ;
if ( setupControlFPGAScript ! = " " ) {
printf ( " [INFO] Setting up control FPGA \n \r " ) ; fflush ( stdout ) ;
system ( setupControlFPGAScript . ascii ( ) ) ;
}
printf ( " [INFO] Server started on port %d \n \r " , port ) ; fflush ( stdout ) ;
}
FPGAServer : : ~ FPGAServer ( ) {
//
}
void FPGAServer : : newConnection ( int socket ) {
FPGASocket * s = new FPGASocket ( 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 FPGAServer : : remoteConnectionClosed ( ) {
m_numberOfConnections - - ;
}