Try another fix for wvlinkerhack and static libraries.
[wvstreams.git] / streams / wvmodem.cc
blobc0893c3a84780740ce33d3348902fcc63f672d95
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4 * Copyright (C) 1999 Red Hat, Inc.
6 * Implementation of the WvModem class. Inherits from WvFile, but
7 * handles various important details related to modems, like setting
8 * the baud rate, checking carrier detect, and dropping DTR.
12 #include "wvmodem.h"
13 #include <sys/ioctl.h>
15 #if HAVE_LINUX_SERIAL_H
16 # include <linux/serial.h>
17 #endif
19 #if ! HAVE_CFMAKERAW
20 static inline void cfmakeraw(struct termios *termios_p)
22 termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
23 termios_p->c_oflag &= ~OPOST;
24 termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
25 termios_p->c_cflag &= ~(CSIZE|PARENB);
26 termios_p->c_cflag |= CS8;
28 #endif
30 struct SpeedLookup {
31 int baud;
32 speed_t speedt;
36 static SpeedLookup speeds[] = {
37 #ifdef B460800
38 {460800, B460800},
39 #endif
40 #ifdef B230400
41 {230400, B230400},
42 #endif
43 {115200, B115200},
44 { 57600, B57600},
45 { 38400, B38400},
46 { 19200, B19200},
47 { 9600, B9600},
48 { 4800, B4800},
49 { 2400, B2400},
50 { 1200, B1200},
51 { 300, B300}
55 WvModemBase::WvModemBase(int _fd) : WvFile(_fd)
57 get_real_speed();
61 WvModemBase::~WvModemBase()
63 // nothing needed
67 int WvModemBase::get_real_speed()
69 speed_t s;
71 if (!isok()) return 0;
73 tcgetattr( getrfd(), &t );
74 s = cfgetospeed( &t );
75 for (unsigned int i = 0; i < sizeof(speeds) / sizeof(*speeds); i++)
77 if (speeds[i].speedt == s)
79 baud = speeds[i].baud;
80 break;
84 return baud;
88 void WvModemBase::close()
90 // no file open, no need to close it
94 bool WvModemBase::carrier()
96 return true;
100 int WvModemBase::speed(int)
102 return baud;
106 void WvModemBase::hangup()
108 int i, oldbaud = baud;
110 if (die_fast || !isok()) return;
112 // politely abort any dial in progress, to avoid locking USR modems.
113 // we should only do this if we have received any response from the modem,
114 // so that WvModemScan can run faster.
115 drain();
116 write( "\r", 1 );
117 // FIXME: should be iswritable, but based on the numer of msec params
118 // tossed around I assume modems are very timing-sensitive
119 for (i = 0; !select(200, false, true) && i < 10; i++)
120 write( "\r", 1 );
121 drain();
123 // drop DTR for a while, if still online
124 if (carrier())
126 cfsetospeed( &t, B0 );
127 tcsetattr( getrfd(), TCSANOW, &t );
128 for (i = 0; carrier() && i < 10; i++)
129 usleep( 100 * 1000 );
131 // raise DTR again, restoring the old baud rate
132 speed(oldbaud);
135 if (carrier())
137 // need to do +++ manual-disconnect stuff
138 write( "+++", 3 );
139 usleep( 1500 * 1000 );
140 write( "ATH\r", 4 );
142 for (i = 0; carrier() && i < 5; i++)
143 usleep( 100 * 1000 );
149 WvModem::WvModem(WvStringParm filename, int _baud, bool rtscts, bool _no_reset)
150 : WvModemBase(), lock(filename), log("WvModem", WvLog::Debug1)
152 closing = false;
153 baud = _baud;
154 die_fast = false;
155 no_reset = _no_reset;
156 have_old_t = false;
158 if (!lock.lock())
160 seterr(EBUSY);
161 return;
164 // note: if CLOCAL is not set on the modem, open will
165 // block until a carrier detect. Since we have to open the modem to
166 // generate a carrier detect, we have a problem. So we open the modem
167 // nonblocking. It would then be safe to switch to blocking mode,
168 // but that is no longer recommended for WvStream.
169 open(filename, O_RDWR|O_NONBLOCK|O_NOCTTY);
171 if (isok())
172 setup_modem(rtscts);
176 WvModem::~WvModem()
178 close();
182 void WvModem::setup_modem(bool rtscts)
184 if (!isok()) return;
186 if (tcgetattr(getrfd(), &t) || tcgetattr(getrfd(), &old_t))
188 closing = true;
189 seterr(errno);
190 return;
192 have_old_t = true;
194 drain();
196 #if HAVE_LINUX_SERIAL_H
197 struct serial_struct old_sinfo, sinfo;
198 sinfo.reserved_char[0] = 0;
199 if (ioctl(getrfd(), TIOCGSERIAL, &old_sinfo) < 0)
200 log("Cannot get information for serial port.");
201 else
203 sinfo = old_sinfo;
204 // Why there are two closing wait timeouts, is beyond me
205 // but there are... apparently the second one is deprecated
206 // but why take a chance...
207 sinfo.closing_wait = ASYNC_CLOSING_WAIT_NONE;
208 sinfo.closing_wait2 = ASYNC_CLOSING_WAIT_NONE;
210 if (ioctl(getrfd(), TIOCSSERIAL, &sinfo) < 0)
211 log("Cannot set information for serial port.");
213 #endif
215 // set up the terminal characteristics.
216 // see "man tcsetattr" for more information about these options.
217 t.c_iflag &= ~(BRKINT | ISTRIP | IUCLC | IXON | IXANY | IXOFF | IMAXBEL);
218 t.c_iflag |= (IGNBRK | IGNPAR);
219 t.c_oflag &= ~(OLCUC);
220 t.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD);
221 t.c_cflag |= (CS8 | CREAD | HUPCL | CLOCAL);
222 if(rtscts)
223 t.c_cflag |= CRTSCTS;
224 t.c_lflag &= ~(ISIG | XCASE | ECHO);
225 tcsetattr(getrfd(), TCSANOW, &t);
227 // make sure we leave the modem in CLOCAL when we exit, so normal user
228 // tasks can open the modem without using nonblocking.
229 old_t.c_cflag |= CLOCAL;
231 // Send a few returns to make sure the modem is "good and zonked".
232 if (cfgetospeed(&t) != B0 && !no_reset)
234 for(int i=0; i<5; i++)
236 write("\r", 1);
237 usleep(10 * 1000);
241 // Set the baud rate to 0 for half a second to drop DTR...
242 cfsetispeed(&t, B0);
243 cfsetospeed(&t, B0);
244 cfmakeraw(&t);
245 tcsetattr(getrfd(), TCSANOW, &t);
246 if (carrier())
247 usleep(500 * 1000);
249 speed(baud);
250 usleep(10 * 1000);
252 drain();
256 void WvModem::close()
258 if (!closed)
260 if (!closing)
262 closing = true;
263 if (!no_reset)
264 hangup();
265 else
267 drain();
268 cfsetospeed(&t, B0);
269 // If this works??
270 write("\r");
274 closing = true;
275 if (getrfd() >= 0)
277 tcflush(getrfd(), TCIOFLUSH);
278 if (have_old_t)
279 tcsetattr(getrfd(), TCSANOW, &old_t);
280 tcflush(getrfd(), TCIOFLUSH);
282 WvFile::close();
283 closing = false;
288 int WvModem::speed(int _baud)
290 speed_t s = B0;
291 baud = 0;
292 for (unsigned int i = 0; i < sizeof(speeds) / sizeof(*speeds); i++)
294 if (speeds[i].baud <= _baud)
296 s = speeds[i].speedt;
297 break;
301 cfsetispeed(&t, B0); // auto-match to output speed
302 cfsetospeed(&t, s);
303 tcsetattr(getrfd(), TCSANOW, &t);
305 return get_real_speed();
309 int WvModem::getstatus()
311 if (!isok()) return 0;
312 int status = 0;
313 ioctl(getrfd(), TIOCMGET, &status);
314 return status;
318 bool WvModem::carrier()
320 return (getstatus() & TIOCM_CD) ? 1 : 0;