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