kandy

modem.cpp

00001 /*
00002     KMLOCfg
00003 
00004     A utility to configure the ELSA MicroLink(tm) Office modem.
00005 
00006     Copyright (C) 2000 Oliver Gantz <Oliver.Gantz@epost.de>
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00021 
00022     ------
00023     ELSA and MicroLink are trademarks of ELSA AG, Aachen.
00024 */
00025 
00026 #ifdef HAVE_CONFIG_H
00027 #include "config.h"
00028 #endif
00029 
00030 #include <sys/types.h>
00031 #include <sys/stat.h>
00032 #include <sys/ioctl.h>
00033 #include <fcntl.h>
00034 #include <termios.h>
00035 #include <unistd.h>
00036 #include <stdlib.h>
00037 #include <stdio.h>
00038 #include <string.h>
00039 #include <signal.h>
00040 #include <pwd.h>
00041 #include <errno.h>
00042 
00043 #include <qglobal.h>
00044 
00045 #include <klocale.h>
00046 #include <kdebug.h>
00047 
00048 #include "modem.h"
00049 
00050 
00051 #ifndef CSOH
00052 #define CSOH  01
00053 #endif
00054 
00055 #ifndef CSTX
00056 #define CSTX  02
00057 #endif
00058 
00059 #ifndef CEOT
00060 #define CEOT  04
00061 #endif
00062 
00063 #ifndef CACK
00064 #define CACK  06
00065 #endif
00066 
00067 #ifndef CNAK
00068 #define CNAK 025
00069 #endif
00070 
00071 #ifndef CCAN
00072 #define CCAN 030
00073 #endif
00074 
00075 
00076 
00077 Modem::Modem( KandyPrefs *kprefs, QObject *parent, const char *name ) :
00078   QObject(parent, name)
00079 {
00080   mOpen = false;
00081   
00082   prefs = kprefs;
00083 
00084   timer = new QTimer( this, "modemtimer" );
00085   Q_CHECK_PTR( timer );
00086   connect( timer, SIGNAL( timeout() ), SLOT( timerDone() ) );
00087 
00088   init();
00089   xreset();
00090 }
00091 
00092 
00093 Modem::~Modem()
00094 {
00095   close();
00096 }
00097 
00098 
00099 void Modem::setSpeed( int speed )
00100 {
00101   switch ( speed ) {
00102     case 300:
00103       cspeed = B300;
00104       break;
00105     case 600:
00106       cspeed = B600;
00107       break;
00108     case 1200:
00109       cspeed = B1200;
00110       break;
00111     case 2400:
00112       cspeed = B2400;
00113       break;
00114     case 4800:
00115       cspeed = B4800;
00116       break;
00117     case 9600:
00118       cspeed = B9600;
00119       break;
00120     case 19200:
00121       cspeed = B19200;
00122       break;
00123     case 38400:
00124       cspeed = B38400;
00125       break;
00126     case 57600:
00127       cspeed = B57600;
00128       break;
00129     case 115200:
00130       cspeed = B115200;
00131       break;
00132     case 230400:
00133       cspeed = B230400;
00134       break;
00135     default:
00136 #ifdef MODEM_DEBUG
00137       fprintf(stderr, "Modem: setSpeed(): fallback to default speed.\n");
00138 #endif
00139       cspeed = B38400;
00140   }
00141 }
00142 
00143 
00144 void Modem::setData( int data )
00145 {
00146   cflag &= ~CSIZE;
00147 
00148   switch ( data ) {
00149     case 5:
00150       cflag |= CS5;
00151       break;
00152     case 6:
00153       cflag |= CS6;
00154       break;
00155     case 7:
00156       cflag |= CS7;
00157       break;
00158     default:
00159       cflag |= CS8;
00160   }
00161 }
00162 
00163 
00164 void Modem::setParity( char parity )
00165 {
00166   cflag &= ~( PARENB | PARODD );
00167 
00168   if ( parity == 'E' )
00169     cflag |= PARENB;
00170   else if ( parity == 'O' )
00171     cflag |= PARENB | PARODD;
00172 }
00173 
00174 
00175 void Modem::setStop( int stop )
00176 {
00177   if (stop == 2)
00178     cflag |= CSTOPB;
00179   else
00180     cflag &= ~CSTOPB;
00181 }
00182 
00183 
00184 bool Modem::open()
00185 {
00186   struct termios tty;
00187 
00188 
00189   close();
00190 
00191   if ( !lockDevice() )
00192     return false;
00193 
00194   QCString dev = QFile::encodeName( (*prefs).serialDevice() );
00195   const char *fdev = dev.data();
00196   if ( ( fd = ::open( fdev, O_RDWR | O_NOCTTY | O_NONBLOCK ) ) == -1 ) {
00197     emit errorMessage( i18n( "Unable to open device '%1'. "
00198                              "Please check that you have sufficient permissions." )
00199                              .arg( fdev ) );
00200     return false;
00201   }
00202 
00203   tcflush( fd, TCIOFLUSH );
00204   if ( tcgetattr( fd, &init_tty ) == -1 ) {
00205     int errnumber = errno;
00206     emit errorMessage( i18n( "Communication setup failed (tcgetattr code: %1)" )
00207         .arg(strerror(errnumber)) );
00208     ::close( fd );
00209     fd = 0;
00210     return false;
00211   }
00212 
00213   memset( &tty, 0, sizeof( tty ) );
00214   tty.c_iflag = IGNBRK | IGNPAR;
00215   tty.c_oflag = 0;
00216   tty.c_cflag = cflag;
00217   tty.c_lflag = 0;
00218   cfsetospeed( &tty, cspeed );
00219   cfsetispeed( &tty, cspeed );
00220   tcdrain( fd );
00221 
00222   if ( tcsetattr( fd, TCSANOW, &tty ) == -1 ) {
00223     emit errorMessage( i18n( "tcsetattr() failed." ) );
00224     ::close( fd );
00225     fd = 0;
00226     return false;
00227   }
00228 
00229   sn = new QSocketNotifier( fd, QSocketNotifier::Read, this,
00230                             "modemsocketnotifier" );
00231   Q_CHECK_PTR( sn );
00232   connect( sn, SIGNAL( activated( int ) ), SLOT( readChar( int ) ) );
00233 
00234   mOpen = true;
00235 
00236   return true;
00237 }
00238 
00239 
00240 void Modem::close()
00241 {
00242   timer->stop();
00243 
00244   delete sn;
00245   sn = 0;
00246 
00247   if ( fd ) {
00248     tcflush( fd, TCIOFLUSH );
00249     tcsetattr( fd, TCSANOW, &init_tty );
00250     ::close( fd );
00251     fd = 0;
00252   }
00253 
00254   xreset();
00255 
00256   unlockDevice();
00257 
00258   mOpen = false;
00259 }
00260 
00261 
00262 void Modem::flush()
00263 {
00264   if ( fd ) {
00265     tcflush( fd, TCIOFLUSH );
00266     bufpos = 0;
00267   }
00268 }
00269 
00270 #ifdef HAVE_LOCKDEV
00271 #include <lockdev.h>
00272 #endif
00273 
00274 bool Modem::lockDevice()
00275 {
00276   if ( is_locked )
00277     return true;
00278 
00279 #ifdef HAVE_LOCKDEV
00280   is_locked = !dev_lock( (*prefs).serialDevice().local8Bit() );
00281   if (!is_locked)
00282       emit errorMessage( i18n( "Unable to lock device '%1'." ).arg(
00283                              (*prefs).serialDevice() ));
00284   return is_locked;
00285 #else
00286   ssize_t count;
00287   pid_t pid;
00288   int lfd;
00289   struct passwd *pw;
00290   QStringList pathList;
00291   QString fileName, content;
00292 
00293   pathList = QStringList::split( "/", (*prefs).serialDevice() );
00294   fileName = (*prefs).lockDirectory() + "/LCK.." + pathList.last();
00295 
00296   if ( !access( QFile::encodeName( fileName ).data(), F_OK ) ) {
00297     char buf[256];
00298 
00299 
00300     if ( ( lfd = ::open( QFile::encodeName( fileName ), O_RDONLY ) ) < 0 ) {
00301       emit errorMessage( i18n( "Unable to open lock file '%1'.")
00302                                .arg( fileName ) );
00303       return false;
00304     }
00305 
00306     count = read( lfd, buf, 79 );
00307 
00308     if ( count < 0 ) {
00309        emit errorMessage( i18n( "Unable to read lock file '%1'.")
00310                                 .arg( fileName ) );
00311        ::close( lfd );
00312        return false;
00313     }
00314     buf[ count ] = 0;
00315     ::close( lfd );
00316 
00317     count = sscanf( buf, "%d", &pid );
00318     if ( ( count != 1 ) || ( pid <= 0 ) ) {
00319        emit errorMessage( i18n( "Unable to get PID from file '%1'.")
00320                                 .arg( fileName ) );
00321        return false;
00322     }
00323 
00324     if ( !kill( (pid_t) pid, 0 ) ) {
00325        emit errorMessage( i18n( "Process with PID %1, which is locking the device, is still running.")
00326                                 .arg( pid ) );
00327        return false;
00328     }
00329 
00330     if ( errno != ESRCH ) {
00331       emit errorMessage( i18n( "Unable to emit signal to PID of existing lock file.") );
00332       return false;
00333     }
00334   }
00335     
00336   if ( ( lfd = creat( QFile::encodeName( fileName ).data(), 0644 ) ) == -1 ) {
00337     emit errorMessage( i18n( "Unable to create lock file '%1'. "
00338                              "Please check that you have sufficient permissions.")
00339                              .arg( fileName ) );
00340     return false;
00341   }
00342 
00343   pid = (int) getpid();
00344   pw = getpwuid( getuid() );
00345   content.sprintf( "%08d %s %s", pid, "kandy", pw->pw_name );
00346   write( lfd, QFile::encodeName( content ).data(), content.length() );
00347   ::close( lfd );
00348 
00349   is_locked = true;
00350 
00351   return true;
00352 #endif
00353 }
00354 
00355 
00356 void Modem::unlockDevice()
00357 {
00358 #ifdef HAVE_LOCKDEV
00359   dev_unlock( (*prefs).serialDevice().local8Bit(), getpid() );
00360 #else
00361   if ( is_locked ) {
00362     QStringList pathList = QStringList::split( "/", (*prefs).serialDevice() );
00363 
00364     QFile::remove( (*prefs).lockDirectory() + "/LCK.." + pathList.last() );
00365     is_locked = false;
00366   }
00367 #endif
00368 }
00369 
00370 
00371 bool Modem::dsrOn()
00372 {
00373   int flags;
00374 
00375 
00376   if ( !fd ) {
00377 #ifdef MODEM_DEBUG
00378     fprintf( stderr, "Modem: dsrOn(): File not open.\n" );
00379 #endif
00380     return false;
00381   }
00382 
00383   if ( ioctl( fd, TIOCMGET, &flags ) == -1 ) {
00384 #ifdef MODEM_DEBUG
00385     fprintf( stderr, "Modem: dsrOn(): ioctl() failed.\n" );
00386 #endif
00387     return false;
00388   }
00389 
00390   return ( flags & TIOCM_DSR ) != 0;
00391 }
00392 
00393 
00394 bool Modem::ctsOn()
00395 {
00396   int flags;
00397 
00398 
00399   if ( !fd ) {
00400 #ifdef MODEM_DEBUG
00401     fprintf( stderr, "Modem: ctsOn(): File not open.\n" );
00402 #endif
00403     return false;
00404   }
00405 
00406   if ( ioctl( fd, TIOCMGET, &flags ) == -1) {
00407 #ifdef MODEM_DEBUG
00408     fprintf( stderr, "Modem: ctsOn(): ioctl() failed.\n" );
00409 #endif
00410     return false;
00411   }
00412 
00413   return ( flags & TIOCM_CTS ) != 0;
00414 }
00415 
00416 
00417 void Modem::writeChar( const char c )
00418 {
00419   write( fd, (const void *) &c, 1 );
00420 }
00421 
00422 
00423 void Modem::writeLine( const char *line )
00424 {
00425   kdDebug() << "Modem::writeLine(): " << line << endl;
00426 
00427   write( fd, (const void *) line, strlen( line ) );
00428   writeChar( '\r' );
00429 }
00430 
00431 
00432 void Modem::timerStart( int msec )
00433 {
00434   timer->start( msec, true );
00435 }
00436 
00437 
00438 void Modem::receiveXModem( bool crc )
00439 {
00440   disconnect( sn, 0, this, 0 );
00441   connect( sn, SIGNAL( activated( int ) ), SLOT( readXChar( int ) ) );
00442 
00443   xcrc = crc;
00444 
00445   if ( xcrc ) {
00446     writeChar( 'C' );
00447     xstate = 1;
00448     timerStart( 3000 );
00449   } else {
00450     writeChar( CNAK );
00451     xstate = 5;
00452     timerStart( 10000 );
00453   }
00454 
00455   xblock = 1;
00456 }
00457 
00458 
00459 void Modem::abortXModem()
00460 {
00461   timer->stop();
00462   writeChar( CCAN );
00463   xreset();
00464   emit xmodemDone( false );
00465 }
00466 
00467 
00468 void Modem::timerDone()
00469 {
00470 #ifdef MODEM_DEBUG
00471   fprintf( stderr, "Modem: timeout, xstate = %d.\n", xstate );
00472 #endif
00473 
00474   switch ( xstate ) {
00475     case  0:            /* non-XModem mode  */
00476       emit timeout();
00477       break;
00478 
00479     case  1:            /* 1st 'C' sent     */
00480     case  2:            /* 2nd 'C' sent     */
00481     case  3:            /* 3rd 'C' sent     */
00482       writeChar( 'C' );
00483       xstate++;
00484       timerStart( 1000 );   /* Should be 3000 in original XModem    */
00485       break;
00486 
00487     case  4:            /* 4th 'C' sent     */
00488       xcrc = false;
00489 
00490     case  5:            /* 1st <NAK> sent   */
00491     case  6:            /* 2nd <NAK> sent   */
00492     case  7:            /* 3rd <NAK> sent   */
00493     case  8:            /* 4th <NAK> sent   */
00494     case  9:            /* 5th <NAK> sent   */
00495       writeChar( CNAK );
00496       xstate++;
00497       timerStart( 1000 );   /* Should be 10000 in original XModem   */
00498       break;
00499 
00500     case 10:            /* 6th <NAK> sent   */
00501       xreset();
00502       emit xmodemDone( false );
00503       break;
00504 
00505     default:            /* pending XModem block */
00506       writeChar( CNAK );
00507       xstate = 5;
00508       timerStart( 1000 );   /* Should be 10000 in original XModem   */
00509     }
00510 }
00511 
00512 
00513 void Modem::readChar( int )
00514 {
00515   uchar c;
00516 
00517 
00518   while ( read( fd, (void *) &c, 1 ) == 1 ) {
00519     if ( c == '\n' ) {
00520       buffer[ bufpos ] = 0;
00521       bufpos = 0;
00522       emit gotLine( (const char *) buffer );
00523       break;
00524     } else
00525     if ( ( bufpos < 1000 ) && ( c != '\r' ) )
00526       buffer[ bufpos++ ] = c;
00527   }
00528 }
00529 
00530 
00531 void Modem::readXChar( int )
00532 {
00533   uchar c;
00534   static uchar crc_hi, block, cblock;
00535 
00536 
00537   while ( read( fd, (void *) &c, 1 ) == 1 ) {
00538     switch ( xstate ) {
00539       case  1:  /* 1st 'C' sent     */
00540       case  2:  /* 2nd 'C' sent     */
00541       case  3:  /* 3rd 'C' sent     */
00542       case  4:  /* 4th 'C' sent     */
00543       case  5:  /* 1st <NAK> sent   */
00544       case  6:  /* 2nd <NAK> sent   */
00545       case  7:  /* 3rd <NAK> sent   */
00546       case  8:  /* 4th <NAK> sent   */
00547       case  9:  /* 5th <NAK> sent   */
00548       case 10:  /* 6th <NAK> sent   */
00549     if ( c == CSOH ) {
00550       timerStart( 1000 );
00551       xsize = 128;
00552       xstate = 11;
00553     } else
00554     if ( c == CSTX ) {
00555       timerStart( 1000 );
00556       xsize = 1024;
00557       xstate = 11;
00558         } else
00559     if ( c == CEOT ) {
00560       timer->stop();
00561       writeChar( CACK );
00562       xreset();
00563       emit xmodemDone( true );
00564     } else
00565       timerStart( 1000 );
00566     break;
00567 
00568       case 11:  /* <SOH> or <STX> received   */
00569     timerStart( 1000 );
00570     block = c;
00571     xstate++;
00572     break;
00573 
00574       case 12:  /* block number received    */
00575     timerStart( 1000 );
00576     cblock = c;
00577     xstate++;
00578     bufpos = 0;
00579     break;
00580 
00581       case 13:  /* complement block number received */
00582     timerStart( 1000 );
00583     buffer[ bufpos++ ] = c;
00584     if ( bufpos == xsize ) {
00585       bufpos = 0;
00586       xstate++;
00587       if ( !xcrc )
00588         xstate++;
00589     }
00590     break;
00591 
00592       case 14:  /* data block received  */
00593     timerStart( 1000 );
00594     crc_hi = c;
00595     xstate++;
00596     break;
00597 
00598       case 15:  /* crc high-byte received   */
00599     timerStart( 10000 );
00600     xstate = 4;
00601     if ( (uchar) ( block ^ cblock ) != 0xff ) {
00602       writeChar( CNAK );
00603       break;
00604     }
00605     if ( block+1 == xblock ) {
00606       writeChar( CACK );
00607       break;
00608     }
00609     if ( block != xblock ) {
00610       timer->stop();
00611       writeChar( CCAN );
00612       xreset();
00613       emit xmodemDone( false );
00614       break;
00615     }
00616     if ( xcrc ) {
00617       if ( ( (ushort) crc_hi << 8 | (ushort) c ) != calcCRC() ) {
00618         writeChar( CNAK );
00619         break;
00620       }
00621     } else {
00622       if ( c != calcChecksum() ) {
00623         writeChar( CNAK );
00624         break;
00625       }
00626     }
00627     writeChar( CACK );
00628     xblock++;
00629     emit gotXBlock( buffer, xsize );
00630     break;
00631 
00632       default:
00633     break;
00634     }
00635   }
00636 }
00637 
00638 
00639 void Modem::init()
00640 {
00641   is_locked = false;
00642 
00643   fd = 0;
00644   sn = 0;
00645 
00646   cspeed = B38400;
00647 
00648   // No flow control
00649   cflag = CS8 | CREAD | CLOCAL;
00650   // cflag = CS8 | CREAD | CLOCAL | CRTSCTS;
00651 
00652   bufpos = 0;
00653 }
00654 
00655 
00656 void Modem::xreset()
00657 {
00658   bufpos = 0;
00659 
00660   xstate = 0;
00661   xcrc = false;
00662   xblock = 0;
00663   xsize = 0;
00664 
00665   if ( sn ) {
00666     disconnect( sn, 0, this, 0 );
00667     connect( sn, SIGNAL( activated( int ) ), SLOT( readChar( int ) ) );
00668   }
00669 }
00670 
00671 
00672 uchar Modem::calcChecksum()
00673 {
00674   int i;
00675   uchar c = 0;
00676 
00677 
00678   for ( i = 0; i < xsize; i++ )
00679     c += buffer[ i ];
00680 
00681   return c;
00682 }
00683 
00684 
00685 ushort Modem::calcCRC()
00686 {
00687   int i, j;
00688   ushort c = 0;
00689 
00690     
00691   for ( i = 0; i < xsize; i++ ) {
00692     c ^= (ushort) buffer[ i ] << 8;
00693     for ( j = 0; j < 8; j++ )
00694       if ( c & 0x8000 )
00695         c = c << 1 ^ 0x1021;
00696       else
00697     c <<= 1;
00698   }
00699 
00700   return c;
00701 }
00702 
00703 #include "modem.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys