kandy

modem.cpp
1 /*
2  KMLOCfg
3 
4  A utility to configure the ELSA MicroLink(tm) Office modem.
5 
6  Copyright (C) 2000 Oliver Gantz <Oliver.Gantz@epost.de>
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 
22  ------
23  ELSA and MicroLink are trademarks of ELSA AG, Aachen.
24 */
25 
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/ioctl.h>
33 #include <fcntl.h>
34 #include <termios.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <signal.h>
40 #include <pwd.h>
41 #include <errno.h>
42 
43 #include <tqglobal.h>
44 
45 #include <klocale.h>
46 #include <kdebug.h>
47 
48 #include "modem.h"
49 
50 
51 #ifndef CSOH
52 #define CSOH 01
53 #endif
54 
55 #ifndef CSTX
56 #define CSTX 02
57 #endif
58 
59 #ifndef CEOT
60 #define CEOT 04
61 #endif
62 
63 #ifndef CACK
64 #define CACK 06
65 #endif
66 
67 #ifndef CNAK
68 #define CNAK 025
69 #endif
70 
71 #ifndef CCAN
72 #define CCAN 030
73 #endif
74 
75 
76 
77 Modem::Modem( KandyPrefs *kprefs, TQObject *parent, const char *name ) :
78  TQObject(parent, name)
79 {
80  mOpen = false;
81 
82  prefs = kprefs;
83 
84  timer = new TQTimer( this, "modemtimer" );
85  Q_CHECK_PTR( timer );
86  connect( timer, TQT_SIGNAL( timeout() ), TQT_SLOT( timerDone() ) );
87 
88  init();
89  xreset();
90 }
91 
92 
93 Modem::~Modem()
94 {
95  close();
96 }
97 
98 
99 void Modem::setSpeed( int speed )
100 {
101  switch ( speed ) {
102  case 300:
103  cspeed = B300;
104  break;
105  case 600:
106  cspeed = B600;
107  break;
108  case 1200:
109  cspeed = B1200;
110  break;
111  case 2400:
112  cspeed = B2400;
113  break;
114  case 4800:
115  cspeed = B4800;
116  break;
117  case 9600:
118  cspeed = B9600;
119  break;
120  case 19200:
121  cspeed = B19200;
122  break;
123  case 38400:
124  cspeed = B38400;
125  break;
126  case 57600:
127  cspeed = B57600;
128  break;
129  case 115200:
130  cspeed = B115200;
131  break;
132  case 230400:
133  cspeed = B230400;
134  break;
135  default:
136 #ifdef MODEM_DEBUG
137  fprintf(stderr, "Modem: setSpeed(): fallback to default speed.\n");
138 #endif
139  cspeed = B38400;
140  }
141 }
142 
143 
144 void Modem::setData( int data )
145 {
146  cflag &= ~CSIZE;
147 
148  switch ( data ) {
149  case 5:
150  cflag |= CS5;
151  break;
152  case 6:
153  cflag |= CS6;
154  break;
155  case 7:
156  cflag |= CS7;
157  break;
158  default:
159  cflag |= CS8;
160  }
161 }
162 
163 
164 void Modem::setParity( char parity )
165 {
166  cflag &= ~( PARENB | PARODD );
167 
168  if ( parity == 'E' )
169  cflag |= PARENB;
170  else if ( parity == 'O' )
171  cflag |= PARENB | PARODD;
172 }
173 
174 
175 void Modem::setStop( int stop )
176 {
177  if (stop == 2)
178  cflag |= CSTOPB;
179  else
180  cflag &= ~CSTOPB;
181 }
182 
183 
184 bool Modem::open()
185 {
186  struct termios tty;
187 
188 
189  close();
190 
191  if ( !lockDevice() )
192  return false;
193 
194  TQCString dev = TQFile::encodeName( (*prefs).serialDevice() );
195  const char *fdev = dev.data();
196  if ( ( fd = ::open( fdev, O_RDWR | O_NOCTTY | O_NONBLOCK ) ) == -1 ) {
197  emit errorMessage( i18n( "Unable to open device '%1'. "
198  "Please check that you have sufficient permissions." )
199  .arg( fdev ) );
200  return false;
201  }
202 
203  tcflush( fd, TCIOFLUSH );
204  if ( tcgetattr( fd, &init_tty ) == -1 ) {
205  int errnumber = errno;
206  emit errorMessage( i18n( "Communication setup failed (tcgetattr code: %1)" )
207  .arg(strerror(errnumber)) );
208  ::close( fd );
209  fd = 0;
210  return false;
211  }
212 
213  memset( &tty, 0, sizeof( tty ) );
214  tty.c_iflag = IGNBRK | IGNPAR;
215  tty.c_oflag = 0;
216  tty.c_cflag = cflag;
217  tty.c_lflag = 0;
218  cfsetospeed( &tty, cspeed );
219  cfsetispeed( &tty, cspeed );
220  tcdrain( fd );
221 
222  if ( tcsetattr( fd, TCSANOW, &tty ) == -1 ) {
223  emit errorMessage( i18n( "tcsetattr() failed." ) );
224  ::close( fd );
225  fd = 0;
226  return false;
227  }
228 
229  sn = new TQSocketNotifier( fd, TQSocketNotifier::Read, this,
230  "modemsocketnotifier" );
231  Q_CHECK_PTR( sn );
232  connect( sn, TQT_SIGNAL( activated( int ) ), TQT_SLOT( readChar( int ) ) );
233 
234  mOpen = true;
235 
236  return true;
237 }
238 
239 
240 void Modem::close()
241 {
242  timer->stop();
243 
244  delete sn;
245  sn = 0;
246 
247  if ( fd ) {
248  tcflush( fd, TCIOFLUSH );
249  tcsetattr( fd, TCSANOW, &init_tty );
250  ::close( fd );
251  fd = 0;
252  }
253 
254  xreset();
255 
256  unlockDevice();
257 
258  mOpen = false;
259 }
260 
261 
262 void Modem::flush()
263 {
264  if ( fd ) {
265  tcflush( fd, TCIOFLUSH );
266  bufpos = 0;
267  }
268 }
269 
270 #ifdef HAVE_LOCKDEV
271 #include <lockdev.h>
272 #endif
273 
274 bool Modem::lockDevice()
275 {
276  if ( is_locked )
277  return true;
278 
279 #ifdef HAVE_LOCKDEV
280  is_locked = !dev_lock( (*prefs).serialDevice().local8Bit() );
281  if (!is_locked)
282  emit errorMessage( i18n( "Unable to lock device '%1'." ).arg(
283  (*prefs).serialDevice() ));
284  return is_locked;
285 #else
286  ssize_t count;
287  pid_t pid;
288  int lfd;
289  struct passwd *pw;
290  TQStringList pathList;
291  TQString fileName, content;
292 
293  pathList = TQStringList::split( "/", (*prefs).serialDevice() );
294  fileName = (*prefs).lockDirectory() + "/LCK.." + pathList.last();
295 
296  if ( !access( TQFile::encodeName( fileName ).data(), F_OK ) ) {
297  char buf[256];
298 
299 
300  if ( ( lfd = ::open( TQFile::encodeName( fileName ), O_RDONLY ) ) < 0 ) {
301  emit errorMessage( i18n( "Unable to open lock file '%1'.")
302  .arg( fileName ) );
303  return false;
304  }
305 
306  count = read( lfd, buf, 79 );
307 
308  if ( count < 0 ) {
309  emit errorMessage( i18n( "Unable to read lock file '%1'.")
310  .arg( fileName ) );
311  ::close( lfd );
312  return false;
313  }
314  buf[ count ] = 0;
315  ::close( lfd );
316 
317  count = sscanf( buf, "%d", &pid );
318  if ( ( count != 1 ) || ( pid <= 0 ) ) {
319  emit errorMessage( i18n( "Unable to get PID from file '%1'.")
320  .arg( fileName ) );
321  return false;
322  }
323 
324  if ( !kill( (pid_t) pid, 0 ) ) {
325  emit errorMessage( i18n( "Process with PID %1, which is locking the device, is still running.")
326  .arg( pid ) );
327  return false;
328  }
329 
330  if ( errno != ESRCH ) {
331  emit errorMessage( i18n( "Unable to emit signal to PID of existing lock file.") );
332  return false;
333  }
334  }
335 
336  if ( ( lfd = creat( TQFile::encodeName( fileName ).data(), 0644 ) ) == -1 ) {
337  emit errorMessage( i18n( "Unable to create lock file '%1'. "
338  "Please check that you have sufficient permissions.")
339  .arg( fileName ) );
340  return false;
341  }
342 
343  pid = (int) getpid();
344  pw = getpwuid( getuid() );
345  content.sprintf( "%08d %s %s", pid, "kandy", pw->pw_name );
346  write( lfd, TQFile::encodeName( content ).data(), content.length() );
347  ::close( lfd );
348 
349  is_locked = true;
350 
351  return true;
352 #endif
353 }
354 
355 
356 void Modem::unlockDevice()
357 {
358 #ifdef HAVE_LOCKDEV
359  dev_unlock( (*prefs).serialDevice().local8Bit(), getpid() );
360 #else
361  if ( is_locked ) {
362  TQStringList pathList = TQStringList::split( "/", (*prefs).serialDevice() );
363 
364  TQFile::remove( (*prefs).lockDirectory() + "/LCK.." + pathList.last() );
365  is_locked = false;
366  }
367 #endif
368 }
369 
370 
371 bool Modem::dsrOn()
372 {
373  int flags;
374 
375 
376  if ( !fd ) {
377 #ifdef MODEM_DEBUG
378  fprintf( stderr, "Modem: dsrOn(): File not open.\n" );
379 #endif
380  return false;
381  }
382 
383  if ( ioctl( fd, TIOCMGET, &flags ) == -1 ) {
384 #ifdef MODEM_DEBUG
385  fprintf( stderr, "Modem: dsrOn(): ioctl() failed.\n" );
386 #endif
387  return false;
388  }
389 
390  return ( flags & TIOCM_DSR ) != 0;
391 }
392 
393 
394 bool Modem::ctsOn()
395 {
396  int flags;
397 
398 
399  if ( !fd ) {
400 #ifdef MODEM_DEBUG
401  fprintf( stderr, "Modem: ctsOn(): File not open.\n" );
402 #endif
403  return false;
404  }
405 
406  if ( ioctl( fd, TIOCMGET, &flags ) == -1) {
407 #ifdef MODEM_DEBUG
408  fprintf( stderr, "Modem: ctsOn(): ioctl() failed.\n" );
409 #endif
410  return false;
411  }
412 
413  return ( flags & TIOCM_CTS ) != 0;
414 }
415 
416 
417 void Modem::writeChar( const char c )
418 {
419  write( fd, (const void *) &c, 1 );
420 }
421 
422 
423 void Modem::writeLine( const char *line )
424 {
425  kdDebug() << "Modem::writeLine(): " << line << endl;
426 
427  write( fd, (const void *) line, strlen( line ) );
428  writeChar( '\r' );
429 }
430 
431 
432 void Modem::timerStart( int msec )
433 {
434  timer->start( msec, true );
435 }
436 
437 
438 void Modem::receiveXModem( bool crc )
439 {
440  disconnect( sn, 0, this, 0 );
441  connect( sn, TQT_SIGNAL( activated( int ) ), TQT_SLOT( readXChar( int ) ) );
442 
443  xcrc = crc;
444 
445  if ( xcrc ) {
446  writeChar( 'C' );
447  xstate = 1;
448  timerStart( 3000 );
449  } else {
450  writeChar( CNAK );
451  xstate = 5;
452  timerStart( 10000 );
453  }
454 
455  xblock = 1;
456 }
457 
458 
459 void Modem::abortXModem()
460 {
461  timer->stop();
462  writeChar( CCAN );
463  xreset();
464  emit xmodemDone( false );
465 }
466 
467 
468 void Modem::timerDone()
469 {
470 #ifdef MODEM_DEBUG
471  fprintf( stderr, "Modem: timeout, xstate = %d.\n", xstate );
472 #endif
473 
474  switch ( xstate ) {
475  case 0: /* non-XModem mode */
476  emit timeout();
477  break;
478 
479  case 1: /* 1st 'C' sent */
480  case 2: /* 2nd 'C' sent */
481  case 3: /* 3rd 'C' sent */
482  writeChar( 'C' );
483  xstate++;
484  timerStart( 1000 ); /* Should be 3000 in original XModem */
485  break;
486 
487  case 4: /* 4th 'C' sent */
488  xcrc = false;
489 
490  case 5: /* 1st <NAK> sent */
491  case 6: /* 2nd <NAK> sent */
492  case 7: /* 3rd <NAK> sent */
493  case 8: /* 4th <NAK> sent */
494  case 9: /* 5th <NAK> sent */
495  writeChar( CNAK );
496  xstate++;
497  timerStart( 1000 ); /* Should be 10000 in original XModem */
498  break;
499 
500  case 10: /* 6th <NAK> sent */
501  xreset();
502  emit xmodemDone( false );
503  break;
504 
505  default: /* pending XModem block */
506  writeChar( CNAK );
507  xstate = 5;
508  timerStart( 1000 ); /* Should be 10000 in original XModem */
509  }
510 }
511 
512 
513 void Modem::readChar( int )
514 {
515  uchar c;
516 
517 
518  while ( read( fd, (void *) &c, 1 ) == 1 ) {
519  if ( c == '\n' ) {
520  buffer[ bufpos ] = 0;
521  bufpos = 0;
522  emit gotLine( (const char *) buffer );
523  break;
524  } else
525  if ( ( bufpos < 1000 ) && ( c != '\r' ) )
526  buffer[ bufpos++ ] = c;
527  }
528 }
529 
530 
531 void Modem::readXChar( int )
532 {
533  uchar c;
534  static uchar crc_hi, block, cblock;
535 
536 
537  while ( read( fd, (void *) &c, 1 ) == 1 ) {
538  switch ( xstate ) {
539  case 1: /* 1st 'C' sent */
540  case 2: /* 2nd 'C' sent */
541  case 3: /* 3rd 'C' sent */
542  case 4: /* 4th 'C' sent */
543  case 5: /* 1st <NAK> sent */
544  case 6: /* 2nd <NAK> sent */
545  case 7: /* 3rd <NAK> sent */
546  case 8: /* 4th <NAK> sent */
547  case 9: /* 5th <NAK> sent */
548  case 10: /* 6th <NAK> sent */
549  if ( c == CSOH ) {
550  timerStart( 1000 );
551  xsize = 128;
552  xstate = 11;
553  } else
554  if ( c == CSTX ) {
555  timerStart( 1000 );
556  xsize = 1024;
557  xstate = 11;
558  } else
559  if ( c == CEOT ) {
560  timer->stop();
561  writeChar( CACK );
562  xreset();
563  emit xmodemDone( true );
564  } else
565  timerStart( 1000 );
566  break;
567 
568  case 11: /* <SOH> or <STX> received */
569  timerStart( 1000 );
570  block = c;
571  xstate++;
572  break;
573 
574  case 12: /* block number received */
575  timerStart( 1000 );
576  cblock = c;
577  xstate++;
578  bufpos = 0;
579  break;
580 
581  case 13: /* complement block number received */
582  timerStart( 1000 );
583  buffer[ bufpos++ ] = c;
584  if ( bufpos == xsize ) {
585  bufpos = 0;
586  xstate++;
587  if ( !xcrc )
588  xstate++;
589  }
590  break;
591 
592  case 14: /* data block received */
593  timerStart( 1000 );
594  crc_hi = c;
595  xstate++;
596  break;
597 
598  case 15: /* crc high-byte received */
599  timerStart( 10000 );
600  xstate = 4;
601  if ( (uchar) ( block ^ cblock ) != 0xff ) {
602  writeChar( CNAK );
603  break;
604  }
605  if ( block+1 == xblock ) {
606  writeChar( CACK );
607  break;
608  }
609  if ( block != xblock ) {
610  timer->stop();
611  writeChar( CCAN );
612  xreset();
613  emit xmodemDone( false );
614  break;
615  }
616  if ( xcrc ) {
617  if ( ( (ushort) crc_hi << 8 | (ushort) c ) != calcCRC() ) {
618  writeChar( CNAK );
619  break;
620  }
621  } else {
622  if ( c != calcChecksum() ) {
623  writeChar( CNAK );
624  break;
625  }
626  }
627  writeChar( CACK );
628  xblock++;
629  emit gotXBlock( buffer, xsize );
630  break;
631 
632  default:
633  break;
634  }
635  }
636 }
637 
638 
639 void Modem::init()
640 {
641  is_locked = false;
642 
643  fd = 0;
644  sn = 0;
645 
646  cspeed = B38400;
647 
648  // No flow control
649  cflag = CS8 | CREAD | CLOCAL;
650  // cflag = CS8 | CREAD | CLOCAL | CRTSCTS;
651 
652  bufpos = 0;
653 }
654 
655 
656 void Modem::xreset()
657 {
658  bufpos = 0;
659 
660  xstate = 0;
661  xcrc = false;
662  xblock = 0;
663  xsize = 0;
664 
665  if ( sn ) {
666  disconnect( sn, 0, this, 0 );
667  connect( sn, TQT_SIGNAL( activated( int ) ), TQT_SLOT( readChar( int ) ) );
668  }
669 }
670 
671 
672 uchar Modem::calcChecksum()
673 {
674  int i;
675  uchar c = 0;
676 
677 
678  for ( i = 0; i < xsize; i++ )
679  c += buffer[ i ];
680 
681  return c;
682 }
683 
684 
685 ushort Modem::calcCRC()
686 {
687  int i, j;
688  ushort c = 0;
689 
690 
691  for ( i = 0; i < xsize; i++ ) {
692  c ^= (ushort) buffer[ i ] << 8;
693  for ( j = 0; j < 8; j++ )
694  if ( c & 0x8000 )
695  c = c << 1 ^ 0x1021;
696  else
697  c <<= 1;
698  }
699 
700  return c;
701 }
702 
703 #include "modem.moc"