1*eabc0478Schristos /* $NetBSD: refclock_jupiter.c,v 1.13 2024/08/18 20:47:18 christos Exp $ */ 2abb0f93cSkardel 3abb0f93cSkardel /* 4abb0f93cSkardel * Copyright (c) 1997, 1998, 2003 5abb0f93cSkardel * The Regents of the University of California. All rights reserved. 6abb0f93cSkardel * 7abb0f93cSkardel * Redistribution and use in source and binary forms, with or without 8abb0f93cSkardel * modification, are permitted provided that the following conditions 9abb0f93cSkardel * are met: 10abb0f93cSkardel * 1. Redistributions of source code must retain the above copyright 11abb0f93cSkardel * notice, this list of conditions and the following disclaimer. 12abb0f93cSkardel * 2. Redistributions in binary form must reproduce the above copyright 13abb0f93cSkardel * notice, this list of conditions and the following disclaimer in the 14abb0f93cSkardel * documentation and/or other materials provided with the distribution. 15abb0f93cSkardel * 3. All advertising materials mentioning features or use of this software 16abb0f93cSkardel * must display the following acknowledgement: 17abb0f93cSkardel * This product includes software developed by the University of 18abb0f93cSkardel * California, Lawrence Berkeley Laboratory. 19abb0f93cSkardel * 4. The name of the University may not be used to endorse or promote 20abb0f93cSkardel * products derived from this software without specific prior 21abb0f93cSkardel * written permission. 22abb0f93cSkardel * 23abb0f93cSkardel * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24abb0f93cSkardel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25abb0f93cSkardel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26abb0f93cSkardel * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27abb0f93cSkardel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28abb0f93cSkardel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29abb0f93cSkardel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30abb0f93cSkardel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31abb0f93cSkardel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32abb0f93cSkardel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33abb0f93cSkardel * SUCH DAMAGE. 34abb0f93cSkardel */ 35abb0f93cSkardel 36abb0f93cSkardel #ifdef HAVE_CONFIG_H 37abb0f93cSkardel # include <config.h> 38abb0f93cSkardel #endif 39abb0f93cSkardel 40cdfa2a7eSchristos /* This clock *REQUIRES* the PPS API to be available */ 41abb0f93cSkardel #if defined(REFCLOCK) && defined(CLOCK_JUPITER) && defined(HAVE_PPSAPI) 42abb0f93cSkardel 43abb0f93cSkardel #include "ntpd.h" 44abb0f93cSkardel #include "ntp_io.h" 45abb0f93cSkardel #include "ntp_refclock.h" 46abb0f93cSkardel #include "ntp_unixtime.h" 47abb0f93cSkardel #include "ntp_stdlib.h" 48cdfa2a7eSchristos #include "ntp_calendar.h" 49cdfa2a7eSchristos #include "ntp_calgps.h" 50cdfa2a7eSchristos #include "timespecops.h" 51abb0f93cSkardel 52abb0f93cSkardel #include <stdio.h> 53abb0f93cSkardel #include <ctype.h> 54abb0f93cSkardel 55abb0f93cSkardel #include "jupiter.h" 56abb0f93cSkardel #include "ppsapi_timepps.h" 57abb0f93cSkardel 582950cc38Schristos #ifdef WORDS_BIGENDIAN 59abb0f93cSkardel #define getshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) 60abb0f93cSkardel #define putshort(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) 61abb0f93cSkardel #else 622950cc38Schristos #define getshort(s) ((u_short)(s)) 632950cc38Schristos #define putshort(s) ((u_short)(s)) 64abb0f93cSkardel #endif 65abb0f93cSkardel 66abb0f93cSkardel /* 67abb0f93cSkardel * This driver supports the Rockwell Jupiter GPS Receiver board 68abb0f93cSkardel * adapted to precision timing applications. It requires the 69abb0f93cSkardel * ppsclock line discipline or streams module described in the 70abb0f93cSkardel * Line Disciplines and Streams Drivers page. It also requires a 71abb0f93cSkardel * gadget box and 1-PPS level converter, such as described in the 72abb0f93cSkardel * Pulse-per-second (PPS) Signal Interfacing page. 73abb0f93cSkardel * 74abb0f93cSkardel * It may work (with minor modifications) with other Rockwell GPS 75abb0f93cSkardel * receivers such as the CityTracker. 76abb0f93cSkardel */ 77abb0f93cSkardel 78abb0f93cSkardel /* 79abb0f93cSkardel * GPS Definitions 80abb0f93cSkardel */ 81abb0f93cSkardel #define DEVICE "/dev/gps%d" /* device name and unit */ 82abb0f93cSkardel #define SPEED232 B9600 /* baud */ 83abb0f93cSkardel 84abb0f93cSkardel /* 85abb0f93cSkardel * Radio interface parameters 86abb0f93cSkardel */ 87abb0f93cSkardel #define PRECISION (-18) /* precision assumed (about 4 us) */ 88abb0f93cSkardel #define REFID "GPS\0" /* reference id */ 89abb0f93cSkardel #define DESCRIPTION "Rockwell Jupiter GPS Receiver" /* who we are */ 90abb0f93cSkardel #define DEFFUDGETIME 0 /* default fudge time (ms) */ 91abb0f93cSkardel 92abb0f93cSkardel /* Unix timestamp for the GPS epoch: January 6, 1980 */ 93abb0f93cSkardel #define GPS_EPOCH 315964800 94abb0f93cSkardel 9503cfe0ffSchristos /* Rata Die Number of first day of GPS epoch. This is the number of days 9603cfe0ffSchristos * since 0000-12-31 to 1980-01-06 in the proleptic Gregorian Calendar. 9703cfe0ffSchristos */ 9803cfe0ffSchristos #define RDN_GPS_EPOCH (4*146097 + 138431 + 1) 9903cfe0ffSchristos 100abb0f93cSkardel /* Double short to unsigned int */ 101abb0f93cSkardel #define DS2UI(p) ((getshort((p)[1]) << 16) | getshort((p)[0])) 102abb0f93cSkardel 103abb0f93cSkardel /* Double short to signed int */ 104abb0f93cSkardel #define DS2I(p) ((getshort((p)[1]) << 16) | getshort((p)[0])) 105abb0f93cSkardel 106abb0f93cSkardel /* One week's worth of seconds */ 107abb0f93cSkardel #define WEEKSECS (7 * 24 * 60 * 60) 108abb0f93cSkardel 109abb0f93cSkardel /* 110abb0f93cSkardel * Jupiter unit control structure. 111abb0f93cSkardel */ 112abb0f93cSkardel struct instance { 113abb0f93cSkardel struct peer *peer; /* peer */ 114cdfa2a7eSchristos 115abb0f93cSkardel pps_params_t pps_params; /* pps parameters */ 116abb0f93cSkardel pps_info_t pps_info; /* last pps data */ 117abb0f93cSkardel pps_handle_t pps_handle; /* pps handle */ 118abb0f93cSkardel u_int assert; /* pps edge to use */ 119abb0f93cSkardel u_int hardpps; /* enable kernel mode */ 120cdfa2a7eSchristos l_fp rcv_pps; /* last pps timestamp */ 121cdfa2a7eSchristos l_fp rcv_next; /* rcv time of next reftime */ 122cdfa2a7eSchristos TGpsDatum ref_next; /* next GPS time stamp to use with PPS */ 123cdfa2a7eSchristos TGpsDatum piv_next; /* pivot for week date unfolding */ 124cdfa2a7eSchristos uint16_t piv_hold; /* TTL for pivot value */ 125cdfa2a7eSchristos uint16_t rcvtout; /* receive timeout ticker */ 126abb0f93cSkardel int wantid; /* don't reconfig on channel id msg */ 127abb0f93cSkardel u_int moving; /* mobile platform? */ 128abb0f93cSkardel u_char sloppyclockflag; /* fudge flags */ 129abb0f93cSkardel u_short sbuf[512]; /* local input buffer */ 130abb0f93cSkardel int ssize; /* space used in sbuf */ 131abb0f93cSkardel }; 132abb0f93cSkardel 133abb0f93cSkardel /* 134abb0f93cSkardel * Function prototypes 135abb0f93cSkardel */ 136cdfa2a7eSchristos static void jupiter_canmsg (struct instance * const, u_int); 137abb0f93cSkardel static u_short jupiter_cksum (u_short *, u_int); 138cdfa2a7eSchristos static int jupiter_config (struct instance * const); 1392950cc38Schristos static void jupiter_debug (struct peer *, const char *, 140cdfa2a7eSchristos const char *, ...) NTP_PRINTF(3, 4); 141cdfa2a7eSchristos static const char * jupiter_parse_t (struct instance * const, u_short *, l_fp); 142cdfa2a7eSchristos static const char * jupiter_parse_gpos(struct instance * const, u_short *); 143cdfa2a7eSchristos static void jupiter_platform(struct instance * const, u_int); 144abb0f93cSkardel static void jupiter_poll (int, struct peer *); 1452950cc38Schristos static void jupiter_control (int, const struct refclockstat *, 1462950cc38Schristos struct refclockstat *, struct peer *); 147cdfa2a7eSchristos static int jupiter_ppsapi (struct instance * const); 148cdfa2a7eSchristos static int jupiter_pps (struct instance * const); 149cdfa2a7eSchristos static int jupiter_recv (struct instance * const); 150cdfa2a7eSchristos static void jupiter_receive (struct recvbuf * const rbufp); 151cdfa2a7eSchristos static void jupiter_reqmsg (struct instance * const, u_int, u_int); 152cdfa2a7eSchristos static void jupiter_reqonemsg(struct instance * const, u_int); 153cdfa2a7eSchristos static char * jupiter_send (struct instance * const, struct jheader *); 154abb0f93cSkardel static void jupiter_shutdown(int, struct peer *); 155abb0f93cSkardel static int jupiter_start (int, struct peer *); 156cdfa2a7eSchristos static void jupiter_ticker (int, struct peer *); 15703cfe0ffSchristos 158abb0f93cSkardel /* 159abb0f93cSkardel * Transfer vector 160abb0f93cSkardel */ 161abb0f93cSkardel struct refclock refclock_jupiter = { 162abb0f93cSkardel jupiter_start, /* start up driver */ 163abb0f93cSkardel jupiter_shutdown, /* shut down driver */ 164abb0f93cSkardel jupiter_poll, /* transmit poll message */ 165abb0f93cSkardel jupiter_control, /* (clock control) */ 166abb0f93cSkardel noentry, /* (clock init) */ 167abb0f93cSkardel noentry, /* (clock buginfo) */ 168cdfa2a7eSchristos jupiter_ticker /* 1HZ ticker */ 169abb0f93cSkardel }; 170abb0f93cSkardel 171abb0f93cSkardel /* 172abb0f93cSkardel * jupiter_start - open the devices and initialize data for processing 173abb0f93cSkardel */ 174abb0f93cSkardel static int 175abb0f93cSkardel jupiter_start( 176abb0f93cSkardel int unit, 177abb0f93cSkardel struct peer *peer 178abb0f93cSkardel ) 179abb0f93cSkardel { 180cdfa2a7eSchristos struct refclockproc * const pp = peer->procptr; 181cdfa2a7eSchristos struct instance * up; 1822950cc38Schristos int fd; 183abb0f93cSkardel char gpsdev[20]; 184abb0f93cSkardel 185abb0f93cSkardel /* 186abb0f93cSkardel * Open serial port 187abb0f93cSkardel */ 1883123f114Skardel snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit); 189*eabc0478Schristos fd = refclock_open(&peer->srcadr, gpsdev, SPEED232, LDISC_RAW); 1902950cc38Schristos if (fd <= 0) { 191cdfa2a7eSchristos jupiter_debug(peer, "jupiter_start", "open %s: %s", 192cdfa2a7eSchristos gpsdev, strerror(errno)); 193abb0f93cSkardel return (0); 194abb0f93cSkardel } 195abb0f93cSkardel 196abb0f93cSkardel /* Allocate unit structure */ 197cdfa2a7eSchristos up = emalloc_zero(sizeof(*up)); 198cdfa2a7eSchristos up->peer = peer; 199abb0f93cSkardel pp->io.clock_recv = jupiter_receive; 2002950cc38Schristos pp->io.srcclock = peer; 201abb0f93cSkardel pp->io.datalen = 0; 202abb0f93cSkardel pp->io.fd = fd; 203abb0f93cSkardel if (!io_addclock(&pp->io)) { 2043123f114Skardel close(fd); 2052950cc38Schristos pp->io.fd = -1; 206cdfa2a7eSchristos free(up); 207abb0f93cSkardel return (0); 208abb0f93cSkardel } 209cdfa2a7eSchristos pp->unitptr = up; 210abb0f93cSkardel 211abb0f93cSkardel /* 212abb0f93cSkardel * Initialize miscellaneous variables 213abb0f93cSkardel */ 214abb0f93cSkardel peer->precision = PRECISION; 215abb0f93cSkardel pp->clockdesc = DESCRIPTION; 216abb0f93cSkardel memcpy((char *)&pp->refid, REFID, 4); 217abb0f93cSkardel 218cdfa2a7eSchristos up->assert = 1; 219cdfa2a7eSchristos up->hardpps = 0; 220abb0f93cSkardel /* 221abb0f93cSkardel * Start the PPSAPI interface if it is there. Default to use 222abb0f93cSkardel * the assert edge and do not enable the kernel hardpps. 223abb0f93cSkardel */ 224cdfa2a7eSchristos if (time_pps_create(fd, &up->pps_handle) < 0) { 225cdfa2a7eSchristos up->pps_handle = 0; 226abb0f93cSkardel msyslog(LOG_ERR, 227abb0f93cSkardel "refclock_jupiter: time_pps_create failed: %m"); 228abb0f93cSkardel } 229cdfa2a7eSchristos else if (!jupiter_ppsapi(up)) 230abb0f93cSkardel goto clean_up; 231abb0f93cSkardel 232abb0f93cSkardel /* Ensure the receiver is properly configured */ 233cdfa2a7eSchristos if (!jupiter_config(up)) 234abb0f93cSkardel goto clean_up; 235abb0f93cSkardel 236cdfa2a7eSchristos jupiter_pps(up); /* get current PPS state */ 237abb0f93cSkardel return (1); 238abb0f93cSkardel 239abb0f93cSkardel clean_up: 240abb0f93cSkardel jupiter_shutdown(unit, peer); 241abb0f93cSkardel pp->unitptr = 0; 242abb0f93cSkardel return (0); 243abb0f93cSkardel } 244abb0f93cSkardel 245abb0f93cSkardel /* 246abb0f93cSkardel * jupiter_shutdown - shut down the clock 247abb0f93cSkardel */ 248abb0f93cSkardel static void 249abb0f93cSkardel jupiter_shutdown(int unit, struct peer *peer) 250abb0f93cSkardel { 251cdfa2a7eSchristos struct refclockproc * const pp = peer->procptr; 252cdfa2a7eSchristos struct instance * const up = pp->unitptr; 253abb0f93cSkardel 254cdfa2a7eSchristos if (!up) 255abb0f93cSkardel return; 256abb0f93cSkardel 257cdfa2a7eSchristos if (up->pps_handle) { 258cdfa2a7eSchristos time_pps_destroy(up->pps_handle); 259cdfa2a7eSchristos up->pps_handle = 0; 260abb0f93cSkardel } 261abb0f93cSkardel 2622950cc38Schristos if (pp->io.fd != -1) 263abb0f93cSkardel io_closeclock(&pp->io); 264cdfa2a7eSchristos free(up); 265abb0f93cSkardel } 266abb0f93cSkardel 267abb0f93cSkardel /* 268abb0f93cSkardel * jupiter_config - Configure the receiver 269abb0f93cSkardel */ 270abb0f93cSkardel static int 271cdfa2a7eSchristos jupiter_config(struct instance * const up) 272abb0f93cSkardel { 273cdfa2a7eSchristos jupiter_debug(up->peer, __func__, "init receiver"); 274abb0f93cSkardel 275abb0f93cSkardel /* 276abb0f93cSkardel * Initialize the unit variables 277abb0f93cSkardel */ 278cdfa2a7eSchristos up->sloppyclockflag = up->peer->procptr->sloppyclockflag; 279cdfa2a7eSchristos up->moving = !!(up->sloppyclockflag & CLK_FLAG2); 280cdfa2a7eSchristos if (up->moving) 281cdfa2a7eSchristos jupiter_debug(up->peer, __func__, "mobile platform"); 282abb0f93cSkardel 283cdfa2a7eSchristos ZERO(up->rcv_next); 284cdfa2a7eSchristos ZERO(up->ref_next); 285cdfa2a7eSchristos ZERO(up->piv_next); 286cdfa2a7eSchristos up->ssize = 0; 287abb0f93cSkardel 288abb0f93cSkardel /* Stop outputting all messages */ 289cdfa2a7eSchristos jupiter_canmsg(up, JUPITER_ALL); 290abb0f93cSkardel 291abb0f93cSkardel /* Request the receiver id so we can syslog the firmware version */ 292cdfa2a7eSchristos jupiter_reqonemsg(up, JUPITER_O_ID); 293abb0f93cSkardel 294abb0f93cSkardel /* Flag that this the id was requested (so we don't get called again) */ 295cdfa2a7eSchristos up->wantid = 1; 296abb0f93cSkardel 297abb0f93cSkardel /* Request perodic time mark pulse messages */ 298cdfa2a7eSchristos jupiter_reqmsg(up, JUPITER_O_PULSE, 1); 299abb0f93cSkardel 300abb0f93cSkardel /* Request perodic geodetic position status */ 301cdfa2a7eSchristos jupiter_reqmsg(up, JUPITER_O_GPOS, 1); 302abb0f93cSkardel 303abb0f93cSkardel /* Set application platform type */ 304cdfa2a7eSchristos if (up->moving) 305cdfa2a7eSchristos jupiter_platform(up, JUPITER_I_PLAT_MED); 306abb0f93cSkardel else 307cdfa2a7eSchristos jupiter_platform(up, JUPITER_I_PLAT_LOW); 308abb0f93cSkardel 309abb0f93cSkardel return (1); 310abb0f93cSkardel } 311abb0f93cSkardel 312cdfa2a7eSchristos static void 313cdfa2a7eSchristos jupiter_checkpps( 314cdfa2a7eSchristos struct refclockproc * const pp, 315cdfa2a7eSchristos struct instance * const up 316cdfa2a7eSchristos ) 317cdfa2a7eSchristos { 318cdfa2a7eSchristos l_fp tstamp, delta; 319cdfa2a7eSchristos struct calendar cd; 320cdfa2a7eSchristos 321cdfa2a7eSchristos if (jupiter_pps(up) || !up->piv_next.weeks) 322cdfa2a7eSchristos return; 323cdfa2a7eSchristos 324cdfa2a7eSchristos /* check delay between pulse message and pulse. */ 325cdfa2a7eSchristos delta = up->rcv_pps; /* set by jupiter_pps() */ 326cdfa2a7eSchristos L_SUB(&delta, &up->rcv_next); /* recv time pulse message */ 327cdfa2a7eSchristos if (delta.l_ui != 0 || delta.l_uf >= 0xC0000000) { 328cdfa2a7eSchristos up->ref_next.weeks = 0; /* consider as consumed... */ 329cdfa2a7eSchristos return; 330cdfa2a7eSchristos } 331cdfa2a7eSchristos 332cdfa2a7eSchristos pp->lastrec = up->rcv_pps; 333cdfa2a7eSchristos tstamp = ntpfp_from_gpsdatum(&up->ref_next); 334cdfa2a7eSchristos refclock_process_offset(pp, tstamp, up->rcv_pps, pp->fudgetime1); 335cdfa2a7eSchristos up->rcvtout = 2; 336cdfa2a7eSchristos 337cdfa2a7eSchristos gpscal_to_calendar(&cd, &up->ref_next); 338cdfa2a7eSchristos refclock_save_lcode(pp, ntpcal_iso8601std(NULL, 0, &cd), 339cdfa2a7eSchristos (size_t)-1); 340cdfa2a7eSchristos up->ref_next.weeks = 0; /* consumed... */ 341cdfa2a7eSchristos } 342cdfa2a7eSchristos 343cdfa2a7eSchristos /* 344cdfa2a7eSchristos * jupiter_ticker - process periodic checks 345cdfa2a7eSchristos */ 346cdfa2a7eSchristos static void 347cdfa2a7eSchristos jupiter_ticker(int unit, struct peer *peer) 348cdfa2a7eSchristos { 349cdfa2a7eSchristos struct refclockproc * const pp = peer->procptr; 350cdfa2a7eSchristos struct instance * const up = pp->unitptr; 351cdfa2a7eSchristos 352cdfa2a7eSchristos if (!up) 353cdfa2a7eSchristos return; 354cdfa2a7eSchristos 355cdfa2a7eSchristos /* check if we can add another sample now */ 356cdfa2a7eSchristos jupiter_checkpps(pp, up); 357cdfa2a7eSchristos 358cdfa2a7eSchristos /* check the pivot update cycle */ 359cdfa2a7eSchristos if (up->piv_hold && !--up->piv_hold) 360cdfa2a7eSchristos ZERO(up->piv_next); 361cdfa2a7eSchristos 362cdfa2a7eSchristos if (up->rcvtout) 363cdfa2a7eSchristos --up->rcvtout; 364cdfa2a7eSchristos else if (pp->coderecv != pp->codeproc) 365cdfa2a7eSchristos refclock_samples_expire(pp, 1); 366cdfa2a7eSchristos } 367cdfa2a7eSchristos 368abb0f93cSkardel /* 369abb0f93cSkardel * Initialize PPSAPI 370abb0f93cSkardel */ 371abb0f93cSkardel int 372abb0f93cSkardel jupiter_ppsapi( 373cdfa2a7eSchristos struct instance * const up /* unit structure pointer */ 374abb0f93cSkardel ) 375abb0f93cSkardel { 376abb0f93cSkardel int capability; 377abb0f93cSkardel 378cdfa2a7eSchristos if (time_pps_getcap(up->pps_handle, &capability) < 0) { 379abb0f93cSkardel msyslog(LOG_ERR, 380abb0f93cSkardel "refclock_jupiter: time_pps_getcap failed: %m"); 381abb0f93cSkardel return (0); 382abb0f93cSkardel } 383cdfa2a7eSchristos memset(&up->pps_params, 0, sizeof(pps_params_t)); 384cdfa2a7eSchristos if (!up->assert) 385cdfa2a7eSchristos up->pps_params.mode = capability & PPS_CAPTURECLEAR; 386abb0f93cSkardel else 387cdfa2a7eSchristos up->pps_params.mode = capability & PPS_CAPTUREASSERT; 388cdfa2a7eSchristos if (!(up->pps_params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) { 389abb0f93cSkardel msyslog(LOG_ERR, 390abb0f93cSkardel "refclock_jupiter: invalid capture edge %d", 391cdfa2a7eSchristos up->assert); 392abb0f93cSkardel return (0); 393abb0f93cSkardel } 394cdfa2a7eSchristos up->pps_params.mode |= PPS_TSFMT_TSPEC; 395cdfa2a7eSchristos if (time_pps_setparams(up->pps_handle, &up->pps_params) < 0) { 396abb0f93cSkardel msyslog(LOG_ERR, 397abb0f93cSkardel "refclock_jupiter: time_pps_setparams failed: %m"); 398abb0f93cSkardel return (0); 399abb0f93cSkardel } 400cdfa2a7eSchristos if (up->hardpps) { 401cdfa2a7eSchristos if (time_pps_kcbind(up->pps_handle, PPS_KC_HARDPPS, 402cdfa2a7eSchristos up->pps_params.mode & ~PPS_TSFMT_TSPEC, 403abb0f93cSkardel PPS_TSFMT_TSPEC) < 0) { 404abb0f93cSkardel msyslog(LOG_ERR, 405abb0f93cSkardel "refclock_jupiter: time_pps_kcbind failed: %m"); 406abb0f93cSkardel return (0); 407abb0f93cSkardel } 408ea66d795Schristos hardpps_enable = 1; 409abb0f93cSkardel } 410cdfa2a7eSchristos /* up->peer->precision = PPS_PRECISION; */ 411abb0f93cSkardel 412abb0f93cSkardel #if DEBUG 413abb0f93cSkardel if (debug) { 414cdfa2a7eSchristos time_pps_getparams(up->pps_handle, &up->pps_params); 415cdfa2a7eSchristos jupiter_debug(up->peer, __func__, 416abb0f93cSkardel "pps capability 0x%x version %d mode 0x%x kern %d", 417cdfa2a7eSchristos capability, up->pps_params.api_version, 418cdfa2a7eSchristos up->pps_params.mode, up->hardpps); 419abb0f93cSkardel } 420abb0f93cSkardel #endif 421abb0f93cSkardel 422abb0f93cSkardel return (1); 423abb0f93cSkardel } 424abb0f93cSkardel 425abb0f93cSkardel /* 426abb0f93cSkardel * Get PPSAPI timestamps. 427abb0f93cSkardel * 428abb0f93cSkardel * Return 0 on failure and 1 on success. 429abb0f93cSkardel */ 430abb0f93cSkardel static int 431cdfa2a7eSchristos jupiter_pps(struct instance * const up) 432abb0f93cSkardel { 433abb0f93cSkardel pps_info_t pps_info; 434abb0f93cSkardel struct timespec timeout, ts; 435abb0f93cSkardel l_fp tstmp; 436abb0f93cSkardel 437abb0f93cSkardel /* 438abb0f93cSkardel * Convert the timespec nanoseconds field to ntp l_fp units. 439abb0f93cSkardel */ 440cdfa2a7eSchristos if (up->pps_handle == 0) 441abb0f93cSkardel return 1; 442abb0f93cSkardel timeout.tv_sec = 0; 443abb0f93cSkardel timeout.tv_nsec = 0; 444cdfa2a7eSchristos memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t)); 445cdfa2a7eSchristos if (time_pps_fetch(up->pps_handle, PPS_TSFMT_TSPEC, &up->pps_info, 446abb0f93cSkardel &timeout) < 0) 447abb0f93cSkardel return 1; 448cdfa2a7eSchristos if (up->pps_params.mode & PPS_CAPTUREASSERT) { 449abb0f93cSkardel if (pps_info.assert_sequence == 450cdfa2a7eSchristos up->pps_info.assert_sequence) 451abb0f93cSkardel return 1; 452cdfa2a7eSchristos ts = up->pps_info.assert_timestamp; 453cdfa2a7eSchristos } else if (up->pps_params.mode & PPS_CAPTURECLEAR) { 454abb0f93cSkardel if (pps_info.clear_sequence == 455cdfa2a7eSchristos up->pps_info.clear_sequence) 456abb0f93cSkardel return 1; 457cdfa2a7eSchristos ts = up->pps_info.clear_timestamp; 458abb0f93cSkardel } else { 459abb0f93cSkardel return 1; 460abb0f93cSkardel } 461abb0f93cSkardel 462cdfa2a7eSchristos tstmp = tspec_stamp_to_lfp(ts); 463cdfa2a7eSchristos if (L_ISEQU(&tstmp, &up->rcv_pps)) 464cdfa2a7eSchristos return 1; 465cdfa2a7eSchristos 466cdfa2a7eSchristos up->rcv_pps = tstmp; 467abb0f93cSkardel return 0; 468abb0f93cSkardel } 469abb0f93cSkardel 470abb0f93cSkardel /* 471abb0f93cSkardel * jupiter_poll - jupiter watchdog routine 472abb0f93cSkardel */ 473abb0f93cSkardel static void 474abb0f93cSkardel jupiter_poll(int unit, struct peer *peer) 475abb0f93cSkardel { 476cdfa2a7eSchristos struct refclockproc * const pp = peer->procptr; 477cdfa2a7eSchristos struct instance * const up = pp->unitptr; 478abb0f93cSkardel 479cdfa2a7eSchristos pp->polls++; 480abb0f93cSkardel 481abb0f93cSkardel /* 482cdfa2a7eSchristos * If we have new samples since last poll, everything is fine. 483cdfa2a7eSchristos * if not, blarb loudly. 484abb0f93cSkardel */ 485cdfa2a7eSchristos if (pp->coderecv != pp->codeproc) { 486cdfa2a7eSchristos refclock_receive(peer); 487cdfa2a7eSchristos refclock_report(peer, CEVNT_NOMINAL); 488abb0f93cSkardel } else { 489abb0f93cSkardel refclock_report(peer, CEVNT_TIMEOUT); 490abb0f93cSkardel 491abb0f93cSkardel /* Request the receiver id to trigger a reconfig */ 492cdfa2a7eSchristos jupiter_reqonemsg(up, JUPITER_O_ID); 493cdfa2a7eSchristos up->wantid = 0; 494abb0f93cSkardel } 495abb0f93cSkardel } 496abb0f93cSkardel 497abb0f93cSkardel /* 498abb0f93cSkardel * jupiter_control - fudge control 499abb0f93cSkardel */ 500abb0f93cSkardel static void 501abb0f93cSkardel jupiter_control( 502abb0f93cSkardel int unit, /* unit (not used) */ 5032950cc38Schristos const struct refclockstat *in, /* input parameters (not used) */ 504abb0f93cSkardel struct refclockstat *out, /* output parameters (not used) */ 505abb0f93cSkardel struct peer *peer /* peer structure pointer */ 506abb0f93cSkardel ) 507abb0f93cSkardel { 508cdfa2a7eSchristos struct refclockproc * const pp = peer->procptr; 509cdfa2a7eSchristos struct instance * const up = pp->unitptr; 510cdfa2a7eSchristos 511abb0f93cSkardel u_char sloppyclockflag; 512abb0f93cSkardel 513cdfa2a7eSchristos up->assert = !(pp->sloppyclockflag & CLK_FLAG3); 514cdfa2a7eSchristos jupiter_ppsapi(up); 515abb0f93cSkardel 516cdfa2a7eSchristos sloppyclockflag = up->sloppyclockflag; 517cdfa2a7eSchristos up->sloppyclockflag = pp->sloppyclockflag; 518cdfa2a7eSchristos if ((up->sloppyclockflag & CLK_FLAG2) != 519abb0f93cSkardel (sloppyclockflag & CLK_FLAG2)) { 520e19314b7Schristos jupiter_debug(peer, __func__, 521abb0f93cSkardel "mode switch: reset receiver"); 522cdfa2a7eSchristos jupiter_config(up); 523abb0f93cSkardel return; 524abb0f93cSkardel } 525abb0f93cSkardel } 526abb0f93cSkardel 527abb0f93cSkardel /* 528abb0f93cSkardel * jupiter_receive - receive gps data 529abb0f93cSkardel * Gag me! 530abb0f93cSkardel */ 531abb0f93cSkardel static void 532cdfa2a7eSchristos jupiter_receive(struct recvbuf * const rbufp) 533abb0f93cSkardel { 534cdfa2a7eSchristos struct peer * const peer = rbufp->recv_peer; 535cdfa2a7eSchristos struct refclockproc * const pp = peer->procptr; 536cdfa2a7eSchristos struct instance * const up = pp->unitptr; 537cdfa2a7eSchristos 538e19314b7Schristos size_t bpcnt; 539cdfa2a7eSchristos int cc, size; 540e19314b7Schristos const char *cp; 541abb0f93cSkardel u_char *bp; 542abb0f93cSkardel u_short *sp; 543abb0f93cSkardel struct jid *ip; 544abb0f93cSkardel struct jheader *hp; 545abb0f93cSkardel 546abb0f93cSkardel /* Initialize pointers and read the timecode and timestamp */ 547abb0f93cSkardel bp = (u_char *)rbufp->recv_buffer; 548abb0f93cSkardel bpcnt = rbufp->recv_length; 549abb0f93cSkardel 550abb0f93cSkardel /* This shouldn't happen */ 551cdfa2a7eSchristos if (bpcnt > sizeof(up->sbuf) - up->ssize) 552cdfa2a7eSchristos bpcnt = sizeof(up->sbuf) - up->ssize; 553abb0f93cSkardel 554abb0f93cSkardel /* Append to input buffer */ 555cdfa2a7eSchristos memcpy((u_char *)up->sbuf + up->ssize, bp, bpcnt); 556cdfa2a7eSchristos up->ssize += bpcnt; 557abb0f93cSkardel 558abb0f93cSkardel /* While there's at least a header and we parse an intact message */ 559cdfa2a7eSchristos while (up->ssize > (int)sizeof(*hp) && (cc = jupiter_recv(up)) > 0) { 560cdfa2a7eSchristos hp = (struct jheader *)up->sbuf; 561abb0f93cSkardel sp = (u_short *)(hp + 1); 562abb0f93cSkardel size = cc - sizeof(*hp); 563abb0f93cSkardel switch (getshort(hp->id)) { 564abb0f93cSkardel 565abb0f93cSkardel case JUPITER_O_PULSE: 566cdfa2a7eSchristos /* first see if we can push another sample: */ 567cdfa2a7eSchristos jupiter_checkpps(pp, up); 568cdfa2a7eSchristos 569abb0f93cSkardel if (size != sizeof(struct jpulse)) { 570e19314b7Schristos jupiter_debug(peer, __func__, 571e19314b7Schristos "pulse: len %d != %u", 572abb0f93cSkardel size, (int)sizeof(struct jpulse)); 573abb0f93cSkardel refclock_report(peer, CEVNT_BADREPLY); 574abb0f93cSkardel break; 575abb0f93cSkardel } 576abb0f93cSkardel 577cdfa2a7eSchristos /* Parse timecode (even when there's no pps) 578cdfa2a7eSchristos * 579cdfa2a7eSchristos * There appears to be a firmware bug related to 580cdfa2a7eSchristos * the pulse message; in addition to the one per 581cdfa2a7eSchristos * second messages, we get an extra pulse 582abb0f93cSkardel * message once an hour (on the anniversary of 583abb0f93cSkardel * the cold start). It seems to come 200 ms 584cdfa2a7eSchristos * after the one requested. 585cdfa2a7eSchristos * 586cdfa2a7eSchristos * But since we feed samples only when a new PPS 587cdfa2a7eSchristos * pulse is found we can simply ignore that and 588cdfa2a7eSchristos * aggregate/update any existing timing message. 589abb0f93cSkardel */ 590cdfa2a7eSchristos if ((cp = jupiter_parse_t(up, sp, rbufp->recv_time)) != NULL) { 591e19314b7Schristos jupiter_debug(peer, __func__, 592e19314b7Schristos "pulse: %s", cp); 593abb0f93cSkardel } 594abb0f93cSkardel break; 595abb0f93cSkardel 596abb0f93cSkardel case JUPITER_O_GPOS: 597abb0f93cSkardel if (size != sizeof(struct jgpos)) { 598e19314b7Schristos jupiter_debug(peer, __func__, 599e19314b7Schristos "gpos: len %d != %u", 600abb0f93cSkardel size, (int)sizeof(struct jgpos)); 601abb0f93cSkardel refclock_report(peer, CEVNT_BADREPLY); 602abb0f93cSkardel break; 603abb0f93cSkardel } 604abb0f93cSkardel 605cdfa2a7eSchristos if ((cp = jupiter_parse_gpos(up, sp)) != NULL) { 606e19314b7Schristos jupiter_debug(peer, __func__, 607e19314b7Schristos "gpos: %s", cp); 608abb0f93cSkardel break; 609abb0f93cSkardel } 610abb0f93cSkardel break; 611abb0f93cSkardel 612abb0f93cSkardel case JUPITER_O_ID: 613abb0f93cSkardel if (size != sizeof(struct jid)) { 614e19314b7Schristos jupiter_debug(peer, __func__, 615e19314b7Schristos "id: len %d != %u", 616abb0f93cSkardel size, (int)sizeof(struct jid)); 617abb0f93cSkardel refclock_report(peer, CEVNT_BADREPLY); 618abb0f93cSkardel break; 619abb0f93cSkardel } 620abb0f93cSkardel /* 621abb0f93cSkardel * If we got this message because the Jupiter 622abb0f93cSkardel * just powered instance, it needs to be reconfigured. 623abb0f93cSkardel */ 624abb0f93cSkardel ip = (struct jid *)sp; 625e19314b7Schristos jupiter_debug(peer, __func__, 626e19314b7Schristos "%s chan ver %s, %s (%s)", 627abb0f93cSkardel ip->chans, ip->vers, ip->date, ip->opts); 628abb0f93cSkardel msyslog(LOG_DEBUG, 629abb0f93cSkardel "jupiter_receive: %s chan ver %s, %s (%s)", 630abb0f93cSkardel ip->chans, ip->vers, ip->date, ip->opts); 631cdfa2a7eSchristos if (up->wantid) 632cdfa2a7eSchristos up->wantid = 0; 633abb0f93cSkardel else { 634e19314b7Schristos jupiter_debug(peer, __func__, "reset receiver"); 635cdfa2a7eSchristos jupiter_config(up); 636abb0f93cSkardel /* 637abb0f93cSkardel * Restore since jupiter_config() just 638abb0f93cSkardel * zeroed it 639abb0f93cSkardel */ 640cdfa2a7eSchristos up->ssize = cc; 641abb0f93cSkardel } 642abb0f93cSkardel break; 643abb0f93cSkardel 644abb0f93cSkardel default: 645e19314b7Schristos jupiter_debug(peer, __func__, "unknown message id %d", 646abb0f93cSkardel getshort(hp->id)); 647abb0f93cSkardel break; 648abb0f93cSkardel } 649cdfa2a7eSchristos up->ssize -= cc; 650cdfa2a7eSchristos if (up->ssize < 0) { 651abb0f93cSkardel fprintf(stderr, "jupiter_recv: negative ssize!\n"); 652abb0f93cSkardel abort(); 653cdfa2a7eSchristos } else if (up->ssize > 0) 654cdfa2a7eSchristos memcpy(up->sbuf, (u_char *)up->sbuf + cc, up->ssize); 655abb0f93cSkardel } 656abb0f93cSkardel } 657abb0f93cSkardel 658e19314b7Schristos static const char * 659cdfa2a7eSchristos jupiter_parse_t( 660cdfa2a7eSchristos struct instance * const up, 661cdfa2a7eSchristos u_short * sp, 662cdfa2a7eSchristos l_fp rcvtime 663cdfa2a7eSchristos ) 664abb0f93cSkardel { 665abb0f93cSkardel struct jpulse *jp; 666abb0f93cSkardel u_int32 sweek; 667abb0f93cSkardel u_short flags; 668cdfa2a7eSchristos l_fp fofs; 669abb0f93cSkardel 670abb0f93cSkardel jp = (struct jpulse *)sp; 671cdfa2a7eSchristos flags = getshort(jp->flags); 672cdfa2a7eSchristos 673cdfa2a7eSchristos /* Toss if not designated "valid" by the gps. 674cdfa2a7eSchristos * !!NOTE!! do *not* kill data received so far! 675cdfa2a7eSchristos */ 676cdfa2a7eSchristos if ((flags & JUPITER_O_PULSE_VALID) == 0) { 677cdfa2a7eSchristos refclock_report(up->peer, CEVNT_BADTIME); 678cdfa2a7eSchristos return ("time mark not valid"); 679cdfa2a7eSchristos } 680cdfa2a7eSchristos 681cdfa2a7eSchristos up->rcv_next = rcvtime; /* remember when this happened */ 682abb0f93cSkardel 683abb0f93cSkardel /* The timecode is presented as seconds into the current GPS week */ 684abb0f93cSkardel sweek = DS2UI(jp->sweek) % WEEKSECS; 685cdfa2a7eSchristos /* check if we have to apply the UTC offset ourselves */ 686cdfa2a7eSchristos if ((flags & JUPITER_O_PULSE_UTC) == 0) { 687cdfa2a7eSchristos struct timespec tofs; 688cdfa2a7eSchristos tofs.tv_sec = getshort(jp->offs); 689cdfa2a7eSchristos tofs.tv_nsec = DS2I(jp->offns); 690cdfa2a7eSchristos fofs = tspec_intv_to_lfp(tofs); 691cdfa2a7eSchristos L_NEG(&fofs); 692cdfa2a7eSchristos } else { 693cdfa2a7eSchristos ZERO(fofs); 694cdfa2a7eSchristos } 695abb0f93cSkardel 696abb0f93cSkardel /* 697abb0f93cSkardel * If we don't know the current GPS week, calculate it from the 698abb0f93cSkardel * current time. (It's too bad they didn't include this 699cdfa2a7eSchristos * important value in the pulse message). 700abb0f93cSkardel * 701cdfa2a7eSchristos * So we pick the pivot value from the other messages like gpos 702cdfa2a7eSchristos * or chan if we can. Of course, the PULSE message can be in UTC 703cdfa2a7eSchristos * or GPS time scale, and the other messages are simply always 704cdfa2a7eSchristos * GPS time. 705cdfa2a7eSchristos * 706cdfa2a7eSchristos * But as long as the difference between the time stamps is less 707cdfa2a7eSchristos * than a half week, the unfolding of a week time is unambigeous 708cdfa2a7eSchristos * and well suited for the problem we have here. And we won't 709cdfa2a7eSchristos * see *that* many leap seconds, ever. 710abb0f93cSkardel */ 711cdfa2a7eSchristos if (up->piv_next.weeks) { 712cdfa2a7eSchristos up->ref_next = gpscal_from_weektime2( 713cdfa2a7eSchristos sweek, fofs, &up->piv_next); 714cdfa2a7eSchristos up->piv_next = up->ref_next; 715cdfa2a7eSchristos } else { 716cdfa2a7eSchristos up->ref_next = gpscal_from_weektime1( 717cdfa2a7eSchristos sweek, fofs, rcvtime); 718abb0f93cSkardel } 719abb0f93cSkardel 720abb0f93cSkardel 721abb0f93cSkardel 722abb0f93cSkardel return (NULL); 723abb0f93cSkardel } 724abb0f93cSkardel 725e19314b7Schristos static const char * 726cdfa2a7eSchristos jupiter_parse_gpos( 727cdfa2a7eSchristos struct instance * const up, 728cdfa2a7eSchristos u_short * sp 729cdfa2a7eSchristos ) 730abb0f93cSkardel { 731abb0f93cSkardel struct jgpos *jg; 732cdfa2a7eSchristos struct calendar tref; 733abb0f93cSkardel char *cp; 734cdfa2a7eSchristos struct timespec tofs; 735cdfa2a7eSchristos uint16_t raw_week; 736cdfa2a7eSchristos uint32_t raw_secs; 737abb0f93cSkardel 738abb0f93cSkardel jg = (struct jgpos *)sp; 739abb0f93cSkardel 740abb0f93cSkardel if (jg->navval != 0) { 741abb0f93cSkardel /* 742abb0f93cSkardel * Solution not valid. Use caution and refuse 743abb0f93cSkardel * to determine GPS week from this message. 744abb0f93cSkardel */ 745abb0f93cSkardel return ("Navigation solution not valid"); 746abb0f93cSkardel } 747abb0f93cSkardel 748cdfa2a7eSchristos raw_week = getshort(jg->gweek); 749cdfa2a7eSchristos raw_secs = DS2UI(jg->sweek); 750cdfa2a7eSchristos tofs.tv_sec = 0; 751cdfa2a7eSchristos tofs.tv_nsec = DS2UI(jg->nsweek); 752cdfa2a7eSchristos up->piv_next = gpscal_from_gpsweek(raw_week, raw_secs, 753cdfa2a7eSchristos tspec_intv_to_lfp(tofs)); 754cdfa2a7eSchristos up->piv_hold = 60; 75503cfe0ffSchristos 756cdfa2a7eSchristos gpscal_to_calendar(&tref, &up->piv_next); 757cdfa2a7eSchristos cp = ntpcal_iso8601std(NULL, 0, &tref); 758cdfa2a7eSchristos jupiter_debug(up->peer, __func__, 759cdfa2a7eSchristos "GPS %s (gweek/sweek %hu/%u)", 760cdfa2a7eSchristos cp, (unsigned short)raw_week, (unsigned int)raw_secs); 761abb0f93cSkardel return (NULL); 762abb0f93cSkardel } 763abb0f93cSkardel 764abb0f93cSkardel /* 765abb0f93cSkardel * jupiter_debug - print debug messages 766abb0f93cSkardel */ 767abb0f93cSkardel static void 7682950cc38Schristos jupiter_debug( 7692950cc38Schristos struct peer * peer, 7702950cc38Schristos const char * function, 7712950cc38Schristos const char * fmt, 7722950cc38Schristos ... 7732950cc38Schristos ) 774abb0f93cSkardel { 775abb0f93cSkardel char buffer[200]; 776abb0f93cSkardel va_list ap; 777abb0f93cSkardel 778abb0f93cSkardel va_start(ap, fmt); 779abb0f93cSkardel /* 780abb0f93cSkardel * Print debug message to stdout 781abb0f93cSkardel * In the future, we may want to get get more creative... 782abb0f93cSkardel */ 7832950cc38Schristos mvsnprintf(buffer, sizeof(buffer), fmt, ap); 7842950cc38Schristos record_clock_stats(&peer->srcadr, buffer); 785abb0f93cSkardel #ifdef DEBUG 786abb0f93cSkardel if (debug) { 7872950cc38Schristos printf("%s: %s\n", function, buffer); 788abb0f93cSkardel fflush(stdout); 789abb0f93cSkardel } 790abb0f93cSkardel #endif 791abb0f93cSkardel 792abb0f93cSkardel va_end(ap); 793abb0f93cSkardel } 794abb0f93cSkardel 795abb0f93cSkardel /* Checksum and transmit a message to the Jupiter */ 796abb0f93cSkardel static char * 797cdfa2a7eSchristos jupiter_send( 798cdfa2a7eSchristos struct instance * const up, 799cdfa2a7eSchristos struct jheader * hp 800cdfa2a7eSchristos ) 801abb0f93cSkardel { 802abb0f93cSkardel u_int len, size; 803e19314b7Schristos ssize_t cc; 804abb0f93cSkardel u_short *sp; 805abb0f93cSkardel static char errstr[132]; 806abb0f93cSkardel 807abb0f93cSkardel size = sizeof(*hp); 808abb0f93cSkardel hp->hsum = putshort(jupiter_cksum((u_short *)hp, 809abb0f93cSkardel (size / sizeof(u_short)) - 1)); 810abb0f93cSkardel len = getshort(hp->len); 811abb0f93cSkardel if (len > 0) { 812abb0f93cSkardel sp = (u_short *)(hp + 1); 813abb0f93cSkardel sp[len] = putshort(jupiter_cksum(sp, len)); 814abb0f93cSkardel size += (len + 1) * sizeof(u_short); 815abb0f93cSkardel } 816abb0f93cSkardel 817cdfa2a7eSchristos if ((cc = write(up->peer->procptr->io.fd, (char *)hp, size)) < 0) { 8182950cc38Schristos msnprintf(errstr, sizeof(errstr), "write: %m"); 819abb0f93cSkardel return (errstr); 8202950cc38Schristos } else if (cc != (int)size) { 8212950cc38Schristos snprintf(errstr, sizeof(errstr), "short write (%zd != %u)", cc, size); 822abb0f93cSkardel return (errstr); 823abb0f93cSkardel } 824abb0f93cSkardel return (NULL); 825abb0f93cSkardel } 826abb0f93cSkardel 827abb0f93cSkardel /* Request periodic message output */ 828abb0f93cSkardel static struct { 829abb0f93cSkardel struct jheader jheader; 830abb0f93cSkardel struct jrequest jrequest; 831abb0f93cSkardel } reqmsg = { 832abb0f93cSkardel { putshort(JUPITER_SYNC), 0, 833abb0f93cSkardel putshort((sizeof(struct jrequest) / sizeof(u_short)) - 1), 834abb0f93cSkardel 0, JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | 835abb0f93cSkardel JUPITER_FLAG_CONN | JUPITER_FLAG_LOG, 0 }, 836abb0f93cSkardel { 0, 0, 0, 0 } 837abb0f93cSkardel }; 838abb0f93cSkardel 839abb0f93cSkardel /* An interval of zero means to output on trigger */ 840abb0f93cSkardel static void 841cdfa2a7eSchristos jupiter_reqmsg( 842cdfa2a7eSchristos struct instance * const up, 843cdfa2a7eSchristos u_int id, 844cdfa2a7eSchristos u_int interval 845cdfa2a7eSchristos ) 846abb0f93cSkardel { 847abb0f93cSkardel struct jheader *hp; 848abb0f93cSkardel struct jrequest *rp; 849abb0f93cSkardel char *cp; 850abb0f93cSkardel 851abb0f93cSkardel hp = &reqmsg.jheader; 852abb0f93cSkardel hp->id = putshort(id); 853abb0f93cSkardel rp = &reqmsg.jrequest; 854abb0f93cSkardel rp->trigger = putshort(interval == 0); 855abb0f93cSkardel rp->interval = putshort(interval); 856cdfa2a7eSchristos if ((cp = jupiter_send(up, hp)) != NULL) 857cdfa2a7eSchristos jupiter_debug(up->peer, __func__, "%u: %s", id, cp); 858abb0f93cSkardel } 859abb0f93cSkardel 860abb0f93cSkardel /* Cancel periodic message output */ 861abb0f93cSkardel static struct jheader canmsg = { 862abb0f93cSkardel putshort(JUPITER_SYNC), 0, 0, 0, 863abb0f93cSkardel JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_DISC, 864abb0f93cSkardel 0 865abb0f93cSkardel }; 866abb0f93cSkardel 867abb0f93cSkardel static void 868cdfa2a7eSchristos jupiter_canmsg( 869cdfa2a7eSchristos struct instance * const up, 870cdfa2a7eSchristos u_int id 871cdfa2a7eSchristos ) 872abb0f93cSkardel { 873abb0f93cSkardel struct jheader *hp; 874abb0f93cSkardel char *cp; 875abb0f93cSkardel 876abb0f93cSkardel hp = &canmsg; 877abb0f93cSkardel hp->id = putshort(id); 878cdfa2a7eSchristos if ((cp = jupiter_send(up, hp)) != NULL) 879cdfa2a7eSchristos jupiter_debug(up->peer, __func__, "%u: %s", id, cp); 880abb0f93cSkardel } 881abb0f93cSkardel 882abb0f93cSkardel /* Request a single message output */ 883abb0f93cSkardel static struct jheader reqonemsg = { 884abb0f93cSkardel putshort(JUPITER_SYNC), 0, 0, 0, 885abb0f93cSkardel JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK | JUPITER_FLAG_QUERY, 886abb0f93cSkardel 0 887abb0f93cSkardel }; 888abb0f93cSkardel 889abb0f93cSkardel static void 890cdfa2a7eSchristos jupiter_reqonemsg( 891cdfa2a7eSchristos struct instance * const up, 892cdfa2a7eSchristos u_int id 893cdfa2a7eSchristos ) 894abb0f93cSkardel { 895abb0f93cSkardel struct jheader *hp; 896abb0f93cSkardel char *cp; 897abb0f93cSkardel 898abb0f93cSkardel hp = &reqonemsg; 899abb0f93cSkardel hp->id = putshort(id); 900cdfa2a7eSchristos if ((cp = jupiter_send(up, hp)) != NULL) 901cdfa2a7eSchristos jupiter_debug(up->peer, __func__, "%u: %s", id, cp); 902abb0f93cSkardel } 903abb0f93cSkardel 904abb0f93cSkardel /* Set the platform dynamics */ 905abb0f93cSkardel static struct { 906abb0f93cSkardel struct jheader jheader; 907abb0f93cSkardel struct jplat jplat; 908abb0f93cSkardel } platmsg = { 909abb0f93cSkardel { putshort(JUPITER_SYNC), putshort(JUPITER_I_PLAT), 910abb0f93cSkardel putshort((sizeof(struct jplat) / sizeof(u_short)) - 1), 0, 911abb0f93cSkardel JUPITER_FLAG_REQUEST | JUPITER_FLAG_NAK, 0 }, 912abb0f93cSkardel { 0, 0, 0 } 913abb0f93cSkardel }; 914abb0f93cSkardel 915abb0f93cSkardel static void 916cdfa2a7eSchristos jupiter_platform( 917cdfa2a7eSchristos struct instance * const up, 918cdfa2a7eSchristos u_int platform 919cdfa2a7eSchristos ) 920abb0f93cSkardel { 921abb0f93cSkardel struct jheader *hp; 922abb0f93cSkardel struct jplat *pp; 923abb0f93cSkardel char *cp; 924abb0f93cSkardel 925abb0f93cSkardel hp = &platmsg.jheader; 926abb0f93cSkardel pp = &platmsg.jplat; 927abb0f93cSkardel pp->platform = putshort(platform); 928cdfa2a7eSchristos if ((cp = jupiter_send(up, hp)) != NULL) 929cdfa2a7eSchristos jupiter_debug(up->peer, __func__, "%u: %s", platform, cp); 930abb0f93cSkardel } 931abb0f93cSkardel 932abb0f93cSkardel /* Checksum "len" shorts */ 933abb0f93cSkardel static u_short 934abb0f93cSkardel jupiter_cksum(u_short *sp, u_int len) 935abb0f93cSkardel { 936abb0f93cSkardel u_short sum, x; 937abb0f93cSkardel 938abb0f93cSkardel sum = 0; 939abb0f93cSkardel while (len-- > 0) { 940abb0f93cSkardel x = *sp++; 941abb0f93cSkardel sum += getshort(x); 942abb0f93cSkardel } 943abb0f93cSkardel return (~sum + 1); 944abb0f93cSkardel } 945abb0f93cSkardel 946abb0f93cSkardel /* Return the size of the next message (or zero if we don't have it all yet) */ 947abb0f93cSkardel static int 948cdfa2a7eSchristos jupiter_recv( 949cdfa2a7eSchristos struct instance * const up 950cdfa2a7eSchristos ) 951abb0f93cSkardel { 952abb0f93cSkardel int n, len, size, cc; 953abb0f93cSkardel struct jheader *hp; 954abb0f93cSkardel u_char *bp; 955abb0f93cSkardel u_short *sp; 956abb0f93cSkardel 957abb0f93cSkardel /* Must have at least a header's worth */ 958abb0f93cSkardel cc = sizeof(*hp); 959cdfa2a7eSchristos size = up->ssize; 960abb0f93cSkardel if (size < cc) 961abb0f93cSkardel return (0); 962abb0f93cSkardel 963abb0f93cSkardel /* Search for the sync short if missing */ 964cdfa2a7eSchristos sp = up->sbuf; 965abb0f93cSkardel hp = (struct jheader *)sp; 966abb0f93cSkardel if (getshort(hp->sync) != JUPITER_SYNC) { 967abb0f93cSkardel /* Wasn't at the front, sync up */ 968cdfa2a7eSchristos jupiter_debug(up->peer, __func__, "syncing"); 969abb0f93cSkardel bp = (u_char *)sp; 970abb0f93cSkardel n = size; 971abb0f93cSkardel while (n >= 2) { 972abb0f93cSkardel if (bp[0] != (JUPITER_SYNC & 0xff)) { 973abb0f93cSkardel /* 974cdfa2a7eSchristos jupiter_debug(up->peer, __func__, 975e19314b7Schristos "{0x%x}", bp[0]); 976abb0f93cSkardel */ 977abb0f93cSkardel ++bp; 978abb0f93cSkardel --n; 979abb0f93cSkardel continue; 980abb0f93cSkardel } 981abb0f93cSkardel if (bp[1] == ((JUPITER_SYNC >> 8) & 0xff)) 982abb0f93cSkardel break; 983abb0f93cSkardel /* 984cdfa2a7eSchristos jupiter_debug(up->peer, __func__, 985e19314b7Schristos "{0x%x 0x%x}", bp[0], bp[1]); 986abb0f93cSkardel */ 987abb0f93cSkardel bp += 2; 988abb0f93cSkardel n -= 2; 989abb0f93cSkardel } 990abb0f93cSkardel /* 991cdfa2a7eSchristos jupiter_debug(up->peer, __func__, "\n"); 992abb0f93cSkardel */ 993abb0f93cSkardel /* Shuffle data to front of input buffer */ 994abb0f93cSkardel if (n > 0) 995abb0f93cSkardel memcpy(sp, bp, n); 996abb0f93cSkardel size = n; 997cdfa2a7eSchristos up->ssize = size; 998abb0f93cSkardel if (size < cc || hp->sync != JUPITER_SYNC) 999abb0f93cSkardel return (0); 1000abb0f93cSkardel } 1001abb0f93cSkardel 1002abb0f93cSkardel if (jupiter_cksum(sp, (cc / sizeof(u_short) - 1)) != 1003abb0f93cSkardel getshort(hp->hsum)) { 1004cdfa2a7eSchristos jupiter_debug(up->peer, __func__, "bad header checksum!"); 1005abb0f93cSkardel /* This is drastic but checksum errors should be rare */ 1006cdfa2a7eSchristos up->ssize = 0; 1007abb0f93cSkardel return (0); 1008abb0f93cSkardel } 1009abb0f93cSkardel 1010abb0f93cSkardel /* Check for a payload */ 1011abb0f93cSkardel len = getshort(hp->len); 1012abb0f93cSkardel if (len > 0) { 1013abb0f93cSkardel n = (len + 1) * sizeof(u_short); 1014abb0f93cSkardel /* Not enough data yet */ 1015abb0f93cSkardel if (size < cc + n) 1016abb0f93cSkardel return (0); 1017abb0f93cSkardel 1018abb0f93cSkardel /* Check payload checksum */ 1019abb0f93cSkardel sp = (u_short *)(hp + 1); 1020abb0f93cSkardel if (jupiter_cksum(sp, len) != getshort(sp[len])) { 1021cdfa2a7eSchristos jupiter_debug(up->peer, 1022e19314b7Schristos __func__, "bad payload checksum!"); 1023abb0f93cSkardel /* This is drastic but checksum errors should be rare */ 1024cdfa2a7eSchristos up->ssize = 0; 1025abb0f93cSkardel return (0); 1026abb0f93cSkardel } 1027abb0f93cSkardel cc += n; 1028abb0f93cSkardel } 1029abb0f93cSkardel return (cc); 1030abb0f93cSkardel } 1031abb0f93cSkardel 1032abb0f93cSkardel #else /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */ 1033*eabc0478Schristos NONEMPTY_TRANSLATION_UNIT 1034abb0f93cSkardel #endif /* not (REFCLOCK && CLOCK_JUPITER && HAVE_PPSAPI) */ 1035