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