kandy Library API Documentation

modem.cpp

00001 /* 00002 KMLOCfg 00003 00004 A utility to configure the ELSA MicroLink(tm) Office modem. 00005 00006 Copyright (C) 2000 Oliver Gantz <Oliver.Gantz@epost.de> 00007 00008 This program is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 2 of the License, or 00011 (at your option) any later version. 00012 00013 This program is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with this program; if not, write to the Free Software 00020 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00021 00022 ------ 00023 ELSA and MicroLink are trademarks of ELSA AG, Aachen. 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: /* non-XModem mode */ 00471 emit timeout(); 00472 break; 00473 00474 case 1: /* 1st 'C' sent */ 00475 case 2: /* 2nd 'C' sent */ 00476 case 3: /* 3rd 'C' sent */ 00477 writeChar('C'); 00478 xstate++; 00479 timerStart(1000); /* Should be 3000 in original XModem */ 00480 break; 00481 00482 case 4: /* 4th 'C' sent */ 00483 xcrc = false; 00484 00485 case 5: /* 1st <NAK> sent */ 00486 case 6: /* 2nd <NAK> sent */ 00487 case 7: /* 3rd <NAK> sent */ 00488 case 8: /* 4th <NAK> sent */ 00489 case 9: /* 5th <NAK> sent */ 00490 writeChar(CNAK); 00491 xstate++; 00492 timerStart(1000); /* Should be 10000 in original XModem */ 00493 break; 00494 00495 case 10: /* 6th <NAK> sent */ 00496 xreset(); 00497 emit xmodemDone(false); 00498 break; 00499 00500 default: /* pending XModem block */ 00501 writeChar(CNAK); 00502 xstate = 5; 00503 timerStart(1000); /* Should be 10000 in original XModem */ 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: /* 1st 'C' sent */ 00532 case 2: /* 2nd 'C' sent */ 00533 case 3: /* 3rd 'C' sent */ 00534 case 4: /* 4th 'C' sent */ 00535 case 5: /* 1st <NAK> sent */ 00536 case 6: /* 2nd <NAK> sent */ 00537 case 7: /* 3rd <NAK> sent */ 00538 case 8: /* 4th <NAK> sent */ 00539 case 9: /* 5th <NAK> sent */ 00540 case 10: /* 6th <NAK> sent */ 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: /* <SOH> or <STX> received */ 00559 timerStart(1000); 00560 block = c; 00561 xstate++; 00562 break; 00563 00564 case 12: /* block number received */ 00565 timerStart(1000); 00566 cblock = c; 00567 xstate++; 00568 bufpos = 0; 00569 break; 00570 00571 case 13: /* complement block number received */ 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: /* data block received */ 00583 timerStart(1000); 00584 crc_hi = c; 00585 xstate++; 00586 break; 00587 00588 case 15: /* crc high-byte received */ 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 // No flow control 00639 cflag = CS8 | CREAD | CLOCAL; 00640 // cflag = CS8 | CREAD | CLOCAL | CRTSCTS; 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"
KDE Logo
This file is part of the documentation for kandy Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Oct 21 19:46:34 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003