You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
638 lines
11 KiB
638 lines
11 KiB
/*
|
|
KMLOCfg
|
|
|
|
A utility to configure the ELSA MicroLink(tm) Office modem.
|
|
|
|
Copyright (C) 2000 Oliver Gantz <Oliver.Gantz@epost.de>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
------
|
|
ELSA and MicroLink are trademarks of ELSA AG, Aachen.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/file.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <pwd.h>
|
|
#include <errno.h>
|
|
|
|
#include <tqglobal.h>
|
|
|
|
#include <tdelocale.h>
|
|
#include <kdebug.h>
|
|
|
|
#include "modem.h"
|
|
|
|
|
|
#ifndef CSOH
|
|
#define CSOH 01
|
|
#endif
|
|
|
|
#ifndef CSTX
|
|
#define CSTX 02
|
|
#endif
|
|
|
|
#ifndef CEOT
|
|
#define CEOT 04
|
|
#endif
|
|
|
|
#ifndef CACK
|
|
#define CACK 06
|
|
#endif
|
|
|
|
#ifndef CNAK
|
|
#define CNAK 025
|
|
#endif
|
|
|
|
#ifndef CCAN
|
|
#define CCAN 030
|
|
#endif
|
|
|
|
|
|
|
|
Modem::Modem( KandyPrefs *kprefs, TQObject *parent, const char *name ) :
|
|
TQObject(parent, name), fd(-1)
|
|
{
|
|
mOpen = false;
|
|
|
|
prefs = kprefs;
|
|
|
|
timer = new TQTimer( this, "modemtimer" );
|
|
TQ_CHECK_PTR( timer );
|
|
connect( timer, TQT_SIGNAL( timeout() ), TQT_SLOT( timerDone() ) );
|
|
|
|
init();
|
|
xreset();
|
|
}
|
|
|
|
|
|
Modem::~Modem()
|
|
{
|
|
close();
|
|
}
|
|
|
|
|
|
void Modem::setSpeed( int speed )
|
|
{
|
|
switch ( speed ) {
|
|
case 300:
|
|
cspeed = B300;
|
|
break;
|
|
case 600:
|
|
cspeed = B600;
|
|
break;
|
|
case 1200:
|
|
cspeed = B1200;
|
|
break;
|
|
case 2400:
|
|
cspeed = B2400;
|
|
break;
|
|
case 4800:
|
|
cspeed = B4800;
|
|
break;
|
|
case 9600:
|
|
cspeed = B9600;
|
|
break;
|
|
case 19200:
|
|
cspeed = B19200;
|
|
break;
|
|
case 38400:
|
|
cspeed = B38400;
|
|
break;
|
|
case 57600:
|
|
cspeed = B57600;
|
|
break;
|
|
case 115200:
|
|
cspeed = B115200;
|
|
break;
|
|
case 230400:
|
|
cspeed = B230400;
|
|
break;
|
|
default:
|
|
#ifdef MODEM_DEBUG
|
|
fprintf(stderr, "Modem: setSpeed(): fallback to default speed.\n");
|
|
#endif
|
|
cspeed = B38400;
|
|
}
|
|
}
|
|
|
|
|
|
void Modem::setData( int data )
|
|
{
|
|
cflag &= ~CSIZE;
|
|
|
|
switch ( data ) {
|
|
case 5:
|
|
cflag |= CS5;
|
|
break;
|
|
case 6:
|
|
cflag |= CS6;
|
|
break;
|
|
case 7:
|
|
cflag |= CS7;
|
|
break;
|
|
default:
|
|
cflag |= CS8;
|
|
}
|
|
}
|
|
|
|
|
|
void Modem::setParity( char parity )
|
|
{
|
|
cflag &= ~( PARENB | PARODD );
|
|
|
|
if ( parity == 'E' )
|
|
cflag |= PARENB;
|
|
else if ( parity == 'O' )
|
|
cflag |= PARENB | PARODD;
|
|
}
|
|
|
|
|
|
void Modem::setStop( int stop )
|
|
{
|
|
if (stop == 2)
|
|
cflag |= CSTOPB;
|
|
else
|
|
cflag &= ~CSTOPB;
|
|
}
|
|
|
|
|
|
bool Modem::open()
|
|
{
|
|
struct termios tty;
|
|
|
|
close();
|
|
|
|
if (fd == -1)
|
|
{
|
|
TQCString dev = TQFile::encodeName( (*prefs).serialDevice() );
|
|
const char *fdev = dev.data();
|
|
if ( ( fd = ::open( fdev, O_RDWR | O_NOCTTY | O_NONBLOCK ) ) == -1 ) {
|
|
emit errorMessage( i18n( "Unable to open device '%1'. "
|
|
"Please check that you have sufficient permissions." )
|
|
.arg( fdev ) );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( !lockDevice() )
|
|
return false;
|
|
|
|
tcflush( fd, TCIOFLUSH );
|
|
if ( tcgetattr( fd, &init_tty ) == -1 ) {
|
|
int errnumber = errno;
|
|
emit errorMessage( i18n( "Communication setup failed (tcgetattr code: %1)" )
|
|
.arg(strerror(errnumber)) );
|
|
unlockDevice();
|
|
::close( fd );
|
|
fd = -1;
|
|
return false;
|
|
}
|
|
|
|
memset( &tty, 0, sizeof( tty ) );
|
|
tty.c_iflag = IGNBRK | IGNPAR;
|
|
tty.c_oflag = 0;
|
|
tty.c_cflag = cflag;
|
|
tty.c_lflag = 0;
|
|
cfsetospeed( &tty, cspeed );
|
|
cfsetispeed( &tty, cspeed );
|
|
tcdrain( fd );
|
|
|
|
if ( tcsetattr( fd, TCSANOW, &tty ) == -1 ) {
|
|
emit errorMessage( i18n( "tcsetattr() failed." ) );
|
|
unlockDevice();
|
|
::close( fd );
|
|
fd = -1;
|
|
return false;
|
|
}
|
|
|
|
sn = new TQSocketNotifier( fd, TQSocketNotifier::Read, this,
|
|
"modemsocketnotifier" );
|
|
TQ_CHECK_PTR( sn );
|
|
connect( sn, TQT_SIGNAL( activated( int ) ), TQT_SLOT( readChar( int ) ) );
|
|
|
|
mOpen = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void Modem::close()
|
|
{
|
|
timer->stop();
|
|
|
|
delete sn;
|
|
sn = 0;
|
|
|
|
unlockDevice();
|
|
|
|
if ( fd ) {
|
|
tcflush( fd, TCIOFLUSH );
|
|
tcsetattr( fd, TCSANOW, &init_tty );
|
|
::close( fd );
|
|
fd = -1;
|
|
}
|
|
|
|
xreset();
|
|
|
|
mOpen = false;
|
|
}
|
|
|
|
|
|
void Modem::flush()
|
|
{
|
|
if ( fd != -1) {
|
|
tcflush( fd, TCIOFLUSH );
|
|
bufpos = 0;
|
|
}
|
|
}
|
|
|
|
bool Modem::lockDevice()
|
|
{
|
|
if (is_locked)
|
|
return true;
|
|
|
|
if (flock(fd, LOCK_EX))
|
|
{
|
|
// Locking failed
|
|
is_locked = false;
|
|
emit errorMessage(i18n("Unable to lock device '%1'.").arg((*prefs).serialDevice()));
|
|
}
|
|
else
|
|
{
|
|
is_locked = true;
|
|
}
|
|
|
|
return is_locked;
|
|
}
|
|
|
|
|
|
void Modem::unlockDevice()
|
|
{
|
|
if (fd != -1 && is_locked)
|
|
{
|
|
flock(fd, LOCK_UN);
|
|
is_locked = false;
|
|
}
|
|
}
|
|
|
|
|
|
bool Modem::dsrOn()
|
|
{
|
|
int flags;
|
|
|
|
|
|
if ( fd == -1 ) {
|
|
#ifdef MODEM_DEBUG
|
|
fprintf( stderr, "Modem: dsrOn(): File not open.\n" );
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
if ( ioctl( fd, TIOCMGET, &flags ) == -1 ) {
|
|
#ifdef MODEM_DEBUG
|
|
fprintf( stderr, "Modem: dsrOn(): ioctl() failed.\n" );
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
return ( flags & TIOCM_DSR ) != 0;
|
|
}
|
|
|
|
|
|
bool Modem::ctsOn()
|
|
{
|
|
int flags;
|
|
|
|
|
|
if ( fd == -1 ) {
|
|
#ifdef MODEM_DEBUG
|
|
fprintf( stderr, "Modem: ctsOn(): File not open.\n" );
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
if ( ioctl( fd, TIOCMGET, &flags ) == -1) {
|
|
#ifdef MODEM_DEBUG
|
|
fprintf( stderr, "Modem: ctsOn(): ioctl() failed.\n" );
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
return ( flags & TIOCM_CTS ) != 0;
|
|
}
|
|
|
|
|
|
void Modem::writeChar( const char c )
|
|
{
|
|
write( fd, (const void *) &c, 1 );
|
|
}
|
|
|
|
|
|
void Modem::writeLine( const char *line )
|
|
{
|
|
kdDebug() << "Modem::writeLine(): " << line << endl;
|
|
|
|
write( fd, (const void *) line, strlen( line ) );
|
|
writeChar( '\r' );
|
|
}
|
|
|
|
|
|
void Modem::timerStart( int msec )
|
|
{
|
|
timer->start( msec, true );
|
|
}
|
|
|
|
|
|
void Modem::receiveXModem( bool crc )
|
|
{
|
|
disconnect( sn, 0, this, 0 );
|
|
connect( sn, TQT_SIGNAL( activated( int ) ), TQT_SLOT( readXChar( int ) ) );
|
|
|
|
xcrc = crc;
|
|
|
|
if ( xcrc ) {
|
|
writeChar( 'C' );
|
|
xstate = 1;
|
|
timerStart( 3000 );
|
|
} else {
|
|
writeChar( CNAK );
|
|
xstate = 5;
|
|
timerStart( 10000 );
|
|
}
|
|
|
|
xblock = 1;
|
|
}
|
|
|
|
|
|
void Modem::abortXModem()
|
|
{
|
|
timer->stop();
|
|
writeChar( CCAN );
|
|
xreset();
|
|
emit xmodemDone( false );
|
|
}
|
|
|
|
|
|
void Modem::timerDone()
|
|
{
|
|
#ifdef MODEM_DEBUG
|
|
fprintf( stderr, "Modem: timeout, xstate = %d.\n", xstate );
|
|
#endif
|
|
|
|
switch ( xstate ) {
|
|
case 0: /* non-XModem mode */
|
|
emit timeout();
|
|
break;
|
|
|
|
case 1: /* 1st 'C' sent */
|
|
case 2: /* 2nd 'C' sent */
|
|
case 3: /* 3rd 'C' sent */
|
|
writeChar( 'C' );
|
|
xstate++;
|
|
timerStart( 1000 ); /* Should be 3000 in original XModem */
|
|
break;
|
|
|
|
case 4: /* 4th 'C' sent */
|
|
xcrc = false;
|
|
|
|
case 5: /* 1st <NAK> sent */
|
|
case 6: /* 2nd <NAK> sent */
|
|
case 7: /* 3rd <NAK> sent */
|
|
case 8: /* 4th <NAK> sent */
|
|
case 9: /* 5th <NAK> sent */
|
|
writeChar( CNAK );
|
|
xstate++;
|
|
timerStart( 1000 ); /* Should be 10000 in original XModem */
|
|
break;
|
|
|
|
case 10: /* 6th <NAK> sent */
|
|
xreset();
|
|
emit xmodemDone( false );
|
|
break;
|
|
|
|
default: /* pending XModem block */
|
|
writeChar( CNAK );
|
|
xstate = 5;
|
|
timerStart( 1000 ); /* Should be 10000 in original XModem */
|
|
}
|
|
}
|
|
|
|
|
|
void Modem::readChar( int )
|
|
{
|
|
uchar c;
|
|
|
|
|
|
while ( read( fd, (void *) &c, 1 ) == 1 ) {
|
|
if ( c == '\n' ) {
|
|
buffer[ bufpos ] = 0;
|
|
bufpos = 0;
|
|
emit gotLine( (const char *) buffer );
|
|
break;
|
|
} else
|
|
if ( ( bufpos < 1000 ) && ( c != '\r' ) )
|
|
buffer[ bufpos++ ] = c;
|
|
}
|
|
}
|
|
|
|
|
|
void Modem::readXChar( int )
|
|
{
|
|
uchar c;
|
|
static uchar crc_hi, block, cblock;
|
|
|
|
|
|
while ( read( fd, (void *) &c, 1 ) == 1 ) {
|
|
switch ( xstate ) {
|
|
case 1: /* 1st 'C' sent */
|
|
case 2: /* 2nd 'C' sent */
|
|
case 3: /* 3rd 'C' sent */
|
|
case 4: /* 4th 'C' sent */
|
|
case 5: /* 1st <NAK> sent */
|
|
case 6: /* 2nd <NAK> sent */
|
|
case 7: /* 3rd <NAK> sent */
|
|
case 8: /* 4th <NAK> sent */
|
|
case 9: /* 5th <NAK> sent */
|
|
case 10: /* 6th <NAK> sent */
|
|
if ( c == CSOH ) {
|
|
timerStart( 1000 );
|
|
xsize = 128;
|
|
xstate = 11;
|
|
} else
|
|
if ( c == CSTX ) {
|
|
timerStart( 1000 );
|
|
xsize = 1024;
|
|
xstate = 11;
|
|
} else
|
|
if ( c == CEOT ) {
|
|
timer->stop();
|
|
writeChar( CACK );
|
|
xreset();
|
|
emit xmodemDone( true );
|
|
} else
|
|
timerStart( 1000 );
|
|
break;
|
|
|
|
case 11: /* <SOH> or <STX> received */
|
|
timerStart( 1000 );
|
|
block = c;
|
|
xstate++;
|
|
break;
|
|
|
|
case 12: /* block number received */
|
|
timerStart( 1000 );
|
|
cblock = c;
|
|
xstate++;
|
|
bufpos = 0;
|
|
break;
|
|
|
|
case 13: /* complement block number received */
|
|
timerStart( 1000 );
|
|
buffer[ bufpos++ ] = c;
|
|
if ( bufpos == xsize ) {
|
|
bufpos = 0;
|
|
xstate++;
|
|
if ( !xcrc )
|
|
xstate++;
|
|
}
|
|
break;
|
|
|
|
case 14: /* data block received */
|
|
timerStart( 1000 );
|
|
crc_hi = c;
|
|
xstate++;
|
|
break;
|
|
|
|
case 15: /* crc high-byte received */
|
|
timerStart( 10000 );
|
|
xstate = 4;
|
|
if ( (uchar) ( block ^ cblock ) != 0xff ) {
|
|
writeChar( CNAK );
|
|
break;
|
|
}
|
|
if ( block+1 == xblock ) {
|
|
writeChar( CACK );
|
|
break;
|
|
}
|
|
if ( block != xblock ) {
|
|
timer->stop();
|
|
writeChar( CCAN );
|
|
xreset();
|
|
emit xmodemDone( false );
|
|
break;
|
|
}
|
|
if ( xcrc ) {
|
|
if ( ( (ushort) crc_hi << 8 | (ushort) c ) != calcCRC() ) {
|
|
writeChar( CNAK );
|
|
break;
|
|
}
|
|
} else {
|
|
if ( c != calcChecksum() ) {
|
|
writeChar( CNAK );
|
|
break;
|
|
}
|
|
}
|
|
writeChar( CACK );
|
|
xblock++;
|
|
emit gotXBlock( buffer, xsize );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Modem::init()
|
|
{
|
|
is_locked = false;
|
|
|
|
fd = -1;
|
|
sn = 0;
|
|
|
|
cspeed = B38400;
|
|
|
|
// No flow control
|
|
cflag = CS8 | CREAD | CLOCAL;
|
|
// cflag = CS8 | CREAD | CLOCAL | CRTSCTS;
|
|
|
|
bufpos = 0;
|
|
}
|
|
|
|
|
|
void Modem::xreset()
|
|
{
|
|
bufpos = 0;
|
|
|
|
xstate = 0;
|
|
xcrc = false;
|
|
xblock = 0;
|
|
xsize = 0;
|
|
|
|
if ( sn ) {
|
|
disconnect( sn, 0, this, 0 );
|
|
connect( sn, TQT_SIGNAL( activated( int ) ), TQT_SLOT( readChar( int ) ) );
|
|
}
|
|
}
|
|
|
|
|
|
uchar Modem::calcChecksum()
|
|
{
|
|
int i;
|
|
uchar c = 0;
|
|
|
|
|
|
for ( i = 0; i < xsize; i++ )
|
|
c += buffer[ i ];
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
ushort Modem::calcCRC()
|
|
{
|
|
int i, j;
|
|
ushort c = 0;
|
|
|
|
|
|
for ( i = 0; i < xsize; i++ ) {
|
|
c ^= (ushort) buffer[ i ] << 8;
|
|
for ( j = 0; j < 8; j++ )
|
|
if ( c & 0x8000 )
|
|
c = c << 1 ^ 0x1021;
|
|
else
|
|
c <<= 1;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
#include "modem.moc"
|