diff options
Diffstat (limited to 'kandy/src/modem.cpp')
-rw-r--r-- | kandy/src/modem.cpp | 703 |
1 files changed, 703 insertions, 0 deletions
diff --git a/kandy/src/modem.cpp b/kandy/src/modem.cpp new file mode 100644 index 000000000..3341ad589 --- /dev/null +++ b/kandy/src/modem.cpp @@ -0,0 +1,703 @@ +/* + KMLOCfg + + A utility to configure the ELSA MicroLink(tm) Office modem. + + Copyright (C) 2000 Oliver Gantz <Oliver.Gantz@epost.de> + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + ------ + ELSA and MicroLink are trademarks of ELSA AG, Aachen. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <termios.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <pwd.h> +#include <errno.h> + +#include <qglobal.h> + +#include <klocale.h> +#include <kdebug.h> + +#include "modem.h" + + +#ifndef CSOH +#define CSOH 01 +#endif + +#ifndef CSTX +#define CSTX 02 +#endif + +#ifndef CEOT +#define CEOT 04 +#endif + +#ifndef CACK +#define CACK 06 +#endif + +#ifndef CNAK +#define CNAK 025 +#endif + +#ifndef CCAN +#define CCAN 030 +#endif + + + +Modem::Modem( KandyPrefs *kprefs, QObject *parent, const char *name ) : + QObject(parent, name) +{ + mOpen = false; + + prefs = kprefs; + + timer = new QTimer( this, "modemtimer" ); + Q_CHECK_PTR( timer ); + connect( timer, SIGNAL( timeout() ), SLOT( timerDone() ) ); + + init(); + xreset(); +} + + +Modem::~Modem() +{ + close(); +} + + +void Modem::setSpeed( int speed ) +{ + switch ( speed ) { + case 300: + cspeed = B300; + break; + case 600: + cspeed = B600; + break; + case 1200: + cspeed = B1200; + break; + case 2400: + cspeed = B2400; + break; + case 4800: + cspeed = B4800; + break; + case 9600: + cspeed = B9600; + break; + case 19200: + cspeed = B19200; + break; + case 38400: + cspeed = B38400; + break; + case 57600: + cspeed = B57600; + break; + case 115200: + cspeed = B115200; + break; + case 230400: + cspeed = B230400; + break; + default: +#ifdef MODEM_DEBUG + fprintf(stderr, "Modem: setSpeed(): fallback to default speed.\n"); +#endif + cspeed = B38400; + } +} + + +void Modem::setData( int data ) +{ + cflag &= ~CSIZE; + + switch ( data ) { + case 5: + cflag |= CS5; + break; + case 6: + cflag |= CS6; + break; + case 7: + cflag |= CS7; + break; + default: + cflag |= CS8; + } +} + + +void Modem::setParity( char parity ) +{ + cflag &= ~( PARENB | PARODD ); + + if ( parity == 'E' ) + cflag |= PARENB; + else if ( parity == 'O' ) + cflag |= PARENB | PARODD; +} + + +void Modem::setStop( int stop ) +{ + if (stop == 2) + cflag |= CSTOPB; + else + cflag &= ~CSTOPB; +} + + +bool Modem::open() +{ + struct termios tty; + + + close(); + + if ( !lockDevice() ) + return false; + + QCString dev = QFile::encodeName( (*prefs).serialDevice() ); + const char *fdev = dev.data(); + if ( ( fd = ::open( fdev, O_RDWR | O_NOCTTY | O_NONBLOCK ) ) == -1 ) { + emit errorMessage( i18n( "Unable to open device '%1'. " + "Please check that you have sufficient permissions." ) + .arg( fdev ) ); + return false; + } + + tcflush( fd, TCIOFLUSH ); + if ( tcgetattr( fd, &init_tty ) == -1 ) { + int errnumber = errno; + emit errorMessage( i18n( "Communication setup failed (tcgetattr code: %1)" ) + .arg(strerror(errnumber)) ); + ::close( fd ); + fd = 0; + return false; + } + + memset( &tty, 0, sizeof( tty ) ); + tty.c_iflag = IGNBRK | IGNPAR; + tty.c_oflag = 0; + tty.c_cflag = cflag; + tty.c_lflag = 0; + cfsetospeed( &tty, cspeed ); + cfsetispeed( &tty, cspeed ); + tcdrain( fd ); + + if ( tcsetattr( fd, TCSANOW, &tty ) == -1 ) { + emit errorMessage( i18n( "tcsetattr() failed." ) ); + ::close( fd ); + fd = 0; + return false; + } + + sn = new QSocketNotifier( fd, QSocketNotifier::Read, this, + "modemsocketnotifier" ); + Q_CHECK_PTR( sn ); + connect( sn, SIGNAL( activated( int ) ), SLOT( readChar( int ) ) ); + + mOpen = true; + + return true; +} + + +void Modem::close() +{ + timer->stop(); + + delete sn; + sn = 0; + + if ( fd ) { + tcflush( fd, TCIOFLUSH ); + tcsetattr( fd, TCSANOW, &init_tty ); + ::close( fd ); + fd = 0; + } + + xreset(); + + unlockDevice(); + + mOpen = false; +} + + +void Modem::flush() +{ + if ( fd ) { + tcflush( fd, TCIOFLUSH ); + bufpos = 0; + } +} + +#ifdef HAVE_LOCKDEV +#include <lockdev.h> +#endif + +bool Modem::lockDevice() +{ + if ( is_locked ) + return true; + +#ifdef HAVE_LOCKDEV + is_locked = !dev_lock( (*prefs).serialDevice().local8Bit() ); + if (!is_locked) + emit errorMessage( i18n( "Unable to lock device '%1'." ).arg( + (*prefs).serialDevice() )); + return is_locked; +#else + ssize_t count; + pid_t pid; + int lfd; + struct passwd *pw; + QStringList pathList; + QString fileName, content; + + pathList = QStringList::split( "/", (*prefs).serialDevice() ); + fileName = (*prefs).lockDirectory() + "/LCK.." + pathList.last(); + + if ( !access( QFile::encodeName( fileName ).data(), F_OK ) ) { + char buf[256]; + + + if ( ( lfd = ::open( QFile::encodeName( fileName ), O_RDONLY ) ) < 0 ) { + emit errorMessage( i18n( "Unable to open lock file '%1'.") + .arg( fileName ) ); + return false; + } + + count = read( lfd, buf, 79 ); + + if ( count < 0 ) { + emit errorMessage( i18n( "Unable to read lock file '%1'.") + .arg( fileName ) ); + ::close( lfd ); + return false; + } + buf[ count ] = 0; + ::close( lfd ); + + count = sscanf( buf, "%d", &pid ); + if ( ( count != 1 ) || ( pid <= 0 ) ) { + emit errorMessage( i18n( "Unable to get PID from file '%1'.") + .arg( fileName ) ); + return false; + } + + if ( !kill( (pid_t) pid, 0 ) ) { + emit errorMessage( i18n( "Process with PID %1, which is locking the device, is still running.") + .arg( pid ) ); + return false; + } + + if ( errno != ESRCH ) { + emit errorMessage( i18n( "Unable to emit signal to PID of existing lock file.") ); + return false; + } + } + + if ( ( lfd = creat( QFile::encodeName( fileName ).data(), 0644 ) ) == -1 ) { + emit errorMessage( i18n( "Unable to create lock file '%1'. " + "Please check that you have sufficient permissions.") + .arg( fileName ) ); + return false; + } + + pid = (int) getpid(); + pw = getpwuid( getuid() ); + content.sprintf( "%08d %s %s", pid, "kandy", pw->pw_name ); + write( lfd, QFile::encodeName( content ).data(), content.length() ); + ::close( lfd ); + + is_locked = true; + + return true; +#endif +} + + +void Modem::unlockDevice() +{ +#ifdef HAVE_LOCKDEV + dev_unlock( (*prefs).serialDevice().local8Bit(), getpid() ); +#else + if ( is_locked ) { + QStringList pathList = QStringList::split( "/", (*prefs).serialDevice() ); + + QFile::remove( (*prefs).lockDirectory() + "/LCK.." + pathList.last() ); + is_locked = false; + } +#endif +} + + +bool Modem::dsrOn() +{ + int flags; + + + if ( !fd ) { +#ifdef MODEM_DEBUG + fprintf( stderr, "Modem: dsrOn(): File not open.\n" ); +#endif + return false; + } + + if ( ioctl( fd, TIOCMGET, &flags ) == -1 ) { +#ifdef MODEM_DEBUG + fprintf( stderr, "Modem: dsrOn(): ioctl() failed.\n" ); +#endif + return false; + } + + return ( flags & TIOCM_DSR ) != 0; +} + + +bool Modem::ctsOn() +{ + int flags; + + + if ( !fd ) { +#ifdef MODEM_DEBUG + fprintf( stderr, "Modem: ctsOn(): File not open.\n" ); +#endif + return false; + } + + if ( ioctl( fd, TIOCMGET, &flags ) == -1) { +#ifdef MODEM_DEBUG + fprintf( stderr, "Modem: ctsOn(): ioctl() failed.\n" ); +#endif + return false; + } + + return ( flags & TIOCM_CTS ) != 0; +} + + +void Modem::writeChar( const char c ) +{ + write( fd, (const void *) &c, 1 ); +} + + +void Modem::writeLine( const char *line ) +{ + kdDebug() << "Modem::writeLine(): " << line << endl; + + write( fd, (const void *) line, strlen( line ) ); + writeChar( '\r' ); +} + + +void Modem::timerStart( int msec ) +{ + timer->start( msec, true ); +} + + +void Modem::receiveXModem( bool crc ) +{ + disconnect( sn, 0, this, 0 ); + connect( sn, SIGNAL( activated( int ) ), SLOT( readXChar( int ) ) ); + + xcrc = crc; + + if ( xcrc ) { + writeChar( 'C' ); + xstate = 1; + timerStart( 3000 ); + } else { + writeChar( CNAK ); + xstate = 5; + timerStart( 10000 ); + } + + xblock = 1; +} + + +void Modem::abortXModem() +{ + timer->stop(); + writeChar( CCAN ); + xreset(); + emit xmodemDone( false ); +} + + +void Modem::timerDone() +{ +#ifdef MODEM_DEBUG + fprintf( stderr, "Modem: timeout, xstate = %d.\n", xstate ); +#endif + + switch ( xstate ) { + case 0: /* non-XModem mode */ + emit timeout(); + break; + + case 1: /* 1st 'C' sent */ + case 2: /* 2nd 'C' sent */ + case 3: /* 3rd 'C' sent */ + writeChar( 'C' ); + xstate++; + timerStart( 1000 ); /* Should be 3000 in original XModem */ + break; + + case 4: /* 4th 'C' sent */ + xcrc = false; + + case 5: /* 1st <NAK> sent */ + case 6: /* 2nd <NAK> sent */ + case 7: /* 3rd <NAK> sent */ + case 8: /* 4th <NAK> sent */ + case 9: /* 5th <NAK> sent */ + writeChar( CNAK ); + xstate++; + timerStart( 1000 ); /* Should be 10000 in original XModem */ + break; + + case 10: /* 6th <NAK> sent */ + xreset(); + emit xmodemDone( false ); + break; + + default: /* pending XModem block */ + writeChar( CNAK ); + xstate = 5; + timerStart( 1000 ); /* Should be 10000 in original XModem */ + } +} + + +void Modem::readChar( int ) +{ + uchar c; + + + while ( read( fd, (void *) &c, 1 ) == 1 ) { + if ( c == '\n' ) { + buffer[ bufpos ] = 0; + bufpos = 0; + emit gotLine( (const char *) buffer ); + break; + } else + if ( ( bufpos < 1000 ) && ( c != '\r' ) ) + buffer[ bufpos++ ] = c; + } +} + + +void Modem::readXChar( int ) +{ + uchar c; + static uchar crc_hi, block, cblock; + + + while ( read( fd, (void *) &c, 1 ) == 1 ) { + switch ( xstate ) { + case 1: /* 1st 'C' sent */ + case 2: /* 2nd 'C' sent */ + case 3: /* 3rd 'C' sent */ + case 4: /* 4th 'C' sent */ + case 5: /* 1st <NAK> sent */ + case 6: /* 2nd <NAK> sent */ + case 7: /* 3rd <NAK> sent */ + case 8: /* 4th <NAK> sent */ + case 9: /* 5th <NAK> sent */ + case 10: /* 6th <NAK> sent */ + if ( c == CSOH ) { + timerStart( 1000 ); + xsize = 128; + xstate = 11; + } else + if ( c == CSTX ) { + timerStart( 1000 ); + xsize = 1024; + xstate = 11; + } else + if ( c == CEOT ) { + timer->stop(); + writeChar( CACK ); + xreset(); + emit xmodemDone( true ); + } else + timerStart( 1000 ); + break; + + case 11: /* <SOH> or <STX> received */ + timerStart( 1000 ); + block = c; + xstate++; + break; + + case 12: /* block number received */ + timerStart( 1000 ); + cblock = c; + xstate++; + bufpos = 0; + break; + + case 13: /* complement block number received */ + timerStart( 1000 ); + buffer[ bufpos++ ] = c; + if ( bufpos == xsize ) { + bufpos = 0; + xstate++; + if ( !xcrc ) + xstate++; + } + break; + + case 14: /* data block received */ + timerStart( 1000 ); + crc_hi = c; + xstate++; + break; + + case 15: /* crc high-byte received */ + timerStart( 10000 ); + xstate = 4; + if ( (uchar) ( block ^ cblock ) != 0xff ) { + writeChar( CNAK ); + break; + } + if ( block+1 == xblock ) { + writeChar( CACK ); + break; + } + if ( block != xblock ) { + timer->stop(); + writeChar( CCAN ); + xreset(); + emit xmodemDone( false ); + break; + } + if ( xcrc ) { + if ( ( (ushort) crc_hi << 8 | (ushort) c ) != calcCRC() ) { + writeChar( CNAK ); + break; + } + } else { + if ( c != calcChecksum() ) { + writeChar( CNAK ); + break; + } + } + writeChar( CACK ); + xblock++; + emit gotXBlock( buffer, xsize ); + break; + + default: + break; + } + } +} + + +void Modem::init() +{ + is_locked = false; + + fd = 0; + sn = 0; + + cspeed = B38400; + + // No flow control + cflag = CS8 | CREAD | CLOCAL; + // cflag = CS8 | CREAD | CLOCAL | CRTSCTS; + + bufpos = 0; +} + + +void Modem::xreset() +{ + bufpos = 0; + + xstate = 0; + xcrc = false; + xblock = 0; + xsize = 0; + + if ( sn ) { + disconnect( sn, 0, this, 0 ); + connect( sn, SIGNAL( activated( int ) ), SLOT( readChar( int ) ) ); + } +} + + +uchar Modem::calcChecksum() +{ + int i; + uchar c = 0; + + + for ( i = 0; i < xsize; i++ ) + c += buffer[ i ]; + + return c; +} + + +ushort Modem::calcCRC() +{ + int i, j; + ushort c = 0; + + + for ( i = 0; i < xsize; i++ ) { + c ^= (ushort) buffer[ i ] << 8; + for ( j = 0; j < 8; j++ ) + if ( c & 0x8000 ) + c = c << 1 ^ 0x1021; + else + c <<= 1; + } + + return c; +} + +#include "modem.moc" |