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 <sys/file.h>
00034 #include <fcntl.h>
00035 #include <termios.h>
00036 #include <unistd.h>
00037 #include <stdlib.h>
00038 #include <stdio.h>
00039 #include <string.h>
00040 #include <signal.h>
00041 #include <pwd.h>
00042 #include <errno.h>
00043 
00044 #include <tqglobal.h>
00045 
00046 #include <tdelocale.h>
00047 #include <kdebug.h>
00048 
00049 #include "modem.h"
00050 
00051 
00052 #ifndef CSOH
00053 #define CSOH  01
00054 #endif
00055 
00056 #ifndef CSTX
00057 #define CSTX  02
00058 #endif
00059 
00060 #ifndef CEOT
00061 #define CEOT  04
00062 #endif
00063 
00064 #ifndef CACK
00065 #define CACK  06
00066 #endif
00067 
00068 #ifndef CNAK
00069 #define CNAK 025
00070 #endif
00071 
00072 #ifndef CCAN
00073 #define CCAN 030
00074 #endif
00075 
00076 
00077 
00078 Modem::Modem( KandyPrefs *kprefs, TQObject *parent, const char *name ) :
00079   TQObject(parent, name), fd(-1)
00080 {
00081   mOpen = false;
00082 
00083   prefs = kprefs;
00084 
00085   timer = new TQTimer( this, "modemtimer" );
00086   TQ_CHECK_PTR( timer );
00087   connect( timer, TQT_SIGNAL( timeout() ), TQT_SLOT( timerDone() ) );
00088 
00089   init();
00090   xreset();
00091 }
00092 
00093 
00094 Modem::~Modem()
00095 {
00096   close();
00097 }
00098 
00099 
00100 void Modem::setSpeed( int speed )
00101 {
00102   switch ( speed ) {
00103     case 300:
00104       cspeed = B300;
00105       break;
00106     case 600:
00107       cspeed = B600;
00108       break;
00109     case 1200:
00110       cspeed = B1200;
00111       break;
00112     case 2400:
00113       cspeed = B2400;
00114       break;
00115     case 4800:
00116       cspeed = B4800;
00117       break;
00118     case 9600:
00119       cspeed = B9600;
00120       break;
00121     case 19200:
00122       cspeed = B19200;
00123       break;
00124     case 38400:
00125       cspeed = B38400;
00126       break;
00127     case 57600:
00128       cspeed = B57600;
00129       break;
00130     case 115200:
00131       cspeed = B115200;
00132       break;
00133     case 230400:
00134       cspeed = B230400;
00135       break;
00136     default:
00137 #ifdef MODEM_DEBUG
00138       fprintf(stderr, "Modem: setSpeed(): fallback to default speed.\n");
00139 #endif
00140       cspeed = B38400;
00141   }
00142 }
00143 
00144 
00145 void Modem::setData( int data )
00146 {
00147   cflag &= ~CSIZE;
00148 
00149   switch ( data ) {
00150     case 5:
00151       cflag |= CS5;
00152       break;
00153     case 6:
00154       cflag |= CS6;
00155       break;
00156     case 7:
00157       cflag |= CS7;
00158       break;
00159     default:
00160       cflag |= CS8;
00161   }
00162 }
00163 
00164 
00165 void Modem::setParity( char parity )
00166 {
00167   cflag &= ~( PARENB | PARODD );
00168 
00169   if ( parity == 'E' )
00170     cflag |= PARENB;
00171   else if ( parity == 'O' )
00172     cflag |= PARENB | PARODD;
00173 }
00174 
00175 
00176 void Modem::setStop( int stop )
00177 {
00178   if (stop == 2)
00179     cflag |= CSTOPB;
00180   else
00181     cflag &= ~CSTOPB;
00182 }
00183 
00184 
00185 bool Modem::open()
00186 {
00187   struct termios tty;
00188 
00189   close();
00190 
00191     if (fd == -1)
00192     {
00193         TQCString dev = TQFile::encodeName( (*prefs).serialDevice() );
00194         const char *fdev = dev.data();
00195         if ( ( fd = ::open( fdev, O_RDWR | O_NOCTTY | O_NONBLOCK ) ) == -1 ) {
00196             emit errorMessage( i18n( "Unable to open device '%1'. "
00197                                                             "Please check that you have sufficient permissions." )
00198                                                             .arg( fdev ) );
00199             return false;
00200         }
00201     }
00202 
00203   if ( !lockDevice() )
00204     return false;
00205 
00206   tcflush( fd, TCIOFLUSH );
00207   if ( tcgetattr( fd, &init_tty ) == -1 ) {
00208     int errnumber = errno;
00209     emit errorMessage( i18n( "Communication setup failed (tcgetattr code: %1)" )
00210         .arg(strerror(errnumber)) );
00211         unlockDevice();
00212     ::close( fd );
00213     fd = -1;
00214     return false;
00215   }
00216 
00217   memset( &tty, 0, sizeof( tty ) );
00218   tty.c_iflag = IGNBRK | IGNPAR;
00219   tty.c_oflag = 0;
00220   tty.c_cflag = cflag;
00221   tty.c_lflag = 0;
00222   cfsetospeed( &tty, cspeed );
00223   cfsetispeed( &tty, cspeed );
00224   tcdrain( fd );
00225 
00226   if ( tcsetattr( fd, TCSANOW, &tty ) == -1 ) {
00227     emit errorMessage( i18n( "tcsetattr() failed." ) );
00228         unlockDevice();
00229     ::close( fd );
00230     fd = -1;
00231     return false;
00232   }
00233 
00234   sn = new TQSocketNotifier( fd, TQSocketNotifier::Read, this,
00235                             "modemsocketnotifier" );
00236   TQ_CHECK_PTR( sn );
00237   connect( sn, TQT_SIGNAL( activated( int ) ), TQT_SLOT( readChar( int ) ) );
00238 
00239   mOpen = true;
00240 
00241   return true;
00242 }
00243 
00244 
00245 void Modem::close()
00246 {
00247   timer->stop();
00248 
00249   delete sn;
00250   sn = 0;
00251 
00252   unlockDevice();
00253 
00254   if ( fd ) {
00255     tcflush( fd, TCIOFLUSH );
00256     tcsetattr( fd, TCSANOW, &init_tty );
00257     ::close( fd );
00258     fd = -1;
00259   }
00260 
00261   xreset();
00262 
00263   mOpen = false;
00264 }
00265 
00266 
00267 void Modem::flush()
00268 {
00269   if ( fd != -1) {
00270     tcflush( fd, TCIOFLUSH );
00271     bufpos = 0;
00272   }
00273 }
00274 
00275 bool Modem::lockDevice()
00276 {
00277   if (is_locked)
00278     return true;
00279 
00280     if (flock(fd, LOCK_EX))
00281     {
00282       // Locking failed
00283     is_locked = false;
00284     emit errorMessage(i18n("Unable to lock device '%1'.").arg((*prefs).serialDevice()));
00285     }
00286     else
00287     {
00288     is_locked = true;
00289   }
00290 
00291     return is_locked;
00292 }
00293 
00294 
00295 void Modem::unlockDevice()
00296 {
00297   if (fd != -1 && is_locked)
00298   {
00299         flock(fd, LOCK_UN);
00300         is_locked = false;
00301     }
00302 }
00303 
00304 
00305 bool Modem::dsrOn()
00306 {
00307   int flags;
00308 
00309 
00310   if ( fd == -1 ) {
00311 #ifdef MODEM_DEBUG
00312     fprintf( stderr, "Modem: dsrOn(): File not open.\n" );
00313 #endif
00314     return false;
00315   }
00316 
00317   if ( ioctl( fd, TIOCMGET, &flags ) == -1 ) {
00318 #ifdef MODEM_DEBUG
00319     fprintf( stderr, "Modem: dsrOn(): ioctl() failed.\n" );
00320 #endif
00321     return false;
00322   }
00323 
00324   return ( flags & TIOCM_DSR ) != 0;
00325 }
00326 
00327 
00328 bool Modem::ctsOn()
00329 {
00330   int flags;
00331 
00332 
00333   if ( fd == -1 ) {
00334 #ifdef MODEM_DEBUG
00335     fprintf( stderr, "Modem: ctsOn(): File not open.\n" );
00336 #endif
00337     return false;
00338   }
00339 
00340   if ( ioctl( fd, TIOCMGET, &flags ) == -1) {
00341 #ifdef MODEM_DEBUG
00342     fprintf( stderr, "Modem: ctsOn(): ioctl() failed.\n" );
00343 #endif
00344     return false;
00345   }
00346 
00347   return ( flags & TIOCM_CTS ) != 0;
00348 }
00349 
00350 
00351 void Modem::writeChar( const char c )
00352 {
00353   write( fd, (const void *) &c, 1 );
00354 }
00355 
00356 
00357 void Modem::writeLine( const char *line )
00358 {
00359   kdDebug() << "Modem::writeLine(): " << line << endl;
00360 
00361   write( fd, (const void *) line, strlen( line ) );
00362   writeChar( '\r' );
00363 }
00364 
00365 
00366 void Modem::timerStart( int msec )
00367 {
00368   timer->start( msec, true );
00369 }
00370 
00371 
00372 void Modem::receiveXModem( bool crc )
00373 {
00374   disconnect( sn, 0, this, 0 );
00375   connect( sn, TQT_SIGNAL( activated( int ) ), TQT_SLOT( readXChar( int ) ) );
00376 
00377   xcrc = crc;
00378 
00379   if ( xcrc ) {
00380     writeChar( 'C' );
00381     xstate = 1;
00382     timerStart( 3000 );
00383   } else {
00384     writeChar( CNAK );
00385     xstate = 5;
00386     timerStart( 10000 );
00387   }
00388 
00389   xblock = 1;
00390 }
00391 
00392 
00393 void Modem::abortXModem()
00394 {
00395   timer->stop();
00396   writeChar( CCAN );
00397   xreset();
00398   emit xmodemDone( false );
00399 }
00400 
00401 
00402 void Modem::timerDone()
00403 {
00404 #ifdef MODEM_DEBUG
00405   fprintf( stderr, "Modem: timeout, xstate = %d.\n", xstate );
00406 #endif
00407 
00408   switch ( xstate ) {
00409     case  0:            /* non-XModem mode  */
00410       emit timeout();
00411       break;
00412 
00413     case  1:            /* 1st 'C' sent     */
00414     case  2:            /* 2nd 'C' sent     */
00415     case  3:            /* 3rd 'C' sent     */
00416       writeChar( 'C' );
00417       xstate++;
00418       timerStart( 1000 );   /* Should be 3000 in original XModem    */
00419       break;
00420 
00421     case  4:            /* 4th 'C' sent     */
00422       xcrc = false;
00423 
00424     case  5:            /* 1st <NAK> sent   */
00425     case  6:            /* 2nd <NAK> sent   */
00426     case  7:            /* 3rd <NAK> sent   */
00427     case  8:            /* 4th <NAK> sent   */
00428     case  9:            /* 5th <NAK> sent   */
00429       writeChar( CNAK );
00430       xstate++;
00431       timerStart( 1000 );   /* Should be 10000 in original XModem   */
00432       break;
00433 
00434     case 10:            /* 6th <NAK> sent   */
00435       xreset();
00436       emit xmodemDone( false );
00437       break;
00438 
00439     default:            /* pending XModem block */
00440       writeChar( CNAK );
00441       xstate = 5;
00442       timerStart( 1000 );   /* Should be 10000 in original XModem   */
00443     }
00444 }
00445 
00446 
00447 void Modem::readChar( int )
00448 {
00449   uchar c;
00450 
00451 
00452   while ( read( fd, (void *) &c, 1 ) == 1 ) {
00453     if ( c == '\n' ) {
00454       buffer[ bufpos ] = 0;
00455       bufpos = 0;
00456       emit gotLine( (const char *) buffer );
00457       break;
00458     } else
00459     if ( ( bufpos < 1000 ) && ( c != '\r' ) )
00460       buffer[ bufpos++ ] = c;
00461   }
00462 }
00463 
00464 
00465 void Modem::readXChar( int )
00466 {
00467   uchar c;
00468   static uchar crc_hi, block, cblock;
00469 
00470 
00471   while ( read( fd, (void *) &c, 1 ) == 1 ) {
00472     switch ( xstate ) {
00473       case  1:  /* 1st 'C' sent     */
00474       case  2:  /* 2nd 'C' sent     */
00475       case  3:  /* 3rd 'C' sent     */
00476       case  4:  /* 4th 'C' sent     */
00477       case  5:  /* 1st <NAK> sent   */
00478       case  6:  /* 2nd <NAK> sent   */
00479       case  7:  /* 3rd <NAK> sent   */
00480       case  8:  /* 4th <NAK> sent   */
00481       case  9:  /* 5th <NAK> sent   */
00482       case 10:  /* 6th <NAK> sent   */
00483     if ( c == CSOH ) {
00484       timerStart( 1000 );
00485       xsize = 128;
00486       xstate = 11;
00487     } else
00488     if ( c == CSTX ) {
00489       timerStart( 1000 );
00490       xsize = 1024;
00491       xstate = 11;
00492         } else
00493     if ( c == CEOT ) {
00494       timer->stop();
00495       writeChar( CACK );
00496       xreset();
00497       emit xmodemDone( true );
00498     } else
00499       timerStart( 1000 );
00500     break;
00501 
00502       case 11:  /* <SOH> or <STX> received   */
00503     timerStart( 1000 );
00504     block = c;
00505     xstate++;
00506     break;
00507 
00508       case 12:  /* block number received    */
00509     timerStart( 1000 );
00510     cblock = c;
00511     xstate++;
00512     bufpos = 0;
00513     break;
00514 
00515       case 13:  /* complement block number received */
00516     timerStart( 1000 );
00517     buffer[ bufpos++ ] = c;
00518     if ( bufpos == xsize ) {
00519       bufpos = 0;
00520       xstate++;
00521       if ( !xcrc )
00522         xstate++;
00523     }
00524     break;
00525 
00526       case 14:  /* data block received  */
00527     timerStart( 1000 );
00528     crc_hi = c;
00529     xstate++;
00530     break;
00531 
00532       case 15:  /* crc high-byte received   */
00533     timerStart( 10000 );
00534     xstate = 4;
00535     if ( (uchar) ( block ^ cblock ) != 0xff ) {
00536       writeChar( CNAK );
00537       break;
00538     }
00539     if ( block+1 == xblock ) {
00540       writeChar( CACK );
00541       break;
00542     }
00543     if ( block != xblock ) {
00544       timer->stop();
00545       writeChar( CCAN );
00546       xreset();
00547       emit xmodemDone( false );
00548       break;
00549     }
00550     if ( xcrc ) {
00551       if ( ( (ushort) crc_hi << 8 | (ushort) c ) != calcCRC() ) {
00552         writeChar( CNAK );
00553         break;
00554       }
00555     } else {
00556       if ( c != calcChecksum() ) {
00557         writeChar( CNAK );
00558         break;
00559       }
00560     }
00561     writeChar( CACK );
00562     xblock++;
00563     emit gotXBlock( buffer, xsize );
00564     break;
00565 
00566       default:
00567     break;
00568     }
00569   }
00570 }
00571 
00572 
00573 void Modem::init()
00574 {
00575   is_locked = false;
00576 
00577   fd = -1;
00578   sn = 0;
00579 
00580   cspeed = B38400;
00581 
00582   // No flow control
00583   cflag = CS8 | CREAD | CLOCAL;
00584   // cflag = CS8 | CREAD | CLOCAL | CRTSCTS;
00585 
00586   bufpos = 0;
00587 }
00588 
00589 
00590 void Modem::xreset()
00591 {
00592   bufpos = 0;
00593 
00594   xstate = 0;
00595   xcrc = false;
00596   xblock = 0;
00597   xsize = 0;
00598 
00599   if ( sn ) {
00600     disconnect( sn, 0, this, 0 );
00601     connect( sn, TQT_SIGNAL( activated( int ) ), TQT_SLOT( readChar( int ) ) );
00602   }
00603 }
00604 
00605 
00606 uchar Modem::calcChecksum()
00607 {
00608   int i;
00609   uchar c = 0;
00610 
00611 
00612   for ( i = 0; i < xsize; i++ )
00613     c += buffer[ i ];
00614 
00615   return c;
00616 }
00617 
00618 
00619 ushort Modem::calcCRC()
00620 {
00621   int i, j;
00622   ushort c = 0;
00623 
00624 
00625   for ( i = 0; i < xsize; i++ ) {
00626     c ^= (ushort) buffer[ i ] << 8;
00627     for ( j = 0; j < 8; j++ )
00628       if ( c & 0x8000 )
00629         c = c << 1 ^ 0x1021;
00630       else
00631     c <<= 1;
00632   }
00633 
00634   return c;
00635 }
00636 
00637 #include "modem.moc"