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_true.c
Line
Count
Source
1
/*
2
 * refclock_true - clock driver for the Kinemetrics/TrueTime receivers
3
 *  Receiver Version 3.0C - tested plain, with CLKLDISC
4
 *  Development work being done:
5
 *      - Support TL-3 WWV TOD receiver
6
 */
7
8
#ifdef HAVE_CONFIG_H
9
#include <config.h>
10
#endif
11
12
#if defined(REFCLOCK) && defined(CLOCK_TRUETIME)
13
14
#include <stdio.h>
15
#include <ctype.h>
16
17
#include "ntpd.h"
18
#include "ntp_io.h"
19
#include "ntp_refclock.h"
20
#include "ntp_unixtime.h"
21
#include "ntp_stdlib.h"
22
23
/* This should be an atom clock but those are very hard to build.
24
 *
25
 * The PCL720 from P C Labs has an Intel 8253 lookalike, as well as a bunch
26
 * of TTL input and output pins, all brought out to the back panel.  If you
27
 * wire a PPS signal (such as the TTL PPS coming out of a GOES or other
28
 * Kinemetrics/Truetime clock) to the 8253's GATE0, and then also wire the
29
 * 8253's OUT0 to the PCL720's INPUT3.BIT0, then we can read CTR0 to get the
30
 * number of uSecs since the last PPS upward swing, mediated by reading OUT0
31
 * to find out if the counter has wrapped around (this happens if more than
32
 * 65535us (65ms) elapses between the PPS event and our being called.)
33
 */
34
#ifdef CLOCK_PPS720
35
# undef min /* XXX */
36
# undef max /* XXX */
37
# include <machine/inline.h>
38
# include <sys/pcl720.h>
39
# include <sys/i8253.h>
40
# define PCL720_IOB 0x2a0 /* XXX */
41
# define PCL720_CTR 0   /* XXX */
42
#endif
43
44
/*
45
 * Support for Kinemetrics Truetime Receivers
46
 *  GOES:           (468-DC, usable with GPS->GOES converting antenna)
47
 *  GPS/TM-TMD: 
48
 *  XL-DC:    (a 151-602-210, reported by the driver as a GPS/TM-TMD)
49
 *  GPS-800 TCU:  (an 805-957 with the RS232 Talker/Listener module)
50
 *      TL-3:           3 channel WWV/H receiver w/ IRIG and RS-232 outputs
51
 *  OM-DC:    getting stale ("OMEGA")
52
 *
53
 * Most of this code is originally from refclock_wwvb.c with thanks.
54
 * It has been so mangled that wwvb is not a recognizable ancestor.
55
 *
56
 * Timcode format: ADDD:HH:MM:SSQCL
57
 *  A - control A   (this is stripped before we see it)
58
 *  Q - Quality indication  (see below)
59
 *  C - Carriage return
60
 *  L - Line feed
61
 *
62
 * Quality codes indicate possible error of
63
 *   468-DC GOES Receiver:
64
 *   GPS-TM/TMD Receiver: (default quality codes for XL-DC)
65
 *       ?     +/- 1  milliseconds  #     +/- 100 microseconds
66
 *       *     +/- 10 microseconds  .     +/- 1   microsecond
67
 *     space   less than 1 microsecond
68
 *   TL-3 Receiver: (default quality codes for TL-3)
69
 *       ?     unknown quality (receiver is unlocked)
70
 *     space   +/- 5 milliseconds
71
 *   OM-DC OMEGA Receiver: (default quality codes for OMEGA)
72
 *   WARNING OMEGA navigation system is no longer existent
73
 *       >     >+- 5 seconds
74
 *       ?     >+/- 500 milliseconds    #     >+/- 50 milliseconds
75
 *       *     >+/- 5 milliseconds      .     >+/- 1 millisecond
76
 *      A-H    less than 1 millisecond.  Character indicates which station
77
 *         is being received as follows:
78
 *         A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
79
 *         E = La Reunion, F = Argentina, G = Australia, H = Japan.
80
 *
81
 * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
82
 *
83
 * Notes on 468-DC and OMEGA receiver:
84
 *
85
 * Send the clock a 'R' or 'C' and once per second a timestamp will
86
 * appear.  Send a 'P' to get the satellite position once (GOES only.)
87
 *
88
 * Notes on the 468-DC receiver:
89
 *
90
 * Since the old east/west satellite locations are only historical, you can't
91
 * set your clock propagation delay settings correctly and still use
92
 * automatic mode. The manual says to use a compromise when setting the
93
 * switches. This results in significant errors. The solution; use fudge
94
 * time1 and time2 to incorporate corrections. If your clock is set for
95
 * 50 and it should be 58 for using the west and 46 for using the east,
96
 * use the line
97
 *
98
 * fudge 127.127.5.0 time1 +0.008 time2 -0.004
99
 *
100
 * This corrects the 4 milliseconds advance and 8 milliseconds retard
101
 * needed. The software will ask the clock which satellite it sees.
102
 *
103
 * Notes on the TrueTime TimeLink TL-3 WWV TOD receiver:
104
 * 
105
 * This clock may be polled, or send one timecode per second.
106
 * That mode may be toggled via the front panel ("C" mode), or controlled
107
 * from the RS-232 port.  Send the receiver "ST1" to turn it on, and
108
 * "ST0" to turn it off.  Send "QV" to get the firmware revision (useful
109
 * for identifying this model.)
110
 * 
111
 * Note that it can take several polling cycles, especially if the receiver
112
 * was in the continuous timecode mode.  (It can be slow to leave that mode.)
113
 * 
114
 * ntp.conf parameters:
115
 * time1   - offset applied to samples when reading WEST satellite (default = 0)
116
 * time2   - offset applied to samples when reading EAST satellite (default = 0)
117
 * stratum - stratum to assign to this clock (default = 0)
118
 * refid   - refid assigned to this clock (default = "TRUE", see below)
119
 * flag1   - will silence the clock side of ntpd, just reading the clock
120
 *       without trying to write to it.  (default = 0)
121
 * flag2   - generate a debug file /tmp/true%d.
122
 * flag3   - enable ppsclock streams module
123
 * flag4   - use the PCL-720 (BSD/OS only)
124
 */
125
126
127
/*
128
 * Definitions
129
 */
130
#define DEVICE    "/dev/true%d"
131
0
#define SPEED232  B9600  /* 9600 baud */
132
133
/*
134
 * Radio interface parameters
135
 */
136
0
#define PRECISION (-10)  /* precision assumed (about 1 ms) */
137
0
#define REFID   "TRUE"  /* reference id */
138
0
#define DESCRIPTION "Kinemetrics/TrueTime Receiver"
139
140
/*
141
 * Tags which station (satellite) we see
142
 */
143
0
#define GOES_WEST 0  /* Default to WEST satellite and apply time1 */
144
0
#define GOES_EAST 1  /* until you discover otherwise */
145
146
/*
147
 * used by the state machine
148
 */
149
enum true_event {e_Init, e_Huh, e_F18, e_F50, e_F51, e_Satellite,
150
     e_TL3, e_Poll, e_Location, e_TS, e_Max};
151
const char *events[] = {"Init", "Huh", "F18", "F50", "F51", "Satellite",
152
      "TL3", "Poll", "Location", "TS"};
153
0
#define eventStr(x) (((int)x<(int)e_Max) ? events[(int)x] : "?")
154
155
enum true_state {s_Base, s_InqTM, s_InqTCU, s_InqOmega, s_InqGOES,
156
     s_InqTL3, s_Init, s_F18, s_F50, s_Start, s_Auto, s_Max};
157
const char *states[] = {"Base", "InqTM", "InqTCU", "InqOmega", "InqGOES",
158
      "InqTL3", "Init", "F18", "F50", "Start", "Auto"};
159
0
#define stateStr(x) (((int)x<(int)s_Max) ? states[(int)x] : "?")
160
161
enum true_type  {t_unknown, t_goes, t_tm, t_tcu, t_omega, t_tl3, t_Max};
162
const char *types[] = {"unknown", "goes", "tm", "tcu", "omega", "tl3"};
163
0
#define typeStr(x) (((int)x<(int)t_Max) ? types[(int)x] : "?")
164
165
/*
166
 * unit control structure
167
 */
168
struct true_unit {
169
  unsigned int  pollcnt;  /* poll message counter */
170
  unsigned int  station;  /* which station we are on */
171
  unsigned int  polled;   /* Hand in a time sample? */
172
  enum true_state state;    /* state machine */
173
  enum true_type  type;   /* what kind of clock is it? */
174
  int   unit;   /* save an extra copy of this */
175
  FILE    *debug;   /* debug logging file */
176
#ifdef CLOCK_PPS720
177
  int   pcl720init; /* init flag for PCL 720 */
178
#endif
179
};
180
181
/*
182
 * Function prototypes
183
 */
184
static  int true_start  (int, struct peer *);
185
static  void  true_shutdown (int, struct peer *);
186
static  void  true_receive  (struct recvbuf *);
187
static  void  true_poll (int, struct peer *);
188
static  void  true_send (struct peer *, const char *);
189
static  void  true_doevent  (struct peer *, enum true_event);
190
191
#ifdef CLOCK_PPS720
192
static  u_long  true_sample720  (void);
193
#endif
194
195
/*
196
 * Transfer vector
197
 */
198
struct  refclock refclock_true = {
199
  true_start,   /* start up driver */
200
  true_shutdown,    /* shut down driver */
201
  true_poll,    /* transmit poll message */
202
  noentry,    /* not used (old true_control) */
203
  noentry,    /* initialize driver (not used) */
204
  noentry,    /* not used (old true_buginfo) */
205
  NOFLAGS     /* not used */
206
};
207
208
209
#if !defined(__STDC__)
210
# define true_debug (void)
211
#else
212
NTP_PRINTF(2, 3)
213
static void
214
true_debug(struct peer *peer, const char *fmt, ...)
215
0
{
216
0
  va_list ap;
217
0
  int want_debugging, now_debugging;
218
0
  struct refclockproc *pp;
219
0
  struct true_unit *up;
220
221
0
  va_start(ap, fmt);
222
0
  pp = peer->procptr;
223
0
  up = pp->unitptr;
224
225
0
  want_debugging = (pp->sloppyclockflag & CLK_FLAG2) != 0;
226
0
  now_debugging = (up->debug != NULL);
227
0
  if (want_debugging != now_debugging)
228
0
  {
229
0
    if (want_debugging) {
230
0
      char filename[40];
231
0
      int fd;
232
233
0
      snprintf(filename, sizeof(filename),
234
0
         "/tmp/true%d.debug", up->unit);
235
0
      fd = open(filename, O_CREAT | O_WRONLY | O_EXCL,
236
0
          0600);
237
0
      if (fd >= 0 && (up->debug = fdopen(fd, "w"))) {
238
0
#ifdef HAVE_SETVBUF
239
0
        static char buf[BUFSIZ];
240
241
0
        setvbuf(up->debug, buf, _IOLBF, BUFSIZ);
242
#else
243
        setlinebuf(up->debug);
244
#endif
245
0
      }
246
0
    } else {
247
0
      fclose(up->debug);
248
0
      up->debug = NULL;
249
0
    }
250
0
  }
251
252
0
  if (up->debug) {
253
0
    fprintf(up->debug, "true%d: ", up->unit);
254
0
    vfprintf(up->debug, fmt, ap);
255
0
  }
256
0
  va_end(ap);
257
0
}
258
#endif /*STDC*/
259
260
/*
261
 * true_start - open the devices and initialize data for processing
262
 */
263
static int
264
true_start(
265
  int unit,
266
  struct peer *peer
267
  )
268
0
{
269
0
  register struct true_unit *up;
270
0
  struct refclockproc *pp;
271
0
  char device[40];
272
0
  int fd;
273
274
  /*
275
   * Open serial port
276
   */
277
0
  snprintf(device, sizeof(device), DEVICE, unit);
278
0
  fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_CLK);
279
0
  if (fd <= 0)
280
0
    return 0;
281
282
  /*
283
   * Allocate and initialize unit structure
284
   */
285
0
  up = emalloc_zero(sizeof(*up));
286
0
  pp = peer->procptr;
287
0
  pp->io.clock_recv = true_receive;
288
0
  pp->io.srcclock = peer;
289
0
  pp->io.datalen = 0;
290
0
  pp->io.fd = fd;
291
0
  if (!io_addclock(&pp->io)) {
292
0
    close(fd);
293
0
    pp->io.fd = -1;
294
0
    free(up);
295
0
    return (0);
296
0
  }
297
0
  pp->unitptr = up;
298
299
  /*
300
   * Initialize miscellaneous variables
301
   */
302
0
  peer->precision = PRECISION;
303
0
  pp->clockdesc = DESCRIPTION;
304
0
  memcpy(&pp->refid, REFID, 4);
305
0
  up->pollcnt = 2;
306
0
  up->type = t_unknown;
307
0
  up->state = s_Base;
308
309
  /*
310
   * Send a CTRL-C character at the start,
311
   * just in case the clock is already
312
   * sending timecodes
313
   */
314
0
  true_send(peer, "\03\r");
315
  
316
0
  true_doevent(peer, e_Init);
317
318
0
  return (1);
319
0
}
320
321
322
/*
323
 * true_shutdown - shut down the clock
324
 */
325
static void
326
true_shutdown(
327
  int unit,
328
  struct peer *peer
329
  )
330
0
{
331
0
  register struct true_unit *up;
332
0
  struct refclockproc *pp;
333
334
0
  pp = peer->procptr;
335
0
  up = pp->unitptr;
336
0
  if (pp->io.fd != -1)
337
0
    io_closeclock(&pp->io);
338
0
  if (up != NULL)
339
0
    free(up);
340
0
}
341
342
343
/*
344
 * true_receive - receive data from the serial interface on a clock
345
 */
346
static void
347
true_receive(
348
  struct recvbuf *rbufp
349
  )
350
0
{
351
0
  register struct true_unit *up;
352
0
  struct refclockproc *pp;
353
0
  struct peer *peer;
354
0
  u_short new_station;
355
0
  char synced;
356
0
  int i;
357
0
  int lat, lon, off;  /* GOES Satellite position */
358
  /* These variables hold data until we decide to keep it */
359
0
  char  rd_lastcode[BMAX];
360
0
  l_fp  rd_tmp;
361
0
  u_short rd_lencode;
362
363
  /*
364
   * Get the clock this applies to and pointers to the data.
365
   */
366
0
  peer = rbufp->recv_peer;
367
0
  pp = peer->procptr;
368
0
  up = pp->unitptr;
369
370
  /*
371
   * Read clock output.  Automatically handles STREAMS, CLKLDISC.
372
   */
373
0
  rd_lencode = refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp);
374
0
  rd_lastcode[rd_lencode] = '\0';
375
376
  /*
377
   * There is a case where <cr><lf> generates 2 timestamps.
378
   */
379
0
  if (rd_lencode == 0)
380
0
    return;
381
0
  pp->lencode = rd_lencode;
382
0
  strlcpy(pp->a_lastcode, rd_lastcode, sizeof(pp->a_lastcode));
383
0
  pp->lastrec = rd_tmp;
384
0
  true_debug(peer, "receive(%s) [%d]\n", pp->a_lastcode,
385
0
       pp->lencode);
386
387
0
  up->pollcnt = 2;
388
0
  record_clock_stats(&peer->srcadr, pp->a_lastcode);
389
390
  /*
391
   * We get down to business, check the timecode format and decode
392
   * its contents. This code decodes a multitude of different
393
   * clock messages. Timecodes are processed if needed. All replies
394
   * will be run through the state machine to tweak driver options
395
   * and program the clock.
396
   */
397
398
  /*
399
   * Clock misunderstood our last command?
400
   */
401
0
  if (pp->a_lastcode[0] == '?' ||
402
0
      strcmp(pp->a_lastcode, "ERROR 05 NO SUCH FUNCTION") == 0) {
403
0
    true_doevent(peer, e_Huh);
404
0
    return;
405
0
  }
406
407
  /*
408
   * Timecode: "nnnnn+nnn-nnn"
409
   * (from GOES clock when asked about satellite position)
410
   */
411
0
  if ((pp->a_lastcode[5] == '+' || pp->a_lastcode[5] == '-') &&
412
0
      (pp->a_lastcode[9] == '+' || pp->a_lastcode[9] == '-') &&
413
0
      sscanf(pp->a_lastcode, "%5d%*c%3d%*c%3d", &lon, &lat, &off) == 3
414
0
      ) {
415
0
    const char *label = "Botch!";
416
417
    /*
418
     * This is less than perfect.  Call the (satellite)
419
     * either EAST or WEST and adjust slop accodingly
420
     * Perfectionists would recalculate the exact delay
421
     * and adjust accordingly...
422
     */
423
0
    if (lon > 7000 && lon < 14000) {
424
0
      if (lon < 10000) {
425
0
        new_station = GOES_EAST;
426
0
        label = "EAST";
427
0
      } else {
428
0
        new_station = GOES_WEST;
429
0
        label = "WEST";
430
0
      }
431
        
432
0
      if (new_station != up->station) {
433
0
        double dtemp;
434
435
0
        dtemp = pp->fudgetime1;
436
0
        pp->fudgetime1 = pp->fudgetime2;
437
0
        pp->fudgetime2 = dtemp;
438
0
        up->station = new_station;
439
0
      }
440
0
    }
441
0
    else {
442
      /*refclock_report(peer, CEVNT_BADREPLY);*/
443
0
      label = "UNKNOWN";
444
0
    }
445
0
    true_debug(peer, "GOES: station %s\n", label);
446
0
    true_doevent(peer, e_Satellite);
447
0
    return;
448
0
  }
449
450
  /*
451
   * Timecode: "Fnn"
452
   * (from TM/TMD clock when it wants to tell us what it's up to.)
453
   */
454
0
  if (sscanf(pp->a_lastcode, "F%2d", &i) == 1 && i > 0 && i < 80) {
455
0
    switch (i) {
456
0
    case 50:
457
0
      true_doevent(peer, e_F50);
458
0
      break;
459
0
    case 51:
460
0
      true_doevent(peer, e_F51);
461
0
      break;
462
0
    default:
463
0
      true_debug(peer, "got F%02d - ignoring\n", i);
464
0
      break;
465
0
    }
466
0
    return;
467
0
  }
468
469
        /*
470
         * Timecode: "VER xx.xx"
471
         * (from a TL3 when sent "QV", so id's it during initialization.)
472
         */
473
0
        if (pp->a_lastcode[0] == 'V' && pp->a_lastcode[1] == 'E' &&
474
0
            pp->a_lastcode[2] == 'R' && pp->a_lastcode[6] == '.') {
475
0
                true_doevent(peer, e_TL3);
476
0
                NLOG(NLOG_CLOCKSTATUS) {
477
0
                        msyslog(LOG_INFO, "TL3: %s", pp->a_lastcode);
478
0
                }
479
0
                return;
480
0
        }
481
482
  /*
483
   * Timecode: " TRUETIME Mk III" or " TRUETIME XL"
484
   * (from a TM/TMD/XL clock during initialization.)
485
   */
486
0
  if (strncmp(pp->a_lastcode, " TRUETIME Mk III ", 17) == 0 ||
487
0
      strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) {
488
0
    true_doevent(peer, e_F18);
489
0
    NLOG(NLOG_CLOCKSTATUS) {
490
0
      msyslog(LOG_INFO, "TM/TMD/XL: %s", pp->a_lastcode);
491
0
    }
492
0
    return;
493
0
  }
494
495
  /*
496
   * Timecode: "N03726428W12209421+000033"
497
   *      1    2
498
   * index      0123456789012345678901234
499
   * (from a TCU during initialization)
500
   */
501
0
  if ((pp->a_lastcode[0] == 'N' || pp->a_lastcode[0] == 'S') &&
502
0
      (pp->a_lastcode[9] == 'W' || pp->a_lastcode[9] == 'E') &&
503
0
      pp->a_lastcode[18] == '+') {
504
0
    true_doevent(peer, e_Location);
505
0
    NLOG(NLOG_CLOCKSTATUS) {
506
0
      msyslog(LOG_INFO, "TCU-800: %s", pp->a_lastcode);
507
0
    }
508
0
    return;
509
0
  }
510
  /*
511
   * Timecode: "ddd:hh:mm:ssQ"
512
   *      1    2
513
   * index      0123456789012345678901234
514
   * (from all clocks supported by this driver.)
515
   */
516
0
  if (pp->a_lastcode[3] == ':' &&
517
0
      pp->a_lastcode[6] == ':' &&
518
0
      pp->a_lastcode[9] == ':' &&
519
0
      sscanf(pp->a_lastcode, "%3d:%2d:%2d:%2d%c",
520
0
       &pp->day, &pp->hour, &pp->minute,
521
0
       &pp->second, &synced) == 5) {
522
523
    /*
524
     * Adjust the synchronize indicator according to timecode
525
     * say were OK, and then say not if we really are not OK
526
     */
527
0
    if (synced == '>' || synced == '#' || synced == '?'
528
0
        || synced == 'X')
529
0
      pp->leap = LEAP_NOTINSYNC;
530
0
    else
531
0
      pp->leap = LEAP_NOWARNING;
532
533
0
    true_doevent(peer, e_TS);
534
535
#ifdef CLOCK_PPS720
536
    /* If it's taken more than 65ms to get here, we'll lose. */
537
    if ((pp->sloppyclockflag & CLK_FLAG4) && up->pcl720init) {
538
      l_fp   off;
539
540
#ifdef CLOCK_ATOM
541
      /*
542
       * find out what time it really is. Include
543
       * the count from the PCL720
544
       */
545
      if (!clocktime(pp->day, pp->hour, pp->minute, 
546
               pp->second, GMT, pp->lastrec.l_ui, 
547
               &pp->yearstart, &off.l_ui)) {
548
        refclock_report(peer, CEVNT_BADTIME);
549
        return;
550
      }
551
      off.l_uf = 0;
552
#endif
553
554
      pp->usec = true_sample720();
555
#ifdef CLOCK_ATOM
556
      TVUTOTSF(pp->usec, off.l_uf);
557
#endif
558
559
      /*
560
       * Stomp all over the timestamp that was pulled out
561
       * of the input stream. It's irrelevant since we've
562
       * adjusted the input time to reflect now (via pp->usec)
563
       * rather than when the data was collected.
564
       */
565
      get_systime(&pp->lastrec);
566
#ifdef CLOCK_ATOM
567
      /*
568
       * Create a true offset for feeding to pps_sample()
569
       */
570
      L_SUB(&off, &pp->lastrec);
571
572
      pps_sample(peer, &off);
573
#endif
574
      true_debug(peer, "true_sample720: %luus\n", pp->usec);
575
    }
576
#endif
577
578
    /*
579
     * The clock will blurt a timecode every second but we only
580
     * want one when polled.  If we havn't been polled, bail out.
581
     */
582
0
    if (!up->polled)
583
0
      return;
584
585
                /* We only call doevent if additional things need be done
586
                 * at poll interval.  Currently, its only for GOES.  We also
587
                 * call it for clock unknown so that it gets logged.
588
                 */
589
0
                if (up->type == t_goes || up->type == t_unknown)
590
0
                    true_doevent(peer, e_Poll);
591
592
0
    if (!refclock_process(pp)) {
593
0
      refclock_report(peer, CEVNT_BADTIME);
594
0
      return;
595
0
    }
596
    /*
597
     * If clock is good we send a NOMINAL message so that
598
     * any previous BAD messages are nullified
599
     */
600
0
    pp->lastref = pp->lastrec;
601
0
    refclock_receive(peer);
602
0
    refclock_report(peer, CEVNT_NOMINAL);
603
604
    /*
605
     * We have succedded in answering the poll.
606
     * Turn off the flag and return
607
     */
608
0
    up->polled = 0;
609
610
0
    return;
611
0
  }
612
613
  /*
614
   * No match to known timecodes, report failure and return
615
   */
616
0
  refclock_report(peer, CEVNT_BADREPLY);
617
0
  return;
618
0
}
619
620
621
/*
622
 * true_send - time to send the clock a signal to cough up a time sample
623
 */
624
static void
625
true_send(
626
  struct peer *peer,
627
  const char *cmd
628
  )
629
0
{
630
0
  struct refclockproc *pp;
631
632
0
  pp = peer->procptr;
633
0
  if (!(pp->sloppyclockflag & CLK_FLAG1)) {
634
0
    size_t len = strlen(cmd);
635
636
0
    true_debug(peer, "Send '%s'\n", cmd);
637
0
    if (refclock_write(peer, cmd, len, NULL) != (ssize_t)len)
638
0
      refclock_report(peer, CEVNT_FAULT);
639
0
    else
640
0
      pp->polls++;
641
0
  }
642
0
}
643
644
645
/*
646
 * state machine for initializing and controlling a clock
647
 */
648
static void
649
true_doevent(
650
  struct peer *peer,
651
  enum true_event event
652
  )
653
0
{
654
0
  struct true_unit *up;
655
0
  struct refclockproc *pp;
656
657
0
  pp = peer->procptr;
658
0
  up = pp->unitptr;
659
0
  if (event != e_TS) {
660
0
    NLOG(NLOG_CLOCKSTATUS) {
661
0
      msyslog(LOG_INFO, "TRUE: clock %s, state %s, event %s",
662
0
        typeStr(up->type),
663
0
        stateStr(up->state),
664
0
        eventStr(event));
665
0
    }
666
0
  }
667
0
  true_debug(peer, "clock %s, state %s, event %s\n",
668
0
       typeStr(up->type), stateStr(up->state), eventStr(event));
669
0
  switch (up->type) {
670
0
  case t_goes:
671
0
    switch (event) {
672
0
    case e_Init:  /* FALLTHROUGH */
673
0
    case e_Satellite:
674
      /*
675
       * Switch back to on-second time codes and return.
676
       */
677
0
      true_send(peer, "C");
678
0
      up->state = s_Start;
679
0
      break;
680
0
    case e_Poll:
681
      /*
682
       * After each poll, check the station (satellite).
683
       */
684
0
      true_send(peer, "P");
685
      /* No state change needed. */
686
0
      break;
687
0
    default:
688
0
      break;
689
0
    }
690
    /* FALLTHROUGH */
691
0
  case t_omega:
692
0
    switch (event) {
693
0
    case e_Init:
694
0
      true_send(peer, "C");
695
0
      up->state = s_Start;
696
0
      break;
697
0
    case e_TS:
698
0
      if (up->state != s_Start && up->state != s_Auto) {
699
0
        true_send(peer, "\03\r");
700
0
        break;
701
0
      }
702
0
      up->state = s_Auto;
703
0
      break;
704
0
    default:
705
0
      break;
706
0
    }
707
0
    break;
708
0
  case t_tm:
709
0
    switch (event) {
710
0
    case e_Init:
711
0
      true_send(peer, "F18\r");
712
0
      up->state = s_Init;
713
0
      break;
714
0
    case e_F18:
715
0
      true_send(peer, "F50\r");
716
                        /*
717
                         * Timecode: " TRUETIME Mk III" or " TRUETIME XL"
718
                         * (from a TM/TMD/XL clock during initialization.)
719
                         */
720
0
                        if ( strcmp(pp->a_lastcode, " TRUETIME Mk III") == 0 ||
721
0
                            strncmp(pp->a_lastcode, " TRUETIME XL", 12) == 0) {
722
0
                                true_doevent(peer, e_F18);
723
0
                                NLOG(NLOG_CLOCKSTATUS) {
724
0
                                    msyslog(LOG_INFO, "TM/TMD/XL: %s", 
725
0
                                            pp->a_lastcode);
726
0
                                }
727
0
                                return;
728
0
                        }
729
0
      up->state = s_F18;
730
0
      break;
731
0
    case e_F50:
732
0
      true_send(peer, "F51\r");
733
0
      up->state = s_F50;
734
0
      break;
735
0
    case e_F51:
736
0
      true_send(peer, "F08\r");
737
0
      up->state = s_Start;
738
0
      break;
739
0
    case e_TS:
740
0
      if (up->state != s_Start && up->state != s_Auto) {
741
0
        true_send(peer, "\03\r");
742
0
        break;
743
0
      }
744
0
      up->state = s_Auto;
745
0
      break;
746
0
    default:
747
0
      break;
748
0
    }
749
0
    break;
750
0
  case t_tcu:
751
0
    switch (event) {
752
0
    case e_Init:
753
0
      true_send(peer, "MD3\r"); /* GPS Synch'd Gen. */
754
0
      true_send(peer, "TSU\r"); /* UTC, not GPS. */
755
0
      true_send(peer, "AU\r");  /* Auto Timestamps. */
756
0
      up->state = s_Start;
757
0
      break;
758
0
    case e_TS:
759
0
      if (up->state != s_Start && up->state != s_Auto) {
760
0
        true_send(peer, "\03\r");
761
0
        break;
762
0
      }
763
0
      up->state = s_Auto;
764
0
      break;
765
0
    default:
766
0
      break;
767
0
    }
768
0
    break;
769
0
  case t_tl3:
770
0
                switch (event) {
771
0
                    case e_Init:
772
0
                        true_send(peer, "ST1"); /* Turn on continuous stream */
773
0
                        break;
774
0
                    case e_TS:
775
0
                        up->state = s_Auto;
776
0
                        break;
777
0
                    default:
778
0
                        break;
779
0
                }
780
0
                break;
781
0
  case t_unknown:
782
0
               if (event == e_Poll)
783
0
                   break;
784
0
    switch (up->state) {
785
0
    case s_Base:
786
0
      if (event != e_Init)
787
0
          abort();
788
0
      true_send(peer, "P\r");
789
0
      up->state = s_InqGOES;
790
0
      break;
791
0
    case s_InqGOES:
792
0
      switch (event) {
793
0
      case e_Satellite:
794
0
        up->type = t_goes;
795
0
        true_doevent(peer, e_Init);
796
0
        break;
797
0
      case e_Init:  /*FALLTHROUGH*/
798
0
      case e_Huh:
799
0
      case e_TS:
800
0
                                true_send(peer, "ST0"); /* turn off TL3 auto */
801
0
                                sleep(1);               /* wait for it */
802
0
                                up->state = s_InqTL3;
803
0
                                true_send(peer, "QV");  /* see if its a TL3 */
804
0
                                break;
805
0
                            default:
806
0
                                abort();
807
0
                        }
808
0
                        break;
809
0
                    case s_InqTL3:
810
0
                        switch (event) {
811
0
                            case e_TL3:
812
0
                                up->type = t_tl3;
813
0
                                up->state = s_Auto;     /* Inq side-effect. */
814
0
                                true_send(peer, "ST1"); /* Turn on 1/sec data */
815
0
                                break;
816
0
                            case e_Init:        /*FALLTHROUGH*/
817
0
                            case e_Huh:
818
0
        up->state = s_InqOmega;
819
0
        true_send(peer, "C\r");
820
0
        break;
821
0
                            case e_TS:
822
0
                                 up->type = t_tl3;    /* Already sending data */
823
0
                                 up->state = s_Auto;
824
0
                                 break;
825
0
          default:
826
0
                                msyslog(LOG_INFO, 
827
0
                                        "TRUE: TL3 init fellthrough! (%d)", event);
828
0
                                break; 
829
0
      }
830
0
      break;
831
0
    case s_InqOmega:
832
0
      switch (event) {
833
0
      case e_TS:
834
0
        up->type = t_omega;
835
0
        up->state = s_Auto; /* Inq side-effect. */
836
0
        break;
837
0
      case e_Init:  /*FALLTHROUGH*/
838
0
      case e_Huh:
839
0
        up->state = s_InqTM;
840
0
        true_send(peer, "F18\r");
841
0
        break;
842
0
      default:
843
0
        abort();
844
0
      }
845
0
      break;
846
0
    case s_InqTM:
847
0
      switch (event) {
848
0
      case e_F18:
849
0
        up->type = t_tm;
850
0
        true_doevent(peer, e_Init);
851
0
        break;
852
0
      case e_Init:  /*FALLTHROUGH*/
853
0
      case e_Huh:
854
0
        true_send(peer, "PO\r");
855
0
        up->state = s_InqTCU;
856
0
        break;
857
0
      default:
858
0
                                msyslog(LOG_INFO, 
859
0
                                        "TRUE: TM/TMD init fellthrough!");
860
0
              break;
861
0
      }
862
0
      break;
863
0
    case s_InqTCU:
864
0
      switch (event) {
865
0
      case e_Location:
866
0
        up->type = t_tcu;
867
0
        true_doevent(peer, e_Init);
868
0
        break;
869
0
      case e_Init:  /*FALLTHROUGH*/
870
0
      case e_Huh:
871
0
        up->state = s_Base;
872
0
        sleep(1); /* XXX */
873
0
        break;
874
0
      default:
875
0
                                msyslog(LOG_INFO, 
876
0
                                        "TRUE: TCU init fellthrough!");
877
0
                                break;
878
0
      }
879
0
      break;
880
      /*
881
       * An expedient hack to prevent lint complaints,
882
       * these don't actually need to be used here...
883
       */
884
0
    case s_Init:
885
0
    case s_F18:
886
0
    case s_F50:
887
0
    case s_Start:
888
0
    case s_Auto:
889
0
    case s_Max:
890
0
      msyslog(LOG_INFO, "TRUE: state %s is unexpected!",
891
0
        stateStr(up->state));
892
0
    }
893
0
    break;
894
0
  default:
895
0
                msyslog(LOG_INFO, "TRUE: cannot identify refclock!");
896
0
    abort();    
897
    /* NOTREACHED */
898
0
  }
899
900
#ifdef CLOCK_PPS720
901
  if ((pp->sloppyclockflag & CLK_FLAG4) && !up->pcl720init) {
902
    /* Make counter trigger on gate0, count down from 65535. */
903
    pcl720_load(PCL720_IOB, PCL720_CTR, i8253_oneshot, 65535);
904
    /*
905
     * (These constants are OK since
906
     * they represent hardware maximums.)
907
     */
908
    NLOG(NLOG_CLOCKINFO) {
909
      msyslog(LOG_NOTICE, "PCL-720 initialized");
910
    }
911
    up->pcl720init++;
912
  }
913
#endif
914
915
916
0
}
917
918
/*
919
 * true_poll - called by the transmit procedure
920
 */
921
static void
922
true_poll(
923
  int unit,
924
  struct peer *peer
925
  )
926
0
{
927
0
  struct true_unit *up;
928
0
  struct refclockproc *pp;
929
930
  /*
931
   * You don't need to poll this clock.  It puts out timecodes
932
   * once per second.  If asked for a timestamp, take note.
933
   * The next time a timecode comes in, it will be fed back.
934
   */
935
0
  pp = peer->procptr;
936
0
  up = pp->unitptr;
937
0
  if (up->pollcnt > 0) {
938
0
    up->pollcnt--;
939
0
  } else {
940
0
    true_doevent(peer, e_Init);
941
0
    refclock_report(peer, CEVNT_TIMEOUT);
942
0
  }
943
944
  /*
945
   * polled every 64 seconds. Ask true_receive to hand in a
946
   * timestamp.
947
   */
948
0
  up->polled = 1;
949
0
  pp->polls++;
950
0
}
951
952
#ifdef CLOCK_PPS720
953
/*
954
 * true_sample720 - sample the PCL-720
955
 */
956
static u_long
957
true_sample720(void)
958
{
959
  unsigned long f;
960
961
  /* We wire the PCL-720's 8253.OUT0 to bit 0 of connector 3.
962
   * If it is not being held low now, we did not get called
963
   * within 65535us.
964
   */
965
  if (inb(pcl720_data_16_23(PCL720_IOB)) & 0x01) {
966
    NLOG(NLOG_CLOCKINFO) {
967
      msyslog(LOG_NOTICE, "PCL-720 out of synch");
968
    }
969
    return (0);
970
  }
971
  f = (65536 - pcl720_read(PCL720_IOB, PCL720_CTR));
972
#ifdef PPS720_DEBUG
973
  msyslog(LOG_DEBUG, "PCL-720: %luus", f);
974
#endif
975
  return (f);
976
}
977
#endif
978
979
#else
980
NONEMPTY_TRANSLATION_UNIT
981
#endif /* REFCLOCK */