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