WvStreams
|
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 have_old_t = false; 00157 00158 if (!lock.lock()) 00159 { 00160 seterr(EBUSY); 00161 return; 00162 } 00163 00164 // note: if CLOCAL is not set on the modem, open will 00165 // block until a carrier detect. Since we have to open the modem to 00166 // generate a carrier detect, we have a problem. So we open the modem 00167 // nonblocking. It would then be safe to switch to blocking mode, 00168 // but that is no longer recommended for WvStream. 00169 open(filename, O_RDWR|O_NONBLOCK|O_NOCTTY); 00170 00171 if (isok()) 00172 setup_modem(rtscts); 00173 } 00174 00175 00176 WvModem::~WvModem() 00177 { 00178 close(); 00179 } 00180 00181 00182 void WvModem::setup_modem(bool rtscts) 00183 { 00184 if (!isok()) return; 00185 00186 if (tcgetattr(getrfd(), &t) || tcgetattr(getrfd(), &old_t)) 00187 { 00188 closing = true; 00189 seterr(errno); 00190 return; 00191 } 00192 have_old_t = true; 00193 00194 drain(); 00195 00196 #if 0 00197 struct serial_struct old_sinfo, sinfo; 00198 sinfo.reserved_char[0] = 0; 00199 if (ioctl(getrfd(), TIOCGSERIAL, &old_sinfo) < 0) 00200 log("Cannot get information for serial port."); 00201 else 00202 { 00203 sinfo = old_sinfo; 00204 // Why there are two closing wait timeouts, is beyond me 00205 // but there are... apparently the second one is deprecated 00206 // but why take a chance... 00207 sinfo.closing_wait = ASYNC_CLOSING_WAIT_NONE; 00208 sinfo.closing_wait2 = ASYNC_CLOSING_WAIT_NONE; 00209 00210 if (ioctl(getrfd(), TIOCSSERIAL, &sinfo) < 0) 00211 log("Cannot set information for serial port."); 00212 } 00213 #endif 00214 00215 // set up the terminal characteristics. 00216 // see "man tcsetattr" for more information about these options. 00217 t.c_iflag &= ~(BRKINT | ISTRIP | IUCLC | IXON | IXANY | IXOFF | IMAXBEL); 00218 t.c_iflag |= (IGNBRK | IGNPAR); 00219 t.c_oflag &= ~(OLCUC); 00220 t.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD); 00221 t.c_cflag |= (CS8 | CREAD | HUPCL | CLOCAL); 00222 if(rtscts) 00223 t.c_cflag |= CRTSCTS; 00224 t.c_lflag &= ~(ISIG | XCASE | ECHO); 00225 tcsetattr(getrfd(), TCSANOW, &t); 00226 00227 // make sure we leave the modem in CLOCAL when we exit, so normal user 00228 // tasks can open the modem without using nonblocking. 00229 old_t.c_cflag |= CLOCAL; 00230 00231 // Send a few returns to make sure the modem is "good and zonked". 00232 if (cfgetospeed(&t) != B0 && !no_reset) 00233 { 00234 for(int i=0; i<5; i++) 00235 { 00236 write("\r", 1); 00237 usleep(10 * 1000); 00238 } 00239 } 00240 00241 // Set the baud rate to 0 for half a second to drop DTR... 00242 cfsetispeed(&t, B0); 00243 cfsetospeed(&t, B0); 00244 cfmakeraw(&t); 00245 tcsetattr(getrfd(), TCSANOW, &t); 00246 if (carrier()) 00247 usleep(500 * 1000); 00248 00249 speed(baud); 00250 usleep(10 * 1000); 00251 00252 drain(); 00253 } 00254 00255 00256 void WvModem::close() 00257 { 00258 if (!closed) 00259 { 00260 if (!closing) 00261 { 00262 closing = true; 00263 if (!no_reset) 00264 hangup(); 00265 else 00266 { 00267 drain(); 00268 cfsetospeed(&t, B0); 00269 // If this works?? 00270 write("\r"); 00271 } 00272 } 00273 00274 closing = true; 00275 if (getrfd() >= 0) 00276 { 00277 tcflush(getrfd(), TCIOFLUSH); 00278 if (have_old_t) 00279 tcsetattr(getrfd(), TCSANOW, &old_t); 00280 tcflush(getrfd(), TCIOFLUSH); 00281 } 00282 WvFile::close(); 00283 closing = false; 00284 } 00285 } 00286 00287 00288 int WvModem::speed(int _baud) 00289 { 00290 speed_t s = B0; 00291 baud = 0; 00292 for (unsigned int i = 0; i < sizeof(speeds) / sizeof(*speeds); i++) 00293 { 00294 if (speeds[i].baud <= _baud) 00295 { 00296 s = speeds[i].speedt; 00297 break; 00298 } 00299 } 00300 00301 cfsetispeed(&t, B0); // auto-match to output speed 00302 cfsetospeed(&t, s); 00303 tcsetattr(getrfd(), TCSANOW, &t); 00304 00305 return get_real_speed(); 00306 } 00307 00308 00309 int WvModem::getstatus() 00310 { 00311 if (!isok()) return 0; 00312 int status = 0; 00313 ioctl(getrfd(), TIOCMGET, &status); 00314 return status; 00315 } 00316 00317 00318 bool WvModem::carrier() 00319 { 00320 return (getstatus() & TIOCM_CD) ? 1 : 0; 00321 }