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
78Modem::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
94Modem::~Modem()
95{
96 close();
97}
98
99
100void 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
145void 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
165void 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
176void Modem::setStop( int stop )
177{
178 if (stop == 2)
179 cflag |= CSTOPB;
180 else
181 cflag &= ~CSTOPB;
182}
183
184
185bool 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
245void 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
267void Modem::flush()
268{
269 if ( fd != -1) {
270 tcflush( fd, TCIOFLUSH );
271 bufpos = 0;
272 }
273}
274
275bool 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
295void Modem::unlockDevice()
296{
297 if (fd != -1 && is_locked)
298 {
299 flock(fd, LOCK_UN);
300 is_locked = false;
301 }
302}
303
304
305bool 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
328bool 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
351void Modem::writeChar( const char c )
352{
353 write( fd, (const void *) &c, 1 );
354}
355
356
357void 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
366void Modem::timerStart( int msec )
367{
368 timer->start( msec, true );
369}
370
371
372void 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
393void Modem::abortXModem()
394{
395 timer->stop();
396 writeChar( CCAN );
397 xreset();
398 emit xmodemDone( false );
399}
400
401
402void 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
447void 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
465void 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
573void 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
590void 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
606uchar 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
619ushort 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"