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() << "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"