WvStreams
wvmodem.cc
1 /*
2  * Worldvisions Weaver Software:
3  * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4  * Copyright (C) 1999 Red Hat, Inc.
5  *
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.
9  *
10  */
11 
12 #include "wvmodem.h"
13 #include <sys/ioctl.h>
14 
15 #if HAVE_LINUX_SERIAL_H
16 # include <linux/serial.h>
17 #endif
18 
19 #if ! HAVE_CFMAKERAW
20 static inline void cfmakeraw(struct termios *termios_p)
21 {
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;
27 }
28 #endif
29 
30 struct SpeedLookup {
31  int baud;
32  speed_t speedt;
33 };
34 
35 
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}
52 };
53 
54 
55 WvModemBase::WvModemBase(int _fd) : WvFile(_fd)
56 {
57  get_real_speed();
58 }
59 
60 
61 WvModemBase::~WvModemBase()
62 {
63  // nothing needed
64 }
65 
66 
67 int WvModemBase::get_real_speed()
68 {
69  speed_t s;
70 
71  if (!isok()) return 0;
72 
73  tcgetattr( getrfd(), &t );
74  s = cfgetospeed( &t );
75  for (unsigned int i = 0; i < sizeof(speeds) / sizeof(*speeds); i++)
76  {
77  if (speeds[i].speedt == s)
78  {
79  baud = speeds[i].baud;
80  break;
81  }
82  }
83 
84  return baud;
85 }
86 
87 
89 {
90  // no file open, no need to close it
91 }
92 
93 
95 {
96  return true;
97 }
98 
99 
101 {
102  return baud;
103 }
104 
105 
107 {
108  int i, oldbaud = baud;
109 
110  if (die_fast || !isok()) return;
111 
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();
122 
123  // drop DTR for a while, if still online
124  if (carrier())
125  {
126  cfsetospeed( &t, B0 );
127  tcsetattr( getrfd(), TCSANOW, &t );
128  for (i = 0; carrier() && i < 10; i++)
129  usleep( 100 * 1000 );
130 
131  // raise DTR again, restoring the old baud rate
132  speed(oldbaud);
133  }
134 
135  if (carrier())
136  {
137  // need to do +++ manual-disconnect stuff
138  write( "+++", 3 );
139  usleep( 1500 * 1000 );
140  write( "ATH\r", 4 );
141 
142  for (i = 0; carrier() && i < 5; i++)
143  usleep( 100 * 1000 );
144  }
145 }
146 
147 
148 
149 WvModem::WvModem(WvStringParm filename, int _baud, bool rtscts, bool _no_reset)
150  : WvModemBase(), lock(filename), log("WvModem", WvLog::Debug1)
151 {
152  closing = false;
153  baud = _baud;
154  die_fast = false;
155  no_reset = _no_reset;
156  have_old_t = false;
157 
158  if (!lock.lock())
159  {
160  seterr(EBUSY);
161  return;
162  }
163 
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);
170 
171  if (isok())
172  setup_modem(rtscts);
173 }
174 
175 
176 WvModem::~WvModem()
177 {
178  close();
179 }
180 
181 
182 void WvModem::setup_modem(bool rtscts)
183 {
184  if (!isok()) return;
185 
186  if (tcgetattr(getrfd(), &t) || tcgetattr(getrfd(), &old_t))
187  {
188  closing = true;
189  seterr(errno);
190  return;
191  }
192  have_old_t = true;
193 
194  drain();
195 
196 #if 0
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
202  {
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;
209 
210  if (ioctl(getrfd(), TIOCSSERIAL, &sinfo) < 0)
211  log("Cannot set information for serial port.");
212  }
213 #endif
214 
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);
226 
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;
230 
231  // Send a few returns to make sure the modem is "good and zonked".
232  if (cfgetospeed(&t) != B0 && !no_reset)
233  {
234  for(int i=0; i<5; i++)
235  {
236  write("\r", 1);
237  usleep(10 * 1000);
238  }
239  }
240 
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);
248 
249  speed(baud);
250  usleep(10 * 1000);
251 
252  drain();
253 }
254 
255 
257 {
258  if (!closed)
259  {
260  if (!closing)
261  {
262  closing = true;
263  if (!no_reset)
264  hangup();
265  else
266  {
267  drain();
268  cfsetospeed(&t, B0);
269  // If this works??
270  write("\r");
271  }
272  }
273 
274  closing = true;
275  if (getrfd() >= 0)
276  {
277  tcflush(getrfd(), TCIOFLUSH);
278  if (have_old_t)
279  tcsetattr(getrfd(), TCSANOW, &old_t);
280  tcflush(getrfd(), TCIOFLUSH);
281  }
282  WvFile::close();
283  closing = false;
284  }
285 }
286 
287 
288 int WvModem::speed(int _baud)
289 {
290  speed_t s = B0;
291  baud = 0;
292  for (unsigned int i = 0; i < sizeof(speeds) / sizeof(*speeds); i++)
293  {
294  if (speeds[i].baud <= _baud)
295  {
296  s = speeds[i].speedt;
297  break;
298  }
299  }
300 
301  cfsetispeed(&t, B0); // auto-match to output speed
302  cfsetospeed(&t, s);
303  tcsetattr(getrfd(), TCSANOW, &t);
304 
305  return get_real_speed();
306 }
307 
308 
309 int WvModem::getstatus()
310 {
311  if (!isok()) return 0;
312  int status = 0;
313  ioctl(getrfd(), TIOCMGET, &status);
314  return status;
315 }
316 
317 
319 {
320  return (getstatus() & TIOCM_CD) ? 1 : 0;
321 }
WvStream::write
virtual size_t write(const void *buf, size_t count)
Write data to the stream.
Definition: wvstream.cc:532
WvModemBase::close
virtual void close()
do-nothing method that is not needed in WvModemBase
Definition: wvmodem.cc:88
WvFdStream::isok
virtual bool isok() const
return true if the stream is actually usable right now
Definition: wvfdstream.cc:134
WvModem::close
virtual void close()
Close the connection to the modem.
Definition: wvmodem.cc:256
WvModem::speed
virtual int speed(int _baud)
_baud is the desired speed, that you wish the modem to communicate with, and this method returns the ...
Definition: wvmodem.cc:288
WvFile
WvFile implements a stream connected to a file or Unix device.
Definition: wvfile.h:28
WvStream::select
bool select(time_t msec_timeout)
Return true if any of the requested features are true on the stream.
Definition: wvstream.h:376
WvFdStream::getrfd
int getrfd() const
Returns the Unix file descriptor for reading from this stream.
Definition: wvfdstream.h:63
WvStream::seterr
virtual void seterr(int _errnum)
Override seterr() from WvError so that it auto-closes the stream.
Definition: wvstream.cc:451
WvLog
A WvLog stream accepts log messages from applications and forwards them to all registered WvLogRcv's.
Definition: wvlog.h:56
WvModemBase
WvModemBase provides the methods used to control a modem, but without real implementation for most of...
Definition: wvmodem.h:39
WvModem::carrier
virtual bool carrier()
Is there a carrier present?
Definition: wvmodem.cc:318
WvModemBase::hangup
virtual void hangup()
may need to hangup for redial reasons
Definition: wvmodem.cc:106
SpeedLookup
Definition: wvmodem.cc:30
WvFdStream::close
virtual void close()
Closes the file descriptors.
Definition: wvfdstream.cc:117
WvModemBase::speed
virtual int speed(int _baud)
do-nothing method that is not needed in WvModemBase
Definition: wvmodem.cc:100
WvModemBase::carrier
virtual bool carrier()
do-nothing method that is not needed in WvModemBase
Definition: wvmodem.cc:94
WvStream::drain
void drain()
drain the input buffer (read and discard data until select(0) returns false)
Definition: wvstream.cc:699