Coverage Report

Created: 2025-07-11 06:47

/src/gpsd/gpsd-3.26.2~dev/gpsd/serial.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * This file is Copyright by the GPSD project
3
 * SPDX-License-Identifier: BSD-2-clause
4
 */
5
6
#include "../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
#ifdef HAVE_LINUX_SERIAL_H
13
    #include <linux/serial.h>
14
#endif
15
#include <stdbool.h>
16
#include <stdio.h>
17
#include <stdlib.h>                  // for realpath()
18
#include <string.h>
19
#include <sys/ioctl.h>
20
#include <sys/param.h>               // defines BSD
21
#include <sys/socket.h>
22
#include <sys/stat.h>
23
#include <sys/types.h>
24
#include <termios.h>
25
#include <unistd.h>
26
27
#ifdef HAVE_SYS_SYSMACROS_H
28
    #include <sys/sysmacros.h>       // defines major()
29
#endif   // HAVE_SYS_SYSMACROS_H
30
31
#ifdef ENABLE_BLUEZ
32
    #include <bluetooth/bluetooth.h>
33
    #include <bluetooth/hci.h>
34
    #include <bluetooth/hci_lib.h>
35
    #include <bluetooth/rfcomm.h>
36
#endif   // ENABLE_BLUEZ
37
38
#include "../include/compiler.h"     // for FALLTHROUGH
39
#include "../include/gpsd.h"
40
41
// Workaround for HP-UX 11.23, which is missing CRTSCTS
42
#ifndef CRTSCTS
43
#  ifdef CNEW_RTSCTS
44
#    define CRTSCTS CNEW_RTSCTS
45
#  else
46
#    define CRTSCTS 0
47
#  endif  // CNEW_RTSCTS
48
#endif    // !CRTSCTS
49
50
// soupcetypes
51
static struct vlist_t sourcetypes[] = {
52
    {SOURCE_UNKNOWN, "Unknown"},
53
    {SOURCE_BLOCKDEV, "Blockdev"},
54
    {SOURCE_RS232, "Serial"},
55
    {SOURCE_USB, "USB"},
56
    {SOURCE_BLUETOOTH, "Bluetooth"},
57
    {SOURCE_CAN, "Canbus"},
58
    {SOURCE_PTY, "PTY"},
59
    {SOURCE_TCP, "TCP"},
60
    {SOURCE_UDP, "UDP"},
61
    {SOURCE_GPSD, "GPSD JSON"},
62
    {SOURCE_PPS, "PPS"},
63
    {SOURCE_PIPE, "PIPE"},
64
    {SOURCE_ACM, "ACM"},
65
    {0, NULL},
66
};
67
68
// figure out what kind of device we're looking at
69
static sourcetype_t gpsd_classify(struct gps_device_t *session)
70
0
{
71
0
    struct stat sb;
72
0
    const char *path = session->gpsdata.dev.path;
73
74
0
    if (-1 == stat(path, &sb)) {
75
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
76
0
                 "SER: stat(%s) failed: %s(%d)\n",
77
0
                 session->gpsdata.dev.path, strerror(errno), errno);
78
0
        return SOURCE_UNKNOWN;
79
0
    }
80
0
    switch (sb.st_mode & S_IFMT) {
81
0
    case S_IFBLK:
82
        // block device
83
0
        return SOURCE_BLOCKDEV;
84
0
    case S_IFCHR:
85
        // some char device
86
0
        break;
87
0
    case S_IFDIR:
88
        // directory??
89
0
        return SOURCE_UNKNOWN;
90
0
    case S_IFIFO:
91
0
        return SOURCE_PIPE;
92
0
    case S_IFLNK:
93
        // symlink ?!, assume char??
94
0
        break;
95
0
    case S_IFREG:
96
        // regular file
97
0
        return SOURCE_BLOCKDEV;
98
0
    case S_IFSOCK:
99
        // this assumes we won't get UDP from a filesystem socket
100
0
        return SOURCE_TCP;
101
0
    default:
102
        // huh??
103
0
        GPSD_LOG(LOG_WARNING, &session->context->errout,
104
0
                 "SER: gpsd_classify(%s) Unknown st_mode %d\n",
105
0
                 path, sb.st_mode & S_IFMT);
106
0
        break;
107
0
    }
108
109
    // OS-independent check for ptys using Unix98 naming convention
110
0
    if (0 == strncmp(path, "/dev/pts/", 9)) {
111
0
        return SOURCE_PTY;
112
0
    }
113
114
    // some more direct way to check for PPS?
115
0
    if (0 == strncmp(path, "/dev/pps", 8)) {
116
0
        return SOURCE_PPS;
117
0
    }
118
119
0
    if (S_ISCHR(sb.st_mode)) {
120
0
        sourcetype_t devtype = SOURCE_RS232;
121
0
#ifdef __linux__
122
        /* Linux major device numbers live here
123
         *
124
         * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/devices.txt
125
         *
126
         * Note: This code works because Linux major device numbers are
127
         * stable and architecture-independent.  It is *not* a good model
128
         * for other Unixes where either or both assumptions may break.
129
         */
130
0
        int devmajor = major(sb.st_rdev);
131
0
        int devminor = minor(sb.st_rdev);
132
133
0
        switch (devmajor) {
134
0
        case 3:      // First MFM, RLL and IDE hard disk/CD-ROM interface ?
135
0
            FALLTHROUGH
136
0
        case 136:    // Unix98 PTY slaves
137
0
            FALLTHROUGH
138
0
        case 137:    // Unix98 PTY slaves
139
0
            FALLTHROUGH
140
0
        case 138:    // Unix98 PTY slaves
141
0
            FALLTHROUGH
142
0
        case 139:    // Unix98 PTY slaves
143
0
            FALLTHROUGH
144
0
        case 140:    // Unix98 PTY slaves
145
0
            FALLTHROUGH
146
0
        case 141:    // Unix98 PTY slaves
147
0
            FALLTHROUGH
148
0
        case 142:    // Unix98 PTY slaves
149
0
            FALLTHROUGH
150
0
        case 143:    // Unix98 PTY slaves
151
0
            devtype = SOURCE_PTY;
152
0
            break;
153
154
0
        case 4:      // TTY Devices
155
0
            FALLTHROUGH
156
0
        case 22:     // Digiboard serial card
157
0
            FALLTHROUGH
158
0
        case 23:     // Digiboard serial card
159
0
            FALLTHROUGH
160
0
        case 24:     // Stallion serial card
161
0
            FALLTHROUGH
162
0
        case 32:     // Specialx serial card
163
0
            FALLTHROUGH
164
0
        case 46:     // Rocketport serial card
165
0
            FALLTHROUGH
166
0
        case 48:     // SDL RISCom serial card
167
0
            FALLTHROUGH
168
0
        case 57:     // Hayes ESP serial card
169
0
            FALLTHROUGH
170
0
        case 71:     // Computone IntelliPort II serial card
171
0
            FALLTHROUGH
172
0
        case 75:     // Specialix IO8+ serial card
173
0
            FALLTHROUGH
174
0
        case 105:    // Comtrol VS-1000+ serial card
175
0
            FALLTHROUGH
176
0
        case 112:    // ISI serial card
177
0
            FALLTHROUGH
178
0
        case 148:    // TCL serial card
179
0
            FALLTHROUGH
180
0
        case 154:    // Specialix RIO serial card
181
0
            FALLTHROUGH
182
0
        case 156:    // Specialix RIO serial card
183
0
            FALLTHROUGH
184
0
        case 164:    // Chase Research serial card
185
0
            FALLTHROUGH
186
0
        case 172:    // Moxa Intellio serial card
187
0
            FALLTHROUGH
188
0
        case 174:    // SmartIO serial card
189
0
            FALLTHROUGH
190
0
        case 204:    // Low-density serial ports
191
0
            FALLTHROUGH
192
0
        case 207:    // 207 FREESCALE I.MX UARTS (TTYMXC*)
193
0
            FALLTHROUGH
194
0
        case 208:    // User space serial ports
195
0
            FALLTHROUGH
196
0
        case 210:    // SBE serial ports
197
0
            FALLTHROUGH
198
0
        case 2224:    // A2232 serial ports
199
0
            devtype = SOURCE_RS232;
200
0
            break;
201
202
0
        case 10:      // Non-serial mice, misc features
203
0
            if (223 == devminor) {
204
0
                devtype = SOURCE_PPS;
205
0
            } // else WTF?
206
0
            break;
207
208
0
        case 166:    // ACM USB modems
209
            // ACM has no speed, otherwise similar to SOURCE_USB
210
0
            devtype = SOURCE_ACM;
211
0
            break;
212
0
        case 188:    // USB serial converters
213
0
            devtype = SOURCE_USB;
214
0
            break;
215
216
0
        case 216:    // Bluetooth RFCOMM TTY devices
217
0
            FALLTHROUGH
218
0
        case 217:    // Bluetooth RFCOMM TTY devices (alternate devices)
219
0
            devtype = SOURCE_BLUETOOTH;
220
0
            break;
221
222
0
        default:
223
            /* 234 to 254 Reserved for dynamic assignment
224
             * 240 to 254 Reserved for local/experimental use
225
             *
226
             * reports that Buildroot may use major 241 for serial,
227
             *  instead of its documented purpose (usbmon). */
228
0
            GPSD_LOG(LOG_WARNING, &session->context->errout,
229
0
                     "SER: gpsd_classify(%s) Unknown Major Device # %d\n",
230
0
                     path, devmajor);
231
            // Give up, default to rs232
232
0
            devtype = SOURCE_RS232;
233
0
            break;
234
0
        }
235
0
#endif  // __linux__
236
        /*
237
         * See http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system
238
         * for discussion how this works.  Key graphs:
239
         *
240
         * Compilers for the old BSD base for these distributions
241
         * defined the __bsdi__ macro, but none of these distributions
242
         * define it now. This leaves no generic "BSD" macro defined
243
         * by the compiler itself, but all UNIX-style OSes provide a
244
         * <sys/param.h> file. On BSD distributions, and only on BSD
245
         * distributions, this file defines a BSD macro that's set to
246
         * the OS version. Checking for this generic macro is more
247
         * robust than looking for known BSD distributions with
248
         * __DragonFly__, __FreeBSD__, __NetBSD__, and __OpenBSD__
249
         * macros.
250
         *
251
         * Apple's OSX for the Mac and iOS for iPhones and iPads are
252
         * based in part on a fork of FreeBSD distributed as
253
         * Darwin. As such, OSX and iOS also define the BSD macro
254
         * within <sys/param.h>. However, compilers for OSX, iOS, and
255
         * Darwin do not define __unix__. To detect all BSD OSes,
256
         * including OSX, iOS, and Darwin, use an #if/#endif that
257
         * checks for __unix__ along with __APPLE__ and __MACH__ (see
258
         * the later section on OSX and iOS).
259
         */
260
#ifdef BSD
261
        /*
262
         * Hacky check for pty, which is what really matters for avoiding
263
         * adaptive delay.
264
         */
265
        if (0 == strncmp(path, "/dev/ttyp", 9) ||
266
            0 == strncmp(path, "/dev/ttyq", 9)) {
267
            devtype = SOURCE_PTY;
268
        } else if (0 == strncmp(path, "/dev/ttyU", 9) ||
269
                   0 == strncmp(path, "/dev/dtyU", 9)) {
270
            devtype = SOURCE_USB;
271
        }
272
        // XXX bluetooth
273
#endif  // BSD
274
0
        return devtype;
275
0
    }
276
277
0
    return SOURCE_UNKNOWN;
278
0
}
279
280
#ifdef __linux__
281
282
/* Try to find how many times a file is open.  0 to n.
283
 * return -1 on failure
284
 */
285
static int fusercount(struct gps_device_t *session)
286
0
{
287
0
    DIR *procd, *fdd;
288
0
    struct dirent *procentry, *fdentry;
289
0
    char procpath[GPS_PATH_MAX], fdpath[GPS_PATH_MAX], linkpath[GPS_PATH_MAX];
290
0
    char *fullpath = NULL;        // what dev.path points to
291
0
    int cnt = 0;
292
293
    // POSIX 2008
294
0
    fullpath = realpath(session->gpsdata.dev.path, NULL);
295
0
    if (NULL == fullpath) {
296
        // Huh?
297
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
298
0
                 "SER: fusercount(): realpath(%s) failed: %s(%d)\n",
299
0
                 session->gpsdata.dev.path,
300
0
                 strerror(errno), errno);
301
0
        return -1;
302
0
    }
303
304
0
    if (NULL == (procd = opendir("/proc"))) {
305
0
        free(fullpath);
306
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
307
0
                 "SER: fusercount(): opendir(/proc) failed: %s(%d)\n",
308
0
                 strerror(errno), errno);
309
0
        return -1;
310
0
    }
311
312
    // debug
313
    // GPSD_LOG(LOG_SHOUT, &session->context->errout,
314
    //          "SER: fusercount: path %s fullpath %s\n",
315
    //          session->gpsdata.dev.path, fullpath);
316
317
0
    while (NULL != (procentry = readdir(procd))) {
318
0
        if (0 == isdigit(procentry->d_name[0])) {
319
            // skip non-precess entries
320
0
            continue;
321
0
        }
322
        // longest procentry->d_name I could find was 12
323
0
        (void)snprintf(procpath, sizeof(procpath),
324
0
                       "/proc/%.20s/fd/", procentry->d_name);
325
0
        if (NULL == (fdd = opendir(procpath))) {
326
0
            continue;
327
0
        }
328
0
        while (NULL != (fdentry = readdir(fdd))) {
329
0
            ssize_t rd;
330
331
0
            if (0 == isdigit(fdentry->d_name[0])) {
332
                // skip . and ..
333
0
                continue;
334
0
            }
335
0
            (void)strlcpy(fdpath, procpath, sizeof(fdpath));
336
0
            (void)strlcat(fdpath, fdentry->d_name, sizeof(fdpath));
337
338
            // readlink does not always NUL terminate.
339
0
            rd = readlink(fdpath, linkpath, sizeof(linkpath) - 1);
340
0
            if (0 > rd) {
341
                // maybe access error, maybe too long, maybe...
342
0
                continue;
343
0
            }
344
            // pacify coverity by setting NUL
345
0
            linkpath[rd] = '\0';
346
0
            if ('/' != linkpath[0]) {
347
                // not a full path
348
0
                continue;
349
0
            }
350
            // debug
351
            // GPSD_LOG(LOG_SHOUT, &session->context->errout,
352
            //          "SER: fusercount: %s linkpath %s fullpath %s\n",
353
            //          fdpath, linkpath, fullpath);
354
0
            if (0 == strcmp(linkpath, fullpath)) {
355
0
                ++cnt;
356
0
            }
357
0
        }
358
0
        (void)closedir(fdd);
359
0
    }
360
0
    (void)closedir(procd);
361
0
    GPSD_LOG(LOG_IO, &session->context->errout,
362
0
             "SER: fusercount: path %s fullpath %s cnt %d\n",
363
0
             session->gpsdata.dev.path, fullpath, cnt);
364
0
    free(fullpath);
365
366
0
    return cnt;
367
0
}
368
#endif   // __linux__
369
370
// to be called on allocating a device
371
void gpsd_tty_init(struct gps_device_t *session)
372
0
{
373
    // mark GPS fd closed and its baud rate unknown
374
0
    session->gpsdata.gps_fd = -1;
375
0
    session->saved_baud = -1;
376
0
    session->zerokill = false;
377
0
    session->reawake = (time_t)0;
378
0
}
379
380
#if !defined(HAVE_CFMAKERAW)
381
/*
382
 * Local implementation of cfmakeraw (which is not specified by
383
 * POSIX; see matching declaration in gpsd.h).
384
 *
385
 * Pasted from man page; added in serial.c arbitrarily
386
 */
387
void cfmakeraw(struct termios *termios_p)
388
{
389
    termios_p->c_iflag &=
390
        ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
391
    termios_p->c_oflag &= ~OPOST;
392
    termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
393
    termios_p->c_cflag &= ~(CSIZE | PARENB);
394
    termios_p->c_cflag |= CS8;
395
}
396
#endif   // !defined(HAVE_CFMAKERAW)
397
398
// speed conversion table for speed2code() and code2speed()
399
static struct speed_code_t {
400
    speed_t code;
401
    int speed;
402
} const speed_code[] = {
403
    // 4800 is gpsd minimum
404
    // list must be sorted ascending speed;
405
    {B4800, 4800},
406
    {B9600, 9600},
407
    {B19200, 19200},
408
    {B38400, 38400},
409
    {B57600, 57600},
410
    {B115200, 115200},
411
    {B230400, 230400},
412
#ifdef B460800
413
    {B460800, 460800},
414
#endif
415
#ifdef B500000
416
    {B500000, 500000},
417
#endif
418
#ifdef B576000
419
    {B576000, 576000},
420
#endif
421
#ifdef B921600
422
    {B921600, 921600},
423
#endif
424
#ifdef B1000000
425
    {B1000000, 1000000},
426
#endif
427
#ifdef B1152000
428
    {B1152000, 1152000},
429
#endif
430
#ifdef B1500000
431
    {B1500000, 1500000},
432
#endif
433
#ifdef B2000000
434
    {B2000000, 2000000},
435
#endif
436
#ifdef B2500000
437
    {B2500000, 2500000},
438
#endif
439
#ifdef B3000000
440
    {B3000000, 3000000},
441
#endif
442
#ifdef B3500000
443
    {B3500000, 3500000},
444
#endif
445
#ifdef B4000000
446
    {B4000000, 4000000},
447
#endif
448
   {B0, 0},           // flag for end of list
449
};
450
451
// Convert speed code into speed
452
static speed_t speed2code(const int speed)
453
0
{
454
0
    speed_t code = B9600;          // fall back
455
0
    speed_t last_code = B9600;     // fall back
456
0
    int index;
457
458
    // dumb linear search
459
0
    for (index = 0; 0 != speed_code[index].speed; index++) {
460
0
        if (speed < speed_code[index].speed) {
461
            // went past desired speed, use next slower valid speed
462
0
            code = last_code;
463
0
            break;
464
0
        }
465
0
        if (speed == speed_code[index].speed) {
466
0
            code = speed_code[index].code;
467
0
            break;
468
0
        }
469
0
        last_code = speed_code[index].code;
470
0
    }
471
0
    return code;
472
0
}
473
474
// Convert speed code into speed
475
static int code2speed(const speed_t code)
476
0
{
477
0
    int speed = 9600;     // fall back
478
0
    int index;
479
480
    // dumb linear search
481
0
    for (index = 0; 0 != speed_code[index].speed; index++) {
482
0
        if (code == speed_code[index].code) {
483
0
            speed = speed_code[index].speed;
484
0
            break;
485
0
        }
486
0
    }
487
0
    return speed;
488
0
}
489
490
// return the speed for a device.  B0, B9600, etc.
491
int gpsd_get_speed(const struct gps_device_t *dev)
492
0
{
493
0
    return code2speed(cfgetospeed(&dev->ttyset));
494
0
}
495
496
// return the old speed for a device.  B0, B9600, etc.
497
int gpsd_get_speed_old(const struct gps_device_t *dev)
498
0
{
499
0
    return code2speed(cfgetospeed(&dev->ttyset_old));
500
0
}
501
502
// return the parity for a device.  N, O or E.
503
char gpsd_get_parity(const struct gps_device_t *dev)
504
0
{
505
0
    char parity = 'N';
506
507
0
    if ((PARENB | PARODD) == (dev->ttyset.c_cflag & (PARENB | PARODD))) {
508
0
        parity = 'O';
509
0
    } else if (PARENB == (dev->ttyset.c_cflag & PARENB)) {
510
0
        parity = 'E';
511
0
    }
512
0
    return parity;
513
0
}
514
515
// return the stop bits for a device.  1 or 2
516
int gpsd_get_stopbits(const struct gps_device_t *dev)
517
0
{
518
0
    int stopbits = 1;
519
520
0
    if (CSTOPB == (dev->ttyset.c_cflag & CSTOPB)) {
521
        // xx2
522
0
        stopbits = 2;
523
0
    }
524
0
    return stopbits;
525
0
}
526
527
/*
528
 * Serious black magic begins here.  Getting this code wrong can cause
529
 * failures to lock to a correct speed, and not clean reproducible
530
 * failures but flukey hardware- and timing-dependent ones.  So
531
 * be very sure you know what you're doing before hacking it, and
532
 * test thoroughly.
533
 *
534
 * The fundamental problem here is that serial devices take time
535
 * to settle into a new baud rate after tcsetattr() is issued. Until
536
 * they do so, input will be arbitrarily garbled.  Normally this
537
 * is not a big problem, but in our hunt loop the garbling can trash
538
 * a long enough prefix of each sample to prevent detection of a
539
 * packet header.  We could address the symptom by making the sample
540
 * size enough larger that subtracting the maximum length of garble
541
 * would still leave a sample longer than the maximum packet size.
542
 * But it's better (and more efficient) to address the disease.
543
 *
544
 * In theory, one might think that not even a tcflush() call would
545
 * be needed, with tcsetattr() delaying its return until the device
546
 * is in a good state.  For simple devices like a 14550 UART that
547
 * have fixed response timings this may even work, if the driver
548
 * writer was smart enough to delay the return by the right number
549
 * of milliseconds after poking the device port(s).
550
 *
551
 * Problems may arise if the driver's timings are off.  Or we may
552
 * be talking to a USB device like the pl2303 commonly used in GPS
553
 * mice; on these, the change will not happen immediately because
554
 * it has to be sent as a message to the external processor that
555
 * has to act upon it, and that processor may still have buffered
556
 * data in its own FIFO.  In this case the expected delay may be
557
 * too large and too variable (depending on the details of how the
558
 * USB device is integrated with its symbiont hardware) to be put
559
 * in the driver.
560
 *
561
 * So, somehow, we have to introduce a delay after tcsatattr()
562
 * returns sufficient to allow *any* device to settle.  On the other
563
 * hand, a really long delay will make gpsd device registration
564
 * unpleasantly laggy.
565
 *
566
 * The classic way to address this is with a tcflush(), counting
567
 * on it to clear the device FIFO. But that call may clear only the
568
 * kernel buffers, not the device's hardware FIFO, so it may not
569
 * be sufficient by itself.
570
 *
571
 * flush followed by a 200-millisecond delay followed by flush has
572
 * been found to work reliably on the pl2303.  It is also known
573
 * from testing that a 100-millisec delay is too short, allowing
574
 * occasional failure to lock.
575
 */
576
static void gpsd_flush(struct gps_device_t * session)
577
0
{
578
0
    struct timespec delay;
579
580
0
    if (0 != tcflush(session->gpsdata.gps_fd, TCIOFLUSH)) {
581
        // cast for 32-bit intptr_t
582
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
583
0
                 "SER: gpsd_flush(%ld): %s(%d)\n",
584
0
                 (long)session->gpsdata.gps_fd, strerror(errno), errno);
585
0
    }
586
587
    // wait 200,000 uSec
588
0
    delay.tv_sec = 0;
589
0
    delay.tv_nsec = 200000000L;
590
0
    nanosleep(&delay, NULL);
591
0
    if (0 != tcflush(session->gpsdata.gps_fd, TCIOFLUSH)) {
592
        // cast for 32-bit intptr_t
593
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
594
0
                 "SER: gpsd_flush(%ld): %s(%d)\n",
595
0
                 (long)session->gpsdata.gps_fd, strerror(errno), errno);
596
0
    }
597
0
}
598
599
// Set device into raw mode
600
bool gpsd_set_raw(struct gps_device_t * session)
601
0
{
602
    // on some OS cfmakeraw() returns an int, POSIX says it is void.
603
0
    (void)cfmakeraw(&session->ttyset);
604
0
    if (0 == tcsetattr(session->gpsdata.gps_fd, TCIOFLUSH,
605
0
                       &session->ttyset)) {
606
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
607
0
                 "SER: error changing port attributes: %s(%d)\n",
608
0
                 strerror(errno), errno);
609
0
        return false;
610
0
    }
611
612
0
    return true;
613
0
}
614
615
/* Check if an fd is a tty
616
 * Return 1 if yes
617
 *        0 if no
618
 */
619
int gpsd_serial_isatty(const struct gps_device_t *session)
620
0
{
621
0
    if (0 > session->gpsdata.gps_fd) {
622
        // PLACEHOLDING_FD, or UNALLOCATED_FD
623
        // no need for expensive iotcl()
624
0
        return 0;
625
0
    }
626
    // POSIX says isatty() does not have to set errno on error...
627
0
    errno = 0;
628
0
    if (0 < isatty(session->gpsdata.gps_fd)) {
629
        // is a tty
630
0
        return 1;
631
0
    }
632
0
    if (ENOTTY == errno ||
633
0
#if defined(ENXIO)
634
0
        ENXIO == errno ||         // Some OSXes return this.  Not POSIX.
635
0
#endif  // defined(ENXIO)
636
0
#if defined(EADDRNOTAVAIL)
637
0
        EADDRNOTAVAIL == errno || // Some BSDs return this.  Not POSIX.
638
0
#endif  // defined(EADDRNOTAVAIL)
639
0
#if defined(EOPNOTSUPP)
640
0
        EOPNOTSUPP == errno ||    // Some BSDs/OSXes return this.  Not POSIX.
641
0
#endif  // defined(EOPNOTSUPP)
642
0
        0 == errno) {
643
        // is not a tty
644
0
        return 0;
645
0
    }
646
647
    // else warning, and assume not a tty./ cast for 32-bit intptr_t
648
0
    GPSD_LOG(LOG_WARNING, &session->context->errout,
649
0
             "SER: gpsd_serial_isatty(%ld) < 1: %s(%d)\n",
650
0
             (long)session->gpsdata.gps_fd,
651
0
             strerror(errno), errno);
652
0
    return 0;
653
0
}
654
655
// Set the port speed
656
void gpsd_set_speed(struct gps_device_t *session,
657
                    speed_t speed, char parity, unsigned int stopbits)
658
0
{
659
0
    speed_t rate;
660
661
    // FIXME:  just return if !isatty() ?
662
663
0
    if (0 < session->context->fixed_port_speed) {
664
0
        speed = session->context->fixed_port_speed;
665
0
    }
666
0
    if ('\0' != session->context->fixed_port_framing[0]) {
667
        // Forced framing?
668
        // ignore length, stopbits=2 forces length 7.
669
0
        parity = session->context->fixed_port_framing[1];
670
0
        stopbits = session->context->fixed_port_framing[2] - '0';
671
0
    }
672
0
    if (!IN(1, stopbits, 2)) {
673
        // invalid stop bits, Assume 8x1
674
0
        stopbits = 1;
675
0
    }
676
677
    /*
678
     * Yes, you can set speeds that aren't in the hunt loop.  If you
679
     * do this, and you aren't on Linux where baud rate is preserved
680
     * across port closings, you've screwed yourself. Don't do that!
681
     * Setting the speed to B0 instructs the modem to "hang up".
682
     */
683
0
    rate = speed2code(speed);
684
685
    // backward-compatibility hack
686
0
    switch (parity) {
687
0
    case 'E':
688
0
        FALLTHROUGH
689
0
    case (char)2:
690
0
        parity = 'E';
691
0
        break;
692
0
    case 'O':
693
0
        FALLTHROUGH
694
0
    case (char)1:
695
0
        parity = 'O';
696
0
        break;
697
0
    case 'N':
698
0
        FALLTHROUGH
699
0
    case (char)0:
700
0
        FALLTHROUGH
701
0
    default:
702
0
        parity = 'N';   // without this we might emit malformed JSON
703
0
        break;
704
0
    }
705
706
0
    if (rate != cfgetispeed(&session->ttyset) ||
707
0
        parity != session->gpsdata.dev.parity ||
708
0
        stopbits != session->gpsdata.dev.stopbits) {
709
710
        /*
711
         *  "Don't mess with this conditional! Speed zero is supposed to mean
712
         *   to leave the port speed at whatever it currently is."
713
         *
714
         * The Linux man page says:
715
         *  "Setting the speed to B0 instructs the modem to "hang up".
716
         *
717
         * We use B0 as an internal flag to leave the speed alone.
718
         * This leads
719
         * to excellent behavior on Linux, which preserves baudrate across
720
         * serial device closes - it means that if you've opened this
721
         * device before you typically don't have to hunt at all because
722
         * it's still at the same speed you left it - you'll typically
723
         * get packet lock within 1.5 seconds.  Alas, the BSDs and OS X
724
         * aren't so nice.
725
         */
726
0
        if (B0 == rate) {
727
            // how does one get here?  cast for 32-bit intptr_t
728
0
            GPSD_LOG(LOG_IO, &session->context->errout,
729
0
                     "SER: fd %ld keeping old speed %d(%d)\n",
730
0
                     (long)session->gpsdata.gps_fd,
731
0
                     code2speed(cfgetispeed(&session->ttyset)),
732
0
                     (int) cfgetispeed(&session->ttyset));
733
0
        } else {
734
0
            (void)cfsetispeed(&session->ttyset, rate);
735
0
            (void)cfsetospeed(&session->ttyset, rate);
736
            // cast for 32-bit intptr_t
737
0
            GPSD_LOG(LOG_IO, &session->context->errout,
738
0
                     "SER: fd %ld set speed %d(%d)\n",
739
0
                     (long)session->gpsdata.gps_fd,
740
0
                     code2speed(cfgetispeed(&session->ttyset)), (int) rate);
741
0
        }
742
0
        session->ttyset.c_iflag &= ~(PARMRK | INPCK);
743
0
        session->ttyset.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD);
744
0
        session->ttyset.c_cflag |= (stopbits == 2 ? CS7 | CSTOPB : CS8);
745
0
        switch (parity) {
746
0
        case 'E':
747
0
            session->ttyset.c_iflag |= INPCK;
748
0
            session->ttyset.c_cflag |= PARENB;
749
0
            break;
750
0
        case 'O':
751
0
            session->ttyset.c_iflag |= INPCK;
752
0
            session->ttyset.c_cflag |= PARENB | PARODD;
753
0
            break;
754
0
        }
755
0
        if (0 != tcsetattr(session->gpsdata.gps_fd, TCSANOW,
756
0
                           &session->ttyset)) {
757
            /* strangely this fails on non-serial ports, but if
758
             * we do not try, we get other failures.
759
             * so ignore for now, as we always have, until it can
760
             * be nailed down.
761
             * cast for 32-bit intptr_t
762
             */
763
0
             GPSD_LOG(LOG_WARN, &session->context->errout,
764
0
                      "SER: fd %ld error setting port attributes: %s(%d), "
765
0
                      "sourcetype: %d\n",
766
0
                      (long)session->gpsdata.gps_fd,
767
0
                      strerror(errno), errno, session->sourcetype);
768
0
        }
769
770
0
        gpsd_flush(session);
771
0
    }
772
    // cast for 32-bit intptr_t
773
0
    GPSD_LOG(LOG_INF, &session->context->errout,
774
0
             "SER: fd %ld current speed %lu, %d%c%d\n",
775
0
             (long)session->gpsdata.gps_fd,
776
0
             (unsigned long)gpsd_get_speed(session), 9 - stopbits, parity,
777
0
             stopbits);
778
779
0
    session->gpsdata.dev.baudrate = (unsigned int)speed;
780
0
    session->gpsdata.dev.parity = parity;
781
0
    session->gpsdata.dev.stopbits = stopbits;
782
783
    /*
784
     * The device might need a wakeup string before it will send data.
785
     * If we don't know the device type, ship it every driver's wakeup
786
     * in hopes it will respond.  But not to USB or Bluetooth, because
787
     * shipping probe strings to unknown USB serial adaptors or
788
     * Bluetooth devices may spam devices that aren't GPSes at all and
789
     * could become confused.
790
     * For now we probe SOURCE_ACM...
791
     */
792
0
    if (!session->context->readonly &&
793
0
        SOURCE_USB != session->sourcetype &&
794
0
        SOURCE_BLUETOOTH != session->sourcetype) {
795
796
        // it is not read-only, not ttyACM, not BT
797
0
        if (0 < gpsd_serial_isatty(session)) {
798
0
            if (NULL == session->device_type) {
799
0
                const struct gps_type_t **dp;
800
801
0
                for (dp = gpsd_drivers; *dp; dp++) {
802
0
                    if (NULL != (*dp)->event_hook) {
803
0
                        (*dp)->event_hook(session, EVENT_WAKEUP);
804
0
                    }
805
0
                }
806
0
            } else if (NULL != session->device_type->event_hook) {
807
0
                session->device_type->event_hook(session, EVENT_WAKEUP);
808
0
            }
809
0
        }
810
0
    }
811
0
    packet_reset(&session->lexer);
812
0
    clock_gettime(CLOCK_REALTIME, &session->ts_startCurrentBaud);
813
0
}
814
815
/* open a device for access to its data
816
 *
817
 * return: the opened file descriptor
818
 *         PLACEHOLDING_FD - for /dev/ppsX
819
 *         UNALLOCATED_FD - for open failure
820
 */
821
int gpsd_serial_open(struct gps_device_t *session)
822
0
{
823
0
    speed_t new_speed;
824
0
    char new_parity;   // E, N, O
825
0
    unsigned int new_stop;
826
827
0
    mode_t mode = (mode_t) O_RDWR;
828
829
0
    session->sourcetype = gpsd_classify(session);
830
831
    // cast for 32-bit intptr_t
832
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
833
0
             "SER: gpsd_serial_open(%s) sourcetype %s(%d) fd %ld\n",
834
0
             session->gpsdata.dev.path,
835
0
             val2str(session->sourcetype, sourcetypes),
836
0
             session->sourcetype,
837
0
             (long)session->gpsdata.gps_fd);
838
839
0
    session->servicetype = SERVICE_SENSOR;
840
841
0
    if (SOURCE_UNKNOWN == session->sourcetype) {
842
0
        return UNALLOCATED_FD;
843
0
    }
844
845
    // we may need to hold on to this slot without opening the device
846
0
    if (SOURCE_PPS == session->sourcetype) {
847
0
        (void)gpsd_switch_driver(session, "PPS");
848
0
        return PLACEHOLDING_FD;
849
0
    }
850
851
0
    if (session->context->readonly ||
852
0
        SOURCE_BLOCKDEV >= session->sourcetype) {
853
0
        mode = (mode_t) O_RDONLY;
854
0
        GPSD_LOG(LOG_INF, &session->context->errout,
855
0
                 "SER: opening read-only GPS data source type %d at '%s'\n",
856
0
                 (int)session->sourcetype, session->gpsdata.dev.path);
857
0
    } else {
858
0
        GPSD_LOG(LOG_INF, &session->context->errout,
859
0
                 "SER: opening GPS data source type %d at '%s'\n",
860
0
                 (int)session->sourcetype, session->gpsdata.dev.path);
861
0
    }
862
#ifdef ENABLE_BLUEZ
863
    if (0 == bachk(session->gpsdata.dev.path)) {
864
        struct sockaddr_rc addr = { 0, *BDADDR_ANY, 0};
865
866
        errno = 0;
867
        session->gpsdata.gps_fd = socket(AF_BLUETOOTH,
868
                                         SOCK_STREAM,
869
                                         BTPROTO_RFCOMM);
870
        if (0 > session->gpsdata.gps_fd) {
871
            GPSD_LOG(LOG_ERROR, &session->context->errout,
872
                     "SER: bluetooth socket() failed: %s(%d)\n",
873
                     strerror(errno), errno);
874
            return UNALLOCATED_FD;
875
        }
876
        addr.rc_family = AF_BLUETOOTH;
877
        addr.rc_channel = (uint8_t) 1;
878
        (void) str2ba(session->gpsdata.dev.path, &addr.rc_bdaddr);
879
        if (-1 == connect(session->gpsdata.gps_fd,
880
                          (struct sockaddr *) &addr,
881
                          sizeof (addr))) {
882
            if (EINPROGRESS != errno &&
883
                EAGAIN != errno) {
884
                (void)close(session->gpsdata.gps_fd);
885
                GPSD_LOG(LOG_ERROR, &session->context->errout,
886
                         "SER: bluetooth socket connect failed: %s(%d)\n",
887
                         strerror(errno), errno);
888
                return UNALLOCATED_FD;
889
            }
890
            GPSD_LOG(LOG_ERROR, &session->context->errout,
891
                     "SER: bluetooth socket connect in progress or "
892
                     "EAGAIN: %s(%d)\n",
893
                     strerror(errno), errno);
894
        }
895
        (void)fcntl(session->gpsdata.gps_fd, F_SETFL, (int)mode);
896
        GPSD_LOG(LOG_PROG, &session->context->errout,
897
                 "SER: bluez device open success: %s %s(%d)\n",
898
                 session->gpsdata.dev.path, strerror(errno), errno);
899
    } else
900
#endif   // BLUEZ
901
0
    {
902
        /*
903
         * We open with O_NONBLOCK because we want to not get hung if
904
         * the CLOCAL flag is off.  Need to keep O_NONBLOCK so the main
905
         * loop does not clock on an unresponsive read() from a receiver.
906
         */
907
0
        errno = 0;
908
0
        if (-1 == (session->gpsdata.gps_fd =
909
0
                   open(session->gpsdata.dev.path,
910
0
                        (int)(mode | O_NONBLOCK | O_NOCTTY)))) {
911
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
912
0
                     "SER: device open of %s failed: %s(%d) - "
913
0
                     "retrying read-only\n",
914
0
                     session->gpsdata.dev.path,
915
0
                     strerror(errno), errno);
916
0
            if (-1 == (session->gpsdata.gps_fd =
917
0
                       open(session->gpsdata.dev.path,
918
0
                            O_RDONLY | O_NONBLOCK | O_NOCTTY))) {
919
0
                GPSD_LOG(LOG_ERROR, &session->context->errout,
920
0
                         "SER: read-only device open of %s failed: %s(%d)\n",
921
0
                         session->gpsdata.dev.path,
922
0
                         strerror(errno), errno);
923
0
                return UNALLOCATED_FD;
924
0
            }
925
926
            // cast for 32-bit intptr_t
927
0
            GPSD_LOG(LOG_PROG, &session->context->errout,
928
0
                     "SER: file device open of %s succeeded fd %ld\n",
929
0
                     session->gpsdata.dev.path,
930
0
                     (long)session->gpsdata.gps_fd);
931
0
        }
932
0
    }
933
934
    /*
935
     * Ideally we want to exclusion-lock the device before doing any reads.
936
     * It would have been best to do this at open(2) time, but O_EXCL
937
     * doesn't work without O_CREAT.
938
     *
939
     * We have to make an exception for ptys, which are intentionally
940
     * opened by another process on the master side, otherwise we'll
941
     * break all our regression tests.
942
     *
943
     * We also exclude bluetooth device because the bluetooth daemon opens them.
944
     */
945
0
    if (!(SOURCE_PTY == session->sourcetype ||
946
0
          SOURCE_BLUETOOTH == session->sourcetype)) {
947
        // FIXME: OK to open PPS devices twice.
948
        // FIXME: Check for duplicates before opening device.
949
950
0
#ifdef __linux__
951
0
        int cnt;
952
        /*
953
         * Don't touch devices already opened by another process.
954
         */
955
0
        cnt = fusercount(session);
956
0
        if (1 < cnt) {
957
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
958
0
                     "SER: %s already opened by another process!!!!!!!!!!!\n",
959
0
                     session->gpsdata.dev.path);
960
            /* caan't really continue, make it obvious to the user as
961
             * none understand what ERROR means. */
962
0
            exit(1);
963
964
            // (void)close(session->gpsdata.gps_fd);
965
            // session->gpsdata.gps_fd = UNALLOCATED_FD;
966
            // return UNALLOCATED_FD;
967
0
        }
968
0
        if (0 == cnt) {
969
0
            GPSD_LOG(LOG_PROG, &session->context->errout,
970
0
                     "SER: fusercount(%s) failed to find own use.\n",
971
0
                     session->gpsdata.dev.path);
972
0
        }
973
0
#endif   // __linux__
974
975
0
#ifdef TIOCEXCL
976
        /*
977
         * Try to block other processes from using this device while we
978
         * have it open (later opens should return EBUSY).  Won't work
979
         * against anything with root privileges, alas.
980
         */
981
0
        (void)ioctl(session->gpsdata.gps_fd, (unsigned long)TIOCEXCL);
982
0
#endif  // TIOCEXCL
983
0
    }
984
985
0
    session->lexer.type = BAD_PACKET;
986
987
0
    if (0 >= gpsd_serial_isatty(session)) {
988
        // cast for 32-bit intptr_t
989
0
        GPSD_LOG(LOG_IO, &session->context->errout,
990
0
                 "SER: gpsd_serial_open(%s) -> %ld, Not tty\n",
991
0
                 session->gpsdata.dev.path, (long)session->gpsdata.gps_fd);
992
        // (int) to pacify Codacy.
993
0
        return (int)session->gpsdata.gps_fd;
994
0
    }
995
996
    // Save original terminal parameters, why?
997
    // At least it tests we can read port parameters.
998
0
    if (0 != tcgetattr(session->gpsdata.gps_fd, &session->ttyset_old)) {
999
        // Maybe still usable somehow?  cast for 32-bit intptr_t
1000
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
1001
0
                 "SER: gpsd_serial_open() tcgetattr(%ld) failed: %s(%d)\n",
1002
0
                 (long)session->gpsdata.gps_fd,
1003
0
                 strerror(errno), errno);
1004
0
        return UNALLOCATED_FD;
1005
0
    }
1006
0
    session->ttyset = session->ttyset_old;
1007
1008
0
    if (0 < session->context->fixed_port_speed) {
1009
0
        session->saved_baud = session->context->fixed_port_speed;
1010
        // cast for 32-bit intptr_t
1011
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
1012
0
                 "SER: fd %ld fixed speed %d\n",
1013
0
                 (long)session->gpsdata.gps_fd, session->saved_baud);
1014
0
    } else if (0 < session->saved_baud) {
1015
        // cast for 32-bit intptr_t
1016
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
1017
0
                 "SER: fd %ld saved speed %d\n",
1018
0
                 (long)session->gpsdata.gps_fd, session->saved_baud);
1019
0
    }
1020
1021
0
    if (0 < session->saved_baud) {
1022
        // FIXME! use gpsd_set_speed()
1023
0
        (void)cfsetispeed(&session->ttyset, (speed_t)session->saved_baud);
1024
0
        (void)cfsetospeed(&session->ttyset, (speed_t)session->saved_baud);
1025
0
        if (0 == tcsetattr(session->gpsdata.gps_fd, TCSANOW,
1026
0
                           &session->ttyset)) {
1027
            // cast for 32-bit intptr_t
1028
0
            GPSD_LOG(LOG_PROG, &session->context->errout,
1029
0
                     "SER: fd %ld restoring fixed/saved speed %d(%d)\n",
1030
0
                     (long)session->gpsdata.gps_fd, session->saved_baud,
1031
0
                     (int) cfgetispeed(&session->ttyset));
1032
0
        } else {
1033
            // cast for 32-bit intptr_t
1034
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
1035
0
                     "SER: fd %ld Error setting port attributes: %s(%d)\n",
1036
0
                     (long)session->gpsdata.gps_fd, strerror(errno), errno);
1037
0
        }
1038
0
        gpsd_flush(session);
1039
0
    }
1040
1041
    // twiddle the speed, parity, etc. but only on real serial ports
1042
0
    memset(session->ttyset.c_cc, 0, sizeof(session->ttyset.c_cc));
1043
    // session->ttyset.c_cc[VTIME] = 1;
1044
    /*
1045
     * Tip from Chris Kuethe: the FIDI chip used in the Trip-Nav
1046
     * 200 (and possibly other USB GPSes) gets completely hosed
1047
     * in the presence of flow control.  Thus, turn off CRTSCTS.
1048
     *
1049
     * This is not ideal.  Setting no parity here will mean extra
1050
     * initialization time for some devices, like certain Trimble
1051
     * boards, that want 7O2 or other non-8N1 settings. But starting
1052
     * the hunt loop at 8N1 will minimize the average sync time
1053
     * over all devices.
1054
     */
1055
0
    session->ttyset.c_cflag &= ~(PARENB | PARODD | CRTSCTS | CSTOPB);
1056
0
    session->ttyset.c_cflag |= CREAD | CLOCAL;
1057
0
    session->ttyset.c_iflag = session->ttyset.c_oflag =
1058
0
        session->ttyset.c_lflag = (tcflag_t) 0;
1059
1060
0
    session->baudindex = 0;  // FIXME: fixed speed
1061
0
    if (0 < session->context->fixed_port_speed) {
1062
0
        new_speed = session->context->fixed_port_speed;
1063
0
    } else {
1064
0
        new_speed = gpsd_get_speed_old(session);
1065
0
    }
1066
0
    if ('\0' == session->context->fixed_port_framing[0]) {
1067
        // FIXME! Try the parity, stop, as it is on startup first.
1068
0
        new_parity = 'N';
1069
0
        new_stop = 1;
1070
0
    } else {
1071
        // Forced framing
1072
        // ignore length, stopbits=2 forces length 7.
1073
0
        new_parity = session->context->fixed_port_framing[1];
1074
0
        new_stop = session->context->fixed_port_framing[2] - '0';
1075
0
    }
1076
0
    if (!IN(1, new_stop, 2)) {
1077
        // invalid stop bits, assume 1
1078
0
        new_stop = 1;
1079
0
    }
1080
    // FIXME: setting speed twice??
1081
0
    gpsd_set_speed(session, new_speed, new_parity, new_stop);
1082
1083
    /* Used to turn off O_NONBLOCK here, but best not to block trying
1084
     * to read from an unresponsive receiver. */
1085
1086
    // required so parity field won't be '\0' if saved speed matches
1087
0
    if (SOURCE_BLOCKDEV >= session->sourcetype) {
1088
0
        session->gpsdata.dev.parity = 'N';
1089
0
        session->gpsdata.dev.stopbits = 1;
1090
0
    }
1091
1092
    // start the autobaud hunt clock.
1093
0
    clock_gettime(CLOCK_REALTIME, &session->ts_startCurrentBaud);
1094
    // cast for 32-bit intptr_t
1095
0
    GPSD_LOG(LOG_IO, &session->context->errout,
1096
0
             "SER: open(%s) -> %ld in gpsd_serial_open()\n",
1097
0
             session->gpsdata.dev.path, (long)session->gpsdata.gps_fd);
1098
0
    return session->gpsdata.gps_fd;
1099
0
}
1100
1101
ssize_t gpsd_serial_write(struct gps_device_t * session,
1102
                          const char *buf, const size_t len)
1103
0
{
1104
0
    ssize_t status;
1105
0
    bool ok;
1106
0
    char scratchbuf[MAX_PACKET_LENGTH*2+1];
1107
1108
0
    if (NULL == session ||
1109
0
        NULL == session->context ||
1110
0
        0 > session->gpsdata.gps_fd ||
1111
0
        session->context->readonly) {
1112
0
        return 0;
1113
0
    }
1114
1115
0
    status = write(session->gpsdata.gps_fd, buf, len);
1116
0
    ok = (status == (ssize_t) len);
1117
0
    if (0 < gpsd_serial_isatty(session)) {
1118
        // do we really need to block on tcdrain?
1119
0
        if (0 != tcdrain(session->gpsdata.gps_fd)) {
1120
            // cast for 32-bit intptr_t
1121
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
1122
0
                     "SER: gpsd_serial_write(%ld) tcdrain() failed: %s(%d)\n",
1123
0
                     (long)session->gpsdata.gps_fd,
1124
0
                     strerror(errno), errno);
1125
0
        }
1126
0
    }
1127
1128
0
    GPSD_LOG(LOG_IO, &session->context->errout,
1129
0
             "SER: => GPS: %s%s\n",
1130
0
             gpsd_packetdump(scratchbuf, sizeof(scratchbuf),
1131
0
                             (const unsigned char *)buf,
1132
0
                             len), ok ? "" : " FAILED");
1133
0
    return status;
1134
0
}
1135
1136
/*
1137
 * This constant controls how many characters the packet sniffer will spend
1138
 * looking for a packet leader before it gives up.  It *must* be larger than
1139
 * MAX_PACKET_LENGTH or we risk never syncing up at all.  Large values
1140
 * will produce annoying startup lag.
1141
 */
1142
0
#define SNIFF_RETRIES   (MAX_PACKET_LENGTH + 128)
1143
1144
// advance to the next hunt setting
1145
bool gpsd_next_hunt_setting(struct gps_device_t * session)
1146
0
{
1147
0
    struct timespec ts_now, ts_diff;
1148
0
#ifdef TIOCGICOUNT
1149
    // serial input counters
1150
0
    struct serial_icounter_struct icount;
1151
0
#endif  // TIOCGICOUNT
1152
1153
    // don't waste time in the hunt loop if this is not actually a tty
1154
    // FIXME: Check for ttys like /dev/ttyACM that have no speed.
1155
0
    if (0 >= gpsd_serial_isatty(session)) {
1156
        // This catches SERVICE_TCP, SERVICE_PIPE, SERVICE_GPSD, etc.
1157
0
        return false;
1158
0
    }
1159
1160
    /* ...or if it's nominally a tty but delivers only PPS and no data
1161
     * ...or if it's a PTY, which has no speed. */
1162
0
    if (SOURCE_PPS == session->sourcetype ||
1163
0
        SOURCE_PTY == session->sourcetype) {
1164
0
        return false;
1165
0
    }
1166
1167
    // ...or if the port speed is fixed.
1168
0
    if (0 < session->context->fixed_port_speed) {
1169
        //  this prevents framing hunt.
1170
0
        return false;
1171
0
    }
1172
1173
0
    clock_gettime(CLOCK_REALTIME, &ts_now);
1174
0
    TS_SUB(&ts_diff, &ts_now, &session->ts_startCurrentBaud);
1175
1176
    // cast for 32-bit intptr_t
1177
0
    GPSD_LOG(LOG_IO, &session->context->errout,
1178
0
             "SER: gpsd_next_hunt_setting(%ld) retries %lu diff %lld\n",
1179
0
             (long)session->gpsdata.gps_fd,
1180
0
             session->lexer.retry_counter,
1181
0
             (long long)ts_diff.tv_sec);
1182
1183
0
    if (SNIFF_RETRIES <= session->lexer.retry_counter++ ||
1184
0
        3 < ts_diff.tv_sec) {
1185
        // no lock after 3 seconds or SNIFF_RETRIES
1186
0
        char new_parity;   // E, N, O
1187
0
        unsigned int new_stop;
1188
        // u-blox 9 can do 921600
1189
        // Javad can ro 1.5 mbps
1190
        // every rate we're likely to see on a GNSS receiver
1191
0
        static unsigned int rates[] =
1192
0
            {0, 4800, 9600, 19200, 38400, 57600, 115200, 230400,
1193
0
             460800, 921600};
1194
1195
0
#ifdef TIOCGICOUNT
1196
        // check input counts
1197
0
        if (LOG_INF > session->context->errout.debug) {
1198
            // do nothing
1199
0
        } else if (-1 == ioctl(session->gpsdata.gps_fd,
1200
0
                               (unsigned long)TIOCGICOUNT, &icount)) {
1201
            // some tty-like devices do not implement TIOCGICOUNT
1202
0
            if (ENOTTY != errno) {
1203
                // cast for 32-bit intptr_t
1204
0
                GPSD_LOG(LOG_ERROR, &session->context->errout,
1205
0
                         "SER: ioctl(%ld, TIOCGICOUNT) failed: %s(%d)\n",
1206
0
                         (long)session->gpsdata.gps_fd,
1207
0
                         strerror(errno), errno);
1208
0
            }
1209
0
        } else {
1210
            // cast for 32-bit intptr_t
1211
0
            GPSD_LOG(LOG_INF, &session->context->errout,
1212
0
                     "SER: ioctl(%ld, TIOCGICOUNT) rx %d tx %d frame %d "
1213
0
                     "overrun %d parity %d brk %d buf_overrun %d\n",
1214
0
                     (long)session->gpsdata.gps_fd, icount.rx, icount.tx,
1215
0
                     icount.frame, icount.overrun, icount.parity, icount.brk,
1216
0
                     icount.buf_overrun);
1217
0
        }
1218
0
#endif  // TIOCGICOUNT
1219
1220
0
        if (ROWS(rates) <= ++session->baudindex) {
1221
1222
            // start over, maybe with new stop bits.
1223
0
            session->baudindex = 1;  // skip reates[0]. which os B0.
1224
0
            if ('\0' == session->context->fixed_port_framing[0]) {
1225
                // This toggles from 7N2 to 8N1, and back to 7N2
1226
                // FIXME?? Also try 8E1 and 8O1 ??  For Trimble
1227
0
                if (2 == session->gpsdata.dev.stopbits) {
1228
0
                    session->gpsdata.dev.stopbits = 1;  // restart at 1
1229
0
                } else {
1230
0
                    session->gpsdata.dev.stopbits = 2;  // increment to 2
1231
0
                }
1232
0
            }
1233
0
        }
1234
1235
0
        if ('\0' == session->context->fixed_port_framing[0]) {
1236
0
            new_parity = session->gpsdata.dev.parity;
1237
0
            new_stop = session->gpsdata.dev.stopbits;
1238
0
        } else {
1239
            // fixed framing
1240
            // ignore length, stopbits=2 forces length 7.
1241
0
            new_parity = session->context->fixed_port_framing[1];
1242
0
            new_stop = session->context->fixed_port_framing[2] - '0';
1243
0
        }
1244
1245
0
        gpsd_set_speed(session, rates[session->baudindex], new_parity,
1246
0
                       new_stop);
1247
0
        session->lexer.retry_counter = 0;
1248
0
    }
1249
0
    return true;                // keep hunting
1250
0
}
1251
1252
// to be called when we want to register that we've synced with a device
1253
void gpsd_assert_sync(struct gps_device_t *session)
1254
0
{
1255
    /*
1256
     * We've achieved first sync with the device. Remember the
1257
     * baudrate so we can try it first next time this device
1258
     * is opened.
1259
     */
1260
0
    if (0 >= session->saved_baud) {
1261
0
        session->saved_baud = (int)cfgetispeed(&session->ttyset);
1262
0
    }
1263
0
}
1264
1265
/* Close an open sensor device
1266
 * could be serial, udp://, tcp://, etc.
1267
 *
1268
 * Return: void
1269
 */
1270
void gpsd_close(struct gps_device_t *session)
1271
0
{
1272
0
    if (0 > session->gpsdata.gps_fd) {
1273
        // UNALLOCATED_FD (-1) or PLACEHOLDING_FD (-2). Nothing to do.
1274
0
        return;
1275
0
    }
1276
1277
0
    if (0 < gpsd_serial_isatty(session)) {
1278
0
#ifdef TIOCNXCL
1279
        // This command resets the exclusive use of a terminal.
1280
0
        if (-1 == ioctl(session->gpsdata.gps_fd, (unsigned long)TIOCNXCL)) {
1281
                // cast for 32-bit intptr_t
1282
0
                GPSD_LOG(LOG_ERROR, &session->context->errout,
1283
0
                         "SER: ioctl(%ld, TIOCNXCL) failed: %s(%d)\n",
1284
0
                         (long)session->gpsdata.gps_fd,
1285
0
                         strerror(errno), errno);
1286
0
        }
1287
0
#endif  // TIOCNXCL
1288
0
        if (!session->context->readonly) {
1289
            // Be sure all output is sent.
1290
0
            if (0 != tcdrain(session->gpsdata.gps_fd)) {
1291
                // cast for 32-bit intptr_t
1292
0
                GPSD_LOG(LOG_ERROR, &session->context->errout,
1293
0
                         "SER: gpsd_close(%ld) tcdrain() failed: %s(%d)\n",
1294
0
                         (long)session->gpsdata.gps_fd,
1295
0
                         strerror(errno), errno);
1296
0
            }
1297
0
        }
1298
1299
        // Save current terminal parameters.  Why?
1300
0
        if (0 != tcgetattr(session->gpsdata.gps_fd, &session->ttyset_old)) {
1301
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
1302
0
                     "SER: gpsd_close() tcgetattr() failed: %s(%d)\n",
1303
0
                     strerror(errno), errno);
1304
0
        }
1305
1306
        // force hangup on close on systems that don't do HUPCL properly
1307
        // is this still an issue?
1308
0
        (void)cfsetispeed(&session->ttyset, (speed_t)B0);
1309
0
        (void)cfsetospeed(&session->ttyset, (speed_t)B0);
1310
0
        if (0 != tcsetattr(session->gpsdata.gps_fd, TCSANOW,
1311
0
                           &session->ttyset)) {
1312
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
1313
0
                     "SER: tcsetattr(B0) failed: %s(%d)\n",
1314
0
                     strerror(errno), errno);
1315
0
        }
1316
1317
        // this is the clean way to do it
1318
0
        session->ttyset_old.c_cflag |= HUPCL;
1319
0
        if (0 != tcsetattr(session->gpsdata.gps_fd, TCSANOW,
1320
0
                           &session->ttyset_old)) {
1321
0
            GPSD_LOG(LOG_ERROR, &session->context->errout,
1322
0
                     "SER: tcsetattr(%d) failed: %s(%d)\n",
1323
0
                     session->gpsdata.dev.baudrate, strerror(errno),
1324
0
                     errno);
1325
0
        }
1326
0
    } else if (SOURCE_TCP == session->sourcetype) {
1327
        /* tcp:// sensor source
1328
         * close, but setup for reconnect
1329
         */
1330
0
        (void)close(session->gpsdata.gps_fd);
1331
        // Prepare for a retry
1332
0
        session->gpsdata.gps_fd = PLACEHOLDING_FD;
1333
0
        session->opentime = time(NULL);
1334
0
    }
1335
    // cast for 32-bit intptr_t
1336
0
    GPSD_LOG(LOG_IO, &session->context->errout,
1337
0
             "SER: gpsd_close(%s), close(%ld)\n",
1338
0
             session->gpsdata.dev.path,
1339
0
             (long)session->gpsdata.gps_fd);
1340
0
    if (!BAD_SOCKET(session->gpsdata.gps_fd)) {
1341
0
        (void)close(session->gpsdata.gps_fd);
1342
0
        session->gpsdata.gps_fd = UNALLOCATED_FD;
1343
0
    }
1344
0
}
1345
1346
// vim: set expandtab shiftwidth=4