wvmodem.cc

00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  *   Copyright (C) 1999 Red Hat, Inc.
00005  *
00006  * Implementation of the WvModem class. Inherits from WvFile, but
00007  * handles various important details related to modems, like setting
00008  * the baud rate, checking carrier detect, and dropping DTR.
00009  *
00010  */
00011 
00012 #include "wvmodem.h"
00013 #include <sys/ioctl.h>
00014 
00015 #if HAVE_LINUX_SERIAL_H
00016 # include <linux/serial.h>
00017 #endif
00018 
00019 #if ! HAVE_CFMAKERAW
00020 static inline void cfmakeraw(struct termios *termios_p)
00021 {
00022     termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
00023     termios_p->c_oflag &= ~OPOST;
00024     termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
00025     termios_p->c_cflag &= ~(CSIZE|PARENB);
00026     termios_p->c_cflag |= CS8;
00027 }
00028 #endif
00029 
00030 struct SpeedLookup {
00031     int baud;
00032     speed_t speedt;
00033 };
00034 
00035 
00036 static SpeedLookup speeds[] = {
00037 #ifdef B460800
00038     {460800, B460800},
00039 #endif
00040 #ifdef B230400
00041     {230400, B230400},
00042 #endif
00043     {115200, B115200},
00044     { 57600, B57600},
00045     { 38400, B38400},
00046     { 19200, B19200},
00047     {  9600, B9600},
00048     {  4800, B4800},
00049     {  2400, B2400},
00050     {  1200, B1200},
00051     {   300, B300}
00052 };
00053 
00054 
00055 WvModemBase::WvModemBase(int _fd) : WvFile(_fd)
00056 {
00057     get_real_speed();
00058 }
00059 
00060 
00061 WvModemBase::~WvModemBase()
00062 {
00063     // nothing needed
00064 }
00065 
00066 
00067 int WvModemBase::get_real_speed()
00068 {
00069     speed_t s;
00070     
00071     if (!isok()) return 0;
00072 
00073     tcgetattr( getrfd(), &t );
00074     s = cfgetospeed( &t );
00075     for (unsigned int i = 0; i < sizeof(speeds) / sizeof(*speeds); i++)
00076     {
00077         if (speeds[i].speedt == s)
00078         {
00079             baud = speeds[i].baud;
00080             break;
00081         }
00082     }
00083 
00084     return baud;
00085 }
00086 
00087 
00088 void WvModemBase::close()
00089 {
00090     // no file open, no need to close it
00091 }
00092 
00093 
00094 bool WvModemBase::carrier()
00095 {
00096     return true;
00097 }
00098 
00099 
00100 int WvModemBase::speed(int)
00101 {
00102     return baud;
00103 }
00104 
00105 
00106 void WvModemBase::hangup()
00107 {
00108     int i, oldbaud = baud;
00109     
00110     if (die_fast || !isok()) return;
00111 
00112     // politely abort any dial in progress, to avoid locking USR modems.
00113     // we should only do this if we have received any response from the modem,
00114     // so that WvModemScan can run faster.
00115     drain();
00116     write( "\r", 1 );
00117     // FIXME: should be iswritable, but based on the numer of msec params
00118     // tossed around I assume modems are very timing-sensitive
00119     for (i = 0; !select(200, false, true) && i < 10; i++)
00120         write( "\r", 1 );
00121     drain();
00122 
00123     // drop DTR for a while, if still online
00124     if (carrier())
00125     {
00126         cfsetospeed( &t, B0 );
00127         tcsetattr( getrfd(), TCSANOW, &t );
00128         for (i = 0; carrier() && i < 10; i++)
00129             usleep( 100 * 1000 );
00130 
00131         // raise DTR again, restoring the old baud rate
00132         speed(oldbaud);
00133     }
00134     
00135     if (carrier())
00136     {
00137         // need to do +++ manual-disconnect stuff
00138         write( "+++", 3 );
00139         usleep( 1500 * 1000 );
00140         write( "ATH\r", 4 );
00141         
00142         for (i = 0; carrier() && i < 5; i++)
00143             usleep( 100 * 1000 );
00144     }
00145 }
00146 
00147 
00148 
00149 WvModem::WvModem(WvStringParm filename, int _baud, bool rtscts, bool _no_reset)
00150     : WvModemBase(), lock(filename), log("WvModem", WvLog::Debug1)
00151 {
00152     closing = false;
00153     baud = _baud;
00154     die_fast = false;
00155     no_reset = _no_reset;
00156     
00157     if (!lock.lock())
00158     {
00159         seterr(EBUSY);
00160         return;
00161     }
00162     
00163     // note: if CLOCAL is not set on the modem, open will
00164     // block until a carrier detect.  Since we have to open the modem to
00165     // generate a carrier detect, we have a problem.  So we open the modem
00166     // nonblocking.  It would then be safe to switch to blocking mode,
00167     // but that is no longer recommended for WvStream.
00168     open(filename, O_RDWR|O_NONBLOCK|O_NOCTTY);
00169     
00170     if (isok())
00171         setup_modem(rtscts);
00172 }
00173 
00174 
00175 WvModem::~WvModem()
00176 {
00177     close();
00178 }
00179 
00180 
00181 void WvModem::setup_modem(bool rtscts)
00182 {
00183     if (!isok()) return;
00184 
00185     if (tcgetattr(getrfd(), &t) || tcgetattr(getrfd(), &old_t))
00186     {
00187         closing = true;
00188         seterr(errno);
00189         return;
00190     }
00191     
00192     drain();
00193     
00194 #if HAVE_LINUX_SERIAL_H
00195     struct serial_struct old_sinfo, sinfo;
00196     sinfo.reserved_char[0] = 0;
00197     if (ioctl(getrfd(), TIOCGSERIAL, &old_sinfo) < 0)
00198         log("Cannot get information for serial port.");
00199     else
00200     {
00201         sinfo = old_sinfo;
00202         // Why there are two closing wait timeouts, is beyond me
00203         // but there are... apparently the second one is deprecated
00204         // but why take a chance...
00205         sinfo.closing_wait = ASYNC_CLOSING_WAIT_NONE;
00206         sinfo.closing_wait2 = ASYNC_CLOSING_WAIT_NONE;
00207 
00208         if (ioctl(getrfd(), TIOCSSERIAL, &sinfo) < 0)
00209             log("Cannot set information for serial port.");
00210     }
00211 #endif
00212 
00213     // set up the terminal characteristics.
00214     // see "man tcsetattr" for more information about these options.
00215     t.c_iflag &= ~(BRKINT | ISTRIP | IUCLC | IXON | IXANY | IXOFF | IMAXBEL);
00216     t.c_iflag |= (IGNBRK | IGNPAR);
00217     t.c_oflag &= ~(OLCUC);
00218     t.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD);
00219     t.c_cflag |= (CS8 | CREAD | HUPCL | CLOCAL);
00220     if(rtscts)
00221         t.c_cflag |= CRTSCTS;
00222     t.c_lflag &= ~(ISIG | XCASE | ECHO);
00223     tcsetattr(getrfd(), TCSANOW, &t);
00224     
00225     // make sure we leave the modem in CLOCAL when we exit, so normal user
00226     // tasks can open the modem without using nonblocking.
00227     old_t.c_cflag |= CLOCAL;
00228     
00229     // Send a few returns to make sure the modem is "good and zonked".
00230     if (cfgetospeed(&t) != B0 && !no_reset)
00231     {
00232         for(int i=0; i<5; i++) 
00233         {
00234             write("\r", 1);
00235             usleep(10 * 1000);
00236         }
00237     }
00238     
00239     // Set the baud rate to 0 for half a second to drop DTR...
00240     cfsetispeed(&t, B0);
00241     cfsetospeed(&t, B0);
00242     cfmakeraw(&t);
00243     tcsetattr(getrfd(), TCSANOW, &t);
00244     if (carrier())
00245         usleep(500 * 1000);
00246     
00247     speed(baud);
00248     usleep(10 * 1000);
00249     
00250     drain();
00251 }
00252 
00253 
00254 void WvModem::close()
00255 {
00256     if (!closed)
00257     {
00258         if (!closing)
00259         {
00260             closing = true;
00261             if (!no_reset)
00262                 hangup();
00263             else
00264             {
00265                 drain();
00266                 cfsetospeed(&t, B0);
00267                 // If this works??
00268                 write("\r");
00269             }
00270         }
00271     
00272         closing = true;
00273         if (getrfd() >= 0)
00274         {
00275             tcflush(getrfd(), TCIOFLUSH);
00276             tcsetattr(getrfd(), TCSANOW, &old_t);
00277         }
00278         WvFile::close();
00279         closing = false;
00280     }
00281 }
00282 
00283 
00284 int WvModem::speed(int _baud)
00285 {
00286     speed_t s = B0;
00287     baud = 0;
00288     for (unsigned int i = 0; i < sizeof(speeds) / sizeof(*speeds); i++)
00289     {
00290         if (speeds[i].baud <= _baud)
00291         {
00292             s = speeds[i].speedt;
00293             break;
00294         }
00295     }
00296 
00297     cfsetispeed(&t, B0); // auto-match to output speed
00298     cfsetospeed(&t, s);
00299     tcsetattr(getrfd(), TCSANOW, &t);
00300 
00301     return get_real_speed();
00302 }
00303 
00304 
00305 int WvModem::getstatus()
00306 {
00307     if (!isok()) return 0;
00308     int status = 0;
00309     ioctl(getrfd(), TIOCMGET, &status);
00310     return status;
00311 }
00312 
00313 
00314 bool WvModem::carrier()
00315 {
00316     return (getstatus() & TIOCM_CD) ? 1 : 0;
00317 }

Generated on Sun Sep 24 20:10:51 2006 for WvStreams by  doxygen 1.4.7