xref: /netbsd-src/external/bsd/ntp/dist/ntpd/refclock_acts.c (revision eabc0478de71e4e011a5b4e0392741e01d491794)
1*eabc0478Schristos /*	$NetBSD: refclock_acts.c,v 1.13 2024/08/18 20:47:18 christos Exp $	*/
2abb0f93cSkardel 
3abb0f93cSkardel /*
4abb0f93cSkardel  * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
5abb0f93cSkardel  *	Services
6abb0f93cSkardel  */
7abb0f93cSkardel #ifdef HAVE_CONFIG_H
8abb0f93cSkardel #include <config.h>
9abb0f93cSkardel #endif
10abb0f93cSkardel 
112950cc38Schristos #if defined(REFCLOCK) && defined(CLOCK_ACTS)
12abb0f93cSkardel 
13abb0f93cSkardel #include "ntpd.h"
14abb0f93cSkardel #include "ntp_io.h"
15abb0f93cSkardel #include "ntp_unixtime.h"
16abb0f93cSkardel #include "ntp_refclock.h"
17abb0f93cSkardel #include "ntp_stdlib.h"
18abb0f93cSkardel #include "ntp_control.h"
19abb0f93cSkardel 
20abb0f93cSkardel #include <stdio.h>
21abb0f93cSkardel #include <ctype.h>
22abb0f93cSkardel #ifdef HAVE_SYS_IOCTL_H
23abb0f93cSkardel # include <sys/ioctl.h>
24abb0f93cSkardel #endif /* HAVE_SYS_IOCTL_H */
25abb0f93cSkardel 
26abb0f93cSkardel /*
27abb0f93cSkardel  * This driver supports the US (NIST, USNO) and European (PTB, NPL,
28abb0f93cSkardel  * etc.) modem time services, as well as Spectracom GPS and WWVB
29abb0f93cSkardel  * receivers connected via a modem. The driver periodically dials a
30abb0f93cSkardel  * number from a telephone list, receives the timecode data and
31abb0f93cSkardel  * calculates the local clock correction. It is designed primarily for
32abb0f93cSkardel  * use as backup when neither a radio clock nor connectivity to Internet
33abb0f93cSkardel  * time servers is available.
34abb0f93cSkardel  *
35abb0f93cSkardel  * This driver requires a modem with a Hayes-compatible command set and
36abb0f93cSkardel  * control over the modem data terminal ready (DTR) control line. The
37abb0f93cSkardel  * modem setup string is hard-coded in the driver and may require
382950cc38Schristos  * changes for nonstandard modems or special circumstances.
39abb0f93cSkardel  *
402950cc38Schristos  * When enabled, the calling program dials the first number in the
412950cc38Schristos  * phones file. If that call fails, it dials the second number and
422950cc38Schristos  * so on. The phone number is specified by the Hayes ATDT prefix
432950cc38Schristos  * followed by the number itself, including the long-distance prefix
442950cc38Schristos  * and delay code, if necessary. The calling program is enabled
452950cc38Schristos  * when (a) fudge flag1 is set by ntpdc, (b) at each poll interval
462950cc38Schristos  * when no other synchronization sources are present, and (c) at each
472950cc38Schristos  * poll interval whether or not other synchronization sources are
482950cc38Schristos  * present. The calling program disconnects if (a) the called party
492950cc38Schristos  * is busy or does not answer, (b) the called party disconnects
502950cc38Schristos  * before a sufficient nuimber of timecodes have been received.
51abb0f93cSkardel  *
52abb0f93cSkardel  * The driver is transparent to each of the modem time services and
53abb0f93cSkardel  * Spectracom radios. It selects the parsing algorithm depending on the
54abb0f93cSkardel  * message length. There is some hazard should the message be corrupted.
55abb0f93cSkardel  * However, the data format is checked carefully and only if all checks
56abb0f93cSkardel  * succeed is the message accepted. Corrupted lines are discarded
57abb0f93cSkardel  * without complaint.
58abb0f93cSkardel  *
59abb0f93cSkardel  * Fudge controls
60abb0f93cSkardel  *
61abb0f93cSkardel  * flag1	force a call in manual mode
62abb0f93cSkardel  * flag2	enable port locking (not verified)
632950cc38Schristos  * flag3	not used
64abb0f93cSkardel  * flag4	not used
65abb0f93cSkardel  *
66abb0f93cSkardel  * time1	offset adjustment (s)
67abb0f93cSkardel  *
682950cc38Schristos  * Ordinarily, the serial port is connected to a modem and the phones
692950cc38Schristos  * list is defined. If no phones list is defined, the port can be
702950cc38Schristos  * connected directly to a device or another computer. In this case the
712950cc38Schristos  * driver will send a single character 'T' at each poll event. If
722950cc38Schristos  * fudge flag2 is enabled, port locking allows the modem to be shared
732950cc38Schristos  * when not in use by this driver.
74abb0f93cSkardel  */
75abb0f93cSkardel /*
76abb0f93cSkardel  * National Institute of Science and Technology (NIST)
77abb0f93cSkardel  *
78abb0f93cSkardel  * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
79abb0f93cSkardel  *
80abb0f93cSkardel  * Data Format
81abb0f93cSkardel  *
82abb0f93cSkardel  * National Institute of Standards and Technology
83abb0f93cSkardel  * Telephone Time Service, Generator 3B
84abb0f93cSkardel  * Enter question mark "?" for HELP
85abb0f93cSkardel  *                         D  L D
86abb0f93cSkardel  *  MJD  YR MO DA H  M  S  ST S UT1 msADV        <OTM>
87abb0f93cSkardel  * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF>
88abb0f93cSkardel  * ...
89abb0f93cSkardel  *
90abb0f93cSkardel  * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is
91abb0f93cSkardel  * the on-time markers echoed by the driver and used by NIST to measure
922950cc38Schristos  * and correct for the propagation delay. Note: the ACTS timecode has
932950cc38Schristos  * recently been changed to eliminate the * on-time indicator. The
942950cc38Schristos  * reason for this and the long term implications are not clear.
95abb0f93cSkardel  *
96abb0f93cSkardel  * US Naval Observatory (USNO)
97abb0f93cSkardel  *
98abb0f93cSkardel  * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
99abb0f93cSkardel  *
100abb0f93cSkardel  * Data Format (two lines, repeating at one-second intervals)
101abb0f93cSkardel  *
102abb0f93cSkardel  * jjjjj nnn hhmmss UTC<CR><LF>
103abb0f93cSkardel  * *<CR><LF>
104abb0f93cSkardel  *
105abb0f93cSkardel  * jjjjj	modified Julian day number (not used)
106abb0f93cSkardel  * nnn		day of year
107abb0f93cSkardel  * hhmmss	second of day
108abb0f93cSkardel  * *		on-time marker for previous timecode
109abb0f93cSkardel  * ...
110abb0f93cSkardel  *
111abb0f93cSkardel  * USNO does not correct for the propagation delay. A fudge time1 of
112abb0f93cSkardel  * about .06 s is advisable.
113abb0f93cSkardel  *
114abb0f93cSkardel  * European Services (PTB, NPL, etc.)
115abb0f93cSkardel  *
116abb0f93cSkardel  * PTB: +49 531 512038 (Germany)
117abb0f93cSkardel  * NPL: 0906 851 6333 (UK only)
118abb0f93cSkardel  *
119abb0f93cSkardel  * Data format (see the documentation for phone numbers and formats.)
120abb0f93cSkardel  *
121abb0f93cSkardel  * 1995-01-23 20:58:51 MEZ  10402303260219950123195849740+40000500<CR><LF>
122abb0f93cSkardel  *
123abb0f93cSkardel  * Spectracom GPS and WWVB Receivers
124abb0f93cSkardel  *
125abb0f93cSkardel  * If a modem is connected to a Spectracom receiver, this driver will
126abb0f93cSkardel  * call it up and retrieve the time in one of two formats. As this
127abb0f93cSkardel  * driver does not send anything, the radio will have to either be
128abb0f93cSkardel  * configured in continuous mode or be polled by another local driver.
129abb0f93cSkardel  */
130abb0f93cSkardel /*
131abb0f93cSkardel  * Interface definitions
132abb0f93cSkardel  */
133abb0f93cSkardel #define	DEVICE		"/dev/acts%d" /* device name and unit */
1342950cc38Schristos #define	SPEED232	B19200	/* uart speed (19200 bps) */
135abb0f93cSkardel #define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
1362950cc38Schristos #define LOCKFILE	"/var/spool/lock/LCK..cua%d"
137abb0f93cSkardel #define DESCRIPTION	"Automated Computer Time Service" /* WRU */
138abb0f93cSkardel #define REFID		"NONE"	/* default reference ID */
139abb0f93cSkardel #define MSGCNT		20	/* max message count */
140abb0f93cSkardel #define	MAXPHONE	10	/* max number of phone numbers */
141abb0f93cSkardel 
142abb0f93cSkardel /*
1432950cc38Schristos  * Calling program modes (mode)
144abb0f93cSkardel  */
1452950cc38Schristos #define MODE_BACKUP	0	/* backup mode */
1462950cc38Schristos #define MODE_AUTO	1	/* automatic mode */
147abb0f93cSkardel #define MODE_MANUAL	2	/* manual mode */
148abb0f93cSkardel 
149abb0f93cSkardel /*
1502950cc38Schristos  * Service identifiers (message length)
151abb0f93cSkardel  */
152abb0f93cSkardel #define REFACTS		"NIST"	/* NIST reference ID */
1532950cc38Schristos #define LENACTS		50	/* NIST format A */
154abb0f93cSkardel #define REFUSNO		"USNO"	/* USNO reference ID */
155abb0f93cSkardel #define LENUSNO		20	/* USNO */
156abb0f93cSkardel #define REFPTB		"PTB\0"	/* PTB/NPL reference ID */
157abb0f93cSkardel #define LENPTB		78	/* PTB/NPL format */
158abb0f93cSkardel #define REFWWVB		"WWVB"	/* WWVB reference ID */
159abb0f93cSkardel #define	LENWWVB0	22	/* WWVB format 0 */
160abb0f93cSkardel #define	LENWWVB2	24	/* WWVB format 2 */
161abb0f93cSkardel #define LF		0x0a	/* ASCII LF */
162abb0f93cSkardel 
163abb0f93cSkardel /*
1642950cc38Schristos  * Modem setup strings. These may have to be changed for
1652950cc38Schristos  * some modems.
166abb0f93cSkardel  *
167abb0f93cSkardel  * AT	command prefix
168abb0f93cSkardel  * B1	US answer tone
169abb0f93cSkardel  * &C0	disable carrier detect
170abb0f93cSkardel  * &D2	hang up and return to command mode on DTR transition
171abb0f93cSkardel  * E0	modem command echo disabled
1722950cc38Schristos  * L1	set modem speaker volume to low level
173abb0f93cSkardel  * M1	speaker enabled until carrier detect
174abb0f93cSkardel  * Q0	return result codes
175abb0f93cSkardel  * V1	return result codes as English words
1762950cc38Schristos  * Y1	enable long-space disconnect
177abb0f93cSkardel  */
1782950cc38Schristos const char def_modem_setup[] = "ATB1&C0&D2E0L1M1Q0V1Y1";
1792950cc38Schristos const char *modem_setup = def_modem_setup;
180abb0f93cSkardel 
181abb0f93cSkardel /*
182abb0f93cSkardel  * Timeouts (all in seconds)
183abb0f93cSkardel  */
184abb0f93cSkardel #define SETUP		3	/* setup timeout */
1852950cc38Schristos #define	REDIAL		30	/* redial timeout */
186abb0f93cSkardel #define ANSWER		60	/* answer timeout */
1872950cc38Schristos #define TIMECODE	60	/* message timeout */
1882950cc38Schristos #define	MAXCODE		20	/* max timecodes */
189abb0f93cSkardel 
190abb0f93cSkardel /*
191abb0f93cSkardel  * State machine codes
192abb0f93cSkardel  */
1932950cc38Schristos typedef enum {
1942950cc38Schristos 	S_IDLE,			/* wait for poll */
1952950cc38Schristos 	S_SETUP,		/* send modem setup */
1962950cc38Schristos 	S_CONNECT,		/* wait for answer */
1972950cc38Schristos 	S_MSG			/* wait for timecode */
1982950cc38Schristos } teModemState;
199abb0f93cSkardel 
200abb0f93cSkardel /*
201abb0f93cSkardel  * Unit control structure
202abb0f93cSkardel  */
203abb0f93cSkardel struct actsunit {
204abb0f93cSkardel 	int	unit;		/* unit number */
205abb0f93cSkardel 	int	state;		/* the first one was Delaware */
206abb0f93cSkardel 	int	timer;		/* timeout counter */
207abb0f93cSkardel 	int	retry;		/* retry index */
208abb0f93cSkardel 	int	msgcnt;		/* count of messages received */
209abb0f93cSkardel 	l_fp	tstamp;		/* on-time timestamp */
2102950cc38Schristos 	char	*bufptr;	/* next incoming char stored here */
2112950cc38Schristos 	char	buf[BMAX];	/* bufptr roams within buf[] */
212abb0f93cSkardel };
213abb0f93cSkardel 
214abb0f93cSkardel /*
215abb0f93cSkardel  * Function prototypes
216abb0f93cSkardel  */
217abb0f93cSkardel static	int	acts_start	(int, struct peer *);
218abb0f93cSkardel static	void	acts_shutdown	(int, struct peer *);
219abb0f93cSkardel static	void	acts_receive	(struct recvbuf *);
2202950cc38Schristos static	void	acts_message	(struct peer *, const char *);
2212950cc38Schristos static	void	acts_timecode	(struct peer *, const char *);
222abb0f93cSkardel static	void	acts_poll	(int, struct peer *);
2232950cc38Schristos static	void	acts_timeout	(struct peer *, teModemState);
224abb0f93cSkardel static	void	acts_timer	(int, struct peer *);
2252950cc38Schristos static	void	acts_close	(struct peer *);
226abb0f93cSkardel 
227abb0f93cSkardel /*
228abb0f93cSkardel  * Transfer vector (conditional structure name)
229abb0f93cSkardel  */
230abb0f93cSkardel struct refclock refclock_acts = {
231abb0f93cSkardel 	acts_start,		/* start up driver */
232abb0f93cSkardel 	acts_shutdown,		/* shut down driver */
233abb0f93cSkardel 	acts_poll,		/* transmit poll message */
234abb0f93cSkardel 	noentry,		/* not used */
235abb0f93cSkardel 	noentry,		/* not used */
236abb0f93cSkardel 	noentry,		/* not used */
237abb0f93cSkardel 	acts_timer		/* housekeeping timer */
238abb0f93cSkardel };
239abb0f93cSkardel 
240abb0f93cSkardel /*
241abb0f93cSkardel  * Initialize data for processing
242abb0f93cSkardel  */
243abb0f93cSkardel static int
244abb0f93cSkardel acts_start(
245abb0f93cSkardel 	int	unit,
246abb0f93cSkardel 	struct peer *peer
247abb0f93cSkardel 	)
248abb0f93cSkardel {
249abb0f93cSkardel 	struct actsunit *up;
250abb0f93cSkardel 	struct refclockproc *pp;
2512950cc38Schristos 	const char *setup;
252abb0f93cSkardel 
253abb0f93cSkardel 	/*
254abb0f93cSkardel 	 * Allocate and initialize unit structure
255abb0f93cSkardel 	 */
2562950cc38Schristos 	up = emalloc_zero(sizeof(struct actsunit));
257abb0f93cSkardel 	up->unit = unit;
258abb0f93cSkardel 	pp = peer->procptr;
2592950cc38Schristos 	pp->unitptr = up;
260abb0f93cSkardel 	pp->io.clock_recv = acts_receive;
2612950cc38Schristos 	pp->io.srcclock = peer;
262abb0f93cSkardel 	pp->io.datalen = 0;
2632950cc38Schristos 	pp->io.fd = -1;
264abb0f93cSkardel 
265abb0f93cSkardel 	/*
266abb0f93cSkardel 	 * Initialize miscellaneous variables
267abb0f93cSkardel 	 */
268abb0f93cSkardel 	peer->precision = PRECISION;
269abb0f93cSkardel 	pp->clockdesc = DESCRIPTION;
2702950cc38Schristos 	memcpy(&pp->refid, REFID, 4);
271abb0f93cSkardel 	peer->sstclktype = CTL_SST_TS_TELEPHONE;
2722950cc38Schristos 	up->bufptr = up->buf;
2732950cc38Schristos 	if (def_modem_setup == modem_setup) {
2742950cc38Schristos 		setup = get_ext_sys_var("modemsetup");
2752950cc38Schristos 		if (setup != NULL)
2762950cc38Schristos 			modem_setup = estrdup(setup);
2772950cc38Schristos 	}
2782950cc38Schristos 
279abb0f93cSkardel 	return (1);
280abb0f93cSkardel }
281abb0f93cSkardel 
282abb0f93cSkardel 
283abb0f93cSkardel /*
284abb0f93cSkardel  * acts_shutdown - shut down the clock
285abb0f93cSkardel  */
286abb0f93cSkardel static void
287abb0f93cSkardel acts_shutdown(
288abb0f93cSkardel 	int	unit,
289abb0f93cSkardel 	struct peer *peer
290abb0f93cSkardel 	)
291abb0f93cSkardel {
292abb0f93cSkardel 	struct actsunit *up;
293abb0f93cSkardel 	struct refclockproc *pp;
294abb0f93cSkardel 
295abb0f93cSkardel 	/*
296abb0f93cSkardel 	 * Warning: do this only when a call is not in progress.
297abb0f93cSkardel 	 */
298abb0f93cSkardel 	pp = peer->procptr;
2992950cc38Schristos 	up = pp->unitptr;
3002950cc38Schristos 	acts_close(peer);
301abb0f93cSkardel 	free(up);
302abb0f93cSkardel }
303abb0f93cSkardel 
304abb0f93cSkardel 
305abb0f93cSkardel /*
306abb0f93cSkardel  * acts_receive - receive data from the serial interface
307abb0f93cSkardel  */
308abb0f93cSkardel static void
309abb0f93cSkardel acts_receive(
310abb0f93cSkardel 	struct recvbuf *rbufp
311abb0f93cSkardel 	)
312abb0f93cSkardel {
313abb0f93cSkardel 	struct actsunit *up;
314abb0f93cSkardel 	struct refclockproc *pp;
315abb0f93cSkardel 	struct peer *peer;
3162950cc38Schristos 	char	tbuf[sizeof(up->buf)];
317abb0f93cSkardel 	char *	tptr;
3182950cc38Schristos 	int	octets;
319abb0f93cSkardel 
320abb0f93cSkardel 	/*
321abb0f93cSkardel 	 * Initialize pointers and read the timecode and timestamp. Note
322abb0f93cSkardel 	 * we are in raw mode and victim of whatever the terminal
323abb0f93cSkardel 	 * interface kicks up; so, we have to reassemble messages from
324abb0f93cSkardel 	 * arbitrary fragments. Capture the timecode at the beginning of
325abb0f93cSkardel 	 * the message and at the '*' and '#' on-time characters.
326abb0f93cSkardel 	 */
3272950cc38Schristos 	peer = rbufp->recv_peer;
328abb0f93cSkardel 	pp = peer->procptr;
3292950cc38Schristos 	up = pp->unitptr;
3302950cc38Schristos 	octets = sizeof(up->buf) - (up->bufptr - up->buf);
3312950cc38Schristos 	refclock_gtraw(rbufp, tbuf, octets, &pp->lastrec);
332abb0f93cSkardel 	for (tptr = tbuf; *tptr != '\0'; tptr++) {
333abb0f93cSkardel 		if (*tptr == LF) {
3342950cc38Schristos 			if (up->bufptr == up->buf) {
335abb0f93cSkardel 				up->tstamp = pp->lastrec;
336abb0f93cSkardel 				continue;
337abb0f93cSkardel 			} else {
338abb0f93cSkardel 				*up->bufptr = '\0';
3392950cc38Schristos 				up->bufptr = up->buf;
3402950cc38Schristos 				acts_message(peer, up->buf);
341abb0f93cSkardel 			}
342eff9ac27Schristos 		} else if (!iscntrl((unsigned char)*tptr)) {
343abb0f93cSkardel 			*up->bufptr++ = *tptr;
344abb0f93cSkardel 			if (*tptr == '*' || *tptr == '#') {
345abb0f93cSkardel 				up->tstamp = pp->lastrec;
346*eabc0478Schristos 				refclock_write(peer, tptr, 1, "data");
347abb0f93cSkardel 			}
348abb0f93cSkardel 		}
349abb0f93cSkardel 	}
350abb0f93cSkardel }
351abb0f93cSkardel 
352abb0f93cSkardel 
353abb0f93cSkardel /*
354abb0f93cSkardel  * acts_message - process message
355abb0f93cSkardel  */
356abb0f93cSkardel void
357abb0f93cSkardel acts_message(
3582950cc38Schristos 	struct peer *peer,
3592950cc38Schristos 	const char *msg
3602950cc38Schristos 	)
3612950cc38Schristos {
3622950cc38Schristos 	struct actsunit *up;
3632950cc38Schristos 	struct refclockproc *pp;
3642950cc38Schristos 	char	tbuf[BMAX];
3652950cc38Schristos 	int		dtr = TIOCM_DTR;
3662950cc38Schristos 
3672950cc38Schristos 	DPRINTF(1, ("acts: %d %s\n", (int)strlen(msg), msg));
3682950cc38Schristos 
3692950cc38Schristos 	/*
3702950cc38Schristos 	 * What to do depends on the state and the first token in the
3712950cc38Schristos 	 * message.
3722950cc38Schristos 	 */
3732950cc38Schristos 	pp = peer->procptr;
3742950cc38Schristos 	up = pp->unitptr;
3752950cc38Schristos 
3762950cc38Schristos 	/*
3772950cc38Schristos 	 * Extract the first token in the line.
3782950cc38Schristos 	 */
3792950cc38Schristos 	strlcpy(tbuf, msg, sizeof(tbuf));
3802950cc38Schristos 	strtok(tbuf, " ");
3812950cc38Schristos 	switch (up->state) {
3822950cc38Schristos 
3832950cc38Schristos 	/*
3842950cc38Schristos 	 * We are waiting for the OK response to the modem setup
3852950cc38Schristos 	 * command. When this happens, dial the number followed.
3862950cc38Schristos 	 * If anything other than OK is received, just ignore it
3872950cc38Schristos 	 * and wait for timeoue.
3882950cc38Schristos 	 */
3892950cc38Schristos 	case S_SETUP:
3902950cc38Schristos 		if (strcmp(tbuf, "OK") != 0) {
3912950cc38Schristos 			/*
3922950cc38Schristos 			 * We disable echo with MODEM_SETUP's E0 but
3932950cc38Schristos 			 * if the modem was previously E1, we will
3942950cc38Schristos 			 * see MODEM_SETUP echoed before the OK/ERROR.
3952950cc38Schristos 			 * Ignore it.
3962950cc38Schristos 			 */
3972950cc38Schristos 			if (!strcmp(tbuf, modem_setup))
3982950cc38Schristos 				return;
3992950cc38Schristos 			break;
4002950cc38Schristos 		}
4012950cc38Schristos 
4022950cc38Schristos 		mprintf_event(PEVNT_CLOCK, peer, "DIAL #%d %s",
4032950cc38Schristos 			      up->retry, sys_phone[up->retry]);
4042950cc38Schristos 		if (ioctl(pp->io.fd, TIOCMBIS, &dtr) < 0)
4052950cc38Schristos 			msyslog(LOG_ERR, "acts: ioctl(TIOCMBIS) failed: %m");
406*eabc0478Schristos 		refclock_write(peer, sys_phone[up->retry],
407*eabc0478Schristos 			       strlen(sys_phone[up->retry]),
408*eabc0478Schristos 			       "DIAL");
409*eabc0478Schristos 		refclock_write(peer, "\r", 1, "CR");
4102950cc38Schristos 		up->retry++;
4112950cc38Schristos 		up->state = S_CONNECT;
4122950cc38Schristos 		up->timer = ANSWER;
4132950cc38Schristos 		return;
4142950cc38Schristos 
4152950cc38Schristos 	/*
4162950cc38Schristos 	 * We are waiting for the CONNECT response to the dial
4172950cc38Schristos 	 * command. When this happens, listen for timecodes. If
4182950cc38Schristos 	 * somthing other than CONNECT is received, like BUSY
4192950cc38Schristos 	 * or NO CARRIER, abort the call.
4202950cc38Schristos 	 */
4212950cc38Schristos 	case S_CONNECT:
4222950cc38Schristos 		if (strcmp(tbuf, "CONNECT") != 0)
4232950cc38Schristos 			break;
4242950cc38Schristos 
4252950cc38Schristos 		report_event(PEVNT_CLOCK, peer, msg);
4262950cc38Schristos 		up->state = S_MSG;
4272950cc38Schristos 		up->timer = TIMECODE;
4282950cc38Schristos 		return;
4292950cc38Schristos 
4302950cc38Schristos 	/*
4312950cc38Schristos 	 * We are waiting for a timecode response. Pass it to
4322950cc38Schristos 	 * the parser. If NO CARRIER is received, save the
4332950cc38Schristos 	 * messages and abort the call.
4342950cc38Schristos 	 */
4352950cc38Schristos 	case S_MSG:
4362950cc38Schristos 		if (strcmp(tbuf, "NO") == 0)
4372950cc38Schristos 			report_event(PEVNT_CLOCK, peer, msg);
4382950cc38Schristos 		if (up->msgcnt < MAXCODE)
4392950cc38Schristos 			acts_timecode(peer, msg);
4402950cc38Schristos 		else
4412950cc38Schristos 			acts_timeout(peer, S_MSG);
4422950cc38Schristos 		return;
4432950cc38Schristos 	}
4442950cc38Schristos 
4452950cc38Schristos 	/*
4462950cc38Schristos 	 * Other response. Tell us about it.
4472950cc38Schristos 	 */
4482950cc38Schristos 	report_event(PEVNT_CLOCK, peer, msg);
4492950cc38Schristos 	acts_close(peer);
4502950cc38Schristos }
4512950cc38Schristos 
4522950cc38Schristos 
4532950cc38Schristos /*
4542950cc38Schristos  * acts_timeout - called on timeout
4552950cc38Schristos  */
4562950cc38Schristos static void
4572950cc38Schristos acts_timeout(
4582950cc38Schristos 	struct peer *peer,
4592950cc38Schristos 	teModemState	dstate
4602950cc38Schristos 	)
4612950cc38Schristos {
4622950cc38Schristos 	struct actsunit *up;
4632950cc38Schristos 	struct refclockproc *pp;
4642950cc38Schristos 	int	fd;
4652950cc38Schristos 	char	device[20];
4662950cc38Schristos 	char	lockfile[128], pidbuf[8];
4672950cc38Schristos 
4682950cc38Schristos 	/*
4692950cc38Schristos 	 * The state machine is driven by messages from the modem,
4702950cc38Schristos 	 * when first started and at timeout.
4712950cc38Schristos 	 */
4722950cc38Schristos 	pp = peer->procptr;
4732950cc38Schristos 	up = pp->unitptr;
4742950cc38Schristos 	switch (dstate) {
4752950cc38Schristos 
4762950cc38Schristos 	/*
4772950cc38Schristos 	 * System poll event. Lock the modem port, open the device
4782950cc38Schristos 	 * and send the setup command.
4792950cc38Schristos 	 */
4802950cc38Schristos 	case S_IDLE:
4812950cc38Schristos 		if (-1 != pp->io.fd)
4822950cc38Schristos 			return;		/* port is already open */
4832950cc38Schristos 
4842950cc38Schristos 		/*
4852950cc38Schristos 		 * Lock the modem port. If busy, retry later. Note: if
4862950cc38Schristos 		 * something fails between here and the close, the lock
4872950cc38Schristos 		 * file may not be removed.
4882950cc38Schristos 		 */
4892950cc38Schristos 		if (pp->sloppyclockflag & CLK_FLAG2) {
4902950cc38Schristos 			snprintf(lockfile, sizeof(lockfile), LOCKFILE,
4912950cc38Schristos 			    up->unit);
4922950cc38Schristos 			fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
4932950cc38Schristos 			    0644);
4942950cc38Schristos 			if (fd < 0) {
4952950cc38Schristos 				report_event(PEVNT_CLOCK, peer, "acts: port busy");
4962950cc38Schristos 				return;
4972950cc38Schristos 			}
4982950cc38Schristos 			snprintf(pidbuf, sizeof(pidbuf), "%d\n",
4992950cc38Schristos 			    (u_int)getpid());
5002950cc38Schristos 			if (write(fd, pidbuf, strlen(pidbuf)) < 0)
5012950cc38Schristos 				msyslog(LOG_ERR, "acts: write lock fails %m");
5022950cc38Schristos 			close(fd);
5032950cc38Schristos 		}
5042950cc38Schristos 
5052950cc38Schristos 		/*
5062950cc38Schristos 		 * Open the device in raw mode and link the I/O.
5072950cc38Schristos 		 */
5082950cc38Schristos 		snprintf(device, sizeof(device), DEVICE,
5092950cc38Schristos 		    up->unit);
510*eabc0478Schristos 		fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_ACTS |
5112950cc38Schristos 		    LDISC_RAW | LDISC_REMOTE);
512f69b3e79Schristos 		if (fd < 0) {
5132950cc38Schristos 			msyslog(LOG_ERR, "acts: open fails %m");
5142950cc38Schristos 			return;
5152950cc38Schristos 		}
5162950cc38Schristos 		pp->io.fd = fd;
5172950cc38Schristos 		if (!io_addclock(&pp->io)) {
5182950cc38Schristos 			msyslog(LOG_ERR, "acts: addclock fails");
5192950cc38Schristos 			close(fd);
5202950cc38Schristos 			pp->io.fd = -1;
5212950cc38Schristos 			return;
5222950cc38Schristos 		}
5232950cc38Schristos 		up->msgcnt = 0;
5242950cc38Schristos 		up->bufptr = up->buf;
5252950cc38Schristos 
5262950cc38Schristos 		/*
5272950cc38Schristos 		 * If the port is directly connected to the device, skip
5282950cc38Schristos 		 * the modem business and send 'T' for Spectrabum.
5292950cc38Schristos 		 */
5302950cc38Schristos 		if (sys_phone[up->retry] == NULL) {
531*eabc0478Schristos 			refclock_write(peer, "T", 1, "T");
5322950cc38Schristos 			up->state = S_MSG;
5332950cc38Schristos 			up->timer = TIMECODE;
5342950cc38Schristos 			return;
5352950cc38Schristos 		}
5362950cc38Schristos 
5372950cc38Schristos 		/*
5382950cc38Schristos 		 * Initialize the modem. This works with Hayes-
5392950cc38Schristos 		 * compatible modems.
5402950cc38Schristos 		 */
5412950cc38Schristos 		mprintf_event(PEVNT_CLOCK, peer, "SETUP %s",
5422950cc38Schristos 			      modem_setup);
543*eabc0478Schristos 		refclock_write(peer, modem_setup, strlen(modem_setup),
544*eabc0478Schristos 			       "SETUP");
545*eabc0478Schristos 		refclock_write(peer, "\r", 1, "CR");
5462950cc38Schristos 		up->state = S_SETUP;
5472950cc38Schristos 		up->timer = SETUP;
5482950cc38Schristos 		return;
5492950cc38Schristos 
5502950cc38Schristos 	/*
5512950cc38Schristos 	 * In SETUP state the modem did not respond OK to setup string.
5522950cc38Schristos 	 */
5532950cc38Schristos 	case S_SETUP:
5542950cc38Schristos 		report_event(PEVNT_CLOCK, peer, "no modem");
5552950cc38Schristos 		break;
5562950cc38Schristos 
5572950cc38Schristos 	/*
5582950cc38Schristos 	 * In CONNECT state the call did not complete. Abort the call.
5592950cc38Schristos 	 */
5602950cc38Schristos 	case S_CONNECT:
5612950cc38Schristos 		report_event(PEVNT_CLOCK, peer, "no answer");
5622950cc38Schristos 		break;
5632950cc38Schristos 
5642950cc38Schristos 	/*
5652950cc38Schristos 	 * In MSG states no further timecodes are expected. If any
5662950cc38Schristos 	 * timecodes have arrived, update the clock. In any case,
5672950cc38Schristos 	 * terminate the call.
5682950cc38Schristos 	 */
5692950cc38Schristos 	case S_MSG:
5702950cc38Schristos 		if (up->msgcnt == 0) {
5712950cc38Schristos 			report_event(PEVNT_CLOCK, peer, "no timecodes");
5722950cc38Schristos 		} else {
5732950cc38Schristos 			pp->lastref = pp->lastrec;
5742950cc38Schristos 			record_clock_stats(&peer->srcadr, pp->a_lastcode);
5752950cc38Schristos 			refclock_receive(peer);
5762950cc38Schristos 		}
5772950cc38Schristos 		break;
5782950cc38Schristos 	}
5792950cc38Schristos 	acts_close(peer);
5802950cc38Schristos }
5812950cc38Schristos 
5822950cc38Schristos 
5832950cc38Schristos /*
5842950cc38Schristos  * acts_close - close and prepare for next call.
5852950cc38Schristos  *
5862950cc38Schristos  * In ClOSE state no further protocol actions are required
5872950cc38Schristos  * other than to close and release the device and prepare to
5882950cc38Schristos  * dial the next number if necessary.
5892950cc38Schristos  */
5902950cc38Schristos void
5912950cc38Schristos acts_close(
592abb0f93cSkardel 	struct peer *peer
593abb0f93cSkardel 	)
594abb0f93cSkardel {
595abb0f93cSkardel 	struct actsunit *up;
596abb0f93cSkardel 	struct refclockproc *pp;
5972950cc38Schristos 	char	lockfile[128];
5982950cc38Schristos 	int	dtr;
599abb0f93cSkardel 
600abb0f93cSkardel 	pp = peer->procptr;
6012950cc38Schristos 	up = pp->unitptr;
6022950cc38Schristos 	if (pp->io.fd != -1) {
6032950cc38Schristos 		report_event(PEVNT_CLOCK, peer, "close");
6042950cc38Schristos 		dtr = TIOCM_DTR;
6052950cc38Schristos 		if (ioctl(pp->io.fd, TIOCMBIC, &dtr) < 0)
6062950cc38Schristos 			msyslog(LOG_ERR, "acts: ioctl(TIOCMBIC) failed: %m");
6072950cc38Schristos 		io_closeclock(&pp->io);
6082950cc38Schristos 		pp->io.fd = -1;
6092950cc38Schristos 	}
6102950cc38Schristos 	if (pp->sloppyclockflag & CLK_FLAG2) {
6112950cc38Schristos 		snprintf(lockfile, sizeof(lockfile),
6122950cc38Schristos 		    LOCKFILE, up->unit);
6132950cc38Schristos 		unlink(lockfile);
6142950cc38Schristos 	}
6152950cc38Schristos 	if (up->msgcnt == 0 && up->retry > 0) {
6162950cc38Schristos 		if (sys_phone[up->retry] != NULL) {
6172950cc38Schristos 			up->state = S_IDLE;
6182950cc38Schristos 			up->timer = REDIAL;
619abb0f93cSkardel 			return;
620abb0f93cSkardel 		}
621abb0f93cSkardel 	}
6222950cc38Schristos 	up->state = S_IDLE;
6232950cc38Schristos 	up->timer = 0;
624abb0f93cSkardel }
6252950cc38Schristos 
6262950cc38Schristos 
6272950cc38Schristos /*
6282950cc38Schristos  * acts_poll - called by the transmit routine
6292950cc38Schristos  */
6302950cc38Schristos static void
6312950cc38Schristos acts_poll(
6322950cc38Schristos 	int	unit,
6332950cc38Schristos 	struct peer *peer
6342950cc38Schristos 	)
6352950cc38Schristos {
6362950cc38Schristos 	struct actsunit *up;
6372950cc38Schristos 	struct refclockproc *pp;
6382950cc38Schristos 
6392950cc38Schristos 	/*
6402950cc38Schristos 	 * This routine is called at every system poll. All it does is
6412950cc38Schristos 	 * set flag1 under certain conditions. The real work is done by
6422950cc38Schristos 	 * the timeout routine and state machine.
6432950cc38Schristos 	 */
6442950cc38Schristos 	pp = peer->procptr;
6452950cc38Schristos 	up = pp->unitptr;
6462950cc38Schristos 	switch (peer->ttl) {
6472950cc38Schristos 
6482950cc38Schristos 	/*
6492950cc38Schristos 	 * In manual mode the calling program is activated by the ntpdc
6502950cc38Schristos 	 * program using the enable flag (fudge flag1), either manually
6512950cc38Schristos 	 * or by a cron job.
6522950cc38Schristos 	 */
6532950cc38Schristos 	case MODE_MANUAL:
654abb0f93cSkardel 		return;
655abb0f93cSkardel 
656abb0f93cSkardel 	/*
6572950cc38Schristos 	 * In automatic mode the calling program runs continuously at
6582950cc38Schristos 	 * intervals determined by the poll event or specified timeout.
659abb0f93cSkardel 	 */
6602950cc38Schristos 	case MODE_AUTO:
661abb0f93cSkardel 		break;
6622950cc38Schristos 
6632950cc38Schristos 	/*
6642950cc38Schristos 	 * In backup mode the calling program runs continuously as long
6652950cc38Schristos 	 * as either no peers are available or this peer is selected.
6662950cc38Schristos 	 */
6672950cc38Schristos 	case MODE_BACKUP:
6682950cc38Schristos 		if (!(sys_peer == NULL || sys_peer == peer))
6692950cc38Schristos 			return;
6702950cc38Schristos 
6712950cc38Schristos 		break;
6722950cc38Schristos 	}
6732950cc38Schristos 	pp->polls++;
6742950cc38Schristos 	if (S_IDLE == up->state) {
6752950cc38Schristos 		up->retry = 0;
6762950cc38Schristos 		acts_timeout(peer, S_IDLE);
6772950cc38Schristos 	}
6782950cc38Schristos }
6792950cc38Schristos 
6802950cc38Schristos 
6812950cc38Schristos /*
6822950cc38Schristos  * acts_timer - called at one-second intervals
6832950cc38Schristos  */
6842950cc38Schristos static void
6852950cc38Schristos acts_timer(
6862950cc38Schristos 	int	unit,
6872950cc38Schristos 	struct peer *peer
6882950cc38Schristos 	)
6892950cc38Schristos {
6902950cc38Schristos 	struct actsunit *up;
6912950cc38Schristos 	struct refclockproc *pp;
6922950cc38Schristos 
6932950cc38Schristos 	/*
6942950cc38Schristos 	 * This routine implments a timeout which runs for a programmed
6952950cc38Schristos 	 * interval. The counter is initialized by the state machine and
6962950cc38Schristos 	 * counts down to zero. Upon reaching zero, the state machine is
6972950cc38Schristos 	 * called. If flag1 is set while timer is zero, force a call.
6982950cc38Schristos 	 */
6992950cc38Schristos 	pp = peer->procptr;
7002950cc38Schristos 	up = pp->unitptr;
7012950cc38Schristos 	if (up->timer == 0) {
7022950cc38Schristos 		if (pp->sloppyclockflag & CLK_FLAG1) {
7032950cc38Schristos 			pp->sloppyclockflag &= ~CLK_FLAG1;
7042950cc38Schristos 			acts_timeout(peer, S_IDLE);
7052950cc38Schristos 		}
7062950cc38Schristos 	} else {
7072950cc38Schristos 		up->timer--;
7082950cc38Schristos 		if (up->timer == 0)
7092950cc38Schristos 			acts_timeout(peer, up->state);
710abb0f93cSkardel 	}
711abb0f93cSkardel }
712abb0f93cSkardel 
713abb0f93cSkardel /*
714abb0f93cSkardel  * acts_timecode - identify the service and parse the timecode message
715abb0f93cSkardel  */
716abb0f93cSkardel void
717abb0f93cSkardel acts_timecode(
718abb0f93cSkardel 	struct peer *	peer,	/* peer structure pointer */
7192950cc38Schristos 	const char *	str	/* timecode string */
720abb0f93cSkardel 	)
721abb0f93cSkardel {
722abb0f93cSkardel 	struct actsunit *up;
723abb0f93cSkardel 	struct refclockproc *pp;
724abb0f93cSkardel 	int	day;		/* day of the month */
725abb0f93cSkardel 	int	month;		/* month of the year */
726abb0f93cSkardel 	u_long	mjd;		/* Modified Julian Day */
727abb0f93cSkardel 	double	dut1;		/* DUT adjustment */
728abb0f93cSkardel 
729abb0f93cSkardel 	u_int	dst;		/* ACTS daylight/standard time */
730abb0f93cSkardel 	u_int	leap;		/* ACTS leap indicator */
731abb0f93cSkardel 	double	msADV;		/* ACTS transmit advance (ms) */
732abb0f93cSkardel 	char	utc[10];	/* ACTS timescale */
733abb0f93cSkardel 	char	flag;		/* ACTS on-time character (* or #) */
734abb0f93cSkardel 
735abb0f93cSkardel 	char	synchar;	/* WWVB synchronized indicator */
736abb0f93cSkardel 	char	qualchar;	/* WWVB quality indicator */
737abb0f93cSkardel 	char	leapchar;	/* WWVB leap indicator */
738abb0f93cSkardel 	char	dstchar;	/* WWVB daylight/savings indicator */
739abb0f93cSkardel 	int	tz;		/* WWVB timezone */
740abb0f93cSkardel 
741e19314b7Schristos 	int	leapmonth;	/* PTB/NPL month of leap */
742abb0f93cSkardel 	char	leapdir;	/* PTB/NPL leap direction */
743abb0f93cSkardel 
744abb0f93cSkardel 	/*
745abb0f93cSkardel 	 * The parser selects the modem format based on the message
746abb0f93cSkardel 	 * length. Since the data are checked carefully, occasional
747abb0f93cSkardel 	 * errors due noise are forgivable.
748abb0f93cSkardel 	 */
749abb0f93cSkardel 	pp = peer->procptr;
7502950cc38Schristos 	up = pp->unitptr;
751abb0f93cSkardel 	pp->nsec = 0;
752abb0f93cSkardel 	switch (strlen(str)) {
753abb0f93cSkardel 
754abb0f93cSkardel 	/*
755abb0f93cSkardel 	 * For USNO format on-time character '*', which is on a line by
756abb0f93cSkardel 	 * itself. Be sure a timecode has been received.
757abb0f93cSkardel 	 */
758abb0f93cSkardel 	case 1:
759abb0f93cSkardel 		if (*str == '*' && up->msgcnt > 0)
760abb0f93cSkardel 			break;
761abb0f93cSkardel 
762abb0f93cSkardel 		return;
763abb0f93cSkardel 
764abb0f93cSkardel 	/*
7652950cc38Schristos 	 * ACTS format A: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
7662950cc38Schristos 	 * UTC(NIST) *".
767abb0f93cSkardel 	 */
768abb0f93cSkardel 	case LENACTS:
769abb0f93cSkardel 		if (sscanf(str,
770abb0f93cSkardel 		    "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
771abb0f93cSkardel 		    &mjd, &pp->year, &month, &day, &pp->hour,
772abb0f93cSkardel 		    &pp->minute, &pp->second, &dst, &leap, &dut1,
773abb0f93cSkardel 		    &msADV, utc, &flag) != 13) {
774abb0f93cSkardel 			refclock_report(peer, CEVNT_BADREPLY);
775abb0f93cSkardel 			return;
776abb0f93cSkardel 		}
777abb0f93cSkardel 		pp->day = ymd2yd(pp->year, month, day);
778abb0f93cSkardel 		pp->leap = LEAP_NOWARNING;
779abb0f93cSkardel 		if (leap == 1)
780abb0f93cSkardel 			pp->leap = LEAP_ADDSECOND;
7812950cc38Schristos 		else if (leap == 2)
782abb0f93cSkardel 			pp->leap = LEAP_DELSECOND;
783abb0f93cSkardel 		memcpy(&pp->refid, REFACTS, 4);
784abb0f93cSkardel 		up->msgcnt++;
7852950cc38Schristos 		if (flag != '#' && up->msgcnt < 10)
7862950cc38Schristos 			return;
7872950cc38Schristos 
788abb0f93cSkardel 		break;
789abb0f93cSkardel 
790abb0f93cSkardel 	/*
791abb0f93cSkardel 	 * USNO format: "jjjjj nnn hhmmss UTC"
792abb0f93cSkardel 	 */
793abb0f93cSkardel 	case LENUSNO:
794abb0f93cSkardel 		if (sscanf(str, "%5ld %3d %2d%2d%2d %3s",
795abb0f93cSkardel 		    &mjd, &pp->day, &pp->hour, &pp->minute,
796abb0f93cSkardel 		    &pp->second, utc) != 6) {
797abb0f93cSkardel 			refclock_report(peer, CEVNT_BADREPLY);
798abb0f93cSkardel 			return;
799abb0f93cSkardel 		}
800abb0f93cSkardel 
801abb0f93cSkardel 		/*
802abb0f93cSkardel 		 * Wait for the on-time character, which follows in a
803abb0f93cSkardel 		 * separate message. There is no provision for leap
804abb0f93cSkardel 		 * warning.
805abb0f93cSkardel 		 */
806abb0f93cSkardel 		pp->leap = LEAP_NOWARNING;
807abb0f93cSkardel 		memcpy(&pp->refid, REFUSNO, 4);
808abb0f93cSkardel 		up->msgcnt++;
8092950cc38Schristos 		break;
810abb0f93cSkardel 
811abb0f93cSkardel 	/*
812abb0f93cSkardel 	 * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ"
813abb0f93cSkardel 	 */
814abb0f93cSkardel 	case LENPTB:
815abb0f93cSkardel 		if (sscanf(str,
816abb0f93cSkardel 		    "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
817abb0f93cSkardel 		    &pp->second, &pp->year, &month, &day, &pp->hour,
818abb0f93cSkardel 		    &pp->minute, &mjd, &dut1, &leapdir, &leapmonth,
819abb0f93cSkardel 		    &msADV, &flag) != 12) {
820abb0f93cSkardel 			refclock_report(peer, CEVNT_BADREPLY);
821abb0f93cSkardel 			return;
822abb0f93cSkardel 		}
823abb0f93cSkardel 		pp->leap = LEAP_NOWARNING;
824abb0f93cSkardel 		if (leapmonth == month) {
825abb0f93cSkardel 			if (leapdir == '+')
826abb0f93cSkardel 				pp->leap = LEAP_ADDSECOND;
827abb0f93cSkardel 			else if (leapdir == '-')
828abb0f93cSkardel 				pp->leap = LEAP_DELSECOND;
829abb0f93cSkardel 		}
830abb0f93cSkardel 		pp->day = ymd2yd(pp->year, month, day);
831abb0f93cSkardel 		memcpy(&pp->refid, REFPTB, 4);
832abb0f93cSkardel 		up->msgcnt++;
833abb0f93cSkardel 		break;
834abb0f93cSkardel 
835abb0f93cSkardel 
836abb0f93cSkardel 	/*
837abb0f93cSkardel 	 * WWVB format 0: "I  ddd hh:mm:ss DTZ=nn"
838abb0f93cSkardel 	 */
839abb0f93cSkardel 	case LENWWVB0:
840abb0f93cSkardel 		if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d",
841abb0f93cSkardel 		    &synchar, &pp->day, &pp->hour, &pp->minute,
842abb0f93cSkardel 		    &pp->second, &dstchar, &tz) != 7) {
843abb0f93cSkardel 			refclock_report(peer, CEVNT_BADREPLY);
844abb0f93cSkardel 			return;
845abb0f93cSkardel 		}
846abb0f93cSkardel 		pp->leap = LEAP_NOWARNING;
847abb0f93cSkardel 		if (synchar != ' ')
848abb0f93cSkardel 			pp->leap = LEAP_NOTINSYNC;
849abb0f93cSkardel 		memcpy(&pp->refid, REFWWVB, 4);
850abb0f93cSkardel 		up->msgcnt++;
851abb0f93cSkardel 		break;
852abb0f93cSkardel 
853abb0f93cSkardel 	/*
854abb0f93cSkardel 	 * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
855abb0f93cSkardel 	 */
856abb0f93cSkardel 	case LENWWVB2:
857abb0f93cSkardel 		if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
858abb0f93cSkardel 		    &synchar, &qualchar, &pp->year, &pp->day,
859abb0f93cSkardel 		    &pp->hour, &pp->minute, &pp->second, &pp->nsec,
860abb0f93cSkardel 		    &dstchar, &leapchar, &dstchar) != 11) {
861abb0f93cSkardel 			refclock_report(peer, CEVNT_BADREPLY);
862abb0f93cSkardel 			return;
863abb0f93cSkardel 		}
864abb0f93cSkardel 		pp->nsec *= 1000000;
865abb0f93cSkardel 		pp->leap = LEAP_NOWARNING;
866abb0f93cSkardel 		if (synchar != ' ')
867abb0f93cSkardel 			pp->leap = LEAP_NOTINSYNC;
868abb0f93cSkardel 		else if (leapchar == 'L')
869abb0f93cSkardel 			pp->leap = LEAP_ADDSECOND;
870abb0f93cSkardel 		memcpy(&pp->refid, REFWWVB, 4);
871abb0f93cSkardel 		up->msgcnt++;
872abb0f93cSkardel 		break;
873abb0f93cSkardel 
874abb0f93cSkardel 	/*
875abb0f93cSkardel 	 * None of the above. Just forget about it and wait for the next
876abb0f93cSkardel 	 * message or timeout.
877abb0f93cSkardel 	 */
878abb0f93cSkardel 	default:
879abb0f93cSkardel 		return;
880abb0f93cSkardel 	}
881abb0f93cSkardel 
882abb0f93cSkardel 	/*
883abb0f93cSkardel 	 * We have a valid timecode. The fudge time1 value is added to
884abb0f93cSkardel 	 * each sample by the main line routines. Note that in current
885abb0f93cSkardel 	 * telephone networks the propatation time can be different for
886abb0f93cSkardel 	 * each call and can reach 200 ms for some calls.
887abb0f93cSkardel 	 */
888abb0f93cSkardel 	peer->refid = pp->refid;
889abb0f93cSkardel 	pp->lastrec = up->tstamp;
8902950cc38Schristos 	if (up->msgcnt == 0)
8912950cc38Schristos 		return;
8922950cc38Schristos 
8932950cc38Schristos 	strlcpy(pp->a_lastcode, str, sizeof(pp->a_lastcode));
8942950cc38Schristos 	pp->lencode = strlen(pp->a_lastcode);
895abb0f93cSkardel 	if (!refclock_process(pp)) {
896abb0f93cSkardel 		refclock_report(peer, CEVNT_BADTIME);
897abb0f93cSkardel 		return;
898abb0f93cSkardel 	}
899abb0f93cSkardel 	pp->lastref = pp->lastrec;
900abb0f93cSkardel }
901abb0f93cSkardel #else
902*eabc0478Schristos NONEMPTY_TRANSLATION_UNIT
903abb0f93cSkardel #endif /* REFCLOCK */
904