1 /*
2  * This file is Copyright (c) 2010-2018 by the GPSD project
3  * SPDX-License-Identifier: BSD-2-clause
4  */
5 
6 #include "gpsd_config.h"  /* must be before all includes */
7 
8 #include <ctype.h>              /* for isdigit() */
9 #include <dirent.h>             /* for DIR */
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <stdbool.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <sys/ioctl.h>
16 #include <sys/param.h>		/* defines BSD */
17 #include <sys/socket.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 
22 #ifdef HAVE_SYS_SYSMACROS_H
23 #include <sys/sysmacros.h>	/* defines major() */
24 #endif	/* HAVE_SYS_SYSMACROS_H */
25 
26 #ifdef ENABLE_BLUEZ
27 #include <bluetooth/bluetooth.h>
28 #include <bluetooth/hci.h>
29 #include <bluetooth/hci_lib.h>
30 #include <bluetooth/rfcomm.h>
31 #endif /* ENABLE_BLUEZ */
32 
33 #include "gpsd.h"
34 
35 /* Workaround for HP-UX 11.23, which is missing CRTSCTS */
36 #ifndef CRTSCTS
37 #  ifdef CNEW_RTSCTS
38 #    define CRTSCTS CNEW_RTSCTS
39 #  else
40 #    define CRTSCTS 0
41 #  endif /* CNEW_RTSCTS */
42 #endif /* !CRTSCTS */
43 
gpsd_classify(const char * path)44 static sourcetype_t gpsd_classify(const char *path)
45 /* figure out what kind of device we're looking at */
46 {
47     struct stat sb;
48 
49     if (stat(path, &sb) == -1)
50 	return source_unknown;
51     else if (S_ISREG(sb.st_mode))
52 	return source_blockdev;
53     /* this assumes we won't get UDP from a filesystem socket */
54     else if (S_ISSOCK(sb.st_mode))
55 	return source_tcp;
56     /* OS-independent check for ptys using Unix98 naming convention */
57     else if (strncmp(path, "/dev/pts/", 9) == 0)
58 	return source_pty;
59     else if (strncmp(path, "/dev/pps", 8) == 0)
60 	return source_pps;
61     else if (S_ISFIFO(sb.st_mode))
62 	return source_pipe;
63     else if (S_ISCHR(sb.st_mode)) {
64 	sourcetype_t devtype = source_rs232;
65 #ifdef __linux__
66 	/* Linux major device numbers live here
67 	 *
68          * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/devices.txt
69 	 *
70 	 * Note: This code works because Linux major device numbers are
71 	 * stable and architecture-independent.  It is *not* a good model
72 	 * for other Unixes where either or both assumptions may break.
73 	 */
74 	int devmajor = major(sb.st_rdev);
75         /* 207 are Freescale i.MX UARTs (ttymxc*) */
76 	if (devmajor == 4 || devmajor == 204 || devmajor == 207)
77 	    devtype = source_rs232;
78 	else if (devmajor == 188 || devmajor == 166)
79 	    devtype = source_usb;
80 	else if (devmajor == 216 || devmajor == 217)
81 	    devtype = source_bluetooth;
82 	else if (devmajor == 3 || (devmajor >= 136 && devmajor <= 143))
83 	    devtype = source_pty;
84 #endif /* __linux__ */
85 	/*
86 	 * See http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system
87 	 * for discussion how this works.  Key graphs:
88 	 *
89 	 * Compilers for the old BSD base for these distributions
90 	 * defined the __bsdi__ macro, but none of these distributions
91 	 * define it now. This leaves no generic "BSD" macro defined
92 	 * by the compiler itself, but all UNIX-style OSes provide a
93 	 * <sys/param.h> file. On BSD distributions, and only on BSD
94 	 * distributions, this file defines a BSD macro that's set to
95 	 * the OS version. Checking for this generic macro is more
96 	 * robust than looking for known BSD distributions with
97 	 * __DragonFly__, __FreeBSD__, __NetBSD__, and __OpenBSD__
98 	 * macros.
99 	 *
100 	 * Apple's OSX for the Mac and iOS for iPhones and iPads are
101 	 * based in part on a fork of FreeBSD distributed as
102 	 * Darwin. As such, OSX and iOS also define the BSD macro
103 	 * within <sys/param.h>. However, compilers for OSX, iOS, and
104 	 * Darwin do not define __unix__. To detect all BSD OSes,
105 	 * including OSX, iOS, and Darwin, use an #if/#endif that
106 	 * checks for __unix__ along with __APPLE__ and __MACH__ (see
107 	 * the later section on OSX and iOS).
108 	 */
109 #ifdef BSD
110 	/*
111 	 * Hacky check for pty, which is what really matters for avoiding
112 	 * adaptive delay.
113 	 */
114 	if (strncmp(path, "/dev/ttyp", 9) == 0 ||
115 	    strncmp(path, "/dev/ttyq", 9) == 0)
116 	    devtype = source_pty;
117 	else if (strncmp(path, "/dev/ttyU", 9) == 0 ||
118 	    strncmp(path, "/dev/dtyU", 9) == 0)
119 	    devtype = source_usb;
120 	/* XXX bluetooth */
121 #endif /* BSD */
122 	return devtype;
123     } else
124 	return source_unknown;
125 }
126 
127 #ifdef __linux__
128 
fusercount(const char * path)129 static int fusercount(const char *path)
130 /* return true if any process has the specified path open */
131 {
132     DIR *procd, *fdd;
133     struct dirent *procentry, *fdentry;
134     char procpath[64], fdpath[64], linkpath[64];
135     int cnt = 0;
136 
137     if ((procd = opendir("/proc")) == NULL)
138 	return -1;
139     while ((procentry = readdir(procd)) != NULL) {
140 	if (isdigit(procentry->d_name[0])==0)
141 	    continue;
142         /* longest procentry->d_name I could find was 12 */
143 	(void)snprintf(procpath, sizeof(procpath),
144 		       "/proc/%.20s/fd/", procentry->d_name);
145 	if ((fdd = opendir(procpath)) == NULL)
146 	    continue;
147 	while ((fdentry = readdir(fdd)) != NULL) {
148 	    (void)strlcpy(fdpath, procpath, sizeof(fdpath));
149 	    (void)strlcat(fdpath, fdentry->d_name, sizeof(fdpath));
150 	    (void)memset(linkpath, '\0', sizeof(linkpath));
151 	    if (readlink(fdpath, linkpath, sizeof(linkpath)) == -1)
152 		continue;
153 	    if (strcmp(linkpath, path) == 0) {
154 		++cnt;
155 	    }
156 	}
157 	(void)closedir(fdd);
158     }
159     (void)closedir(procd);
160 
161     return cnt;
162 }
163 #endif /* __linux__ */
164 
gpsd_tty_init(struct gps_device_t * session)165 void gpsd_tty_init(struct gps_device_t *session)
166 /* to be called on allocating a device */
167 {
168     /* mark GPS fd closed and its baud rate unknown */
169     session->gpsdata.gps_fd = -1;
170     session->saved_baud = -1;
171     session->zerokill = false;
172     session->reawake = (time_t)0;
173 }
174 
175 #if !defined(HAVE_CFMAKERAW)
176 /*
177  * Local implementation of cfmakeraw (which is not specified by
178  * POSIX; see matching declaration in gpsd.h).
179  */
180 /* Pasted from man page; added in serial.c arbitrarily */
cfmakeraw(struct termios * termios_p)181 void cfmakeraw(struct termios *termios_p)
182 {
183     termios_p->c_iflag &=
184 	~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
185     termios_p->c_oflag &= ~OPOST;
186     termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
187     termios_p->c_cflag &= ~(CSIZE | PARENB);
188     termios_p->c_cflag |= CS8;
189 }
190 #endif /* !defined(HAVE_CFMAKERAW) */
191 
gpsd_get_speed_termios(const struct termios * ttyctl)192 static speed_t gpsd_get_speed_termios(const struct termios *ttyctl)
193 {
194     speed_t code = cfgetospeed(ttyctl);
195     switch (code) {
196     case B300:
197 	return (300);
198     case B1200:
199 	return (1200);
200     case B2400:
201 	return (2400);
202     case B4800:
203 	return (4800);
204     case B9600:
205 	return (9600);
206     case B19200:
207 	return (19200);
208     case B38400:
209 	return (38400);
210     case B57600:
211 	return (57600);
212     case B115200:
213 	return (115200);
214     case B230400:
215 	return (230400);
216     default: /* B0 */
217 	return 0;
218     }
219 }
220 
gpsd_get_speed(const struct gps_device_t * dev)221 speed_t gpsd_get_speed(const struct gps_device_t *dev)
222 {
223     return gpsd_get_speed_termios(&dev->ttyset);
224 }
225 
gpsd_get_speed_old(const struct gps_device_t * dev)226 speed_t gpsd_get_speed_old(const struct gps_device_t *dev)
227 {
228     return gpsd_get_speed_termios(&dev->ttyset_old);
229 }
230 
gpsd_get_parity(const struct gps_device_t * dev)231 char gpsd_get_parity(const struct gps_device_t *dev)
232 {
233     char parity = 'N';
234     if ((dev->ttyset.c_cflag & (PARENB | PARODD)) == (PARENB | PARODD))
235 	parity = 'O';
236     else if ((dev->ttyset.c_cflag & PARENB) == PARENB)
237 	parity = 'E';
238     return parity;
239 }
240 
gpsd_get_stopbits(const struct gps_device_t * dev)241 int gpsd_get_stopbits(const struct gps_device_t *dev)
242 {
243     int stopbits = 0;
244     if ((dev->ttyset.c_cflag & CS8) == CS8)
245 	stopbits = 1;
246     else if ((dev->ttyset.c_cflag & (CS7 | CSTOPB)) == (CS7 | CSTOPB))
247 	stopbits = 2;
248     return stopbits;
249 }
250 
gpsd_set_raw(struct gps_device_t * session)251 bool gpsd_set_raw(struct gps_device_t * session)
252 {
253     (void)cfmakeraw(&session->ttyset);
254     if (-1 == tcsetattr(session->gpsdata.gps_fd, TCIOFLUSH, &session->ttyset)) {
255 	GPSD_LOG(LOG_ERROR, &session->context->errout,
256 		 "SER: error changing port attributes: %s\n", strerror(errno));
257 	return false;
258     }
259 
260     return true;
261 }
262 
gpsd_set_speed(struct gps_device_t * session,speed_t speed,char parity,unsigned int stopbits)263 void gpsd_set_speed(struct gps_device_t *session,
264 		    speed_t speed, char parity, unsigned int stopbits)
265 {
266     speed_t rate;
267     struct timespec delay;
268 
269     if (0 < session->context->fixed_port_speed) {
270         speed = session->context->fixed_port_speed;
271     }
272     if ('\0' != session->context->fixed_port_framing[0]) {
273         // ignore length, stopbits=2 forces length 7.
274         parity = session->context->fixed_port_framing[1];
275         stopbits = session->context->fixed_port_framing[2] - '0';
276     }
277 
278     /*
279      * Yes, you can set speeds that aren't in the hunt loop.  If you
280      * do this, and you aren't on Linux where baud rate is preserved
281      * across port closings, you've screwed yourself. Don't do that!
282      */
283     if (speed < 300)
284 	rate = B0;
285     else if (speed < 1200)
286 	rate = B300;
287     else if (speed < 2400)
288 	rate = B1200;
289     else if (speed < 4800)
290 	rate = B2400;
291     else if (speed < 9600)
292 	rate = B4800;
293     else if (speed < 19200)
294 	rate = B9600;
295     else if (speed < 38400)
296 	rate = B19200;
297     else if (speed < 57600)
298 	rate = B38400;
299     else if (speed < 115200)
300 	rate = B57600;
301     else if (speed < 230400)
302 	rate = B115200;
303     else
304 	rate = B230400;
305 
306     /* backward-compatibility hack */
307     switch (parity) {
308     case 'E':
309         // FALLTHROUGH
310     case (char)2:
311 	parity = 'E';
312 	break;
313     case 'O':
314         // FALLTHROUGH
315     case (char)1:
316 	parity = 'O';
317 	break;
318     case 'N':
319         // FALLTHROUGH
320     case (char)0:
321         // FALLTHROUGH
322     default:
323 	parity = 'N';	/* without this we might emit malformed JSON */
324 	break;
325     }
326 
327     if (rate != cfgetispeed(&session->ttyset)
328 	|| parity != session->gpsdata.dev.parity
329 	|| stopbits != session->gpsdata.dev.stopbits) {
330 
331 	/*
332 	 * Don't mess with this conditional! Speed zero is supposed to mean
333 	 * to leave the port speed at whatever it currently is. This leads
334 	 * to excellent behavior on Linux, which preserves baudrate across
335 	 * serial device closes - it means that if you've opened this
336 	 * device before you typically don't have to hunt at all because
337 	 * it's still at the same speed you left it - you'll typically
338 	 * get packet lock within 1.5 seconds.  Alas, the BSDs and OS X
339 	 * aren't so nice.
340 	 */
341 	if (rate != B0) {
342 	    (void)cfsetispeed(&session->ttyset, rate);
343 	    (void)cfsetospeed(&session->ttyset, rate);
344 	}
345 	session->ttyset.c_iflag &= ~(PARMRK | INPCK);
346 	session->ttyset.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD);
347 	session->ttyset.c_cflag |= (stopbits == 2 ? CS7 | CSTOPB : CS8);
348 	switch (parity) {
349 	case 'E':
350 	    session->ttyset.c_iflag |= INPCK;
351 	    session->ttyset.c_cflag |= PARENB;
352 	    break;
353 	case 'O':
354 	    session->ttyset.c_iflag |= INPCK;
355 	    session->ttyset.c_cflag |= PARENB | PARODD;
356 	    break;
357 	}
358         if (0 != tcsetattr(session->gpsdata.gps_fd, TCSANOW,
359                            &session->ttyset)) {
360 	    /* strangely this fails on non-serial ports, but if
361              * we do not try, we get other failures.
362              * so ignore for now, as we always have, until it can
363              * be nailed down.
364              *
365 	     * GPSD_LOG(&session->context->errout, LOG_ERROR,
366 	     *	     "SER: error setting port attributes: %s, sourcetype: %d\n",
367 	     *	     strerror(errno), session->sourcetype);
368 	     * return;
369              */
370 	}
371 
372 	/*
373 	 * Serious black magic begins here.  Getting this code wrong can cause
374 	 * failures to lock to a correct speed, and not clean reproducible
375 	 * failures but flukey hardware- and timing-dependent ones.  So
376 	 * be very sure you know what you're doing before hacking it, and
377 	 * test thoroughly.
378 	 *
379 	 * The fundamental problem here is that serial devices take time
380 	 * to settle into a new baud rate after tcsetattr() is issued. Until
381 	 * they do so, input will be arbitarily garbled.  Normally this
382 	 * is not a big problem, but in our hunt loop the garbling can trash
383 	 * a long enough prefix of each sample to prevent detection of a
384 	 * packet header.  We could address the symptom by making the sample
385 	 * size enough larger that subtracting the maximum length of garble
386 	 * would still leave a sample longer than the maximum packet size.
387 	 * But it's better (and more efficient) to address the disease.
388 	 *
389 	 * In theory, one might think that not even a tcflush() call would
390 	 * be needed, with tcsetattr() delaying its return until the device
391 	 * is in a good state.  For simple devices like a 14550 UART that
392 	 * have fixed response timings this may even work, if the driver
393 	 * writer was smart enough to delay the return by the right number
394 	 * of milliseconds after poking the device port(s).
395 	 *
396 	 * Problems may arise if the driver's timings are off.  Or we may
397 	 * be talking to a USB device like the pl2303 commonly used in GPS
398 	 * mice; on these, the change will not happen immediately because
399 	 * it has to be sent as a message to the external processor that
400 	 * has to act upon it, and that processor may still have buffered
401 	 * data in its own FIFO.  In this case the expected delay may be
402 	 * too large and too variable (depending on the details of how the
403 	 * USB device is integrated with its symbiont hardware) to be put
404 	 * in the driver.
405 	 *
406 	 * So, somehow, we have to introduce a delay after tcsatattr()
407 	 * returns sufficient to allow *any* device to settle.  On the other
408 	 * hand, a really long delay will make gpsd device registration
409 	 * unpleasantly laggy.
410 	 *
411 	 * The classic way to address this is with a tcflush(), counting
412 	 * on it to clear the device FIFO. But that call may clear only the
413 	 * kernel buffers, not the device's hardware FIFO, so it may not
414 	 * be sufficient by itself.
415 	 *
416 	 * flush followed by a 200-millisecond delay followed by flush has
417 	 * been found to work reliably on the pl2303.  It is also known
418 	 * from testing that a 100-millisec delay is too short, allowing
419 	 * occasional failure to lock.
420 	 */
421 	(void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH);
422 
423         /* wait 200,000 uSec */
424 	delay.tv_sec = 0;
425 	delay.tv_nsec = 200000000L;
426 	nanosleep(&delay, NULL);
427 	(void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH);
428     }
429     GPSD_LOG(LOG_INF, &session->context->errout,
430 	     "SER: speed %lu, %d%c%d\n",
431 	     (unsigned long)gpsd_get_speed(session), 9 - stopbits, parity,
432 	     stopbits);
433 
434     session->gpsdata.dev.baudrate = (unsigned int)speed;
435     session->gpsdata.dev.parity = parity;
436     session->gpsdata.dev.stopbits = stopbits;
437 
438     /*
439      * The device might need a wakeup string before it will send data.
440      * If we don't know the device type, ship it every driver's wakeup
441      * in hopes it will respond.  But not to USB or Bluetooth, because
442      * shipping probe strings to unknown USB serial adaptors or
443      * Bluetooth devices may spam devices that aren't GPSes at all and
444      * could become confused.
445      */
446     if (!session->context->readonly
447 		&& session->sourcetype != source_usb
448 		&& session->sourcetype != source_bluetooth) {
449 	if (isatty(session->gpsdata.gps_fd) != 0
450 	    && !session->context->readonly) {
451 	    if (session->device_type == NULL) {
452 		const struct gps_type_t **dp;
453 		for (dp = gpsd_drivers; *dp; dp++)
454 		    if ((*dp)->event_hook != NULL)
455 			(*dp)->event_hook(session, event_wakeup);
456 	    } else if (session->device_type->event_hook != NULL)
457 		session->device_type->event_hook(session, event_wakeup);
458 	}
459     }
460     packet_reset(&session->lexer);
461 }
462 
gpsd_serial_open(struct gps_device_t * session)463 int gpsd_serial_open(struct gps_device_t *session)
464 /* open a device for access to its data
465  * return: the opened file descriptor
466  *         PLACEHOLDING_FD - for /dev/ppsX
467  *         UNALLOCATED_FD - for open failure
468  */
469 
470 {
471     mode_t mode = (mode_t) O_RDWR;
472 
473     session->sourcetype = gpsd_classify(session->gpsdata.dev.path);
474     session->servicetype = service_sensor;
475 
476     /* we may need to hold on to this slot without opening the device */
477     if (source_pps == session->sourcetype) {
478 	(void)gpsd_switch_driver(session, "PPS");
479 	return PLACEHOLDING_FD;
480     }
481 
482     if (session->context->readonly
483 	|| (session->sourcetype <= source_blockdev)) {
484 	mode = (mode_t) O_RDONLY;
485 	GPSD_LOG(LOG_INF, &session->context->errout,
486 		 "SER: opening read-only GPS data source type %d and at '%s'\n",
487 		 (int)session->sourcetype, session->gpsdata.dev.path);
488     } else {
489 	GPSD_LOG(LOG_INF, &session->context->errout,
490 		 "SER: opening GPS data source type %d at '%s'\n",
491 		 (int)session->sourcetype, session->gpsdata.dev.path);
492     }
493 #ifdef ENABLE_BLUEZ
494     if (bachk(session->gpsdata.dev.path) == 0) {
495         struct sockaddr_rc addr = { 0, *BDADDR_ANY, 0};
496         session->gpsdata.gps_fd = socket(AF_BLUETOOTH,
497 					 SOCK_STREAM,
498 					 BTPROTO_RFCOMM);
499         addr.rc_family = AF_BLUETOOTH;
500         addr.rc_channel = (uint8_t) 1;
501         (void) str2ba(session->gpsdata.dev.path, &addr.rc_bdaddr);
502         if (connect(session->gpsdata.gps_fd, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
503 	    if (errno != EINPROGRESS && errno != EAGAIN) {
504 		(void)close(session->gpsdata.gps_fd);
505 		GPSD_LOG(LOG_ERROR, &session->context->errout,
506 			 "SER: bluetooth socket connect failed: %s\n",
507 			 strerror(errno));
508 		return UNALLOCATED_FD;
509 	    }
510 	    GPSD_LOG(LOG_ERROR, &session->context->errout,
511 		     "SER: bluetooth socket connect in progress or again : %s\n",
512 		     strerror(errno));
513         }
514 	(void)fcntl(session->gpsdata.gps_fd, F_SETFL, (int)mode);
515 	GPSD_LOG(LOG_PROG, &session->context->errout,
516 		 "SER: bluez device open success: %s %s\n",
517 		 session->gpsdata.dev.path, strerror(errno));
518     } else
519 #endif /* BLUEZ */
520     {
521 	/*
522 	 * We open with O_NONBLOCK because we want to not get hung if
523 	 * the CLOCAL flag is off.  Need to keep O_NONBLOCK so the main
524          * loop does not clock on an unresponsive read() from a receiver.
525 	 */
526 	errno = 0;
527         if ((session->gpsdata.gps_fd =
528 	     open(session->gpsdata.dev.path,
529                   (int)(mode | O_NONBLOCK | O_NOCTTY))) == -1) {
530             GPSD_LOG(LOG_ERROR, &session->context->errout,
531 		     "SER: device open of %s failed: %s - "
532                      "retrying read-only\n",
533 		     session->gpsdata.dev.path,
534 		     strerror(errno));
535 	    if ((session->gpsdata.gps_fd =
536 		 open(session->gpsdata.dev.path,
537                       O_RDONLY | O_NONBLOCK | O_NOCTTY)) == -1) {
538 		GPSD_LOG(LOG_ERROR, &session->context->errout,
539 			 "SER: read-only device open of %s failed: %s\n",
540 			 session->gpsdata.dev.path,
541 			 strerror(errno));
542 		return UNALLOCATED_FD;
543 	    }
544 
545 	    GPSD_LOG(LOG_PROG, &session->context->errout,
546 		     "SER: file device open of %s succeeded\n",
547 		     session->gpsdata.dev.path);
548 	}
549     }
550 
551     /*
552      * Ideally we want to exclusion-lock the device before doing any reads.
553      * It would have been best to do this at open(2) time, but O_EXCL
554      * doesn't work without O_CREAT.
555      *
556      * We have to make an exception for ptys, which are intentionally
557      * opened by another process on the master side, otherwise we'll
558      * break all our regression tests.
559      *
560      * We also exclude bluetooth device because the bluetooth daemon opens them.
561      */
562     if (!(session->sourcetype == source_pty ||
563           session->sourcetype == source_bluetooth)) {
564 #ifdef TIOCEXCL
565 	/*
566 	 * Try to block other processes from using this device while we
567 	 * have it open (later opens should return EBUSY).  Won't work
568 	 * against anything with root privileges, alas.
569 	 */
570 	(void)ioctl(session->gpsdata.gps_fd, (unsigned long)TIOCEXCL);
571 #endif /* TIOCEXCL */
572 
573 #ifdef __linux__
574 	/*
575 	 * Don't touch devices already opened by another process.
576 	 */
577 	if (fusercount(session->gpsdata.dev.path) > 1) {
578             GPSD_LOG(LOG_ERROR, &session->context->errout,
579 		     "SER: %s already opened by another process\n",
580 		     session->gpsdata.dev.path);
581 	    (void)close(session->gpsdata.gps_fd);
582 	    session->gpsdata.gps_fd = UNALLOCATED_FD;
583 	    return UNALLOCATED_FD;
584 	}
585 #endif /* __linux__ */
586     }
587 
588     if (0 < session->context->fixed_port_speed) {
589         session->saved_baud = session->context->fixed_port_speed;
590     }
591 
592     if (session->saved_baud != -1) {
593 	(void)cfsetispeed(&session->ttyset, (speed_t)session->saved_baud);
594 	(void)cfsetospeed(&session->ttyset, (speed_t)session->saved_baud);
595         if (0 != tcsetattr(session->gpsdata.gps_fd, TCSANOW,
596                            &session->ttyset)) {
597 	    GPSD_LOG(LOG_ERROR, &session->context->errout,
598 		     "SER: Error setting port attributes: %s\n",
599 		     strerror(errno));
600 	}
601 	(void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH);
602     }
603 
604     session->lexer.type = BAD_PACKET;
605     if ( 0 != isatty(session->gpsdata.gps_fd) ) {
606         speed_t new_speed;
607         char new_parity;   // E, N, O
608         unsigned int new_stop;
609 
610 	/* Save original terminal parameters */
611 	if (tcgetattr(session->gpsdata.gps_fd, &session->ttyset_old) != 0)
612 	    return UNALLOCATED_FD;
613 	session->ttyset = session->ttyset_old;
614         /* twiddle the speed, parity, etc. but only on real serial ports */
615 	memset(session->ttyset.c_cc, 0, sizeof(session->ttyset.c_cc));
616 	//session->ttyset.c_cc[VTIME] = 1;
617 	/*
618 	 * Tip from Chris Kuethe: the FIDI chip used in the Trip-Nav
619 	 * 200 (and possibly other USB GPSes) gets completely hosed
620 	 * in the presence of flow control.  Thus, turn off CRTSCTS.
621 	 *
622 	 * This is not ideal.  Setting no parity here will mean extra
623 	 * initialization time for some devices, like certain Trimble
624 	 * boards, that want 7O2 or other non-8N1 settings. But starting
625 	 * the hunt loop at 8N1 will minimize the average sync time
626 	 * over all devices.
627 	 */
628 	session->ttyset.c_cflag &= ~(PARENB | PARODD | CRTSCTS | CSTOPB);
629 	session->ttyset.c_cflag |= CREAD | CLOCAL;
630 	session->ttyset.c_iflag = session->ttyset.c_oflag =
631 	    session->ttyset.c_lflag = (tcflag_t) 0;
632 
633 	session->baudindex = 0;
634         if (0 < session->context->fixed_port_speed) {
635             new_speed = session->context->fixed_port_speed;
636         } else {
637             new_speed = gpsd_get_speed_old(session);
638         }
639         if ('\0' == session->context->fixed_port_framing[0]) {
640             new_parity = 'N';
641             new_stop = 1;
642         } else {
643             // ignore length, stopbits=2 forces length 7.
644             new_parity = session->context->fixed_port_framing[1];
645             new_stop = session->context->fixed_port_framing[2] - '0';
646         }
647         gpsd_set_speed(session, new_speed, new_parity, new_stop);
648     }
649 
650     /* Used to turn off O_NONBLOCK here, but best not to block trying
651      * to read from an unresponsive receiver. */
652 
653     /* required so parity field won't be '\0' if saved speed matches */
654     if (session->sourcetype <= source_blockdev) {
655 	session->gpsdata.dev.parity = 'N';
656 	session->gpsdata.dev.stopbits = 1;
657     }
658 
659     GPSD_LOG(LOG_SPIN, &session->context->errout,
660 	     "SER: open(%s) -> %d in gpsd_serial_open()\n",
661 	     session->gpsdata.dev.path, session->gpsdata.gps_fd);
662     return session->gpsdata.gps_fd;
663 }
664 
gpsd_serial_write(struct gps_device_t * session,const char * buf,const size_t len)665 ssize_t gpsd_serial_write(struct gps_device_t * session,
666 			  const char *buf, const size_t len)
667 {
668     ssize_t status;
669     bool ok;
670     if (session == NULL ||
671 	session->context == NULL || session->context->readonly)
672 	return 0;
673     status = write(session->gpsdata.gps_fd, buf, len);
674     ok = (status == (ssize_t) len);
675     (void)tcdrain(session->gpsdata.gps_fd);
676     /* extra guard prevents expensive hexdump calls */
677     if (session->context->errout.debug >= LOG_IO) {
678 	char scratchbuf[MAX_PACKET_LENGTH*2+1];
679 	GPSD_LOG(LOG_IO, &session->context->errout,
680 		 "SER: => GPS: %s%s\n",
681 		 gpsd_packetdump(scratchbuf, sizeof(scratchbuf),
682 				 (char *)buf, len), ok ? "" : " FAILED");
683     }
684     return status;
685 }
686 
687 /*
688  * This constant controls how long the packet sniffer will spend looking
689  * for a packet leader before it gives up.  It *must* be larger than
690  * MAX_PACKET_LENGTH or we risk never syncing up at all.  Large values
691  * will produce annoying startup lag.
692  */
693 #define SNIFF_RETRIES	(MAX_PACKET_LENGTH + 128)
694 
695 /* advance to the next hunt setting  */
gpsd_next_hunt_setting(struct gps_device_t * session)696 bool gpsd_next_hunt_setting(struct gps_device_t * session)
697 {
698     /* every rate we're likely to see on an old GPS */
699     // FIXME add new rates
700     static unsigned int rates[] =
701         {0, 4800, 9600, 19200, 38400, 57600, 115200, 230400};
702 
703     char new_parity;   // E, N, O
704     unsigned int new_stop;
705 
706     /* don't waste time in the hunt loop if this is not actually a tty */
707     if (0 == isatty(session->gpsdata.gps_fd))
708 	return false;
709 
710     /* ...or if it's nominally a tty but delivers only PPS and no data */
711     if (session->sourcetype == source_pps)
712 	return false;
713 
714     if (session->lexer.retry_counter++ >= SNIFF_RETRIES) {
715         if (0 < session->context->fixed_port_speed) {
716             //  fixed speed, don't hunt
717             //  this prevents framing hunt?
718             return false;
719         }
720 
721         if (session->baudindex++ >=
722             (unsigned int)(sizeof(rates) / sizeof(rates[0])) - 1) {
723 
724             session->baudindex = 0;
725             if ('\0' != session->context->fixed_port_framing[0]) {
726                 return false;	/* hunt is over, no sync */
727             }
728 
729             // More stop bits to try?
730             if (session->gpsdata.dev.stopbits++ >= 2) {
731                 return false;	/* hunt is over, no sync */
732             }
733         }
734 
735         if ('\0' == session->context->fixed_port_framing[0]) {
736             new_parity = session->gpsdata.dev.parity;
737             new_stop = session->gpsdata.dev.stopbits;
738         } else {
739             // ignore length, stopbits=2 forces length 7.
740             new_parity = session->context->fixed_port_framing[1];
741             new_stop = session->context->fixed_port_framing[2] - '0';
742         }
743 
744         gpsd_set_speed(session, rates[session->baudindex], new_parity,
745                        new_stop);
746         session->lexer.retry_counter = 0;
747     }
748     return true;		/* keep hunting */
749 }
750 
gpsd_assert_sync(struct gps_device_t * session)751 void gpsd_assert_sync(struct gps_device_t *session)
752 /* to be called when we want to register that we've synced with a device */
753 {
754     /*
755      * We've achieved first sync with the device. Remember the
756      * baudrate so we can try it first next time this device
757      * is opened.
758      */
759     if (session->saved_baud == -1)
760 	session->saved_baud = (int)cfgetispeed(&session->ttyset);
761 }
762 
gpsd_close(struct gps_device_t * session)763 void gpsd_close(struct gps_device_t *session)
764 {
765     if (!BAD_SOCKET(session->gpsdata.gps_fd)) {
766 #ifdef TIOCNXCL
767 	(void)ioctl(session->gpsdata.gps_fd, (unsigned long)TIOCNXCL);
768 #endif /* TIOCNXCL */
769 	if (!session->context->readonly)
770 		(void)tcdrain(session->gpsdata.gps_fd);
771 	if (isatty(session->gpsdata.gps_fd) != 0) {
772 	    /* force hangup on close on systems that don't do HUPCL properly */
773 	    (void)cfsetispeed(&session->ttyset, (speed_t) B0);
774 	    (void)cfsetospeed(&session->ttyset, (speed_t) B0);
775 	    (void)tcsetattr(session->gpsdata.gps_fd, TCSANOW,
776 			    &session->ttyset);
777 	}
778 	/* this is the clean way to do it */
779 	session->ttyset_old.c_cflag |= HUPCL;
780 	/*
781 	 * Don't revert the serial parameters if we didn't have to mess with
782 	 * them the first time.  Economical, and avoids tripping over an
783 	 * obscure Linux 2.6 kernel bug that disables threaded
784 	 * ioctl(TIOCMWAIT) on a device after tcsetattr() is called.
785 	 */
786 	if ((cfgetispeed(&session->ttyset_old) !=
787              cfgetispeed(&session->ttyset)) ||
788             ((session->ttyset_old.c_cflag & CSTOPB) !=
789              (session->ttyset.c_cflag & CSTOPB))) {
790 	    /*
791 	     * If we revert, keep the most recent baud rate.
792 	     * Cuts down on autobaud overhead the next time.
793 	     */
794 	    (void)cfsetispeed(&session->ttyset_old,
795 			      (speed_t) session->gpsdata.dev.baudrate);
796 	    (void)cfsetospeed(&session->ttyset_old,
797 			      (speed_t) session->gpsdata.dev.baudrate);
798 	    (void)tcsetattr(session->gpsdata.gps_fd, TCSANOW,
799 			    &session->ttyset_old);
800 	}
801 	GPSD_LOG(LOG_SPIN, &session->context->errout,
802 		 "SER: close(%d) in gpsd_close(%s)\n",
803 		 session->gpsdata.gps_fd, session->gpsdata.dev.path);
804 	(void)close(session->gpsdata.gps_fd);
805 	session->gpsdata.gps_fd = -1;
806     }
807 }
808