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 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:
00476 emit timeout();
00477 break;
00478
00479 case 1:
00480 case 2:
00481 case 3:
00482 writeChar( 'C' );
00483 xstate++;
00484 timerStart( 1000 );
00485 break;
00486
00487 case 4:
00488 xcrc = false;
00489
00490 case 5:
00491 case 6:
00492 case 7:
00493 case 8:
00494 case 9:
00495 writeChar( CNAK );
00496 xstate++;
00497 timerStart( 1000 );
00498 break;
00499
00500 case 10:
00501 xreset();
00502 emit xmodemDone( false );
00503 break;
00504
00505 default:
00506 writeChar( CNAK );
00507 xstate = 5;
00508 timerStart( 1000 );
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:
00540 case 2:
00541 case 3:
00542 case 4:
00543 case 5:
00544 case 6:
00545 case 7:
00546 case 8:
00547 case 9:
00548 case 10:
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:
00569 timerStart( 1000 );
00570 block = c;
00571 xstate++;
00572 break;
00573
00574 case 12:
00575 timerStart( 1000 );
00576 cblock = c;
00577 xstate++;
00578 bufpos = 0;
00579 break;
00580
00581 case 13:
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:
00593 timerStart( 1000 );
00594 crc_hi = c;
00595 xstate++;
00596 break;
00597
00598 case 15:
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
00649 cflag = CS8 | CREAD | CLOCAL;
00650
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"