1 /*
2 * The contents of this file are subject to the Mozilla Public License
3 * Version 1.0 (the "License"); you may not use this file except in
4 * compliance with the License. You may obtain a copy of the License at
5 * http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS IS"
8 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
9 * License for the specific language governing rights and limitations
10 * under the License.
11 *
12 * The Initial Developer of this code is Rodd Zurcher.
13 * Portions created by Rodd Zurcher are Copyright (C) 1998 Rodd Zurcher.
14 * All Rights Reserved.
15 */
16
17 /*
18 * NOTE - this is only a partial implementation of the PSerial abstract
19 * class. It defaults to 2400 bps, 8 data bits, 1 stop, odd parity. It
20 * does not support changing the baud rate or other attributes. It does
21 * now support manual control of RTS and DTR lines.
22 */
23
24 #include <sys/time.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/ioctl.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <cstring>
31 #include <termios.h>
32
33 // need another #include for Solaris - Richard Mitchell <gpsphoto@designprofessionals.com>
34 #ifdef SOLARIS
35 #include <sys/filio.h>
36 #endif
37
38 #include "PSerial.h"
39
40 #ifndef DEFAULT_SERIAL_NAME
41 #define DEFAULT_SERIAL_NAME "/dev/ttyS0"
42 #endif
43
44
45 class PSerial_unix : public PSerial
46 {
47 public:
48 PSerial_unix(void);
49 virtual bool Open(const char *name);
50 virtual void Close();
51 virtual long Write(const void *ptr, long count);
52 virtual void FlushWrite();
53
54 virtual long Read(void *ptr, long count);
55 virtual bool SetTimeout(long timeout_ms);
56 virtual bool SetSpeed(int speed, int opts = 0);
57 virtual bool SetDTR(bool state);
58 virtual bool SetRTS(bool state);
59
60
61 private:
62 bool SetRaw(void);
63 bool SetLineData(int bits, bool state);
64
65 int fTerm;
66 long fTimeout;
67 bool fUseTcDrain;
68 };
69
70
71 static int tv_ge(const struct timeval *a, const struct timeval *b);
72 static void tv_sub(const struct timeval *a, struct timeval *b);
73 static bool SetTermiosSpeed(termios &tios, int speed);
74
75
NewSerial()76 PSerial* PSerial::NewSerial()
77 {
78 return new PSerial_unix();
79 }
80
81
GetDefaultName()82 const char *PSerial::GetDefaultName()
83 {
84 return DEFAULT_SERIAL_NAME;
85 }
86
87
PSerial_unix(void)88 PSerial_unix::PSerial_unix(void) :
89 fTerm(-1),
90 fTimeout(kPStream_NeverTimeout)
91 {
92 }
93
94
Open(const char * name)95 bool PSerial_unix::Open(const char *name)
96 {
97 bool ok;
98 char *buf = 0;
99
100 fUseTcDrain = true;
101
102 // check for option
103 const char *ptr = (const char *)strchr(name, ':');
104 if (ptr)
105 {
106 // extract basename
107 int n = ptr - name;
108 buf = new char[n+1];
109 memcpy(buf, name, n);
110 buf[n] = 0;
111 if (strcmp(ptr+1, "nodrain")==0)
112 {
113 fUseTcDrain = false;
114 }
115 name = buf;
116 }
117
118 ok = ((fTerm = open(name, O_RDWR)) >= 0);
119 if (ok)
120 {
121 ok = SetRaw();
122 }
123
124 delete [] buf;
125 return ok;
126 }
127
128
129 // Put the tty into raw mode
130 // Adapted from tty_raw, "Advanced Programing in the UNIX
131 // Environment", W. Richard Stevens, pp354-355
SetRaw(void)132 bool PSerial_unix::SetRaw(void)
133 {
134 termios tios;
135
136 if (tcgetattr(fTerm, &tios) < 0)
137 return false;
138
139 // echo off, canonical mode off, extended input processing off
140 // signal chars off
141 tios.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
142
143 // no SIGINT on BREAK, CR-to-NL off, input parity check off,
144 // don't strip 8th bit on input, output flow control off
145 tios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
146
147 // clear size bits, 1 stop bit, disable parity
148 tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD);
149
150 // set 8 bits/char
151 tios.c_cflag |= (CS8);
152
153 // output processing off
154 tios.c_oflag &= ~(OPOST);
155
156 // 1 byte at a time, no timer
157 tios.c_cc[VMIN] = 1;
158 tios.c_cc[VTIME] = 0;
159
160
161 if (tcsetattr(fTerm, TCSAFLUSH, &tios) < 0)
162 return false;
163
164
165 SetSpeed(9600, 0);
166
167 return true;
168 }
169
170
Close()171 void PSerial_unix::Close()
172 {
173 if (fTerm != -1)
174 {
175 close(fTerm);
176 }
177 fTerm = -1;
178 }
179
180
SetSpeed(int speed,int opts)181 bool PSerial_unix::SetSpeed(int speed, int opts)
182 {
183 termios tios;
184
185 if (tcgetattr(fTerm, &tios) < 0)
186 return false;
187
188 // set the speed
189 if (!SetTermiosSpeed(tios, speed))
190 return false;
191
192 // set the parity
193 switch(opts & kPSerial_ParityMask)
194 {
195 case kPSerial_ParityNone:
196 tios.c_cflag &= ~(PARENB);
197 break;
198 case kPSerial_ParityOdd:
199 tios.c_cflag |= (PARENB | PARODD);
200 break;
201 case kPSerial_ParityEven:
202 tios.c_cflag |= (PARENB);
203 tios.c_cflag &= ~(PARODD);
204 break;
205 };
206
207 if (tcsetattr(fTerm, TCSAFLUSH, &tios) < 0)
208 return false;
209
210 return true;
211 }
212
213
Write(const void * ptr,long count)214 long PSerial_unix::Write(const void *ptr, long count)
215 {
216 return write(fTerm, ptr, count);
217 }
218
219 #include <cstdio>
FlushWrite()220 void PSerial_unix::FlushWrite()
221 {
222 #ifndef NO_TCDRAIN
223 if (fUseTcDrain)
224 {
225 tcdrain(fTerm);
226 }
227 #endif
228 }
229
230
SetTimeout(long timeout_ms)231 bool PSerial_unix::SetTimeout(long timeout_ms)
232 {
233 fTimeout = timeout_ms;
234 return true;
235 }
236
237
Read(void * ptr,long count)238 long PSerial_unix::Read(void *ptr, long count)
239 {
240 // Blocking read
241 if (fTimeout == kPStream_NeverTimeout)
242 {
243 long rval = read(fTerm, ptr, count);
244 return rval;
245 }
246
247 // time limited read
248 char *cur = (char *) ptr;
249 struct timeval expire;
250 struct timezone tz;
251
252 if (gettimeofday(&expire, &tz) < 0)
253 {
254 return -1;
255 }
256
257 expire.tv_sec += fTimeout / 1000;
258 expire.tv_usec += (fTimeout % 1000) * 1000;
259
260 while (count)
261 {
262 fd_set rfds;
263 FD_ZERO(&rfds);
264 FD_SET(fTerm, &rfds);
265 struct timeval delay;
266 long nread;
267
268 if (gettimeofday(&delay, &tz) < 0)
269 {
270 return -1;
271 }
272
273 if (tv_ge(&delay, &expire))
274 {
275 // delay (current time) >= expire
276 break;
277 }
278
279 tv_sub(&expire, &delay); // delay = expire - delay
280
281 if (select(fTerm + 1, &rfds, NULL, NULL, &delay))
282 {
283 if (ioctl(fTerm, FIONREAD, &nread) < 0)
284 {
285 return -1;
286 }
287
288 nread = (count < nread) ? count : nread;
289 if ((nread = read(fTerm, cur, nread)) < 0)
290 {
291 return -1;
292 }
293 count -= nread;
294 cur += nread;
295 }
296 else
297 {
298 // timeout
299 break;
300 }
301 } // while
302
303 return (cur - (char *)ptr);
304 }
305
306 //
307 // This part implemented by Peter Ljungstrand (SetDTR and SetRTS)
308 // modifided by Dave Baum
309 //
310
SetDTR(bool state)311 bool PSerial_unix::SetDTR(bool state)
312 {
313 return SetLineData(TIOCM_DTR, state);
314 }
315
316
SetRTS(bool state)317 bool PSerial_unix::SetRTS(bool state)
318 {
319 return SetLineData(TIOCM_RTS, state);
320 }
321
322
SetLineData(int bits,bool state)323 bool PSerial_unix::SetLineData(int bits, bool state)
324 {
325 int lineData;
326
327 if (ioctl(fTerm, TIOCMGET, &lineData))
328 {
329 return false;
330 }
331 else
332 {
333 if (state)
334 {
335 lineData |= bits;
336 }
337 else
338 {
339 lineData &= ~bits;
340 }
341 return !ioctl(fTerm, TIOCMSET, &lineData);
342 }
343 }
344
345
346
SetTermiosSpeed(termios & tios,int speed)347 bool SetTermiosSpeed(termios &tios, int speed)
348 {
349 speed_t s;
350
351 switch(speed)
352 {
353 case 2400:
354 s = B2400;
355 break;
356 case 4800:
357 s = B4800;
358 break;
359 case 9600:
360 s = B9600;
361 break;
362 default:
363 return false;
364 }
365
366 if (cfsetispeed(&tios, s) < 0)
367 return false;
368
369 if (cfsetospeed(&tios, s) < 0)
370 return false;
371
372 return true;
373 }
374
375
tv_ge(const struct timeval * a,const struct timeval * b)376 int tv_ge(const struct timeval *a, const struct timeval *b)
377 {
378 if (a->tv_sec < b->tv_sec)
379 return 0; // asec < bsec
380
381 if (a->tv_sec == b->tv_sec)
382 {
383 if (a->tv_usec < b->tv_usec)
384 {
385 return 0; // ausec < busec
386 }
387 return 1; // ausec >= busec
388 }
389 return 1; // asec > bsec
390 }
391
392
tv_sub(const struct timeval * a,struct timeval * b)393 void tv_sub(const struct timeval *a, struct timeval *b)
394 {
395 b->tv_sec = a->tv_sec - b->tv_sec;
396 b->tv_usec = a->tv_usec - b->tv_usec;
397 if (b->tv_usec < 0)
398 {
399 b->tv_usec += 1000000;
400 b->tv_sec--;
401 }
402 }
403
404
405