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() << "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.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:37:59 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003