1275970Scy/*******************************************************************************
2275970Scy*
3275970Scy*  Module  : refclock_tsyncpci.c
4275970Scy*  Date    : 09/08/08
5275970Scy*  Purpose : Implements a reference clock driver for the NTP daemon.  This
6275970Scy*            reference clock driver provides a means to communicate with
7275970Scy*            the Spectracom TSYNC PCI timing devices and use them as a time
8275970Scy*            source.
9275970Scy*
10275970Scy*  (C) Copyright 2008 Spectracom Corporation
11275970Scy*
12275970Scy*  This software is provided by Spectracom Corporation 'as is' and
13275970Scy*  any express or implied warranties, including, but not limited to, the
14275970Scy*  implied warranties of merchantability and fitness for a particular purpose
15275970Scy*  are disclaimed.  In no event shall Spectracom Corporation be liable
16275970Scy*  for any direct, indirect, incidental, special, exemplary, or consequential
17275970Scy*  damages (including, but not limited to, procurement of substitute goods
18275970Scy*  or services; loss of use, data, or profits; or business interruption)
19275970Scy*  however caused and on any theory of liability, whether in contract, strict
20275970Scy*  liability, or tort (including negligence or otherwise) arising in any way
21275970Scy*  out of the use of this software, even if advised of the possibility of
22275970Scy*  such damage.
23275970Scy*
24275970Scy*  This software is released for distribution according to the NTP copyright
25275970Scy*  and license contained in html/copyright.html of NTP source.
26275970Scy*
27275970Scy*******************************************************************************/
28275970Scy#ifdef HAVE_CONFIG_H
29275970Scy#include <config.h>
30275970Scy#endif
31275970Scy
32275970Scy#if defined(REFCLOCK) && defined(CLOCK_TSYNCPCI)
33275970Scy
34275970Scy#include <asm/ioctl.h>
35275970Scy#ifdef HAVE_SYS_IOCTL_H
36275970Scy# include <sys/ioctl.h>
37275970Scy#endif
38275970Scy
39275970Scy#include <stdio.h>
40275970Scy#include <ctype.h>
41275970Scy#include <netinet/in.h>
42275970Scy
43275970Scy
44275970Scy#include "ntpd.h"
45275970Scy#include "ntp_io.h"
46275970Scy#include "ntp_refclock.h"
47275970Scy#include "ntp_unixtime.h"
48275970Scy#include "ntp_stdlib.h"
49275970Scy#include "ntp_calendar.h"
50275970Scy
51275970Scy
52275970Scy/*******************************************************************************
53275970Scy**
54275970Scy** This driver supports the Spectracom TSYNC PCI GPS receiver.  It requires
55275970Scy** that the tsyncpci.o device driver be installed and loaded.
56275970Scy**
57275970Scy*******************************************************************************/
58275970Scy
59275970Scy#define TSYNC_PCI_REVISION "1.11"
60275970Scy
61275970Scy/*
62275970Scy** TPRO interface definitions
63275970Scy*/
64275970Scy#define DEVICE      "/dev/tsyncpci"             /* device name */
65275970Scy#define PRECISION   (-20)                       /* precision assumed (1 us) */
66275970Scy#define DESCRIPTION "Spectracom TSYNC-PCI"      /* WRU */
67275970Scy
68275970Scy#define SECONDS_1900_TO_1970 (2208988800U)
69275970Scy
70275970Scy#define TSYNC_REF_IID               (0x2500)    // SS CAI, REF IID
71275970Scy#define TSYNC_REF_DEST_ID           (0x0001)    // KTS Firmware
72275970Scy#define TSYNC_REF_IN_PYLD_OFF       (0)
73275970Scy#define TSYNC_REF_IN_LEN            (0)
74275970Scy#define TSYNC_REF_OUT_PYLD_OFF      (0)
75275970Scy#define TSYNC_REF_OUT_LEN           (8)
76275970Scy#define TSYNC_REF_MAX_OUT_LEN       (16)
77275970Scy#define TSYNC_REF_PYLD_LEN          (TSYNC_REF_IN_LEN +                     \
78275970Scy                                     TSYNC_REF_MAX_OUT_LEN)
79275970Scy#define TSYNC_REF_LEN               (4)
80275970Scy#define TSYNC_REF_LOCAL             ("LOCL")
81275970Scy
82275970Scy#define TSYNC_TMSCL_IID              (0x2301)    // CS CAI, TIMESCALE IID
83275970Scy#define TSYNC_TMSCL_DEST_ID          (0x0001)    // KTS Firmware
84275970Scy#define TSYNC_TMSCL_IN_PYLD_OFF      (0)
85275970Scy#define TSYNC_TMSCL_IN_LEN           (0)
86275970Scy#define TSYNC_TMSCL_OUT_PYLD_OFF     (0)
87275970Scy#define TSYNC_TMSCL_OUT_LEN          (4)
88275970Scy#define TSYNC_TMSCL_MAX_OUT_LEN      (12)
89275970Scy#define TSYNC_TMSCL_PYLD_LEN         (TSYNC_TMSCL_IN_LEN +                    \
90275970Scy                                     TSYNC_TMSCL_MAX_OUT_LEN)
91275970Scy
92275970Scy#define TSYNC_LEAP_IID              (0x2307)    // CS CAI, LEAP SEC IID
93275970Scy#define TSYNC_LEAP_DEST_ID          (0x0001)    // KTS Firmware
94275970Scy#define TSYNC_LEAP_IN_PYLD_OFF      (0)
95275970Scy#define TSYNC_LEAP_IN_LEN           (0)
96275970Scy#define TSYNC_LEAP_OUT_PYLD_OFF     (0)
97275970Scy#define TSYNC_LEAP_OUT_LEN          (28)
98275970Scy#define TSYNC_LEAP_MAX_OUT_LEN      (36)
99275970Scy#define TSYNC_LEAP_PYLD_LEN         (TSYNC_LEAP_IN_LEN +                    \
100275970Scy                                     TSYNC_LEAP_MAX_OUT_LEN)
101275970Scy
102275970Scy// These define the base date/time of the system clock.  The system time will
103275970Scy// be tracked as the number of seconds from this date/time.
104275970Scy#define TSYNC_TIME_BASE_YEAR        (1970) // earliest acceptable year
105275970Scy
106275970Scy#define TSYNC_LCL_STRATUM           (0)
107275970Scy
108275970Scy/*
109275970Scy** TSYNC Time Scales type
110275970Scy*/
111275970Scytypedef enum
112275970Scy{
113275970Scy    TIME_SCALE_UTC    = 0,   // Universal Coordinated Time
114275970Scy    TIME_SCALE_TAI    = 1,   // International Atomic Time
115275970Scy    TIME_SCALE_GPS    = 2,   // Global Positioning System
116275970Scy    TIME_SCALE_LOCAL  = 3,   // UTC w/local rules for time zone and DST
117275970Scy    NUM_TIME_SCALES   = 4,   // Number of time scales
118275970Scy
119275970Scy    TIME_SCALE_MAX    = 15   // Maximum number of timescales
120275970Scy
121275970Scy} TIME_SCALE;
122275970Scy
123275970Scy/*
124275970Scy** TSYNC Board Object
125275970Scy*/
126275970Scytypedef struct BoardObj {
127275970Scy
128275970Scy  int            file_descriptor;
129275970Scy  unsigned short devid;
130275970Scy  unsigned short options;
131275970Scy  unsigned char  firmware[5];
132275970Scy  unsigned char  FPGA[5];
133275970Scy  unsigned char  driver[7];
134275970Scy
135275970Scy} BoardObj;
136275970Scy
137275970Scy/*
138275970Scy** TSYNC Time Object
139275970Scy*/
140275970Scytypedef struct TimeObj {
141275970Scy
142275970Scy  unsigned char  syncOption;  /* -M option */
143275970Scy  unsigned int   secsDouble;  /* seconds floating pt */
144275970Scy  unsigned char  seconds;     /* seconds whole num */
145275970Scy  unsigned char  minutes;
146275970Scy  unsigned char  hours;
147275970Scy  unsigned short days;
148275970Scy  unsigned short year;
149275970Scy  unsigned short flags;      /* bit 2 SYNC, bit 1 TCODE; all others 0 */
150275970Scy
151275970Scy} TimeObj;
152275970Scy
153275970Scy/*
154275970Scy** NTP Time Object
155275970Scy*/
156275970Scytypedef struct NtpTimeObj {
157275970Scy
158275970Scy    TimeObj        timeObj;
159275970Scy    struct timeval tv;
160275970Scy    unsigned int   refId;
161275970Scy
162275970Scy} NtpTimeObj;
163275970Scy/*
164275970Scy** TSYNC Supervisor Reference Object
165275970Scy*/
166275970Scytypedef struct ReferenceObj {
167275970Scy
168275970Scy    char time[TSYNC_REF_LEN];
169275970Scy    char pps[TSYNC_REF_LEN];
170275970Scy
171275970Scy} ReferenceObj;
172275970Scy
173275970Scy/*
174275970Scy** TSYNC Seconds Time Object
175275970Scy*/
176275970Scytypedef struct SecTimeObj
177275970Scy{
178275970Scy    unsigned int seconds;
179275970Scy    unsigned int ns;
180275970Scy}
181275970ScySecTimeObj;
182275970Scy
183275970Scy/*
184275970Scy** TSYNC DOY Time Object
185275970Scy*/
186275970Scytypedef struct DoyTimeObj
187275970Scy{
188275970Scy    unsigned int year;
189275970Scy    unsigned int doy;
190275970Scy    unsigned int hour;
191275970Scy    unsigned int minute;
192275970Scy    unsigned int second;
193275970Scy    unsigned int ns;
194275970Scy}
195275970ScyDoyTimeObj;
196275970Scy
197275970Scy/*
198275970Scy** TSYNC Leap Second Object
199275970Scy*/
200275970Scytypedef struct LeapSecondObj
201275970Scy{
202275970Scy    int        offset;
203275970Scy    DoyTimeObj utcDate;
204275970Scy}
205275970ScyLeapSecondObj;
206275970Scy
207275970Scy/*
208275970Scy * structures for ioctl interactions with driver
209275970Scy */
210275970Scy#define DI_PAYLOADS_STARTER_LENGTH 4
211275970Scytypedef struct ioctl_trans_di {
212275970Scy
213275970Scy    // input parameters
214275970Scy    uint16_t        dest;
215275970Scy    uint16_t        iid;
216275970Scy
217275970Scy    uint32_t        inPayloadOffset;
218275970Scy    uint32_t        inLength;
219275970Scy    uint32_t        outPayloadOffset;
220275970Scy    uint32_t        maxOutLength;
221275970Scy
222275970Scy    // output parameters
223275970Scy    uint32_t        actualOutLength;
224275970Scy    int32_t         status;
225275970Scy
226275970Scy    // Input and output
227275970Scy
228275970Scy    // The payloads field MUST be last in ioctl_trans_di.
229275970Scy    uint8_t         payloads[DI_PAYLOADS_STARTER_LENGTH];
230275970Scy
231275970Scy}ioctl_trans_di;
232275970Scy
233275970Scy/*
234275970Scy * structure for looking up a reference ID from a reference name
235275970Scy */
236275970Scytypedef struct
237275970Scy{
238275970Scy    const char* pRef;           // KTS Reference Name
239275970Scy    const char* pRefId;         // NTP Reference ID
240275970Scy
241275970Scy} RefIdLookup;
242275970Scy
243275970Scy/*
244275970Scy * unit control structure
245275970Scy */
246275970Scytypedef struct  {
247275970Scy    uint32_t refPrefer;         // Reference prefer flag
248275970Scy    uint32_t refId;             // Host peer reference ID
249275970Scy    uint8_t  refStratum;        // Host peer reference stratum
250275970Scy
251275970Scy} TsyncUnit;
252275970Scy
253275970Scy/*
254275970Scy**  Function prototypes
255275970Scy*/
256275970Scystatic void tsync_poll     (int unit, struct peer *);
257275970Scystatic void tsync_shutdown (int, struct peer *);
258275970Scystatic int  tsync_start    (int, struct peer *);
259275970Scy
260275970Scy/*
261275970Scy**  Helper functions
262275970Scy*/
263275970Scystatic void ApplyTimeOffset    (DoyTimeObj* pDt, int off);
264275970Scystatic void SecTimeFromDoyTime (SecTimeObj* pSt, DoyTimeObj* pDt);
265275970Scystatic void DoyTimeFromSecTime (DoyTimeObj* pDt, SecTimeObj* pSt);
266275970Scy
267275970Scy/*
268275970Scy**  Transfer vector
269275970Scy*/
270275970Scystruct refclock refclock_tsyncpci = {
271275970Scy    tsync_start,    /* start up driver */
272275970Scy    tsync_shutdown, /* shut down driver */
273275970Scy    tsync_poll,     /* transmit poll message */
274275970Scy    noentry,        /* not used (old tsync_control) */
275275970Scy    noentry,        /* initialize driver (not used) */
276275970Scy    noentry,        /* not used (old tsync_buginfo) */
277275970Scy    NOFLAGS         /* not used */
278275970Scy};
279275970Scy
280275970Scy/*
281275970Scy * Reference ID lookup table
282275970Scy */
283275970Scystatic RefIdLookup RefIdLookupTbl[] =
284275970Scy{
285275970Scy    {"gps",  "GPS"},
286275970Scy    {"ir",   "IRIG"},
287275970Scy    {"hvq",  "HVQ"},
288275970Scy    {"frq",  "FREQ"},
289275970Scy    {"mdm",  "ACTS"},
290275970Scy    {"epp",  "PPS"},
291275970Scy    {"ptp",  "PTP"},
292275970Scy    {"asc",  "ATC"},
293275970Scy    {"hst0", "USER"},
294275970Scy    {"hst",  TSYNC_REF_LOCAL},
295275970Scy    {"self", TSYNC_REF_LOCAL},
296275970Scy    {NULL,   NULL}
297275970Scy};
298275970Scy
299275970Scy/*******************************************************************************
300275970Scy**          IOCTL DEFINITIONS
301275970Scy*******************************************************************************/
302275970Scy#define IOCTL_TPRO_ID            't'
303275970Scy#define IOCTL_TPRO_OPEN          _IOWR(IOCTL_TPRO_ID, 0,  BoardObj)
304275970Scy#define IOCTL_TPRO_GET_NTP_TIME  _IOWR(IOCTL_TPRO_ID, 25, NtpTimeObj)
305275970Scy#define IOCTL_TSYNC_GET          _IOWR(IOCTL_TPRO_ID, 26, ioctl_trans_di)
306275970Scy
307275970Scy/******************************************************************************
308275970Scy *
309275970Scy * Function:    tsync_start()
310275970Scy * Description: Used to intialize the Spectracom TSYNC reference driver.
311275970Scy *
312275970Scy * Parameters:
313275970Scy *     IN:  unit - not used.
314275970Scy *         *peer - pointer to this reference clock's peer structure
315275970Scy *     Returns: 0 - unsuccessful
316275970Scy *              1 - successful
317275970Scy *
318275970Scy*******************************************************************************/
319275970Scystatic int tsync_start(int unit, struct peer *peer)
320275970Scy{
321275970Scy    struct refclockproc *pp;
322275970Scy    TsyncUnit           *up;
323275970Scy
324275970Scy
325275970Scy    /*
326275970Scy    **  initialize reference clock and peer parameters
327275970Scy    */
328275970Scy    pp                = peer->procptr;
329275970Scy    pp->clockdesc     = DESCRIPTION;
330275970Scy    pp->io.clock_recv = noentry;
331275970Scy    pp->io.srcclock   = peer;
332275970Scy    pp->io.datalen    = 0;
333275970Scy    peer->precision   = PRECISION;
334275970Scy
335275970Scy    // Allocate and initialize unit structure
336275970Scy    if (!(up = (TsyncUnit*)emalloc(sizeof(TsyncUnit))))
337275970Scy    {
338275970Scy        return (0);
339275970Scy    }
340275970Scy
341275970Scy    // Store reference preference
342275970Scy    up->refPrefer = peer->flags & FLAG_PREFER;
343275970Scy
344275970Scy    // Initialize reference stratum level and ID
345275970Scy    up->refStratum = STRATUM_UNSPEC;
346275970Scy    strncpy((char *)&up->refId, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
347275970Scy
348275970Scy    // Attach unit structure
349275970Scy    pp->unitptr = (caddr_t)up;
350275970Scy
351275970Scy    /* Declare our refId as local in the beginning because we do not know
352275970Scy     * what our actual refid is yet.
353275970Scy     */
354275970Scy    strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
355275970Scy
356275970Scy    return (1);
357275970Scy
358275970Scy} /* End - tsync_start() */
359275970Scy
360275970Scy/*******************************************************************************
361275970Scy**
362275970Scy** Function:    tsync_shutdown()
363275970Scy** Description: Handles anything related to shutting down the reference clock
364275970Scy**              driver. Nothing at this point in time.
365275970Scy**
366275970Scy** Parameters:
367275970Scy**     IN:  unit - not used.
368275970Scy**         *peer - pointer to this reference clock's peer structure
369275970Scy**     Returns: none.
370275970Scy**
371275970Scy*******************************************************************************/
372275970Scystatic void tsync_shutdown(int unit, struct peer *peer)
373275970Scy{
374275970Scy
375275970Scy} /* End - tsync_shutdown() */
376275970Scy
377275970Scy/******************************************************************************
378275970Scy *
379275970Scy * Function:    tsync_poll()
380275970Scy * Description: Retrieve time from the TSYNC device.
381275970Scy *
382275970Scy * Parameters:
383275970Scy *     IN:  unit - not used.
384275970Scy *         *peer - pointer to this reference clock's peer structure
385275970Scy *     Returns: none.
386275970Scy *
387275970Scy*******************************************************************************/
388275970Scystatic void tsync_poll(int unit, struct peer *peer)
389275970Scy{
390275970Scy    char                 device[32];
391275970Scy    struct refclockproc *pp;
392275970Scy    struct calendar      jt;
393275970Scy    TsyncUnit           *up;
394275970Scy    unsigned char        synch;
395275970Scy    double               seconds;
396275970Scy    int                  err;
397275970Scy    int                  err1;
398275970Scy    int                  err2;
399275970Scy    int                  err3;
400275970Scy    int                  i;
401275970Scy    int                  j;
402275970Scy    unsigned int         itAllocationLength;
403275970Scy    unsigned int         itAllocationLength1;
404275970Scy    unsigned int         itAllocationLength2;
405275970Scy    NtpTimeObj           TimeContext;
406275970Scy    BoardObj             hBoard;
407275970Scy    char                 timeRef[TSYNC_REF_LEN + 1];
408275970Scy    char                 ppsRef [TSYNC_REF_LEN + 1];
409275970Scy    TIME_SCALE           tmscl = TIME_SCALE_UTC;
410275970Scy    LeapSecondObj        leapSec;
411275970Scy    ioctl_trans_di      *it;
412275970Scy    ioctl_trans_di      *it1;
413275970Scy    ioctl_trans_di      *it2;
414275970Scy    l_fp                 offset;
415275970Scy    l_fp                 ltemp;
416275970Scy    ReferenceObj *	 pRefObj;
417275970Scy
418275970Scy
419275970Scy    /* Construct the device name */
420275970Scy    sprintf(device, "%s%d", DEVICE, (int)peer->refclkunit);
421275970Scy
422275970Scy    printf("Polling device number %d...\n", (int)peer->refclkunit);
423275970Scy
424275970Scy    /* Open the TSYNC device */
425275970Scy    hBoard.file_descriptor = open(device, O_RDONLY | O_NDELAY, 0777);
426275970Scy
427275970Scy    /* If error opening TSYNC device... */
428275970Scy    if (hBoard.file_descriptor < 0)
429275970Scy    {
430275970Scy        msyslog(LOG_ERR, "Couldn't open device");
431275970Scy        return;
432275970Scy    }
433275970Scy
434275970Scy    /* If error while initializing the board... */
435275970Scy    if (ioctl(hBoard.file_descriptor, IOCTL_TPRO_OPEN, &hBoard) < 0)
436275970Scy    {
437275970Scy        msyslog(LOG_ERR, "Couldn't initialize device");
438275970Scy        close(hBoard.file_descriptor);
439275970Scy        return;
440275970Scy    }
441275970Scy
442275970Scy    /* Allocate memory for ioctl message */
443275970Scy    itAllocationLength =
444275970Scy        (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
445275970Scy        TSYNC_REF_IN_LEN + TSYNC_REF_MAX_OUT_LEN;
446275970Scy
447275970Scy    it = (ioctl_trans_di*)alloca(itAllocationLength);
448275970Scy    if (it == NULL) {
449275970Scy        msyslog(LOG_ERR, "Couldn't allocate transaction memory - Reference");
450275970Scy        return;
451275970Scy    }
452275970Scy
453275970Scy    /* Build SS_GetRef ioctl message */
454275970Scy    it->dest             = TSYNC_REF_DEST_ID;
455275970Scy    it->iid              = TSYNC_REF_IID;
456275970Scy    it->inPayloadOffset  = TSYNC_REF_IN_PYLD_OFF;
457275970Scy    it->inLength         = TSYNC_REF_IN_LEN;
458275970Scy    it->outPayloadOffset = TSYNC_REF_OUT_PYLD_OFF;
459275970Scy    it->maxOutLength     = TSYNC_REF_MAX_OUT_LEN;
460275970Scy    it->actualOutLength  = 0;
461275970Scy    it->status           = 0;
462275970Scy    memset(it->payloads, 0, TSYNC_REF_MAX_OUT_LEN);
463275970Scy
464275970Scy    /* Read the reference from the TSYNC-PCI device */
465275970Scy    err = ioctl(hBoard.file_descriptor,
466275970Scy                 IOCTL_TSYNC_GET,
467275970Scy                (char *)it);
468275970Scy
469275970Scy    /* Allocate memory for ioctl message */
470275970Scy    itAllocationLength1 =
471275970Scy        (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
472275970Scy        TSYNC_TMSCL_IN_LEN + TSYNC_TMSCL_MAX_OUT_LEN;
473275970Scy
474275970Scy    it1 = (ioctl_trans_di*)alloca(itAllocationLength1);
475275970Scy    if (it1 == NULL) {
476275970Scy        msyslog(LOG_ERR, "Couldn't allocate transaction memory - Time Scale");
477275970Scy        return;
478275970Scy    }
479275970Scy
480275970Scy    /* Build CS_GetTimeScale ioctl message */
481275970Scy    it1->dest             = TSYNC_TMSCL_DEST_ID;
482275970Scy    it1->iid              = TSYNC_TMSCL_IID;
483275970Scy    it1->inPayloadOffset  = TSYNC_TMSCL_IN_PYLD_OFF;
484275970Scy    it1->inLength         = TSYNC_TMSCL_IN_LEN;
485275970Scy    it1->outPayloadOffset = TSYNC_TMSCL_OUT_PYLD_OFF;
486275970Scy    it1->maxOutLength     = TSYNC_TMSCL_MAX_OUT_LEN;
487275970Scy    it1->actualOutLength  = 0;
488275970Scy    it1->status           = 0;
489275970Scy    memset(it1->payloads, 0, TSYNC_TMSCL_MAX_OUT_LEN);
490275970Scy
491275970Scy    /* Read the Time Scale info from the TSYNC-PCI device */
492275970Scy    err1 = ioctl(hBoard.file_descriptor,
493275970Scy                 IOCTL_TSYNC_GET,
494275970Scy                 (char *)it1);
495275970Scy
496275970Scy    /* Allocate memory for ioctl message */
497275970Scy    itAllocationLength2 =
498275970Scy        (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
499275970Scy        TSYNC_LEAP_IN_LEN + TSYNC_LEAP_MAX_OUT_LEN;
500275970Scy
501275970Scy    it2 = (ioctl_trans_di*)alloca(itAllocationLength2);
502275970Scy    if (it2 == NULL) {
503275970Scy        msyslog(LOG_ERR, "Couldn't allocate transaction memory - Leap Second");
504275970Scy        return;
505275970Scy    }
506275970Scy
507275970Scy    /* Build CS_GetLeapSec ioctl message */
508275970Scy    it2->dest             = TSYNC_LEAP_DEST_ID;
509275970Scy    it2->iid              = TSYNC_LEAP_IID;
510275970Scy    it2->inPayloadOffset  = TSYNC_LEAP_IN_PYLD_OFF;
511275970Scy    it2->inLength         = TSYNC_LEAP_IN_LEN;
512275970Scy    it2->outPayloadOffset = TSYNC_LEAP_OUT_PYLD_OFF;
513275970Scy    it2->maxOutLength     = TSYNC_LEAP_MAX_OUT_LEN;
514275970Scy    it2->actualOutLength  = 0;
515275970Scy    it2->status           = 0;
516275970Scy    memset(it2->payloads, 0, TSYNC_LEAP_MAX_OUT_LEN);
517275970Scy
518275970Scy    /* Read the leap seconds info from the TSYNC-PCI device */
519275970Scy    err2 = ioctl(hBoard.file_descriptor,
520275970Scy                 IOCTL_TSYNC_GET,
521275970Scy                 (char *)it2);
522275970Scy
523275970Scy    pp = peer->procptr;
524275970Scy    up = (TsyncUnit*)pp->unitptr;
525275970Scy
526275970Scy    /* Read the time from the TSYNC-PCI device */
527275970Scy    err3 = ioctl(hBoard.file_descriptor,
528275970Scy                 IOCTL_TPRO_GET_NTP_TIME,
529275970Scy                 (char *)&TimeContext);
530275970Scy
531275970Scy    /* Close the TSYNC device */
532275970Scy    close(hBoard.file_descriptor);
533275970Scy
534275970Scy    // Check for errors
535275970Scy    if ((err < 0) ||(err1 < 0) || (err2 < 0) || (err3 < 0) ||
536275970Scy        (it->status != 0) || (it1->status != 0) || (it2->status != 0) ||
537275970Scy        (it->actualOutLength  != TSYNC_REF_OUT_LEN) ||
538275970Scy        (it1->actualOutLength != TSYNC_TMSCL_OUT_LEN) ||
539275970Scy        (it2->actualOutLength != TSYNC_LEAP_OUT_LEN)) {
540275970Scy        refclock_report(peer, CEVNT_FAULT);
541275970Scy        return;
542275970Scy    }
543275970Scy
544275970Scy    // Extract reference identifiers from ioctl payload
545275970Scy    memset(timeRef, '\0', sizeof(timeRef));
546275970Scy    memset(ppsRef, '\0', sizeof(ppsRef));
547275970Scy    pRefObj = (void *)it->payloads;
548275970Scy    memcpy(timeRef, pRefObj->time, TSYNC_REF_LEN);
549275970Scy    memcpy(ppsRef, pRefObj->pps, TSYNC_REF_LEN);
550275970Scy
551275970Scy    // Extract the Clock Service Time Scale and convert to correct byte order
552293423Sdelphij    memcpy(&tmscl, it1->payloads, sizeof(tmscl));
553275970Scy    tmscl = ntohl(tmscl);
554275970Scy
555275970Scy    // Extract leap second info from ioctl payload and perform byte swapping
556275970Scy    for (i = 0; i < (sizeof(leapSec) / 4); i++)
557275970Scy    {
558275970Scy        for (j = 0; j < 4; j++)
559275970Scy        {
560275970Scy            ((unsigned char*)&leapSec)[(i * 4) + j] =
561275970Scy                    ((unsigned char*)(it2->payloads))[(i * 4) + (3 - j)];
562275970Scy        }
563275970Scy    }
564275970Scy
565275970Scy    // Determine time reference ID from reference name
566275970Scy    for (i = 0; RefIdLookupTbl[i].pRef != NULL; i++)
567275970Scy    {
568275970Scy       // Search RefID table
569275970Scy       if (strstr(timeRef, RefIdLookupTbl[i].pRef) != NULL)
570275970Scy       {
571275970Scy          // Found the matching string
572275970Scy          break;
573275970Scy       }
574275970Scy    }
575275970Scy
576275970Scy    // Determine pps reference ID from reference name
577275970Scy    for (j = 0; RefIdLookupTbl[j].pRef != NULL; j++)
578275970Scy    {
579275970Scy       // Search RefID table
580275970Scy       if (strstr(ppsRef, RefIdLookupTbl[j].pRef) != NULL)
581275970Scy       {
582275970Scy          // Found the matching string
583275970Scy          break;
584275970Scy       }
585275970Scy    }
586275970Scy
587275970Scy    // Determine synchronization state from flags
588275970Scy    synch = (TimeContext.timeObj.flags == 0x4) ? 1 : 0;
589275970Scy
590275970Scy    // Pull seconds information from time object
591275970Scy    seconds = (double) (TimeContext.timeObj.secsDouble);
592275970Scy    seconds /= (double) 1000000.0;
593275970Scy
594275970Scy    /*
595275970Scy    ** Convert the number of microseconds to double and then place in the
596275970Scy    ** peer's last received long floating point format.
597275970Scy    */
598275970Scy    DTOLFP(((double)TimeContext.tv.tv_usec / 1000000.0), &pp->lastrec);
599275970Scy
600275970Scy    /*
601275970Scy    ** The specTimeStamp is the number of seconds since 1/1/1970, while the
602275970Scy    ** peer's lastrec time should be compatible with NTP which is seconds since
603275970Scy    ** 1/1/1900.  So Add the number of seconds between 1900 and 1970 to the
604275970Scy    ** specTimeStamp and place in the peer's lastrec long floating point struct.
605275970Scy    */
606275970Scy    pp->lastrec.Ul_i.Xl_ui += (unsigned int)TimeContext.tv.tv_sec +
607275970Scy                                            SECONDS_1900_TO_1970;
608275970Scy
609275970Scy    pp->polls++;
610275970Scy
611275970Scy    /*
612275970Scy    **  set the reference clock object
613275970Scy    */
614275970Scy    sprintf(pp->a_lastcode, "%03d %02d:%02d:%02.6f",
615275970Scy            TimeContext.timeObj.days, TimeContext.timeObj.hours,
616275970Scy            TimeContext.timeObj.minutes, seconds);
617275970Scy
618275970Scy    pp->lencode = strlen (pp->a_lastcode);
619275970Scy    pp->day     = TimeContext.timeObj.days;
620275970Scy    pp->hour    = TimeContext.timeObj.hours;
621275970Scy    pp->minute  = TimeContext.timeObj.minutes;
622275970Scy    pp->second  = (int) seconds;
623275970Scy    seconds     = (seconds - (double) (pp->second / 1.0)) * 1000000000;
624275970Scy    pp->nsec    = (long) seconds;
625275970Scy
626275970Scy    /*
627275970Scy    **  calculate year start
628275970Scy    */
629275970Scy    jt.year       = TimeContext.timeObj.year;
630275970Scy    jt.yearday    = 1;
631275970Scy    jt.monthday   = 1;
632275970Scy    jt.month      = 1;
633275970Scy    jt.hour       = 0;
634275970Scy    jt.minute     = 0;
635275970Scy    jt.second     = 0;
636275970Scy    pp->yearstart = caltontp(&jt);
637275970Scy
638275970Scy    // Calculate and report reference clock offset
639275970Scy    offset.l_ui = (long)(((pp->day - 1) * 24) + pp->hour + GMT);
640275970Scy    offset.l_ui = (offset.l_ui * 60) + (long)pp->minute;
641275970Scy    offset.l_ui = (offset.l_ui * 60) + (long)pp->second;
642275970Scy    offset.l_ui = offset.l_ui + (long)pp->yearstart;
643275970Scy    offset.l_uf = 0;
644275970Scy    DTOLFP(pp->nsec / 1e9, &ltemp);
645275970Scy    L_ADD(&offset, &ltemp);
646275970Scy    refclock_process_offset(pp, offset, pp->lastrec,
647275970Scy                            pp->fudgetime1);
648275970Scy
649275970Scy    // KTS in sync
650275970Scy    if (synch) {
651275970Scy        // Subtract leap second info by one second to determine effective day
652275970Scy        ApplyTimeOffset(&(leapSec.utcDate), -1);
653275970Scy
654275970Scy        // If there is a leap second today and the KTS is using a time scale
655275970Scy        // which handles leap seconds then
656275970Scy        if ((tmscl != TIME_SCALE_GPS) && (tmscl != TIME_SCALE_TAI) &&
657275970Scy            (leapSec.utcDate.year == (unsigned int)TimeContext.timeObj.year) &&
658275970Scy            (leapSec.utcDate.doy  == (unsigned int)TimeContext.timeObj.days))
659275970Scy        {
660275970Scy            // If adding a second
661275970Scy            if (leapSec.offset == 1)
662275970Scy            {
663275970Scy                pp->leap = LEAP_ADDSECOND;
664275970Scy            }
665275970Scy            // Else if removing a second
666275970Scy            else if (leapSec.offset == -1)
667275970Scy            {
668275970Scy                pp->leap = LEAP_DELSECOND;
669275970Scy            }
670275970Scy            // Else report no leap second pending (no handling of offsets
671275970Scy            // other than +1 or -1)
672275970Scy            else
673275970Scy            {
674275970Scy                pp->leap = LEAP_NOWARNING;
675275970Scy            }
676275970Scy        }
677275970Scy        // Else report no leap second pending
678275970Scy        else
679275970Scy        {
680275970Scy            pp->leap = LEAP_NOWARNING;
681275970Scy        }
682275970Scy
683275970Scy        peer->leap = pp->leap;
684275970Scy        refclock_report(peer, CEVNT_NOMINAL);
685275970Scy
686275970Scy        // If reference name reported, then not in holdover
687275970Scy        if ((RefIdLookupTbl[i].pRef != NULL) &&
688275970Scy            (RefIdLookupTbl[j].pRef != NULL))
689275970Scy        {
690275970Scy            // Determine if KTS being synchronized by host (identified as
691275970Scy            // "LOCL")
692275970Scy            if ((strcmp(RefIdLookupTbl[i].pRefId, TSYNC_REF_LOCAL) == 0) ||
693275970Scy                (strcmp(RefIdLookupTbl[j].pRefId, TSYNC_REF_LOCAL) == 0))
694275970Scy            {
695275970Scy                // Clear prefer flag
696275970Scy                peer->flags &= ~FLAG_PREFER;
697275970Scy
698275970Scy                // Set reference clock stratum level as unusable
699275970Scy                pp->stratum   = STRATUM_UNSPEC;
700275970Scy                peer->stratum = pp->stratum;
701275970Scy
702275970Scy                // If a valid peer is available
703275970Scy                if ((sys_peer != NULL) && (sys_peer != peer))
704275970Scy                {
705275970Scy                    // Store reference peer stratum level and ID
706275970Scy                    up->refStratum = sys_peer->stratum;
707275970Scy                    up->refId      = addr2refid(&sys_peer->srcadr);
708275970Scy                }
709275970Scy            }
710275970Scy            else
711275970Scy            {
712275970Scy                // Restore prefer flag
713275970Scy                peer->flags |= up->refPrefer;
714275970Scy
715275970Scy                // Store reference stratum as local clock
716275970Scy                up->refStratum = TSYNC_LCL_STRATUM;
717275970Scy                strncpy((char *)&up->refId, RefIdLookupTbl[j].pRefId,
718275970Scy                    TSYNC_REF_LEN);
719275970Scy
720275970Scy                // Set reference clock stratum level as local clock
721275970Scy                pp->stratum   = TSYNC_LCL_STRATUM;
722275970Scy                peer->stratum = pp->stratum;
723275970Scy            }
724275970Scy
725275970Scy            // Update reference name
726275970Scy            strncpy((char *)&pp->refid, RefIdLookupTbl[j].pRefId,
727275970Scy                TSYNC_REF_LEN);
728275970Scy            peer->refid = pp->refid;
729275970Scy        }
730275970Scy        // Else in holdover
731275970Scy        else
732275970Scy        {
733275970Scy            // Restore prefer flag
734275970Scy            peer->flags |= up->refPrefer;
735275970Scy
736275970Scy            // Update reference ID to saved ID
737275970Scy            pp->refid   = up->refId;
738275970Scy            peer->refid = pp->refid;
739275970Scy
740275970Scy            // Update stratum level to saved stratum level
741275970Scy            pp->stratum   = up->refStratum;
742275970Scy            peer->stratum = pp->stratum;
743275970Scy        }
744275970Scy    }
745275970Scy    // Else KTS not in sync
746275970Scy    else {
747275970Scy        // Place local identifier in peer RefID
748275970Scy        strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
749275970Scy        peer->refid = pp->refid;
750275970Scy
751275970Scy        // Report not in sync
752275970Scy        pp->leap   = LEAP_NOTINSYNC;
753275970Scy        peer->leap = pp->leap;
754275970Scy    }
755275970Scy
756275970Scy    if (pp->coderecv == pp->codeproc) {
757275970Scy        refclock_report(peer, CEVNT_TIMEOUT);
758275970Scy        return;
759275970Scy    }
760275970Scy
761275970Scy    record_clock_stats(&peer->srcadr, pp->a_lastcode);
762275970Scy    refclock_receive(peer);
763275970Scy
764275970Scy    /* Increment the number of times the reference has been polled */
765275970Scy    pp->polls++;
766275970Scy
767275970Scy} /* End - tsync_poll() */
768275970Scy
769275970Scy
770275970Scy////////////////////////////////////////////////////////////////////////////////
771275970Scy// Function:    ApplyTimeOffset
772275970Scy// Description: The ApplyTimeOffset function adds an offset (in seconds) to a
773275970Scy//              specified date and time.  The specified date and time is passed
774275970Scy//              back after being modified.
775275970Scy//
776275970Scy// Assumptions: 1. Every fourth year is a leap year.  Therefore, this function
777275970Scy//                 is only accurate through Feb 28, 2100.
778275970Scy////////////////////////////////////////////////////////////////////////////////
779275970Scyvoid ApplyTimeOffset(DoyTimeObj* pDt, int off)
780275970Scy{
781275970Scy    SecTimeObj st;                  // Time, in seconds
782275970Scy
783275970Scy
784275970Scy    // Convert date and time to seconds
785275970Scy    SecTimeFromDoyTime(&st, pDt);
786275970Scy
787275970Scy    // Apply offset
788275970Scy    st.seconds = (int)((signed long long)st.seconds + (signed long long)off);
789275970Scy
790275970Scy    // Convert seconds to date and time
791275970Scy    DoyTimeFromSecTime(pDt, &st);
792275970Scy
793275970Scy} // End ApplyTimeOffset
794275970Scy
795275970Scy
796275970Scy////////////////////////////////////////////////////////////////////////////////
797275970Scy// Function:    SecTimeFromDoyTime
798275970Scy// Description: The SecTimeFromDoyTime function converts a specified date
799275970Scy//              and time into a count of seconds since the base time.  This
800275970Scy//              function operates across the range Base Time to Max Time for
801275970Scy//              the system.
802275970Scy//
803275970Scy// Assumptions: 1. A leap year is any year evenly divisible by 4.  Therefore,
804275970Scy//                 this function is only accurate through Feb 28, 2100.
805275970Scy//              2. Conversion does not account for leap seconds.
806275970Scy////////////////////////////////////////////////////////////////////////////////
807275970Scyvoid SecTimeFromDoyTime(SecTimeObj* pSt, DoyTimeObj* pDt)
808275970Scy{
809275970Scy    unsigned int yrs;               // Years
810275970Scy    unsigned int lyrs;              // Leap years
811275970Scy
812275970Scy
813275970Scy    // Start with accumulated time of 0
814275970Scy    pSt->seconds  = 0;
815275970Scy
816275970Scy    // Calculate the number of years and leap years
817275970Scy    yrs           = pDt->year - TSYNC_TIME_BASE_YEAR;
818275970Scy    lyrs          = (yrs + 1) / 4;
819275970Scy
820275970Scy    // Convert leap years and years
821275970Scy    pSt->seconds += lyrs           * SECSPERLEAPYEAR;
822275970Scy    pSt->seconds += (yrs - lyrs)   * SECSPERYEAR;
823275970Scy
824275970Scy    // Convert days, hours, minutes and seconds
825275970Scy    pSt->seconds += (pDt->doy - 1) * SECSPERDAY;
826275970Scy    pSt->seconds += pDt->hour      * SECSPERHR;
827275970Scy    pSt->seconds += pDt->minute    * SECSPERMIN;
828275970Scy    pSt->seconds += pDt->second;
829275970Scy
830275970Scy    // Copy the subseconds count
831275970Scy    pSt->ns       = pDt->ns;
832275970Scy
833275970Scy} // End SecTimeFromDoyTime
834275970Scy
835275970Scy
836275970Scy////////////////////////////////////////////////////////////////////////////////
837275970Scy// Function:    DoyTimeFromSecTime
838275970Scy// Description: The DoyTimeFromSecTime function converts a specified count
839275970Scy//              of seconds since the start of our base time into a SecTimeObj
840275970Scy//              structure.
841275970Scy//
842275970Scy// Assumptions: 1. A leap year is any year evenly divisible by 4.  Therefore,
843275970Scy//                 this function is only accurate through Feb 28, 2100.
844275970Scy//              2. Conversion does not account for leap seconds.
845275970Scy////////////////////////////////////////////////////////////////////////////////
846275970Scyvoid DoyTimeFromSecTime(DoyTimeObj* pDt, SecTimeObj* pSt)
847275970Scy{
848275970Scy    signed long long secs;          // Seconds accumulator variable
849275970Scy    unsigned int     yrs;           // Years accumulator variable
850275970Scy    unsigned int     doys;          // Days accumulator variable
851275970Scy    unsigned int     hrs;           // Hours accumulator variable
852275970Scy    unsigned int     mins;          // Minutes accumulator variable
853275970Scy
854275970Scy
855275970Scy    // Convert the seconds count into a signed 64-bit number for calculations
856275970Scy    secs  = (signed long long)(pSt->seconds);
857275970Scy
858275970Scy    // Calculate the number of 4 year chunks
859275970Scy    yrs   = (unsigned int)((secs /
860275970Scy                           ((SECSPERYEAR * 3) + SECSPERLEAPYEAR)) * 4);
861275970Scy    secs %= ((SECSPERYEAR * 3) + SECSPERLEAPYEAR);
862275970Scy
863275970Scy    // If there is at least a normal year worth of time left
864275970Scy    if (secs >= SECSPERYEAR)
865275970Scy    {
866275970Scy        // Increment the number of years and subtract a normal year of time
867275970Scy        yrs++;
868275970Scy        secs -= SECSPERYEAR;
869275970Scy    }
870275970Scy
871275970Scy    // If there is still at least a normal year worth of time left
872275970Scy    if (secs >= SECSPERYEAR)
873275970Scy    {
874275970Scy        // Increment the number of years and subtract a normal year of time
875275970Scy        yrs++;
876275970Scy        secs -= SECSPERYEAR;
877275970Scy    }
878275970Scy
879275970Scy    // If there is still at least a leap year worth of time left
880275970Scy    if (secs >= SECSPERLEAPYEAR)
881275970Scy    {
882275970Scy        // Increment the number of years and subtract a leap year of time
883275970Scy        yrs++;
884275970Scy        secs -= SECSPERLEAPYEAR;
885275970Scy    }
886275970Scy
887275970Scy    // Calculate the day of year as the number of days left, then add 1
888275970Scy    // because months start on the 1st.
889275970Scy    doys  = (unsigned int)((secs / SECSPERDAY) + 1);
890275970Scy    secs %= SECSPERDAY;
891275970Scy
892275970Scy    // Calculate the hour
893275970Scy    hrs   = (unsigned int)(secs / SECSPERHR);
894275970Scy    secs %= SECSPERHR;
895275970Scy
896275970Scy    // Calculate the minute
897275970Scy    mins  = (unsigned int)(secs / SECSPERMIN);
898275970Scy    secs %= SECSPERMIN;
899275970Scy
900275970Scy    // Fill in the doytime structure
901275970Scy    pDt->year   = yrs + TSYNC_TIME_BASE_YEAR;
902275970Scy    pDt->doy    = doys;
903275970Scy    pDt->hour   = hrs;
904275970Scy    pDt->minute = mins;
905275970Scy    pDt->second = (unsigned int)secs;
906275970Scy    pDt->ns     = pSt->ns;
907275970Scy
908275970Scy} // End DoyTimeFromSecTime
909275970Scy
910275970Scy#else
911275970Scyint refclock_tsyncpci_bs;
912275970Scy#endif /* REFCLOCK */
913