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