refclock_oncore.c 120 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
 * ----------------------------------------------------------------------------
 *
 * refclock_oncore.c
 *
 * Driver for some of the various the Motorola Oncore GPS receivers.
12 13
 *   should work with Basic, PVT6, VP, UT, UT+, GT, GT+, SL, M12, M12+T
 *	The receivers with TRAIM (VP, UT, UT+, M12+T), will be more accurate
14
 *	   than the others.
15
 *	The receivers without position hold (GT, GT+) will be less accurate.
16 17 18 19
 *
 * Tested with:
 *
 *		(UT)				   (VP)
20
 *   COPYRIGHT 1991-1997 MOTOROLA INC.	COPYRIGHT 1991-1996 MOTOROLA INC.
21
 *   SFTW P/N #     98-P36848P		SFTW P/N # 98-P36830P
22 23 24 25 26 27 28 29 30
 *   SOFTWARE VER # 2			SOFTWARE VER # 8
 *   SOFTWARE REV # 2			SOFTWARE REV # 8
 *   SOFTWARE DATE  APR 24 1998 	SOFTWARE DATE  06 Aug 1996
 *   MODEL #	R1121N1114		MODEL #    B4121P1155
 *   HWDR P/N # 1			HDWR P/N # _
 *   SERIAL #	R0010A			SERIAL #   SSG0226478
 *   MANUFACTUR DATE 6H07		MANUFACTUR DATE 7E02
 *					OPTIONS LIST	IB
 *
31
 *	      (Basic)				   (M12)
32 33 34 35 36 37
 *   COPYRIGHT 1991-1994 MOTOROLA INC.	COPYRIGHT 1991-2000 MOTOROLA INC.
 *   SFTW P/N # 98-P39949M		SFTW P/N # 61-G10002A
 *   SOFTWARE VER # 5			SOFTWARE VER # 1
 *   SOFTWARE REV # 0			SOFTWARE REV # 3
 *   SOFTWARE DATE  20 JAN 1994 	SOFTWARE DATE  Mar 13 2000
 *   MODEL #	A11121P116		MODEL #    P143T12NR1
38
 *   HDWR P/N # _			HWDR P/N # 1
39 40 41
 *   SERIAL #	SSG0049809		SERIAL #   P003UD
 *   MANUFACTUR DATE 417AMA199		MANUFACTUR DATE 0C27
 *   OPTIONS LIST    AB
42
 *
43 44 45 46 47 48 49 50 51 52 53 54
 *	      (M12+T)				  (M12+T later version)
 *   COPYRIGHT 1991-2002 MOTOROLA INC.	COPYRIGHT 1991-2003 MOTOROLA INC.
 *   SFTW P/N #     61-G10268A		SFTW P/N #     61-G10268A
 *   SOFTWARE VER # 2			SOFTWARE VER # 2
 *   SOFTWARE REV # 0			SOFTWARE REV # 1
 *   SOFTWARE DATE  AUG 14 2002 	SOFTWARE DATE  APR 16 2003
 *   MODEL #	P283T12T11		MODEL #    P273T12T12
 *   HWDR P/N # 2			HWDR P/N # 2
 *   SERIAL #	P04DC2			SERIAL #   P05Z7Z
 *   MANUFACTUR DATE 2J17		MANUFACTUR DATE 3G15
 *
 * --------------------------------------------------------------------------
Reg Clemens's avatar
Reg Clemens committed
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
 * Reg Clemens (June 2009)
 * BUG[1220] OK, big patch, but mostly done mechanically.  Change direct calls to write
 * to clockstats to a call to oncore_log, which now calls the old routine plus msyslog.
 * Have to set the LOG_LEVELS of the calls for msyslog, and this was done by hand. New
 * routine oncore_log.
 * --------------------------------------------------------------------------
 * Reg Clemens (June 2009)
 * BUG[1218] The comment on where the oncore driver gets its input file does not
 * agree with the code.  Change the comment.
 * --------------------------------------------------------------------------
 * Reg Clemens (June 2009)
 * change exit statements to return(0) in main program.  I had assumed that if the
 * PPS driver did not start for some reason, we shuould stop NTPD itelf.  Others
 * disagree.  We now give an ERR log message and stop this driver.
 * --------------------------------------------------------------------------
 * Reg Clemens (June 2009)
 * A bytes available message for the input subsystem (Debug message).
 * --------------------------------------------------------------------------
 * Reg Clemens (Nov 2008)
 * This code adds a message for TRAIM messages.  Users often worry about the
 * driver not starting up, and it is often because of signal strength being low.
 * Low signal strength will give TRAIM messages.
 * --------------------------------------------------------------------------
 * Reg Clemens (Nov 2008)
 * Add waiting on Almanac Message.
 * --------------------------------------------------------------------------
 * Reg Clemens (Nov 2008)
 * Add back in @@Bl code to do the @@Bj/@@Gj that is in later ONCOREs
 * LEAP SECONDS: All of the ONCORE receivers, VP -> M12T have the @@Bj command
 * that says 'Leap Pending'.  As documented it only becomes true in the month
 * before the leap second is to be applied, but in practice at least some of
 * the receivers turn this indicator on as soon as the message is posted, which
 * can be 6months early.  As such, we use the Bj command to turn on the
 * instance->pp->leap indicator but only run this test in December and June for
 * updates on 1Jan and 1July.
 *
 * The @@Gj command exists in later ONCOREs, and it gives the exact date
 * and size of the Leap Update.  It can be emulated in the VP using the @@Bl
 * command which reads the raw Satellite Broadcast Messages.
 * We use these two commands to print informative messages in the clockstats
 * file once per day as soon as the message appears on the satellites.
 * --------------------------------------------------------------------------
97 98 99 100 101 102 103 104 105 106 107 108
 * Reg Clemens (Feb 2006)
 * Fix some gcc4 compiler complaints
 * Fix possible segfault in oncore_init_shmem
 * change all (possible) fprintf(stderr, to record_clock_stats
 * Apply patch from Russell J. Yount <rjy@cmu.edu> Fixed (new) MT12+T UTC not correct
 *   immediately after new Almanac Read.
 * Apply patch for new PPS implementation by Rodolfo Giometti <giometti@linux.it>
 *   now code can use old Ulrich Windl <Ulrich.Windl@rz.uni-regensburg.de> or
 *   the new one.  Compiles depending on timepps.h seen.
 * --------------------------------------------------------------------------
 * Luis Batanero Guerrero <luisba@rao.es> (Dec 2005) Patch for leap seconds
 * (the oncore driver was setting the wrong ntpd variable)
109
 * --------------------------------------------------------------------------
110 111 112 113 114
 * Reg.Clemens (Mar 2004)
 * Support for interfaces other than PPSAPI removed, for Solaris, SunOS,
 * SCO, you now need to use one of the timepps.h files in the root dir.
 * this driver will 'grab' it for you if you dont have one in /usr/include
 * --------------------------------------------------------------------------
115
 * This code uses the two devices
116 117
 *	/dev/oncore.serial.n
 *	/dev/oncore.pps.n
118 119
 * which may be linked to the same device.
 * and can read initialization data from the file
120 121
 *	/etc/ntp.oncoreN, /etc/ntp.oncore.N, or /etc/ntp.oncore, where
 *	n or N are the unit number, viz 127.127.30.N.
122 123
 * --------------------------------------------------------------------------
 * Reg.Clemens <reg@dwf.com> Sep98.
124 125
 *  Original code written for FreeBSD.
 *  With these mods it works on FreeBSD, SunOS, Solaris and Linux
126 127 128
 *    (SunOS 4.1.3 + ppsclock)
 *    (Solaris7 + MU4)
 *    (RedHat 5.1 2.0.35 + PPSKit, 2.1.126 + or later).
129 130 131 132 133 134
 *
 *  Lat,Long,Ht, cable-delay, offset, and the ReceiverID (along with the
 *  state machine state) are printed to CLOCKSTATS if that file is enabled
 *  in /etc/ntp.conf.
 *
 * --------------------------------------------------------------------------
135
 *
136 137 138 139 140 141 142 143 144
 * According to the ONCORE manual (TRM0003, Rev 3.2, June 1998, page 3.13)
 * doing an average of 10000 valid 2D and 3D fixes is what the automatic
 * site survey mode does.  Looking at the output from the receiver
 * it seems like it is only using 3D fixes.
 * When we do it ourselves, take 10000 3D fixes.
 */

#define POS_HOLD_AVERAGE	10000	/* nb, 10000s ~= 2h45m */

145
/*
Harlan Stenn's avatar
Harlan Stenn committed
146 147
 * ONCORE_SHMEM_STATUS will create a mmap(2)'ed file named according to a
 * "STATUS" line in the oncore config file, which contains the most recent
148
 * copy of all types of messages we recognize.	This file can be mmap(2)'ed
Harlan Stenn's avatar
Harlan Stenn committed
149
 * by monitoring and statistics programs.
150
 *
151
 * See separate HTML documentation for this option.
152 153
 */

154
#include <config.h>
155 156 157 158
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_unixtime.h"
#include "ntp_refclock.h"
159
#include "ntp_calendar.h"
160 161
#include "ntp_stdlib.h"

162
#include <stdio.h>
163
#include <stdarg.h>
164 165
#include <ctype.h>
#include <sys/stat.h>
166
#ifdef ENABLE_ONCORE_SHMEM
167
#  include <sys/mman.h>
168
#  ifndef MAP_FAILED
169
#   define MAP_FAILED ((uint8_t *) -1)
170
#  endif  /* MAP_FAILED */
171
#endif /* ENABLE_ONCORE_SHMEM */
172

Harlan Stenn's avatar
Harlan Stenn committed
173 174 175
#ifdef HAVE_PPSAPI
# include "ppsapi_timepps.h"
#endif
176

Reg Clemens's avatar
Reg Clemens committed
177 178 179 180 181 182 183 184 185 186 187 188
struct Bl {
	int	dt_ls;
	int	dt_lsf;
	int	WN;
	int	DN;
	int	WN_lsf;
	int	DN_lsf;
	int	wn_flg;
	int	lsf_flg;
	int	Bl_day;
} Bl;

189 190
enum receive_state {
	ONCORE_NO_IDEA,
191 192 193
	ONCORE_CHECK_ID,
	ONCORE_CHECK_CHAN,
	ONCORE_HAVE_CHAN,
194 195
	ONCORE_RESET_SENT,
	ONCORE_TEST_SENT,
196
	ONCORE_INIT,
197 198 199 200 201 202
	ONCORE_ALMANAC,
	ONCORE_RUN
};

enum site_survey_state {
	ONCORE_SS_UNKNOWN,
203
	ONCORE_SS_TESTING,
204 205 206 207 208
	ONCORE_SS_HW,
	ONCORE_SS_SW,
	ONCORE_SS_DONE
};

209 210 211 212 213 214 215 216
enum antenna_state {
      ONCORE_ANTENNA_UNKNOWN = -1,
      ONCORE_ANTENNA_OK      =	0,
      ONCORE_ANTENNA_OC      =	1,
      ONCORE_ANTENNA_UC      =	2,
      ONCORE_ANTENNA_NV      =	3
};

217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
/* Model Name, derived from the @@Cj message.
 * Used to initialize some variables.
 */

enum oncore_model {
	ONCORE_BASIC,
	ONCORE_PVT6,
	ONCORE_VP,
	ONCORE_UT,
	ONCORE_UTPLUS,
	ONCORE_GT,
	ONCORE_GTPLUS,
	ONCORE_SL,
	ONCORE_M12,
	ONCORE_UNKNOWN
};

/* the bits that describe these properties are in the same place
 * on the VP/UT, but have moved on the M12.  As such we extract
 * them, and use them from this struct.
 *
 */

struct RSM {
241 242 243 244 245
	uint8_t	posn0D;
	uint8_t	posn2D;
	uint8_t	posn3D;
	uint8_t	bad_almanac;
	uint8_t	bad_fix;
246 247 248 249 250 251 252 253 254 255 256 257 258 259
};

/* It is possible to test the VP/UT each cycle (@@Ea or equivalent) to
 * see what mode it is in.  The bits on the M12 are multiplexed with
 * other messages, so we have to 'keep' the last known mode here.
 */

enum posn_mode {
	MODE_UNKNOWN,
	MODE_0D,
	MODE_2D,
	MODE_3D
};

260 261
struct instance {
	int	unit;		/* 127.127.30.unit */
262 263 264
	struct	refclockproc *pp;
	struct	peer *peer;

265 266
	int	ttyfd;		/* TTY file descriptor */
	int	ppsfd;		/* PPS file descriptor */
267
	int	shmemfd;	/* Status shm descriptor */
268 269
	pps_handle_t pps_h;
	pps_params_t pps_p;
270
	enum receive_state o_state;		/* Receive state */
271
	enum posn_mode mode;			/* 0D, 2D, 3D */
272
	enum site_survey_state site_survey;	/* Site Survey state */
273
	enum antenna_state ant_state;		/* antenna state */
274 275 276

	int	Bj_day;

277
	u_long	delay;		/* ns */
278 279
	long	offset; 	/* ns */

280
	uint8_t	*shmem;
281
	char	*shmem_fname;
282 283 284 285
	u_int	shmem_Cb;
	u_int	shmem_Ba;
	u_int	shmem_Ea;
	u_int	shmem_Ha;
286 287 288 289
	uint8_t	shmem_reset;
	uint8_t	shmem_Posn;
	uint8_t	shmem_bad_Ea;
	uint8_t	almanac_from_shmem;
290

291 292 293
	double	ss_lat;
	double	ss_long;
	double	ss_ht;
294
	double	dH;
295
	int	ss_count;
296
	uint8_t	posn_set;
297 298 299 300

	enum oncore_model model;
	u_int	version;
	u_int	revision;
301

302 303
	uint8_t	chan;		/* 6 for PVT6 or BASIC, 8 for UT/VP, 12 for m12, 0 if unknown */
	int8_t	traim;		/* do we have traim? yes UT/VP, M12+T, no BASIC, GT, M12, -1 unknown, 0 no, +1 yes */
304
				/* the following 7 are all timing counters */
305 306 307 308 309 310 311 312 313 314 315 316 317
	uint8_t	traim_delay;	/* seconds counter, waiting for reply */
	uint8_t	count;		/* cycles thru Ea before starting */
	uint8_t	count1; 	/* cycles thru Ea after SS_TESTING, waiting for SS_HW */
	uint8_t	count2; 	/* cycles thru Ea after count, to check for @@Ea */
	uint8_t	count3; 	/* cycles thru Ea checking for # channels */
	uint8_t	count4; 	/* cycles thru leap after Gj to issue Bj */
	uint8_t	count5; 	/* cycles thru get_timestamp waiting for valid UTC correction */
	uint8_t	count5_set;	/* only set count5 once */
	uint8_t	counta; 	/* count for waiting on almanac message */
	uint8_t	pollcnt;
	uint8_t	timeout;	/* count to retry Cj after Fa self-test */
	uint8_t	max_len;	/* max length message seen by oncore_log, for debugging */
	uint8_t	max_count;	/* count for message statistics */
318 319

	struct	RSM rsm;	/* bits extracted from Receiver Status Msg in @@Ea */
Reg Clemens's avatar
Reg Clemens committed
320
	struct	Bl Bl;		/* Satellite Broadcast Data Message */
321 322
	uint8_t	printed;
	uint8_t	polled;
323
	u_long	ev_serial;
324
	unsigned	Rcvptr;
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
	uint8_t	Rcvbuf[500];
	uint8_t	BEHa[160];	/* Ba, Ea or Ha */
	uint8_t	BEHn[80];	/* Bn , En , or Hn */
	uint8_t	Cj[300];
	uint8_t	Ag;		/* Satellite mask angle */
	uint8_t	saw_At;
	uint8_t	saw_Ay;
	uint8_t	saw_Az;
	int8_t	saw_Bj;
	int8_t	saw_Gj;
	uint8_t	have_dH;
	uint8_t	init_type;
	int8_t	saw_tooth;
	int8_t	chan_in;	/* chan number from INPUT, will always use it */
	uint8_t	chan_id;	/* chan number determined from part number */
	uint8_t	chan_ck;	/* chan number determined by sending commands to hardware */
	int8_t	traim_in;	/* TRAIM from INPUT, will always use ON/OFF specified */
	int8_t	traim_id;	/* TRAIM determined from part number */
	uint8_t	traim_ck;	/* TRAIM determined by sending commands to hardware */
	uint8_t	once;		/* one pass code at top of BaEaHa */
	int8_t	assert;
	uint8_t	hardpps;
	int8_t	pps_control;	/* PPS control, M12 only */
	int8_t	pps_control_msg_seen;
349 350 351 352 353
};

#define rcvbuf	instance->Rcvbuf
#define rcvptr	instance->Rcvptr

354
static	bool	oncore_start	      (int, struct peer *);
355 356 357 358 359
static	void	oncore_poll	      (int, struct peer *);
static	void	oncore_shutdown       (int, struct peer *);
static	void	oncore_consume	      (struct instance *);
static	void	oncore_read_config    (struct instance *);
static	void	oncore_receive	      (struct recvbuf *);
360
static	bool	oncore_ppsapi	      (struct instance *);
361
static	void	oncore_get_timestamp  (struct instance *, long, long);
362
#ifdef ENABLE_ONCORE_SHMEM
363
static	void	oncore_init_shmem     (struct instance *);
364
#endif
365 366 367 368 369 370

static	void	oncore_antenna_report (struct instance *, enum antenna_state);
static	void	oncore_chan_test      (struct instance *);
static	void	oncore_check_almanac  (struct instance *);
static	void	oncore_check_antenna  (struct instance *);
static	void	oncore_check_leap_sec (struct instance *);
371
static	int	oncore_checksum_ok    (uint8_t *, int);
372 373
static	void	oncore_compute_dH     (struct instance *);
static	void	oncore_load_almanac   (struct instance *);
Reg Clemens's avatar
Reg Clemens committed
374
static	void	oncore_log	      (struct instance *, int, const char *);
375
static	int	oncore_log_f	      (struct instance *, int, const char *, ...)
376
		NTP_PRINTF(3, 4);
377 378
static	void	oncore_print_Cb       (struct instance *, uint8_t *);
/* static  void    oncore_print_array	 (uint8_t *, int);	*/
379
static	void	oncore_print_posn     (struct instance *);
380
static	void	oncore_sendmsg	      (struct instance *, uint8_t *, size_t);
381 382 383 384
static	void	oncore_set_posn       (struct instance *);
static	void	oncore_set_traim      (struct instance *);
static	void	oncore_shmem_get_3D   (struct instance *);
static	void	oncore_ss	      (struct instance *);
385
static	bool	oncore_wait_almanac   (struct instance *);
386

387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
static	void	oncore_msg_any	   (struct instance *, uint8_t *, size_t, int);
static	void	oncore_msg_Adef    (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Ag	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_As	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_At	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Ay	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Az	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_BaEaHa  (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Bd	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Bj	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Bl	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_BnEnHn  (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_CaFaIa  (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Cb	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Cf	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Cj	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Cj_id   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Cj_init (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Ga	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Gb	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Gc	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Gj	   (struct instance *, uint8_t *, size_t);
static	void	oncore_msg_Sz	   (struct instance *, uint8_t *, size_t);
410 411 412 413 414

struct	refclock refclock_oncore = {
	oncore_start,		/* start up driver */
	oncore_shutdown,	/* shut down driver */
	oncore_poll,		/* transmit poll message */
415
	noentry,		/* not used */
416 417
	noentry,		/* not used */
	noentry,		/* not used */
Hal Murray's avatar
Hal Murray committed
418
	noentry 		/* not used */
419 420 421 422
};

/*
 * Understanding the next bit here is not easy unless you have a manual
423
 * for the the various Oncore Models.
424 425
 */

426
static struct msg_desc {
427 428
	const char	flag[3];
	const int	len;
429
	void		(*handler) (struct instance *, uint8_t *, size_t);
430
	const char	*fmt;
431
	int		shmem;
432
} oncore_messages[] = {
433
			/* Ea and En first since they're most common */
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
	{ "Ea",  76,    oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC", 0 },
	{ "Ba",  68,    oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdsC", 0 },
	{ "Ha", 154,    oncore_msg_BaEaHa, "mdyyhmsffffaaaaoooohhhhmmmmaaaaoooohhhhmmmmVVvvhhddntimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddimsiddssrrccooooTTushmvvvvvvC", 0 },
	{ "Bn",  59,    oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffC", 0 },
	{ "En",  69,    oncore_msg_BnEnHn, "otaapxxxxxxxxxxpysreensffffsffffsffffsffffsffffsffffsffffsffffC", 0 },
	{ "Hn",  78,    oncore_msg_BnEnHn, "", 0 },
	{ "Ab",  10,    0,                 "", 0 },
	{ "Ac",  11,    0,                 "", 0 },
	{ "Ad",  11,    oncore_msg_Adef,   "", 0 },
	{ "Ae",  11,    oncore_msg_Adef,   "", 0 },
	{ "Af",  15,    oncore_msg_Adef,   "", 0 },
	{ "Ag",   8,    oncore_msg_Ag,     "", 0 }, /* Satellite mask angle */
	{ "As",  20,    oncore_msg_As,     "", 0 },
	{ "At",   8,    oncore_msg_At,     "", 0 },
	{ "Au",  12,    0,                 "", 0 },
	{ "Av",   8,    0,                 "", 0 },
	{ "Aw",   8,    0,                 "", 0 },
	{ "Ay",  11,    oncore_msg_Ay,     "", 0 },
	{ "Az",  11,    oncore_msg_Az,     "", 0 },
	{ "AB",   8,    0,                 "", 0 },
	{ "Bb",  92,    0,                 "", 0 },
	{ "Bd",  23,    oncore_msg_Bd,     "", 0 },
	{ "Bj",   8,    oncore_msg_Bj,     "", 0 },
	{ "Bl",  41,    oncore_msg_Bl,     "", 0 },
	{ "Ca",   9,    oncore_msg_CaFaIa, "", 0 },
	{ "Cb",  33,    oncore_msg_Cb,     "", 0 },
	{ "Cf",   7,    oncore_msg_Cf,     "", 0 },
	{ "Cg",   8,    0,                 "", 0 },
	{ "Ch",   9,    0,                 "", 0 },
	{ "Cj", 294,    oncore_msg_Cj,     "", 0 },
	{ "Ek",  71,    0,                 "", 0 },
	{ "Fa",   9,    oncore_msg_CaFaIa, "", 0 },
	{ "Ga",  20,    oncore_msg_Ga,     "", 0 },
	{ "Gb",  17,    oncore_msg_Gb,     "", 0 },
	{ "Gc",   8,    oncore_msg_Gc,     "", 0 },
	{ "Gd",   8,    0,                 "", 0 },
	{ "Ge",   8,    0,                 "", 0 },
	{ "Gj",  21,    oncore_msg_Gj,     "", 0 },
	{ "Ia",  10,    oncore_msg_CaFaIa, "", 0 },
	{ "Sz",   8,    oncore_msg_Sz,     "", 0 },
	{ {0},	  7,	0,		   "", 0 }
475 476 477
};


478 479 480 481 482 483 484 485 486 487 488
static uint8_t oncore_cmd_Aa[]  = { 'A', 'a', 0, 0, 0 }; 			    /* 6/8	Time of Day				*/
static uint8_t oncore_cmd_Ab[]  = { 'A', 'b', 0, 0, 0 }; 			    /* 6/8	GMT Correction				*/
static uint8_t oncore_cmd_AB[]  = { 'A', 'B', 4 };				    /* VP	Application Type: Static		*/
static uint8_t oncore_cmd_Ac[]  = { 'A', 'c', 0, 0, 0, 0 };			    /* 6/8	Date					*/
static uint8_t oncore_cmd_Ad[]  = { 'A', 'd', 0,0,0,0 }; 			    /* 6/8	Latitude				*/
static uint8_t oncore_cmd_Ae[]  = { 'A', 'e', 0,0,0,0 }; 			    /* 6/8	Longitude				*/
static uint8_t oncore_cmd_Af[]  = { 'A', 'f', 0,0,0,0, 0 };			    /* 6/8	Height					*/
static uint8_t oncore_cmd_Ag[]  = { 'A', 'g', 0 };				    /* 6/8/12	Satellite Mask Angle			*/
static uint8_t oncore_cmd_Agx[] = { 'A', 'g', 0xff };				    /* 6/8/12	Satellite Mask Angle: read		*/
static uint8_t oncore_cmd_As[]  = { 'A', 's', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 };	    /* 6/8/12	Posn Hold Parameters			*/
static uint8_t oncore_cmd_Asx[] = { 'A', 's', 0x7f,0xff,0xff,0xff,		    /* 6/8/12	Posn Hold Readback			*/
489 490
					     0x7f,0xff,0xff,0xff,		    /*		 on UT+ this doesnt work with 0xff	*/
					     0x7f,0xff,0xff,0xff, 0xff };	    /*		 but does work with 0x7f (sigh).	*/
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
static uint8_t oncore_cmd_At0[] = { 'A', 't', 0 };				    /* 6/8	Posn Hold: off				*/
static uint8_t oncore_cmd_At1[] = { 'A', 't', 1 };				    /* 6/8	Posn Hold: on				*/
static uint8_t oncore_cmd_At2[] = { 'A', 't', 2 };				    /* 6/8	Posn Hold: Start Site Survey		*/
static uint8_t oncore_cmd_Atx[] = { 'A', 't', 0xff };				    /* 6/8	Posn Hold: Read Back			*/
static uint8_t oncore_cmd_Au[]  = { 'A', 'u', 0,0,0,0, 0 };			    /* GT/M12	Altitude Hold Ht.			*/
static uint8_t oncore_cmd_Av0[] = { 'A', 'v', 0 };				    /* VP/GT	Altitude Hold: off			*/
static uint8_t oncore_cmd_Av1[] = { 'A', 'v', 1 };				    /* VP/GT	Altitude Hold: on			*/
static uint8_t oncore_cmd_Aw[]  = { 'A', 'w', 1 };				    /* 6/8/12	UTC/GPS time selection			*/
static uint8_t oncore_cmd_Ay[]  = { 'A', 'y', 0, 0, 0, 0 };			    /* Timing	1PPS time offset: set			*/
static uint8_t oncore_cmd_Ayx[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff };		    /* Timing	1PPS time offset: Read			*/
static uint8_t oncore_cmd_Az[]  = { 'A', 'z', 0, 0, 0, 0 };			    /* 6/8UT/12 1PPS Cable Delay: set			*/
static uint8_t oncore_cmd_Azx[] = { 'A', 'z', 0xff, 0xff, 0xff, 0xff };		    /* 6/8UT/12 1PPS Cable Delay: Read			*/
static uint8_t oncore_cmd_Ba0[] = { 'B', 'a', 0 };				    /* 6	Position/Data/Status: off		*/
static uint8_t oncore_cmd_Ba[]  = { 'B', 'a', 1 };				    /* 6	Position/Data/Status: on		*/
static uint8_t oncore_cmd_Bb[]  = { 'B', 'b', 1 };				    /* 6/8/12	Visible Satellites			*/
static uint8_t oncore_cmd_Bd[]  = { 'B', 'd', 1 };				    /* 6/8/12?	Almanac Status Msg.			*/
static uint8_t oncore_cmd_Be[]  = { 'B', 'e', 1 };				    /* 6/8/12	Request Almanac Data			*/
static uint8_t oncore_cmd_Bj[]  = { 'B', 'j', 0 };				    /* 6/8	Leap Second Pending			*/
static uint8_t oncore_cmd_Bl[]  = { 'B', 'l', 1 };				    /* VP	Satellite Broadcast Data Msg		*/
static uint8_t oncore_cmd_Bn0[] = { 'B', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6	TRAIM setup/status: msg off, traim on	*/
static uint8_t oncore_cmd_Bn[]  = { 'B', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6	TRAIM setup/status: msg on,  traim on	*/
static uint8_t oncore_cmd_Bnx[] = { 'B', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 6	TRAIM setup/status: msg off, traim off	*/
static uint8_t oncore_cmd_Ca[]  = { 'C', 'a' };					    /* 6	Self Test				*/
static uint8_t oncore_cmd_Cf[]  = { 'C', 'f' };					    /* 6/8/12	Set to Defaults 			*/
static uint8_t oncore_cmd_Cg[]  = { 'C', 'g', 1 };				    /* VP	Posn Fix/Idle Mode			*/
static uint8_t oncore_cmd_Cj[]  = { 'C', 'j' };					    /* 6/8/12	Receiver ID				*/
static uint8_t oncore_cmd_Ea0[] = { 'E', 'a', 0 };				    /* 8	Position/Data/Status: off		*/
static uint8_t oncore_cmd_Ea[]  = { 'E', 'a', 1 };				    /* 8	Position/Data/Status: on		*/
static uint8_t oncore_cmd_Ek[]  = { 'E', 'k', 0 }; /* just turn off */		    /* 8	Posn/Status/Data - extension		*/
static uint8_t oncore_cmd_En0[] = { 'E', 'n', 0, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT	TRAIM setup/status: msg off, traim on	*/
static uint8_t oncore_cmd_En[]  = { 'E', 'n', 1, 1, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT	TRAIM setup/status: msg on,  traim on	*/
static uint8_t oncore_cmd_Enx[] = { 'E', 'n', 0, 0, 0,10, 2, 0,0,0, 0,0,0,0,0,0,0 }; /* 8/GT	TRAIM setup/status: msg off, traim off	*/
static uint8_t oncore_cmd_Fa[]  = { 'F', 'a' };					    /* 8	Self Test				*/
static uint8_t oncore_cmd_Ga[]  = { 'G', 'a', 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 };	    /* 12	Position Set				*/
static uint8_t oncore_cmd_Gax[] = { 'G', 'a', 0xff, 0xff, 0xff, 0xff,		    /* 12	Position Set: Read			*/
526 527
					     0xff, 0xff, 0xff, 0xff,		    /*							*/
					     0xff, 0xff, 0xff, 0xff, 0xff };	    /*							*/
528 529 530 531 532 533 534 535 536 537 538 539 540 541
static uint8_t oncore_cmd_Gb[]  = { 'G', 'b', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };	    /* 12	set Date/Time				*/
static uint8_t oncore_cmd_Gc[]  = { 'G', 'c', 0 };				    /* 12	PPS Control: Off, On, 1+satellite,TRAIM */
static uint8_t oncore_cmd_Gd0[] = { 'G', 'd', 0 };				    /* 12	Position Control: 3D (no hold)		*/
static uint8_t oncore_cmd_Gd1[] = { 'G', 'd', 1 };				    /* 12	Position Control: 0D (3D hold)		*/
static uint8_t oncore_cmd_Gd2[] = { 'G', 'd', 2 };				    /* 12	Position Control: 2D (Alt Hold) 	*/
static uint8_t oncore_cmd_Gd3[] = { 'G', 'd', 3 };				    /* 12	Position Coltrol: Start Site Survey	*/
static uint8_t oncore_cmd_Ge0[] = { 'G', 'e', 0 };				    /* M12+T	TRAIM: off				*/
static uint8_t oncore_cmd_Ge[]  = { 'G', 'e', 1 };				    /* M12+T	TRAIM: on				*/
static uint8_t oncore_cmd_Gj[]  = { 'G', 'j' };					    /* 8?/12	Leap Second Pending			*/
static uint8_t oncore_cmd_Ha0[] = { 'H', 'a', 0 };				    /* 12	Position/Data/Status: off		*/
static uint8_t oncore_cmd_Ha[]  = { 'H', 'a', 1 };				    /* 12	Position/Data/Status: on		*/
static uint8_t oncore_cmd_Hn0[] = { 'H', 'n', 0 };				    /* 12	TRAIM Status: off			*/
static uint8_t oncore_cmd_Hn[]  = { 'H', 'n', 1 };				    /* 12	TRAIM Status: on			*/
static uint8_t oncore_cmd_Ia[]  = { 'I', 'a' };					    /* 12	Self Test				*/
542 543 544 545 546 547 548

/* it appears that as of 1997/1998, the UT had As,At, but not Au,Av
 *				    the GT had Au,Av, but not As,At
 * This was as of v2.0 of both firmware sets. possibly 1.3 for UT.
 * Bj in UT at v1.3
 * dont see Bd in UT/GT thru 1999
 * Gj in UT as of 3.0, 1999 , Bj as of 1.3
549 550
 */

551 552
#define DEVICE1 	"/dev/oncore.serial.%d" /* name of serial device */
#define DEVICE2 	"/dev/oncore.pps.%d"    /* name of pps device */
553 554 555 556 557 558 559 560

#define SPEED		B9600		/* Oncore Binary speed (9600 bps) */

/*
 * Assemble and disassemble 32bit signed quantities from a buffer.
 *
 */

561
	/* to buffer, int w, uint8_t *buf */
562
#define w32_buf(buf,w)	{ u_int i_tmp;			   \
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
			  i_tmp = (w<0) ? (~(-w)+1) : (w); \
			  (buf)[0] = (i_tmp >> 24) & 0xff; \
			  (buf)[1] = (i_tmp >> 16) & 0xff; \
			  (buf)[2] = (i_tmp >>	8) & 0xff; \
			  (buf)[3] = (i_tmp	 ) & 0xff; \
			}

#define w32(buf)      (((buf)[0]&0xff) << 24 | \
		       ((buf)[1]&0xff) << 16 | \
		       ((buf)[2]&0xff) <<  8 | \
		       ((buf)[3]&0xff) )

	/* from buffer, char *buf, result to an int */
#define buf_w32(buf) (((buf)[0]&0200) ? (-(~w32(buf)+1)) : w32(buf))

578

579 580 581
/*
 * oncore_start - initialize data for processing
 */
582

583
static bool
584 585 586 587 588
oncore_start(
	int unit,
	struct peer *peer
	)
{
589
#define STRING_LEN	32
590 591
	register struct instance *instance;
	struct refclockproc *pp;
Reg Clemens's avatar
Reg Clemens committed
592
	int fd1, fd2;
593
	char device1[STRING_LEN], device2[STRING_LEN];
594
#ifndef SYS_WINNT
595
	struct stat stat1, stat2;
596
#endif
597

598 599
	/* create instance structure for this unit */

600 601
	instance = emalloc(sizeof(*instance));
	memset(instance, 0, sizeof(*instance));
602 603

	/* initialize miscellaneous variables */
604

605
	pp = peer->procptr;
606 607 608
	instance->pp   = pp;
	instance->unit = unit;
	instance->peer = peer;
609 610
	instance->assert = 1;
	instance->once = 1;
611

612
	instance->Bj_day = -1;
613
	instance->traim = -1;
614 615
	instance->traim_in = -1;
	instance->chan_in = -1;
616 617
	instance->pps_control = -1;	/* PPS control, M12 only */
	instance->pps_control_msg_seen = -1;	/* Have seen response to Gc msg */
618 619 620
	instance->model = ONCORE_UNKNOWN;
	instance->mode = MODE_UNKNOWN;
	instance->site_survey = ONCORE_SS_UNKNOWN;
621 622
	instance->Ag = 0xff;		/* Satellite mask angle, unset by user */
	instance->ant_state = ONCORE_ANTENNA_UNKNOWN;
623

624
	peer->flags &= ~FLAG_PPS;	/* PPS not active yet */
625 626 627 628 629
	peer->precision = -26;
	peer->minpoll = 4;
	peer->maxpoll = 4;
	pp->clockdesc = "Motorola Oncore GPS Receiver";
	memcpy((char *)&pp->refid, "GPS\0", (size_t) 4);
630

Reg Clemens's avatar
Reg Clemens committed
631
	oncore_log(instance, LOG_NOTICE, "ONCORE DRIVER -- CONFIGURING");
632
	instance->o_state = ONCORE_NO_IDEA;
633
	oncore_log(instance, LOG_NOTICE, "state = ONCORE_NO_IDEA");
634 635 636 637 638

	/* Now open files.
	 * This is a bit complicated, a we dont want to open the same file twice
	 * (its a problem on some OS), and device2 may not exist for the new PPS
	 */
639

640 641
	(void)snprintf(device1, sizeof(device1), DEVICE1, unit);
	(void)snprintf(device2, sizeof(device2), DEVICE2, unit);
642 643 644 645 646 647 648 649 650 651

	/* OPEN DEVICES */
	/* opening different devices for fd1 and fd2 presents no problems */
	/* opening the SAME device twice, seems to be OS dependent.
		(a) on Linux (no streams) no problem
		(b) on SunOS (and possibly Solaris, untested), (streams)
			never see the line discipline.
	   Since things ALWAYS work if we only open the device once, we check
	     to see if the two devices are in fact the same, then proceed to
	     do one open or two.
652 653 654 655 656 657 658 659 660

	   For use with linuxPPS we assume that the N_TTY file has been opened
	     and that the line discipline has been changed to N_PPS by another
	     program (say ppsldisc) so that the two files expected by the oncore
	     driver can be opened.

	   Note that the linuxPPS N_PPS file is just like a N_TTY, so we can do
	     the stat below without error even though the file has already had its
	     line discipline changed by another process.
661

662 663 664 665 666
	   The Windows port of ntpd arranges to return duplicate handles for
	     multiple opens of the same serial device, and doesn't have inodes
	     for serial handles, so we just open both on Windows.
	*/
#ifndef SYS_WINNT
667
	if (stat(device1, &stat1)) {
668 669
		oncore_log_f(instance, LOG_ERR, "Can't stat fd1 (%s)",
			     device1);
670
		return false;		/* exit, no file, can't start driver */
671 672
	}

673 674
	if (stat(device2, &stat2)) {
		stat2.st_dev = stat2.st_ino = -2;
675 676
		oncore_log_f(instance, LOG_ERR, "Can't stat fd2 (%s) %d %m",
			     device2, errno);
677
	}
678
#endif	/* !SYS_WINNT */
679

680 681
	fd1 = refclock_open(device1, SPEED, LDISC_RAW);
	if (fd1 <= 0) {
682 683
		oncore_log_f(instance, LOG_ERR, "Can't open fd1 (%s)",
			     device1);
684
		/* coverity[leaked_storage] */
685
		return false;		/* exit, can't open file, can't start driver */
686 687
	}

688 689 690 691 692
	/* for LINUX the PPS device is the result of a line discipline.
	   It seems simplest to let an external program create the appropriate
	   /dev/pps<n> file, and only check (carefully) for its existance here
	 */

693
#ifndef SYS_WINNT
694 695
	if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino))	/* same device here */
		fd2 = fd1;
696 697 698 699
	else
#endif	/* !SYS_WINNT */
	{	/* different devices here */
		if ((fd2=tty_open(device2, O_RDWR, 0777)) < 0) {
700 701
			oncore_log_f(instance, LOG_ERR,
				     "Can't open fd2 (%s)", device2);
702 703
			close(fd1);
			free(instance);
704
			return false;	/* exit, can't open PPS file, can't start driver */
705 706
		}
	}
707

708
	/* open ppsapi source */
709

Reg Clemens's avatar
Reg Clemens committed
710
	if (time_pps_create(fd2, &instance->pps_h) < 0) {
Reg Clemens's avatar
Reg Clemens committed
711
		oncore_log(instance, LOG_ERR, "exit, PPSAPI not found in kernel");
712
		free(instance);
713
		return false;		/* exit, don't find PPSAPI in kernel */
714 715
	}

716 717 718 719 720 721 722 723 724
	/* continue initialization */

	instance->ttyfd = fd1;
	instance->ppsfd = fd2;

	/* go read any input data in /etc/ntp.oncoreX or /etc/ntp/oncore.X */

	oncore_read_config(instance);

725 726
	if (!oncore_ppsapi(instance)) {
		free(instance);
727
		return false;
728
	}
729 730

	pp->io.clock_recv = oncore_receive;
731
	pp->io.srcclock = peer;
732 733 734
	pp->io.datalen = 0;
	pp->io.fd = fd1;
	if (!io_addclock(&pp->io)) {
Reg Clemens's avatar
Reg Clemens committed
735
		oncore_log(instance, LOG_ERR, "can't do io_addclock");
736 737
		close(fd1);
		pp->io.fd = -1;
738
		free(instance);
739
		return false;
740
	}
741
	pp->unitptr = instance;
742

743
#ifdef ENABLE_ONCORE_SHMEM
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
	/*
	 * Before starting ONCORE, lets setup SHMEM
	 * This will include merging an old SHMEM into the new one if
	 * an old one is found.
	 */

	oncore_init_shmem(instance);
#endif

	/*
	 * This will return the Model of the Oncore receiver.
	 * and start the Initialization loop in oncore_msg_Cj.
	 */

	instance->o_state = ONCORE_CHECK_ID;
Reg Clemens's avatar
Reg Clemens committed
759
	oncore_log(instance, LOG_NOTICE, "state = ONCORE_CHECK_ID");
760 761

	instance->timeout = 4;
Reg Clemens's avatar
Reg Clemens committed
762 763
	oncore_sendmsg(instance, oncore_cmd_Cg, sizeof(oncore_cmd_Cg)); /* Set Posn Fix mode (not Idle (VP)) */
	oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
764 765

	instance->pollcnt = 2;
766
	return true;
767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
}


/*
 * oncore_shutdown - shut down the clock
 */

static void
oncore_shutdown(
	int unit,
	struct peer *peer
	)
{
	register struct instance *instance;
	struct refclockproc *pp;

	pp = peer->procptr;
784
	instance = pp->unitptr;
785

786 787
	if (pp->io.fd != -1)
		io_closeclock(&pp->io);
788

789 790
	if (instance != NULL) {
		time_pps_destroy (instance->pps_h);
Reg Clemens's avatar
Reg Clemens committed
791

792
		close(instance->ttyfd);
Reg Clemens's avatar
Reg Clemens committed
793

794 795
		if ((instance->ppsfd != -1) && (instance->ppsfd != instance->ttyfd))
			close(instance->ppsfd);
Reg Clemens's avatar
Reg Clemens committed
796

797 798
		if (instance->shmemfd)
			close(instance->shmemfd);
Reg Clemens's avatar
Reg Clemens committed
799

800 801
		free(instance);
	}
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
}



/*
 * oncore_poll - called by the transmit procedure
 */

static void
oncore_poll(
	int unit,
	struct peer *peer
	)
{
	struct instance *instance;

818
	instance = peer->procptr->unitptr;
819 820 821
	if (instance->timeout) {
		instance->timeout--;
		if (instance->timeout == 0) {
Reg Clemens's avatar
Reg Clemens committed
822 823
			oncore_log(instance, LOG_ERR,
			    "Oncore: No response from @@Cj, shutting down driver");
824 825
			oncore_shutdown(unit, peer);
		} else {
Reg Clemens's avatar
Reg Clemens committed
826 827
			oncore_sendmsg(instance, oncore_cmd_Cj, sizeof(oncore_cmd_Cj));
			oncore_log(instance, LOG_WARNING, "Oncore: Resend @@Cj");
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
		}
		return;
	}

	if (!instance->pollcnt)
		refclock_report(peer, CEVNT_TIMEOUT);
	else
		instance->pollcnt--;
	peer->procptr->polls++;
	instance->polled = 1;
}



/*
 * Initialize PPSAPI
 */

846
static bool
847 848 849 850
oncore_ppsapi(
	struct instance *instance
	)
{
851
	int cap, mode, mode1;
852
	const char *cp;
853

854
	if (time_pps_getcap(instance->pps_h, &cap) < 0) {
855
		oncore_log_f(instance, LOG_ERR, "time_pps_getcap failed: %m");
856
		return false;
857 858 859
	}

	if (time_pps_getparams(instance->pps_h, &instance->pps_p) < 0) {
860
		oncore_log_f(instance, LOG_ERR, "time_pps_getparams failed: %m");
861
		return false;
862 863 864
	}

	/* nb. only turn things on, if someone else has turned something
865
	 *	on before we get here, leave it alone!
866 867
	 */

868
	if (instance->assert) {
Reg Clemens's avatar
Reg Clemens committed
869
		cp = "Assert";
870 871
		mode = PPS_CAPTUREASSERT;
		mode1 = PPS_OFFSETASSERT;
872
	} else {
Reg Clemens's avatar
Reg Clemens committed
873
		cp = "Clear";
874 875 876
		mode = PPS_CAPTURECLEAR;
		mode1 = PPS_OFFSETCLEAR;
	}
877 878
	oncore_log_f(instance, LOG_INFO, "Initializing timing to %s.",
		     cp);
879 880

	if (!(mode & cap)) {
881 882
		oncore_log_f(instance, LOG_ERR,
			     "Can't set timing to %s, exiting...", cp);
883
		return false;
884 885 886
	}

	if (!(mode1 & cap)) {
887 888 889
		oncore_log_f(instance, LOG_NOTICE,
			     "Can't set %s, this will increase jitter.",
			     cp);
890
		mode1 = 0;
891
	}
892 893 894 895

	/* only set what is legal */

	instance->pps_p.mode = (mode | mode1 | PPS_TSFMT_TSPEC) & cap;
896

897
	if (time_pps_setparams(instance->pps_h, &instance->pps_p) < 0) {
898
		oncore_log_f(instance, LOG_ERR, "ONCORE: time_pps_setparams fails %m");
899
		return false;	/* exit, can't do time_pps_setparans on PPS file */
900
	}
901

902
	/* If HARDPPS is on, we tell kernel */
903

904 905
	if (instance->hardpps) {
		int	i;
906

Reg Clemens's avatar
Reg Clemens committed
907
		oncore_log(instance, LOG_INFO, "HARDPPS Set.");
908

909 910 911 912
		if (instance->assert)
			i = PPS_CAPTUREASSERT;
		else
			i = PPS_CAPTURECLEAR;
913

914 915 916 917
		/* we know that 'i' is legal from above */

		if (time_pps_kcbind(instance->pps_h, PPS_KC_HARDPPS, i,
		    PPS_TSFMT_TSPEC) < 0) {
918
			oncore_log_f(instance, LOG_ERR, "time_pps_kcbind failed: %m");
Reg Clemens's avatar
Reg Clemens committed
919
			oncore_log(instance, LOG_ERR, "HARDPPS failed, abort...");
920
			return false;
921
		}
922

923
		hardpps_enable = true;
924
	}
925
	return true;
926
}
927

928 929


930
#ifdef ENABLE_ONCORE_SHMEM
931 932 933 934 935
static void
oncore_init_shmem(
	struct instance *instance
	)
{
936
	int l, fd;
937
	uint8_t *cp, *cp1, *buf, *shmem_old;
938 939
	struct msg_desc *mp;
	struct stat sbuf;
940
	size_t i, n, n1, shmem_length, shmem_old_size;
941

942
	/*
943 944
	* The first thing we do is see if there is an instance->shmem_fname file (still)
	* out there from a previous run.  If so, we copy it in and use it to initialize
945
	* shmem (so we won't lose our almanac if we need it).
946
	*/
947

948
	shmem_old = 0;
949
	shmem_old_size = 0;
950
	if ((fd = open(instance->shmem_fname, O_RDONLY)) < 0)
Reg Clemens's avatar
Reg Clemens committed
951
		oncore_log(instance, LOG_WARNING, "ONCORE: Can't open SHMEM file");
952 953 954
	else {
		fstat(fd, &sbuf);
		shmem_old_size = sbuf.st_size;
955
		if (shmem_old_size != 0) {
956 957
			shmem_old = emalloc((unsigned) sbuf.st_size);
			read(fd, shmem_old, shmem_old_size);
958 959 960
		}
		close(fd);
	}
961

962
	/* OK, we now create the NEW SHMEM. */
963

964
	if ((instance->shmemfd = open(instance->shmem_fname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) {
Reg Clemens's avatar
Reg Clemens committed
965
		oncore_log(instance, LOG_WARNING, "ONCORE: Can't open shmem");
966 967 968
		if (shmem_old)
			free(shmem_old);

969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
		return;
	}

	/* see how big it needs to be */

	n = 1;
	for (mp=oncore_messages; mp->flag[0]; mp++) {
		mp->shmem = n;
		/* Allocate space for multiplexed almanac, and 0D/2D/3D @@Ea records */
		if (!strcmp(mp->flag, "Cb")) {
			instance->shmem_Cb = n;
			n += (mp->len + 3) * 34;
		}
		if (!strcmp(mp->flag, "Ba")) {
			instance->shmem_Ba = n;
			n += (mp->len + 3) * 3;
		}
		if (!strcmp(mp->flag, "Ea")) {
			instance->shmem_Ea = n;
			n += (mp->len + 3) * 3;
		}
		if (!strcmp(mp->flag, "Ha")) {
			instance->shmem_Ha = n;
			n += (mp->len + 3) * 3;
		}
		n += (mp->len + 3);
	}
	shmem_length = n + 2;

998
	buf = emalloc(shmem_length);
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
	memset(buf, 0, shmem_length);

	/* next build the new SHMEM buffer in memory */

	for (mp=oncore_messages; mp->flag[0]; mp++) {
		l = mp->shmem;
		buf[l + 0] = mp->len >> 8;
		buf[l + 1] = mp->len & 0xff;
		buf[l + 2] = 0;
		buf[l + 3] = '@';
		buf[l + 4] = '@';
		buf[l + 5] = mp->flag[0];
		buf[l + 6] = mp->flag[1];
		if (!strcmp(mp->flag, "Cb") || !strcmp(mp->flag, "Ba") || !strcmp(mp->flag, "Ea") || !strcmp(mp->flag, "Ha")) {
			if (!strcmp(mp->flag, "Cb"))
				n = 35;
			else
				n = 4;
			for (i=1; i<n; i++) {
				buf[l + i * (mp->len+3) + 0] = mp->len >> 8;
				buf[l + i * (mp->len+3) + 1] = mp->len & 0xff;
				buf[l + i * (mp->len+3) + 2] = 0;
				buf[l + i * (mp->len+3) + 3] = '@';
				buf[l + i * (mp->len+3) + 4] = '@';
				buf[l + i * (mp->len+3) + 5] = mp->flag[0];
				buf[l + i * (mp->len+3) + 6] = mp->flag[1];
			}
		}
	}

	/* we now walk thru the two buffers (shmem_old and buf, soon to become shmem)
1030 1031 1032 1033
	 * copying the data in shmem_old to buf.
	 * When we are done we write it out and free both buffers.
	 * If the structure sizes dont agree, I will not copy.
	 * This could be due to an addition/deletion or a problem with the disk file.
1034 1035 1036
	 */

	if (shmem_old) {
1037 1038 1039 1040 1041
		if (shmem_old_size == shmem_length) {
			for (cp=buf+4, cp1=shmem_old+4; (n = 256*(*(cp-3)) + *(cp-2));	cp+=(n+3), cp1+=(n+3)) {
				n1 = 256*(*(cp1-3)) + *(cp1-2);
				if (n == 0 || n1 != n || strncmp((char *) cp, (char *) cp1, 4))
					break;
1042

1043 1044
				memcpy(cp, cp1, (size_t) n);
			}
1045 1046 1047 1048 1049 1050 1051 1052
		}
		free(shmem_old);
	}

	i = write(instance->shmemfd, buf, shmem_length);
	free(buf);

	if (i != shmem_length) {
Reg Clemens's avatar
Reg Clemens committed
1053
		oncore_log(instance, LOG_ERR, "ONCORE: error writing shmem");
1054 1055 1056 1057
		close(instance->shmemfd);
		return;
	}

1058
	instance->shmem = (uint8_t *) mmap(0, shmem_length,
1059 1060 1061 1062 1063 1064
		PROT_READ | PROT_WRITE,
#ifdef MAP_HASSEMAPHORE
		MAP_HASSEMAPHORE |
#endif
		MAP_SHARED, instance->shmemfd, (off_t)0);

1065
	if (instance->shmem == (uint8_t *)MAP_FAILED) {
1066 1067 1068 1069 1070
		instance->shmem = 0;
		close(instance->shmemfd);
		return;
	}

1071 1072 1073
	oncore_log_f(instance, LOG_NOTICE,
		     "SHMEM (size = %ld) is CONFIGURED and available as %s",
		     (u_long) shmem_length, instance->shmem_fname);
1074
}
1075
#endif /* ENABLE_ONCORE_SHMEM */
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088



/*
 * Read Input file if it exists.
 */

static void
oncore_read_config(
	struct instance *instance
	)
{
/*
1089
 * First we try to open the configuration file
Reg Clemens's avatar
Reg Clemens committed
1090
 *    /etc/ntp.oncore.N
1091 1092
 * where N is the unit number viz 127.127.30.N.
 * If we don't find it we try
Reg Clemens's avatar
Reg Clemens committed
1093
 *    /etc/ntp.oncoreN
1094 1095
 * and then
 *    /etc/ntp.oncore
1096
 *
1097
 * If we don't find any then we don't have the cable delay or PPS offset
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
 * and we choose MODE (4) below.
 *
 * Five Choices for MODE
 *    (0) ONCORE is preinitialized, don't do anything to change it.
 *	    nb, DON'T set 0D mode, DON'T set Delay, position...
 *    (1) NO RESET, Read Position, delays from data file, lock it in, go to 0D mode.
 *    (2) NO RESET, Read Delays from data file, do SITE SURVEY to get position,
 *		    lock this in, go to 0D mode.
 *    (3) HARD RESET, Read Position, delays from data file, lock it in, go to 0D mode.
 *    (4) HARD RESET, Read Delays from data file, do SITE SURVEY to get position,
 *		    lock this in, go to 0D mode.
 *     NB. If a POSITION is specified in the config file with mode=(2,4) [SITE SURVEY]
 *	   then this position is set as the INITIAL position of the ONCORE.
 *	   This can reduce the time to first fix.
 * -------------------------------------------------------------------------------
 * Note that an Oncore UT without a battery backup retains NO information if it is
 *   power cycled, with a Battery Backup it remembers the almanac, etc.
 * For an Oncore VP, there is an eeprom that will contain this data, along with the
 *   option of Battery Backup.
 * So a UT without Battery Backup is equivalent to doing a HARD RESET on each
 *   power cycle, since there is nowhere to store the data.
 * -------------------------------------------------------------------------------
 *
 * If we open one or the other of the files, we read it looking for
1122 1123
 *   MODE, LAT, LON, (HT, HTGPS, HTMSL), DELAY, OFFSET, ASSERT, CLEAR, HARDPPS,
 *   STATUS, POSN3D, POSN2D, CHAN, TRAIM
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140
 * then initialize using method MODE.  For Mode = (1,3) all of (LAT, LON, HT) must
 *   be present or mode reverts to (2,4).
 *
 * Read input file.
 *
 *	# is comment to end of line
 *	= allowed between 1st and 2nd fields.
 *
 *	Expect to see one line with 'MODE' as first field, followed by an integer
 *	   in the range 0-4 (default = 4).
 *
 *	Expect to see two lines with 'LONG', 'LAT' followed by 1-3 fields.
 *	All numbers are floating point.
 *		DDD.ddd
 *		DDD  MMM.mmm
 *		DDD  MMM  SSS.sss
 *
1141 1142 1143
 *	Expect to see one line with 'HT' as first field,
 *	   followed by 1-2 fields.  First is a number, the second is 'FT' or 'M'
 *	   for feet or meters.	HT is the height above the GPS ellipsoid.
1144
 *	   If the receiver reports height in both GPS and MSL, then we will report
1145
 *	   the difference GPS-MSL on the clockstats file.
1146
 *
1147
 *	There is an optional line, starting with DELAY, followed
1148 1149
 *	   by 1 or two fields.	The first is a number (a time) the second is
 *	   'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
1150 1151 1152 1153 1154 1155
 *	    DELAY  is cable delay, typically a few tens of ns.
 *
 *	There is an optional line, starting with OFFSET, followed
 *	   by 1 or two fields.	The first is a number (a time) the second is
 *	   'MS', 'US' or 'NS' for miliseconds, microseconds or nanoseconds.
 *	   OFFSET is the offset of the PPS pulse from 0. (only fully implemented
1156 1157 1158 1159 1160
 *		with the PPSAPI, we need to be able to tell the Kernel about this
 *		offset if the Kernel PLL is in use, but can only do this presently
 *		when using the PPSAPI interface.  If not using the Kernel PLL,
 *		then there is no problem.
 *
1161
 *	There is an optional line, with either ASSERT or CLEAR on it, which
1162 1163
 *	   determine which transition of the PPS signal is used for timing by the
 *	   PPSAPI.  If neither is present, then ASSERT is assumed.
1164 1165 1166 1167
 *	   ASSERT/CLEAR can also be set with FLAG2 of the ntp.conf input.
 *	   For Flag2, ASSERT=0, and hence is default.
 *
 *	There is an optional line, with HARDPPS on it.	Including this line causes
1168
 *	     the PPS signal to control the kernel PLL.
1169 1170
 *	   HARDPPS can also be set with FLAG3 of the ntp.conf input.
 *	   For Flag3, 0 is disabled, and the default.
1171
 *
1172 1173
 *	There are three options that have to do with using the shared memory option.
 *	   First, to enable the option there must be a SHMEM line with a file name.
1174 1175
 *	   The file name is the file associated with the shared memory.
 *
1176 1177 1178 1179 1180 1181
 *	In shared memory, there is one 'record' for each returned variable.
 *	For the @@Ea data there are three 'records' containing position data.
 *	   There will always be data in the record corresponding to the '0D' @@Ea record,
 *	   and the user has a choice of filling the '3D' record by specifying POSN3D,
 *	   or the '2D' record by specifying POSN2D.  In either case the '2D' or '3D'
 *	   record is filled once every 15s.
1182 1183 1184
 *
 *	Two additional variables that can be set are CHAN and TRAIM.  These should be
 *	   set correctly by the code examining the @@Cj record, but we bring them out here
1185
 *	   to allow the user to override either the # of channels, or the existence of TRAIM.
1186 1187 1188
 *	   CHAN expects to be followed by in integer: 6, 8, or 12. TRAIM expects to be
 *	   followed by YES or NO.
 *
1189 1190 1191 1192 1193
 *	There is an optional line with MASK on it followed by one integer field in the
 *	   range 0 to 89. This sets the satellite mask angle and will determine the minimum
 *	   elevation angle for satellites to be tracked by the receiver. The default value
 *	   is 10 deg for the VP and 0 deg for all other receivers.
 *
1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
 *	There is an optional line with PPSCONTROL on it (only valid for M12 or M12+T
 *	   receivers, the option is read, but ignored for all others)
 *	   and it is followed by:
 *		ON	   Turn PPS on.  This is the default and the default for other
 *			       oncore receivers.  The PPS is on even if not tracking
 *			       any satellites.
 *		SATELLITE  Turns PPS on if tracking at least 1 satellite, else off.
 *		TRAIM	   Turns PPS on or off controlled by TRAIM.
 *	  The OFF option is NOT implemented, since the Oncore driver will not work
 *	     without the PPS signal.
 *
1205 1206 1207 1208 1209 1210 1211 1212 1213
 * So acceptable input would be
 *	# these are my coordinates (RWC)
 *	LON  -106 34.610
 *	LAT    35 08.999
 *	HT	1589	# could equally well say HT 5215 FT
 *	DELAY  60 ns
 */

	FILE	*fd;
1214 1215 1216
	char	*cc, *ca, line[100], units[2], device[64];
	const char	*dirs[] = { "/etc/ntp", "/etc", 0 };
	const char *cp, **cpp;
1217
	int	i, sign, lat_flg, long_flg, ht_flg, mode, mask;
1218 1219
	double	f1, f2, f3;

1220 1221 1222
	fd = NULL;	/* just to shutup gcc complaint */
	for (cpp=dirs; *cpp; cpp++) {
		cp = *cpp;
1223 1224
		snprintf(device, sizeof(device), "%s/ntp.oncore.%d",
			 cp, instance->unit);  /* try "ntp.oncore.0 */
1225 1226
		if ((fd=fopen(device, "r")))
			break;
1227 1228
		snprintf(device, sizeof(device), "%s/ntp.oncore%d",
			 cp, instance->unit);  /* try "ntp.oncore0" */
1229 1230
		if ((fd=fopen(device, "r")))
			break;
1231 1232
		snprintf(device, sizeof(device), "%s/ntp.oncore", cp);
		if ((fd=fopen(device, "r")))   /* last try "ntp.oncore" */
1233 1234 1235 1236 1237 1238
			break;
	}

	if (!fd) {	/* no inputfile, default to the works ... */
		instance->init_type = 4;
		return;
1239
	}
1240

1241
	mode = mask = 0;
1242 1243
	lat_flg = long_flg = ht_flg = 0;
	while (fgets(line, 100, fd)) {
1244
		char *cpw;
1245 1246

		/* Remove comments */
1247 1248
		if ((cpw = strchr(line, '#')))
			*cpw = '\0';
1249

1250 1251
		/* Remove trailing space */
		for (i = strlen(line);
1252
		     i > 0 && isascii((unsigned char)line[i - 1]) && isspace((unsigned char)line[i - 1]);
1253
			)
1254
			line[--i] = '\0';
1255 1256

		/* Remove leading space */
1257
		for (cc = line; *cc && isascii((unsigned char)*cc) && isspace((unsigned char)*cc); cc++)
1258 1259 1260 1261 1262 1263
			continue;

		/* Stop if nothing left */
		if (!*cc)
			continue;

1264
		/* Uppercase the command and find the arg */
1265
		for (ca = cc; *ca; ca++) {
1266 1267 1268 1269
			if (isascii((unsigned char)*ca)) {
				if (islower((unsigned char)*ca)) {
					*ca = toupper((unsigned char)*ca);
				} else if (isspace((unsigned char)*ca) || (*ca == '='))
1270 1271
					break;
			}
1272
		}
1273

1274
		/* Remove space (and possible =) leading the arg */
1275
		for (; *ca && isascii((unsigned char)*ca) && (isspace((unsigned char)*ca) || (*ca == '=')); ca++)
1276 1277
			continue;

1278
		if (!strncmp(cc, "STATUS", (size_t) 6) || !strncmp(cc, "SHMEM", (size_t) 5)) {
1279
			instance->shmem_fname = estrdup(ca);
1280 1281
			continue;
		}
1282 1283

		/* Uppercase argument as well */
1284 1285 1286
		for (cpw = ca; *cpw; cpw++)
			if (isascii((unsigned char)*cpw) && islower((unsigned char)*cpw))
				*cpw = toupper((unsigned char)*cpw);
1287

1288
		if (!strncmp(cc, "LAT", (size_t) 3)) {
1289
			f1 = f2 = f3 = 0;
1290
			sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
1291 1292 1293 1294 1295 1296 1297
			sign = 1;
			if (f1 < 0) {
				f1 = -f1;
				sign = -1;
			}
			instance->ss_lat = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
			lat_flg++;
1298
		} else if (!strncmp(cc, "LON", (size_t) 3)) {
1299
			f1 = f2 = f3 = 0;
1300
			sscanf(ca, "%lf %lf %lf", &f1, &f2, &f3);
1301 1302 1303 1304 1305 1306 1307
			sign = 1;
			if (f1 < 0) {
				f1 = -f1;
				sign = -1;
			}
			instance->ss_long = sign*1000*(fabs(f3) + 60*(fabs(f2) + 60*f1)); /*miliseconds*/
			long_flg++;
1308
		} else if (!strncmp(cc, "HT", (size_t) 2)) {
1309 1310
			f1 = 0;
			units[0] = '\0';
1311
			sscanf(ca, "%lf %1s", &f1, units);
1312 1313 1314 1315
			if (units[0] == 'F')
				f1 = 0.3048 * f1;
			instance->ss_ht = 100 * f1;    /* cm */
			ht_flg++;
1316
		} else if (!strncmp(cc, "DELAY", (size_t) 5)) {
1317 1318
			f1 = 0;
			units[0] = '\0';
1319
			sscanf(ca, "%lf %1s", &f1, units);
1320 1321 1322 1323 1324 1325 1326 1327 1328 1329
			if (units[0] == 'N')
				;
			else if (units[0] == 'U')
				f1 = 1000 * f1;
			else if (units[0] == 'M')
				f1 = 1000000 * f1;
			else
				f1 = 1000000000 * f1;
			if (f1 < 0 || f1 > 1.e9)
				f1 = 0;
1330 1331 1332 1333 1334
			if (f1 < 0 || f1 > 999999)
				oncore_log_f(instance, LOG_WARNING,
					     "PPS Cable delay of %fns out of Range, ignored",
					     f1);
			else
1335 1336
				instance->delay = f1;		/* delay in ns */
		} else if (!strncmp(cc, "OFFSET", (size_t) 6)) {
1337 1338
			f1 = 0;
			units[0] = '\0';
1339
			sscanf(ca, "%lf %1s", &f1, units);
1340 1341 1342 1343 1344 1345 1346 1347 1348 1349
			if (units[0] == 'N')
				;
			else if (units[0] == 'U')
				f1 = 1000 * f1;
			else if (units[0] == 'M')
				f1 = 1000000 * f1;
			else
				f1 = 1000000000 * f1;
			if (f1 < 0 || f1 > 1.e9)
				f1 = 0;
1350 1351 1352 1353 1354
			if (f1 < 0 || f1 > 999999999.)
				oncore_log_f(instance, LOG_WARNING,
					     "PPS Offset of %fns out of Range, ignored",
					     f1);
			else
1355 1356
				instance->offset = f1;		/* offset in ns */
		} else if (!strncmp(cc, "MODE", (size_t) 4)) {
1357
			sscanf(ca, "%d", &mode);
1358 1359
			if (mode < 0 || mode > 4)
				mode = 4;
1360
		} else if (!strncmp(cc, "ASSERT", (size_t) 6)) {
1361
			instance->assert = 1;
1362
		} else if (!strncmp(cc, "CLEAR", (size_t) 5)) {
1363
			instance->assert = 0;
1364 1365
		} else if (!strncmp(cc, "HARDPPS", (size_t) 7)) {
			instance->hardpps = 1;
1366
		} else if (!strncmp(cc, "POSN2D", (size_t) 6)) {
1367
			instance->shmem_Posn = 2;
1368
		} else if (!strncmp(cc, "POSN3D", (size_t) 6)) {
1369
			instance->shmem_Posn = 3;
1370 1371 1372
		} else if (!strncmp(cc, "CHAN", (size_t) 4)) {
			sscanf(ca, "%d", &i);
			if ((i == 6) || (i == 8) || (i == 12))
1373
				instance->chan_in = i;
1374
		} else if (!strncmp(cc, "TRAIM", (size_t) 5)) {
1375
			instance->traim_in = 1; 	/* so TRAIM alone is YES */
1376
			if (!strcmp(ca, "NO") || !strcmp(ca, "OFF"))    /* Yes/No, On/Off */
1377 1378
				instance->traim_in = 0;
		} else if (!strncmp(cc, "MASK", (size_t) 4)) {
1379
			/* coverity[unchecked_value] */
1380 1381 1382
			sscanf(ca, "%d", &mask);
			if (mask > -1 && mask < 90)
				instance->Ag = mask;			/* Satellite mask angle */
1383 1384 1385 1386 1387 1388 1389 1390
		} else if (!strncmp(cc,"PPSCONTROL",10)) {              /* pps control M12 only */
			if (!strcmp(ca,"ON") || !strcmp(ca, "CONTINUOUS")) {
				instance->pps_control = 1;		/* PPS always on */
			} else if (!strcmp(ca,"SATELLITE")) {
				instance->pps_control = 2;		/* PPS on when satellite is available */
			} else if (!strcmp(ca,"TRAIM")) {
				instance->pps_control = 3;		/* PPS on when TRAIM status is OK */
			} else {