00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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 const char *fdev = QFile::encodeName( (*prefs).serialDevice() ).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 tcflush( fd, TCIOFLUSH );
00203 if ( tcgetattr( fd, &init_tty ) == -1 ) {
00204 int errnumber = errno;
00205 emit errorMessage( i18n( "Communication setup failed (tcgetattr code: %1)" )
00206 .arg(strerror(errnumber)) );
00207 ::close( fd );
00208 fd = 0;
00209 return false;
00210 }
00211
00212 memset( &tty, 0, sizeof( tty ) );
00213 tty.c_iflag = IGNBRK | IGNPAR;
00214 tty.c_oflag = 0;
00215 tty.c_cflag = cflag;
00216 tty.c_lflag = 0;
00217 cfsetospeed( &tty, cspeed );
00218 cfsetispeed( &tty, cspeed );
00219 tcdrain( fd );
00220
00221 if ( tcsetattr( fd, TCSANOW, &tty ) == -1 ) {
00222 emit errorMessage( i18n( "tcsetattr() failed." ) );
00223 ::close( fd );
00224 fd = 0;
00225 return false;
00226 }
00227
00228 sn = new QSocketNotifier( fd, QSocketNotifier::Read, this,
00229 "modemsocketnotifier" );
00230 Q_CHECK_PTR( sn );
00231 connect( sn, SIGNAL( activated( int ) ), SLOT( readChar( int ) ) );
00232
00233 mOpen = true;
00234
00235 return true;
00236 }
00237
00238
00239 void Modem::close()
00240 {
00241 timer->stop();
00242
00243 delete sn;
00244 sn = 0;
00245
00246 if ( fd ) {
00247 tcflush( fd, TCIOFLUSH );
00248 tcsetattr( fd, TCSANOW, &init_tty );
00249 ::close( fd );
00250 fd = 0;
00251 }
00252
00253 xreset();
00254
00255 unlockDevice();
00256
00257 mOpen = false;
00258 }
00259
00260
00261 void Modem::flush()
00262 {
00263 if ( fd ) {
00264 tcflush( fd, TCIOFLUSH );
00265 bufpos = 0;
00266 }
00267 }
00268
00269 #ifdef HAVE_LOCKDEV
00270 #include <lockdev.h>
00271 #endif
00272
00273 bool Modem::lockDevice()
00274 {
00275 if ( is_locked )
00276 return true;
00277
00278 #ifdef HAVE_LOCKDEV
00279 is_locked = !dev_lock( (*prefs).serialDevice().local8Bit() );
00280 if (!is_locked)
00281 emit errorMessage( i18n( "Unable to lock device '%1'." ).arg(
00282 (*prefs).serialDevice() ));
00283 return is_locked;
00284 #else
00285 ssize_t count;
00286 pid_t pid;
00287 int lfd;
00288 struct passwd *pw;
00289 QStringList pathList;
00290 QString fileName, content;
00291
00292 pathList = QStringList::split( "/", (*prefs).serialDevice() );
00293 fileName = (*prefs).lockDirectory() + "/LCK.." + pathList.last();
00294
00295 if ( !access( QFile::encodeName( fileName ).data(), F_OK ) ) {
00296 char buf[256];
00297
00298
00299 if ( ( lfd = ::open( QFile::encodeName( fileName ), O_RDONLY ) ) < 0 ) {
00300 emit errorMessage( i18n( "Unable to open lock file '%1'.")
00301 .arg( fileName ) );
00302 return false;
00303 }
00304
00305 count = read( lfd, buf, 79 );
00306
00307 if ( count < 0 ) {
00308 emit errorMessage( i18n( "Unable to read lock file '%1'.")
00309 .arg( fileName ) );
00310 ::close( lfd );
00311 return false;
00312 }
00313 buf[ count ] = 0;
00314 ::close( lfd );
00315
00316 count = sscanf( buf, "%d", &pid );
00317 if ( ( count != 1 ) || ( pid <= 0 ) ) {
00318 emit errorMessage( i18n( "Unable to get PID from file '%1'.")
00319 .arg( fileName ) );
00320 return false;
00321 }
00322
00323 if ( !kill( (pid_t) pid, 0 ) ) {
00324 emit errorMessage( i18n( "Process with PID %1, which is locking the device, is still running.")
00325 .arg( pid ) );
00326 return false;
00327 }
00328
00329 if ( errno != ESRCH ) {
00330 emit errorMessage( i18n( "Unable to emit signal to PID of existing lock file.") );
00331 return false;
00332 }
00333 }
00334
00335 if ( ( lfd = creat( QFile::encodeName( fileName ).data(), 0644 ) ) == -1 ) {
00336 emit errorMessage( i18n( "Unable to create lock file '%1'. "
00337 "Please check that you have sufficient permissions.")
00338 .arg( fileName ) );
00339 return false;
00340 }
00341
00342 pid = (int) getpid();
00343 pw = getpwuid( getuid() );
00344 content.sprintf( "%08d %s %s", pid, "kandy", pw->pw_name );
00345 write( lfd, QFile::encodeName( content ).data(), content.length() );
00346 ::close( lfd );
00347
00348 is_locked = true;
00349
00350 return true;
00351 #endif
00352 }
00353
00354
00355 void Modem::unlockDevice()
00356 {
00357 #ifdef HAVE_LOCKDEV
00358 dev_unlock( (*prefs).serialDevice().local8Bit(), getpid() );
00359 #else
00360 if ( is_locked ) {
00361 QStringList pathList = QStringList::split( "/", (*prefs).serialDevice() );
00362
00363 QFile::remove( (*prefs).lockDirectory() + "/LCK.." + pathList.last() );
00364 is_locked = false;
00365 }
00366 #endif
00367 }
00368
00369
00370 bool Modem::dsrOn()
00371 {
00372 int flags;
00373
00374
00375 if ( !fd ) {
00376 #ifdef MODEM_DEBUG
00377 fprintf( stderr, "Modem: dsrOn(): File not open.\n" );
00378 #endif
00379 return false;
00380 }
00381
00382 if ( ioctl( fd, TIOCMGET, &flags ) == -1 ) {
00383 #ifdef MODEM_DEBUG
00384 fprintf( stderr, "Modem: dsrOn(): ioctl() failed.\n" );
00385 #endif
00386 return false;
00387 }
00388
00389 return ( flags & TIOCM_DSR ) != 0;
00390 }
00391
00392
00393 bool Modem::ctsOn()
00394 {
00395 int flags;
00396
00397
00398 if ( !fd ) {
00399 #ifdef MODEM_DEBUG
00400 fprintf( stderr, "Modem: ctsOn(): File not open.\n" );
00401 #endif
00402 return false;
00403 }
00404
00405 if ( ioctl( fd, TIOCMGET, &flags ) == -1) {
00406 #ifdef MODEM_DEBUG
00407 fprintf( stderr, "Modem: ctsOn(): ioctl() failed.\n" );
00408 #endif
00409 return false;
00410 }
00411
00412 return ( flags & TIOCM_CTS ) != 0;
00413 }
00414
00415
00416 void Modem::writeChar( const char c )
00417 {
00418 write( fd, (const void *) &c, 1 );
00419 }
00420
00421
00422 void Modem::writeLine( const char *line )
00423 {
00424 kdDebug() << "Modem::writeLine(): " << line << endl;
00425
00426 write( fd, (const void *) line, strlen( line ) );
00427 writeChar( '\r' );
00428 }
00429
00430
00431 void Modem::timerStart( int msec )
00432 {
00433 timer->start( msec, true );
00434 }
00435
00436
00437 void Modem::receiveXModem( bool crc )
00438 {
00439 disconnect( sn, 0, this, 0 );
00440 connect( sn, SIGNAL( activated( int ) ), SLOT( readXChar( int ) ) );
00441
00442 xcrc = crc;
00443
00444 if ( xcrc ) {
00445 writeChar( 'C' );
00446 xstate = 1;
00447 timerStart( 3000 );
00448 } else {
00449 writeChar( CNAK );
00450 xstate = 5;
00451 timerStart( 10000 );
00452 }
00453
00454 xblock = 1;
00455 }
00456
00457
00458 void Modem::abortXModem()
00459 {
00460 timer->stop();
00461 writeChar( CCAN );
00462 xreset();
00463 emit xmodemDone( false );
00464 }
00465
00466
00467 void Modem::timerDone()
00468 {
00469 #ifdef MODEM_DEBUG
00470 fprintf( stderr, "Modem: timeout, xstate = %d.\n", xstate );
00471 #endif
00472
00473 switch ( xstate ) {
00474 case 0:
00475 emit timeout();
00476 break;
00477
00478 case 1:
00479 case 2:
00480 case 3:
00481 writeChar( 'C' );
00482 xstate++;
00483 timerStart( 1000 );
00484 break;
00485
00486 case 4:
00487 xcrc = false;
00488
00489 case 5:
00490 case 6:
00491 case 7:
00492 case 8:
00493 case 9:
00494 writeChar( CNAK );
00495 xstate++;
00496 timerStart( 1000 );
00497 break;
00498
00499 case 10:
00500 xreset();
00501 emit xmodemDone( false );
00502 break;
00503
00504 default:
00505 writeChar( CNAK );
00506 xstate = 5;
00507 timerStart( 1000 );
00508 }
00509 }
00510
00511
00512 void Modem::readChar( int )
00513 {
00514 uchar c;
00515
00516
00517 while ( read( fd, (void *) &c, 1 ) == 1 ) {
00518 if ( c == '\n' ) {
00519 buffer[ bufpos ] = 0;
00520 bufpos = 0;
00521 emit gotLine( (const char *) buffer );
00522 break;
00523 } else
00524 if ( ( bufpos < 1000 ) && ( c != '\r' ) )
00525 buffer[ bufpos++ ] = c;
00526 }
00527 }
00528
00529
00530 void Modem::readXChar( int )
00531 {
00532 uchar c;
00533 static uchar crc_hi, block, cblock;
00534
00535
00536 while ( read( fd, (void *) &c, 1 ) == 1 ) {
00537 switch ( xstate ) {
00538 case 1:
00539 case 2:
00540 case 3:
00541 case 4:
00542 case 5:
00543 case 6:
00544 case 7:
00545 case 8:
00546 case 9:
00547 case 10:
00548 if ( c == CSOH ) {
00549 timerStart( 1000 );
00550 xsize = 128;
00551 xstate = 11;
00552 } else
00553 if ( c == CSTX ) {
00554 timerStart( 1000 );
00555 xsize = 1024;
00556 xstate = 11;
00557 } else
00558 if ( c == CEOT ) {
00559 timer->stop();
00560 writeChar( CACK );
00561 xreset();
00562 emit xmodemDone( true );
00563 } else
00564 timerStart( 1000 );
00565 break;
00566
00567 case 11:
00568 timerStart( 1000 );
00569 block = c;
00570 xstate++;
00571 break;
00572
00573 case 12:
00574 timerStart( 1000 );
00575 cblock = c;
00576 xstate++;
00577 bufpos = 0;
00578 break;
00579
00580 case 13:
00581 timerStart( 1000 );
00582 buffer[ bufpos++ ] = c;
00583 if ( bufpos == xsize ) {
00584 bufpos = 0;
00585 xstate++;
00586 if ( !xcrc )
00587 xstate++;
00588 }
00589 break;
00590
00591 case 14:
00592 timerStart( 1000 );
00593 crc_hi = c;
00594 xstate++;
00595 break;
00596
00597 case 15:
00598 timerStart( 10000 );
00599 xstate = 4;
00600 if ( (uchar) ( block ^ cblock ) != 0xff ) {
00601 writeChar( CNAK );
00602 break;
00603 }
00604 if ( block+1 == xblock ) {
00605 writeChar( CACK );
00606 break;
00607 }
00608 if ( block != xblock ) {
00609 timer->stop();
00610 writeChar( CCAN );
00611 xreset();
00612 emit xmodemDone( false );
00613 break;
00614 }
00615 if ( xcrc ) {
00616 if ( ( (ushort) crc_hi << 8 | (ushort) c ) != calcCRC() ) {
00617 writeChar( CNAK );
00618 break;
00619 }
00620 } else {
00621 if ( c != calcChecksum() ) {
00622 writeChar( CNAK );
00623 break;
00624 }
00625 }
00626 writeChar( CACK );
00627 xblock++;
00628 emit gotXBlock( buffer, xsize );
00629 break;
00630
00631 default:
00632 break;
00633 }
00634 }
00635 }
00636
00637
00638 void Modem::init()
00639 {
00640 is_locked = false;
00641
00642 fd = 0;
00643 sn = 0;
00644
00645 cspeed = B38400;
00646
00647
00648 cflag = CS8 | CREAD | CLOCAL;
00649
00650
00651 bufpos = 0;
00652 }
00653
00654
00655 void Modem::xreset()
00656 {
00657 bufpos = 0;
00658
00659 xstate = 0;
00660 xcrc = false;
00661 xblock = 0;
00662 xsize = 0;
00663
00664 if ( sn ) {
00665 disconnect( sn, 0, this, 0 );
00666 connect( sn, SIGNAL( activated( int ) ), SLOT( readChar( int ) ) );
00667 }
00668 }
00669
00670
00671 uchar Modem::calcChecksum()
00672 {
00673 int i;
00674 uchar c = 0;
00675
00676
00677 for ( i = 0; i < xsize; i++ )
00678 c += buffer[ i ];
00679
00680 return c;
00681 }
00682
00683
00684 ushort Modem::calcCRC()
00685 {
00686 int i, j;
00687 ushort c = 0;
00688
00689
00690 for ( i = 0; i < xsize; i++ ) {
00691 c ^= (ushort) buffer[ i ] << 8;
00692 for ( j = 0; j < 8; j++ )
00693 if ( c & 0x8000 )
00694 c = c << 1 ^ 0x1021;
00695 else
00696 c <<= 1;
00697 }
00698
00699 return c;
00700 }
00701
00702 #include "modem.moc"