1 /*        $NetBSD: refclock_arc.c,v 1.11 2024/08/18 20:47:18 christos Exp $     */
2 
3 /*
4  * refclock_arc - clock driver for ARCRON MSF/DCF/WWVB receivers
5  */
6 
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10 
11 #include "ntp_types.h"
12 
13 #if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF)
14 
15 static const char arc_version[] = { "V1.3 2003/02/21" };
16 
17 /* define PRE_NTP420 for compatibility to previous versions of NTP (at least
18    to 4.1.0 */
19 #undef PRE_NTP420
20 
21 #ifndef ARCRON_NOT_KEEN
22 #define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */
23 #endif
24 
25 #ifndef ARCRON_NOT_MULTIPLE_SAMPLES
26 #define ARCRON_MULTIPLE_SAMPLES 1 /* Use all timestamp bytes as samples. */
27 #endif
28 
29 #ifndef ARCRON_NOT_LEAPSECOND_KEEN
30 #ifndef ARCRON_LEAPSECOND_KEEN
31 #undef ARCRON_LEAPSECOND_KEEN /* Respond quickly to leap seconds: doesn't work yet. */
32 #endif
33 #endif
34 
35 /*
36 Code by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997.
37 Modifications by Damon Hart-Davis, <d@hd.org>, 1997.
38 Modifications by Paul Alfille, <palfille@partners.org>, 2003.
39 Modifications by Christopher Price, <cprice@cs-home.com>, 2003.
40 Modifications by Nigel Roles <nigel@9fs.org>, 2003.
41 
42 
43 THIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND.  USE AT
44 YOUR OWN RISK.
45 
46 Orginally developed and used with ntp3-5.85 by Derek Mulcahy.
47 
48 Built against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2.
49 
50 This code may be freely copied and used and incorporated in other
51 systems providing the disclaimer and notice of authorship are
52 reproduced.
53 
54 -------------------------------------------------------------------------------
55 
56 Nigel's notes:
57 
58 1) Called tcgetattr() before modifying, so that fields correctly initialised
59    for all operating systems
60 
61 2) Altered parsing of timestamp line so that it copes with fields which are
62    not always ASCII digits (e.g. status field when battery low)
63 
64 -------------------------------------------------------------------------------
65 
66 Christopher's notes:
67 
68 MAJOR CHANGES SINCE V1.2
69 ========================
70  1) Applied patch by Andrey Bray <abuse@madhouse.demon.co.uk>
71     2001-02-17 comp.protocols.time.ntp
72 
73  2) Added WWVB support via clock mode command, localtime/UTC time configured
74     via flag1=(0=UTC, 1=localtime)
75 
76  3) Added ignore resync request via flag2=(0=resync, 1=ignore resync)
77 
78  4) Added simplified conversion from localtime to UTC with dst/bst translation
79 
80  5) Added average signal quality poll
81 
82  6) Fixed a badformat error when no code is available due to stripping
83     \n & \r's
84 
85  7) Fixed a badformat error when clearing lencode & memset a_lastcode in poll
86     routine
87 
88  8) Lots of code cleanup, including standardized DEBUG macros and removal
89     of unused code
90 
91 -------------------------------------------------------------------------------
92 
93 Author's original note:
94 
95 I enclose my ntp driver for the Galleon Systems Arc MSF receiver.
96 
97 It works (after a fashion) on both Solaris-1 and Solaris-2.
98 
99 I am currently using ntp3-5.85.  I have been running the code for
100 about 7 months without any problems.  Even coped with the change to BST!
101 
102 I had to do some funky things to read from the clock because it uses the
103 power from the receive lines to drive the transmit lines.  This makes the
104 code look a bit stupid but it works.  I also had to put in some delays to
105 allow for the turnaround time from receive to transmit.  These delays
106 are between characters when requesting a time stamp so that shouldn't affect
107 the results too drastically.
108 
109 ...
110 
111 The bottom line is that it works but could easily be improved.  You are
112 free to do what you will with the code.  I haven't been able to determine
113 how good the clock is.  I think that this requires a known good clock
114 to compare it against.
115 
116 -------------------------------------------------------------------------------
117 
118 Damon's notes for adjustments:
119 
120 MAJOR CHANGES SINCE V1.0
121 ========================
122  1) Removal of pollcnt variable that made the clock go permanently
123     off-line once two time polls failed to gain responses.
124 
125  2) Avoiding (at least on Solaris-2) terminal becoming the controlling
126     terminal of the process when we do a low-level open().
127 
128  3) Additional logic (conditional on ARCRON_LEAPSECOND_KEEN being
129     defined) to try to resync quickly after a potential leap-second
130     insertion or deletion.
131 
132  4) Code significantly slimmer at run-time than V1.0.
133 
134 
135 GENERAL
136 =======
137 
138  1) The C preprocessor symbol to have the clock built has been changed
139     from ARC to ARCRON_MSF to CLOCK_ARCRON_MSF to minimise the
140     possiblity of clashes with other symbols in the future.
141 
142  2) PRECISION should be -4/-5 (63ms/31ms) for the following reasons:
143 
144      a) The ARC documentation claims the internal clock is (only)
145           accurate to about 20ms relative to Rugby (plus there must be
146           noticable drift and delay in the ms range due to transmission
147           delays and changing atmospheric effects).  This clock is not
148           designed for ms accuracy as NTP has spoilt us all to expect.
149 
150      b) The clock oscillator looks like a simple uncompensated quartz
151           crystal of the sort used in digital watches (ie 32768Hz) which
152           can have large temperature coefficients and drifts; it is not
153           clear if this oscillator is properly disciplined to the MSF
154           transmission, but as the default is to resync only once per
155           *day*, we can imagine that it is not, and is free-running.  We
156           can minimise drift by resyncing more often (at the cost of
157           reduced battery life), but drift/wander may still be
158           significant.
159 
160      c) Note that the bit time of 3.3ms adds to the potential error in
161           the the clock timestamp, since the bit clock of the serial link
162           may effectively be free-running with respect to the host clock
163           and the MSF clock.  Actually, the error is probably 1/16th of
164           the above, since the input data is probably sampled at at least
165           16x the bit rate.
166 
167     By keeping the clock marked as not very precise, it will have a
168     fairly large dispersion, and thus will tend to be used as a
169     `backup' time source and sanity checker, which this clock is
170     probably ideal for.  For an isolated network without other time
171     sources, this clock can probably be expected to provide *much*
172     better than 1s accuracy, which will be fine.
173 
174     By default, PRECISION is set to -4, but experience, especially at a
175     particular geographic location with a particular clock, may allow
176     this to be altered to -5.  (Note that skews of +/- 10ms are to be
177     expected from the clock from time-to-time.)  This improvement of
178     reported precision can be instigated by setting flag3 to 1, though
179     the PRECISION will revert to the normal value while the clock
180     signal quality is unknown whatever the flag3 setting.
181 
182     IN ANY CASE, BE SURE TO SET AN APPROPRIATE FUDGE FACTOR TO REMOVE
183     ANY RESIDUAL SKEW, eg:
184 
185           server 127.127.27.0 # ARCRON MSF radio clock unit 0.
186           # Fudge timestamps by about 20ms.
187           fudge 127.127.27.0 time1 0.020
188 
189     You will need to observe your system's behaviour, assuming you have
190     some other NTP source to compare it with, to work out what the
191     fudge factor should be.  For my Sun SS1 running SunOS 4.1.3_U1 with
192     my MSF clock with my distance from the MSF transmitter, +20ms
193     seemed about right, after some observation.
194 
195  3) REFID has been made "MSFa" to reflect the MSF time source and the
196     ARCRON receiver.
197 
198  4) DEFAULT_RESYNC_TIME is the time in seconds (by default) before
199     forcing a resync since the last attempt.  This is picked to give a
200     little less than an hour between resyncs and to try to avoid
201     clashing with any regular event at a regular time-past-the-hour
202     which might cause systematic errors.
203 
204     The INITIAL_RESYNC_DELAY is to avoid bothering the clock and
205     running down its batteries unnecesarily if ntpd is going to crash
206     or be killed or reconfigured quickly.  If ARCRON_KEEN is defined
207     then this period is long enough for (with normal polling rates)
208     enough time samples to have been taken to allow ntpd to sync to
209     the clock before the interruption for the clock to resync to MSF.
210     This avoids ntpd syncing to another peer first and then
211     almost immediately hopping to the MSF clock.
212 
213     The RETRY_RESYNC_TIME is used before rescheduling a resync after a
214     resync failed to reveal a statisfatory signal quality (too low or
215     unknown).
216 
217  5) The clock seems quite jittery, so I have increased the
218     median-filter size from the typical (previous) value of 3.  I
219     discard up to half the results in the filter.  It looks like maybe
220     1 sample in 10 or so (maybe less) is a spike, so allow the median
221     filter to discard at least 10% of its entries or 1 entry, whichever
222     is greater.
223 
224  6) Sleeping *before* each character sent to the unit to allow required
225     inter-character time but without introducting jitter and delay in
226     handling the response if possible.
227 
228  7) If the flag ARCRON_KEEN is defined, take time samples whenever
229     possible, even while resyncing, etc.  We rely, in this case, on the
230     clock always giving us a reasonable time or else telling us in the
231     status byte at the end of the timestamp that it failed to sync to
232     MSF---thus we should never end up syncing to completely the wrong
233     time.
234 
235  8) If the flag ARCRON_OWN_FILTER is defined, use own versions of
236     refclock median-filter routines to get round small bug in 3-5.90
237     code which does not return the median offset. XXX Removed this
238     bit due NTP Version 4 upgrade - dlm.
239 
240  9) We would appear to have a year-2000 problem with this clock since
241     it returns only the two least-significant digits of the year.  But
242     ntpd ignores the year and uses the local-system year instead, so
243     this is in fact not a problem.  Nevertheless, we attempt to do a
244     sensible thing with the dates, wrapping them into a 100-year
245     window.
246 
247  10)Logs stats information that can be used by Derek's Tcl/Tk utility
248     to show the status of the clock.
249 
250  11)The clock documentation insists that the number of bits per
251     character to be sent to the clock, and sent by it, is 11, including
252     one start bit and two stop bits.  The data format is either 7+even
253     or 8+none.
254 
255 
256 TO-DO LIST
257 ==========
258 
259   * Eliminate use of scanf(), and maybe sprintf().
260 
261   * Allow user setting of resync interval to trade battery life for
262     accuracy; maybe could be done via fudge factor or unit number.
263 
264   * Possibly note the time since the last resync of the MSF clock to
265     MSF as the age of the last reference timestamp, ie trust the
266     clock's oscillator not very much...
267 
268   * Add very slow auto-adjustment up to a value of +/- time2 to correct
269     for long-term errors in the clock value (time2 defaults to 0 so the
270     correction would be disabled by default).
271 
272   * Consider trying to use the tty_clk/ppsclock support.
273 
274   * Possibly use average or maximum signal quality reported during
275     resync, rather than just the last one, which may be atypical.
276 
277 */
278 
279 
280 /* Notes for HKW Elektronik GmBH Radio clock driver */
281 /* Author Lyndon David, Sentinet Ltd, Feb 1997      */
282 /* These notes seem also to apply usefully to the ARCRON clock. */
283 
284 /* The HKW clock module is a radio receiver tuned into the Rugby */
285 /* MSF time signal tranmitted on 60 kHz. The clock module connects */
286 /* to the computer via a serial line and transmits the time encoded */
287 /* in 15 bytes at 300 baud 7 bits two stop bits even parity */
288 
289 /* Clock communications, from the datasheet */
290 /* All characters sent to the clock are echoed back to the controlling */
291 /* device. */
292 /* Transmit time/date information */
293 /* syntax ASCII o<cr> */
294 /* Character o may be replaced if neccesary by a character whose code */
295 /* contains the lowest four bits f(hex) eg */
296 /* syntax binary: xxxx1111 00001101 */
297 
298 /* DHD note:
299 You have to wait for character echo + 10ms before sending next character.
300 */
301 
302 /* The clock replies to this command with a sequence of 15 characters */
303 /* which contain the complete time and a final <cr> making 16 characters */
304 /* in total. */
305 /* The RC computer clock will not reply immediately to this command because */
306 /* the start bit edge of the first reply character marks the beginning of */
307 /* the second. So the RC Computer Clock will reply to this command at the */
308 /* start of the next second */
309 /* The characters have the following meaning */
310 /* 1. hours tens   */
311 /* 2. hours units  */
312 /* 3. minutes tens */
313 /* 4. minutes units */
314 /* 5. seconds tens  */
315 /* 6. seconds units */
316 /* 7. day of week 1-monday 7-sunday */
317 /* 8. day of month tens */
318 /* 9. day of month units */
319 /* 10. month tens */
320 /* 11. month units */
321 /* 12. year tens */
322 /* 13. year units */
323 /* 14. BST/UTC status */
324 /*        bit 7     parity */
325 /*        bit 6     always 0 */
326 /*        bit 5     always 1 */
327 /*        bit 4     always 1 */
328 /*        bit 3     always 0 */
329 /*        bit 2     =1 if UTC is in effect, complementary to the BST bit */
330 /*        bit 1     =1 if BST is in effect, according to the BST bit     */
331 /*        bit 0     BST/UTC change impending bit=1 in case of change impending */
332 /* 15. status */
333 /*        bit 7     parity */
334 /*        bit 6     always 0 */
335 /*        bit 5     always 1 */
336 /*        bit 4     always 1 */
337 /*        bit 3     =1 if low battery is detected */
338 /*        bit 2     =1 if the very last reception attempt failed and a valid */
339 /*                  time information already exists (bit0=1) */
340 /*                  =0 if the last reception attempt was successful */
341 /*        bit 1     =1 if at least one reception since 2:30 am was successful */
342 /*                  =0 if no reception attempt since 2:30 am was successful */
343 /*        bit 0     =1 if the RC Computer Clock contains valid time information */
344 /*                  This bit is zero after reset and one after the first */
345 /*                  successful reception attempt */
346 
347 /* DHD note:
348 Also note g<cr> command which confirms that a resync is in progress, and
349 if so what signal quality (0--5) is available.
350 Also note h<cr> command which starts a resync to MSF signal.
351 */
352 
353 
354 #include "ntpd.h"
355 #include "ntp_io.h"
356 #include "ntp_refclock.h"
357 #include "ntp_calendar.h"
358 #include "ntp_stdlib.h"
359 
360 #include <stdio.h>
361 #include <ctype.h>
362 
363 #if defined(HAVE_BSD_TTYS)
364 #include <sgtty.h>
365 #endif /* HAVE_BSD_TTYS */
366 
367 #if defined(HAVE_SYSV_TTYS)
368 #include <termio.h>
369 #endif /* HAVE_SYSV_TTYS */
370 
371 #if defined(HAVE_TERMIOS)
372 #include <termios.h>
373 #endif
374 
375 /*
376  * This driver supports the ARCRON MSF/DCF/WWVB Radio Controlled Clock
377  */
378 
379 /*
380  * Interface definitions
381  */
382 #define DEVICE                "/dev/arc%d"        /* Device name and unit. */
383 #define SPEED                 B300                /* UART speed (300 baud) */
384 #define PRECISION   (-4)                /* Precision  (~63 ms). */
385 #define HIGHPRECISION         (-5)                /* If things are going well... */
386 #define REFID                 "MSFa"              /* Reference ID. */
387 #define REFID_MSF   "MSF"               /* Reference ID. */
388 #define REFID_DCF77 "DCF"               /* Reference ID. */
389 #define REFID_WWVB  "WWVB"              /* Reference ID. */
390 #define DESCRIPTION "ARCRON MSF/DCF/WWVB Receiver"
391 
392 #ifdef PRE_NTP420
393 #define MODE ttlmax
394 #else
395 #define MODE ttl
396 #endif
397 
398 #define LENARC                16                  /* Format `o' timecode length. */
399 
400 #define BITSPERCHAR 11                  /* Bits per character. */
401 #define BITTIME               0x0DA740E /* Time for 1 bit at 300bps. */
402 #define CHARTIME10  0x8888888 /* Time for 10-bit char at 300bps. */
403 #define CHARTIME11  0x962FC96 /* Time for 11-bit char at 300bps. */
404 #define CHARTIME                        /* Time for char at 300bps. */ \
405 ( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \
406                                                (BITSPERCHAR * BITTIME) ) )
407 
408      /* Allow for UART to accept char half-way through final stop bit. */
409 #define INITIALOFFSET ((u_int32)(-BITTIME/2))
410 
411      /*
412     charoffsets[x] is the time after the start of the second that byte
413     x (with the first byte being byte 1) is received by the UART,
414     assuming that the initial edge of the start bit of the first byte
415     is on-time.  The values are represented as the fractional part of
416     an l_fp.
417 
418     We store enough values to have the offset of each byte including
419     the trailing \r, on the assumption that the bytes follow one
420     another without gaps.
421     */
422      static const u_int32 charoffsets[LENARC+1] = {
423 #if BITSPERCHAR == 11 /* Usual case. */
424                /* Offsets computed as accurately as possible... */
425                0,
426                INITIALOFFSET + 0x0962fc96, /*  1 chars,  11 bits */
427                INITIALOFFSET + 0x12c5f92c, /*  2 chars,  22 bits */
428                INITIALOFFSET + 0x1c28f5c3, /*  3 chars,  33 bits */
429                INITIALOFFSET + 0x258bf259, /*  4 chars,  44 bits */
430                INITIALOFFSET + 0x2eeeeeef, /*  5 chars,  55 bits */
431                INITIALOFFSET + 0x3851eb85, /*  6 chars,  66 bits */
432                INITIALOFFSET + 0x41b4e81b, /*  7 chars,  77 bits */
433                INITIALOFFSET + 0x4b17e4b1, /*  8 chars,  88 bits */
434                INITIALOFFSET + 0x547ae148, /*  9 chars,  99 bits */
435                INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */
436                INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */
437                INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */
438                INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */
439                INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */
440                INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */
441                INITIALOFFSET + 0x962fc963  /* 16 chars, 176 bits */
442 #else
443                /* Offsets computed with a small rounding error... */
444                0,
445                INITIALOFFSET +  1 * CHARTIME,
446                INITIALOFFSET +  2 * CHARTIME,
447                INITIALOFFSET +  3 * CHARTIME,
448                INITIALOFFSET +  4 * CHARTIME,
449                INITIALOFFSET +  5 * CHARTIME,
450                INITIALOFFSET +  6 * CHARTIME,
451                INITIALOFFSET +  7 * CHARTIME,
452                INITIALOFFSET +  8 * CHARTIME,
453                INITIALOFFSET +  9 * CHARTIME,
454                INITIALOFFSET + 10 * CHARTIME,
455                INITIALOFFSET + 11 * CHARTIME,
456                INITIALOFFSET + 12 * CHARTIME,
457                INITIALOFFSET + 13 * CHARTIME,
458                INITIALOFFSET + 14 * CHARTIME,
459                INITIALOFFSET + 15 * CHARTIME,
460                INITIALOFFSET + 16 * CHARTIME
461 #endif
462      };
463 
464 #define DEFAULT_RESYNC_TIME  (57*60)    /* Gap between resync attempts (s). */
465 #define RETRY_RESYNC_TIME    (27*60)    /* Gap to emergency resync attempt. */
466 #ifdef ARCRON_KEEN
467 #define INITIAL_RESYNC_DELAY 500        /* Delay before first resync. */
468 #else
469 #define INITIAL_RESYNC_DELAY 50                   /* Delay before first resync. */
470 #endif
471 
472      static const int moff[12] =
473 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
474 /* Flags for a raw open() of the clock serial device. */
475 #ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */
476 #define OPEN_FLAGS (O_RDWR | O_NOCTTY)
477 #else               /* Oh well, it may not matter... */
478 #define OPEN_FLAGS (O_RDWR)
479 #endif
480 
481 
482 /* Length of queue of command bytes to be sent. */
483 #define CMDQUEUELEN 4                             /* Enough for two cmds + each \r. */
484 /* Queue tick time; interval in seconds between chars taken off queue. */
485 /* Must be >= 2 to allow o\r response to come back uninterrupted. */
486 #define QUEUETICK   2                             /* Allow o\r reply to finish. */
487 
488 /*
489  * ARC unit control structure
490  */
491 struct arcunit {
492           l_fp lastrec;           /* Time tag for the receive time (system). */
493           int status;             /* Clock status. */
494 
495           int quality;            /* Quality of reception 0--5 for unit. */
496           /* We may also use the values -1 or 6 internally. */
497           u_long quality_stamp; /* Next time to reset quality average. */
498 
499           u_long next_resync; /* Next resync time (s) compared to current_time. */
500           int resyncing;          /* Resync in progress if true. */
501 
502           /* In the outgoing queue, cmdqueue[0] is next to be sent. */
503           char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */
504 
505           u_long saved_flags; /* Saved fudge flags. */
506 };
507 
508 #ifdef ARCRON_LEAPSECOND_KEEN
509 /* The flag `possible_leap' is set non-zero when any MSF unit
510        thinks a leap-second may have happened.
511 
512        Set whenever we receive a valid time sample in the first hour of
513        the first day of the first/seventh months.
514 
515        Outside the special hour this value is unconditionally set
516        to zero by the receive routine.
517 
518        On finding itself in this timeslot, as long as the value is
519        non-negative, the receive routine sets it to a positive value to
520        indicate a resync to MSF should be performed.
521 
522        In the poll routine, if this value is positive and we are not
523        already resyncing (eg from a sync that started just before
524        midnight), start resyncing and set this value negative to
525        indicate that a leap-triggered resync has been started.  Having
526        set this negative prevents the receive routine setting it
527        positive and thus prevents multiple resyncs during the witching
528        hour.
529      */
530 static int possible_leap = 0;       /* No resync required by default. */
531 #endif
532 
533 #if 0
534 static void dummy_event_handler (struct peer *);
535 static void   arc_event_handler (struct peer *);
536 #endif /* 0 */
537 
538 #define QUALITY_UNKNOWN           -1 /* Indicates unknown clock quality. */
539 #define MIN_CLOCK_QUALITY    0 /* Min quality clock will return. */
540 #define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */
541 #define MAX_CLOCK_QUALITY    5 /* Max quality clock will return. */
542 
543 /*
544  * Function prototypes
545  */
546 static    int       arc_start (int, struct peer *);
547 static    void      arc_shutdown        (int, struct peer *);
548 static    void      arc_receive         (struct recvbuf *);
549 static    void      arc_poll  (int, struct peer *);
550 
551 /*
552  * Transfer vector
553  */
554 struct  refclock refclock_arc = {
555           arc_start,                    /* start up driver */
556           arc_shutdown,                 /* shut down driver */
557           arc_poll,           /* transmit poll message */
558           noentry,            /* not used (old arc_control) */
559           noentry,            /* initialize driver (not used) */
560           noentry,            /* not used (old arc_buginfo) */
561           NOFLAGS                       /* not used */
562 };
563 
564 /* Queue us up for the next tick. */
565 #define ENQUEUE(up) \
566           do { \
567                peer->procptr->nextaction = current_time + QUEUETICK; \
568           } while(0)
569 
570 /* Placeholder event handler---does nothing safely---soaks up loose tick. */
571 static void
dummy_event_handler(struct peer * peer)572 dummy_event_handler(
573           struct peer *peer
574           )
575 {
576 #ifdef DEBUG
577           if(debug) { printf("arc: dummy_event_handler() called.\n"); }
578 #endif
579 }
580 
581 /*
582 Normal event handler.
583 
584 Take first character off queue and send to clock if not a null.
585 
586 Shift characters down and put a null on the end.
587 
588 We assume that there is no parallelism so no race condition, but even
589 if there is nothing bad will happen except that we might send some bad
590 data to the clock once in a while.
591 */
592 static void
arc_event_handler(struct peer * peer)593 arc_event_handler(
594           struct peer *peer
595           )
596 {
597           struct refclockproc *pp = peer->procptr;
598           register struct arcunit *up = pp->unitptr;
599           int i;
600           char c;
601 #ifdef DEBUG
602           if(debug > 2) { printf("arc: arc_event_handler() called.\n"); }
603 #endif
604 
605           c = up->cmdqueue[0];       /* Next char to be sent. */
606           /* Shift down characters, shifting trailing \0 in at end. */
607           for(i = 0; i < CMDQUEUELEN; ++i)
608           { up->cmdqueue[i] = up->cmdqueue[i+1]; }
609 
610           /* Don't send '\0' characters. */
611           if(c != '\0') {
612                     if(write(pp->io.fd, &c, 1) != 1) {
613                               msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd);
614                     }
615 #ifdef DEBUG
616                     else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); }
617 #endif
618           }
619 
620           ENQUEUE(up);
621 }
622 
623 /*
624  * arc_start - open the devices and initialize data for processing
625  */
626 static int
arc_start(int unit,struct peer * peer)627 arc_start(
628           int unit,
629           struct peer *peer
630           )
631 {
632           register struct arcunit *up;
633           struct refclockproc *pp;
634           int temp_fd;
635           int fd;
636           char device[20];
637 #ifdef HAVE_TERMIOS
638           struct termios arg;
639 #endif
640 
641           msyslog(LOG_NOTICE, "MSF_ARCRON %s: opening unit %d",
642                     arc_version, unit);
643           DPRINTF(1, ("arc: %s: attempt to open unit %d.\n", arc_version,
644                     unit));
645 
646           /*
647            * Open serial port. Use CLK line discipline, if available.
648            */
649           snprintf(device, sizeof(device), DEVICE, unit);
650           temp_fd = refclock_open(&peer->srcadr, device, SPEED, LDISC_CLK);
651           if (temp_fd <= 0)
652                     return 0;
653           DPRINTF(1, ("arc: unit %d using tty_open().\n", unit));
654           fd = tty_open(device, OPEN_FLAGS, 0777);
655           if (fd < 0) {
656                     msyslog(LOG_ERR, "MSF_ARCRON(%d): failed second open(%s, 0777): %m.",
657                               unit, device);
658                     close(temp_fd);
659                     return 0;
660           }
661           close(temp_fd);
662           temp_fd = -1;                 /* not used after this, at *this* time. */
663 
664 #ifndef SYS_WINNT
665           if (-1 == fcntl(fd, F_SETFL, 0)) /* clear the descriptor flags */
666                     msyslog(LOG_ERR, "MSF_ARCRON(%d): fcntl(F_SETFL, 0): %m.",
667                               unit);
668 
669 #endif
670           DPRINTF(1, ("arc: opened RS232 port with file descriptor %d.\n", fd));
671 
672 #ifdef HAVE_TERMIOS
673 
674           if (tcgetattr(fd, &arg) < 0) {
675                     msyslog(LOG_ERR, "MSF_ARCRON(%d): tcgetattr(%s): %m.",
676                               unit, device);
677                     close(fd);
678                     return 0;
679           }
680 
681           arg.c_iflag = IGNBRK | ISTRIP;
682           arg.c_oflag = 0;
683           arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB;
684           arg.c_lflag = 0;
685           arg.c_cc[VMIN] = 1;
686           arg.c_cc[VTIME] = 0;
687 
688           if (tcsetattr(fd, TCSANOW, &arg) < 0) {
689                     msyslog(LOG_ERR, "MSF_ARCRON(%d): tcsetattr(%s): %m.",
690                               unit, device);
691                     close(fd);
692                     return 0;
693           }
694 
695 #else
696 
697           msyslog(LOG_ERR, "ARCRON: termios required by this driver");
698           (void)close(fd);
699 
700           return 0;
701 
702 #endif
703 
704           /* Set structure to all zeros... */
705           up = emalloc_zero(sizeof(*up));
706           pp = peer->procptr;
707           pp->io.clock_recv = arc_receive;
708           pp->io.srcclock = peer;
709           pp->io.datalen = 0;
710           pp->io.fd = fd;
711           if (!io_addclock(&pp->io)) {
712                     close(fd);
713                     pp->io.fd = -1;
714                     free(up);
715                     return(0);
716           }
717           pp->unitptr = up;
718 
719           /*
720            * Initialize miscellaneous variables
721            */
722           peer->precision = PRECISION;
723           peer->stratum = 2;              /* Default to stratum 2 not 0. */
724           pp->clockdesc = DESCRIPTION;
725           if (peer->MODE > 3) {
726                     msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", peer->MODE);
727                     return 0;
728           }
729 #ifdef DEBUG
730           if(debug) { printf("arc: mode = %d.\n", peer->MODE); }
731 #endif
732           switch (peer->MODE) {
733               case 1:
734                     memcpy((char *)&pp->refid, REFID_MSF, 4);
735                     break;
736               case 2:
737                     memcpy((char *)&pp->refid, REFID_DCF77, 4);
738                     break;
739               case 3:
740                     memcpy((char *)&pp->refid, REFID_WWVB, 4);
741                     break;
742               default:
743                     memcpy((char *)&pp->refid, REFID, 4);
744                     break;
745           }
746           /* Spread out resyncs so that they should remain separated. */
747           up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009;
748 
749 #if 0 /* Not needed because of zeroing of arcunit structure... */
750           up->resyncing = 0;              /* Not resyncing yet. */
751           up->saved_flags = 0;            /* Default is all flags off. */
752           /* Clear send buffer out... */
753           {
754                     int i;
755                     for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; }
756           }
757 #endif
758 
759 #ifdef ARCRON_KEEN
760           up->quality = QUALITY_UNKNOWN;  /* Trust the clock immediately. */
761 #else
762           up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
763 #endif
764 
765           peer->procptr->action = arc_event_handler;
766 
767           ENQUEUE(up);
768 
769           return(1);
770 }
771 
772 
773 /*
774  * arc_shutdown - shut down the clock
775  */
776 static void
arc_shutdown(int unit,struct peer * peer)777 arc_shutdown(
778           int unit,
779           struct peer *peer
780           )
781 {
782           register struct arcunit *up;
783           struct refclockproc *pp;
784 
785           peer->procptr->action = dummy_event_handler;
786 
787           pp = peer->procptr;
788           up = pp->unitptr;
789           if (-1 != pp->io.fd)
790                     io_closeclock(&pp->io);
791           if (NULL != up)
792                     free(up);
793 }
794 
795 /*
796 Compute space left in output buffer.
797 */
798 static int
space_left(register struct arcunit * up)799 space_left(
800           register struct arcunit *up
801           )
802 {
803           int spaceleft;
804 
805           /* Compute space left in buffer after any pending output. */
806           for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft)
807           { if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } }
808           return(spaceleft);
809 }
810 
811 /*
812 Send command by copying into command buffer as far forward as possible,
813 after any pending output.
814 
815 Indicate an error by returning 0 if there is not space for the command.
816 */
817 static int
send_slow(register struct arcunit * up,int fd,const char * s)818 send_slow(
819           register struct arcunit *up,
820           int fd,
821           const char *s
822           )
823 {
824           int sl = strlen(s);
825           int spaceleft = space_left(up);
826 
827 #ifdef DEBUG
828           if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
829 #endif
830           if(spaceleft < sl) { /* Should not normally happen... */
831 #ifdef DEBUG
832                     msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)",
833                               sl, spaceleft);
834 #endif
835                     return(0);                              /* FAILED! */
836           }
837 
838           /* Copy in the command to be sent. */
839           while(*s && spaceleft > 0) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; }
840 
841           return(1);
842 }
843 
844 
845 static int
get2(char * p,int * val)846 get2(char *p, int *val)
847 {
848   if (!isdigit((unsigned char)p[0]) || !isdigit((unsigned char)p[1])) return 0;
849   *val = (p[0] - '0') * 10 + p[1] - '0';
850   return 1;
851 }
852 
853 static int
get1(char * p,int * val)854 get1(char *p, int *val)
855 {
856   if (!isdigit((unsigned char)p[0])) return 0;
857   *val = p[0] - '0';
858   return 1;
859 }
860 
861 /* Macro indicating action we will take for different quality values. */
862 #define quality_action(q) \
863 (((q) == QUALITY_UNKNOWN) ?         "UNKNOWN, will use clock anyway" : \
864  (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \
865   "OK, will use clock"))
866 
867 /*
868  * arc_receive - receive data from the serial interface
869  */
870 static void
arc_receive(struct recvbuf * rbufp)871 arc_receive(
872           struct recvbuf *rbufp
873           )
874 {
875           static int quality_average = 0;
876           static int quality_sum = 0;
877           static int quality_polls = 0;
878           register struct arcunit *up;
879           struct refclockproc *pp;
880           struct peer *peer;
881           char c;
882           int i, wday, month, flags, status;
883           int arc_last_offset;
884     #ifdef DEBUG
885           int n;
886     #endif
887 
888           /*
889            * Initialize pointers and read the timecode and timestamp
890            */
891           peer = rbufp->recv_peer;
892           pp = peer->procptr;
893           up = pp->unitptr;
894 
895 
896           /*
897             If the command buffer is empty, and we are resyncing, insert a
898             g\r quality request into it to poll for signal quality again.
899           */
900           if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) {
901 #ifdef DEBUG
902                     if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); }
903 #endif
904                     send_slow(up, pp->io.fd, "g\r");
905           }
906 
907           /*
908             The `arc_last_offset' is the offset in lastcode[] of the last byte
909             received, and which we assume actually received the input
910             timestamp.
911 
912             (When we get round to using tty_clk and it is available, we
913             assume that we will receive the whole timecode with the
914             trailing \r, and that that \r will be timestamped.  But this
915             assumption also works if receive the characters one-by-one.)
916           */
917           arc_last_offset = pp->lencode+rbufp->recv_length - 1;
918 
919           /*
920             We catch a timestamp iff:
921 
922             * The command code is `o' for a timestamp.
923 
924             * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have
925             exactly char in the buffer (the command code) so that we
926             only sample the first character of the timecode as our
927             `on-time' character.
928 
929             * The first character in the buffer is not the echoed `\r'
930             from the `o` command (so if we are to timestamp an `\r' it
931             must not be first in the receive buffer with lencode==1.
932             (Even if we had other characters following it, we probably
933             would have a premature timestamp on the '\r'.)
934 
935             * We have received at least one character (I cannot imagine
936             how it could be otherwise, but anyway...).
937           */
938           c = rbufp->recv_buffer[0];
939           if((pp->a_lastcode[0] == 'o') &&
940 #ifndef ARCRON_MULTIPLE_SAMPLES
941              (pp->lencode == 1) &&
942 #endif
943              ((pp->lencode != 1) || (c != '\r')) &&
944              (arc_last_offset >= 1)) {
945                     /* Note that the timestamp should be corrected if >1 char rcvd. */
946                     l_fp timestamp;
947                     timestamp = rbufp->recv_time;
948 #ifdef DEBUG
949                     if(debug) { /* Show \r as `R', other non-printing char as `?'. */
950                               printf("arc: stamp -->%c<-- (%d chars rcvd)\n",
951                                      ((c == '\r') ? 'R' : (isgraph((unsigned char)c) ? c : '?')),
952                                      rbufp->recv_length);
953                     }
954 #endif
955 
956                     /*
957                       Now correct timestamp by offset of last byte received---we
958                       subtract from the receive time the delay implied by the
959                       extra characters received.
960 
961                       Reject the input if the resulting code is too long, but
962                       allow for the trailing \r, normally not used but a good
963                       handle for tty_clk or somesuch kernel timestamper.
964                     */
965                     if(arc_last_offset > LENARC) {
966 #ifdef DEBUG
967                               if(debug) {
968                                         printf("arc: input code too long (%d cf %d); rejected.\n",
969                                                arc_last_offset, LENARC);
970                               }
971 #endif
972                               pp->lencode = 0;
973                               refclock_report(peer, CEVNT_BADREPLY);
974                               return;
975                     }
976 
977                     L_SUBUF(&timestamp, charoffsets[arc_last_offset]);
978 #ifdef DEBUG
979                     if(debug > 1) {
980                               printf(
981                                         "arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
982                                         ((rbufp->recv_length > 1) ? "*** " : ""),
983                                         rbufp->recv_length,
984                                         arc_last_offset,
985                                         mfptoms((unsigned long)0,
986                                                   charoffsets[arc_last_offset],
987                                                   1));
988                     }
989 #endif
990 
991 #ifdef ARCRON_MULTIPLE_SAMPLES
992                     /*
993                       If taking multiple samples, capture the current adjusted
994                       sample iff:
995 
996                       * No timestamp has yet been captured (it is zero), OR
997 
998                       * This adjusted timestamp is earlier than the one already
999                       captured, on the grounds that this one suffered less
1000                       delay in being delivered to us and is more accurate.
1001 
1002                     */
1003                     if(L_ISZERO(&(up->lastrec)) ||
1004                        L_ISGEQ(&(up->lastrec), &timestamp))
1005 #endif
1006                     {
1007 #ifdef DEBUG
1008                               if(debug > 1) {
1009                                         printf("arc: system timestamp captured.\n");
1010 #ifdef ARCRON_MULTIPLE_SAMPLES
1011                                         if(!L_ISZERO(&(up->lastrec))) {
1012                                                   l_fp diff;
1013                                                   diff = up->lastrec;
1014                                                   L_SUB(&diff, &timestamp);
1015                                                   printf("arc: adjusted timestamp by -%sms.\n",
1016                                                          mfptoms(diff.l_ui, diff.l_uf, 3));
1017                                         }
1018 #endif
1019                               }
1020 #endif
1021                               up->lastrec = timestamp;
1022                     }
1023 
1024           }
1025 
1026           /* Just in case we still have lots of rubbish in the buffer... */
1027           /* ...and to avoid the same timestamp being reused by mistake, */
1028           /* eg on receipt of the \r coming in on its own after the      */
1029           /* timecode.                                                                 */
1030           if(pp->lencode >= LENARC) {
1031 #ifdef DEBUG
1032                     if(debug && (rbufp->recv_buffer[0] != '\r'))
1033                     { printf("arc: rubbish in pp->a_lastcode[].\n"); }
1034 #endif
1035                     pp->lencode = 0;
1036                     return;
1037           }
1038 
1039           /* Append input to code buffer, avoiding overflow. */
1040           for(i = 0; i < rbufp->recv_length; i++) {
1041                     if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */
1042                     c = rbufp->recv_buffer[i];
1043 
1044                     /* Drop trailing '\r's and drop `h' command echo totally. */
1045                     if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; }
1046 
1047                     /*
1048                       If we've just put an `o' in the lastcode[0], clear the
1049                       timestamp in anticipation of a timecode arriving soon.
1050 
1051                       We would expect to get to process this before any of the
1052                       timecode arrives.
1053                     */
1054                     if((c == 'o') && (pp->lencode == 1)) {
1055                               L_CLR(&(up->lastrec));
1056 #ifdef DEBUG
1057                               if(debug > 1) { printf("arc: clearing timestamp.\n"); }
1058 #endif
1059                     }
1060           }
1061           if (pp->lencode == 0) return;
1062 
1063           /* Handle a quality message. */
1064           if(pp->a_lastcode[0] == 'g') {
1065                     int r, q;
1066 
1067                     if(pp->lencode < 3) { return; } /* Need more data... */
1068                     r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */
1069                     q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */
1070                     if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
1071                        ((r & 0x70) != 0x30)) {
1072                               /* Badly formatted response. */
1073 #ifdef DEBUG
1074                               if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
1075 #endif
1076                               return;
1077                     }
1078                     if(r == '3') { /* Only use quality value whilst sync in progress. */
1079                               if (up->quality_stamp < current_time) {
1080                                         struct calendar cal;
1081                                         l_fp new_stamp;
1082 
1083                                         get_systime (&new_stamp);
1084                                         caljulian (new_stamp.l_ui, &cal);
1085                                         up->quality_stamp =
1086                                                   current_time + 60 - cal.second + 5;
1087                                         quality_sum = 0;
1088                                         quality_polls = 0;
1089                               }
1090                               quality_sum += (q & 0xf);
1091                               quality_polls++;
1092                               quality_average = (quality_sum / quality_polls);
1093 #ifdef DEBUG
1094                               if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); }
1095 #endif
1096                     } else if( /* (r == '2') && */ up->resyncing) {
1097                               up->quality = quality_average;
1098 #ifdef DEBUG
1099                               if(debug)
1100                               {
1101                                         printf("arc: sync finished, signal quality %d: %s\n",
1102                                                up->quality,
1103                                                quality_action(up->quality));
1104                               }
1105 #endif
1106                               msyslog(LOG_NOTICE,
1107                                         "ARCRON: sync finished, signal quality %d: %s",
1108                                         up->quality,
1109                                         quality_action(up->quality));
1110                               up->resyncing = 0; /* Resync is over. */
1111                               quality_average = 0;
1112                               quality_sum = 0;
1113                               quality_polls = 0;
1114 
1115 #ifdef ARCRON_KEEN
1116                               /* Clock quality dubious; resync earlier than usual. */
1117                               if((up->quality == QUALITY_UNKNOWN) ||
1118                                  (up->quality < MIN_CLOCK_QUALITY_OK))
1119                               { up->next_resync = current_time + RETRY_RESYNC_TIME; }
1120 #endif
1121                     }
1122                     pp->lencode = 0;
1123                     return;
1124           }
1125 
1126           /* Stop now if this is not a timecode message. */
1127           if(pp->a_lastcode[0] != 'o') {
1128                     pp->lencode = 0;
1129                     refclock_report(peer, CEVNT_BADREPLY);
1130                     return;
1131           }
1132 
1133           /* If we don't have enough data, wait for more... */
1134           if(pp->lencode < LENARC) { return; }
1135 
1136 
1137           /* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
1138 #ifdef DEBUG
1139           if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
1140 #endif
1141 
1142           /* But check that we actually captured a system timestamp on it. */
1143           if(L_ISZERO(&(up->lastrec))) {
1144 #ifdef DEBUG
1145                     if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
1146 #endif
1147                     pp->lencode = 0;
1148                     refclock_report(peer, CEVNT_BADREPLY);
1149                     return;
1150           }
1151           /*
1152             Append a mark of the clock's received signal quality for the
1153             benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown'
1154             quality value to `6' for his s/w) and terminate the string for
1155             sure.  This should not go off the buffer end.
1156           */
1157           pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
1158                                                '6' : ('0' + up->quality));
1159           pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
1160 
1161 #ifdef PRE_NTP420
1162           /* We don't use the micro-/milli- second part... */
1163           pp->usec = 0;
1164           pp->msec = 0;
1165 #else
1166           /* We don't use the nano-second part... */
1167           pp->nsec = 0;
1168 #endif
1169           /* Validate format and numbers. */
1170           if (pp->a_lastcode[0] != 'o'
1171                     || !get2(pp->a_lastcode + 1, &pp->hour)
1172                     || !get2(pp->a_lastcode + 3, &pp->minute)
1173                     || !get2(pp->a_lastcode + 5, &pp->second)
1174                     || !get1(pp->a_lastcode + 7, &wday)
1175                     || !get2(pp->a_lastcode + 8, &pp->day)
1176                     || !get2(pp->a_lastcode + 10, &month)
1177                     || !get2(pp->a_lastcode + 12, &pp->year)) {
1178 #ifdef DEBUG
1179                     /* Would expect to have caught major problems already... */
1180                     if(debug) { printf("arc: badly formatted data.\n"); }
1181 #endif
1182                     pp->lencode = 0;
1183                     refclock_report(peer, CEVNT_BADREPLY);
1184                     return;
1185           }
1186           flags = pp->a_lastcode[14];
1187           status = pp->a_lastcode[15];
1188 #ifdef DEBUG
1189           if(debug) { printf("arc: status 0x%.2x flags 0x%.2x\n", flags, status); }
1190           n = 9;
1191 #endif
1192 
1193           /*
1194             Validate received values at least enough to prevent internal
1195             array-bounds problems, etc.
1196           */
1197           if((pp->hour < 0) || (pp->hour > 23) ||
1198              (pp->minute < 0) || (pp->minute > 59) ||
1199              (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
1200              (wday < 1) || (wday > 7) ||
1201              (pp->day < 1) || (pp->day > 31) ||
1202              (month < 1) || (month > 12) ||
1203              (pp->year < 0) || (pp->year > 99)) {
1204                     /* Data out of range. */
1205                     pp->lencode = 0;
1206                     refclock_report(peer, CEVNT_BADREPLY);
1207                     return;
1208           }
1209 
1210 
1211           if(peer->MODE == 0) { /* compatiblity to original version */
1212                     int bst = flags;
1213                     /* Check that BST/UTC bits are the complement of one another. */
1214                     if(!(bst & 2) == !(bst & 4)) {
1215                               pp->lencode = 0;
1216                               refclock_report(peer, CEVNT_BADREPLY);
1217                               return;
1218                     }
1219           }
1220           if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
1221 
1222           /* Year-2000 alert! */
1223           /* Attempt to wrap 2-digit date into sensible window. */
1224           if(pp->year < YEAR_PIVOT) { pp->year += 100; }              /* Y2KFixes */
1225           pp->year += 1900;   /* use full four-digit year */          /* Y2KFixes */
1226           /*
1227             Attempt to do the right thing by screaming that the code will
1228             soon break when we get to the end of its useful life.  What a
1229             hero I am...  PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X!
1230           */
1231           if(pp->year >= YEAR_PIVOT+2000-2 ) {                        /* Y2KFixes */
1232                     /*This should get attention B^> */
1233                     msyslog(LOG_NOTICE,
1234                               "ARCRON: fix me!  EITHER YOUR DATE IS BADLY WRONG or else I will break soon!");
1235           }
1236 #ifdef DEBUG
1237           if(debug) {
1238                     printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
1239                            n,
1240                            pp->hour, pp->minute, pp->second,
1241                            pp->day, month, pp->year, flags, status);
1242           }
1243 #endif
1244 
1245           /*
1246             The status value tested for is not strictly supported by the
1247             clock spec since the value of bit 2 (0x4) is claimed to be
1248             undefined for MSF, yet does seem to indicate if the last resync
1249             was successful or not.
1250           */
1251           pp->leap = LEAP_NOWARNING;
1252           status &= 0x7;
1253           if(status == 0x3) {
1254                     if(status != up->status)
1255                     { msyslog(LOG_NOTICE, "ARCRON: signal acquired"); }
1256           } else {
1257                     if(status != up->status) {
1258                               msyslog(LOG_NOTICE, "ARCRON: signal lost");
1259                               pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
1260                               up->status = status;
1261                               pp->lencode = 0;
1262                               refclock_report(peer, CEVNT_FAULT);
1263                               return;
1264                     }
1265           }
1266           up->status = status;
1267 
1268           if (peer->MODE == 0) { /* compatiblity to original version */
1269                     int bst = flags;
1270 
1271                     pp->day += moff[month - 1];
1272 
1273                     if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */
1274 
1275                     /* Convert to UTC if required */
1276                     if(bst & 2) {
1277                               pp->hour--;
1278                               if (pp->hour < 0) {
1279                                         pp->hour = 23;
1280                                         pp->day--;
1281                                         /* If we try to wrap round the year
1282                                          * (BST on 1st Jan), reject.*/
1283                                         if(pp->day < 0) {
1284                                                   pp->lencode = 0;
1285                                                   refclock_report(peer, CEVNT_BADTIME);
1286                                                   return;
1287                                         }
1288                               }
1289                     }
1290           }
1291 
1292           if(peer->MODE > 0) {
1293                     if(pp->sloppyclockflag & CLK_FLAG1) {
1294                               struct tm  local;
1295                               struct tm *gmtp;
1296                               time_t       unixtime;
1297 
1298                               /*
1299                                * Convert to GMT for sites that distribute localtime.
1300                                * This means we have to do Y2K conversion on the
1301                                * 2-digit year; otherwise, we get the time wrong.
1302                                */
1303 
1304                               memset(&local, 0, sizeof(local));
1305 
1306                               local.tm_year  = pp->year-1900;
1307                               local.tm_mon   = month-1;
1308                               local.tm_mday  = pp->day;
1309                               local.tm_hour  = pp->hour;
1310                               local.tm_min   = pp->minute;
1311                               local.tm_sec   = pp->second;
1312                               switch (peer->MODE) {
1313                                   case 1:
1314                                         local.tm_isdst = (flags & 2);
1315                                         break;
1316                                   case 2:
1317                                         local.tm_isdst = (flags & 2);
1318                                         break;
1319                                   case 3:
1320                                         switch (flags & 3) {
1321                                             case 0: /* It is unclear exactly when the
1322                                                          Arcron changes from DST->ST and
1323                                                          ST->DST. Testing has shown this
1324                                                          to be irregular. For the time
1325                                                          being, let the OS decide. */
1326                                                   local.tm_isdst = 0;
1327 #ifdef DEBUG
1328                                                   if (debug)
1329                                                       printf ("arc: DST = 00 (0)\n");
1330 #endif
1331                                                   break;
1332                                             case 1: /* dst->st time */
1333                                                   local.tm_isdst = -1;
1334 #ifdef DEBUG
1335                                                   if (debug)
1336                                                       printf ("arc: DST = 01 (1)\n");
1337 #endif
1338                                                   break;
1339                                             case 2: /* st->dst time */
1340                                                   local.tm_isdst = -1;
1341 #ifdef DEBUG
1342                                                   if (debug)
1343                                                       printf ("arc: DST = 10 (2)\n");
1344 #endif
1345                                                   break;
1346                                             case 3: /* dst time */
1347                                                 local.tm_isdst = 1;
1348 #ifdef DEBUG
1349                                                   if (debug)
1350                                                       printf ("arc: DST = 11 (3)\n");
1351 #endif
1352                                                   break;
1353                                         }
1354                                         break;
1355                                   default:
1356                                         msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d",
1357                                                   peer->MODE);
1358                                         return;
1359                                         break;
1360                               }
1361                               unixtime = mktime (&local);
1362                               if ((gmtp = gmtime (&unixtime)) == NULL)
1363                               {
1364                                         pp->lencode = 0;
1365                                         refclock_report (peer, CEVNT_FAULT);
1366                                         return;
1367                               }
1368                               pp->year = gmtp->tm_year+1900;
1369                               month = gmtp->tm_mon+1;
1370                               pp->day = ymd2yd(pp->year,month,gmtp->tm_mday);
1371                               /* pp->day = gmtp->tm_yday; */
1372                               pp->hour = gmtp->tm_hour;
1373                               pp->minute = gmtp->tm_min;
1374                               pp->second = gmtp->tm_sec;
1375 #ifdef DEBUG
1376                               if (debug)
1377                               {
1378                                         printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
1379                                                   pp->year,month,gmtp->tm_mday,pp->hour,pp->minute,
1380                                                   pp->second);
1381                               }
1382 #endif
1383                     } else
1384                     {
1385                               /*
1386                               * For more rational sites distributing UTC
1387                               */
1388                               pp->day    = ymd2yd(pp->year,month,pp->day);
1389                     }
1390           }
1391 
1392           if (peer->MODE == 0) { /* compatiblity to original version */
1393                                         /* If clock signal quality is
1394                                          * unknown, revert to default PRECISION...*/
1395                     if(up->quality == QUALITY_UNKNOWN) {
1396                               peer->precision = PRECISION;
1397                     } else { /* ...else improve precision if flag3 is set... */
1398                               peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1399                                                      HIGHPRECISION : PRECISION);
1400                     }
1401           } else {
1402                     if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) {
1403                               peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1404                                                      HIGHPRECISION : PRECISION);
1405                     } else if (up->quality == QUALITY_UNKNOWN) {
1406                               peer->precision = PRECISION;
1407                     } else {
1408                               peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1409                                                      HIGHPRECISION : PRECISION);
1410                     }
1411           }
1412 
1413           /* Notice and log any change (eg from initial defaults) for flags. */
1414           if(up->saved_flags != pp->sloppyclockflag) {
1415 #ifdef DEBUG
1416                     msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
1417                               ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
1418                               ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
1419                               ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."),
1420                               ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
1421                     /* Note effects of flags changing... */
1422                     if(debug) {
1423                               printf("arc: PRECISION = %d.\n", peer->precision);
1424                     }
1425 #endif
1426                     up->saved_flags = pp->sloppyclockflag;
1427           }
1428 
1429           /* Note time of last believable timestamp. */
1430           pp->lastrec = up->lastrec;
1431 
1432 #ifdef ARCRON_LEAPSECOND_KEEN
1433           /* Find out if a leap-second might just have happened...
1434              (ie is this the first hour of the first day of Jan or Jul?)
1435           */
1436           if((pp->hour == 0) &&
1437              (pp->day == 1) &&
1438              ((month == 1) || (month == 7))) {
1439                     if(possible_leap >= 0) {
1440                               /* A leap may have happened, and no resync has started yet...*/
1441                               possible_leap = 1;
1442                     }
1443           } else {
1444                     /* Definitely not leap-second territory... */
1445                     possible_leap = 0;
1446           }
1447 #endif
1448 
1449           if (!refclock_process(pp)) {
1450                     pp->lencode = 0;
1451                     refclock_report(peer, CEVNT_BADTIME);
1452                     return;
1453           }
1454           record_clock_stats(&peer->srcadr, pp->a_lastcode);
1455           refclock_receive(peer);
1456 }
1457 
1458 
1459 /* request_time() sends a time request to the clock with given peer. */
1460 /* This automatically reports a fault if necessary. */
1461 /* No data should be sent after this until arc_poll() returns. */
1462 static  void    request_time    (int, struct peer *);
1463 static void
request_time(int unit,struct peer * peer)1464 request_time(
1465           int unit,
1466           struct peer *peer
1467           )
1468 {
1469           struct refclockproc *pp = peer->procptr;
1470           register struct arcunit *up = pp->unitptr;
1471 #ifdef DEBUG
1472           if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
1473 #endif
1474           if (!send_slow(up, pp->io.fd, "o\r")) {
1475 #ifdef DEBUG
1476                     if (debug) {
1477                               printf("arc: unit %d: problem sending", unit);
1478                     }
1479 #endif
1480                     pp->lencode = 0;
1481                     refclock_report(peer, CEVNT_FAULT);
1482                     return;
1483           }
1484           pp->polls++;
1485 }
1486 
1487 /*
1488  * arc_poll - called by the transmit procedure
1489  */
1490 static void
arc_poll(int unit,struct peer * peer)1491 arc_poll(
1492           int unit,
1493           struct peer *peer
1494           )
1495 {
1496           register struct arcunit *up;
1497           struct refclockproc *pp;
1498           int resync_needed;              /* Should we start a resync? */
1499 
1500           pp = peer->procptr;
1501           up = pp->unitptr;
1502 #if 0
1503           pp->lencode = 0;
1504           memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
1505 #endif
1506 
1507 #if 0
1508           /* Flush input. */
1509           tcflush(pp->io.fd, TCIFLUSH);
1510 #endif
1511 
1512           /* Resync if our next scheduled resync time is here or has passed. */
1513           resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) &&
1514                                 (up->next_resync <= current_time) );
1515 
1516 #ifdef ARCRON_LEAPSECOND_KEEN
1517           /*
1518             Try to catch a potential leap-second insertion or deletion quickly.
1519 
1520             In addition to the normal NTP fun of clocks that don't report
1521             leap-seconds spooking their hosts, this clock does not even
1522             sample the radio sugnal the whole time, so may miss a
1523             leap-second insertion or deletion for up to a whole sample
1524             time.
1525 
1526             To try to minimise this effect, if in the first few minutes of
1527             the day immediately following a leap-second-insertion point
1528             (ie in the first hour of the first day of the first and sixth
1529             months), and if the last resync was in the previous day, and a
1530             resync is not already in progress, resync the clock
1531             immediately.
1532 
1533           */
1534           if((possible_leap > 0) &&       /* Must be 00:XX 01/0{1,7}/XXXX. */
1535              (!up->resyncing)) {          /* No resync in progress yet. */
1536                     resync_needed = 1;
1537                     possible_leap = -1;          /* Prevent multiple resyncs. */
1538                     msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit);
1539           }
1540 #endif
1541 
1542           /* Do a resync if required... */
1543           if(resync_needed) {
1544                     /* First, reset quality value to `unknown' so we can detect */
1545                     /* when a quality message has been responded to by this     */
1546                     /* being set to some other value.                           */
1547                     up->quality = QUALITY_UNKNOWN;
1548 
1549                     /* Note that we are resyncing... */
1550                     up->resyncing = 1;
1551 
1552                     /* Now actually send the resync command and an immediate poll. */
1553 #ifdef DEBUG
1554                     if(debug) { printf("arc: sending resync command (h\\r).\n"); }
1555 #endif
1556                     msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit);
1557                     send_slow(up, pp->io.fd, "h\r");
1558 
1559                     /* Schedule our next resync... */
1560                     up->next_resync = current_time + DEFAULT_RESYNC_TIME;
1561 
1562                     /* Drop through to request time if appropriate. */
1563           }
1564 
1565           /* If clock quality is too poor to trust, indicate a fault. */
1566           /* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/
1567           /* we'll cross our fingers and just hope that the thing     */
1568           /* synced so quickly we did not catch it---we'll            */
1569           /* double-check the clock is OK elsewhere.                  */
1570           if(
1571 #ifdef ARCRON_KEEN
1572                     (up->quality != QUALITY_UNKNOWN) &&
1573 #else
1574                     (up->quality == QUALITY_UNKNOWN) ||
1575 #endif
1576                     (up->quality < MIN_CLOCK_QUALITY_OK)) {
1577 #ifdef DEBUG
1578                     if(debug) {
1579                               printf("arc: clock quality %d too poor.\n", up->quality);
1580                     }
1581 #endif
1582                     pp->lencode = 0;
1583                     refclock_report(peer, CEVNT_FAULT);
1584                     return;
1585           }
1586           /* This is the normal case: request a timestamp. */
1587           request_time(unit, peer);
1588 }
1589 
1590 #else
1591 NONEMPTY_TRANSLATION_UNIT
1592 #endif
1593