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"