Coverage Report

Created: 2026-02-26 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntp-dev/ntpd/refclock_nmea.c
Line
Count
Source
1
/*
2
 * refclock_nmea.c - clock driver for an NMEA GPS CLOCK
3
 *    Michael Petry Jun 20, 1994
4
 *     based on refclock_heathn.c
5
 *
6
 * Updated to add support for Accord GPS Clock
7
 *    Venu Gopal Dec 05, 2007
8
 *    neo.venu@gmail.com, venugopal_d@pgad.gov.in
9
 *
10
 * Updated to process 'time1' fudge factor
11
 *    Venu Gopal May 05, 2008
12
 *
13
 * Converted to common PPSAPI code, separate PPS fudge time1
14
 * from serial timecode fudge time2.
15
 *    Dave Hart July 1, 2009
16
 *    hart@ntp.org, davehart@davehart.com
17
 */
18
19
#ifdef HAVE_CONFIG_H
20
#include <config.h>
21
#endif
22
23
#include "ntp_types.h"
24
25
#if defined(REFCLOCK) && defined(CLOCK_NMEA)
26
27
#define NMEA_WRITE_SUPPORT 0 /* no write support at the moment */
28
29
#include <sys/stat.h>
30
#include <stdio.h>
31
#include <ctype.h>
32
#ifdef HAVE_SYS_SOCKET_H
33
#include <sys/socket.h>
34
#endif
35
36
#include "ntpd.h"
37
#include "ntp_io.h"
38
#include "ntp_unixtime.h"
39
#include "ntp_refclock.h"
40
#include "ntp_stdlib.h"
41
#include "ntp_calgps.h"
42
#include "timespecops.h"
43
44
#ifdef HAVE_PPSAPI
45
# include "ppsapi_timepps.h"
46
# include "refclock_atom.h"
47
#endif /* HAVE_PPSAPI */
48
49
50
/*
51
 * This driver supports NMEA-compatible GPS receivers
52
 *
53
 * Prototype was refclock_trak.c, Thanks a lot.
54
 *
55
 * The receiver used spits out the NMEA sentences for boat navigation.
56
 * And you thought it was an information superhighway.  Try a raging river
57
 * filled with rapids and whirlpools that rip away your data and warp time.
58
 *
59
 * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in.
60
 * On startup if initialization of the PPSAPI fails, it will fall back
61
 * to the "normal" timestamps.
62
 *
63
 * The PPSAPI part of the driver understands fudge flag2 and flag3. If
64
 * flag2 is set, it will use the clear edge of the pulse. If flag3 is
65
 * set, kernel hardpps is enabled.
66
 *
67
 * GPS sentences other than RMC (the default) may be enabled by setting
68
 * the relevent bits of 'mode' in the server configuration line
69
 * server 127.127.20.x mode X
70
 *
71
 * bit 0 - enables RMC (1)
72
 * bit 1 - enables GGA (2)
73
 * bit 2 - enables GLL (4)
74
 * bit 3 - enables ZDA (8) - Standard Time & Date
75
 * bit 3 - enables ZDG (8) - Accord GPS Clock's custom sentence with GPS time
76
 *           very close to standard ZDA
77
 *
78
 * Multiple sentences may be selected except when ZDG/ZDA is selected.
79
 *
80
 * bit 4/5/6 - selects the baudrate for serial port :
81
 *    0 for 4800 (default)
82
 *    1 for 9600
83
 *    2 for 19200
84
 *    3 for 38400
85
 *    4 for 57600
86
 *    5 for 115200
87
 */
88
0
#define NMEA_MESSAGE_MASK 0x0000FF0FU
89
0
#define NMEA_BAUDRATE_MASK  0x00000070U
90
0
#define NMEA_BAUDRATE_SHIFT 4
91
92
0
#define NMEA_DELAYMEAS_MASK 0x00000080U
93
0
#define NMEA_EXTLOG_MASK  0x00010000U
94
#define NMEA_QUIETPPS_MASK  0x00020000U
95
0
#define NMEA_DATETRUST_MASK 0x00040000U
96
0
#define NMEA_IGNSTATUS_MASK 0x00080000U
97
98
0
#define NMEA_PROTO_IDLEN  4  /* tag name must be at least 4 chars */
99
#define NMEA_PROTO_MINLEN 6 /* min chars in sentence, excluding CS */
100
0
#define NMEA_PROTO_MAXLEN 80  /* max chars in sentence, excluding CS */
101
0
#define NMEA_PROTO_FIELDS 32  /* not official; limit on fields per record */
102
103
/*
104
 * We check the timecode format and decode its contents.  We only care
105
 * about a few of them, the most important being the $GPRMC format:
106
 *
107
 * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
108
 *
109
 * mode (0,1,2,3) selects sentence ANY/ALL, RMC, GGA, GLL, ZDA
110
 * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21
111
 * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F
112
 * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77
113
 *
114
 * Defining GPZDA to support Standard Time & Date
115
 * sentence. The sentence has the following format
116
 *
117
 *  $--ZDA,HHMMSS.SS,DD,MM,YYYY,TH,TM,*CS<CR><LF>
118
 *
119
 *  Apart from the familiar fields,
120
 *  'TH'    Time zone Hours
121
 *  'TM'    Time zone Minutes
122
 *
123
 * Defining GPZDG to support Accord GPS Clock's custom NMEA
124
 * sentence. The sentence has the following format
125
 *
126
 *  $GPZDG,HHMMSS.S,DD,MM,YYYY,AA.BB,V*CS<CR><LF>
127
 *
128
 *  It contains the GPS timestamp valid for next PPS pulse.
129
 *  Apart from the familiar fields,
130
 *  'AA.BB' denotes the signal strength( should be < 05.00 )
131
 *  'V'     denotes the GPS sync status :
132
 *     '0' indicates INVALID time,
133
 *     '1' indicates accuracy of +/-20 ms
134
 *     '2' indicates accuracy of +/-100 ns
135
 *
136
 * Defining PGRMF for Garmin GPS Fix Data
137
 * $PGRMF,WN,WS,DATE,TIME,LS,LAT,LAT_DIR,LON,LON_DIR,MODE,FIX,SPD,DIR,PDOP,TDOP
138
 * WN  -- GPS week number (weeks since 1980-01-06, mod 1024)
139
 * WS  -- GPS seconds in week
140
 * LS  -- GPS leap seconds, accumulated ( UTC + LS == GPS )
141
 * FIX -- Fix type: 0=nofix, 1=2D, 2=3D
142
 * DATE/TIME are standard date/time strings in UTC time scale
143
 *
144
 * The GPS time can be used to get the full century for the truncated
145
 * date spec.
146
 */
147
148
/*
149
 * Definitions
150
 */
151
#define DEVICE    "/dev/gps%d"  /* GPS serial device */
152
#define PPSDEV    "/dev/gpspps%d" /* PPSAPI device override */
153
0
#define SPEED232  B4800  /* uart speed (4800 bps) */
154
0
#define PRECISION (-9)  /* precision assumed (about 2 ms) */
155
#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
156
0
#define DATE_HOLD 16  /* seconds to hold on provided GPS date */
157
0
#define DATE_HLIM 4  /* when do we take ANY date format */
158
0
#define REFID   "GPS\0"  /* reference id */
159
0
#define DESCRIPTION "NMEA GPS Clock" /* who we are */
160
#ifndef O_NOCTTY
161
#define M_NOCTTY  0
162
#else
163
#define M_NOCTTY  O_NOCTTY
164
#endif
165
#ifndef O_NONBLOCK
166
#define M_NONBLOCK  0
167
#else
168
#define M_NONBLOCK  O_NONBLOCK
169
#endif
170
#define PPSOPENMODE (O_RDWR | M_NOCTTY | M_NONBLOCK)
171
172
/* NMEA sentence array indexes for those we use */
173
0
#define NMEA_GPRMC  0  /* recommended min. nav. */
174
0
#define NMEA_GPGGA  1  /* fix and quality */
175
0
#define NMEA_GPGLL  2  /* geo. lat/long */
176
0
#define NMEA_GPZDA  3  /* date/time */
177
/*
178
 * $GPZDG is a proprietary sentence that violates the spec, by not
179
 * using $P and an assigned company identifier to prefix the sentence
180
 * identifier.  When used with this driver, the system needs to be
181
 * isolated from other NTP networks, as it operates in GPS time, not
182
 * UTC as is much more common.  GPS time is >15 seconds different from
183
 * UTC due to not respecting leap seconds since 1970 or so.  Other
184
 * than the different timebase, $GPZDG is similar to $GPZDA.
185
 */
186
0
#define NMEA_GPZDG  4
187
0
#define NMEA_PGRMF  5
188
0
#define NMEA_PUBX04 6
189
#define NMEA_ARRAY_SIZE (NMEA_PUBX04 + 1)
190
191
/*
192
 * Sentence selection mode bits
193
 */
194
#define USE_GPRMC   0x00000001u
195
#define USE_GPGGA   0x00000002u
196
#define USE_GPGLL   0x00000004u
197
#define USE_GPZDA   0x00000008u
198
#define USE_PGRMF   0x00000100u
199
#define USE_PUBX04    0x00000200u
200
201
/* mapping from sentence index to controlling mode bit */
202
static const u_int32 sentence_mode[NMEA_ARRAY_SIZE] =
203
{
204
  USE_GPRMC,
205
  USE_GPGGA,
206
  USE_GPGLL,
207
  USE_GPZDA,
208
  USE_GPZDA,
209
  USE_PGRMF,
210
  USE_PUBX04
211
};
212
213
/* date formats we support */
214
enum date_fmt {
215
  DATE_1_DDMMYY,  /* use 1 field  with 2-digit year */
216
  DATE_3_DDMMYYYY /* use 3 fields with 4-digit year */
217
};
218
219
/* date type */
220
enum date_type {
221
  DTYP_NONE,
222
  DTYP_Y2D, /* 2-digit year */
223
  DTYP_W10B,  /* 10-bit week in GPS epoch */
224
  DTYP_Y4D, /* 4-digit (full) year */
225
  DTYP_WEXT /* extended week in GPS epoch */
226
};
227
228
/* results for 'field_init()'
229
 *
230
 * Note: If a checksum is present, the checksum test must pass OK or the
231
 * sentence is tagged invalid.
232
 */
233
0
#define CHECK_EMPTY  -1  /* no data      */
234
0
#define CHECK_INVALID 0  /* not a valid NMEA sentence  */
235
0
#define CHECK_VALID   1  /* valid but without checksum */
236
0
#define CHECK_CSVALID 2  /* valid with checksum OK */
237
238
/*
239
 * Unit control structure
240
 */
241
struct refclock_atom;
242
typedef struct refclock_atom TAtomUnit;
243
typedef struct {
244
#   ifdef HAVE_PPSAPI
245
  TAtomUnit atom;   /* PPSAPI structure */
246
  int   ppsapi_fd;  /* fd used with PPSAPI */
247
  u_char    ppsapi_tried; /* attempt PPSAPI once */
248
  u_char    ppsapi_lit; /* time_pps_create() worked */
249
#   endif /* HAVE_PPSAPI */
250
  uint16_t  rcvtout;  /* one-shot for sample expiration */
251
  u_char    ppsapi_gate;  /* system is on PPS */
252
  u_char    gps_time; /* use GPS time, not UTC */
253
  l_fp    last_reftime; /* last processed reference stamp */
254
  TNtpDatum last_gpsdate; /* last processed split date/time */
255
  u_short   hold_gpsdate; /* validity ticker for above */
256
  u_short   type_gpsdate; /* date info type for above */
257
  /* tally stats, reset each poll cycle */
258
  struct
259
  {
260
    u_int total;
261
    u_int accepted;
262
    u_int rejected;   /* GPS said not enough signal */
263
    u_int malformed;  /* Bad checksum, invalid date or time */
264
    u_int filtered;   /* mode bits, not GPZDG, same second */
265
    u_int pps_used;
266
  }
267
    tally;
268
  /* per sentence checksum seen flag */
269
  u_char    cksum_type[NMEA_ARRAY_SIZE];
270
271
  /* line assembly buffer (NMEAD support) */
272
  u_short lb_len;
273
  char  lb_buf[BMAX]; /* assembly buffer */
274
} nmea_unit;
275
276
/*
277
 * helper for faster field access
278
 */
279
typedef struct {
280
  char  *base;  /* buffer base    */
281
  char  *cptr;  /* current field ptr  */
282
  int    blen;  /* buffer length  */
283
  int    cidx;  /* current field index  */
284
} nmea_data;
285
286
/*
287
 * Function prototypes
288
 */
289
static  int nmea_start  (int, struct peer *);
290
static  void  nmea_shutdown (int, struct peer *);
291
static  void  nmea_receive  (struct recvbuf *);
292
static  void  nmea_poll (int, struct peer *);
293
static  void  nmea_procrec  (struct peer * const, l_fp);
294
#ifdef HAVE_PPSAPI
295
static  double  tabsdiffd (l_fp, l_fp);
296
static  void  nmea_control  (int, const struct refclockstat *,
297
         struct refclockstat *, struct peer *);
298
#define   NMEA_CONTROL  nmea_control
299
#else
300
#define   NMEA_CONTROL  noentry
301
#endif /* HAVE_PPSAPI */
302
static  void  nmea_timer  (int, struct peer *);
303
304
/* parsing helpers */
305
static int  field_init  (nmea_data * data, char * cp, int len);
306
static char * field_parse (nmea_data * data, int fn);
307
static void field_wipe  (nmea_data * data, ...);
308
static u_char parse_qual  (nmea_data * data, int idx,
309
         char tag, int inv);
310
static int  parse_time  (TCivilDate * jd, l_fp * fofs,
311
         nmea_data *, int idx);
312
static int  parse_date  (TCivilDate * jd, nmea_data *,
313
         int idx, enum date_fmt fmt);
314
static int  parse_gpsw  (TGpsDatum *, nmea_data *,
315
         int weekidx, int timeidx, int leapidx);
316
317
static int  nmead_open  (const char * device);
318
319
/*
320
 * If we want the driver to output sentences, too: re-enable the send
321
 * support functions by defining NMEA_WRITE_SUPPORT to non-zero...
322
 */
323
#if NMEA_WRITE_SUPPORT
324
static  void gps_send(int, const char *, struct peer *);
325
#endif /* NMEA_WRITE_SUPPORT */
326
327
/*
328
 * -------------------------------------------------------------------
329
 * Transfer vector
330
 * -------------------------------------------------------------------
331
 */
332
struct refclock refclock_nmea = {
333
  nmea_start,   /* start up driver */
334
  nmea_shutdown,    /* shut down driver */
335
  nmea_poll,    /* transmit poll message */
336
  NMEA_CONTROL,   /* fudge control */
337
  noentry,    /* initialize driver */
338
  noentry,    /* buginfo */
339
  nmea_timer    /* called once per second */
340
};
341
342
343
/*
344
 * -------------------------------------------------------------------
345
 * nmea_start - open the GPS devices and initialize data for processing
346
 *
347
 * return 0 on error, 1 on success. Even on error the peer structures
348
 * must be in a state that permits 'nmea_shutdown()' to clean up all
349
 * resources, because it will be called immediately to do so.
350
 * -------------------------------------------------------------------
351
 */
352
static int
353
nmea_start(
354
  int   unit,
355
  struct peer * peer
356
  )
357
0
{
358
0
  struct refclockproc * const pp = peer->procptr;
359
0
  nmea_unit * const   up = emalloc_zero(sizeof(*up));
360
0
  char        device[20];
361
0
  size_t        devlen;
362
0
  u_int32       rate;
363
0
  int       baudrate;
364
365
  /* Get baudrate choice from mode byte bits 4/5/6 */
366
0
  rate = (peer->ttl & NMEA_BAUDRATE_MASK) >> NMEA_BAUDRATE_SHIFT;
367
368
0
  switch (rate) {
369
0
  default:
370
0
  case 0:
371
0
    baudrate = SPEED232;
372
0
    break;
373
0
  case 1:
374
0
    baudrate = B9600;
375
0
    break;
376
0
  case 2:
377
0
    baudrate = B19200;
378
0
    break;
379
0
  case 3:
380
0
    baudrate = B38400;
381
0
    break;
382
0
#   ifdef B57600
383
0
  case 4:
384
0
    baudrate = B57600;
385
0
    break;
386
0
#   endif
387
0
#   ifdef B115200
388
0
  case 5:
389
0
    baudrate = B115200;
390
0
    break;
391
0
#   endif
392
0
  }
393
394
  /* Allocate and initialize unit structure */
395
0
  pp->unitptr = (caddr_t)up;
396
0
  pp->io.fd = -1;
397
0
  pp->io.clock_recv = nmea_receive;
398
0
  pp->io.srcclock = peer;
399
0
  pp->io.datalen = 0;
400
  /* force change detection on first valid message */
401
0
  memset(&up->last_reftime, 0xFF, sizeof(up->last_reftime));
402
0
  memset(&up->last_gpsdate, 0x00, sizeof(up->last_gpsdate));
403
  /* force checksum on GPRMC, see below */
404
0
  up->cksum_type[NMEA_GPRMC] = CHECK_CSVALID;
405
#   ifdef HAVE_PPSAPI
406
  up->ppsapi_fd = -1;
407
#   endif /* HAVE_PPSAPI */
408
0
  ZERO(up->tally);
409
410
  /* Initialize miscellaneous variables */
411
0
  peer->precision = PRECISION;
412
0
  pp->clockdesc = DESCRIPTION;
413
0
  memcpy(&pp->refid, REFID, 4);
414
415
  /* Open serial port. Use CLK line discipline, if available. */
416
0
  devlen = snprintf(device, sizeof(device), DEVICE, unit);
417
0
  if (devlen >= sizeof(device)) {
418
0
    msyslog(LOG_ERR, "%s clock device name too long",
419
0
      refnumtoa(&peer->srcadr));
420
0
    return FALSE; /* buffer overflow */
421
0
  }
422
0
  pp->io.fd = refclock_open(&peer->srcadr, device, baudrate, LDISC_CLK);
423
0
  if (0 >= pp->io.fd) {
424
0
    pp->io.fd = nmead_open(device);
425
0
    if (-1 == pp->io.fd)
426
0
      return FALSE;
427
0
  }
428
429
  /* succeed if this clock can be added */
430
0
  return io_addclock(&pp->io) != 0;
431
0
}
432
433
/*
434
 * -------------------------------------------------------------------
435
 * nmea_shutdown - shut down a GPS clock
436
 *
437
 * NOTE this routine is called after nmea_start() returns failure,
438
 * as well as during a normal shutdown due to ntpq :config unpeer.
439
 * -------------------------------------------------------------------
440
 */
441
static void
442
nmea_shutdown(
443
  int           unit,
444
  struct peer * peer
445
  )
446
0
{
447
0
  struct refclockproc * const pp = peer->procptr;
448
0
  nmea_unit     * const up = (nmea_unit *)pp->unitptr;
449
450
0
  UNUSED_ARG(unit);
451
452
0
  if (up != NULL) {
453
#     ifdef HAVE_PPSAPI
454
    if (up->ppsapi_lit)
455
      time_pps_destroy(up->atom.handle);
456
    ppsdev_close(pp->io.fd, up->ppsapi_fd);
457
#     endif
458
0
    free(up);
459
0
  }
460
0
  pp->unitptr = (caddr_t)NULL;
461
0
  if (-1 != pp->io.fd)
462
0
    io_closeclock(&pp->io);
463
0
  pp->io.fd = -1;
464
0
}
465
466
/*
467
 * -------------------------------------------------------------------
468
 * nmea_control - configure fudge params
469
 * -------------------------------------------------------------------
470
 */
471
#ifdef HAVE_PPSAPI
472
static void
473
nmea_control(
474
  int                         unit,
475
  const struct refclockstat * in_st,
476
  struct refclockstat       * out_st,
477
  struct peer               * peer
478
  )
479
{
480
  struct refclockproc * const pp = peer->procptr;
481
  nmea_unit     * const up = (nmea_unit *)pp->unitptr;
482
483
  char   device[32];
484
  size_t devlen;
485
486
  UNUSED_ARG(in_st);
487
  UNUSED_ARG(out_st);
488
489
  /*
490
   * PPS control
491
   *
492
   * If /dev/gpspps$UNIT can be opened that will be used for
493
   * PPSAPI.  On Linux, a PPS device mathing the TTY will be
494
   * searched for and possibly created on the fly.  Otherwise, the
495
   * GPS serial device /dev/gps$UNIT already opened is used for
496
   * PPSAPI as well. (This might not work, in which case the PPS
497
   * API remains unavailable...)
498
   */
499
500
  /* Light up the PPSAPI interface if not yet attempted. */
501
  if ((CLK_FLAG1 & pp->sloppyclockflag) && !up->ppsapi_tried) {
502
    const char *ppsname = device;
503
    up->ppsapi_tried = TRUE;
504
    /* get FD for the pps device; might be the tty itself! */
505
    devlen = snprintf(device, sizeof(device), PPSDEV, unit);
506
    if (devlen >= sizeof(device)) {
507
      msyslog(LOG_ERR, "%s PPS device name too long",
508
        refnumtoa(&peer->srcadr));
509
      ppsname = NULL;
510
    }
511
    up->ppsapi_fd = ppsdev_reopen(
512
      &peer->srcadr,
513
      pp->io.fd, up->ppsapi_fd,
514
      ppsname, PPSOPENMODE, (S_IRUSR|S_IWUSR));
515
    /* note 1: the pps fd might be the same as the tty fd
516
     * note 2: the current PPS fd remains valid until
517
     *  - the clock is shut down
518
     *  - flag1 is set again after being cleared
519
     */
520
    if (refclock_ppsapi(up->ppsapi_fd, &up->atom)) {
521
      /* use the PPS API for our own purposes now. */
522
      up->ppsapi_lit = refclock_params(
523
        pp->sloppyclockflag, &up->atom);
524
      if (!up->ppsapi_lit) {
525
        /* failed to configure, drop PPS unit */
526
        time_pps_destroy(up->atom.handle);
527
        msyslog(LOG_WARNING,
528
          "%s set PPSAPI params fails",
529
          refnumtoa(&peer->srcadr));
530
      }
531
    } else {
532
      msyslog(LOG_WARNING,
533
        "%s flag1 1 but PPSAPI fails",
534
        refnumtoa(&peer->srcadr));
535
    }
536
  }
537
538
  /* shut down PPS API if activated */
539
  if ( !(CLK_FLAG1 & pp->sloppyclockflag) && up->ppsapi_tried) {
540
    /* shutdown PPS API */
541
    if (up->ppsapi_lit)
542
      time_pps_destroy(up->atom.handle);
543
    up->atom.handle = 0;
544
    /* do !!NOT!! close/drop PPS fd here! */
545
546
    /* clear markers and peer items */
547
    up->ppsapi_gate  = FALSE;
548
    up->ppsapi_lit   = FALSE;
549
    up->ppsapi_tried = FALSE;
550
551
    peer->flags &= ~FLAG_PPS;
552
    peer->precision = PRECISION;
553
  }
554
}
555
#endif /* HAVE_PPSAPI */
556
557
/*
558
 * -------------------------------------------------------------------
559
 * nmea_timer - called once per second
560
 *
561
 * Usually 'nmea_receive()' can get a timestamp every second, but at
562
 * least one Motorola unit needs prompting each time. Doing so in
563
 * 'nmea_poll()' gives only one sample per poll cycle, which actually
564
 * defeats the purpose of the median filter. Polling once per second
565
 * seems a much better idea.
566
 *
567
 * Also takes care of sample expiration if the receiver fails to
568
 * provide new input data.
569
 * -------------------------------------------------------------------
570
 */
571
static void
572
nmea_timer(
573
  int       unit,
574
  struct peer * peer
575
  )
576
0
{
577
0
  struct refclockproc * const pp = peer->procptr;
578
0
  nmea_unit     * const up = (nmea_unit *)pp->unitptr;
579
580
0
  UNUSED_ARG(unit);
581
582
#   if NMEA_WRITE_SUPPORT
583
584
  if (-1 != pp->io.fd) /* any mode bits to evaluate here? */
585
    gps_send(pp->io.fd, "$PMOTG,RMC,0000*1D\r\n", peer);
586
587
#   endif /* NMEA_WRITE_SUPPORT */
588
589
  /* receive timeout occurred? */
590
0
  if (up->rcvtout) {
591
0
    --up->rcvtout;
592
0
  } else if (pp->codeproc != pp->coderecv) {
593
    /* expire one (the oldest) sample, if any */
594
0
    refclock_samples_expire(pp, 1);
595
    /* reset message assembly buffer */
596
0
    up->lb_buf[0] = '\0';
597
0
    up->lb_len    = 0;
598
0
  }
599
600
0
  if (up->hold_gpsdate && (--up->hold_gpsdate < DATE_HLIM))
601
0
    up->type_gpsdate = DTYP_NONE;
602
0
}
603
604
/*
605
 * -------------------------------------------------------------------
606
 * nmea_procrec - receive data from the serial interface
607
 *
608
 * This is the workhorse for NMEA data evaluation:
609
 *
610
 * + it checks all NMEA data, and rejects sentences that are not valid
611
 *   NMEA sentences
612
 * + it checks whether a sentence is known and to be used
613
 * + it parses the time and date data from the NMEA data string and
614
 *   augments the missing bits. (century in date, whole date, ...)
615
 * + it rejects data that is not from the first accepted sentence in a
616
 *   burst
617
 * + it eventually replaces the receive time with the PPS edge time.
618
 * + it feeds the data to the internal processing stages.
619
 *
620
 * This function assumes a non-empty line in the unit line buffer.
621
 * -------------------------------------------------------------------
622
 */
623
static void
624
nmea_procrec(
625
  struct peer * const peer,
626
  l_fp        rd_timestamp
627
  )
628
0
{
629
  /* declare & init control structure pointers */
630
0
  struct refclockproc * const pp = peer->procptr;
631
0
  nmea_unit     * const up = (nmea_unit*)pp->unitptr;
632
633
  /* Use these variables to hold data until we decide its worth keeping */
634
0
  nmea_data rdata;
635
0
  l_fp    rd_reftime;
636
637
  /* working stuff */
638
0
  TCivilDate  date; /* to keep & convert the time stamp */
639
0
  TGpsDatum wgps; /* week time storage */
640
0
  TNtpDatum dntp;
641
0
  l_fp    tofs; /* offset to full-second reftime */
642
  /* results of sentence/date/time parsing */
643
0
  u_char    sentence; /* sentence tag */
644
0
  int   checkres;
645
0
  int   warp;   /* warp to GPS base date */
646
0
  char *    cp;
647
0
  int   rc_date, rc_time;
648
0
  u_short   rc_dtyp;
649
#   ifdef HAVE_PPSAPI
650
  int   withpps = 0;
651
#   endif /* HAVE_PPSAPI */
652
653
  /* make sure data has defined pristine state */
654
0
  ZERO(tofs);
655
0
  ZERO(date);
656
0
  ZERO(wgps);
657
0
  ZERO(dntp);
658
659
  /*
660
   * Read the timecode and timestamp, then initialize field
661
   * processing. The <CR><LF> at the NMEA line end is translated
662
   * to <LF><LF> by the terminal input routines on most systems,
663
   * and this gives us one spurious empty read per record which we
664
   * better ignore silently.
665
   */
666
0
  checkres = field_init(&rdata, up->lb_buf, up->lb_len);
667
0
  switch (checkres) {
668
669
0
  case CHECK_INVALID:
670
0
    DPRINTF(1, ("%s invalid data: '%s'\n",
671
0
      refnumtoa(&peer->srcadr), up->lb_buf));
672
0
    refclock_report(peer, CEVNT_BADREPLY);
673
0
    return;
674
675
0
  case CHECK_EMPTY:
676
0
    return;
677
678
0
  default:
679
0
    DPRINTF(1, ("%s gpsread: %d '%s'\n",
680
0
      refnumtoa(&peer->srcadr), up->lb_len,
681
0
      up->lb_buf));
682
0
    break;
683
0
  }
684
0
  up->tally.total++;
685
686
  /*
687
   * --> below this point we have a valid NMEA sentence <--
688
   *
689
   * Check sentence name. Skip first 2 chars (talker ID) in most
690
   * cases, to allow for $GLGGA and $GPGGA etc. Since the name
691
   * field has at least 5 chars we can simply shift the field
692
   * start.
693
   */
694
0
  cp = field_parse(&rdata, 0);
695
0
  if      (strncmp(cp + 2, "RMC,", 4) == 0)
696
0
    sentence = NMEA_GPRMC;
697
0
  else if (strncmp(cp + 2, "GGA,", 4) == 0)
698
0
    sentence = NMEA_GPGGA;
699
0
  else if (strncmp(cp + 2, "GLL,", 4) == 0)
700
0
    sentence = NMEA_GPGLL;
701
0
  else if (strncmp(cp + 2, "ZDA,", 4) == 0)
702
0
    sentence = NMEA_GPZDA;
703
0
  else if (strncmp(cp + 2, "ZDG,", 4) == 0)
704
0
    sentence = NMEA_GPZDG;
705
0
  else if (strncmp(cp,   "PGRMF,", 6) == 0)
706
0
    sentence = NMEA_PGRMF;
707
0
  else if (strncmp(cp,   "PUBX,04,", 8) == 0)
708
0
    sentence = NMEA_PUBX04;
709
0
  else
710
0
    return; /* not something we know about */
711
712
  /* Eventually output delay measurement now. */
713
0
  if (peer->ttl & NMEA_DELAYMEAS_MASK) {
714
0
    mprintf_clock_stats(&peer->srcadr, "delay %0.6f %.*s",
715
0
       ldexp(rd_timestamp.l_uf, -32),
716
0
       (int)(strchr(up->lb_buf, ',') - up->lb_buf),
717
0
       up->lb_buf);
718
0
  }
719
720
  /* See if I want to process this message type */
721
0
  if ((peer->ttl & NMEA_MESSAGE_MASK) &&
722
0
      !(peer->ttl & sentence_mode[sentence])) {
723
0
    up->tally.filtered++;
724
0
    return;
725
0
  }
726
727
  /*
728
   * make sure it came in clean
729
   *
730
   * Apparently, older NMEA specifications (which are expensive)
731
   * did not require the checksum for all sentences.  $GPMRC is
732
   * the only one so far identified which has always been required
733
   * to include a checksum.
734
   *
735
   * Today, most NMEA GPS receivers checksum every sentence.  To
736
   * preserve its error-detection capabilities with modern GPSes
737
   * while allowing operation without checksums on all but $GPMRC,
738
   * we keep track of whether we've ever seen a valid checksum on
739
   * a given sentence, and if so, reject future instances without
740
   * checksum.  ('up->cksum_type[NMEA_GPRMC]' is set in
741
   * 'nmea_start()' to enforce checksums for $GPRMC right from the
742
   * start.)
743
   */
744
0
  if (up->cksum_type[sentence] <= (u_char)checkres) {
745
0
    up->cksum_type[sentence] = (u_char)checkres;
746
0
  } else {
747
0
    DPRINTF(1, ("%s checksum missing: '%s'\n",
748
0
      refnumtoa(&peer->srcadr), up->lb_buf));
749
0
    refclock_report(peer, CEVNT_BADREPLY);
750
0
    up->tally.malformed++;
751
0
    return;
752
0
  }
753
754
  /*
755
   * $GPZDG provides GPS time not UTC, and the two mix poorly.
756
   * Once have processed a $GPZDG, do not process any further UTC
757
   * sentences (all but $GPZDG currently).
758
   */
759
0
  if (sentence == NMEA_GPZDG) {
760
0
    if (!up->gps_time) {
761
0
      msyslog(LOG_INFO,
762
0
        "%s using GPS time as if it were UTC",
763
0
        refnumtoa(&peer->srcadr));
764
0
      up->gps_time = 1;
765
0
    }
766
0
  } else {
767
0
    if (up->gps_time) {
768
0
      up->tally.filtered++;
769
0
      return;
770
0
    }
771
0
  }
772
773
0
  DPRINTF(1, ("%s processing %d bytes, timecode '%s'\n",
774
0
    refnumtoa(&peer->srcadr), up->lb_len, up->lb_buf));
775
776
  /*
777
   * Grab fields depending on clock string type and possibly wipe
778
   * sensitive data from the last timecode.
779
   */
780
0
  rc_date = -1; /* assume we have to do day-time mapping */
781
0
  rc_dtyp = DTYP_NONE;
782
0
        switch (sentence) {
783
784
0
  case NMEA_GPRMC:
785
    /* Check quality byte, fetch data & time */
786
0
    rc_time  = parse_time(&date, &tofs, &rdata, 1);
787
0
    pp->leap = parse_qual(&rdata, 2, 'A', 0);
788
0
    if (up->type_gpsdate <= DTYP_Y2D) {
789
0
      rc_date = parse_date(&date, &rdata, 9, DATE_1_DDMMYY);
790
0
      rc_dtyp = DTYP_Y2D;
791
0
    }
792
0
    if (CLK_FLAG4 & pp->sloppyclockflag)
793
0
      field_wipe(&rdata, 3, 4, 5, 6, -1);
794
0
    break;
795
796
0
  case NMEA_GPGGA:
797
    /* Check quality byte, fetch time only */
798
0
    rc_time  = parse_time(&date, &tofs, &rdata, 1);
799
0
    pp->leap = parse_qual(&rdata, 6, '0', 1);
800
0
    if (CLK_FLAG4 & pp->sloppyclockflag)
801
0
      field_wipe(&rdata, 2, 4, -1);
802
0
    break;
803
804
0
  case NMEA_GPGLL:
805
    /* Check quality byte, fetch time only */
806
0
    rc_time  = parse_time(&date, &tofs, &rdata, 5);
807
0
    pp->leap = parse_qual(&rdata, 6, 'A', 0);
808
0
    if (CLK_FLAG4 & pp->sloppyclockflag)
809
0
      field_wipe(&rdata, 1, 3, -1);
810
0
    break;
811
812
0
  case NMEA_GPZDA:
813
    /* No quality.  Assume best, fetch time & full date */
814
0
    rc_time = parse_time(&date, &tofs, &rdata, 1);
815
0
    if (up->type_gpsdate <= DTYP_Y4D) {
816
0
      rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY);
817
0
      rc_dtyp = DTYP_Y4D;
818
0
    }
819
0
    break;
820
821
0
  case NMEA_GPZDG:
822
    /* Check quality byte, fetch time & full date */
823
0
    rc_time  = parse_time(&date, &tofs, &rdata, 1);
824
0
    pp->leap = parse_qual(&rdata, 4, '0', 1);
825
0
    --tofs.l_ui; /* GPZDG gives *following* second */
826
0
    if (up->type_gpsdate <= DTYP_Y4D) {
827
0
      rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY);
828
0
      rc_dtyp = DTYP_Y4D;
829
0
    }
830
0
    break;
831
832
0
  case NMEA_PGRMF:
833
    /* get time, qualifier and GPS weektime. */
834
0
    rc_time = parse_time(&date, &tofs, &rdata, 4);
835
0
    if (up->type_gpsdate <= DTYP_W10B) {
836
0
      rc_date = parse_gpsw(&wgps, &rdata, 1, 2, 5);
837
0
      rc_dtyp = DTYP_W10B;
838
0
    }
839
0
    pp->leap = parse_qual(&rdata, 11, '0', 1);
840
0
    if (CLK_FLAG4 & pp->sloppyclockflag)
841
0
      field_wipe(&rdata, 6, 8, -1);
842
0
    break;
843
844
0
  case NMEA_PUBX04:
845
    /* PUBX,04 is peculiar. The UTC time-of-week is the *internal*
846
     * time base, which is not exactly on par with the fix time.
847
     */
848
0
    rc_time = parse_time(&date, &tofs, &rdata, 2);
849
0
    if (up->type_gpsdate <= DTYP_WEXT) {
850
0
      rc_date = parse_gpsw(&wgps, &rdata, 5, 4, -1);
851
0
      rc_dtyp = DTYP_WEXT;
852
0
    }
853
0
    break;
854
855
0
  default:
856
0
    INVARIANT(0);  /* Coverity 97123 */
857
0
    return;
858
0
  }
859
860
  /* ignore receiver status? [bug 3694] */
861
0
  if (peer->ttl & NMEA_IGNSTATUS_MASK) { /* assume always good? */
862
0
    pp->leap = LEAP_NOWARNING;
863
0
  }
864
  
865
  /* check clock sanity; [bug 2143] */
866
0
  if (pp->leap == LEAP_NOTINSYNC) { /* no good status? */
867
0
    checkres = CEVNT_PROP;
868
0
    up->tally.rejected++;
869
0
  }
870
  /* Check sanity of time-of-day. */
871
0
  else if (rc_time == 0) { /* no time or conversion error? */
872
0
    checkres = CEVNT_BADTIME;
873
0
    up->tally.malformed++;
874
0
  }
875
  /* Check sanity of date. */
876
0
  else if (rc_date == 0) { /* no date or conversion error? */
877
0
    checkres = CEVNT_BADDATE;
878
0
    up->tally.malformed++;
879
0
  }
880
0
  else {
881
0
    checkres = -1;
882
0
  }
883
884
0
  if (checkres != -1) {
885
0
    refclock_save_lcode(pp, up->lb_buf, up->lb_len);
886
0
    refclock_report(peer, checkres);
887
0
    return;
888
0
  }
889
890
  /* See if we can augment the receive time stamp. If not, apply
891
   * fudge time 2 to the receive time stamp directly.
892
   */
893
#   ifdef HAVE_PPSAPI
894
  if (up->ppsapi_lit && pp->leap != LEAP_NOTINSYNC)
895
    withpps = refclock_ppsaugment(
896
      &up->atom, &rd_timestamp,
897
      pp->fudgetime2, pp->fudgetime1);
898
  else
899
#   endif /* HAVE_PPSAPI */
900
0
    rd_timestamp = ntpfp_with_fudge(
901
0
      rd_timestamp, pp->fudgetime2);
902
903
  /* set the GPS base date, if possible */
904
0
  warp = !(peer->ttl & NMEA_DATETRUST_MASK);
905
0
  if (rc_dtyp != DTYP_NONE) {
906
0
    DPRINTF(1, ("%s saving date, type=%hu\n",
907
0
          refnumtoa(&peer->srcadr), rc_dtyp));
908
0
    switch (rc_dtyp) {
909
0
    case DTYP_W10B:
910
0
      up->last_gpsdate = gpsntp_from_gpscal_ex(
911
0
        &wgps, (warp = TRUE));
912
0
      break;
913
0
    case DTYP_WEXT:
914
0
      up->last_gpsdate = gpsntp_from_gpscal_ex(
915
0
        &wgps, warp);
916
0
      break;
917
0
    default:
918
0
      up->last_gpsdate = gpsntp_from_calendar_ex(
919
0
        &date, tofs, warp);
920
0
      break;
921
0
    }
922
0
    up->type_gpsdate = rc_dtyp;
923
0
    up->hold_gpsdate = DATE_HOLD;
924
0
  }
925
  /* now convert and possibly extend/expand the time stamp. */
926
0
  if (up->hold_gpsdate) { /* time of day, based */
927
0
    dntp = gpsntp_from_daytime2_ex(
928
0
      &date, tofs, &up->last_gpsdate, warp);
929
0
  } else {   /* time of day, floating */
930
0
    dntp = gpsntp_from_daytime1_ex(
931
0
      &date, tofs, rd_timestamp, warp);
932
0
  }
933
934
0
  if (debug) {
935
    /* debug print time stamp */
936
0
    gpsntp_to_calendar(&date, &dntp);
937
#     ifdef HAVE_PPSAPI
938
    DPRINTF(1, ("%s effective timecode: %s (%s PPS)\n",
939
          refnumtoa(&peer->srcadr),
940
          ntpcal_iso8601std(NULL, 0, &date),
941
          (withpps ? "with" : "without")));
942
#     else /* ?HAVE_PPSAPI */
943
0
    DPRINTF(1, ("%s effective timecode: %s\n",
944
0
          refnumtoa(&peer->srcadr),
945
0
          ntpcal_iso8601std(NULL, 0, &date)));
946
0
#     endif /* !HAVE_PPSAPI */
947
0
  }
948
949
  /* Get the reference time stamp from the calendar buffer.
950
   * Process the new sample in the median filter and determine the
951
   * timecode timestamp, but only if the PPS is not in control.
952
   * Discard sentence if reference time did not change.
953
   */
954
0
  rd_reftime = ntpfp_from_ntpdatum(&dntp);
955
0
  if (L_ISEQU(&up->last_reftime, &rd_reftime)) {
956
    /* Do not touch pp->a_lastcode on purpose! */
957
0
    up->tally.filtered++;
958
0
    return;
959
0
  }
960
0
  up->last_reftime = rd_reftime;
961
962
0
  DPRINTF(1, ("%s using '%s'\n",
963
0
        refnumtoa(&peer->srcadr), up->lb_buf));
964
965
  /* Data will be accepted. Update stats & log data. */
966
0
  up->tally.accepted++;
967
0
  refclock_save_lcode(pp, up->lb_buf, up->lb_len);
968
0
  pp->lastrec = rd_timestamp;
969
970
  /* If we have PPS augmented receive time, we *must* have a
971
   * working PPS source and we must set the flags accordingly.
972
   */
973
#   ifdef HAVE_PPSAPI
974
  if (withpps) {
975
    up->ppsapi_gate = TRUE;
976
    peer->precision = PPS_PRECISION;
977
    if (tabsdiffd(rd_reftime, rd_timestamp) < 0.5) {
978
      if ( ! (peer->ttl & NMEA_QUIETPPS_MASK))
979
        peer->flags |= FLAG_PPS;
980
      DPRINTF(2, ("%s PPS_RELATE_PHASE\n",
981
            refnumtoa(&peer->srcadr)));
982
      up->tally.pps_used++;
983
    } else {
984
      DPRINTF(2, ("%s PPS_RELATE_EDGE\n",
985
            refnumtoa(&peer->srcadr)));
986
    }
987
    /* !Note! 'FLAG_PPS' is reset in 'nmea_poll()' */
988
  }
989
#   endif /* HAVE_PPSAPI */
990
  /* Whether the receive time stamp is PPS-augmented or not,
991
   * the proper fudge offset is already applied. There's no
992
   * residual fudge to process.
993
   */
994
0
  refclock_process_offset(pp, rd_reftime, rd_timestamp, 0.0);
995
0
  up->rcvtout = 2;
996
0
}
997
998
/*
999
 * -------------------------------------------------------------------
1000
 * nmea_receive - receive data from the serial interface
1001
 *
1002
 * With serial IO only, a single call to 'refclock_gtlin()' to get the
1003
 * string would suffice to get the NMEA data. When using NMEAD, this
1004
 * does unfortunately no longer hold, since TCP is stream oriented and
1005
 * not line oriented, and there's no one to do the line-splitting work
1006
 * of the TTY driver in line/cooked mode.
1007
 *
1008
 * So we have to do this manually here, and we have to live with the
1009
 * fact that there could be more than one sentence in a receive buffer.
1010
 * Likewise, there can be partial messages on either end. (Strictly
1011
 * speaking, a receive buffer could also contain just a single fragment,
1012
 * though that's unlikely.)
1013
 *
1014
 * We deal with that by scanning the input buffer, copying bytes from
1015
 * the receive buffer to the assembly buffer as we go and calling the
1016
 * record processor every time we hit a CR/LF, provided the resulting
1017
 * line is not empty. Any leftovers are kept for the next round.
1018
 *
1019
 * Note: When used with a serial data stream, there's no change to the
1020
 * previous line-oriented input: One line is copied to the buffer and
1021
 * processed per call. Only with NMEAD the behavior changes, and the
1022
 * timing is badly affected unless a PPS channel is also associated with
1023
 * the clock instance. TCP leaves us nothing to improve on here.
1024
 * -------------------------------------------------------------------
1025
 */
1026
static void
1027
nmea_receive(
1028
  struct recvbuf * rbufp
1029
  )
1030
0
{
1031
  /* declare & init control structure pointers */
1032
0
  struct peer     * const peer = rbufp->recv_peer;
1033
0
  struct refclockproc * const pp = peer->procptr;
1034
0
  nmea_unit     * const up = (nmea_unit*)pp->unitptr;
1035
1036
0
  const char *sp, *se;
1037
0
  char     *dp, *de;
1038
1039
  /* paranoia check: */
1040
0
  if (up->lb_len >= sizeof(up->lb_buf))
1041
0
    up->lb_len = 0;
1042
1043
  /* pick up last assembly position; leave room for NUL */
1044
0
  dp = up->lb_buf + up->lb_len;
1045
0
  de = up->lb_buf + sizeof(up->lb_buf) - 1;
1046
  /* set up input range */
1047
0
  sp = (const char *)rbufp->recv_buffer;
1048
0
  se = sp + rbufp->recv_length;
1049
1050
  /* walk over the input data, dropping parity bits and control
1051
   * chars as we go, and calling the record processor for each
1052
   * complete non-empty line.
1053
   */
1054
0
  while (sp != se) {
1055
0
    char ch = (*sp++ & 0x7f);
1056
0
    if (dp == up->lb_buf) {
1057
0
      if (ch == '$')
1058
0
        *dp++ = ch;
1059
0
    } else if (dp > de) {
1060
0
      dp = up->lb_buf;
1061
0
    } else if (ch == '\n' || ch == '\r') {
1062
0
      *dp = '\0';
1063
0
      up->lb_len = (int)(dp - up->lb_buf);
1064
0
      dp = up->lb_buf;
1065
0
      nmea_procrec(peer, rbufp->recv_time);
1066
0
    } else if (ch >= 0x20 && ch < 0x7f) {
1067
0
      *dp++ = ch;
1068
0
    }
1069
0
  }
1070
  /* update state to keep for next round */
1071
0
  *dp = '\0';
1072
0
  up->lb_len = (int)(dp - up->lb_buf);
1073
0
}
1074
1075
/*
1076
 * -------------------------------------------------------------------
1077
 * nmea_poll - called by the transmit procedure
1078
 *
1079
 * Does the necessary bookkeeping stuff to keep the reported state of
1080
 * the clock in sync with reality.
1081
 *
1082
 * We go to great pains to avoid changing state here, since there may
1083
 * be more than one eavesdropper receiving the same timecode.
1084
 * -------------------------------------------------------------------
1085
 */
1086
static void
1087
nmea_poll(
1088
  int           unit,
1089
  struct peer * peer
1090
  )
1091
0
{
1092
0
  struct refclockproc * const pp = peer->procptr;
1093
0
  nmea_unit     * const up = (nmea_unit *)pp->unitptr;
1094
1095
  /*
1096
   * Process median filter samples. If none received, declare a
1097
   * timeout and keep going.
1098
   */
1099
#   ifdef HAVE_PPSAPI
1100
  /*
1101
   * If we don't have PPS pulses and time stamps, turn PPS down
1102
   * for now.
1103
   */
1104
  if (!up->ppsapi_gate) {
1105
    peer->flags &= ~FLAG_PPS;
1106
    peer->precision = PRECISION;
1107
  } else {
1108
    up->ppsapi_gate = FALSE;
1109
  }
1110
#   endif /* HAVE_PPSAPI */
1111
1112
  /*
1113
   * If the median filter is empty, claim a timeout. Else process
1114
   * the input data and keep the stats going.
1115
   */
1116
0
  if (pp->coderecv == pp->codeproc) {
1117
0
    peer->flags &= ~FLAG_PPS;
1118
0
    if (pp->currentstatus < CEVNT_TIMEOUT)
1119
0
        refclock_report(peer, CEVNT_TIMEOUT);
1120
0
    memset(&up->last_gpsdate, 0, sizeof(up->last_gpsdate));
1121
0
  } else {
1122
0
    pp->polls++;
1123
0
    pp->lastref = pp->lastrec;
1124
0
    refclock_receive(peer);
1125
0
    if (pp->currentstatus > CEVNT_NOMINAL)
1126
0
        refclock_report(peer, CEVNT_NOMINAL);
1127
0
  }
1128
1129
  /*
1130
   * If extended logging is required, write the tally stats to the
1131
   * clockstats file; otherwise just do a normal clock stats
1132
   * record. Clear the tally stats anyway.
1133
  */
1134
0
  if (peer->ttl & NMEA_EXTLOG_MASK) {
1135
    /* Log & reset counters with extended logging */
1136
0
    const char *nmea = pp->a_lastcode;
1137
0
    if (*nmea == '\0') nmea = "(none)";
1138
0
    mprintf_clock_stats(
1139
0
      &peer->srcadr, "%s  %u %u %u %u %u %u",
1140
0
      nmea,
1141
0
      up->tally.total, up->tally.accepted,
1142
0
      up->tally.rejected, up->tally.malformed,
1143
0
      up->tally.filtered, up->tally.pps_used);
1144
0
  } else {
1145
0
    record_clock_stats(&peer->srcadr, pp->a_lastcode);
1146
0
  }
1147
0
  ZERO(up->tally);
1148
0
}
1149
1150
#if NMEA_WRITE_SUPPORT
1151
/*
1152
 * -------------------------------------------------------------------
1153
 *  gps_send(fd, cmd, peer) Sends a command to the GPS receiver.
1154
 *   as in gps_send(fd, "rqts,u", peer);
1155
 *
1156
 * If 'cmd' starts with a '$' it is assumed that this command is in raw
1157
 * format, that is, starts with '$', ends with '<cr><lf>' and that any
1158
 * checksum is correctly provided; the command will be send 'as is' in
1159
 * that case. Otherwise the function will create the necessary frame
1160
 * (start char, chksum, final CRLF) on the fly.
1161
 *
1162
 * We don't currently send any data, but would like to send RTCM SC104
1163
 * messages for differential positioning. It should also give us better
1164
 * time. Without a PPS output, we're Just fooling ourselves because of
1165
 * the serial code paths
1166
 * -------------------------------------------------------------------
1167
 */
1168
static void
1169
gps_send(
1170
  int           fd,
1171
  const char  * cmd,
1172
  struct peer * peer
1173
  )
1174
{
1175
  /* $...*xy<CR><LF><NUL> add 7 */
1176
  char        buf[NMEA_PROTO_MAXLEN + 7];
1177
  int       len;
1178
  u_char        dcs;
1179
  const u_char *beg, *end;
1180
1181
  if (*cmd != '$') {
1182
    /* get checksum and length */
1183
    beg = end = (const u_char*)cmd;
1184
    dcs = 0;
1185
    while (*end >= ' ' && *end != '*')
1186
      dcs ^= *end++;
1187
    len = end - beg;
1188
    /* format into output buffer with overflow check */
1189
    len = snprintf(buf, sizeof(buf), "$%.*s*%02X\r\n",
1190
             len, beg, dcs);
1191
    if ((size_t)len >= sizeof(buf)) {
1192
      DPRINTF(1, ("%s gps_send: buffer overflow for command '%s'\n",
1193
            refnumtoa(&peer->srcadr), cmd));
1194
      return; /* game over player 1 */
1195
    }
1196
    cmd = buf;
1197
  } else {
1198
    len = strlen(cmd);
1199
  }
1200
1201
  DPRINTF(1, ("%s gps_send: '%.*s'\n", refnumtoa(&peer->srcadr),
1202
    len - 2, cmd));
1203
1204
  /* send out the whole stuff */
1205
  if (refclock_fdwrite(peer, fd, cmd, len) != len)
1206
    refclock_report(peer, CEVNT_FAULT);
1207
}
1208
#endif /* NMEA_WRITE_SUPPORT */
1209
1210
/*
1211
 * -------------------------------------------------------------------
1212
 * helpers for faster field splitting
1213
 * -------------------------------------------------------------------
1214
 *
1215
 * set up a field record, check syntax and verify checksum
1216
 *
1217
 * format is $XXXXX,1,2,3,4*ML
1218
 *
1219
 * 8-bit XOR of characters between $ and * noninclusive is transmitted
1220
 * in last two chars M and L holding most and least significant nibbles
1221
 * in hex representation such as:
1222
 *
1223
 *   $GPGLL,5057.970,N,00146.110,E,142451,A*27
1224
 *   $GPVTG,089.0,T,,,15.2,N,,*7F
1225
 *
1226
 * Some other constraints:
1227
 * + The field name must be at least 5 upcase characters or digits and
1228
 *   must start with a character.
1229
 * + The checksum (if present) must be uppercase hex digits.
1230
 * + The length of a sentence is limited to 80 characters (not including
1231
 *   the final CR/LF nor the checksum, but including the leading '$')
1232
 *
1233
 * Return values:
1234
 *  + CHECK_INVALID
1235
 *  The data does not form a valid NMEA sentence or a checksum error
1236
 *  occurred.
1237
 *  + CHECK_VALID
1238
 *  The data is a valid NMEA sentence but contains no checksum.
1239
 *  + CHECK_CSVALID
1240
 *  The data is a valid NMEA sentence and passed the checksum test.
1241
 * -------------------------------------------------------------------
1242
 */
1243
static int
1244
field_init(
1245
  nmea_data * data, /* context structure           */
1246
  char    * cptr, /* start of raw data           */
1247
  int     dlen  /* data len, not counting trailing NUL */
1248
  )
1249
0
{
1250
0
  u_char cs_l;  /* checksum local computed  */
1251
0
  u_char cs_r;  /* checksum remote given  */
1252
0
  char * eptr;  /* buffer end end pointer */
1253
0
  char   tmp; /* char buffer      */
1254
1255
0
  cs_l = 0;
1256
0
  cs_r = 0;
1257
  /* some basic input constraints */
1258
0
  if (dlen < 0)
1259
0
    dlen = 0;
1260
0
  eptr = cptr + dlen;
1261
0
  *eptr = '\0';
1262
1263
  /* load data context */
1264
0
  data->base = cptr;
1265
0
  data->cptr = cptr;
1266
0
  data->cidx = 0;
1267
0
  data->blen = dlen;
1268
1269
  /* syntax check follows here. check allowed character
1270
   * sequences, updating the local computed checksum as we go.
1271
   *
1272
   * regex equiv: '^\$[A-Z][A-Z0-9]{4,}[^*]*(\*[0-9A-F]{2})?$'
1273
   */
1274
1275
  /* -*- start character: '^\$' */
1276
0
  if (*cptr == '\0')
1277
0
    return CHECK_EMPTY;
1278
0
  if (*cptr++ != '$')
1279
0
    return CHECK_INVALID;
1280
1281
  /* -*- advance context beyond start character */
1282
0
  data->base++;
1283
0
  data->cptr++;
1284
0
  data->blen--;
1285
1286
  /* -*- field name: '[A-Z][A-Z0-9]{4,},' */
1287
0
  if (*cptr < 'A' || *cptr > 'Z')
1288
0
    return CHECK_INVALID;
1289
0
  cs_l ^= *cptr++;
1290
0
  while ((*cptr >= 'A' && *cptr <= 'Z') ||
1291
0
         (*cptr >= '0' && *cptr <= '9')  )
1292
0
    cs_l ^= *cptr++;
1293
0
  if (*cptr != ',' || (cptr - data->base) < NMEA_PROTO_IDLEN)
1294
0
    return CHECK_INVALID;
1295
0
  cs_l ^= *cptr++;
1296
1297
  /* -*- data: '[^*]*' */
1298
0
  while (*cptr && *cptr != '*')
1299
0
    cs_l ^= *cptr++;
1300
1301
  /* -*- checksum field: (\*[0-9A-F]{2})?$ */
1302
0
  if (*cptr == '\0')
1303
0
    return CHECK_VALID;
1304
0
  if (*cptr != '*' || cptr != eptr - 3 ||
1305
0
      (cptr - data->base) >= NMEA_PROTO_MAXLEN)
1306
0
    return CHECK_INVALID;
1307
1308
0
  for (cptr++; (tmp = *cptr) != '\0'; cptr++) {
1309
0
    if (tmp >= '0' && tmp <= '9')
1310
0
      cs_r = (cs_r << 4) + (tmp - '0');
1311
0
    else if (tmp >= 'A' && tmp <= 'F')
1312
0
      cs_r = (cs_r << 4) + (tmp - 'A' + 10);
1313
0
    else
1314
0
      break;
1315
0
  }
1316
1317
  /* -*- make sure we are at end of string and csum matches */
1318
0
  if (cptr != eptr || cs_l != cs_r)
1319
0
    return CHECK_INVALID;
1320
1321
0
  return CHECK_CSVALID;
1322
0
}
1323
1324
/*
1325
 * -------------------------------------------------------------------
1326
 * fetch a data field by index, zero being the name field. If this
1327
 * function is called repeatedly with increasing indices, the total load
1328
 * is O(n), n being the length of the string; if it is called with
1329
 * decreasing indices, the total load is O(n^2). Try not to go backwards
1330
 * too often.
1331
 * -------------------------------------------------------------------
1332
 */
1333
static char *
1334
field_parse(
1335
  nmea_data * data,
1336
  int       fn
1337
  )
1338
0
{
1339
0
  char tmp;
1340
1341
0
  if (fn < data->cidx) {
1342
0
    data->cidx = 0;
1343
0
    data->cptr = data->base;
1344
0
  }
1345
0
  while ((fn > data->cidx) && (tmp = *data->cptr) != '\0') {
1346
0
    data->cidx += (tmp == ',');
1347
0
    data->cptr++;
1348
0
  }
1349
0
  return data->cptr;
1350
0
}
1351
1352
/*
1353
 * -------------------------------------------------------------------
1354
 * Wipe (that is, overwrite with '_') data fields and the checksum in
1355
 * the last timecode.  The list of field indices is given as integers
1356
 * in a varargs list, preferably in ascending order, in any case
1357
 * terminated by a negative field index.
1358
 *
1359
 * A maximum number of 8 fields can be overwritten at once to guard
1360
 * against runaway (that is, unterminated) argument lists.
1361
 *
1362
 * This function affects what a remote user can see with
1363
 *
1364
 * ntpq -c clockvar <server>
1365
 *
1366
 * Note that this also removes the wiped fields from any clockstats
1367
 * log.  Some NTP operators monitor their NMEA GPS using the change in
1368
 * location in clockstats over time as as a proxy for the quality of
1369
 * GPS reception and thereby time reported.
1370
 * -------------------------------------------------------------------
1371
 */
1372
static void
1373
field_wipe(
1374
  nmea_data * data,
1375
  ...
1376
  )
1377
0
{
1378
0
  va_list va;   /* vararg index list */
1379
0
  int fcnt;   /* safeguard against runaway arglist */
1380
0
  int fidx;   /* field to nuke, or -1 for checksum */
1381
0
  char  * cp;   /* overwrite destination */
1382
1383
0
  fcnt = 8;
1384
0
  cp = NULL;
1385
0
  va_start(va, data);
1386
0
  do {
1387
0
    fidx = va_arg(va, int);
1388
0
    if (fidx >= 0 && fidx <= NMEA_PROTO_FIELDS) {
1389
0
      cp = field_parse(data, fidx);
1390
0
    } else {
1391
0
      cp = data->base + data->blen;
1392
0
      if (data->blen >= 3 && cp[-3] == '*')
1393
0
        cp -= 2;
1394
0
    }
1395
0
    for ( ; '\0' != *cp && '*' != *cp && ',' != *cp; cp++)
1396
0
      if ('.' != *cp)
1397
0
        *cp = '_';
1398
0
  } while (fcnt-- && fidx >= 0);
1399
0
  va_end(va);
1400
0
}
1401
1402
/*
1403
 * -------------------------------------------------------------------
1404
 * PARSING HELPERS
1405
 * -------------------------------------------------------------------
1406
 */
1407
typedef unsigned char const UCC;
1408
1409
static char const * const s_eof_chars = ",*\r\n";
1410
1411
#ifdef DEBUG
1412
static int field_length(UCC *cp, unsigned int nfields)
1413
0
{
1414
0
  char const * ep = (char const*)cp;
1415
0
  ep = strpbrk(ep, s_eof_chars);
1416
0
  if (ep && nfields)
1417
0
    while (--nfields && ep && *ep == ',')
1418
0
      ep = strpbrk(ep + 1, s_eof_chars);
1419
0
  return (ep)
1420
0
      ? (int)((UCC*)ep - cp)
1421
0
      : (int)strlen((char const*)cp);
1422
0
}
1423
#endif  /* DEBUG */
1424
1425
/* /[,*\r\n]/ --> skip */
1426
static int _parse_eof(UCC *cp, UCC ** ep)
1427
0
{
1428
0
  int rc = (strchr(s_eof_chars, *(char const*)cp) != NULL);
1429
0
  *ep = cp + rc;
1430
0
  return rc;
1431
0
}
1432
1433
/* /,/ --> skip */
1434
static int _parse_sep(UCC *cp, UCC ** ep)
1435
0
{
1436
0
  int rc = (*cp == ',');
1437
0
  *ep = cp + rc;
1438
0
  return rc;
1439
0
}
1440
1441
/* /[[:digit:]]{2}/ --> uint16_t */
1442
static int _parse_num2d(UCC *cp, UCC ** ep, uint16_t *into)
1443
0
{
1444
0
  int rc = FALSE;
1445
1446
0
  if (isdigit(cp[0]) && isdigit(cp[1])) {
1447
0
    *into = (cp[0] - '0') * 10 + (cp[1] - '0');
1448
0
    cp += 2;
1449
0
    rc = TRUE;
1450
0
  }
1451
0
  *ep = cp;
1452
0
  return rc;
1453
0
}
1454
1455
/* /[[:digit:]]+/ --> uint16_t */
1456
static int _parse_u16(UCC *cp, UCC **ep, uint16_t *into, unsigned int ndig)
1457
0
{
1458
0
  uint16_t  num = 0;
1459
0
  int   rc  = FALSE;
1460
0
  if (isdigit(*cp) && ndig) {
1461
0
    rc = TRUE;
1462
0
    do
1463
0
      num = (num * 10) + (*cp - '0');
1464
0
    while (isdigit(*++cp) && --ndig);
1465
0
    *into = num;
1466
0
  }
1467
0
  *ep = cp;
1468
0
  return rc;
1469
0
}
1470
1471
/* /[[:digit:]]+/ --> uint32_t */
1472
static int _parse_u32(UCC *cp, UCC **ep, uint32_t *into, unsigned int ndig)
1473
0
{
1474
0
  uint32_t  num = 0;
1475
0
  int   rc  = FALSE;
1476
0
  if (isdigit(*cp) && ndig) {
1477
0
    rc = TRUE;
1478
0
    do
1479
0
      num = (num * 10) + (*cp - '0');
1480
0
    while (isdigit(*++cp) && --ndig);
1481
0
    *into = num;
1482
0
  }
1483
0
  *ep = cp;
1484
0
  return rc;
1485
0
}
1486
1487
/* /(\.[[:digit:]]*)?/ --> l_fp{0, f}
1488
 * read fractional seconds, convert to l_fp
1489
 *
1490
 * Only the first 9 decimal digits are evaluated; any excess is parsed
1491
 * away but silently ignored. (--> truncation to 1 nanosecond)
1492
 */
1493
static int _parse_frac(UCC *cp, UCC **ep, l_fp *into)
1494
0
{
1495
0
  static const uint32_t powtab[10] = {
1496
0
            0,
1497
0
    100000000, 10000000, 1000000,
1498
0
       100000,    10000,    1000,
1499
0
          100,       10,       1
1500
0
  };
1501
1502
0
  struct timespec ts;
1503
0
  ZERO(ts);
1504
0
  if (*cp == '.') {
1505
0
    uint32_t fval = 0;
1506
0
    UCC *    sp   = cp + 1;
1507
0
    if (_parse_u32(sp, &cp, &fval, 9))
1508
0
      ts.tv_nsec = fval * powtab[(size_t)(cp - sp)];
1509
0
    while (isdigit(*cp))
1510
0
      ++cp;
1511
0
  }
1512
1513
0
  *ep   = cp;
1514
0
  *into = tspec_intv_to_lfp(ts);
1515
0
  return TRUE;
1516
0
}
1517
1518
/* /[[:digit:]]{6}/ --> time-of-day
1519
 * parses a number string representing 'HHMMSS'
1520
 */
1521
static int _parse_time(UCC *cp, UCC ** ep, TCivilDate *into)
1522
0
{
1523
0
  uint16_t  s, m, h;
1524
0
  int   rc;
1525
0
  UCC *   xp = cp;
1526
1527
0
  rc =   _parse_num2d(cp, &cp, &h) && (h < 24)
1528
0
      && _parse_num2d(cp, &cp, &m) && (m < 60)
1529
0
      && _parse_num2d(cp, &cp, &s) && (s < 61); /* leap seconds! */
1530
1531
0
  if (rc) {
1532
0
    into->hour   = (uint8_t)h;
1533
0
    into->minute = (uint8_t)m;
1534
0
    into->second = (uint8_t)s;
1535
0
    *ep = cp;
1536
0
  } else {
1537
0
    *ep = xp;
1538
0
    DPRINTF(1, ("nmea: invalid time code: '%.*s'\n",
1539
0
          field_length(xp, 1), xp));
1540
0
  }
1541
0
  return rc;
1542
0
}
1543
1544
/* /[[:digit:]]{6}/ --> civil date
1545
 * parses a number string representing 'ddmmyy'
1546
 */
1547
static int _parse_date1(UCC *cp, UCC **ep, TCivilDate *into)
1548
0
{
1549
0
  unsigned short  d, m, y;
1550
0
  int   rc;
1551
0
  UCC *   xp = cp;
1552
1553
0
  rc =   _parse_num2d(cp, &cp, &d) && (d - 1 < 31)
1554
0
      && _parse_num2d(cp, &cp, &m) && (m - 1 < 12)
1555
0
      && _parse_num2d(cp, &cp, &y)
1556
0
      && _parse_eof(cp, ep);
1557
0
  if (rc) {
1558
0
    into->monthday = (uint8_t )d;
1559
0
    into->month    = (uint8_t )m;
1560
0
    into->year     = (uint16_t)y;
1561
0
    *ep = cp;
1562
0
  } else {
1563
0
    *ep = xp;
1564
0
    DPRINTF(1, ("nmea: invalid date code: '%.*s'\n",
1565
0
          field_length(xp, 1), xp));
1566
0
  }
1567
0
  return rc;
1568
0
}
1569
1570
/* /[[:digit:]]+,[[:digit:]]+,[[:digit:]]+/ --> civil date
1571
 * parses three successive numeric fields as date: day,month,year
1572
 */
1573
static int _parse_date3(UCC *cp, UCC **ep, TCivilDate *into)
1574
0
{
1575
0
  uint16_t  d, m, y;
1576
0
  int   rc;
1577
0
  UCC *   xp = cp;
1578
1579
0
  rc =   _parse_u16(cp, &cp, &d, 2) && (d - 1 < 31)
1580
0
      && _parse_sep(cp, &cp)
1581
0
      && _parse_u16(cp, &cp, &m, 2) && (m - 1 < 12)
1582
0
      && _parse_sep(cp, &cp)
1583
0
      && _parse_u16(cp, &cp, &y, 4) && (y > 1980)
1584
0
      && _parse_eof(cp, ep);
1585
0
  if (rc) {
1586
0
    into->monthday = (uint8_t )d;
1587
0
    into->month    = (uint8_t )m;
1588
0
    into->year     = (uint16_t)y;
1589
0
    *ep = cp;
1590
0
  } else {
1591
0
    *ep = xp;
1592
0
    DPRINTF(1, ("nmea: invalid date code: '%.*s'\n",
1593
0
          field_length(xp, 3), xp));
1594
0
  }
1595
0
  return rc;
1596
0
}
1597
1598
/*
1599
 * -------------------------------------------------------------------
1600
 * Check sync status
1601
 *
1602
 * If the character at the data field start matches the tag value,
1603
 * return LEAP_NOWARNING and LEAP_NOTINSYNC otherwise. If the 'inverted'
1604
 * flag is given, just the opposite value is returned. If there is no
1605
 * data field (*cp points to the NUL byte) the result is LEAP_NOTINSYNC.
1606
 * -------------------------------------------------------------------
1607
 */
1608
static u_char
1609
parse_qual(
1610
  nmea_data * rd,
1611
  int         idx,
1612
  char        tag,
1613
  int         inv
1614
  )
1615
0
{
1616
0
  static const u_char table[2] = {
1617
0
    LEAP_NOTINSYNC, LEAP_NOWARNING };
1618
1619
0
  char * dp = field_parse(rd, idx);
1620
1621
0
  return table[ *dp && ((*dp == tag) == !inv) ];
1622
0
}
1623
1624
/*
1625
 * -------------------------------------------------------------------
1626
 * Parse a time stamp in HHMMSS[.sss] format with error checking.
1627
 *
1628
 * returns 1 on success, 0 on failure
1629
 * -------------------------------------------------------------------
1630
 */
1631
static int
1632
parse_time(
1633
  struct calendar * jd, /* result calendar pointer */
1634
  l_fp    * fofs, /* storage for nsec fraction */
1635
  nmea_data       * rd,
1636
  int     idx
1637
  )
1638
0
{
1639
0
  UCC *   dp = (UCC*)field_parse(rd, idx);
1640
1641
0
  return _parse_time(dp, &dp, jd)
1642
0
      && _parse_frac(dp, &dp, fofs)
1643
0
      && _parse_eof (dp, &dp);
1644
0
}
1645
1646
/*
1647
 * -------------------------------------------------------------------
1648
 * Parse a date string from an NMEA sentence. This could either be a
1649
 * partial date in DDMMYY format in one field, or DD,MM,YYYY full date
1650
 * spec spanning three fields. This function does some extensive error
1651
 * checking to make sure the date string was consistent.
1652
 *
1653
 * returns 1 on success, 0 on failure
1654
 * -------------------------------------------------------------------
1655
 */
1656
static int
1657
parse_date(
1658
  struct calendar * jd, /* result pointer */
1659
  nmea_data       * rd,
1660
  int     idx,
1661
  enum date_fmt   fmt
1662
  )
1663
0
{
1664
0
  UCC  * dp = (UCC*)field_parse(rd, idx);
1665
1666
0
  switch (fmt) {
1667
0
  case DATE_1_DDMMYY:
1668
0
    return _parse_date1(dp, &dp, jd);
1669
0
  case DATE_3_DDMMYYYY:
1670
0
    return _parse_date3(dp, &dp, jd);
1671
0
  default:
1672
0
    DPRINTF(1, ("nmea: invalid parse format: %d\n", fmt));
1673
0
    break;
1674
0
  }
1675
0
  return FALSE;
1676
0
}
1677
1678
/*
1679
 * -------------------------------------------------------------------
1680
 * Parse GPS week time info from an NMEA sentence. This info contains
1681
 * the GPS week number, the GPS time-of-week and the leap seconds GPS
1682
 * to UTC.
1683
 *
1684
 * returns 1 on success, 0 on failure
1685
 * -------------------------------------------------------------------
1686
 */
1687
static int
1688
parse_gpsw(
1689
  TGpsDatum *  wd,
1690
  nmea_data *  rd,
1691
  int          weekidx,
1692
  int          timeidx,
1693
  int          leapidx
1694
  )
1695
0
{
1696
0
  uint32_t  secs;
1697
0
  uint16_t  week, leap = 0;
1698
0
  l_fp    fofs;
1699
0
  int   rc;
1700
1701
0
  UCC * dpw = (UCC*)field_parse(rd, weekidx);
1702
0
  UCC * dps = (UCC*)field_parse(rd, timeidx);
1703
1704
0
  rc =   _parse_u16 (dpw, &dpw, &week, 5)
1705
0
      && _parse_eof (dpw, &dpw)
1706
0
      && _parse_u32 (dps, &dps, &secs, 9)
1707
0
      && _parse_frac(dps, &dps, &fofs)
1708
0
      && _parse_eof (dps, &dps)
1709
0
      && (secs < 7*SECSPERDAY);
1710
0
  if (rc && leapidx > 0) {
1711
0
    UCC * dpl = (UCC*)field_parse(rd, leapidx);
1712
0
    rc =   _parse_u16 (dpl, &dpl, &leap, 5)
1713
0
        && _parse_eof (dpl, &dpl);
1714
0
  }
1715
0
  if (rc) {
1716
0
    fofs.l_ui -= leap;
1717
0
    *wd = gpscal_from_gpsweek(week, secs, fofs);
1718
0
  } else {
1719
0
    DPRINTF(1, ("nmea: parse_gpsw: invalid weektime spec\n"));
1720
0
  }
1721
0
  return rc;
1722
0
}
1723
1724
1725
#ifdef HAVE_PPSAPI
1726
static double
1727
tabsdiffd(
1728
  l_fp  t1,
1729
  l_fp  t2
1730
  )
1731
{
1732
  double  dd;
1733
  L_SUB(&t1, &t2);
1734
  LFPTOD(&t1, dd);
1735
  return fabs(dd);
1736
}
1737
#endif /* HAVE_PPSAPI */
1738
1739
/*
1740
 * ===================================================================
1741
 *
1742
 * NMEAD support
1743
 *
1744
 * original nmead support added by Jon Miner (cp_n18@yahoo.com)
1745
 *
1746
 * See http://home.hiwaay.net/~taylorc/gps/nmea-server/
1747
 * for information about nmead
1748
 *
1749
 * To use this, you need to create a link from /dev/gpsX to
1750
 * the server:port where nmead is running.  Something like this:
1751
 *
1752
 * ln -s server:port /dev/gps1
1753
 *
1754
 * Split into separate function by Juergen Perlinger
1755
 * (perlinger-at-ntp-dot-org)
1756
 *
1757
 * ===================================================================
1758
 */
1759
static int
1760
nmead_open(
1761
  const char * device
1762
  )
1763
0
{
1764
0
  int fd = -1;    /* result file descriptor */
1765
1766
0
#   ifdef HAVE_READLINK
1767
0
  char  host[80];   /* link target buffer */
1768
0
  char  * port;     /* port name or number  */
1769
0
  int rc;     /* result code (several)*/
1770
0
  int     sh;     /* socket handle  */
1771
0
  struct addrinfo  ai_hint; /* resolution hint  */
1772
0
  struct addrinfo *ai_list; /* resolution result  */
1773
0
  struct addrinfo *ai;    /* result scan ptr  */
1774
1775
0
  fd = -1;
1776
1777
  /* try to read as link, make sure no overflow occurs */
1778
0
  rc = readlink(device, host, sizeof(host));
1779
0
  if ((size_t)rc >= sizeof(host))
1780
0
    return fd; /* error / overflow / truncation */
1781
0
  host[rc] = '\0';  /* readlink does not place NUL  */
1782
1783
  /* get port */
1784
0
  port = strchr(host, ':');
1785
0
  if (!port)
1786
0
    return fd; /* not 'host:port' syntax ? */
1787
0
  *port++ = '\0'; /* put in separator */
1788
1789
  /* get address infos and try to open socket
1790
   *
1791
   * This getaddrinfo() is naughty in ntpd's nonblocking main
1792
   * thread, but you have to go out of your wary to use this code
1793
   * and typically the blocking is at startup where its impact is
1794
   * reduced. The same holds for the 'connect()', as it is
1795
   * blocking, too...
1796
   */
1797
0
  ZERO(ai_hint);
1798
0
  ai_hint.ai_protocol = IPPROTO_TCP;
1799
0
  ai_hint.ai_socktype = SOCK_STREAM;
1800
0
  if (getaddrinfo(host, port, &ai_hint, &ai_list))
1801
0
    return fd;
1802
1803
0
  for (ai = ai_list; ai && (fd == -1); ai = ai->ai_next) {
1804
0
    sh = socket(ai->ai_family, ai->ai_socktype,
1805
0
          ai->ai_protocol);
1806
0
    if (INVALID_SOCKET == sh)
1807
0
      continue;
1808
0
    rc = connect(sh, ai->ai_addr, ai->ai_addrlen);
1809
0
    if (-1 != rc)
1810
0
      fd = sh;
1811
0
    else
1812
0
      close(sh);
1813
0
  }
1814
0
  freeaddrinfo(ai_list);
1815
0
  if (fd != -1)
1816
0
    make_socket_nonblocking(fd);
1817
#   else
1818
  fd = -1;
1819
#   endif
1820
1821
0
  return fd;
1822
0
}
1823
#else
1824
NONEMPTY_TRANSLATION_UNIT
1825
#endif /* REFCLOCK && CLOCK_NMEA */