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 <tqglobal.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, TQObject *parent, const char *name ) : 00078 TQObject(parent, name) 00079 { 00080 mOpen = false; 00081 00082 prefs = kprefs; 00083 00084 timer = new TQTimer( this, "modemtimer" ); 00085 Q_CHECK_PTR( timer ); 00086 connect( timer, TQT_SIGNAL( timeout() ), TQT_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 TQCString dev = TQFile::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 TQSocketNotifier( fd, TQSocketNotifier::Read, this, 00230 "modemsocketnotifier" ); 00231 Q_CHECK_PTR( sn ); 00232 connect( sn, TQT_SIGNAL( activated( int ) ), TQT_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 TQStringList pathList; 00291 TQString fileName, content; 00292 00293 pathList = TQStringList::split( "/", (*prefs).serialDevice() ); 00294 fileName = (*prefs).lockDirectory() + "/LCK.." + pathList.last(); 00295 00296 if ( !access( TQFile::encodeName( fileName ).data(), F_OK ) ) { 00297 char buf[256]; 00298 00299 00300 if ( ( lfd = ::open( TQFile::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( TQFile::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, TQFile::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 TQStringList pathList = TQStringList::split( "/", (*prefs).serialDevice() ); 00363 00364 TQFile::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, TQT_SIGNAL( activated( int ) ), TQT_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, TQT_SIGNAL( activated( int ) ), TQT_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"