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 <sys/file.h>
34 #include <fcntl.h>
35 #include <termios.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <signal.h>
41 #include <pwd.h>
42 #include <errno.h>
43 
44 #include <tqglobal.h>
45 
46 #include <tdelocale.h>
47 #include <kdebug.h>
48 
49 #include "modem.h"
50 
51 
52 #ifndef CSOH
53 #define CSOH 01
54 #endif
55 
56 #ifndef CSTX
57 #define CSTX 02
58 #endif
59 
60 #ifndef CEOT
61 #define CEOT 04
62 #endif
63 
64 #ifndef CACK
65 #define CACK 06
66 #endif
67 
68 #ifndef CNAK
69 #define CNAK 025
70 #endif
71 
72 #ifndef CCAN
73 #define CCAN 030
74 #endif
75 
76 
77 
78 Modem::Modem( KandyPrefs *kprefs, TQObject *parent, const char *name ) :
79  TQObject(parent, name), fd(-1)
80 {
81  mOpen = false;
82 
83  prefs = kprefs;
84 
85  timer = new TQTimer( this, "modemtimer" );
86  TQ_CHECK_PTR( timer );
87  connect( timer, TQ_SIGNAL( timeout() ), TQ_SLOT( timerDone() ) );
88 
89  init();
90  xreset();
91 }
92 
93 
94 Modem::~Modem()
95 {
96  close();
97 }
98 
99 
100 void Modem::setSpeed( int speed )
101 {
102  switch ( speed ) {
103  case 300:
104  cspeed = B300;
105  break;
106  case 600:
107  cspeed = B600;
108  break;
109  case 1200:
110  cspeed = B1200;
111  break;
112  case 2400:
113  cspeed = B2400;
114  break;
115  case 4800:
116  cspeed = B4800;
117  break;
118  case 9600:
119  cspeed = B9600;
120  break;
121  case 19200:
122  cspeed = B19200;
123  break;
124  case 38400:
125  cspeed = B38400;
126  break;
127  case 57600:
128  cspeed = B57600;
129  break;
130  case 115200:
131  cspeed = B115200;
132  break;
133  case 230400:
134  cspeed = B230400;
135  break;
136  default:
137 #ifdef MODEM_DEBUG
138  fprintf(stderr, "Modem: setSpeed(): fallback to default speed.\n");
139 #endif
140  cspeed = B38400;
141  }
142 }
143 
144 
145 void Modem::setData( int data )
146 {
147  cflag &= ~CSIZE;
148 
149  switch ( data ) {
150  case 5:
151  cflag |= CS5;
152  break;
153  case 6:
154  cflag |= CS6;
155  break;
156  case 7:
157  cflag |= CS7;
158  break;
159  default:
160  cflag |= CS8;
161  }
162 }
163 
164 
165 void Modem::setParity( char parity )
166 {
167  cflag &= ~( PARENB | PARODD );
168 
169  if ( parity == 'E' )
170  cflag |= PARENB;
171  else if ( parity == 'O' )
172  cflag |= PARENB | PARODD;
173 }
174 
175 
176 void Modem::setStop( int stop )
177 {
178  if (stop == 2)
179  cflag |= CSTOPB;
180  else
181  cflag &= ~CSTOPB;
182 }
183 
184 
185 bool Modem::open()
186 {
187  struct termios tty;
188 
189  close();
190 
191  if (fd == -1)
192  {
193  TQCString dev = TQFile::encodeName( (*prefs).serialDevice() );
194  const char *fdev = dev.data();
195  if ( ( fd = ::open( fdev, O_RDWR | O_NOCTTY | O_NONBLOCK ) ) == -1 ) {
196  emit errorMessage( i18n( "Unable to open device '%1'. "
197  "Please check that you have sufficient permissions." )
198  .arg( fdev ) );
199  return false;
200  }
201  }
202 
203  if ( !lockDevice() )
204  return false;
205 
206  tcflush( fd, TCIOFLUSH );
207  if ( tcgetattr( fd, &init_tty ) == -1 ) {
208  int errnumber = errno;
209  emit errorMessage( i18n( "Communication setup failed (tcgetattr code: %1)" )
210  .arg(strerror(errnumber)) );
211  unlockDevice();
212  ::close( fd );
213  fd = -1;
214  return false;
215  }
216 
217  memset( &tty, 0, sizeof( tty ) );
218  tty.c_iflag = IGNBRK | IGNPAR;
219  tty.c_oflag = 0;
220  tty.c_cflag = cflag;
221  tty.c_lflag = 0;
222  cfsetospeed( &tty, cspeed );
223  cfsetispeed( &tty, cspeed );
224  tcdrain( fd );
225 
226  if ( tcsetattr( fd, TCSANOW, &tty ) == -1 ) {
227  emit errorMessage( i18n( "tcsetattr() failed." ) );
228  unlockDevice();
229  ::close( fd );
230  fd = -1;
231  return false;
232  }
233 
234  sn = new TQSocketNotifier( fd, TQSocketNotifier::Read, this,
235  "modemsocketnotifier" );
236  TQ_CHECK_PTR( sn );
237  connect( sn, TQ_SIGNAL( activated( int ) ), TQ_SLOT( readChar( int ) ) );
238 
239  mOpen = true;
240 
241  return true;
242 }
243 
244 
245 void Modem::close()
246 {
247  timer->stop();
248 
249  delete sn;
250  sn = 0;
251 
252  unlockDevice();
253 
254  if ( fd ) {
255  tcflush( fd, TCIOFLUSH );
256  tcsetattr( fd, TCSANOW, &init_tty );
257  ::close( fd );
258  fd = -1;
259  }
260 
261  xreset();
262 
263  mOpen = false;
264 }
265 
266 
267 void Modem::flush()
268 {
269  if ( fd != -1) {
270  tcflush( fd, TCIOFLUSH );
271  bufpos = 0;
272  }
273 }
274 
275 bool Modem::lockDevice()
276 {
277  if (is_locked)
278  return true;
279 
280  if (flock(fd, LOCK_EX))
281  {
282  // Locking failed
283  is_locked = false;
284  emit errorMessage(i18n("Unable to lock device '%1'.").arg((*prefs).serialDevice()));
285  }
286  else
287  {
288  is_locked = true;
289  }
290 
291  return is_locked;
292 }
293 
294 
295 void Modem::unlockDevice()
296 {
297  if (fd != -1 && is_locked)
298  {
299  flock(fd, LOCK_UN);
300  is_locked = false;
301  }
302 }
303 
304 
305 bool Modem::dsrOn()
306 {
307  int flags;
308 
309 
310  if ( fd == -1 ) {
311 #ifdef MODEM_DEBUG
312  fprintf( stderr, "Modem: dsrOn(): File not open.\n" );
313 #endif
314  return false;
315  }
316 
317  if ( ioctl( fd, TIOCMGET, &flags ) == -1 ) {
318 #ifdef MODEM_DEBUG
319  fprintf( stderr, "Modem: dsrOn(): ioctl() failed.\n" );
320 #endif
321  return false;
322  }
323 
324  return ( flags & TIOCM_DSR ) != 0;
325 }
326 
327 
328 bool Modem::ctsOn()
329 {
330  int flags;
331 
332 
333  if ( fd == -1 ) {
334 #ifdef MODEM_DEBUG
335  fprintf( stderr, "Modem: ctsOn(): File not open.\n" );
336 #endif
337  return false;
338  }
339 
340  if ( ioctl( fd, TIOCMGET, &flags ) == -1) {
341 #ifdef MODEM_DEBUG
342  fprintf( stderr, "Modem: ctsOn(): ioctl() failed.\n" );
343 #endif
344  return false;
345  }
346 
347  return ( flags & TIOCM_CTS ) != 0;
348 }
349 
350 
351 void Modem::writeChar( const char c )
352 {
353  write( fd, (const void *) &c, 1 );
354 }
355 
356 
357 void Modem::writeLine( const char *line )
358 {
359  kdDebug() << "Modem::writeLine(): " << line << endl;
360 
361  write( fd, (const void *) line, strlen( line ) );
362  writeChar( '\r' );
363 }
364 
365 
366 void Modem::timerStart( int msec )
367 {
368  timer->start( msec, true );
369 }
370 
371 
372 void Modem::receiveXModem( bool crc )
373 {
374  disconnect( sn, 0, this, 0 );
375  connect( sn, TQ_SIGNAL( activated( int ) ), TQ_SLOT( readXChar( int ) ) );
376 
377  xcrc = crc;
378 
379  if ( xcrc ) {
380  writeChar( 'C' );
381  xstate = 1;
382  timerStart( 3000 );
383  } else {
384  writeChar( CNAK );
385  xstate = 5;
386  timerStart( 10000 );
387  }
388 
389  xblock = 1;
390 }
391 
392 
393 void Modem::abortXModem()
394 {
395  timer->stop();
396  writeChar( CCAN );
397  xreset();
398  emit xmodemDone( false );
399 }
400 
401 
402 void Modem::timerDone()
403 {
404 #ifdef MODEM_DEBUG
405  fprintf( stderr, "Modem: timeout, xstate = %d.\n", xstate );
406 #endif
407 
408  switch ( xstate ) {
409  case 0: /* non-XModem mode */
410  emit timeout();
411  break;
412 
413  case 1: /* 1st 'C' sent */
414  case 2: /* 2nd 'C' sent */
415  case 3: /* 3rd 'C' sent */
416  writeChar( 'C' );
417  xstate++;
418  timerStart( 1000 ); /* Should be 3000 in original XModem */
419  break;
420 
421  case 4: /* 4th 'C' sent */
422  xcrc = false;
423 
424  case 5: /* 1st <NAK> sent */
425  case 6: /* 2nd <NAK> sent */
426  case 7: /* 3rd <NAK> sent */
427  case 8: /* 4th <NAK> sent */
428  case 9: /* 5th <NAK> sent */
429  writeChar( CNAK );
430  xstate++;
431  timerStart( 1000 ); /* Should be 10000 in original XModem */
432  break;
433 
434  case 10: /* 6th <NAK> sent */
435  xreset();
436  emit xmodemDone( false );
437  break;
438 
439  default: /* pending XModem block */
440  writeChar( CNAK );
441  xstate = 5;
442  timerStart( 1000 ); /* Should be 10000 in original XModem */
443  }
444 }
445 
446 
447 void Modem::readChar( int )
448 {
449  uchar c;
450 
451 
452  while ( read( fd, (void *) &c, 1 ) == 1 ) {
453  if ( c == '\n' ) {
454  buffer[ bufpos ] = 0;
455  bufpos = 0;
456  emit gotLine( (const char *) buffer );
457  break;
458  } else
459  if ( ( bufpos < 1000 ) && ( c != '\r' ) )
460  buffer[ bufpos++ ] = c;
461  }
462 }
463 
464 
465 void Modem::readXChar( int )
466 {
467  uchar c;
468  static uchar crc_hi, block, cblock;
469 
470 
471  while ( read( fd, (void *) &c, 1 ) == 1 ) {
472  switch ( xstate ) {
473  case 1: /* 1st 'C' sent */
474  case 2: /* 2nd 'C' sent */
475  case 3: /* 3rd 'C' sent */
476  case 4: /* 4th 'C' sent */
477  case 5: /* 1st <NAK> sent */
478  case 6: /* 2nd <NAK> sent */
479  case 7: /* 3rd <NAK> sent */
480  case 8: /* 4th <NAK> sent */
481  case 9: /* 5th <NAK> sent */
482  case 10: /* 6th <NAK> sent */
483  if ( c == CSOH ) {
484  timerStart( 1000 );
485  xsize = 128;
486  xstate = 11;
487  } else
488  if ( c == CSTX ) {
489  timerStart( 1000 );
490  xsize = 1024;
491  xstate = 11;
492  } else
493  if ( c == CEOT ) {
494  timer->stop();
495  writeChar( CACK );
496  xreset();
497  emit xmodemDone( true );
498  } else
499  timerStart( 1000 );
500  break;
501 
502  case 11: /* <SOH> or <STX> received */
503  timerStart( 1000 );
504  block = c;
505  xstate++;
506  break;
507 
508  case 12: /* block number received */
509  timerStart( 1000 );
510  cblock = c;
511  xstate++;
512  bufpos = 0;
513  break;
514 
515  case 13: /* complement block number received */
516  timerStart( 1000 );
517  buffer[ bufpos++ ] = c;
518  if ( bufpos == xsize ) {
519  bufpos = 0;
520  xstate++;
521  if ( !xcrc )
522  xstate++;
523  }
524  break;
525 
526  case 14: /* data block received */
527  timerStart( 1000 );
528  crc_hi = c;
529  xstate++;
530  break;
531 
532  case 15: /* crc high-byte received */
533  timerStart( 10000 );
534  xstate = 4;
535  if ( (uchar) ( block ^ cblock ) != 0xff ) {
536  writeChar( CNAK );
537  break;
538  }
539  if ( block+1 == xblock ) {
540  writeChar( CACK );
541  break;
542  }
543  if ( block != xblock ) {
544  timer->stop();
545  writeChar( CCAN );
546  xreset();
547  emit xmodemDone( false );
548  break;
549  }
550  if ( xcrc ) {
551  if ( ( (ushort) crc_hi << 8 | (ushort) c ) != calcCRC() ) {
552  writeChar( CNAK );
553  break;
554  }
555  } else {
556  if ( c != calcChecksum() ) {
557  writeChar( CNAK );
558  break;
559  }
560  }
561  writeChar( CACK );
562  xblock++;
563  emit gotXBlock( buffer, xsize );
564  break;
565 
566  default:
567  break;
568  }
569  }
570 }
571 
572 
573 void Modem::init()
574 {
575  is_locked = false;
576 
577  fd = -1;
578  sn = 0;
579 
580  cspeed = B38400;
581 
582  // No flow control
583  cflag = CS8 | CREAD | CLOCAL;
584  // cflag = CS8 | CREAD | CLOCAL | CRTSCTS;
585 
586  bufpos = 0;
587 }
588 
589 
590 void Modem::xreset()
591 {
592  bufpos = 0;
593 
594  xstate = 0;
595  xcrc = false;
596  xblock = 0;
597  xsize = 0;
598 
599  if ( sn ) {
600  disconnect( sn, 0, this, 0 );
601  connect( sn, TQ_SIGNAL( activated( int ) ), TQ_SLOT( readChar( int ) ) );
602  }
603 }
604 
605 
606 uchar Modem::calcChecksum()
607 {
608  int i;
609  uchar c = 0;
610 
611 
612  for ( i = 0; i < xsize; i++ )
613  c += buffer[ i ];
614 
615  return c;
616 }
617 
618 
619 ushort Modem::calcCRC()
620 {
621  int i, j;
622  ushort c = 0;
623 
624 
625  for ( i = 0; i < xsize; i++ ) {
626  c ^= (ushort) buffer[ i ] << 8;
627  for ( j = 0; j < 8; j++ )
628  if ( c & 0x8000 )
629  c = c << 1 ^ 0x1021;
630  else
631  c <<= 1;
632  }
633 
634  return c;
635 }
636 
637 #include "modem.moc"