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
#define LOCK_PATH "/var/lock"
00052
00053
00054
#ifndef CSOH
00055
#define CSOH 01
00056
#endif
00057
00058
#ifndef CSTX
00059
#define CSTX 02
00060
#endif
00061
00062
#ifndef CEOT
00063
#define CEOT 04
00064
#endif
00065
00066
#ifndef CACK
00067
#define CACK 06
00068
#endif
00069
00070
#ifndef CNAK
00071
#define CNAK 025
00072
#endif
00073
00074
#ifndef CCAN
00075
#define CCAN 030
00076
#endif
00077
00078
00079
00080 Modem::Modem(
QObject *parent,
const char *name) :
QObject(parent, name)
00081 {
00082 mOpen =
false;
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::setDevice(
const QString& name)
00100 {
00101
if (fdev)
00102 free(fdev);
00103
00104 fdev = strdup(name.latin1());
00105 }
00106
00107
00108
void Modem::setSpeed(
int speed)
00109 {
00110
switch (speed) {
00111
case 300:
00112 cspeed = B300;
00113
break;
00114
case 600:
00115 cspeed = B600;
00116
break;
00117
case 1200:
00118 cspeed = B1200;
00119
break;
00120
case 2400:
00121 cspeed = B2400;
00122
break;
00123
case 4800:
00124 cspeed = B4800;
00125
break;
00126
case 9600:
00127 cspeed = B9600;
00128
break;
00129
case 19200:
00130 cspeed = B19200;
00131
break;
00132
case 38400:
00133 cspeed = B38400;
00134
break;
00135
#ifdef B57600
00136
case 57600:
00137 cspeed = B57600;
00138
break;
00139
#endif
00140
#ifdef B115200
00141
case 115200:
00142 cspeed = B115200;
00143
break;
00144
#endif
00145
#ifdef B230400
00146
case 230400:
00147 cspeed = B230400;
00148
break;
00149
#endif
00150
default:
00151
#ifdef MODEM_DEBUG
00152
fprintf(stderr,
"Modem: setSpeed(): fallback to default speed.\n");
00153
#endif
00154
cspeed = B38400;
00155 }
00156 }
00157
00158
00159
void Modem::setData(
int data)
00160 {
00161 cflag &= ~CSIZE;
00162
00163
switch (data) {
00164
case 5:
00165 cflag |= CS5;
00166
break;
00167
case 6:
00168 cflag |= CS6;
00169
break;
00170
case 7:
00171 cflag |= CS7;
00172
break;
00173
default:
00174 cflag |= CS8;
00175 }
00176 }
00177
00178
00179
void Modem::setParity(
char parity)
00180 {
00181 cflag &= ~(PARENB | PARODD);
00182
00183
if (parity ==
'E')
00184 cflag |= PARENB;
00185
else if (parity ==
'O')
00186 cflag |= PARENB | PARODD;
00187 }
00188
00189
00190
void Modem::setStop(
int stop)
00191 {
00192
if (stop == 2)
00193 cflag |= CSTOPB;
00194
else
00195 cflag &= ~CSTOPB;
00196 }
00197
00198
00199
bool Modem::open()
00200 {
00201
struct termios tty;
00202
00203 close();
00204
00205
if (!lockDevice()) {
00206
return false;
00207 }
00208
00209
if ((fd = ::open(fdev, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1) {
00210 emit errorMessage( i18n(
"Unable to open device '%1'. "
00211
"Please check that you have sufficient permissions.")
00212 .arg( fdev ) );
00213
return false;
00214 }
00215 tcflush(fd, TCIOFLUSH);
00216
if (tcgetattr(fd, &init_tty) == -1) {
00217 emit errorMessage( i18n(
"tcgetattr() failed.") );
00218 ::close(fd);
00219 fd = 0;
00220
return false;
00221 }
00222 memset(&tty, 0,
sizeof(tty));
00223 tty.c_iflag = IGNBRK | IGNPAR;
00224 tty.c_oflag = 0;
00225 tty.c_cflag = cflag;
00226 tty.c_lflag = 0;
00227 cfsetospeed(&tty, cspeed);
00228 cfsetispeed(&tty, cspeed);
00229 tcdrain(fd);
00230
if (tcsetattr(fd, TCSANOW, &tty) == -1) {
00231 emit errorMessage( i18n(
"tcsetattr() failed.") );
00232 ::close(fd);
00233 fd = 0;
00234
return false;
00235 }
00236
00237 sn =
new QSocketNotifier(fd, QSocketNotifier::Read,
this,
"modemsocketnotifier");
00238 Q_CHECK_PTR(sn);
00239 connect(sn, SIGNAL(activated(
int)), SLOT(readChar(
int)));
00240
00241 mOpen =
true;
00242
00243
return true;
00244 }
00245
00246
00247
void Modem::close()
00248 {
00249 timer->stop();
00250
00251
delete sn;
00252 sn = 0;
00253
00254
if (fd) {
00255 tcflush(fd, TCIOFLUSH);
00256 tcsetattr(fd, TCSANOW, &init_tty);
00257 ::close(fd);
00258 fd = 0;
00259 }
00260
00261 xreset();
00262
00263 unlockDevice();
00264
00265 mOpen =
false;
00266 }
00267
00268
00269
void Modem::flush()
00270 {
00271
if (fd) {
00272 tcflush(fd, TCIOFLUSH);
00273 bufpos = 0;
00274 }
00275 }
00276
00277
00278
bool Modem::lockDevice()
00279 {
00280
char *p;
00281
char fname[1024];
00282
char content[256];
00283 ssize_t count;
00284 pid_t pid;
00285
int lfd;
00286
struct passwd *pw;
00287
00288
if (is_locked)
00289
return true;
00290
00291
if ((p = strrchr(fdev,
'/')))
00292 p++;
00293
else
00294 p = fdev;
00295
00296 sprintf(fname,
"%s/LCK..%s", LOCK_PATH, p);
00297
if (!access(fname, F_OK)) {
00298
if ((lfd = ::open(fname, O_RDONLY)) < 0) {
00299 emit errorMessage( i18n(
"Unable to open lock file '%1'.")
00300 .arg( fname ) );
00301
return false;
00302 }
00303
00304 count = read(lfd, content, 79);
00305
if (count < 0) {
00306 emit errorMessage( i18n(
"Unable to read lock file '%1'.")
00307 .arg( fname ) );
00308 ::close(lfd);
00309
return false;
00310 }
00311 content[count] = 0;
00312 ::close(lfd);
00313
00314 count = sscanf(content,
"%d", &pid);
00315
if ((count != 1) || (pid <= 0)) {
00316 emit errorMessage( i18n(
"Unable to get PID from file '%1'.")
00317 .arg( fname ) );
00318
return false;
00319 }
00320
00321
if (!kill((pid_t)pid, 0)) {
00322 emit errorMessage( i18n(
"Process with PID %1, which is locking the device, is still running.")
00323 .arg( pid ) );
00324
return false;
00325 }
00326
00327
if (errno != ESRCH) {
00328 emit errorMessage( i18n(
"Unable to emit signal to PID of existing lock file.") );
00329
return false;
00330 }
00331 }
00332
00333
if ((lfd = creat(fname, 0644)) == -1) {
00334 emit errorMessage( i18n(
"Unable to create lock file '%1'. "
00335
"Please check that you have sufficient permissions.")
00336 .arg( fname ) );
00337
return false;
00338 }
00339
00340 pid = (
int)getpid();
00341 pw = getpwuid(getuid());
00342 sprintf(content,
"%08d %s %s", pid,
"kmlofax", pw->pw_name);
00343 write(lfd, content, strlen(content));
00344 ::close(lfd);
00345
00346 is_locked =
true;
00347
00348
return true;
00349 }
00350
00351
00352
void Modem::unlockDevice()
00353 {
00354
char *p;
00355
char fname[1024];
00356
00357
if (is_locked) {
00358
if ((p = strrchr(fdev,
'/')))
00359 p++;
00360
else
00361 p = fdev;
00362
00363 sprintf(fname,
"%s/LCK..%s", LOCK_PATH, p);
00364 unlink(fname);
00365 is_locked =
false;
00366 }
00367 }
00368
00369
00370
bool Modem::dsrOn()
00371 {
00372
int flags;
00373
00374
if (!fd) {
00375
#ifdef MODEM_DEBUG
00376
fprintf(stderr,
"Modem: dsrOn(): File not open.\n");
00377
#endif
00378
return false;
00379 }
00380
00381
if (ioctl(fd, TIOCMGET, &flags) == -1) {
00382
#ifdef MODEM_DEBUG
00383
fprintf(stderr,
"Modem: dsrOn(): ioctl() failed.\n");
00384
#endif
00385
return false;
00386 }
00387
00388
return (flags & TIOCM_DSR) != 0;
00389 }
00390
00391
00392
bool Modem::ctsOn()
00393 {
00394
int flags;
00395
00396
if (!fd) {
00397
#ifdef MODEM_DEBUG
00398
fprintf(stderr,
"Modem: ctsOn(): File not open.\n");
00399
#endif
00400
return false;
00401 }
00402
00403
if (ioctl(fd, TIOCMGET, &flags) == -1) {
00404
#ifdef MODEM_DEBUG
00405
fprintf(stderr,
"Modem: ctsOn(): ioctl() failed.\n");
00406
#endif
00407
return false;
00408 }
00409
00410
return (flags & TIOCM_CTS) != 0;
00411 }
00412
00413
00414
void Modem::writeChar(
const char c)
00415 {
00416 write(fd, (
const void *)&c, 1);
00417 }
00418
00419
00420
void Modem::writeLine(
const char *line)
00421 {
00422 kdDebug(5960) <<
"Modem::writeLine(): " << line << endl;
00423
00424 write(fd, (
const void *)line, strlen(line));
00425 writeChar(
'\r');
00426 }
00427
00428
00429
void Modem::timerStart(
int msec)
00430 {
00431 timer->start(msec,
true);
00432 }
00433
00434
00435
void Modem::receiveXModem(
bool crc)
00436 {
00437 disconnect(sn, 0,
this, 0);
00438 connect(sn, SIGNAL(activated(
int)), SLOT(readXChar(
int)));
00439
00440 xcrc = crc;
00441
00442
if (xcrc) {
00443 writeChar(
'C');
00444 xstate = 1;
00445 timerStart(3000);
00446 }
else {
00447 writeChar(CNAK);
00448 xstate = 5;
00449 timerStart(10000);
00450 }
00451 xblock = 1;
00452 }
00453
00454
00455
void Modem::abortXModem()
00456 {
00457 timer->stop();
00458 writeChar(CCAN);
00459 xreset();
00460 emit xmodemDone(
false);
00461 }
00462
00463
00464
void Modem::timerDone()
00465 {
00466
#ifdef MODEM_DEBUG
00467
fprintf(stderr,
"Modem: timeout, xstate = %d.\n", xstate);
00468
#endif
00469
switch (xstate) {
00470
case 0:
00471 emit timeout();
00472
break;
00473
00474
case 1:
00475
case 2:
00476
case 3:
00477 writeChar(
'C');
00478 xstate++;
00479 timerStart(1000);
00480
break;
00481
00482
case 4:
00483 xcrc =
false;
00484
00485
case 5:
00486
case 6:
00487
case 7:
00488
case 8:
00489
case 9:
00490 writeChar(CNAK);
00491 xstate++;
00492 timerStart(1000);
00493
break;
00494
00495
case 10:
00496 xreset();
00497 emit xmodemDone(
false);
00498
break;
00499
00500
default:
00501 writeChar(CNAK);
00502 xstate = 5;
00503 timerStart(1000);
00504 }
00505 }
00506
00507
00508
void Modem::readChar(
int)
00509 {
00510 uchar c;
00511
00512
while (read(fd, (
void *)&c, 1) == 1) {
00513
if (c ==
'\n') {
00514 buffer[bufpos] = 0;
00515 bufpos = 0;
00516 emit gotLine((
const char *)buffer);
00517
break;
00518 }
else if ((bufpos < 1000) && (c !=
'\r'))
00519 buffer[bufpos++] = c;
00520 }
00521 }
00522
00523
00524
void Modem::readXChar(
int)
00525 {
00526 uchar c;
00527
static uchar crc_hi, block, cblock;
00528
00529
while (read(fd, (
void *)&c, 1) == 1) {
00530
switch (xstate) {
00531
case 1:
00532
case 2:
00533
case 3:
00534
case 4:
00535
case 5:
00536
case 6:
00537
case 7:
00538
case 8:
00539
case 9:
00540
case 10:
00541
if (c == CSOH) {
00542 timerStart(1000);
00543 xsize = 128;
00544 xstate = 11;
00545 }
else if (c == CSTX) {
00546 timerStart(1000);
00547 xsize = 1024;
00548 xstate = 11;
00549 }
else if (c == CEOT) {
00550 timer->stop();
00551 writeChar(CACK);
00552 xreset();
00553 emit xmodemDone(
true);
00554 }
else
00555 timerStart(1000);
00556
break;
00557
00558
case 11:
00559 timerStart(1000);
00560 block = c;
00561 xstate++;
00562
break;
00563
00564
case 12:
00565 timerStart(1000);
00566 cblock = c;
00567 xstate++;
00568 bufpos = 0;
00569
break;
00570
00571
case 13:
00572 timerStart(1000);
00573 buffer[bufpos++] = c;
00574
if (bufpos == xsize) {
00575 bufpos = 0;
00576 xstate++;
00577
if (!xcrc)
00578 xstate++;
00579 }
00580
break;
00581
00582
case 14:
00583 timerStart(1000);
00584 crc_hi = c;
00585 xstate++;
00586
break;
00587
00588
case 15:
00589 timerStart(10000);
00590 xstate = 4;
00591
if ((uchar)(block ^ cblock) != 0xff) {
00592 writeChar(CNAK);
00593
break;
00594 }
00595
if (block+1 == xblock) {
00596 writeChar(CACK);
00597
break;
00598 }
00599
if (block != xblock) {
00600 timer->stop();
00601 writeChar(CCAN);
00602 xreset();
00603 emit xmodemDone(
false);
00604
break;
00605 }
00606
if (xcrc) {
00607
if (((ushort)crc_hi << 8 | (ushort)c) != calcCRC()) {
00608 writeChar(CNAK);
00609
break;
00610 }
00611 }
else {
00612
if (c != calcChecksum()) {
00613 writeChar(CNAK);
00614
break;
00615 }
00616 }
00617 writeChar(CACK);
00618 xblock++;
00619 emit gotXBlock(buffer, xsize);
00620
break;
00621
00622
default:
00623
break;
00624 }
00625 }
00626 }
00627
00628
00629
void Modem::init()
00630 {
00631 is_locked =
false;
00632
00633 fdev = 0;
00634 fd = 0;
00635 sn = 0;
00636
00637 cspeed = B38400;
00638
00639 cflag = CS8 | CREAD | CLOCAL;
00640
00641
00642 bufpos = 0;
00643 }
00644
00645
00646
void Modem::xreset()
00647 {
00648 bufpos = 0;
00649
00650 xstate = 0;
00651 xcrc =
false;
00652 xblock = 0;
00653 xsize = 0;
00654
00655
if (sn) {
00656 disconnect(sn, 0,
this, 0);
00657 connect(sn, SIGNAL(activated(
int)), SLOT(readChar(
int)));
00658 }
00659 }
00660
00661
00662 uchar Modem::calcChecksum()
00663 {
00664
int i;
00665 uchar c = 0;
00666
00667
for (i=0; i < xsize; i++)
00668 c += buffer[i];
00669
00670
return c;
00671 }
00672
00673
00674 ushort Modem::calcCRC()
00675 {
00676
int i, j;
00677 ushort c = 0;
00678
00679
for (i=0; i < xsize; i++) {
00680 c ^= (ushort)buffer[i] << 8;
00681
for (j=0; j < 8; j++)
00682
if (c & 0x8000)
00683 c = c << 1 ^ 0x1021;
00684
else
00685 c <<= 1;
00686 }
00687
00688
return c;
00689 }
00690
#include "modem.moc"