cleanup specfile for packaging
[profile/ivi/gpsd.git] / serial.c
1 /*
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.
4  */
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #ifndef S_SPLINT_S
8 #include <unistd.h>
9 #endif /* S_SPLINT_S */
10 #include <string.h>
11 #include <fcntl.h>
12 #include <errno.h>
13 #include <ctype.h>
14
15 #include "gpsd_config.h"
16
17 #ifdef HAVE_BLUEZ
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/socket.h>
21
22 #include <bluetooth/bluetooth.h>
23 #include <bluetooth/hci.h>
24 #include <bluetooth/hci_lib.h>
25 #include <bluetooth/rfcomm.h>
26 #endif
27
28 #if defined(HAVE_SYS_MODEM_H)
29 #include <sys/modem.h>
30 #endif /* HAVE_SYS_MODEM_H */
31
32 #include "gpsd.h"
33
34 /* Workaround for HP-UX 11.23, which is missing CRTSCTS */
35 #ifndef CRTSCTS
36 #  ifdef CNEW_RTSCTS
37 #    define CRTSCTS CNEW_RTSCTS
38 #  else
39 #    define CRTSCTS 0
40 #  endif /* CNEW_RTSCTS */
41 #endif /* !CRTSCTS */
42
43 static sourcetype_t gpsd_classify(const char *path)
44 /* figure out what kind of device we're looking at */
45 {
46     struct stat sb;
47
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))
54         return source_tcp;
55     else if (S_ISCHR(sb.st_mode)) {
56         sourcetype_t devtype = source_unknown;
57 #ifdef __linux__
58         /* Linux major device numbers live here
59          * ftp://ftp.kernel.org/pub/linux/docs/device-list/devices-2.6+.txt
60          */
61         int devmajor = major(sb.st_rdev);
62         if (devmajor == 4)
63             devtype = source_rs232;
64         else if (devmajor == 188)
65             devtype = source_usb;
66         else if (devmajor == 216 || devtype == 217)
67             devtype = source_bluetooth;
68         else if (devmajor == 3 || (devmajor >= 136 && devmajor <= 143))
69             devtype = source_pty;
70 #endif /* __linux__ */
71         return devtype;
72     } else
73         return source_unknown;
74 }
75
76 void gpsd_tty_init(struct gps_device_t *session)
77 /* to be called on allocating a device */
78 {
79     /* mark GPS fd closed and its baud rate unknown */
80     session->gpsdata.gps_fd = -1;
81     session->saved_baud = -1;
82 #ifdef NTPSHM_ENABLE
83     /* mark NTPD shared memory segments as unused */
84     session->shmindex = -1;
85 # ifdef PPS_ENABLE
86     session->shmTimeP = -1;
87 # endif /* PPS_ENABLE */
88 #endif /* NTPSHM_ENABLE */
89     session->zerokill = false;
90     session->reawake = 0;
91 }
92
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)
97 {
98     termios_p->c_iflag &=
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;
104 }
105 #endif /* defined(__CYGWIN__) */
106
107 speed_t gpsd_get_speed(const struct termios *ttyctl)
108 {
109     speed_t code = cfgetospeed(ttyctl);
110     switch (code) {
111     case B0:
112         return (0);
113     case B300:
114         return (300);
115     case B1200:
116         return (1200);
117     case B2400:
118         return (2400);
119     case B4800:
120         return (4800);
121     case B9600:
122         return (9600);
123     case B19200:
124         return (19200);
125     case B38400:
126         return (38400);
127     case B57600:
128         return (57600);
129     default:
130         return (115200);
131     }
132 }
133
134 bool gpsd_set_raw(struct gps_device_t * session)
135 {
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));
140         return false;
141     }
142
143     return true;
144 }
145
146 void gpsd_set_speed(struct gps_device_t *session,
147                     speed_t speed, char parity, unsigned int stopbits)
148 {
149     speed_t rate;
150
151     /*
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!
155      */
156     if (speed < 300)
157         rate = B0;
158     else if (speed < 1200)
159         rate = B300;
160     else if (speed < 2400)
161         rate = B1200;
162     else if (speed < 4800)
163         rate = B2400;
164     else if (speed < 9600)
165         rate = B4800;
166     else if (speed < 19200)
167         rate = B9600;
168     else if (speed < 38400)
169         rate = B19200;
170     else if (speed < 57600)
171         rate = B38400;
172     else if (speed < 115200)
173         rate = B57600;
174     else
175         rate = B115200;
176
177     if (rate != cfgetispeed(&session->ttyset)
178         || parity != session->gpsdata.dev.parity
179         || stopbits != session->gpsdata.dev.stopbits) {
180
181         /* 
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
189          * aren't so nice.
190          */
191         /*@ignore@*/
192         if (rate != B0) {
193             (void)cfsetispeed(&session->ttyset, rate);
194             (void)cfsetospeed(&session->ttyset, rate);
195         }
196         /*@end@*/
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);
200         switch (parity) {
201         case 'E':
202         case (char)2:
203             session->ttyset.c_iflag |= INPCK;
204             session->ttyset.c_cflag |= PARENB;
205             break;
206         case 'O':
207         case (char)1:
208             session->ttyset.c_iflag |= INPCK;
209             session->ttyset.c_cflag |= PARENB | PARODD;
210             break;
211         }
212         if (tcsetattr(session->gpsdata.gps_fd, TCSANOW, &session->ttyset) !=
213             0)
214             return;
215
216         /*
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
221          * test thoroughly.
222          *
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.
232          *
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).
239          *
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
248          * in the driver.
249          *
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.
254          *
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.
259          *
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.
264          */
265         (void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH);
266         (void)usleep(200000);
267         (void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH);
268     }
269     gpsd_report(LOG_INF, "speed %u, %d%c%d\n",
270                 gpsd_get_speed(&session->ttyset), 9 - stopbits, parity,
271                 stopbits);
272
273     session->gpsdata.dev.baudrate = (unsigned int)speed;
274     session->gpsdata.dev.parity = parity;
275     session->gpsdata.dev.stopbits = stopbits;
276
277     if (!session->context->readonly) {
278         /*
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.
282          */
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);
292         }
293     }
294     packet_reset(&session->packet);
295 }
296
297 int gpsd_open(struct gps_device_t *session)
298 {
299     mode_t mode = (mode_t) O_RDWR;
300
301     session->sourcetype = gpsd_classify(session->gpsdata.dev.path);
302
303     /*@ -boolops -type @*/
304     if (session->context->readonly
305         || (session->sourcetype <= source_blockdev)) {
306         mode = (mode_t) O_RDONLY;
307         gpsd_report(LOG_INF,
308                     "opening read-only GPS data source type %d and at '%s'\n",
309                     (int)session->sourcetype, session->gpsdata.dev.path);
310     } else {
311         gpsd_report(LOG_INF,
312                     "opening GPS data source type %d at '%s'\n",
313                     (int)session->sourcetype, session->gpsdata.dev.path);
314     }
315     /*@ +boolops +type @*/
316 #ifdef HAVE_BLUEZ
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",
326                             strerror(errno));
327                 return -1;
328             }
329             gpsd_report(LOG_ERROR, "bluetooth socket connect in progress or again : %s\n",
330                         strerror(errno));
331         }
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));
335     } else 
336 #endif /* BLUEZ */
337     {
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",
343                             strerror(errno));
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",
348                                 strerror(errno));
349                 return -1;
350             }
351             gpsd_report(LOG_PROG, "file device open success: %s\n",
352                         strerror(errno));
353         }
354     }
355
356 #ifdef FIXED_PORT_SPEED
357     session->saved_baud = FIXED_PORT_SPEED;
358 #endif
359
360     if (session->saved_baud != -1) {
361         /*@i@*/ (void)
362             cfsetispeed(&session->ttyset, (speed_t) session->saved_baud);
363         /*@i@*/ (void)
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);
367     }
368
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)
373             return -1;
374         (void)memcpy(&session->ttyset,
375                      &session->ttyset_old, sizeof(session->ttyset));
376         /*
377          * Only block until we get at least one character, whatever the
378          * third arg of read(2) says.
379          */
380         /*@ ignore @*/
381         memset(session->ttyset.c_cc, 0, sizeof(session->ttyset.c_cc));
382         session->ttyset.c_cc[VMIN] = 1;
383         /*@ end @*/
384         /*
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.
388          */
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;
393
394         session->baudindex = 0;
395         gpsd_set_speed(session, gpsd_get_speed(&session->ttyset_old), 'N', 1);
396     }
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;
401 }
402
403 ssize_t gpsd_write(struct gps_device_t * session, void const *buf, size_t len)
404 {
405     ssize_t status;
406     bool ok;
407     if (session == NULL ||
408         session->context == NULL || session->context->readonly)
409         return 0;
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");
416     return status;
417 }
418
419 /*
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.
424  */
425 #define SNIFF_RETRIES   256
426
427 bool gpsd_next_hunt_setting(struct gps_device_t * session)
428 /* advance to the next hunt setting  */
429 {
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 */
438
439     /* don't waste time in the hunt loop if this is not actually a tty */
440     if (isatty(session->gpsdata.gps_fd) == 0)
441         return false;
442
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 */
450         }
451         gpsd_set_speed(session,
452                        rates[session->baudindex],
453                        session->gpsdata.dev.parity,
454                        session->gpsdata.dev.stopbits);
455     }
456
457     return true;                /* keep hunting */
458
459 }
460
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 */
463 {
464     /*
465      * We've achieved first sync with the device. Remember the
466      * baudrate so we can try it first next time this device
467      * is opened.
468      */
469     if (session->saved_baud == -1)
470         session->saved_baud = (int)cfgetispeed(&session->ttyset);
471
472 #ifdef NTPSHM_ENABLE
473     /*
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.
477      */
478
479     /* do not start more than one ntp thread */
480     if (!(session->shmindex >= 0))
481         ntpd_link_activate(session);
482
483     gpsd_report(LOG_INF, "NTPD ntpd_link_activate: %d\n",
484                 (int)session->shmindex >= 0);
485
486 #endif /* NTPSHM_ENABLE */
487 }
488
489 void gpsd_close(struct gps_device_t *session)
490 {
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 */
495             /*@ ignore @*/
496             (void)cfsetispeed(&session->ttyset, (speed_t) B0);
497             (void)cfsetospeed(&session->ttyset, (speed_t) B0);
498             /*@ end @*/
499             (void)tcsetattr(session->gpsdata.gps_fd, TCSANOW,
500                             &session->ttyset);
501         }
502         /* this is the clean way to do it */
503         session->ttyset_old.c_cflag |= HUPCL;
504         /* keep the most recent baud rate */
505         /*@ ignore @*/
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);
510         /*@ end @*/
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;
517     }
518 }