2 * This file is Copyright (c) 2010 by the GPSD project
3 * BSD terms apply: see the file COPYING in the distribution root for details.
9 #endif /* S_SPLINT_S */
15 #include "gpsd_config.h"
18 #include <sys/types.h>
20 #include <sys/socket.h>
22 #include <bluetooth/bluetooth.h>
23 #include <bluetooth/hci.h>
24 #include <bluetooth/hci_lib.h>
25 #include <bluetooth/rfcomm.h>
28 #if defined(HAVE_SYS_MODEM_H)
29 #include <sys/modem.h>
30 #endif /* HAVE_SYS_MODEM_H */
34 /* Workaround for HP-UX 11.23, which is missing CRTSCTS */
37 # define CRTSCTS CNEW_RTSCTS
40 # endif /* CNEW_RTSCTS */
43 static sourcetype_t gpsd_classify(const char *path)
44 /* figure out what kind of device we're looking at */
48 if (stat(path, &sb) == -1)
49 return source_unknown;
50 else if (S_ISREG(sb.st_mode))
51 return source_blockdev;
52 /* this assumes we won't get UDP from a filesystem socket */
53 else if (S_ISSOCK(sb.st_mode))
55 else if (S_ISCHR(sb.st_mode)) {
56 sourcetype_t devtype = source_unknown;
58 /* Linux major device numbers live here
59 * ftp://ftp.kernel.org/pub/linux/docs/device-list/devices-2.6+.txt
61 int devmajor = major(sb.st_rdev);
63 devtype = source_rs232;
64 else if (devmajor == 188)
66 else if (devmajor == 216 || devtype == 217)
67 devtype = source_bluetooth;
68 else if (devmajor == 3 || (devmajor >= 136 && devmajor <= 143))
70 #endif /* __linux__ */
73 return source_unknown;
76 void gpsd_tty_init(struct gps_device_t *session)
77 /* to be called on allocating a device */
79 /* mark GPS fd closed and its baud rate unknown */
80 session->gpsdata.gps_fd = -1;
81 session->saved_baud = -1;
83 /* mark NTPD shared memory segments as unused */
84 session->shmindex = -1;
86 session->shmTimeP = -1;
87 # endif /* PPS_ENABLE */
88 #endif /* NTPSHM_ENABLE */
89 session->zerokill = false;
93 #if defined(__CYGWIN__)
94 /* Workaround for Cygwin, which is missing cfmakeraw */
95 /* Pasted from man page; added in serial.c arbitrarily */
96 void cfmakeraw(struct termios *termios_p)
99 ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
100 termios_p->c_oflag &= ~OPOST;
101 termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
102 termios_p->c_cflag &= ~(CSIZE | PARENB);
103 termios_p->c_cflag |= CS8;
105 #endif /* defined(__CYGWIN__) */
107 speed_t gpsd_get_speed(const struct termios *ttyctl)
109 speed_t code = cfgetospeed(ttyctl);
134 bool gpsd_set_raw(struct gps_device_t * session)
136 (void)cfmakeraw(&session->ttyset);
137 if (tcsetattr(session->gpsdata.gps_fd, TCIOFLUSH, &session->ttyset) == -1) {
138 gpsd_report(LOG_ERROR,
139 "error changing port attributes: %s\n", strerror(errno));
146 void gpsd_set_speed(struct gps_device_t *session,
147 speed_t speed, char parity, unsigned int stopbits)
152 * Yes, you can set speeds that aren't in the hunt loop. If you
153 * do this, and you aren't on Linux where baud rate is preserved
154 * across port closings, you've screwed yourself. Don't do that!
158 else if (speed < 1200)
160 else if (speed < 2400)
162 else if (speed < 4800)
164 else if (speed < 9600)
166 else if (speed < 19200)
168 else if (speed < 38400)
170 else if (speed < 57600)
172 else if (speed < 115200)
177 if (rate != cfgetispeed(&session->ttyset)
178 || parity != session->gpsdata.dev.parity
179 || stopbits != session->gpsdata.dev.stopbits) {
182 * Don't mess with this conditional! Speed zero is supposed to mean
183 * to leave the port speed at whatever it currently is. This leads
184 * to excellent behavior on Linux, which preserves baudrate across
185 * serial device closes - it means that if you've opended this
186 * device before you typically don't have to hunt at all because
187 * it's still at the same speed you left it - you'll typically
188 * get packet lock within 1.5 seconds. Alas, the BSDs and OS X
193 (void)cfsetispeed(&session->ttyset, rate);
194 (void)cfsetospeed(&session->ttyset, rate);
197 session->ttyset.c_iflag &= ~(PARMRK | INPCK);
198 session->ttyset.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD);
199 session->ttyset.c_cflag |= (stopbits == 2 ? CS7 | CSTOPB : CS8);
203 session->ttyset.c_iflag |= INPCK;
204 session->ttyset.c_cflag |= PARENB;
208 session->ttyset.c_iflag |= INPCK;
209 session->ttyset.c_cflag |= PARENB | PARODD;
212 if (tcsetattr(session->gpsdata.gps_fd, TCSANOW, &session->ttyset) !=
217 * Serious black magic begins here. Getting this code wrong can cause
218 * failures to lock to a correct speed, and not clean reproducible
219 * failures but flukey hardware- and timing-dependent ones. So
220 * be very sure you know what you're doing before hacking it, and
223 * The fundamental problem here is that serial devices take time
224 * to settle into a new baud rate after tcsetattr() is issued. Until
225 * they do so, input will be arbitarily garbled. Normally this
226 * is not a big problem, but in our hunt loop the garbling can trash
227 * a long enough prefix of each sample to prevent detection of a
228 * packet header. We could address the symptom by making the sample
229 * size enough larger that subtracting the maximum length of garble
230 * would still leave a sample longer than the maximum packet size.
231 * But it's better (and more efficient) to address the disease.
233 * In theory, one might think that not even a tcflush() call would
234 * be needed, with tcsetattr() delaying its return until the device
235 * is in a good state. For simple devices like a 14550 UART that
236 * have fixed response timings this may even work, if the driver
237 * writer was smart enough to delay the return by the right number
238 * of milliseconds after poking the device port(s).
240 * Problems may arise if the driver's timings are off. Or we may
241 * be talking to a USB device like the pl2303 commonly used in GPS
242 * mice; on these, the change will not happen immediately because
243 * it has to be sent as a message to the external processor that
244 * has to act upon it, and that processor may still have buffered
245 * data in its own FIFO. In this case the expected delay may be
246 * too large and too variable (depending on the details of how the
247 * USB device is integrated with its symbiont hardware) to be put
250 * So, somehow, we have to introduce a delay after tcsatattr()
251 * returns sufficient to allow *any* device to settle. On the other
252 * hand, a really long delay will make gpsd device registration
253 * unpleasantly laggy.
255 * The classic way to address this is with a tcflush(), counting
256 * on it to clear the device FIFO. But that call may clear only the
257 * kernel buffers, not the device's hardware FIFO, so it may not
258 * be sufficient by itself.
260 * flush followed by a 200-millisecond delay followed by flush has
261 * been found to work reliably on the pl2303. It is also known
262 * from testing that a 100-millisec delay is too short, allowing
263 * occasional failure to lock.
265 (void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH);
266 (void)usleep(200000);
267 (void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH);
269 gpsd_report(LOG_INF, "speed %u, %d%c%d\n",
270 gpsd_get_speed(&session->ttyset), 9 - stopbits, parity,
273 session->gpsdata.dev.baudrate = (unsigned int)speed;
274 session->gpsdata.dev.parity = parity;
275 session->gpsdata.dev.stopbits = stopbits;
277 if (!session->context->readonly) {
279 * The device might need a wakeup string before it will send data.
280 * If we don't know the device type, ship it every driver's wakeup
281 * in hopes it will respond.
283 if (isatty(session->gpsdata.gps_fd) != 0
284 && !session->context->readonly) {
285 const struct gps_type_t **dp;
286 if (session->device_type == NULL) {
287 for (dp = gpsd_drivers; *dp; dp++)
288 if ((*dp)->event_hook != NULL)
289 (*dp)->event_hook(session, event_wakeup);
290 } else if (session->device_type->event_hook != NULL)
291 session->device_type->event_hook(session, event_wakeup);
294 packet_reset(&session->packet);
297 int gpsd_open(struct gps_device_t *session)
299 mode_t mode = (mode_t) O_RDWR;
301 session->sourcetype = gpsd_classify(session->gpsdata.dev.path);
303 /*@ -boolops -type @*/
304 if (session->context->readonly
305 || (session->sourcetype <= source_blockdev)) {
306 mode = (mode_t) O_RDONLY;
308 "opening read-only GPS data source type %d and at '%s'\n",
309 (int)session->sourcetype, session->gpsdata.dev.path);
312 "opening GPS data source type %d at '%s'\n",
313 (int)session->sourcetype, session->gpsdata.dev.path);
315 /*@ +boolops +type @*/
317 if (bachk(session->gpsdata.dev.path) == 0) {
318 session->gpsdata.gps_fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
319 struct sockaddr_rc addr = { 0 };
320 addr.rc_family = AF_BLUETOOTH;
321 addr.rc_channel = (uint8_t) 1;
322 str2ba(session->gpsdata.dev.path, &addr.rc_bdaddr);
323 if (connect(session->gpsdata.gps_fd, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
324 if (errno != EINPROGRESS && errno != EAGAIN) {
325 gpsd_report(LOG_ERROR, "bluetooth socket connect failed: %s\n",
329 gpsd_report(LOG_ERROR, "bluetooth socket connect in progress or again : %s\n",
332 (void)fcntl(session->gpsdata.gps_fd, F_SETFL, (int)mode | O_NONBLOCK);
333 gpsd_report(LOG_PROG, "bluez device open success: %s %s\n",
334 session->gpsdata.dev.path, strerror(errno));
338 if ((session->gpsdata.gps_fd =
339 open(session->gpsdata.dev.path,
340 (int)(mode | O_NONBLOCK | O_NOCTTY))) == -1) {
341 gpsd_report(LOG_ERROR,
342 "device open failed: %s - retrying read-only\n",
344 if ((session->gpsdata.gps_fd =
345 open(session->gpsdata.dev.path,
346 O_RDONLY | O_NONBLOCK | O_NOCTTY)) == -1) {
347 gpsd_report(LOG_ERROR, "read-only device open failed: %s\n",
351 gpsd_report(LOG_PROG, "file device open success: %s\n",
356 #ifdef FIXED_PORT_SPEED
357 session->saved_baud = FIXED_PORT_SPEED;
360 if (session->saved_baud != -1) {
362 cfsetispeed(&session->ttyset, (speed_t) session->saved_baud);
364 cfsetospeed(&session->ttyset, (speed_t) session->saved_baud);
365 (void)tcsetattr(session->gpsdata.gps_fd, TCSANOW, &session->ttyset);
366 (void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH);
369 session->packet.type = BAD_PACKET;
370 if (isatty(session->gpsdata.gps_fd) != 0) {
371 /* Save original terminal parameters */
372 if (tcgetattr(session->gpsdata.gps_fd, &session->ttyset_old) != 0)
374 (void)memcpy(&session->ttyset,
375 &session->ttyset_old, sizeof(session->ttyset));
377 * Only block until we get at least one character, whatever the
378 * third arg of read(2) says.
381 memset(session->ttyset.c_cc, 0, sizeof(session->ttyset.c_cc));
382 session->ttyset.c_cc[VMIN] = 1;
385 * Tip from Chris Kuethe: the FIDI chip used in the Trip-Nav
386 * 200 (and possibly other USB GPSes) gets completely hosed
387 * in the presence of flow control. Thus, turn off CRTSCTS.
389 session->ttyset.c_cflag &= ~(PARENB | PARODD | CRTSCTS);
390 session->ttyset.c_cflag |= CREAD | CLOCAL;
391 session->ttyset.c_iflag = session->ttyset.c_oflag =
392 session->ttyset.c_lflag = (tcflag_t) 0;
394 session->baudindex = 0;
395 gpsd_set_speed(session, gpsd_get_speed(&session->ttyset_old), 'N', 1);
397 session->is_serial = true;
398 gpsd_report(LOG_SPIN, "open(%s) -> %d in gpsd_open()\n",
399 session->gpsdata.dev.path, session->gpsdata.gps_fd);
400 return session->gpsdata.gps_fd;
403 ssize_t gpsd_write(struct gps_device_t * session, void const *buf, size_t len)
407 if (session == NULL ||
408 session->context == NULL || session->context->readonly)
410 status = write(session->gpsdata.gps_fd, buf, len);
411 ok = (status == (ssize_t) len);
412 (void)tcdrain(session->gpsdata.gps_fd);
413 /* no test here now, always print as hex */
414 gpsd_report(LOG_IO, "=> GPS: %s%s\n",
415 gpsd_hexdump_wrapper(buf, len, LOG_IO), ok ? "" : " FAILED");
420 * This constant controls how long the packet sniffer will spend looking
421 * for a packet leader before it gives up. It *must* be larger than
422 * MAX_PACKET_LENGTH or we risk never syncing up at all. Large values
423 * will produce annoying startup lag.
425 #define SNIFF_RETRIES 256
427 bool gpsd_next_hunt_setting(struct gps_device_t * session)
428 /* advance to the next hunt setting */
430 #ifdef FIXED_PORT_SPEED
431 /* just the one fixed port speed... */
432 static unsigned int rates[] = { FIXED_PORT_SPEED };
433 #else /* FIXED_PORT_SPEED not defined */
434 /* every rate we're likely to see on a GPS */
435 static unsigned int rates[] =
436 { 0, 4800, 9600, 19200, 38400, 57600, 115200 };
437 #endif /* FIXED_PORT_SPEED defined */
439 /* don't waste time in the hunt loop if this is not actually a tty */
440 if (isatty(session->gpsdata.gps_fd) == 0)
443 if (session->packet.retry_counter++ >= SNIFF_RETRIES) {
444 session->packet.retry_counter = 0;
445 if (session->baudindex++ >=
446 (unsigned int)(sizeof(rates) / sizeof(rates[0])) - 1) {
447 session->baudindex = 0;
448 if (session->gpsdata.dev.stopbits++ >= 2)
449 return false; /* hunt is over, no sync */
451 gpsd_set_speed(session,
452 rates[session->baudindex],
453 session->gpsdata.dev.parity,
454 session->gpsdata.dev.stopbits);
457 return true; /* keep hunting */
461 void gpsd_assert_sync(struct gps_device_t *session)
462 /* to be called when we want to register that we've synced with a device */
465 * We've achieved first sync with the device. Remember the
466 * baudrate so we can try it first next time this device
469 if (session->saved_baud == -1)
470 session->saved_baud = (int)cfgetispeed(&session->ttyset);
474 * Now is the right time to grab the shared memory segment(s)
475 * to communicate the navigation message derived and (possibly)
476 * 1pps derived time data to ntpd.
479 /* do not start more than one ntp thread */
480 if (!(session->shmindex >= 0))
481 ntpd_link_activate(session);
483 gpsd_report(LOG_INF, "NTPD ntpd_link_activate: %d\n",
484 (int)session->shmindex >= 0);
486 #endif /* NTPSHM_ENABLE */
489 void gpsd_close(struct gps_device_t *session)
491 if (session->gpsdata.gps_fd != -1) {
492 (void)tcdrain(session->gpsdata.gps_fd);
493 if (isatty(session->gpsdata.gps_fd) != 0) {
494 /* force hangup on close on systems that don't do HUPCL properly */
496 (void)cfsetispeed(&session->ttyset, (speed_t) B0);
497 (void)cfsetospeed(&session->ttyset, (speed_t) B0);
499 (void)tcsetattr(session->gpsdata.gps_fd, TCSANOW,
502 /* this is the clean way to do it */
503 session->ttyset_old.c_cflag |= HUPCL;
504 /* keep the most recent baud rate */
506 (void)cfsetispeed(&session->ttyset_old,
507 (speed_t) session->gpsdata.dev.baudrate);
508 (void)cfsetospeed(&session->ttyset_old,
509 (speed_t) session->gpsdata.dev.baudrate);
511 (void)tcsetattr(session->gpsdata.gps_fd, TCSANOW,
512 &session->ttyset_old);
513 gpsd_report(LOG_SPIN, "close(%d) in gpsd_close(%s)\n",
514 session->gpsdata.gps_fd, session->gpsdata.dev.path);
515 (void)close(session->gpsdata.gps_fd);
516 session->gpsdata.gps_fd = -1;