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