xref: /freebsd/contrib/ntp/ntpd/refclock_tsyncpci.c (revision 8aac90f18aef7c9eea906c3ff9a001ca7b94f375)
1 /*******************************************************************************
2 *
3 *  Module  : refclock_tsyncpci.c
4 *  Date    : 09/08/08
5 *  Purpose : Implements a reference clock driver for the NTP daemon.  This
6 *            reference clock driver provides a means to communicate with
7 *            the Spectracom TSYNC PCI timing devices and use them as a time
8 *            source.
9 *
10 *  (C) Copyright 2008 Spectracom Corporation
11 *
12 *  This software is provided by Spectracom Corporation 'as is' and
13 *  any express or implied warranties, including, but not limited to, the
14 *  implied warranties of merchantability and fitness for a particular purpose
15 *  are disclaimed.  In no event shall Spectracom Corporation be liable
16 *  for any direct, indirect, incidental, special, exemplary, or consequential
17 *  damages (including, but not limited to, procurement of substitute goods
18 *  or services; loss of use, data, or profits; or business interruption)
19 *  however caused and on any theory of liability, whether in contract, strict
20 *  liability, or tort (including negligence or otherwise) arising in any way
21 *  out of the use of this software, even if advised of the possibility of
22 *  such damage.
23 *
24 *  This software is released for distribution according to the NTP copyright
25 *  and license contained in html/copyright.html of NTP source.
26 *
27 *******************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 
32 #if defined(REFCLOCK) && defined(CLOCK_TSYNCPCI)
33 
34 #include <asm/ioctl.h>
35 #ifdef HAVE_SYS_IOCTL_H
36 # include <sys/ioctl.h>
37 #endif
38 
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <netinet/in.h>
42 
43 
44 #include "ntpd.h"
45 #include "ntp_io.h"
46 #include "ntp_refclock.h"
47 #include "ntp_unixtime.h"
48 #include "ntp_stdlib.h"
49 #include "ntp_calendar.h"
50 
51 
52 /*******************************************************************************
53 **
54 ** This driver supports the Spectracom TSYNC PCI GPS receiver.  It requires
55 ** that the tsyncpci.o device driver be installed and loaded.
56 **
57 *******************************************************************************/
58 
59 #define TSYNC_PCI_REVISION "1.11"
60 
61 /*
62 ** TPRO interface definitions
63 */
64 #define DEVICE      "/dev/tsyncpci"             /* device name */
65 #define PRECISION   (-20)                       /* precision assumed (1 us) */
66 #define DESCRIPTION "Spectracom TSYNC-PCI"      /* WRU */
67 
68 #define SECONDS_1900_TO_1970 (2208988800U)
69 
70 #define TSYNC_REF_IID               (0x2500)    // SS CAI, REF IID
71 #define TSYNC_REF_DEST_ID           (0x0001)    // KTS Firmware
72 #define TSYNC_REF_IN_PYLD_OFF       (0)
73 #define TSYNC_REF_IN_LEN            (0)
74 #define TSYNC_REF_OUT_PYLD_OFF      (0)
75 #define TSYNC_REF_OUT_LEN           (8)
76 #define TSYNC_REF_MAX_OUT_LEN       (16)
77 #define TSYNC_REF_PYLD_LEN          (TSYNC_REF_IN_LEN +                     \
78                                      TSYNC_REF_MAX_OUT_LEN)
79 #define TSYNC_REF_LEN               (4)
80 #define TSYNC_REF_LOCAL             ("LOCL")
81 
82 #define TSYNC_TMSCL_IID              (0x2301)    // CS CAI, TIMESCALE IID
83 #define TSYNC_TMSCL_DEST_ID          (0x0001)    // KTS Firmware
84 #define TSYNC_TMSCL_IN_PYLD_OFF      (0)
85 #define TSYNC_TMSCL_IN_LEN           (0)
86 #define TSYNC_TMSCL_OUT_PYLD_OFF     (0)
87 #define TSYNC_TMSCL_OUT_LEN          (4)
88 #define TSYNC_TMSCL_MAX_OUT_LEN      (12)
89 #define TSYNC_TMSCL_PYLD_LEN         (TSYNC_TMSCL_IN_LEN +                    \
90                                      TSYNC_TMSCL_MAX_OUT_LEN)
91 
92 #define TSYNC_LEAP_IID              (0x2307)    // CS CAI, LEAP SEC IID
93 #define TSYNC_LEAP_DEST_ID          (0x0001)    // KTS Firmware
94 #define TSYNC_LEAP_IN_PYLD_OFF      (0)
95 #define TSYNC_LEAP_IN_LEN           (0)
96 #define TSYNC_LEAP_OUT_PYLD_OFF     (0)
97 #define TSYNC_LEAP_OUT_LEN          (28)
98 #define TSYNC_LEAP_MAX_OUT_LEN      (36)
99 #define TSYNC_LEAP_PYLD_LEN         (TSYNC_LEAP_IN_LEN +                    \
100                                      TSYNC_LEAP_MAX_OUT_LEN)
101 
102 // These define the base date/time of the system clock.  The system time will
103 // be tracked as the number of seconds from this date/time.
104 #define TSYNC_TIME_BASE_YEAR        (1970) // earliest acceptable year
105 
106 #define TSYNC_LCL_STRATUM           (0)
107 
108 /*
109 ** TSYNC Time Scales type
110 */
111 typedef enum
112 {
113     TIME_SCALE_UTC    = 0,   // Universal Coordinated Time
114     TIME_SCALE_TAI    = 1,   // International Atomic Time
115     TIME_SCALE_GPS    = 2,   // Global Positioning System
116     TIME_SCALE_LOCAL  = 3,   // UTC w/local rules for time zone and DST
117     NUM_TIME_SCALES   = 4,   // Number of time scales
118 
119     TIME_SCALE_MAX    = 15   // Maximum number of timescales
120 
121 } TIME_SCALE;
122 
123 /*
124 ** TSYNC Board Object
125 */
126 typedef struct BoardObj {
127 
128   int            file_descriptor;
129   unsigned short devid;
130   unsigned short options;
131   unsigned char  firmware[5];
132   unsigned char  FPGA[5];
133   unsigned char  driver[7];
134 
135 } BoardObj;
136 
137 /*
138 ** TSYNC Time Object
139 */
140 typedef struct TimeObj {
141 
142   unsigned char  syncOption;  /* -M option */
143   unsigned int   secsDouble;  /* seconds floating pt */
144   unsigned char  seconds;     /* seconds whole num */
145   unsigned char  minutes;
146   unsigned char  hours;
147   unsigned short days;
148   unsigned short year;
149   unsigned short flags;      /* bit 2 SYNC, bit 1 TCODE; all others 0 */
150 
151 } TimeObj;
152 
153 /*
154 ** NTP Time Object
155 */
156 typedef struct NtpTimeObj {
157 
158     TimeObj        timeObj;
159     struct timeval tv;
160     unsigned int   refId;
161 
162 } NtpTimeObj;
163 /*
164 ** TSYNC Supervisor Reference Object
165 */
166 typedef struct ReferenceObj {
167 
168     char time[TSYNC_REF_LEN];
169     char pps[TSYNC_REF_LEN];
170 
171 } ReferenceObj;
172 
173 /*
174 ** TSYNC Seconds Time Object
175 */
176 typedef struct SecTimeObj
177 {
178     unsigned int seconds;
179     unsigned int ns;
180 }
181 SecTimeObj;
182 
183 /*
184 ** TSYNC DOY Time Object
185 */
186 typedef struct DoyTimeObj
187 {
188     unsigned int year;
189     unsigned int doy;
190     unsigned int hour;
191     unsigned int minute;
192     unsigned int second;
193     unsigned int ns;
194 }
195 DoyTimeObj;
196 
197 /*
198 ** TSYNC Leap Second Object
199 */
200 typedef struct LeapSecondObj
201 {
202     int        offset;
203     DoyTimeObj utcDate;
204 }
205 LeapSecondObj;
206 
207 /*
208  * structures for ioctl interactions with driver
209  */
210 #define DI_PAYLOADS_STARTER_LENGTH 4
211 typedef struct ioctl_trans_di {
212 
213     // input parameters
214     uint16_t        dest;
215     uint16_t        iid;
216 
217     uint32_t        inPayloadOffset;
218     uint32_t        inLength;
219     uint32_t        outPayloadOffset;
220     uint32_t        maxOutLength;
221 
222     // output parameters
223     uint32_t        actualOutLength;
224     int32_t         status;
225 
226     // Input and output
227 
228     // The payloads field MUST be last in ioctl_trans_di.
229     uint8_t         payloads[DI_PAYLOADS_STARTER_LENGTH];
230 
231 }ioctl_trans_di;
232 
233 /*
234  * structure for looking up a reference ID from a reference name
235  */
236 typedef struct
237 {
238     const char* pRef;           // KTS Reference Name
239     const char* pRefId;         // NTP Reference ID
240 
241 } RefIdLookup;
242 
243 /*
244  * unit control structure
245  */
246 typedef struct  {
247     uint32_t refPrefer;         // Reference prefer flag
248     uint32_t refId;             // Host peer reference ID
249     uint8_t  refStratum;        // Host peer reference stratum
250 
251 } TsyncUnit;
252 
253 /*
254 **  Function prototypes
255 */
256 static void tsync_poll     (int unit, struct peer *);
257 static void tsync_shutdown (int, struct peer *);
258 static int  tsync_start    (int, struct peer *);
259 
260 /*
261 **  Helper functions
262 */
263 static void ApplyTimeOffset    (DoyTimeObj* pDt, int off);
264 static void SecTimeFromDoyTime (SecTimeObj* pSt, DoyTimeObj* pDt);
265 static void DoyTimeFromSecTime (DoyTimeObj* pDt, SecTimeObj* pSt);
266 
267 /*
268 **  Transfer vector
269 */
270 struct refclock refclock_tsyncpci = {
271     tsync_start,    /* start up driver */
272     tsync_shutdown, /* shut down driver */
273     tsync_poll,     /* transmit poll message */
274     noentry,        /* not used (old tsync_control) */
275     noentry,        /* initialize driver (not used) */
276     noentry,        /* not used (old tsync_buginfo) */
277     NOFLAGS         /* not used */
278 };
279 
280 /*
281  * Reference ID lookup table
282  */
283 static RefIdLookup RefIdLookupTbl[] =
284 {
285     {"gps",  "GPS"},
286     {"ir",   "IRIG"},
287     {"hvq",  "HVQ"},
288     {"frq",  "FREQ"},
289     {"mdm",  "ACTS"},
290     {"epp",  "PPS"},
291     {"ptp",  "PTP"},
292     {"asc",  "ATC"},
293     {"hst0", "USER"},
294     {"hst",  TSYNC_REF_LOCAL},
295     {"self", TSYNC_REF_LOCAL},
296     {NULL,   NULL}
297 };
298 
299 /*******************************************************************************
300 **          IOCTL DEFINITIONS
301 *******************************************************************************/
302 #define IOCTL_TPRO_ID            't'
303 #define IOCTL_TPRO_OPEN          _IOWR(IOCTL_TPRO_ID, 0,  BoardObj)
304 #define IOCTL_TPRO_GET_NTP_TIME  _IOWR(IOCTL_TPRO_ID, 25, NtpTimeObj)
305 #define IOCTL_TSYNC_GET          _IOWR(IOCTL_TPRO_ID, 26, ioctl_trans_di)
306 
307 /******************************************************************************
308  *
309  * Function:    tsync_start()
310  * Description: Used to intialize the Spectracom TSYNC reference driver.
311  *
312  * Parameters:
313  *     IN:  unit - not used.
314  *         *peer - pointer to this reference clock's peer structure
315  *     Returns: 0 - unsuccessful
316  *              1 - successful
317  *
318 *******************************************************************************/
319 static int tsync_start(int unit, struct peer *peer)
320 {
321     struct refclockproc *pp;
322     TsyncUnit           *up;
323 
324 
325     /*
326     **  initialize reference clock and peer parameters
327     */
328     pp                = peer->procptr;
329     pp->clockdesc     = DESCRIPTION;
330     pp->io.clock_recv = noentry;
331     pp->io.srcclock   = peer;
332     pp->io.datalen    = 0;
333     peer->precision   = PRECISION;
334 
335     // Allocate and initialize unit structure
336     if (!(up = (TsyncUnit*)emalloc(sizeof(TsyncUnit))))
337     {
338         return (0);
339     }
340 
341     // Store reference preference
342     up->refPrefer = peer->flags & FLAG_PREFER;
343 
344     // Initialize reference stratum level and ID
345     up->refStratum = STRATUM_UNSPEC;
346     strncpy((char *)&up->refId, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
347 
348     // Attach unit structure
349     pp->unitptr = (caddr_t)up;
350 
351     /* Declare our refId as local in the beginning because we do not know
352      * what our actual refid is yet.
353      */
354     strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
355 
356     return (1);
357 
358 } /* End - tsync_start() */
359 
360 /*******************************************************************************
361 **
362 ** Function:    tsync_shutdown()
363 ** Description: Handles anything related to shutting down the reference clock
364 **              driver. Nothing at this point in time.
365 **
366 ** Parameters:
367 **     IN:  unit - not used.
368 **         *peer - pointer to this reference clock's peer structure
369 **     Returns: none.
370 **
371 *******************************************************************************/
372 static void tsync_shutdown(int unit, struct peer *peer)
373 {
374 
375 } /* End - tsync_shutdown() */
376 
377 /******************************************************************************
378  *
379  * Function:    tsync_poll()
380  * Description: Retrieve time from the TSYNC device.
381  *
382  * Parameters:
383  *     IN:  unit - not used.
384  *         *peer - pointer to this reference clock's peer structure
385  *     Returns: none.
386  *
387 *******************************************************************************/
388 static void tsync_poll(int unit, struct peer *peer)
389 {
390     char                 device[32];
391     struct refclockproc *pp;
392     struct calendar      jt;
393     TsyncUnit           *up;
394     unsigned char        synch;
395     double               seconds;
396     int                  err;
397     int                  err1;
398     int                  err2;
399     int                  err3;
400     int                  i;
401     int                  j;
402     unsigned int         itAllocationLength;
403     unsigned int         itAllocationLength1;
404     unsigned int         itAllocationLength2;
405     NtpTimeObj           TimeContext;
406     BoardObj             hBoard;
407     char                 timeRef[TSYNC_REF_LEN + 1];
408     char                 ppsRef [TSYNC_REF_LEN + 1];
409     TIME_SCALE           tmscl = TIME_SCALE_UTC;
410     LeapSecondObj        leapSec;
411     ioctl_trans_di      *it;
412     ioctl_trans_di      *it1;
413     ioctl_trans_di      *it2;
414     l_fp                 offset;
415     l_fp                 ltemp;
416     ReferenceObj *	 pRefObj;
417 
418 
419     /* Construct the device name */
420     sprintf(device, "%s%d", DEVICE, (int)peer->refclkunit);
421 
422     printf("Polling device number %d...\n", (int)peer->refclkunit);
423 
424     /* Open the TSYNC device */
425     hBoard.file_descriptor = open(device, O_RDONLY | O_NDELAY, 0777);
426 
427     /* If error opening TSYNC device... */
428     if (hBoard.file_descriptor < 0)
429     {
430         msyslog(LOG_ERR, "Couldn't open device");
431         return;
432     }
433 
434     /* If error while initializing the board... */
435     if (ioctl(hBoard.file_descriptor, IOCTL_TPRO_OPEN, &hBoard) < 0)
436     {
437         msyslog(LOG_ERR, "Couldn't initialize device");
438         close(hBoard.file_descriptor);
439         return;
440     }
441 
442     /* Allocate memory for ioctl message */
443     itAllocationLength =
444         (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
445         TSYNC_REF_IN_LEN + TSYNC_REF_MAX_OUT_LEN;
446 
447     it = (ioctl_trans_di*)alloca(itAllocationLength);
448     if (it == NULL) {
449         msyslog(LOG_ERR, "Couldn't allocate transaction memory - Reference");
450         return;
451     }
452 
453     /* Build SS_GetRef ioctl message */
454     it->dest             = TSYNC_REF_DEST_ID;
455     it->iid              = TSYNC_REF_IID;
456     it->inPayloadOffset  = TSYNC_REF_IN_PYLD_OFF;
457     it->inLength         = TSYNC_REF_IN_LEN;
458     it->outPayloadOffset = TSYNC_REF_OUT_PYLD_OFF;
459     it->maxOutLength     = TSYNC_REF_MAX_OUT_LEN;
460     it->actualOutLength  = 0;
461     it->status           = 0;
462     memset(it->payloads, 0, TSYNC_REF_MAX_OUT_LEN);
463 
464     /* Read the reference from the TSYNC-PCI device */
465     err = ioctl(hBoard.file_descriptor,
466                  IOCTL_TSYNC_GET,
467                 (char *)it);
468 
469     /* Allocate memory for ioctl message */
470     itAllocationLength1 =
471         (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
472         TSYNC_TMSCL_IN_LEN + TSYNC_TMSCL_MAX_OUT_LEN;
473 
474     it1 = (ioctl_trans_di*)alloca(itAllocationLength1);
475     if (it1 == NULL) {
476         msyslog(LOG_ERR, "Couldn't allocate transaction memory - Time Scale");
477         return;
478     }
479 
480     /* Build CS_GetTimeScale ioctl message */
481     it1->dest             = TSYNC_TMSCL_DEST_ID;
482     it1->iid              = TSYNC_TMSCL_IID;
483     it1->inPayloadOffset  = TSYNC_TMSCL_IN_PYLD_OFF;
484     it1->inLength         = TSYNC_TMSCL_IN_LEN;
485     it1->outPayloadOffset = TSYNC_TMSCL_OUT_PYLD_OFF;
486     it1->maxOutLength     = TSYNC_TMSCL_MAX_OUT_LEN;
487     it1->actualOutLength  = 0;
488     it1->status           = 0;
489     memset(it1->payloads, 0, TSYNC_TMSCL_MAX_OUT_LEN);
490 
491     /* Read the Time Scale info from the TSYNC-PCI device */
492     err1 = ioctl(hBoard.file_descriptor,
493                  IOCTL_TSYNC_GET,
494                  (char *)it1);
495 
496     /* Allocate memory for ioctl message */
497     itAllocationLength2 =
498         (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
499         TSYNC_LEAP_IN_LEN + TSYNC_LEAP_MAX_OUT_LEN;
500 
501     it2 = (ioctl_trans_di*)alloca(itAllocationLength2);
502     if (it2 == NULL) {
503         msyslog(LOG_ERR, "Couldn't allocate transaction memory - Leap Second");
504         return;
505     }
506 
507     /* Build CS_GetLeapSec ioctl message */
508     it2->dest             = TSYNC_LEAP_DEST_ID;
509     it2->iid              = TSYNC_LEAP_IID;
510     it2->inPayloadOffset  = TSYNC_LEAP_IN_PYLD_OFF;
511     it2->inLength         = TSYNC_LEAP_IN_LEN;
512     it2->outPayloadOffset = TSYNC_LEAP_OUT_PYLD_OFF;
513     it2->maxOutLength     = TSYNC_LEAP_MAX_OUT_LEN;
514     it2->actualOutLength  = 0;
515     it2->status           = 0;
516     memset(it2->payloads, 0, TSYNC_LEAP_MAX_OUT_LEN);
517 
518     /* Read the leap seconds info from the TSYNC-PCI device */
519     err2 = ioctl(hBoard.file_descriptor,
520                  IOCTL_TSYNC_GET,
521                  (char *)it2);
522 
523     pp = peer->procptr;
524     up = (TsyncUnit*)pp->unitptr;
525 
526     /* Read the time from the TSYNC-PCI device */
527     err3 = ioctl(hBoard.file_descriptor,
528                  IOCTL_TPRO_GET_NTP_TIME,
529                  (char *)&TimeContext);
530 
531     /* Close the TSYNC device */
532     close(hBoard.file_descriptor);
533 
534     // Check for errors
535     if ((err < 0) ||(err1 < 0) || (err2 < 0) || (err3 < 0) ||
536         (it->status != 0) || (it1->status != 0) || (it2->status != 0) ||
537         (it->actualOutLength  != TSYNC_REF_OUT_LEN) ||
538         (it1->actualOutLength != TSYNC_TMSCL_OUT_LEN) ||
539         (it2->actualOutLength != TSYNC_LEAP_OUT_LEN)) {
540         refclock_report(peer, CEVNT_FAULT);
541         return;
542     }
543 
544     // Extract reference identifiers from ioctl payload
545     memset(timeRef, '\0', sizeof(timeRef));
546     memset(ppsRef, '\0', sizeof(ppsRef));
547     pRefObj = (void *)it->payloads;
548     memcpy(timeRef, pRefObj->time, TSYNC_REF_LEN);
549     memcpy(ppsRef, pRefObj->pps, TSYNC_REF_LEN);
550 
551     // Extract the Clock Service Time Scale and convert to correct byte order
552     memcpy(&tmscl, it1->payloads, sizeof(tmscl));
553     tmscl = ntohl(tmscl);
554 
555     // Extract leap second info from ioctl payload and perform byte swapping
556     for (i = 0; i < (sizeof(leapSec) / 4); i++)
557     {
558         for (j = 0; j < 4; j++)
559         {
560             ((unsigned char*)&leapSec)[(i * 4) + j] =
561                     ((unsigned char*)(it2->payloads))[(i * 4) + (3 - j)];
562         }
563     }
564 
565     // Determine time reference ID from reference name
566     for (i = 0; RefIdLookupTbl[i].pRef != NULL; i++)
567     {
568        // Search RefID table
569        if (strstr(timeRef, RefIdLookupTbl[i].pRef) != NULL)
570        {
571           // Found the matching string
572           break;
573        }
574     }
575 
576     // Determine pps reference ID from reference name
577     for (j = 0; RefIdLookupTbl[j].pRef != NULL; j++)
578     {
579        // Search RefID table
580        if (strstr(ppsRef, RefIdLookupTbl[j].pRef) != NULL)
581        {
582           // Found the matching string
583           break;
584        }
585     }
586 
587     // Determine synchronization state from flags
588     synch = (TimeContext.timeObj.flags == 0x4) ? 1 : 0;
589 
590     // Pull seconds information from time object
591     seconds = (double) (TimeContext.timeObj.secsDouble);
592     seconds /= (double) 1000000.0;
593 
594     /*
595     ** Convert the number of microseconds to double and then place in the
596     ** peer's last received long floating point format.
597     */
598     DTOLFP(((double)TimeContext.tv.tv_usec / 1000000.0), &pp->lastrec);
599 
600     /*
601     ** The specTimeStamp is the number of seconds since 1/1/1970, while the
602     ** peer's lastrec time should be compatible with NTP which is seconds since
603     ** 1/1/1900.  So Add the number of seconds between 1900 and 1970 to the
604     ** specTimeStamp and place in the peer's lastrec long floating point struct.
605     */
606     pp->lastrec.Ul_i.Xl_ui += (unsigned int)TimeContext.tv.tv_sec +
607                                             SECONDS_1900_TO_1970;
608 
609     pp->polls++;
610 
611     /*
612     **  set the reference clock object
613     */
614     sprintf(pp->a_lastcode, "%03d %02d:%02d:%02.6f",
615             TimeContext.timeObj.days, TimeContext.timeObj.hours,
616             TimeContext.timeObj.minutes, seconds);
617 
618     pp->lencode = strlen (pp->a_lastcode);
619     pp->day     = TimeContext.timeObj.days;
620     pp->hour    = TimeContext.timeObj.hours;
621     pp->minute  = TimeContext.timeObj.minutes;
622     pp->second  = (int) seconds;
623     seconds     = (seconds - (double) (pp->second / 1.0)) * 1000000000;
624     pp->nsec    = (long) seconds;
625 
626     /*
627     **  calculate year start
628     */
629     jt.year       = TimeContext.timeObj.year;
630     jt.yearday    = 1;
631     jt.monthday   = 1;
632     jt.month      = 1;
633     jt.hour       = 0;
634     jt.minute     = 0;
635     jt.second     = 0;
636     pp->yearstart = caltontp(&jt);
637 
638     // Calculate and report reference clock offset
639     offset.l_ui = (long)(((pp->day - 1) * 24) + pp->hour + GMT);
640     offset.l_ui = (offset.l_ui * 60) + (long)pp->minute;
641     offset.l_ui = (offset.l_ui * 60) + (long)pp->second;
642     offset.l_ui = offset.l_ui + (long)pp->yearstart;
643     offset.l_uf = 0;
644     DTOLFP(pp->nsec / 1e9, &ltemp);
645     L_ADD(&offset, &ltemp);
646     refclock_process_offset(pp, offset, pp->lastrec,
647                             pp->fudgetime1);
648 
649     // KTS in sync
650     if (synch) {
651         // Subtract leap second info by one second to determine effective day
652         ApplyTimeOffset(&(leapSec.utcDate), -1);
653 
654         // If there is a leap second today and the KTS is using a time scale
655         // which handles leap seconds then
656         if ((tmscl != TIME_SCALE_GPS) && (tmscl != TIME_SCALE_TAI) &&
657             (leapSec.utcDate.year == (unsigned int)TimeContext.timeObj.year) &&
658             (leapSec.utcDate.doy  == (unsigned int)TimeContext.timeObj.days))
659         {
660             // If adding a second
661             if (leapSec.offset == 1)
662             {
663                 pp->leap = LEAP_ADDSECOND;
664             }
665             // Else if removing a second
666             else if (leapSec.offset == -1)
667             {
668                 pp->leap = LEAP_DELSECOND;
669             }
670             // Else report no leap second pending (no handling of offsets
671             // other than +1 or -1)
672             else
673             {
674                 pp->leap = LEAP_NOWARNING;
675             }
676         }
677         // Else report no leap second pending
678         else
679         {
680             pp->leap = LEAP_NOWARNING;
681         }
682 
683         peer->leap = pp->leap;
684         refclock_report(peer, CEVNT_NOMINAL);
685 
686         // If reference name reported, then not in holdover
687         if ((RefIdLookupTbl[i].pRef != NULL) &&
688             (RefIdLookupTbl[j].pRef != NULL))
689         {
690             // Determine if KTS being synchronized by host (identified as
691             // "LOCL")
692             if ((strcmp(RefIdLookupTbl[i].pRefId, TSYNC_REF_LOCAL) == 0) ||
693                 (strcmp(RefIdLookupTbl[j].pRefId, TSYNC_REF_LOCAL) == 0))
694             {
695                 // Clear prefer flag
696                 peer->flags &= ~FLAG_PREFER;
697 
698                 // Set reference clock stratum level as unusable
699                 pp->stratum   = STRATUM_UNSPEC;
700                 peer->stratum = pp->stratum;
701 
702                 // If a valid peer is available
703                 if ((sys_peer != NULL) && (sys_peer != peer))
704                 {
705                     // Store reference peer stratum level and ID
706                     up->refStratum = sys_peer->stratum;
707                     up->refId      = addr2refid(&sys_peer->srcadr);
708                 }
709             }
710             else
711             {
712                 // Restore prefer flag
713                 peer->flags |= up->refPrefer;
714 
715                 // Store reference stratum as local clock
716                 up->refStratum = TSYNC_LCL_STRATUM;
717                 strncpy((char *)&up->refId, RefIdLookupTbl[j].pRefId,
718                     TSYNC_REF_LEN);
719 
720                 // Set reference clock stratum level as local clock
721                 pp->stratum   = TSYNC_LCL_STRATUM;
722                 peer->stratum = pp->stratum;
723             }
724 
725             // Update reference name
726             strncpy((char *)&pp->refid, RefIdLookupTbl[j].pRefId,
727                 TSYNC_REF_LEN);
728             peer->refid = pp->refid;
729         }
730         // Else in holdover
731         else
732         {
733             // Restore prefer flag
734             peer->flags |= up->refPrefer;
735 
736             // Update reference ID to saved ID
737             pp->refid   = up->refId;
738             peer->refid = pp->refid;
739 
740             // Update stratum level to saved stratum level
741             pp->stratum   = up->refStratum;
742             peer->stratum = pp->stratum;
743         }
744     }
745     // Else KTS not in sync
746     else {
747         // Place local identifier in peer RefID
748         strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
749         peer->refid = pp->refid;
750 
751         // Report not in sync
752         pp->leap   = LEAP_NOTINSYNC;
753         peer->leap = pp->leap;
754     }
755 
756     if (pp->coderecv == pp->codeproc) {
757         refclock_report(peer, CEVNT_TIMEOUT);
758         return;
759     }
760 
761     record_clock_stats(&peer->srcadr, pp->a_lastcode);
762     refclock_receive(peer);
763 
764     /* Increment the number of times the reference has been polled */
765     pp->polls++;
766 
767 } /* End - tsync_poll() */
768 
769 
770 ////////////////////////////////////////////////////////////////////////////////
771 // Function:    ApplyTimeOffset
772 // Description: The ApplyTimeOffset function adds an offset (in seconds) to a
773 //              specified date and time.  The specified date and time is passed
774 //              back after being modified.
775 //
776 // Assumptions: 1. Every fourth year is a leap year.  Therefore, this function
777 //                 is only accurate through Feb 28, 2100.
778 ////////////////////////////////////////////////////////////////////////////////
779 void ApplyTimeOffset(DoyTimeObj* pDt, int off)
780 {
781     SecTimeObj st;                  // Time, in seconds
782 
783 
784     // Convert date and time to seconds
785     SecTimeFromDoyTime(&st, pDt);
786 
787     // Apply offset
788     st.seconds = (int)((signed long long)st.seconds + (signed long long)off);
789 
790     // Convert seconds to date and time
791     DoyTimeFromSecTime(pDt, &st);
792 
793 } // End ApplyTimeOffset
794 
795 
796 ////////////////////////////////////////////////////////////////////////////////
797 // Function:    SecTimeFromDoyTime
798 // Description: The SecTimeFromDoyTime function converts a specified date
799 //              and time into a count of seconds since the base time.  This
800 //              function operates across the range Base Time to Max Time for
801 //              the system.
802 //
803 // Assumptions: 1. A leap year is any year evenly divisible by 4.  Therefore,
804 //                 this function is only accurate through Feb 28, 2100.
805 //              2. Conversion does not account for leap seconds.
806 ////////////////////////////////////////////////////////////////////////////////
807 void SecTimeFromDoyTime(SecTimeObj* pSt, DoyTimeObj* pDt)
808 {
809     unsigned int yrs;               // Years
810     unsigned int lyrs;              // Leap years
811 
812 
813     // Start with accumulated time of 0
814     pSt->seconds  = 0;
815 
816     // Calculate the number of years and leap years
817     yrs           = pDt->year - TSYNC_TIME_BASE_YEAR;
818     lyrs          = (yrs + 1) / 4;
819 
820     // Convert leap years and years
821     pSt->seconds += lyrs           * SECSPERLEAPYEAR;
822     pSt->seconds += (yrs - lyrs)   * SECSPERYEAR;
823 
824     // Convert days, hours, minutes and seconds
825     pSt->seconds += (pDt->doy - 1) * SECSPERDAY;
826     pSt->seconds += pDt->hour      * SECSPERHR;
827     pSt->seconds += pDt->minute    * SECSPERMIN;
828     pSt->seconds += pDt->second;
829 
830     // Copy the subseconds count
831     pSt->ns       = pDt->ns;
832 
833 } // End SecTimeFromDoyTime
834 
835 
836 ////////////////////////////////////////////////////////////////////////////////
837 // Function:    DoyTimeFromSecTime
838 // Description: The DoyTimeFromSecTime function converts a specified count
839 //              of seconds since the start of our base time into a SecTimeObj
840 //              structure.
841 //
842 // Assumptions: 1. A leap year is any year evenly divisible by 4.  Therefore,
843 //                 this function is only accurate through Feb 28, 2100.
844 //              2. Conversion does not account for leap seconds.
845 ////////////////////////////////////////////////////////////////////////////////
846 void DoyTimeFromSecTime(DoyTimeObj* pDt, SecTimeObj* pSt)
847 {
848     signed long long secs;          // Seconds accumulator variable
849     unsigned int     yrs;           // Years accumulator variable
850     unsigned int     doys;          // Days accumulator variable
851     unsigned int     hrs;           // Hours accumulator variable
852     unsigned int     mins;          // Minutes accumulator variable
853 
854 
855     // Convert the seconds count into a signed 64-bit number for calculations
856     secs  = (signed long long)(pSt->seconds);
857 
858     // Calculate the number of 4 year chunks
859     yrs   = (unsigned int)((secs /
860                            ((SECSPERYEAR * 3) + SECSPERLEAPYEAR)) * 4);
861     secs %= ((SECSPERYEAR * 3) + SECSPERLEAPYEAR);
862 
863     // If there is at least a normal year worth of time left
864     if (secs >= SECSPERYEAR)
865     {
866         // Increment the number of years and subtract a normal year of time
867         yrs++;
868         secs -= SECSPERYEAR;
869     }
870 
871     // If there is still at least a normal year worth of time left
872     if (secs >= SECSPERYEAR)
873     {
874         // Increment the number of years and subtract a normal year of time
875         yrs++;
876         secs -= SECSPERYEAR;
877     }
878 
879     // If there is still at least a leap year worth of time left
880     if (secs >= SECSPERLEAPYEAR)
881     {
882         // Increment the number of years and subtract a leap year of time
883         yrs++;
884         secs -= SECSPERLEAPYEAR;
885     }
886 
887     // Calculate the day of year as the number of days left, then add 1
888     // because months start on the 1st.
889     doys  = (unsigned int)((secs / SECSPERDAY) + 1);
890     secs %= SECSPERDAY;
891 
892     // Calculate the hour
893     hrs   = (unsigned int)(secs / SECSPERHR);
894     secs %= SECSPERHR;
895 
896     // Calculate the minute
897     mins  = (unsigned int)(secs / SECSPERMIN);
898     secs %= SECSPERMIN;
899 
900     // Fill in the doytime structure
901     pDt->year   = yrs + TSYNC_TIME_BASE_YEAR;
902     pDt->doy    = doys;
903     pDt->hour   = hrs;
904     pDt->minute = mins;
905     pDt->second = (unsigned int)secs;
906     pDt->ns     = pSt->ns;
907 
908 } // End DoyTimeFromSecTime
909 
910 #else
911 int refclock_tsyncpci_bs;
912 #endif /* REFCLOCK */
913