/* KMLOCfg A utility to configure the ELSA MicroLink(tm) Office modem. Copyright (C) 2000 Oliver Gantz 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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, TQObject *tqparent, const char *name ) : TQObject(tqparent, name) { mOpen = false; prefs = kprefs; timer = new TQTimer( this, "modemtimer" ); Q_CHECK_PTR( timer ); connect( timer, TQT_SIGNAL( timeout() ), TQT_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; TQCString dev = TQFile::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 tqdevice '%1'. " "Please check that you have sufficient permissions." ) .tqarg( fdev ) ); return false; } tcflush( fd, TCIOFLUSH ); if ( tcgetattr( fd, &init_tty ) == -1 ) { int errnumber = errno; emit errorMessage( i18n( "Communication setup failed (tcgetattr code: %1)" ) .tqarg(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 TQSocketNotifier( fd, TQSocketNotifier::Read, this, "modemsocketnotifier" ); Q_CHECK_PTR( sn ); connect( sn, TQT_SIGNAL( activated( int ) ), TQT_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 #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 tqdevice '%1'." ).tqarg( (*prefs).serialDevice() )); return is_locked; #else ssize_t count; pid_t pid; int lfd; struct passwd *pw; TQStringList pathList; TQString fileName, content; pathList = TQStringList::split( "/", (*prefs).serialDevice() ); fileName = (*prefs).lockDirectory() + "/LCK.." + pathList.last(); if ( !access( TQFile::encodeName( fileName ).data(), F_OK ) ) { char buf[256]; if ( ( lfd = ::open( TQFile::encodeName( fileName ), O_RDONLY ) ) < 0 ) { emit errorMessage( i18n( "Unable to open lock file '%1'.") .tqarg( fileName ) ); return false; } count = read( lfd, buf, 79 ); if ( count < 0 ) { emit errorMessage( i18n( "Unable to read lock file '%1'.") .tqarg( 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'.") .tqarg( fileName ) ); return false; } if ( !kill( (pid_t) pid, 0 ) ) { emit errorMessage( i18n( "Process with PID %1, which is locking the tqdevice, is still running.") .tqarg( pid ) ); return false; } if ( errno != ESRCH ) { emit errorMessage( i18n( "Unable to emit signal to PID of existing lock file.") ); return false; } } if ( ( lfd = creat( TQFile::encodeName( fileName ).data(), 0644 ) ) == -1 ) { emit errorMessage( i18n( "Unable to create lock file '%1'. " "Please check that you have sufficient permissions.") .tqarg( fileName ) ); return false; } pid = (int) getpid(); pw = getpwuid( getuid() ); content.sprintf( "%08d %s %s", pid, "kandy", pw->pw_name ); write( lfd, TQFile::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 ) { TQStringList pathList = TQStringList::split( "/", (*prefs).serialDevice() ); TQFile::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, TQT_SIGNAL( activated( int ) ), TQT_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 sent */ case 6: /* 2nd sent */ case 7: /* 3rd sent */ case 8: /* 4th sent */ case 9: /* 5th sent */ writeChar( CNAK ); xstate++; timerStart( 1000 ); /* Should be 10000 in original XModem */ break; case 10: /* 6th 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 sent */ case 6: /* 2nd sent */ case 7: /* 3rd sent */ case 8: /* 4th sent */ case 9: /* 5th sent */ case 10: /* 6th 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: /* or 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, TQT_SIGNAL( activated( int ) ), TQT_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"