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