1*eabc0478Schristos /* $NetBSD: refclock_tsyncpci.c,v 1.6 2024/08/18 20:47:19 christos Exp $ */ 28585484eSchristos 38585484eSchristos /******************************************************************************* 48585484eSchristos * 58585484eSchristos * Module : refclock_tsyncpci.c 68585484eSchristos * Date : 09/08/08 78585484eSchristos * Purpose : Implements a reference clock driver for the NTP daemon. This 88585484eSchristos * reference clock driver provides a means to communicate with 98585484eSchristos * the Spectracom TSYNC PCI timing devices and use them as a time 108585484eSchristos * source. 118585484eSchristos * 128585484eSchristos * (C) Copyright 2008 Spectracom Corporation 138585484eSchristos * 148585484eSchristos * This software is provided by Spectracom Corporation 'as is' and 158585484eSchristos * any express or implied warranties, including, but not limited to, the 168585484eSchristos * implied warranties of merchantability and fitness for a particular purpose 178585484eSchristos * are disclaimed. In no event shall Spectracom Corporation be liable 188585484eSchristos * for any direct, indirect, incidental, special, exemplary, or consequential 198585484eSchristos * damages (including, but not limited to, procurement of substitute goods 208585484eSchristos * or services; loss of use, data, or profits; or business interruption) 218585484eSchristos * however caused and on any theory of liability, whether in contract, strict 228585484eSchristos * liability, or tort (including negligence or otherwise) arising in any way 238585484eSchristos * out of the use of this software, even if advised of the possibility of 248585484eSchristos * such damage. 258585484eSchristos * 268585484eSchristos * This software is released for distribution according to the NTP copyright 278585484eSchristos * and license contained in html/copyright.html of NTP source. 288585484eSchristos * 298585484eSchristos *******************************************************************************/ 308585484eSchristos #ifdef HAVE_CONFIG_H 318585484eSchristos #include <config.h> 328585484eSchristos #endif 338585484eSchristos 348585484eSchristos #if defined(REFCLOCK) && defined(CLOCK_TSYNCPCI) 358585484eSchristos 368585484eSchristos #include <asm/ioctl.h> 378585484eSchristos #ifdef HAVE_SYS_IOCTL_H 388585484eSchristos # include <sys/ioctl.h> 398585484eSchristos #endif 408585484eSchristos 418585484eSchristos #include <stdio.h> 428585484eSchristos #include <ctype.h> 438585484eSchristos #include <netinet/in.h> 448585484eSchristos 458585484eSchristos 468585484eSchristos #include "ntpd.h" 478585484eSchristos #include "ntp_io.h" 488585484eSchristos #include "ntp_refclock.h" 498585484eSchristos #include "ntp_unixtime.h" 508585484eSchristos #include "ntp_stdlib.h" 518585484eSchristos #include "ntp_calendar.h" 528585484eSchristos 538585484eSchristos 548585484eSchristos /******************************************************************************* 558585484eSchristos ** 568585484eSchristos ** This driver supports the Spectracom TSYNC PCI GPS receiver. It requires 578585484eSchristos ** that the tsyncpci.o device driver be installed and loaded. 588585484eSchristos ** 598585484eSchristos *******************************************************************************/ 608585484eSchristos 618585484eSchristos #define TSYNC_PCI_REVISION "1.11" 628585484eSchristos 638585484eSchristos /* 648585484eSchristos ** TPRO interface definitions 658585484eSchristos */ 668585484eSchristos #define DEVICE "/dev/tsyncpci" /* device name */ 678585484eSchristos #define PRECISION (-20) /* precision assumed (1 us) */ 688585484eSchristos #define DESCRIPTION "Spectracom TSYNC-PCI" /* WRU */ 698585484eSchristos 708585484eSchristos #define SECONDS_1900_TO_1970 (2208988800U) 718585484eSchristos 728585484eSchristos #define TSYNC_REF_IID (0x2500) // SS CAI, REF IID 738585484eSchristos #define TSYNC_REF_DEST_ID (0x0001) // KTS Firmware 748585484eSchristos #define TSYNC_REF_IN_PYLD_OFF (0) 758585484eSchristos #define TSYNC_REF_IN_LEN (0) 768585484eSchristos #define TSYNC_REF_OUT_PYLD_OFF (0) 778585484eSchristos #define TSYNC_REF_OUT_LEN (8) 788585484eSchristos #define TSYNC_REF_MAX_OUT_LEN (16) 798585484eSchristos #define TSYNC_REF_PYLD_LEN (TSYNC_REF_IN_LEN + \ 808585484eSchristos TSYNC_REF_MAX_OUT_LEN) 818585484eSchristos #define TSYNC_REF_LEN (4) 828585484eSchristos #define TSYNC_REF_LOCAL ("LOCL") 838585484eSchristos 848585484eSchristos #define TSYNC_TMSCL_IID (0x2301) // CS CAI, TIMESCALE IID 858585484eSchristos #define TSYNC_TMSCL_DEST_ID (0x0001) // KTS Firmware 868585484eSchristos #define TSYNC_TMSCL_IN_PYLD_OFF (0) 878585484eSchristos #define TSYNC_TMSCL_IN_LEN (0) 888585484eSchristos #define TSYNC_TMSCL_OUT_PYLD_OFF (0) 898585484eSchristos #define TSYNC_TMSCL_OUT_LEN (4) 908585484eSchristos #define TSYNC_TMSCL_MAX_OUT_LEN (12) 918585484eSchristos #define TSYNC_TMSCL_PYLD_LEN (TSYNC_TMSCL_IN_LEN + \ 928585484eSchristos TSYNC_TMSCL_MAX_OUT_LEN) 938585484eSchristos 948585484eSchristos #define TSYNC_LEAP_IID (0x2307) // CS CAI, LEAP SEC IID 958585484eSchristos #define TSYNC_LEAP_DEST_ID (0x0001) // KTS Firmware 968585484eSchristos #define TSYNC_LEAP_IN_PYLD_OFF (0) 978585484eSchristos #define TSYNC_LEAP_IN_LEN (0) 988585484eSchristos #define TSYNC_LEAP_OUT_PYLD_OFF (0) 998585484eSchristos #define TSYNC_LEAP_OUT_LEN (28) 1008585484eSchristos #define TSYNC_LEAP_MAX_OUT_LEN (36) 1018585484eSchristos #define TSYNC_LEAP_PYLD_LEN (TSYNC_LEAP_IN_LEN + \ 1028585484eSchristos TSYNC_LEAP_MAX_OUT_LEN) 1038585484eSchristos 1048585484eSchristos // These define the base date/time of the system clock. The system time will 1058585484eSchristos // be tracked as the number of seconds from this date/time. 1068585484eSchristos #define TSYNC_TIME_BASE_YEAR (1970) // earliest acceptable year 1078585484eSchristos 1088585484eSchristos #define TSYNC_LCL_STRATUM (0) 1098585484eSchristos 1108585484eSchristos /* 1118585484eSchristos ** TSYNC Time Scales type 1128585484eSchristos */ 1138585484eSchristos typedef enum 1148585484eSchristos { 1158585484eSchristos TIME_SCALE_UTC = 0, // Universal Coordinated Time 1168585484eSchristos TIME_SCALE_TAI = 1, // International Atomic Time 1178585484eSchristos TIME_SCALE_GPS = 2, // Global Positioning System 1188585484eSchristos TIME_SCALE_LOCAL = 3, // UTC w/local rules for time zone and DST 1198585484eSchristos NUM_TIME_SCALES = 4, // Number of time scales 1208585484eSchristos 1218585484eSchristos TIME_SCALE_MAX = 15 // Maximum number of timescales 1228585484eSchristos 1238585484eSchristos } TIME_SCALE; 1248585484eSchristos 1258585484eSchristos /* 1268585484eSchristos ** TSYNC Board Object 1278585484eSchristos */ 1288585484eSchristos typedef struct BoardObj { 1298585484eSchristos 1308585484eSchristos int file_descriptor; 1318585484eSchristos unsigned short devid; 1328585484eSchristos unsigned short options; 1338585484eSchristos unsigned char firmware[5]; 1348585484eSchristos unsigned char FPGA[5]; 1358585484eSchristos unsigned char driver[7]; 1368585484eSchristos 1378585484eSchristos } BoardObj; 1388585484eSchristos 1398585484eSchristos /* 1408585484eSchristos ** TSYNC Time Object 1418585484eSchristos */ 1428585484eSchristos typedef struct TimeObj { 1438585484eSchristos 1448585484eSchristos unsigned char syncOption; /* -M option */ 1458585484eSchristos unsigned int secsDouble; /* seconds floating pt */ 1468585484eSchristos unsigned char seconds; /* seconds whole num */ 1478585484eSchristos unsigned char minutes; 1488585484eSchristos unsigned char hours; 1498585484eSchristos unsigned short days; 1508585484eSchristos unsigned short year; 1518585484eSchristos unsigned short flags; /* bit 2 SYNC, bit 1 TCODE; all others 0 */ 1528585484eSchristos 1538585484eSchristos } TimeObj; 1548585484eSchristos 1558585484eSchristos /* 1568585484eSchristos ** NTP Time Object 1578585484eSchristos */ 1588585484eSchristos typedef struct NtpTimeObj { 1598585484eSchristos 1608585484eSchristos TimeObj timeObj; 1618585484eSchristos struct timeval tv; 1628585484eSchristos unsigned int refId; 1638585484eSchristos 1648585484eSchristos } NtpTimeObj; 1658585484eSchristos /* 1668585484eSchristos ** TSYNC Supervisor Reference Object 1678585484eSchristos */ 1688585484eSchristos typedef struct ReferenceObj { 1698585484eSchristos 1708585484eSchristos char time[TSYNC_REF_LEN]; 1718585484eSchristos char pps[TSYNC_REF_LEN]; 1728585484eSchristos 1738585484eSchristos } ReferenceObj; 1748585484eSchristos 1758585484eSchristos /* 1768585484eSchristos ** TSYNC Seconds Time Object 1778585484eSchristos */ 1788585484eSchristos typedef struct SecTimeObj 1798585484eSchristos { 1808585484eSchristos unsigned int seconds; 1818585484eSchristos unsigned int ns; 1828585484eSchristos } 1838585484eSchristos SecTimeObj; 1848585484eSchristos 1858585484eSchristos /* 1868585484eSchristos ** TSYNC DOY Time Object 1878585484eSchristos */ 1888585484eSchristos typedef struct DoyTimeObj 1898585484eSchristos { 1908585484eSchristos unsigned int year; 1918585484eSchristos unsigned int doy; 1928585484eSchristos unsigned int hour; 1938585484eSchristos unsigned int minute; 1948585484eSchristos unsigned int second; 1958585484eSchristos unsigned int ns; 1968585484eSchristos } 1978585484eSchristos DoyTimeObj; 1988585484eSchristos 1998585484eSchristos /* 2008585484eSchristos ** TSYNC Leap Second Object 2018585484eSchristos */ 2028585484eSchristos typedef struct LeapSecondObj 2038585484eSchristos { 2048585484eSchristos int offset; 2058585484eSchristos DoyTimeObj utcDate; 2068585484eSchristos } 2078585484eSchristos LeapSecondObj; 2088585484eSchristos 2098585484eSchristos /* 2108585484eSchristos * structures for ioctl interactions with driver 2118585484eSchristos */ 2128585484eSchristos #define DI_PAYLOADS_STARTER_LENGTH 4 2138585484eSchristos typedef struct ioctl_trans_di { 2148585484eSchristos 2158585484eSchristos // input parameters 2168585484eSchristos uint16_t dest; 2178585484eSchristos uint16_t iid; 2188585484eSchristos 2198585484eSchristos uint32_t inPayloadOffset; 2208585484eSchristos uint32_t inLength; 2218585484eSchristos uint32_t outPayloadOffset; 2228585484eSchristos uint32_t maxOutLength; 2238585484eSchristos 2248585484eSchristos // output parameters 2258585484eSchristos uint32_t actualOutLength; 2268585484eSchristos int32_t status; 2278585484eSchristos 2288585484eSchristos // Input and output 2298585484eSchristos 2308585484eSchristos // The payloads field MUST be last in ioctl_trans_di. 2318585484eSchristos uint8_t payloads[DI_PAYLOADS_STARTER_LENGTH]; 2328585484eSchristos 2338585484eSchristos }ioctl_trans_di; 2348585484eSchristos 2358585484eSchristos /* 2368585484eSchristos * structure for looking up a reference ID from a reference name 2378585484eSchristos */ 2388585484eSchristos typedef struct 2398585484eSchristos { 2408585484eSchristos const char* pRef; // KTS Reference Name 2418585484eSchristos const char* pRefId; // NTP Reference ID 2428585484eSchristos 2438585484eSchristos } RefIdLookup; 2448585484eSchristos 2458585484eSchristos /* 2468585484eSchristos * unit control structure 2478585484eSchristos */ 2488585484eSchristos typedef struct { 2498585484eSchristos uint32_t refPrefer; // Reference prefer flag 2508585484eSchristos uint32_t refId; // Host peer reference ID 2518585484eSchristos uint8_t refStratum; // Host peer reference stratum 2528585484eSchristos 2538585484eSchristos } TsyncUnit; 2548585484eSchristos 2558585484eSchristos /* 2568585484eSchristos ** Function prototypes 2578585484eSchristos */ 2588585484eSchristos static void tsync_poll (int unit, struct peer *); 2598585484eSchristos static void tsync_shutdown (int, struct peer *); 2608585484eSchristos static int tsync_start (int, struct peer *); 2618585484eSchristos 2628585484eSchristos /* 2638585484eSchristos ** Helper functions 2648585484eSchristos */ 2658585484eSchristos static void ApplyTimeOffset (DoyTimeObj* pDt, int off); 2668585484eSchristos static void SecTimeFromDoyTime (SecTimeObj* pSt, DoyTimeObj* pDt); 2678585484eSchristos static void DoyTimeFromSecTime (DoyTimeObj* pDt, SecTimeObj* pSt); 2688585484eSchristos 2698585484eSchristos /* 2708585484eSchristos ** Transfer vector 2718585484eSchristos */ 2728585484eSchristos struct refclock refclock_tsyncpci = { 2738585484eSchristos tsync_start, /* start up driver */ 2748585484eSchristos tsync_shutdown, /* shut down driver */ 2758585484eSchristos tsync_poll, /* transmit poll message */ 2768585484eSchristos noentry, /* not used (old tsync_control) */ 2778585484eSchristos noentry, /* initialize driver (not used) */ 2788585484eSchristos noentry, /* not used (old tsync_buginfo) */ 2798585484eSchristos NOFLAGS /* not used */ 2808585484eSchristos }; 2818585484eSchristos 2828585484eSchristos /* 2838585484eSchristos * Reference ID lookup table 2848585484eSchristos */ 2858585484eSchristos static RefIdLookup RefIdLookupTbl[] = 2868585484eSchristos { 2878585484eSchristos {"gps", "GPS"}, 2888585484eSchristos {"ir", "IRIG"}, 2898585484eSchristos {"hvq", "HVQ"}, 2908585484eSchristos {"frq", "FREQ"}, 2918585484eSchristos {"mdm", "ACTS"}, 2928585484eSchristos {"epp", "PPS"}, 2938585484eSchristos {"ptp", "PTP"}, 2948585484eSchristos {"asc", "ATC"}, 2958585484eSchristos {"hst0", "USER"}, 2968585484eSchristos {"hst", TSYNC_REF_LOCAL}, 2978585484eSchristos {"self", TSYNC_REF_LOCAL}, 2988585484eSchristos {NULL, NULL} 2998585484eSchristos }; 3008585484eSchristos 3018585484eSchristos /******************************************************************************* 3028585484eSchristos ** IOCTL DEFINITIONS 3038585484eSchristos *******************************************************************************/ 3048585484eSchristos #define IOCTL_TPRO_ID 't' 3058585484eSchristos #define IOCTL_TPRO_OPEN _IOWR(IOCTL_TPRO_ID, 0, BoardObj) 3068585484eSchristos #define IOCTL_TPRO_GET_NTP_TIME _IOWR(IOCTL_TPRO_ID, 25, NtpTimeObj) 3078585484eSchristos #define IOCTL_TSYNC_GET _IOWR(IOCTL_TPRO_ID, 26, ioctl_trans_di) 3088585484eSchristos 3098585484eSchristos /****************************************************************************** 3108585484eSchristos * 3118585484eSchristos * Function: tsync_start() 3128585484eSchristos * Description: Used to intialize the Spectracom TSYNC reference driver. 3138585484eSchristos * 3148585484eSchristos * Parameters: 3158585484eSchristos * IN: unit - not used. 3168585484eSchristos * *peer - pointer to this reference clock's peer structure 3178585484eSchristos * Returns: 0 - unsuccessful 3188585484eSchristos * 1 - successful 3198585484eSchristos * 3208585484eSchristos *******************************************************************************/ 3218585484eSchristos static int tsync_start(int unit, struct peer *peer) 3228585484eSchristos { 3238585484eSchristos struct refclockproc *pp; 3248585484eSchristos TsyncUnit *up; 3258585484eSchristos 3268585484eSchristos 3278585484eSchristos /* 3288585484eSchristos ** initialize reference clock and peer parameters 3298585484eSchristos */ 3308585484eSchristos pp = peer->procptr; 3318585484eSchristos pp->clockdesc = DESCRIPTION; 3328585484eSchristos pp->io.clock_recv = noentry; 3338585484eSchristos pp->io.srcclock = peer; 3348585484eSchristos pp->io.datalen = 0; 3358585484eSchristos peer->precision = PRECISION; 3368585484eSchristos 3378585484eSchristos // Allocate and initialize unit structure 3388585484eSchristos if (!(up = (TsyncUnit*)emalloc(sizeof(TsyncUnit)))) 3398585484eSchristos { 3408585484eSchristos return (0); 3418585484eSchristos } 3428585484eSchristos 3438585484eSchristos // Store reference preference 3448585484eSchristos up->refPrefer = peer->flags & FLAG_PREFER; 3458585484eSchristos 3468585484eSchristos // Initialize reference stratum level and ID 3478585484eSchristos up->refStratum = STRATUM_UNSPEC; 3488585484eSchristos strncpy((char *)&up->refId, TSYNC_REF_LOCAL, TSYNC_REF_LEN); 3498585484eSchristos 3508585484eSchristos // Attach unit structure 3518585484eSchristos pp->unitptr = (caddr_t)up; 3528585484eSchristos 3538585484eSchristos /* Declare our refId as local in the beginning because we do not know 3548585484eSchristos * what our actual refid is yet. 3558585484eSchristos */ 3568585484eSchristos strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN); 3578585484eSchristos 3588585484eSchristos return (1); 3598585484eSchristos 3608585484eSchristos } /* End - tsync_start() */ 3618585484eSchristos 3628585484eSchristos /******************************************************************************* 3638585484eSchristos ** 3648585484eSchristos ** Function: tsync_shutdown() 3658585484eSchristos ** Description: Handles anything related to shutting down the reference clock 3668585484eSchristos ** driver. Nothing at this point in time. 3678585484eSchristos ** 3688585484eSchristos ** Parameters: 3698585484eSchristos ** IN: unit - not used. 3708585484eSchristos ** *peer - pointer to this reference clock's peer structure 3718585484eSchristos ** Returns: none. 3728585484eSchristos ** 3738585484eSchristos *******************************************************************************/ 3748585484eSchristos static void tsync_shutdown(int unit, struct peer *peer) 3758585484eSchristos { 3768585484eSchristos 3778585484eSchristos } /* End - tsync_shutdown() */ 3788585484eSchristos 3798585484eSchristos /****************************************************************************** 3808585484eSchristos * 3818585484eSchristos * Function: tsync_poll() 3828585484eSchristos * Description: Retrieve time from the TSYNC device. 3838585484eSchristos * 3848585484eSchristos * Parameters: 3858585484eSchristos * IN: unit - not used. 3868585484eSchristos * *peer - pointer to this reference clock's peer structure 3878585484eSchristos * Returns: none. 3888585484eSchristos * 3898585484eSchristos *******************************************************************************/ 3908585484eSchristos static void tsync_poll(int unit, struct peer *peer) 3918585484eSchristos { 3928585484eSchristos char device[32]; 3938585484eSchristos struct refclockproc *pp; 3948585484eSchristos struct calendar jt; 3958585484eSchristos TsyncUnit *up; 3968585484eSchristos unsigned char synch; 3978585484eSchristos double seconds; 3988585484eSchristos int err; 3998585484eSchristos int err1; 4008585484eSchristos int err2; 4018585484eSchristos int err3; 4028585484eSchristos int i; 4038585484eSchristos int j; 4048585484eSchristos unsigned int itAllocationLength; 4058585484eSchristos unsigned int itAllocationLength1; 4068585484eSchristos unsigned int itAllocationLength2; 4078585484eSchristos NtpTimeObj TimeContext; 4088585484eSchristos BoardObj hBoard; 4098585484eSchristos char timeRef[TSYNC_REF_LEN + 1]; 4108585484eSchristos char ppsRef [TSYNC_REF_LEN + 1]; 4118585484eSchristos TIME_SCALE tmscl = TIME_SCALE_UTC; 4128585484eSchristos LeapSecondObj leapSec; 4138585484eSchristos ioctl_trans_di *it; 4148585484eSchristos ioctl_trans_di *it1; 4158585484eSchristos ioctl_trans_di *it2; 4168585484eSchristos l_fp offset; 4178585484eSchristos l_fp ltemp; 4188585484eSchristos ReferenceObj * pRefObj; 4198585484eSchristos 4208585484eSchristos 4218585484eSchristos /* Construct the device name */ 4228585484eSchristos sprintf(device, "%s%d", DEVICE, (int)peer->refclkunit); 4238585484eSchristos 4248585484eSchristos printf("Polling device number %d...\n", (int)peer->refclkunit); 4258585484eSchristos 4268585484eSchristos /* Open the TSYNC device */ 4278585484eSchristos hBoard.file_descriptor = open(device, O_RDONLY | O_NDELAY, 0777); 4288585484eSchristos 4298585484eSchristos /* If error opening TSYNC device... */ 4308585484eSchristos if (hBoard.file_descriptor < 0) 4318585484eSchristos { 4328585484eSchristos msyslog(LOG_ERR, "Couldn't open device"); 4338585484eSchristos return; 4348585484eSchristos } 4358585484eSchristos 4368585484eSchristos /* If error while initializing the board... */ 4378585484eSchristos if (ioctl(hBoard.file_descriptor, IOCTL_TPRO_OPEN, &hBoard) < 0) 4388585484eSchristos { 4398585484eSchristos msyslog(LOG_ERR, "Couldn't initialize device"); 4408585484eSchristos close(hBoard.file_descriptor); 4418585484eSchristos return; 4428585484eSchristos } 4438585484eSchristos 4448585484eSchristos /* Allocate memory for ioctl message */ 4458585484eSchristos itAllocationLength = 4468585484eSchristos (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) + 4478585484eSchristos TSYNC_REF_IN_LEN + TSYNC_REF_MAX_OUT_LEN; 4488585484eSchristos 4498585484eSchristos it = (ioctl_trans_di*)alloca(itAllocationLength); 4508585484eSchristos if (it == NULL) { 4518585484eSchristos msyslog(LOG_ERR, "Couldn't allocate transaction memory - Reference"); 4528585484eSchristos return; 4538585484eSchristos } 4548585484eSchristos 4558585484eSchristos /* Build SS_GetRef ioctl message */ 4568585484eSchristos it->dest = TSYNC_REF_DEST_ID; 4578585484eSchristos it->iid = TSYNC_REF_IID; 4588585484eSchristos it->inPayloadOffset = TSYNC_REF_IN_PYLD_OFF; 4598585484eSchristos it->inLength = TSYNC_REF_IN_LEN; 4608585484eSchristos it->outPayloadOffset = TSYNC_REF_OUT_PYLD_OFF; 4618585484eSchristos it->maxOutLength = TSYNC_REF_MAX_OUT_LEN; 4628585484eSchristos it->actualOutLength = 0; 4638585484eSchristos it->status = 0; 4648585484eSchristos memset(it->payloads, 0, TSYNC_REF_MAX_OUT_LEN); 4658585484eSchristos 4668585484eSchristos /* Read the reference from the TSYNC-PCI device */ 4678585484eSchristos err = ioctl(hBoard.file_descriptor, 4688585484eSchristos IOCTL_TSYNC_GET, 4698585484eSchristos (char *)it); 4708585484eSchristos 4718585484eSchristos /* Allocate memory for ioctl message */ 4728585484eSchristos itAllocationLength1 = 4738585484eSchristos (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) + 4748585484eSchristos TSYNC_TMSCL_IN_LEN + TSYNC_TMSCL_MAX_OUT_LEN; 4758585484eSchristos 4768585484eSchristos it1 = (ioctl_trans_di*)alloca(itAllocationLength1); 4778585484eSchristos if (it1 == NULL) { 4788585484eSchristos msyslog(LOG_ERR, "Couldn't allocate transaction memory - Time Scale"); 4798585484eSchristos return; 4808585484eSchristos } 4818585484eSchristos 4828585484eSchristos /* Build CS_GetTimeScale ioctl message */ 4838585484eSchristos it1->dest = TSYNC_TMSCL_DEST_ID; 4848585484eSchristos it1->iid = TSYNC_TMSCL_IID; 4858585484eSchristos it1->inPayloadOffset = TSYNC_TMSCL_IN_PYLD_OFF; 4868585484eSchristos it1->inLength = TSYNC_TMSCL_IN_LEN; 4878585484eSchristos it1->outPayloadOffset = TSYNC_TMSCL_OUT_PYLD_OFF; 4888585484eSchristos it1->maxOutLength = TSYNC_TMSCL_MAX_OUT_LEN; 4898585484eSchristos it1->actualOutLength = 0; 4908585484eSchristos it1->status = 0; 4918585484eSchristos memset(it1->payloads, 0, TSYNC_TMSCL_MAX_OUT_LEN); 4928585484eSchristos 4938585484eSchristos /* Read the Time Scale info from the TSYNC-PCI device */ 4948585484eSchristos err1 = ioctl(hBoard.file_descriptor, 4958585484eSchristos IOCTL_TSYNC_GET, 4968585484eSchristos (char *)it1); 4978585484eSchristos 4988585484eSchristos /* Allocate memory for ioctl message */ 4998585484eSchristos itAllocationLength2 = 5008585484eSchristos (sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) + 5018585484eSchristos TSYNC_LEAP_IN_LEN + TSYNC_LEAP_MAX_OUT_LEN; 5028585484eSchristos 5038585484eSchristos it2 = (ioctl_trans_di*)alloca(itAllocationLength2); 5048585484eSchristos if (it2 == NULL) { 5058585484eSchristos msyslog(LOG_ERR, "Couldn't allocate transaction memory - Leap Second"); 5068585484eSchristos return; 5078585484eSchristos } 5088585484eSchristos 5098585484eSchristos /* Build CS_GetLeapSec ioctl message */ 5108585484eSchristos it2->dest = TSYNC_LEAP_DEST_ID; 5118585484eSchristos it2->iid = TSYNC_LEAP_IID; 5128585484eSchristos it2->inPayloadOffset = TSYNC_LEAP_IN_PYLD_OFF; 5138585484eSchristos it2->inLength = TSYNC_LEAP_IN_LEN; 5148585484eSchristos it2->outPayloadOffset = TSYNC_LEAP_OUT_PYLD_OFF; 5158585484eSchristos it2->maxOutLength = TSYNC_LEAP_MAX_OUT_LEN; 5168585484eSchristos it2->actualOutLength = 0; 5178585484eSchristos it2->status = 0; 5188585484eSchristos memset(it2->payloads, 0, TSYNC_LEAP_MAX_OUT_LEN); 5198585484eSchristos 5208585484eSchristos /* Read the leap seconds info from the TSYNC-PCI device */ 5218585484eSchristos err2 = ioctl(hBoard.file_descriptor, 5228585484eSchristos IOCTL_TSYNC_GET, 5238585484eSchristos (char *)it2); 5248585484eSchristos 5258585484eSchristos pp = peer->procptr; 5268585484eSchristos up = (TsyncUnit*)pp->unitptr; 5278585484eSchristos 5288585484eSchristos /* Read the time from the TSYNC-PCI device */ 5298585484eSchristos err3 = ioctl(hBoard.file_descriptor, 5308585484eSchristos IOCTL_TPRO_GET_NTP_TIME, 5318585484eSchristos (char *)&TimeContext); 5328585484eSchristos 5338585484eSchristos /* Close the TSYNC device */ 5348585484eSchristos close(hBoard.file_descriptor); 5358585484eSchristos 5368585484eSchristos // Check for errors 5378585484eSchristos if ((err < 0) ||(err1 < 0) || (err2 < 0) || (err3 < 0) || 5388585484eSchristos (it->status != 0) || (it1->status != 0) || (it2->status != 0) || 5398585484eSchristos (it->actualOutLength != TSYNC_REF_OUT_LEN) || 5408585484eSchristos (it1->actualOutLength != TSYNC_TMSCL_OUT_LEN) || 5418585484eSchristos (it2->actualOutLength != TSYNC_LEAP_OUT_LEN)) { 5428585484eSchristos refclock_report(peer, CEVNT_FAULT); 5438585484eSchristos return; 5448585484eSchristos } 5458585484eSchristos 5468585484eSchristos // Extract reference identifiers from ioctl payload 5478585484eSchristos memset(timeRef, '\0', sizeof(timeRef)); 5488585484eSchristos memset(ppsRef, '\0', sizeof(ppsRef)); 5498585484eSchristos pRefObj = (void *)it->payloads; 5508585484eSchristos memcpy(timeRef, pRefObj->time, TSYNC_REF_LEN); 5518585484eSchristos memcpy(ppsRef, pRefObj->pps, TSYNC_REF_LEN); 5528585484eSchristos 5538585484eSchristos // Extract the Clock Service Time Scale and convert to correct byte order 5548b8da087Schristos memcpy(&tmscl, it1->payloads, sizeof(tmscl)); 5558585484eSchristos tmscl = ntohl(tmscl); 5568585484eSchristos 5578585484eSchristos // Extract leap second info from ioctl payload and perform byte swapping 5588585484eSchristos for (i = 0; i < (sizeof(leapSec) / 4); i++) 5598585484eSchristos { 5608585484eSchristos for (j = 0; j < 4; j++) 5618585484eSchristos { 5628585484eSchristos ((unsigned char*)&leapSec)[(i * 4) + j] = 5638585484eSchristos ((unsigned char*)(it2->payloads))[(i * 4) + (3 - j)]; 5648585484eSchristos } 5658585484eSchristos } 5668585484eSchristos 5678585484eSchristos // Determine time reference ID from reference name 5688585484eSchristos for (i = 0; RefIdLookupTbl[i].pRef != NULL; i++) 5698585484eSchristos { 5708585484eSchristos // Search RefID table 5718585484eSchristos if (strstr(timeRef, RefIdLookupTbl[i].pRef) != NULL) 5728585484eSchristos { 5738585484eSchristos // Found the matching string 5748585484eSchristos break; 5758585484eSchristos } 5768585484eSchristos } 5778585484eSchristos 5788585484eSchristos // Determine pps reference ID from reference name 5798585484eSchristos for (j = 0; RefIdLookupTbl[j].pRef != NULL; j++) 5808585484eSchristos { 5818585484eSchristos // Search RefID table 5828585484eSchristos if (strstr(ppsRef, RefIdLookupTbl[j].pRef) != NULL) 5838585484eSchristos { 5848585484eSchristos // Found the matching string 5858585484eSchristos break; 5868585484eSchristos } 5878585484eSchristos } 5888585484eSchristos 5898585484eSchristos // Determine synchronization state from flags 5908585484eSchristos synch = (TimeContext.timeObj.flags == 0x4) ? 1 : 0; 5918585484eSchristos 5928585484eSchristos // Pull seconds information from time object 5938585484eSchristos seconds = (double) (TimeContext.timeObj.secsDouble); 5948585484eSchristos seconds /= (double) 1000000.0; 5958585484eSchristos 5968585484eSchristos /* 5978585484eSchristos ** Convert the number of microseconds to double and then place in the 5988585484eSchristos ** peer's last received long floating point format. 5998585484eSchristos */ 6008585484eSchristos DTOLFP(((double)TimeContext.tv.tv_usec / 1000000.0), &pp->lastrec); 6018585484eSchristos 6028585484eSchristos /* 6038585484eSchristos ** The specTimeStamp is the number of seconds since 1/1/1970, while the 6048585484eSchristos ** peer's lastrec time should be compatible with NTP which is seconds since 6058585484eSchristos ** 1/1/1900. So Add the number of seconds between 1900 and 1970 to the 6068585484eSchristos ** specTimeStamp and place in the peer's lastrec long floating point struct. 6078585484eSchristos */ 6088585484eSchristos pp->lastrec.Ul_i.Xl_ui += (unsigned int)TimeContext.tv.tv_sec + 6098585484eSchristos SECONDS_1900_TO_1970; 6108585484eSchristos 6118585484eSchristos pp->polls++; 6128585484eSchristos 6138585484eSchristos /* 6148585484eSchristos ** set the reference clock object 6158585484eSchristos */ 6168585484eSchristos sprintf(pp->a_lastcode, "%03d %02d:%02d:%02.6f", 6178585484eSchristos TimeContext.timeObj.days, TimeContext.timeObj.hours, 6188585484eSchristos TimeContext.timeObj.minutes, seconds); 6198585484eSchristos 6208585484eSchristos pp->lencode = strlen (pp->a_lastcode); 6218585484eSchristos pp->day = TimeContext.timeObj.days; 6228585484eSchristos pp->hour = TimeContext.timeObj.hours; 6238585484eSchristos pp->minute = TimeContext.timeObj.minutes; 6248585484eSchristos pp->second = (int) seconds; 6258585484eSchristos seconds = (seconds - (double) (pp->second / 1.0)) * 1000000000; 6268585484eSchristos pp->nsec = (long) seconds; 6278585484eSchristos 6288585484eSchristos /* 6298585484eSchristos ** calculate year start 6308585484eSchristos */ 6318585484eSchristos jt.year = TimeContext.timeObj.year; 6328585484eSchristos jt.yearday = 1; 6338585484eSchristos jt.monthday = 1; 6348585484eSchristos jt.month = 1; 6358585484eSchristos jt.hour = 0; 6368585484eSchristos jt.minute = 0; 6378585484eSchristos jt.second = 0; 6388585484eSchristos pp->yearstart = caltontp(&jt); 6398585484eSchristos 6408585484eSchristos // Calculate and report reference clock offset 6418585484eSchristos offset.l_ui = (long)(((pp->day - 1) * 24) + pp->hour + GMT); 6428585484eSchristos offset.l_ui = (offset.l_ui * 60) + (long)pp->minute; 6438585484eSchristos offset.l_ui = (offset.l_ui * 60) + (long)pp->second; 6448585484eSchristos offset.l_ui = offset.l_ui + (long)pp->yearstart; 6458585484eSchristos offset.l_uf = 0; 6468585484eSchristos DTOLFP(pp->nsec / 1e9, <emp); 6478585484eSchristos L_ADD(&offset, <emp); 6488585484eSchristos refclock_process_offset(pp, offset, pp->lastrec, 6498585484eSchristos pp->fudgetime1); 6508585484eSchristos 6518585484eSchristos // KTS in sync 6528585484eSchristos if (synch) { 6538585484eSchristos // Subtract leap second info by one second to determine effective day 6548585484eSchristos ApplyTimeOffset(&(leapSec.utcDate), -1); 6558585484eSchristos 6568585484eSchristos // If there is a leap second today and the KTS is using a time scale 6578585484eSchristos // which handles leap seconds then 6588585484eSchristos if ((tmscl != TIME_SCALE_GPS) && (tmscl != TIME_SCALE_TAI) && 6598585484eSchristos (leapSec.utcDate.year == (unsigned int)TimeContext.timeObj.year) && 6608585484eSchristos (leapSec.utcDate.doy == (unsigned int)TimeContext.timeObj.days)) 6618585484eSchristos { 6628585484eSchristos // If adding a second 6638585484eSchristos if (leapSec.offset == 1) 6648585484eSchristos { 6658585484eSchristos pp->leap = LEAP_ADDSECOND; 6668585484eSchristos } 6678585484eSchristos // Else if removing a second 6688585484eSchristos else if (leapSec.offset == -1) 6698585484eSchristos { 6708585484eSchristos pp->leap = LEAP_DELSECOND; 6718585484eSchristos } 6728585484eSchristos // Else report no leap second pending (no handling of offsets 6738585484eSchristos // other than +1 or -1) 6748585484eSchristos else 6758585484eSchristos { 6768585484eSchristos pp->leap = LEAP_NOWARNING; 6778585484eSchristos } 6788585484eSchristos } 6798585484eSchristos // Else report no leap second pending 6808585484eSchristos else 6818585484eSchristos { 6828585484eSchristos pp->leap = LEAP_NOWARNING; 6838585484eSchristos } 6848585484eSchristos 6858585484eSchristos peer->leap = pp->leap; 6868585484eSchristos refclock_report(peer, CEVNT_NOMINAL); 6878585484eSchristos 6888585484eSchristos // If reference name reported, then not in holdover 6898585484eSchristos if ((RefIdLookupTbl[i].pRef != NULL) && 6908585484eSchristos (RefIdLookupTbl[j].pRef != NULL)) 6918585484eSchristos { 6928585484eSchristos // Determine if KTS being synchronized by host (identified as 6938585484eSchristos // "LOCL") 6948585484eSchristos if ((strcmp(RefIdLookupTbl[i].pRefId, TSYNC_REF_LOCAL) == 0) || 6958585484eSchristos (strcmp(RefIdLookupTbl[j].pRefId, TSYNC_REF_LOCAL) == 0)) 6968585484eSchristos { 6978585484eSchristos // Clear prefer flag 6988585484eSchristos peer->flags &= ~FLAG_PREFER; 6998585484eSchristos 7008585484eSchristos // Set reference clock stratum level as unusable 7018585484eSchristos pp->stratum = STRATUM_UNSPEC; 7028585484eSchristos peer->stratum = pp->stratum; 7038585484eSchristos 7048585484eSchristos // If a valid peer is available 7058585484eSchristos if ((sys_peer != NULL) && (sys_peer != peer)) 7068585484eSchristos { 7078585484eSchristos // Store reference peer stratum level and ID 7088585484eSchristos up->refStratum = sys_peer->stratum; 7098585484eSchristos up->refId = addr2refid(&sys_peer->srcadr); 7108585484eSchristos } 7118585484eSchristos } 7128585484eSchristos else 7138585484eSchristos { 7148585484eSchristos // Restore prefer flag 7158585484eSchristos peer->flags |= up->refPrefer; 7168585484eSchristos 7178585484eSchristos // Store reference stratum as local clock 7188585484eSchristos up->refStratum = TSYNC_LCL_STRATUM; 7198585484eSchristos strncpy((char *)&up->refId, RefIdLookupTbl[j].pRefId, 7208585484eSchristos TSYNC_REF_LEN); 7218585484eSchristos 7228585484eSchristos // Set reference clock stratum level as local clock 7238585484eSchristos pp->stratum = TSYNC_LCL_STRATUM; 7248585484eSchristos peer->stratum = pp->stratum; 7258585484eSchristos } 7268585484eSchristos 7278585484eSchristos // Update reference name 7288585484eSchristos strncpy((char *)&pp->refid, RefIdLookupTbl[j].pRefId, 7298585484eSchristos TSYNC_REF_LEN); 7308585484eSchristos peer->refid = pp->refid; 7318585484eSchristos } 7328585484eSchristos // Else in holdover 7338585484eSchristos else 7348585484eSchristos { 7358585484eSchristos // Restore prefer flag 7368585484eSchristos peer->flags |= up->refPrefer; 7378585484eSchristos 7388585484eSchristos // Update reference ID to saved ID 7398585484eSchristos pp->refid = up->refId; 7408585484eSchristos peer->refid = pp->refid; 7418585484eSchristos 7428585484eSchristos // Update stratum level to saved stratum level 7438585484eSchristos pp->stratum = up->refStratum; 7448585484eSchristos peer->stratum = pp->stratum; 7458585484eSchristos } 7468585484eSchristos } 7478585484eSchristos // Else KTS not in sync 7488585484eSchristos else { 7498585484eSchristos // Place local identifier in peer RefID 7508585484eSchristos strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN); 7518585484eSchristos peer->refid = pp->refid; 7528585484eSchristos 7538585484eSchristos // Report not in sync 7548585484eSchristos pp->leap = LEAP_NOTINSYNC; 7558585484eSchristos peer->leap = pp->leap; 7568585484eSchristos } 7578585484eSchristos 7588585484eSchristos if (pp->coderecv == pp->codeproc) { 7598585484eSchristos refclock_report(peer, CEVNT_TIMEOUT); 7608585484eSchristos return; 7618585484eSchristos } 7628585484eSchristos 7638585484eSchristos record_clock_stats(&peer->srcadr, pp->a_lastcode); 7648585484eSchristos refclock_receive(peer); 7658585484eSchristos 7668585484eSchristos /* Increment the number of times the reference has been polled */ 7678585484eSchristos pp->polls++; 7688585484eSchristos 7698585484eSchristos } /* End - tsync_poll() */ 7708585484eSchristos 7718585484eSchristos 7728585484eSchristos //////////////////////////////////////////////////////////////////////////////// 7738585484eSchristos // Function: ApplyTimeOffset 7748585484eSchristos // Description: The ApplyTimeOffset function adds an offset (in seconds) to a 7758585484eSchristos // specified date and time. The specified date and time is passed 7768585484eSchristos // back after being modified. 7778585484eSchristos // 7788585484eSchristos // Assumptions: 1. Every fourth year is a leap year. Therefore, this function 7798585484eSchristos // is only accurate through Feb 28, 2100. 7808585484eSchristos //////////////////////////////////////////////////////////////////////////////// 7818585484eSchristos void ApplyTimeOffset(DoyTimeObj* pDt, int off) 7828585484eSchristos { 7838585484eSchristos SecTimeObj st; // Time, in seconds 7848585484eSchristos 7858585484eSchristos 7868585484eSchristos // Convert date and time to seconds 7878585484eSchristos SecTimeFromDoyTime(&st, pDt); 7888585484eSchristos 7898585484eSchristos // Apply offset 7908585484eSchristos st.seconds = (int)((signed long long)st.seconds + (signed long long)off); 7918585484eSchristos 7928585484eSchristos // Convert seconds to date and time 7938585484eSchristos DoyTimeFromSecTime(pDt, &st); 7948585484eSchristos 7958585484eSchristos } // End ApplyTimeOffset 7968585484eSchristos 7978585484eSchristos 7988585484eSchristos //////////////////////////////////////////////////////////////////////////////// 7998585484eSchristos // Function: SecTimeFromDoyTime 8008585484eSchristos // Description: The SecTimeFromDoyTime function converts a specified date 8018585484eSchristos // and time into a count of seconds since the base time. This 8028585484eSchristos // function operates across the range Base Time to Max Time for 8038585484eSchristos // the system. 8048585484eSchristos // 8058585484eSchristos // Assumptions: 1. A leap year is any year evenly divisible by 4. Therefore, 8068585484eSchristos // this function is only accurate through Feb 28, 2100. 8078585484eSchristos // 2. Conversion does not account for leap seconds. 8088585484eSchristos //////////////////////////////////////////////////////////////////////////////// 8098585484eSchristos void SecTimeFromDoyTime(SecTimeObj* pSt, DoyTimeObj* pDt) 8108585484eSchristos { 8118585484eSchristos unsigned int yrs; // Years 8128585484eSchristos unsigned int lyrs; // Leap years 8138585484eSchristos 8148585484eSchristos 8158585484eSchristos // Start with accumulated time of 0 8168585484eSchristos pSt->seconds = 0; 8178585484eSchristos 8188585484eSchristos // Calculate the number of years and leap years 8198585484eSchristos yrs = pDt->year - TSYNC_TIME_BASE_YEAR; 8208585484eSchristos lyrs = (yrs + 1) / 4; 8218585484eSchristos 8228585484eSchristos // Convert leap years and years 8238585484eSchristos pSt->seconds += lyrs * SECSPERLEAPYEAR; 8248585484eSchristos pSt->seconds += (yrs - lyrs) * SECSPERYEAR; 8258585484eSchristos 8268585484eSchristos // Convert days, hours, minutes and seconds 8278585484eSchristos pSt->seconds += (pDt->doy - 1) * SECSPERDAY; 8288585484eSchristos pSt->seconds += pDt->hour * SECSPERHR; 8298585484eSchristos pSt->seconds += pDt->minute * SECSPERMIN; 8308585484eSchristos pSt->seconds += pDt->second; 8318585484eSchristos 8328585484eSchristos // Copy the subseconds count 8338585484eSchristos pSt->ns = pDt->ns; 8348585484eSchristos 8358585484eSchristos } // End SecTimeFromDoyTime 8368585484eSchristos 8378585484eSchristos 8388585484eSchristos //////////////////////////////////////////////////////////////////////////////// 8398585484eSchristos // Function: DoyTimeFromSecTime 8408585484eSchristos // Description: The DoyTimeFromSecTime function converts a specified count 8418585484eSchristos // of seconds since the start of our base time into a SecTimeObj 8428585484eSchristos // structure. 8438585484eSchristos // 8448585484eSchristos // Assumptions: 1. A leap year is any year evenly divisible by 4. Therefore, 8458585484eSchristos // this function is only accurate through Feb 28, 2100. 8468585484eSchristos // 2. Conversion does not account for leap seconds. 8478585484eSchristos //////////////////////////////////////////////////////////////////////////////// 8488585484eSchristos void DoyTimeFromSecTime(DoyTimeObj* pDt, SecTimeObj* pSt) 8498585484eSchristos { 8508585484eSchristos signed long long secs; // Seconds accumulator variable 8518585484eSchristos unsigned int yrs; // Years accumulator variable 8528585484eSchristos unsigned int doys; // Days accumulator variable 8538585484eSchristos unsigned int hrs; // Hours accumulator variable 8548585484eSchristos unsigned int mins; // Minutes accumulator variable 8558585484eSchristos 8568585484eSchristos 8578585484eSchristos // Convert the seconds count into a signed 64-bit number for calculations 8588585484eSchristos secs = (signed long long)(pSt->seconds); 8598585484eSchristos 8608585484eSchristos // Calculate the number of 4 year chunks 8618585484eSchristos yrs = (unsigned int)((secs / 8628585484eSchristos ((SECSPERYEAR * 3) + SECSPERLEAPYEAR)) * 4); 8638585484eSchristos secs %= ((SECSPERYEAR * 3) + SECSPERLEAPYEAR); 8648585484eSchristos 8658585484eSchristos // If there is at least a normal year worth of time left 8668585484eSchristos if (secs >= SECSPERYEAR) 8678585484eSchristos { 8688585484eSchristos // Increment the number of years and subtract a normal year of time 8698585484eSchristos yrs++; 8708585484eSchristos secs -= SECSPERYEAR; 8718585484eSchristos } 8728585484eSchristos 8738585484eSchristos // If there is still at least a normal year worth of time left 8748585484eSchristos if (secs >= SECSPERYEAR) 8758585484eSchristos { 8768585484eSchristos // Increment the number of years and subtract a normal year of time 8778585484eSchristos yrs++; 8788585484eSchristos secs -= SECSPERYEAR; 8798585484eSchristos } 8808585484eSchristos 8818585484eSchristos // If there is still at least a leap year worth of time left 8828585484eSchristos if (secs >= SECSPERLEAPYEAR) 8838585484eSchristos { 8848585484eSchristos // Increment the number of years and subtract a leap year of time 8858585484eSchristos yrs++; 8868585484eSchristos secs -= SECSPERLEAPYEAR; 8878585484eSchristos } 8888585484eSchristos 8898585484eSchristos // Calculate the day of year as the number of days left, then add 1 8908585484eSchristos // because months start on the 1st. 8918585484eSchristos doys = (unsigned int)((secs / SECSPERDAY) + 1); 8928585484eSchristos secs %= SECSPERDAY; 8938585484eSchristos 8948585484eSchristos // Calculate the hour 8958585484eSchristos hrs = (unsigned int)(secs / SECSPERHR); 8968585484eSchristos secs %= SECSPERHR; 8978585484eSchristos 8988585484eSchristos // Calculate the minute 8998585484eSchristos mins = (unsigned int)(secs / SECSPERMIN); 9008585484eSchristos secs %= SECSPERMIN; 9018585484eSchristos 9028585484eSchristos // Fill in the doytime structure 9038585484eSchristos pDt->year = yrs + TSYNC_TIME_BASE_YEAR; 9048585484eSchristos pDt->doy = doys; 9058585484eSchristos pDt->hour = hrs; 9068585484eSchristos pDt->minute = mins; 9078585484eSchristos pDt->second = (unsigned int)secs; 9088585484eSchristos pDt->ns = pSt->ns; 9098585484eSchristos 9108585484eSchristos } // End DoyTimeFromSecTime 9118585484eSchristos 9128585484eSchristos #else 913*eabc0478Schristos NONEMPTY_TRANSLATION_UNIT 9148585484eSchristos #endif /* REFCLOCK */ 915