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 "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:                        /* non-XModem mode      */
00479                         emit timeout();
00480                         break;
00481 
00482                 case  1:                        /* 1st 'C' sent         */
00483                 case  2:                        /* 2nd 'C' sent         */
00484                 case  3:                        /* 3rd 'C' sent         */
00485                         writeChar('C');
00486                         xstate++;
00487                         timerStart(1000);       /* Should be 3000 in original XModem    */
00488                         break;
00489 
00490                 case  4:                        /* 4th 'C' sent         */
00491                         xcrc = false;
00492 
00493                 case  5:                        /* 1st <NAK> sent       */
00494                 case  6:                        /* 2nd <NAK> sent       */
00495                 case  7:                        /* 3rd <NAK> sent       */
00496                 case  8:                        /* 4th <NAK> sent       */
00497                 case  9:                        /* 5th <NAK> sent       */
00498                         writeChar(CNAK);
00499                         xstate++;
00500                         timerStart(1000);       /* Should be 10000 in original XModem   */
00501                         break;
00502 
00503                 case 10:                        /* 6th <NAK> sent       */
00504                         xreset();
00505                         emit xmodemDone(false);
00506                         break;
00507 
00508                 default:                        /* pending XModem block */
00509                         writeChar(CNAK);
00510                         xstate = 5;
00511                         timerStart(1000);       /* Should be 10000 in original XModem   */
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:        /* 1st 'C' sent         */
00540                         case  2:        /* 2nd 'C' sent         */
00541                         case  3:        /* 3rd 'C' sent         */
00542                         case  4:        /* 4th 'C' sent         */
00543                         case  5:        /* 1st <NAK> sent       */
00544                         case  6:        /* 2nd <NAK> sent       */
00545                         case  7:        /* 3rd <NAK> sent       */
00546                         case  8:        /* 4th <NAK> sent       */
00547                         case  9:        /* 5th <NAK> sent       */
00548                         case 10:        /* 6th <NAK> sent       */
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:        /* <SOH> or <STX> received       */
00567                                 timerStart(1000);
00568                                 block = c;
00569                                 xstate++;
00570                                 break;
00571 
00572                         case 12:        /* block number received        */
00573                                 timerStart(1000);
00574                                 cblock = c;
00575                                 xstate++;
00576                                 bufpos = 0;
00577                                 break;
00578 
00579                         case 13:        /* complement block number received     */
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:        /* data block received  */
00591                                 timerStart(1000);
00592                                 crc_hi = c;
00593                                 xstate++;
00594                                 break;
00595 
00596                         case 15:        /* crc high-byte received       */
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 // No flow control
00647         cflag = CS8 | CREAD | CLOCAL;
00648 //      cflag = CS8 | CREAD | CLOCAL | CRTSCTS;
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"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sat Oct 18 02:47:06 2003 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001