1290001Sglebius/******************************************************************************* 2290001Sglebius* 3290001Sglebius* Module : refclock_tsyncpci.c 4290001Sglebius* Date : 09/08/08 5290001Sglebius* Purpose : Implements a reference clock driver for the NTP daemon. This 6290001Sglebius* reference clock driver provides a means to communicate with 7290001Sglebius* the Spectracom TSYNC PCI timing devices and use them as a time 8290001Sglebius* source. 9290001Sglebius* 10290001Sglebius* (C) Copyright 2008 Spectracom Corporation 11290001Sglebius* 12290001Sglebius* This software is provided by Spectracom Corporation 'as is' and 13290001Sglebius* any express or implied warranties, including, but not limited to, the 14290001Sglebius* implied warranties of merchantability and fitness for a particular purpose 15290001Sglebius* are disclaimed. In no event shall Spectracom Corporation be liable 16290001Sglebius* for any direct, indirect, incidental, special, exemplary, or consequential 17290001Sglebius* damages (including, but not limited to, procurement of substitute goods 18290001Sglebius* or services; loss of use, data, or profits; or business interruption) 19290001Sglebius* however caused and on any theory of liability, whether in contract, strict 20290001Sglebius* liability, or tort (including negligence or otherwise) arising in any way 21290001Sglebius* out of the use of this software, even if advised of the possibility of 22290001Sglebius* such damage. 23290001Sglebius* 24290001Sglebius* This software is released for distribution according to the NTP copyright 25290001Sglebius* and license contained in html/copyright.html of NTP source. 26290001Sglebius* 27290001Sglebius*******************************************************************************/ 28290001Sglebius#ifdef HAVE_CONFIG_H 29290001Sglebius#include <config.h> 30290001Sglebius#endif 31290001Sglebius 32290001Sglebius#if defined(REFCLOCK) && defined(CLOCK_TSYNCPCI) 33290001Sglebius 34290001Sglebius#include <asm/ioctl.h> 35290001Sglebius#ifdef HAVE_SYS_IOCTL_H 36290001Sglebius# include <sys/ioctl.h> 37290001Sglebius#endif 38290001Sglebius 39290001Sglebius#include <stdio.h> 40290001Sglebius#include <ctype.h> 41290001Sglebius#include <netinet/in.h> 42290001Sglebius 43290001Sglebius 44290001Sglebius#include "ntpd.h" 45290001Sglebius#include "ntp_io.h" 46290001Sglebius#include "ntp_refclock.h" 47290001Sglebius#include "ntp_unixtime.h" 48290001Sglebius#include "ntp_stdlib.h" 49290001Sglebius#include "ntp_calendar.h" 50290001Sglebius 51290001Sglebius 52290001Sglebius/******************************************************************************* 53290001Sglebius** 54290001Sglebius** This driver supports the Spectracom TSYNC PCI GPS receiver. It requires 55290001Sglebius** that the tsyncpci.o device driver be installed and loaded. 56290001Sglebius** 57290001Sglebius*******************************************************************************/ 58290001Sglebius 59290001Sglebius#define TSYNC_PCI_REVISION "1.11" 60290001Sglebius 61290001Sglebius/* 62290001Sglebius** TPRO interface definitions 63290001Sglebius*/ 64290001Sglebius#define DEVICE "/dev/tsyncpci" /* device name */ 65290001Sglebius#define PRECISION (-20) /* precision assumed (1 us) */ 66290001Sglebius#define DESCRIPTION "Spectracom TSYNC-PCI" /* WRU */ 67290001Sglebius 68290001Sglebius#define SECONDS_1900_TO_1970 (2208988800U) 69290001Sglebius 70290001Sglebius#define TSYNC_REF_IID (0x2500) // SS CAI, REF IID 71290001Sglebius#define TSYNC_REF_DEST_ID (0x0001) // KTS Firmware 72290001Sglebius#define TSYNC_REF_IN_PYLD_OFF (0) 73290001Sglebius#define TSYNC_REF_IN_LEN (0) 74290001Sglebius#define TSYNC_REF_OUT_PYLD_OFF (0) 75290001Sglebius#define TSYNC_REF_OUT_LEN (8) 76290001Sglebius#define TSYNC_REF_MAX_OUT_LEN (16) 77290001Sglebius#define TSYNC_REF_PYLD_LEN (TSYNC_REF_IN_LEN + \ 78290001Sglebius TSYNC_REF_MAX_OUT_LEN) 79290001Sglebius#define TSYNC_REF_LEN (4) 80290001Sglebius#define TSYNC_REF_LOCAL ("LOCL") 81290001Sglebius 82290001Sglebius#define TSYNC_TMSCL_IID (0x2301) // CS CAI, TIMESCALE IID 83290001Sglebius#define TSYNC_TMSCL_DEST_ID (0x0001) // KTS Firmware 84290001Sglebius#define TSYNC_TMSCL_IN_PYLD_OFF (0) 85290001Sglebius#define TSYNC_TMSCL_IN_LEN (0) 86290001Sglebius#define TSYNC_TMSCL_OUT_PYLD_OFF (0) 87290001Sglebius#define TSYNC_TMSCL_OUT_LEN (4) 88290001Sglebius#define TSYNC_TMSCL_MAX_OUT_LEN (12) 89290001Sglebius#define TSYNC_TMSCL_PYLD_LEN (TSYNC_TMSCL_IN_LEN + \ 90290001Sglebius TSYNC_TMSCL_MAX_OUT_LEN) 91290001Sglebius 92290001Sglebius#define TSYNC_LEAP_IID (0x2307) // CS CAI, LEAP SEC IID 93290001Sglebius#define TSYNC_LEAP_DEST_ID (0x0001) // KTS Firmware 94290001Sglebius#define TSYNC_LEAP_IN_PYLD_OFF (0) 95290001Sglebius#define TSYNC_LEAP_IN_LEN (0) 96290001Sglebius#define TSYNC_LEAP_OUT_PYLD_OFF (0) 97290001Sglebius#define TSYNC_LEAP_OUT_LEN (28) 98290001Sglebius#define TSYNC_LEAP_MAX_OUT_LEN (36) 99290001Sglebius#define TSYNC_LEAP_PYLD_LEN (TSYNC_LEAP_IN_LEN + \ 100290001Sglebius TSYNC_LEAP_MAX_OUT_LEN) 101290001Sglebius 102290001Sglebius// These define the base date/time of the system clock. The system time will 103290001Sglebius// be tracked as the number of seconds from this date/time. 104290001Sglebius#define TSYNC_TIME_BASE_YEAR (1970) // earliest acceptable year 105290001Sglebius 106290001Sglebius#define TSYNC_LCL_STRATUM (0) 107290001Sglebius 108290001Sglebius/* 109290001Sglebius** TSYNC Time Scales type 110290001Sglebius*/ 111290001Sglebiustypedef enum 112290001Sglebius{ 113290001Sglebius TIME_SCALE_UTC = 0, // Universal Coordinated Time 114290001Sglebius TIME_SCALE_TAI = 1, // International Atomic Time 115290001Sglebius TIME_SCALE_GPS = 2, // Global Positioning System 116290001Sglebius TIME_SCALE_LOCAL = 3, // UTC w/local rules for time zone and DST 117290001Sglebius NUM_TIME_SCALES = 4, // Number of time scales 118290001Sglebius 119290001Sglebius TIME_SCALE_MAX = 15 // Maximum number of timescales 120290001Sglebius 121290001Sglebius} TIME_SCALE; 122290001Sglebius 123290001Sglebius/* 124290001Sglebius** TSYNC Board Object 125290001Sglebius*/ 126290001Sglebiustypedef struct BoardObj { 127290001Sglebius 128290001Sglebius int file_descriptor; 129290001Sglebius unsigned short devid; 130290001Sglebius unsigned short options; 131290001Sglebius unsigned char firmware[5]; 132290001Sglebius unsigned char FPGA[5]; 133290001Sglebius unsigned char driver[7]; 134290001Sglebius 135290001Sglebius} BoardObj; 136290001Sglebius 137290001Sglebius/* 138290001Sglebius** TSYNC Time Object 139290001Sglebius*/ 140290001Sglebiustypedef struct TimeObj { 141290001Sglebius 142290001Sglebius unsigned char syncOption; /* -M option */ 143290001Sglebius unsigned int secsDouble; /* seconds floating pt */ 144290001Sglebius unsigned char seconds; /* seconds whole num */ 145290001Sglebius unsigned char minutes; 146290001Sglebius unsigned char hours; 147290001Sglebius unsigned short days; 148290001Sglebius unsigned short year; 149290001Sglebius unsigned short flags; /* bit 2 SYNC, bit 1 TCODE; all others 0 */ 150290001Sglebius 151290001Sglebius} TimeObj; 152290001Sglebius 153290001Sglebius/* 154290001Sglebius** NTP Time Object 155290001Sglebius*/ 156290001Sglebiustypedef struct NtpTimeObj { 157290001Sglebius 158290001Sglebius TimeObj timeObj; 159290001Sglebius struct timeval tv; 160290001Sglebius unsigned int refId; 161290001Sglebius 162290001Sglebius} NtpTimeObj; 163290001Sglebius/* 164290001Sglebius** TSYNC Supervisor Reference Object 165290001Sglebius*/ 166290001Sglebiustypedef struct ReferenceObj { 167290001Sglebius 168290001Sglebius char time[TSYNC_REF_LEN]; 169290001Sglebius char pps[TSYNC_REF_LEN]; 170290001Sglebius 171290001Sglebius} ReferenceObj; 172290001Sglebius 173290001Sglebius/* 174290001Sglebius** TSYNC Seconds Time Object 175290001Sglebius*/ 176290001Sglebiustypedef struct SecTimeObj 177290001Sglebius{ 178290001Sglebius unsigned int seconds; 179290001Sglebius unsigned int ns; 180290001Sglebius} 181290001SglebiusSecTimeObj; 182290001Sglebius 183290001Sglebius/* 184290001Sglebius** TSYNC DOY Time Object 185290001Sglebius*/ 186290001Sglebiustypedef struct DoyTimeObj 187290001Sglebius{ 188290001Sglebius unsigned int year; 189290001Sglebius unsigned int doy; 190290001Sglebius unsigned int hour; 191290001Sglebius unsigned int minute; 192290001Sglebius unsigned int second; 193290001Sglebius unsigned int ns; 194290001Sglebius} 195290001SglebiusDoyTimeObj; 196290001Sglebius 197290001Sglebius/* 198290001Sglebius** TSYNC Leap Second Object 199290001Sglebius*/ 200290001Sglebiustypedef struct LeapSecondObj 201290001Sglebius{ 202290001Sglebius int offset; 203290001Sglebius DoyTimeObj utcDate; 204290001Sglebius} 205290001SglebiusLeapSecondObj; 206290001Sglebius 207290001Sglebius/* 208290001Sglebius * structures for ioctl interactions with driver 209290001Sglebius */ 210290001Sglebius#define DI_PAYLOADS_STARTER_LENGTH 4 211290001Sglebiustypedef struct ioctl_trans_di { 212290001Sglebius 213290001Sglebius // input parameters 214290001Sglebius uint16_t dest; 215290001Sglebius uint16_t iid; 216290001Sglebius 217290001Sglebius uint32_t inPayloadOffset; 218290001Sglebius uint32_t inLength; 219290001Sglebius uint32_t outPayloadOffset; 220290001Sglebius uint32_t maxOutLength; 221290001Sglebius 222290001Sglebius // output parameters 223290001Sglebius uint32_t actualOutLength; 224290001Sglebius int32_t status; 225290001Sglebius 226290001Sglebius // Input and output 227290001Sglebius 228290001Sglebius // The payloads field MUST be last in ioctl_trans_di. 229290001Sglebius uint8_t payloads[DI_PAYLOADS_STARTER_LENGTH]; 230290001Sglebius 231290001Sglebius}ioctl_trans_di; 232290001Sglebius 233290001Sglebius/* 234290001Sglebius * structure for looking up a reference ID from a reference name 235290001Sglebius */ 236290001Sglebiustypedef struct 237290001Sglebius{ 238290001Sglebius const char* pRef; // KTS Reference Name 239290001Sglebius const char* pRefId; // NTP Reference ID 240290001Sglebius 241290001Sglebius} RefIdLookup; 242290001Sglebius 243290001Sglebius/* 244290001Sglebius * unit control structure 245290001Sglebius */ 246290001Sglebiustypedef struct { 247290001Sglebius uint32_t refPrefer; // Reference prefer flag 248290001Sglebius uint32_t refId; // Host peer reference ID 249290001Sglebius uint8_t refStratum; // Host peer reference stratum 250290001Sglebius 251290001Sglebius} TsyncUnit; 252290001Sglebius 253290001Sglebius/* 254290001Sglebius** Function prototypes 255290001Sglebius*/ 256290001Sglebiusstatic void tsync_poll (int unit, struct peer *); 257290001Sglebiusstatic void tsync_shutdown (int, struct peer *); 258290001Sglebiusstatic int tsync_start (int, struct peer *); 259290001Sglebius 260290001Sglebius/* 261290001Sglebius** Helper functions 262290001Sglebius*/ 263290001Sglebiusstatic void ApplyTimeOffset (DoyTimeObj* pDt, int off); 264290001Sglebiusstatic void SecTimeFromDoyTime (SecTimeObj* pSt, DoyTimeObj* pDt); 265290001Sglebiusstatic void DoyTimeFromSecTime (DoyTimeObj* pDt, SecTimeObj* pSt); 266290001Sglebius 267290001Sglebius/* 268290001Sglebius** Transfer vector 269290001Sglebius*/ 270290001Sglebiusstruct refclock refclock_tsyncpci = { 271290001Sglebius tsync_start, /* start up driver */ 272290001Sglebius tsync_shutdown, /* shut down driver */ 273290001Sglebius tsync_poll, /* transmit poll message */ 274290001Sglebius noentry, /* not used (old tsync_control) */ 275290001Sglebius noentry, /* initialize driver (not used) */ 276290001Sglebius noentry, /* not used (old tsync_buginfo) */ 277290001Sglebius NOFLAGS /* not used */ 278290001Sglebius}; 279290001Sglebius 280290001Sglebius/* 281290001Sglebius * Reference ID lookup table 282290001Sglebius */ 283290001Sglebiusstatic RefIdLookup RefIdLookupTbl[] = 284290001Sglebius{ 285290001Sglebius {"gps", "GPS"}, 286290001Sglebius {"ir", "IRIG"}, 287290001Sglebius {"hvq", "HVQ"}, 288290001Sglebius {"frq", "FREQ"}, 289290001Sglebius {"mdm", "ACTS"}, 290290001Sglebius {"epp", "PPS"}, 291290001Sglebius {"ptp", "PTP"}, 292290001Sglebius {"asc", "ATC"}, 293290001Sglebius {"hst0", "USER"}, 294290001Sglebius {"hst", TSYNC_REF_LOCAL}, 295290001Sglebius {"self", TSYNC_REF_LOCAL}, 296290001Sglebius {NULL, NULL} 297290001Sglebius}; 298290001Sglebius 299290001Sglebius/******************************************************************************* 300290001Sglebius** IOCTL DEFINITIONS 301290001Sglebius*******************************************************************************/ 302290001Sglebius#define IOCTL_TPRO_ID 't' 303290001Sglebius#define IOCTL_TPRO_OPEN _IOWR(IOCTL_TPRO_ID, 0, BoardObj) 304290001Sglebius#define IOCTL_TPRO_GET_NTP_TIME _IOWR(IOCTL_TPRO_ID, 25, NtpTimeObj) 305290001Sglebius#define IOCTL_TSYNC_GET _IOWR(IOCTL_TPRO_ID, 26, ioctl_trans_di) 306290001Sglebius 307290001Sglebius/****************************************************************************** 308290001Sglebius * 309290001Sglebius * Function: tsync_start() 310290001Sglebius * Description: Used to intialize the Spectracom TSYNC reference driver. 311290001Sglebius * 312290001Sglebius * Parameters: 313290001Sglebius * IN: unit - not used. 314290001Sglebius * *peer - pointer to this reference clock's peer structure 315290001Sglebius * Returns: 0 - unsuccessful 316290001Sglebius * 1 - successful 317290001Sglebius * 318290001Sglebius*******************************************************************************/ 319290001Sglebiusstatic int tsync_start(int unit, struct peer *peer) 320290001Sglebius{ 321290001Sglebius struct refclockproc *pp; 322290001Sglebius TsyncUnit *up; 323290001Sglebius 324290001Sglebius 325290001Sglebius /* 326290001Sglebius ** initialize reference clock and peer parameters 327290001Sglebius */ 328290001Sglebius pp = peer->procptr; 329290001Sglebius pp->clockdesc = DESCRIPTION; 330290001Sglebius pp->io.clock_recv = noentry; 331290001Sglebius pp->io.srcclock = peer; 332290001Sglebius pp->io.datalen = 0; 333290001Sglebius peer->precision = PRECISION; 334290001Sglebius 335290001Sglebius // Allocate and initialize unit structure 336290001Sglebius if (!(up = (TsyncUnit*)emalloc(sizeof(TsyncUnit)))) 337290001Sglebius { 338290001Sglebius return (0); 339290001Sglebius } 340290001Sglebius 341290001Sglebius // Store reference preference 342290001Sglebius up->refPrefer = peer->flags & FLAG_PREFER; 343290001Sglebius 344290001Sglebius // Initialize reference stratum level and ID 345290001Sglebius up->refStratum = STRATUM_UNSPEC; 346290001Sglebius strncpy((char *)&up->refId, TSYNC_REF_LOCAL, TSYNC_REF_LEN); 347290001Sglebius 348290001Sglebius // Attach unit structure 349290001Sglebius pp->unitptr = (caddr_t)up; 350290001Sglebius 351290001Sglebius /* Declare our refId as local in the beginning because we do not know 352290001Sglebius * what our actual refid is yet. 353290001Sglebius */ 354290001Sglebius strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN); 355290001Sglebius 356290001Sglebius return (1); 357290001Sglebius 358290001Sglebius} /* End - tsync_start() */ 359290001Sglebius 360290001Sglebius/******************************************************************************* 361290001Sglebius** 362290001Sglebius** Function: tsync_shutdown() 363290001Sglebius** Description: Handles anything related to shutting down the reference clock 364290001Sglebius** driver. Nothing at this point in time. 365290001Sglebius** 366290001Sglebius** Parameters: 367290001Sglebius** IN: unit - not used. 368290001Sglebius** *peer - pointer to this reference clock's peer structure 369290001Sglebius** Returns: none. 370290001Sglebius** 371290001Sglebius*******************************************************************************/ 372290001Sglebiusstatic void tsync_shutdown(int unit, struct peer *peer) 373290001Sglebius{ 374290001Sglebius 375290001Sglebius} /* End - tsync_shutdown() */ 376290001Sglebius 377290001Sglebius/****************************************************************************** 378290001Sglebius * 379290001Sglebius * Function: tsync_poll() 380290001Sglebius * Description: Retrieve time from the TSYNC device. 381290001Sglebius * 382290001Sglebius * Parameters: 383290001Sglebius * IN: unit - not used. 384290001Sglebius * *peer - pointer to this reference clock's peer structure 385290001Sglebius * Returns: none. 386290001Sglebius * 387290001Sglebius*******************************************************************************/ 388290001Sglebiusstatic void tsync_poll(int unit, struct peer *peer) 389290001Sglebius{ 390290001Sglebius char device[32]; 391290001Sglebius struct refclockproc *pp; 392290001Sglebius struct calendar jt; 393290001Sglebius TsyncUnit *up; 394290001Sglebius unsigned char synch; 395290001Sglebius double seconds; 396290001Sglebius int err; 397290001Sglebius int err1; 398290001Sglebius int err2; 399290001Sglebius int err3; 400290001Sglebius int i; 401290001Sglebius int j; 402290001Sglebius unsigned int itAllocationLength; 403290001Sglebius unsigned int itAllocationLength1; 404290001Sglebius unsigned int itAllocationLength2; 405290001Sglebius NtpTimeObj TimeContext; 406290001Sglebius BoardObj hBoard; 407290001Sglebius char timeRef[TSYNC_REF_LEN + 1]; 408290001Sglebius char ppsRef [TSYNC_REF_LEN + 1]; 409290001Sglebius TIME_SCALE tmscl = TIME_SCALE_UTC; 410290001Sglebius LeapSecondObj leapSec; 411290001Sglebius ioctl_trans_di *it; 412290001Sglebius ioctl_trans_di *it1; 413290001Sglebius ioctl_trans_di *it2; 414290001Sglebius l_fp offset; 415290001Sglebius l_fp ltemp; 416290001Sglebius ReferenceObj * pRefObj; 417290001Sglebius 418290001Sglebius 419290001Sglebius /* Construct the device name */ 420290001Sglebius sprintf(device, "%s%d", DEVICE, (int)peer->refclkunit); 421290001Sglebius 422290001Sglebius printf("Polling device number %d...\n", (int)peer->refclkunit); 423290001Sglebius 424290001Sglebius /* Open the TSYNC device */ 425290001Sglebius hBoard.file_descriptor = open(device, O_RDONLY | O_NDELAY, 0777); 426290001Sglebius 427290001Sglebius /* If error opening TSYNC device... */ 428290001Sglebius if (hBoard.file_descriptor < 0) 429290001Sglebius { 430290001Sglebius msyslog(LOG_ERR, "Couldn't open device"); 431290001Sglebius return; 432290001Sglebius } 433290001Sglebius 434290001Sglebius /* If error while initializing the board... */ 435290001Sglebius if (ioctl(hBoard.file_descriptor, IOCTL_TPRO_OPEN, &hBoard) < 0) 436290001Sglebius { 437290001Sglebius msyslog(LOG_ERR, "Couldn't initialize device"); 438290001Sglebius close(hBoard.file_descriptor); 439290001Sglebius return; 440290001Sglebius } 441290001Sglebius 442290001Sglebius /* Allocate memory for ioctl message */ 443290001Sglebius itAllocationLength = 444290001Sglebius (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) + 445290001Sglebius TSYNC_REF_IN_LEN + TSYNC_REF_MAX_OUT_LEN; 446290001Sglebius 447290001Sglebius it = (ioctl_trans_di*)alloca(itAllocationLength); 448290001Sglebius if (it == NULL) { 449290001Sglebius msyslog(LOG_ERR, "Couldn't allocate transaction memory - Reference"); 450290001Sglebius return; 451290001Sglebius } 452290001Sglebius 453290001Sglebius /* Build SS_GetRef ioctl message */ 454290001Sglebius it->dest = TSYNC_REF_DEST_ID; 455290001Sglebius it->iid = TSYNC_REF_IID; 456290001Sglebius it->inPayloadOffset = TSYNC_REF_IN_PYLD_OFF; 457290001Sglebius it->inLength = TSYNC_REF_IN_LEN; 458290001Sglebius it->outPayloadOffset = TSYNC_REF_OUT_PYLD_OFF; 459290001Sglebius it->maxOutLength = TSYNC_REF_MAX_OUT_LEN; 460290001Sglebius it->actualOutLength = 0; 461290001Sglebius it->status = 0; 462290001Sglebius memset(it->payloads, 0, TSYNC_REF_MAX_OUT_LEN); 463290001Sglebius 464290001Sglebius /* Read the reference from the TSYNC-PCI device */ 465290001Sglebius err = ioctl(hBoard.file_descriptor, 466290001Sglebius IOCTL_TSYNC_GET, 467290001Sglebius (char *)it); 468290001Sglebius 469290001Sglebius /* Allocate memory for ioctl message */ 470290001Sglebius itAllocationLength1 = 471290001Sglebius (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) + 472290001Sglebius TSYNC_TMSCL_IN_LEN + TSYNC_TMSCL_MAX_OUT_LEN; 473290001Sglebius 474290001Sglebius it1 = (ioctl_trans_di*)alloca(itAllocationLength1); 475290001Sglebius if (it1 == NULL) { 476290001Sglebius msyslog(LOG_ERR, "Couldn't allocate transaction memory - Time Scale"); 477290001Sglebius return; 478290001Sglebius } 479290001Sglebius 480290001Sglebius /* Build CS_GetTimeScale ioctl message */ 481290001Sglebius it1->dest = TSYNC_TMSCL_DEST_ID; 482290001Sglebius it1->iid = TSYNC_TMSCL_IID; 483290001Sglebius it1->inPayloadOffset = TSYNC_TMSCL_IN_PYLD_OFF; 484290001Sglebius it1->inLength = TSYNC_TMSCL_IN_LEN; 485290001Sglebius it1->outPayloadOffset = TSYNC_TMSCL_OUT_PYLD_OFF; 486290001Sglebius it1->maxOutLength = TSYNC_TMSCL_MAX_OUT_LEN; 487290001Sglebius it1->actualOutLength = 0; 488290001Sglebius it1->status = 0; 489290001Sglebius memset(it1->payloads, 0, TSYNC_TMSCL_MAX_OUT_LEN); 490290001Sglebius 491290001Sglebius /* Read the Time Scale info from the TSYNC-PCI device */ 492290001Sglebius err1 = ioctl(hBoard.file_descriptor, 493290001Sglebius IOCTL_TSYNC_GET, 494290001Sglebius (char *)it1); 495290001Sglebius 496290001Sglebius /* Allocate memory for ioctl message */ 497290001Sglebius itAllocationLength2 = 498290001Sglebius (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) + 499290001Sglebius TSYNC_LEAP_IN_LEN + TSYNC_LEAP_MAX_OUT_LEN; 500290001Sglebius 501290001Sglebius it2 = (ioctl_trans_di*)alloca(itAllocationLength2); 502290001Sglebius if (it2 == NULL) { 503290001Sglebius msyslog(LOG_ERR, "Couldn't allocate transaction memory - Leap Second"); 504290001Sglebius return; 505290001Sglebius } 506290001Sglebius 507290001Sglebius /* Build CS_GetLeapSec ioctl message */ 508290001Sglebius it2->dest = TSYNC_LEAP_DEST_ID; 509290001Sglebius it2->iid = TSYNC_LEAP_IID; 510290001Sglebius it2->inPayloadOffset = TSYNC_LEAP_IN_PYLD_OFF; 511290001Sglebius it2->inLength = TSYNC_LEAP_IN_LEN; 512290001Sglebius it2->outPayloadOffset = TSYNC_LEAP_OUT_PYLD_OFF; 513290001Sglebius it2->maxOutLength = TSYNC_LEAP_MAX_OUT_LEN; 514290001Sglebius it2->actualOutLength = 0; 515290001Sglebius it2->status = 0; 516290001Sglebius memset(it2->payloads, 0, TSYNC_LEAP_MAX_OUT_LEN); 517290001Sglebius 518290001Sglebius /* Read the leap seconds info from the TSYNC-PCI device */ 519290001Sglebius err2 = ioctl(hBoard.file_descriptor, 520290001Sglebius IOCTL_TSYNC_GET, 521290001Sglebius (char *)it2); 522290001Sglebius 523290001Sglebius pp = peer->procptr; 524290001Sglebius up = (TsyncUnit*)pp->unitptr; 525290001Sglebius 526290001Sglebius /* Read the time from the TSYNC-PCI device */ 527290001Sglebius err3 = ioctl(hBoard.file_descriptor, 528290001Sglebius IOCTL_TPRO_GET_NTP_TIME, 529290001Sglebius (char *)&TimeContext); 530290001Sglebius 531290001Sglebius /* Close the TSYNC device */ 532290001Sglebius close(hBoard.file_descriptor); 533290001Sglebius 534290001Sglebius // Check for errors 535290001Sglebius if ((err < 0) ||(err1 < 0) || (err2 < 0) || (err3 < 0) || 536290001Sglebius (it->status != 0) || (it1->status != 0) || (it2->status != 0) || 537290001Sglebius (it->actualOutLength != TSYNC_REF_OUT_LEN) || 538290001Sglebius (it1->actualOutLength != TSYNC_TMSCL_OUT_LEN) || 539290001Sglebius (it2->actualOutLength != TSYNC_LEAP_OUT_LEN)) { 540290001Sglebius refclock_report(peer, CEVNT_FAULT); 541290001Sglebius return; 542290001Sglebius } 543290001Sglebius 544290001Sglebius // Extract reference identifiers from ioctl payload 545290001Sglebius memset(timeRef, '\0', sizeof(timeRef)); 546290001Sglebius memset(ppsRef, '\0', sizeof(ppsRef)); 547290001Sglebius pRefObj = (void *)it->payloads; 548290001Sglebius memcpy(timeRef, pRefObj->time, TSYNC_REF_LEN); 549290001Sglebius memcpy(ppsRef, pRefObj->pps, TSYNC_REF_LEN); 550290001Sglebius 551290001Sglebius // Extract the Clock Service Time Scale and convert to correct byte order 552293896Sglebius memcpy(&tmscl, it1->payloads, sizeof(tmscl)); 553290001Sglebius tmscl = ntohl(tmscl); 554290001Sglebius 555290001Sglebius // Extract leap second info from ioctl payload and perform byte swapping 556290001Sglebius for (i = 0; i < (sizeof(leapSec) / 4); i++) 557290001Sglebius { 558290001Sglebius for (j = 0; j < 4; j++) 559290001Sglebius { 560290001Sglebius ((unsigned char*)&leapSec)[(i * 4) + j] = 561290001Sglebius ((unsigned char*)(it2->payloads))[(i * 4) + (3 - j)]; 562290001Sglebius } 563290001Sglebius } 564290001Sglebius 565290001Sglebius // Determine time reference ID from reference name 566290001Sglebius for (i = 0; RefIdLookupTbl[i].pRef != NULL; i++) 567290001Sglebius { 568290001Sglebius // Search RefID table 569290001Sglebius if (strstr(timeRef, RefIdLookupTbl[i].pRef) != NULL) 570290001Sglebius { 571290001Sglebius // Found the matching string 572290001Sglebius break; 573290001Sglebius } 574290001Sglebius } 575290001Sglebius 576290001Sglebius // Determine pps reference ID from reference name 577290001Sglebius for (j = 0; RefIdLookupTbl[j].pRef != NULL; j++) 578290001Sglebius { 579290001Sglebius // Search RefID table 580290001Sglebius if (strstr(ppsRef, RefIdLookupTbl[j].pRef) != NULL) 581290001Sglebius { 582290001Sglebius // Found the matching string 583290001Sglebius break; 584290001Sglebius } 585290001Sglebius } 586290001Sglebius 587290001Sglebius // Determine synchronization state from flags 588290001Sglebius synch = (TimeContext.timeObj.flags == 0x4) ? 1 : 0; 589290001Sglebius 590290001Sglebius // Pull seconds information from time object 591290001Sglebius seconds = (double) (TimeContext.timeObj.secsDouble); 592290001Sglebius seconds /= (double) 1000000.0; 593290001Sglebius 594290001Sglebius /* 595290001Sglebius ** Convert the number of microseconds to double and then place in the 596290001Sglebius ** peer's last received long floating point format. 597290001Sglebius */ 598290001Sglebius DTOLFP(((double)TimeContext.tv.tv_usec / 1000000.0), &pp->lastrec); 599290001Sglebius 600290001Sglebius /* 601290001Sglebius ** The specTimeStamp is the number of seconds since 1/1/1970, while the 602290001Sglebius ** peer's lastrec time should be compatible with NTP which is seconds since 603290001Sglebius ** 1/1/1900. So Add the number of seconds between 1900 and 1970 to the 604290001Sglebius ** specTimeStamp and place in the peer's lastrec long floating point struct. 605290001Sglebius */ 606290001Sglebius pp->lastrec.Ul_i.Xl_ui += (unsigned int)TimeContext.tv.tv_sec + 607290001Sglebius SECONDS_1900_TO_1970; 608290001Sglebius 609290001Sglebius pp->polls++; 610290001Sglebius 611290001Sglebius /* 612290001Sglebius ** set the reference clock object 613290001Sglebius */ 614290001Sglebius sprintf(pp->a_lastcode, "%03d %02d:%02d:%02.6f", 615290001Sglebius TimeContext.timeObj.days, TimeContext.timeObj.hours, 616290001Sglebius TimeContext.timeObj.minutes, seconds); 617290001Sglebius 618290001Sglebius pp->lencode = strlen (pp->a_lastcode); 619290001Sglebius pp->day = TimeContext.timeObj.days; 620290001Sglebius pp->hour = TimeContext.timeObj.hours; 621290001Sglebius pp->minute = TimeContext.timeObj.minutes; 622290001Sglebius pp->second = (int) seconds; 623290001Sglebius seconds = (seconds - (double) (pp->second / 1.0)) * 1000000000; 624290001Sglebius pp->nsec = (long) seconds; 625290001Sglebius 626290001Sglebius /* 627290001Sglebius ** calculate year start 628290001Sglebius */ 629290001Sglebius jt.year = TimeContext.timeObj.year; 630290001Sglebius jt.yearday = 1; 631290001Sglebius jt.monthday = 1; 632290001Sglebius jt.month = 1; 633290001Sglebius jt.hour = 0; 634290001Sglebius jt.minute = 0; 635290001Sglebius jt.second = 0; 636290001Sglebius pp->yearstart = caltontp(&jt); 637290001Sglebius 638290001Sglebius // Calculate and report reference clock offset 639290001Sglebius offset.l_ui = (long)(((pp->day - 1) * 24) + pp->hour + GMT); 640290001Sglebius offset.l_ui = (offset.l_ui * 60) + (long)pp->minute; 641290001Sglebius offset.l_ui = (offset.l_ui * 60) + (long)pp->second; 642290001Sglebius offset.l_ui = offset.l_ui + (long)pp->yearstart; 643290001Sglebius offset.l_uf = 0; 644290001Sglebius DTOLFP(pp->nsec / 1e9, <emp); 645290001Sglebius L_ADD(&offset, <emp); 646290001Sglebius refclock_process_offset(pp, offset, pp->lastrec, 647290001Sglebius pp->fudgetime1); 648290001Sglebius 649290001Sglebius // KTS in sync 650290001Sglebius if (synch) { 651290001Sglebius // Subtract leap second info by one second to determine effective day 652290001Sglebius ApplyTimeOffset(&(leapSec.utcDate), -1); 653290001Sglebius 654290001Sglebius // If there is a leap second today and the KTS is using a time scale 655290001Sglebius // which handles leap seconds then 656290001Sglebius if ((tmscl != TIME_SCALE_GPS) && (tmscl != TIME_SCALE_TAI) && 657290001Sglebius (leapSec.utcDate.year == (unsigned int)TimeContext.timeObj.year) && 658290001Sglebius (leapSec.utcDate.doy == (unsigned int)TimeContext.timeObj.days)) 659290001Sglebius { 660290001Sglebius // If adding a second 661290001Sglebius if (leapSec.offset == 1) 662290001Sglebius { 663290001Sglebius pp->leap = LEAP_ADDSECOND; 664290001Sglebius } 665290001Sglebius // Else if removing a second 666290001Sglebius else if (leapSec.offset == -1) 667290001Sglebius { 668290001Sglebius pp->leap = LEAP_DELSECOND; 669290001Sglebius } 670290001Sglebius // Else report no leap second pending (no handling of offsets 671290001Sglebius // other than +1 or -1) 672290001Sglebius else 673290001Sglebius { 674290001Sglebius pp->leap = LEAP_NOWARNING; 675290001Sglebius } 676290001Sglebius } 677290001Sglebius // Else report no leap second pending 678290001Sglebius else 679290001Sglebius { 680290001Sglebius pp->leap = LEAP_NOWARNING; 681290001Sglebius } 682290001Sglebius 683290001Sglebius peer->leap = pp->leap; 684290001Sglebius refclock_report(peer, CEVNT_NOMINAL); 685290001Sglebius 686290001Sglebius // If reference name reported, then not in holdover 687290001Sglebius if ((RefIdLookupTbl[i].pRef != NULL) && 688290001Sglebius (RefIdLookupTbl[j].pRef != NULL)) 689290001Sglebius { 690290001Sglebius // Determine if KTS being synchronized by host (identified as 691290001Sglebius // "LOCL") 692290001Sglebius if ((strcmp(RefIdLookupTbl[i].pRefId, TSYNC_REF_LOCAL) == 0) || 693290001Sglebius (strcmp(RefIdLookupTbl[j].pRefId, TSYNC_REF_LOCAL) == 0)) 694290001Sglebius { 695290001Sglebius // Clear prefer flag 696290001Sglebius peer->flags &= ~FLAG_PREFER; 697290001Sglebius 698290001Sglebius // Set reference clock stratum level as unusable 699290001Sglebius pp->stratum = STRATUM_UNSPEC; 700290001Sglebius peer->stratum = pp->stratum; 701290001Sglebius 702290001Sglebius // If a valid peer is available 703290001Sglebius if ((sys_peer != NULL) && (sys_peer != peer)) 704290001Sglebius { 705290001Sglebius // Store reference peer stratum level and ID 706290001Sglebius up->refStratum = sys_peer->stratum; 707290001Sglebius up->refId = addr2refid(&sys_peer->srcadr); 708290001Sglebius } 709290001Sglebius } 710290001Sglebius else 711290001Sglebius { 712290001Sglebius // Restore prefer flag 713290001Sglebius peer->flags |= up->refPrefer; 714290001Sglebius 715290001Sglebius // Store reference stratum as local clock 716290001Sglebius up->refStratum = TSYNC_LCL_STRATUM; 717290001Sglebius strncpy((char *)&up->refId, RefIdLookupTbl[j].pRefId, 718290001Sglebius TSYNC_REF_LEN); 719290001Sglebius 720290001Sglebius // Set reference clock stratum level as local clock 721290001Sglebius pp->stratum = TSYNC_LCL_STRATUM; 722290001Sglebius peer->stratum = pp->stratum; 723290001Sglebius } 724290001Sglebius 725290001Sglebius // Update reference name 726290001Sglebius strncpy((char *)&pp->refid, RefIdLookupTbl[j].pRefId, 727290001Sglebius TSYNC_REF_LEN); 728290001Sglebius peer->refid = pp->refid; 729290001Sglebius } 730290001Sglebius // Else in holdover 731290001Sglebius else 732290001Sglebius { 733290001Sglebius // Restore prefer flag 734290001Sglebius peer->flags |= up->refPrefer; 735290001Sglebius 736290001Sglebius // Update reference ID to saved ID 737290001Sglebius pp->refid = up->refId; 738290001Sglebius peer->refid = pp->refid; 739290001Sglebius 740290001Sglebius // Update stratum level to saved stratum level 741290001Sglebius pp->stratum = up->refStratum; 742290001Sglebius peer->stratum = pp->stratum; 743290001Sglebius } 744290001Sglebius } 745290001Sglebius // Else KTS not in sync 746290001Sglebius else { 747290001Sglebius // Place local identifier in peer RefID 748290001Sglebius strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN); 749290001Sglebius peer->refid = pp->refid; 750290001Sglebius 751290001Sglebius // Report not in sync 752290001Sglebius pp->leap = LEAP_NOTINSYNC; 753290001Sglebius peer->leap = pp->leap; 754290001Sglebius } 755290001Sglebius 756290001Sglebius if (pp->coderecv == pp->codeproc) { 757290001Sglebius refclock_report(peer, CEVNT_TIMEOUT); 758290001Sglebius return; 759290001Sglebius } 760290001Sglebius 761290001Sglebius record_clock_stats(&peer->srcadr, pp->a_lastcode); 762290001Sglebius refclock_receive(peer); 763290001Sglebius 764290001Sglebius /* Increment the number of times the reference has been polled */ 765290001Sglebius pp->polls++; 766290001Sglebius 767290001Sglebius} /* End - tsync_poll() */ 768290001Sglebius 769290001Sglebius 770290001Sglebius//////////////////////////////////////////////////////////////////////////////// 771290001Sglebius// Function: ApplyTimeOffset 772290001Sglebius// Description: The ApplyTimeOffset function adds an offset (in seconds) to a 773290001Sglebius// specified date and time. The specified date and time is passed 774290001Sglebius// back after being modified. 775290001Sglebius// 776290001Sglebius// Assumptions: 1. Every fourth year is a leap year. Therefore, this function 777290001Sglebius// is only accurate through Feb 28, 2100. 778290001Sglebius//////////////////////////////////////////////////////////////////////////////// 779290001Sglebiusvoid ApplyTimeOffset(DoyTimeObj* pDt, int off) 780290001Sglebius{ 781290001Sglebius SecTimeObj st; // Time, in seconds 782290001Sglebius 783290001Sglebius 784290001Sglebius // Convert date and time to seconds 785290001Sglebius SecTimeFromDoyTime(&st, pDt); 786290001Sglebius 787290001Sglebius // Apply offset 788290001Sglebius st.seconds = (int)((signed long long)st.seconds + (signed long long)off); 789290001Sglebius 790290001Sglebius // Convert seconds to date and time 791290001Sglebius DoyTimeFromSecTime(pDt, &st); 792290001Sglebius 793290001Sglebius} // End ApplyTimeOffset 794290001Sglebius 795290001Sglebius 796290001Sglebius//////////////////////////////////////////////////////////////////////////////// 797290001Sglebius// Function: SecTimeFromDoyTime 798290001Sglebius// Description: The SecTimeFromDoyTime function converts a specified date 799290001Sglebius// and time into a count of seconds since the base time. This 800290001Sglebius// function operates across the range Base Time to Max Time for 801290001Sglebius// the system. 802290001Sglebius// 803290001Sglebius// Assumptions: 1. A leap year is any year evenly divisible by 4. Therefore, 804290001Sglebius// this function is only accurate through Feb 28, 2100. 805290001Sglebius// 2. Conversion does not account for leap seconds. 806290001Sglebius//////////////////////////////////////////////////////////////////////////////// 807290001Sglebiusvoid SecTimeFromDoyTime(SecTimeObj* pSt, DoyTimeObj* pDt) 808290001Sglebius{ 809290001Sglebius unsigned int yrs; // Years 810290001Sglebius unsigned int lyrs; // Leap years 811290001Sglebius 812290001Sglebius 813290001Sglebius // Start with accumulated time of 0 814290001Sglebius pSt->seconds = 0; 815290001Sglebius 816290001Sglebius // Calculate the number of years and leap years 817290001Sglebius yrs = pDt->year - TSYNC_TIME_BASE_YEAR; 818290001Sglebius lyrs = (yrs + 1) / 4; 819290001Sglebius 820290001Sglebius // Convert leap years and years 821290001Sglebius pSt->seconds += lyrs * SECSPERLEAPYEAR; 822290001Sglebius pSt->seconds += (yrs - lyrs) * SECSPERYEAR; 823290001Sglebius 824290001Sglebius // Convert days, hours, minutes and seconds 825290001Sglebius pSt->seconds += (pDt->doy - 1) * SECSPERDAY; 826290001Sglebius pSt->seconds += pDt->hour * SECSPERHR; 827290001Sglebius pSt->seconds += pDt->minute * SECSPERMIN; 828290001Sglebius pSt->seconds += pDt->second; 829290001Sglebius 830290001Sglebius // Copy the subseconds count 831290001Sglebius pSt->ns = pDt->ns; 832290001Sglebius 833290001Sglebius} // End SecTimeFromDoyTime 834290001Sglebius 835290001Sglebius 836290001Sglebius//////////////////////////////////////////////////////////////////////////////// 837290001Sglebius// Function: DoyTimeFromSecTime 838290001Sglebius// Description: The DoyTimeFromSecTime function converts a specified count 839290001Sglebius// of seconds since the start of our base time into a SecTimeObj 840290001Sglebius// structure. 841290001Sglebius// 842290001Sglebius// Assumptions: 1. A leap year is any year evenly divisible by 4. Therefore, 843290001Sglebius// this function is only accurate through Feb 28, 2100. 844290001Sglebius// 2. Conversion does not account for leap seconds. 845290001Sglebius//////////////////////////////////////////////////////////////////////////////// 846290001Sglebiusvoid DoyTimeFromSecTime(DoyTimeObj* pDt, SecTimeObj* pSt) 847290001Sglebius{ 848290001Sglebius signed long long secs; // Seconds accumulator variable 849290001Sglebius unsigned int yrs; // Years accumulator variable 850290001Sglebius unsigned int doys; // Days accumulator variable 851290001Sglebius unsigned int hrs; // Hours accumulator variable 852290001Sglebius unsigned int mins; // Minutes accumulator variable 853290001Sglebius 854290001Sglebius 855290001Sglebius // Convert the seconds count into a signed 64-bit number for calculations 856290001Sglebius secs = (signed long long)(pSt->seconds); 857290001Sglebius 858290001Sglebius // Calculate the number of 4 year chunks 859290001Sglebius yrs = (unsigned int)((secs / 860290001Sglebius ((SECSPERYEAR * 3) + SECSPERLEAPYEAR)) * 4); 861290001Sglebius secs %= ((SECSPERYEAR * 3) + SECSPERLEAPYEAR); 862290001Sglebius 863290001Sglebius // If there is at least a normal year worth of time left 864290001Sglebius if (secs >= SECSPERYEAR) 865290001Sglebius { 866290001Sglebius // Increment the number of years and subtract a normal year of time 867290001Sglebius yrs++; 868290001Sglebius secs -= SECSPERYEAR; 869290001Sglebius } 870290001Sglebius 871290001Sglebius // If there is still at least a normal year worth of time left 872290001Sglebius if (secs >= SECSPERYEAR) 873290001Sglebius { 874290001Sglebius // Increment the number of years and subtract a normal year of time 875290001Sglebius yrs++; 876290001Sglebius secs -= SECSPERYEAR; 877290001Sglebius } 878290001Sglebius 879290001Sglebius // If there is still at least a leap year worth of time left 880290001Sglebius if (secs >= SECSPERLEAPYEAR) 881290001Sglebius { 882290001Sglebius // Increment the number of years and subtract a leap year of time 883290001Sglebius yrs++; 884290001Sglebius secs -= SECSPERLEAPYEAR; 885290001Sglebius } 886290001Sglebius 887290001Sglebius // Calculate the day of year as the number of days left, then add 1 888290001Sglebius // because months start on the 1st. 889290001Sglebius doys = (unsigned int)((secs / SECSPERDAY) + 1); 890290001Sglebius secs %= SECSPERDAY; 891290001Sglebius 892290001Sglebius // Calculate the hour 893290001Sglebius hrs = (unsigned int)(secs / SECSPERHR); 894290001Sglebius secs %= SECSPERHR; 895290001Sglebius 896290001Sglebius // Calculate the minute 897290001Sglebius mins = (unsigned int)(secs / SECSPERMIN); 898290001Sglebius secs %= SECSPERMIN; 899290001Sglebius 900290001Sglebius // Fill in the doytime structure 901290001Sglebius pDt->year = yrs + TSYNC_TIME_BASE_YEAR; 902290001Sglebius pDt->doy = doys; 903290001Sglebius pDt->hour = hrs; 904290001Sglebius pDt->minute = mins; 905290001Sglebius pDt->second = (unsigned int)secs; 906290001Sglebius pDt->ns = pSt->ns; 907290001Sglebius 908290001Sglebius} // End DoyTimeFromSecTime 909290001Sglebius 910290001Sglebius#else 911290001Sglebiusint refclock_tsyncpci_bs; 912290001Sglebius#endif /* REFCLOCK */ 913