xref: /freebsd/contrib/ntp/ntpd/refclock_acts.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1c0b746e5SOllivier Robert /*
2ea906c41SOllivier Robert  * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
3ea906c41SOllivier Robert  *	Services
4c0b746e5SOllivier Robert  */
5c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
6c0b746e5SOllivier Robert #include <config.h>
7c0b746e5SOllivier Robert #endif
8c0b746e5SOllivier Robert 
92b15cb3dSCy Schubert #if defined(REFCLOCK) && defined(CLOCK_ACTS)
10c0b746e5SOllivier Robert 
11c0b746e5SOllivier Robert #include "ntpd.h"
12c0b746e5SOllivier Robert #include "ntp_io.h"
13c0b746e5SOllivier Robert #include "ntp_unixtime.h"
14c0b746e5SOllivier Robert #include "ntp_refclock.h"
15c0b746e5SOllivier Robert #include "ntp_stdlib.h"
16c0b746e5SOllivier Robert #include "ntp_control.h"
17c0b746e5SOllivier Robert 
18224ba2bdSOllivier Robert #include <stdio.h>
19224ba2bdSOllivier Robert #include <ctype.h>
20224ba2bdSOllivier Robert #ifdef HAVE_SYS_IOCTL_H
21224ba2bdSOllivier Robert # include <sys/ioctl.h>
22224ba2bdSOllivier Robert #endif /* HAVE_SYS_IOCTL_H */
23224ba2bdSOllivier Robert 
24c0b746e5SOllivier Robert /*
25ea906c41SOllivier Robert  * This driver supports the US (NIST, USNO) and European (PTB, NPL,
26ea906c41SOllivier Robert  * etc.) modem time services, as well as Spectracom GPS and WWVB
27ea906c41SOllivier Robert  * receivers connected via a modem. The driver periodically dials a
28ea906c41SOllivier Robert  * number from a telephone list, receives the timecode data and
29ea906c41SOllivier Robert  * calculates the local clock correction. It is designed primarily for
30ea906c41SOllivier Robert  * use as backup when neither a radio clock nor connectivity to Internet
31ea906c41SOllivier Robert  * time servers is available.
32c0b746e5SOllivier Robert  *
33ea906c41SOllivier Robert  * This driver requires a modem with a Hayes-compatible command set and
34ea906c41SOllivier Robert  * control over the modem data terminal ready (DTR) control line. The
35ea906c41SOllivier Robert  * modem setup string is hard-coded in the driver and may require
362b15cb3dSCy Schubert  * changes for nonstandard modems or special circumstances.
37c0b746e5SOllivier Robert  *
382b15cb3dSCy Schubert  * When enabled, the calling program dials the first number in the
392b15cb3dSCy Schubert  * phones file. If that call fails, it dials the second number and
402b15cb3dSCy Schubert  * so on. The phone number is specified by the Hayes ATDT prefix
412b15cb3dSCy Schubert  * followed by the number itself, including the long-distance prefix
422b15cb3dSCy Schubert  * and delay code, if necessary. The calling program is enabled
432b15cb3dSCy Schubert  * when (a) fudge flag1 is set by ntpdc, (b) at each poll interval
442b15cb3dSCy Schubert  * when no other synchronization sources are present, and (c) at each
452b15cb3dSCy Schubert  * poll interval whether or not other synchronization sources are
462b15cb3dSCy Schubert  * present. The calling program disconnects if (a) the called party
472b15cb3dSCy Schubert  * is busy or does not answer, (b) the called party disconnects
482b15cb3dSCy Schubert  * before a sufficient nuimber of timecodes have been received.
49c0b746e5SOllivier Robert  *
50ea906c41SOllivier Robert  * The driver is transparent to each of the modem time services and
51ea906c41SOllivier Robert  * Spectracom radios. It selects the parsing algorithm depending on the
52ea906c41SOllivier Robert  * message length. There is some hazard should the message be corrupted.
53ea906c41SOllivier Robert  * However, the data format is checked carefully and only if all checks
54ea906c41SOllivier Robert  * succeed is the message accepted. Corrupted lines are discarded
55ea906c41SOllivier Robert  * without complaint.
56c0b746e5SOllivier Robert  *
57ea906c41SOllivier Robert  * Fudge controls
58c0b746e5SOllivier Robert  *
59ea906c41SOllivier Robert  * flag1	force a call in manual mode
60ea906c41SOllivier Robert  * flag2	enable port locking (not verified)
612b15cb3dSCy Schubert  * flag3	not used
62ea906c41SOllivier Robert  * flag4	not used
63c0b746e5SOllivier Robert  *
64ea906c41SOllivier Robert  * time1	offset adjustment (s)
65c0b746e5SOllivier Robert  *
662b15cb3dSCy Schubert  * Ordinarily, the serial port is connected to a modem and the phones
672b15cb3dSCy Schubert  * list is defined. If no phones list is defined, the port can be
682b15cb3dSCy Schubert  * connected directly to a device or another computer. In this case the
692b15cb3dSCy Schubert  * driver will send a single character 'T' at each poll event. If
702b15cb3dSCy Schubert  * fudge flag2 is enabled, port locking allows the modem to be shared
712b15cb3dSCy Schubert  * when not in use by this driver.
72c0b746e5SOllivier Robert  */
73c0b746e5SOllivier Robert /*
74ea906c41SOllivier Robert  * National Institute of Science and Technology (NIST)
75c0b746e5SOllivier Robert  *
76ea906c41SOllivier Robert  * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
77ea906c41SOllivier Robert  *
78ea906c41SOllivier Robert  * Data Format
79c0b746e5SOllivier Robert  *
80c0b746e5SOllivier Robert  * National Institute of Standards and Technology
81c0b746e5SOllivier Robert  * Telephone Time Service, Generator 3B
82c0b746e5SOllivier Robert  * Enter question mark "?" for HELP
83c0b746e5SOllivier Robert  *                         D  L D
84c0b746e5SOllivier Robert  *  MJD  YR MO DA H  M  S  ST S UT1 msADV        <OTM>
85ea906c41SOllivier Robert  * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF>
86ea906c41SOllivier Robert  * ...
87c0b746e5SOllivier Robert  *
88ea906c41SOllivier Robert  * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is
89ea906c41SOllivier Robert  * the on-time markers echoed by the driver and used by NIST to measure
902b15cb3dSCy Schubert  * and correct for the propagation delay. Note: the ACTS timecode has
912b15cb3dSCy Schubert  * recently been changed to eliminate the * on-time indicator. The
922b15cb3dSCy Schubert  * reason for this and the long term implications are not clear.
93c0b746e5SOllivier Robert  *
94ea906c41SOllivier Robert  * US Naval Observatory (USNO)
95c0b746e5SOllivier Robert  *
96ea906c41SOllivier Robert  * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
97c0b746e5SOllivier Robert  *
98ea906c41SOllivier Robert  * Data Format (two lines, repeating at one-second intervals)
99c0b746e5SOllivier Robert  *
100ea906c41SOllivier Robert  * jjjjj nnn hhmmss UTC<CR><LF>
101ea906c41SOllivier Robert  * *<CR><LF>
102c0b746e5SOllivier Robert  *
103ea906c41SOllivier Robert  * jjjjj	modified Julian day number (not used)
104ea906c41SOllivier Robert  * nnn		day of year
105ea906c41SOllivier Robert  * hhmmss	second of day
106ea906c41SOllivier Robert  * *		on-time marker for previous timecode
107ea906c41SOllivier Robert  * ...
108c0b746e5SOllivier Robert  *
109ea906c41SOllivier Robert  * USNO does not correct for the propagation delay. A fudge time1 of
110ea906c41SOllivier Robert  * about .06 s is advisable.
111c0b746e5SOllivier Robert  *
112ea906c41SOllivier Robert  * European Services (PTB, NPL, etc.)
113c0b746e5SOllivier Robert  *
114ea906c41SOllivier Robert  * PTB: +49 531 512038 (Germany)
115ea906c41SOllivier Robert  * NPL: 0906 851 6333 (UK only)
116c0b746e5SOllivier Robert  *
117ea906c41SOllivier Robert  * Data format (see the documentation for phone numbers and formats.)
118c0b746e5SOllivier Robert  *
119ea906c41SOllivier Robert  * 1995-01-23 20:58:51 MEZ  10402303260219950123195849740+40000500<CR><LF>
120c0b746e5SOllivier Robert  *
121ea906c41SOllivier Robert  * Spectracom GPS and WWVB Receivers
122c0b746e5SOllivier Robert  *
123ea906c41SOllivier Robert  * If a modem is connected to a Spectracom receiver, this driver will
124ea906c41SOllivier Robert  * call it up and retrieve the time in one of two formats. As this
125ea906c41SOllivier Robert  * driver does not send anything, the radio will have to either be
126ea906c41SOllivier Robert  * configured in continuous mode or be polled by another local driver.
127c0b746e5SOllivier Robert  */
128c0b746e5SOllivier Robert /*
129c0b746e5SOllivier Robert  * Interface definitions
130c0b746e5SOllivier Robert  */
131c0b746e5SOllivier Robert #define	DEVICE		"/dev/acts%d" /* device name and unit */
1322b15cb3dSCy Schubert #define	SPEED232	B19200	/* uart speed (19200 bps) */
133ea906c41SOllivier Robert #define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
1342b15cb3dSCy Schubert #define LOCKFILE	"/var/spool/lock/LCK..cua%d"
135ea906c41SOllivier Robert #define DESCRIPTION	"Automated Computer Time Service" /* WRU */
136ea906c41SOllivier Robert #define REFID		"NONE"	/* default reference ID */
137ea906c41SOllivier Robert #define MSGCNT		20	/* max message count */
1382b15cb3dSCy Schubert #define	MAXPHONE	10	/* max number of phone numbers */
139ea906c41SOllivier Robert 
140ea906c41SOllivier Robert /*
1412b15cb3dSCy Schubert  * Calling program modes (mode)
142ea906c41SOllivier Robert  */
1432b15cb3dSCy Schubert #define MODE_BACKUP	0	/* backup mode */
1442b15cb3dSCy Schubert #define MODE_AUTO	1	/* automatic mode */
145c0b746e5SOllivier Robert #define MODE_MANUAL	2	/* manual mode */
146c0b746e5SOllivier Robert 
147ea906c41SOllivier Robert /*
1482b15cb3dSCy Schubert  * Service identifiers (message length)
149ea906c41SOllivier Robert  */
150ea906c41SOllivier Robert #define REFACTS		"NIST"	/* NIST reference ID */
1512b15cb3dSCy Schubert #define LENACTS		50	/* NIST format A */
152ea906c41SOllivier Robert #define REFUSNO		"USNO"	/* USNO reference ID */
153ea906c41SOllivier Robert #define LENUSNO		20	/* USNO */
154ea906c41SOllivier Robert #define REFPTB		"PTB\0"	/* PTB/NPL reference ID */
155ea906c41SOllivier Robert #define LENPTB		78	/* PTB/NPL format */
156ea906c41SOllivier Robert #define REFWWVB		"WWVB"	/* WWVB reference ID */
157ea906c41SOllivier Robert #define	LENWWVB0	22	/* WWVB format 0 */
158ea906c41SOllivier Robert #define	LENWWVB2	24	/* WWVB format 2 */
159ea906c41SOllivier Robert #define LF		0x0a	/* ASCII LF */
160c0b746e5SOllivier Robert 
161c0b746e5SOllivier Robert /*
1622b15cb3dSCy Schubert  * Modem setup strings. These may have to be changed for
1632b15cb3dSCy Schubert  * some modems.
164c0b746e5SOllivier Robert  *
165c0b746e5SOllivier Robert  * AT	command prefix
166ea906c41SOllivier Robert  * B1	US answer tone
167ea906c41SOllivier Robert  * &C0	disable carrier detect
168c0b746e5SOllivier Robert  * &D2	hang up and return to command mode on DTR transition
169c0b746e5SOllivier Robert  * E0	modem command echo disabled
1702b15cb3dSCy Schubert  * L1	set modem speaker volume to low level
171ea906c41SOllivier Robert  * M1	speaker enabled until carrier detect
172c0b746e5SOllivier Robert  * Q0	return result codes
173c0b746e5SOllivier Robert  * V1	return result codes as English words
1742b15cb3dSCy Schubert  * Y1	enable long-space disconnect
175c0b746e5SOllivier Robert  */
1762b15cb3dSCy Schubert const char def_modem_setup[] = "ATB1&C0&D2E0L1M1Q0V1Y1";
1772b15cb3dSCy Schubert const char *modem_setup = def_modem_setup;
178c0b746e5SOllivier Robert 
179c0b746e5SOllivier Robert /*
180ea906c41SOllivier Robert  * Timeouts (all in seconds)
181c0b746e5SOllivier Robert  */
182ea906c41SOllivier Robert #define SETUP		3	/* setup timeout */
1832b15cb3dSCy Schubert #define	REDIAL		30	/* redial timeout */
184ea906c41SOllivier Robert #define ANSWER		60	/* answer timeout */
1852b15cb3dSCy Schubert #define TIMECODE	60	/* message timeout */
1862b15cb3dSCy Schubert #define	MAXCODE		20	/* max timecodes */
187c0b746e5SOllivier Robert 
188c0b746e5SOllivier Robert /*
189ea906c41SOllivier Robert  * State machine codes
190c0b746e5SOllivier Robert  */
1912b15cb3dSCy Schubert typedef enum {
1922b15cb3dSCy Schubert 	S_IDLE,			/* wait for poll */
1932b15cb3dSCy Schubert 	S_SETUP,		/* send modem setup */
1942b15cb3dSCy Schubert 	S_CONNECT,		/* wait for answer */
1952b15cb3dSCy Schubert 	S_MSG			/* wait for timecode */
1962b15cb3dSCy Schubert } teModemState;
197c0b746e5SOllivier Robert 
198c0b746e5SOllivier Robert /*
199c0b746e5SOllivier Robert  * Unit control structure
200c0b746e5SOllivier Robert  */
201c0b746e5SOllivier Robert struct actsunit {
202ea906c41SOllivier Robert 	int	unit;		/* unit number */
203c0b746e5SOllivier Robert 	int	state;		/* the first one was Delaware */
204ea906c41SOllivier Robert 	int	timer;		/* timeout counter */
205ea906c41SOllivier Robert 	int	retry;		/* retry index */
206ea906c41SOllivier Robert 	int	msgcnt;		/* count of messages received */
207ea906c41SOllivier Robert 	l_fp	tstamp;		/* on-time timestamp */
2082b15cb3dSCy Schubert 	char	*bufptr;	/* next incoming char stored here */
2092b15cb3dSCy Schubert 	char	buf[BMAX];	/* bufptr roams within buf[] */
210c0b746e5SOllivier Robert };
211c0b746e5SOllivier Robert 
212c0b746e5SOllivier Robert /*
213c0b746e5SOllivier Robert  * Function prototypes
214c0b746e5SOllivier Robert  */
2152b15cb3dSCy Schubert static	int	acts_start	(int, struct peer *);
2162b15cb3dSCy Schubert static	void	acts_shutdown	(int, struct peer *);
2172b15cb3dSCy Schubert static	void	acts_receive	(struct recvbuf *);
2182b15cb3dSCy Schubert static	void	acts_message	(struct peer *, const char *);
2192b15cb3dSCy Schubert static	void	acts_timecode	(struct peer *, const char *);
2202b15cb3dSCy Schubert static	void	acts_poll	(int, struct peer *);
2212b15cb3dSCy Schubert static	void	acts_timeout	(struct peer *, teModemState);
2222b15cb3dSCy Schubert static	void	acts_timer	(int, struct peer *);
2232b15cb3dSCy Schubert static	void	acts_close	(struct peer *);
224c0b746e5SOllivier Robert 
225c0b746e5SOllivier Robert /*
226c0b746e5SOllivier Robert  * Transfer vector (conditional structure name)
227c0b746e5SOllivier Robert  */
228ea906c41SOllivier Robert struct refclock refclock_acts = {
229c0b746e5SOllivier Robert 	acts_start,		/* start up driver */
230c0b746e5SOllivier Robert 	acts_shutdown,		/* shut down driver */
231c0b746e5SOllivier Robert 	acts_poll,		/* transmit poll message */
232ea906c41SOllivier Robert 	noentry,		/* not used */
233ea906c41SOllivier Robert 	noentry,		/* not used */
234ea906c41SOllivier Robert 	noentry,		/* not used */
235ea906c41SOllivier Robert 	acts_timer		/* housekeeping timer */
236c0b746e5SOllivier Robert };
237c0b746e5SOllivier Robert 
238c0b746e5SOllivier Robert /*
239ea906c41SOllivier Robert  * Initialize data for processing
240c0b746e5SOllivier Robert  */
241c0b746e5SOllivier Robert static int
242c0b746e5SOllivier Robert acts_start(
243c0b746e5SOllivier Robert 	int	unit,
244c0b746e5SOllivier Robert 	struct peer *peer
245c0b746e5SOllivier Robert 	)
246c0b746e5SOllivier Robert {
247ea906c41SOllivier Robert 	struct actsunit *up;
248c0b746e5SOllivier Robert 	struct refclockproc *pp;
2492b15cb3dSCy Schubert 	const char *setup;
250c0b746e5SOllivier Robert 
251c0b746e5SOllivier Robert 	/*
252c0b746e5SOllivier Robert 	 * Allocate and initialize unit structure
253c0b746e5SOllivier Robert 	 */
2542b15cb3dSCy Schubert 	up = emalloc_zero(sizeof(struct actsunit));
255ea906c41SOllivier Robert 	up->unit = unit;
256c0b746e5SOllivier Robert 	pp = peer->procptr;
2572b15cb3dSCy Schubert 	pp->unitptr = up;
258c0b746e5SOllivier Robert 	pp->io.clock_recv = acts_receive;
2592b15cb3dSCy Schubert 	pp->io.srcclock = peer;
260c0b746e5SOllivier Robert 	pp->io.datalen = 0;
2612b15cb3dSCy Schubert 	pp->io.fd = -1;
262c0b746e5SOllivier Robert 
263c0b746e5SOllivier Robert 	/*
264c0b746e5SOllivier Robert 	 * Initialize miscellaneous variables
265c0b746e5SOllivier Robert 	 */
266c0b746e5SOllivier Robert 	peer->precision = PRECISION;
267c0b746e5SOllivier Robert 	pp->clockdesc = DESCRIPTION;
2682b15cb3dSCy Schubert 	memcpy(&pp->refid, REFID, 4);
269c0b746e5SOllivier Robert 	peer->sstclktype = CTL_SST_TS_TELEPHONE;
2702b15cb3dSCy Schubert 	up->bufptr = up->buf;
2712b15cb3dSCy Schubert 	if (def_modem_setup == modem_setup) {
2722b15cb3dSCy Schubert 		setup = get_ext_sys_var("modemsetup");
2732b15cb3dSCy Schubert 		if (setup != NULL)
2742b15cb3dSCy Schubert 			modem_setup = estrdup(setup);
2752b15cb3dSCy Schubert 	}
2762b15cb3dSCy Schubert 
277c0b746e5SOllivier Robert 	return (1);
278c0b746e5SOllivier Robert }
279c0b746e5SOllivier Robert 
280c0b746e5SOllivier Robert 
281c0b746e5SOllivier Robert /*
282c0b746e5SOllivier Robert  * acts_shutdown - shut down the clock
283c0b746e5SOllivier Robert  */
284c0b746e5SOllivier Robert static void
285c0b746e5SOllivier Robert acts_shutdown(
286c0b746e5SOllivier Robert 	int	unit,
287c0b746e5SOllivier Robert 	struct peer *peer
288c0b746e5SOllivier Robert 	)
289c0b746e5SOllivier Robert {
290ea906c41SOllivier Robert 	struct actsunit *up;
291c0b746e5SOllivier Robert 	struct refclockproc *pp;
292c0b746e5SOllivier Robert 
293ea906c41SOllivier Robert 	/*
294ea906c41SOllivier Robert 	 * Warning: do this only when a call is not in progress.
295ea906c41SOllivier Robert 	 */
296c0b746e5SOllivier Robert 	pp = peer->procptr;
2972b15cb3dSCy Schubert 	up = pp->unitptr;
2982b15cb3dSCy Schubert 	acts_close(peer);
299c0b746e5SOllivier Robert 	free(up);
300c0b746e5SOllivier Robert }
301c0b746e5SOllivier Robert 
302c0b746e5SOllivier Robert 
303c0b746e5SOllivier Robert /*
304c0b746e5SOllivier Robert  * acts_receive - receive data from the serial interface
305c0b746e5SOllivier Robert  */
306c0b746e5SOllivier Robert static void
307c0b746e5SOllivier Robert acts_receive(
308c0b746e5SOllivier Robert 	struct recvbuf *rbufp
309c0b746e5SOllivier Robert 	)
310c0b746e5SOllivier Robert {
311ea906c41SOllivier Robert 	struct actsunit *up;
312c0b746e5SOllivier Robert 	struct refclockproc *pp;
313c0b746e5SOllivier Robert 	struct peer *peer;
3142b15cb3dSCy Schubert 	char	tbuf[sizeof(up->buf)];
315ea906c41SOllivier Robert 	char *	tptr;
3162b15cb3dSCy Schubert 	int	octets;
317ea906c41SOllivier Robert 
318c0b746e5SOllivier Robert 	/*
319ea906c41SOllivier Robert 	 * Initialize pointers and read the timecode and timestamp. Note
320ea906c41SOllivier Robert 	 * we are in raw mode and victim of whatever the terminal
321ea906c41SOllivier Robert 	 * interface kicks up; so, we have to reassemble messages from
322ea906c41SOllivier Robert 	 * arbitrary fragments. Capture the timecode at the beginning of
323ea906c41SOllivier Robert 	 * the message and at the '*' and '#' on-time characters.
324c0b746e5SOllivier Robert 	 */
3252b15cb3dSCy Schubert 	peer = rbufp->recv_peer;
326c0b746e5SOllivier Robert 	pp = peer->procptr;
3272b15cb3dSCy Schubert 	up = pp->unitptr;
3282b15cb3dSCy Schubert 	octets = sizeof(up->buf) - (up->bufptr - up->buf);
3292b15cb3dSCy Schubert 	refclock_gtraw(rbufp, tbuf, octets, &pp->lastrec);
330ea906c41SOllivier Robert 	for (tptr = tbuf; *tptr != '\0'; tptr++) {
331ea906c41SOllivier Robert 		if (*tptr == LF) {
3322b15cb3dSCy Schubert 			if (up->bufptr == up->buf) {
333ea906c41SOllivier Robert 				up->tstamp = pp->lastrec;
334ea906c41SOllivier Robert 				continue;
335ea906c41SOllivier Robert 			} else {
336ea906c41SOllivier Robert 				*up->bufptr = '\0';
3372b15cb3dSCy Schubert 				up->bufptr = up->buf;
3382b15cb3dSCy Schubert 				acts_message(peer, up->buf);
339c0b746e5SOllivier Robert 			}
3402b15cb3dSCy Schubert 		} else if (!iscntrl((unsigned char)*tptr)) {
341ea906c41SOllivier Robert 			*up->bufptr++ = *tptr;
342ea906c41SOllivier Robert 			if (*tptr == '*' || *tptr == '#') {
343ea906c41SOllivier Robert 				up->tstamp = pp->lastrec;
344a466cc55SCy Schubert 				refclock_write(peer, tptr, 1, "data");
345ea906c41SOllivier Robert 			}
346ea906c41SOllivier Robert 		}
347ea906c41SOllivier Robert 	}
348ea906c41SOllivier Robert }
349ea906c41SOllivier Robert 
350ea906c41SOllivier Robert 
351ea906c41SOllivier Robert /*
352ea906c41SOllivier Robert  * acts_message - process message
353ea906c41SOllivier Robert  */
354ea906c41SOllivier Robert void
355ea906c41SOllivier Robert acts_message(
3562b15cb3dSCy Schubert 	struct peer *peer,
3572b15cb3dSCy Schubert 	const char *msg
3582b15cb3dSCy Schubert 	)
3592b15cb3dSCy Schubert {
3602b15cb3dSCy Schubert 	struct actsunit *up;
3612b15cb3dSCy Schubert 	struct refclockproc *pp;
3622b15cb3dSCy Schubert 	char	tbuf[BMAX];
3632b15cb3dSCy Schubert 	int		dtr = TIOCM_DTR;
3642b15cb3dSCy Schubert 
3652b15cb3dSCy Schubert 	DPRINTF(1, ("acts: %d %s\n", (int)strlen(msg), msg));
3662b15cb3dSCy Schubert 
3672b15cb3dSCy Schubert 	/*
3682b15cb3dSCy Schubert 	 * What to do depends on the state and the first token in the
3692b15cb3dSCy Schubert 	 * message.
3702b15cb3dSCy Schubert 	 */
3712b15cb3dSCy Schubert 	pp = peer->procptr;
3722b15cb3dSCy Schubert 	up = pp->unitptr;
3732b15cb3dSCy Schubert 
3742b15cb3dSCy Schubert 	/*
3752b15cb3dSCy Schubert 	 * Extract the first token in the line.
3762b15cb3dSCy Schubert 	 */
3772b15cb3dSCy Schubert 	strlcpy(tbuf, msg, sizeof(tbuf));
3782b15cb3dSCy Schubert 	strtok(tbuf, " ");
3792b15cb3dSCy Schubert 	switch (up->state) {
3802b15cb3dSCy Schubert 
3812b15cb3dSCy Schubert 	/*
3822b15cb3dSCy Schubert 	 * We are waiting for the OK response to the modem setup
3832b15cb3dSCy Schubert 	 * command. When this happens, dial the number followed.
3842b15cb3dSCy Schubert 	 * If anything other than OK is received, just ignore it
3852b15cb3dSCy Schubert 	 * and wait for timeoue.
3862b15cb3dSCy Schubert 	 */
3872b15cb3dSCy Schubert 	case S_SETUP:
3882b15cb3dSCy Schubert 		if (strcmp(tbuf, "OK") != 0) {
3892b15cb3dSCy Schubert 			/*
3902b15cb3dSCy Schubert 			 * We disable echo with MODEM_SETUP's E0 but
3912b15cb3dSCy Schubert 			 * if the modem was previously E1, we will
3922b15cb3dSCy Schubert 			 * see MODEM_SETUP echoed before the OK/ERROR.
3932b15cb3dSCy Schubert 			 * Ignore it.
3942b15cb3dSCy Schubert 			 */
3952b15cb3dSCy Schubert 			if (!strcmp(tbuf, modem_setup))
3962b15cb3dSCy Schubert 				return;
3972b15cb3dSCy Schubert 			break;
3982b15cb3dSCy Schubert 		}
3992b15cb3dSCy Schubert 
4002b15cb3dSCy Schubert 		mprintf_event(PEVNT_CLOCK, peer, "DIAL #%d %s",
4012b15cb3dSCy Schubert 			      up->retry, sys_phone[up->retry]);
4022b15cb3dSCy Schubert 		if (ioctl(pp->io.fd, TIOCMBIS, &dtr) < 0)
4032b15cb3dSCy Schubert 			msyslog(LOG_ERR, "acts: ioctl(TIOCMBIS) failed: %m");
404a466cc55SCy Schubert 		refclock_write(peer, sys_phone[up->retry],
405a466cc55SCy Schubert 			       strlen(sys_phone[up->retry]),
406a466cc55SCy Schubert 			       "DIAL");
407a466cc55SCy Schubert 		refclock_write(peer, "\r", 1, "CR");
4082b15cb3dSCy Schubert 		up->retry++;
4092b15cb3dSCy Schubert 		up->state = S_CONNECT;
4102b15cb3dSCy Schubert 		up->timer = ANSWER;
4112b15cb3dSCy Schubert 		return;
4122b15cb3dSCy Schubert 
4132b15cb3dSCy Schubert 	/*
4142b15cb3dSCy Schubert 	 * We are waiting for the CONNECT response to the dial
4152b15cb3dSCy Schubert 	 * command. When this happens, listen for timecodes. If
4162b15cb3dSCy Schubert 	 * somthing other than CONNECT is received, like BUSY
4172b15cb3dSCy Schubert 	 * or NO CARRIER, abort the call.
4182b15cb3dSCy Schubert 	 */
4192b15cb3dSCy Schubert 	case S_CONNECT:
4202b15cb3dSCy Schubert 		if (strcmp(tbuf, "CONNECT") != 0)
4212b15cb3dSCy Schubert 			break;
4222b15cb3dSCy Schubert 
4232b15cb3dSCy Schubert 		report_event(PEVNT_CLOCK, peer, msg);
4242b15cb3dSCy Schubert 		up->state = S_MSG;
4252b15cb3dSCy Schubert 		up->timer = TIMECODE;
4262b15cb3dSCy Schubert 		return;
4272b15cb3dSCy Schubert 
4282b15cb3dSCy Schubert 	/*
4292b15cb3dSCy Schubert 	 * We are waiting for a timecode response. Pass it to
4302b15cb3dSCy Schubert 	 * the parser. If NO CARRIER is received, save the
4312b15cb3dSCy Schubert 	 * messages and abort the call.
4322b15cb3dSCy Schubert 	 */
4332b15cb3dSCy Schubert 	case S_MSG:
4342b15cb3dSCy Schubert 		if (strcmp(tbuf, "NO") == 0)
4352b15cb3dSCy Schubert 			report_event(PEVNT_CLOCK, peer, msg);
4362b15cb3dSCy Schubert 		if (up->msgcnt < MAXCODE)
4372b15cb3dSCy Schubert 			acts_timecode(peer, msg);
4382b15cb3dSCy Schubert 		else
4392b15cb3dSCy Schubert 			acts_timeout(peer, S_MSG);
4402b15cb3dSCy Schubert 		return;
4412b15cb3dSCy Schubert 	}
4422b15cb3dSCy Schubert 
4432b15cb3dSCy Schubert 	/*
4442b15cb3dSCy Schubert 	 * Other response. Tell us about it.
4452b15cb3dSCy Schubert 	 */
4462b15cb3dSCy Schubert 	report_event(PEVNT_CLOCK, peer, msg);
4472b15cb3dSCy Schubert 	acts_close(peer);
4482b15cb3dSCy Schubert }
4492b15cb3dSCy Schubert 
4502b15cb3dSCy Schubert 
4512b15cb3dSCy Schubert /*
4522b15cb3dSCy Schubert  * acts_timeout - called on timeout
4532b15cb3dSCy Schubert  */
4542b15cb3dSCy Schubert static void
4552b15cb3dSCy Schubert acts_timeout(
4562b15cb3dSCy Schubert 	struct peer *peer,
4572b15cb3dSCy Schubert 	teModemState	dstate
4582b15cb3dSCy Schubert 	)
4592b15cb3dSCy Schubert {
4602b15cb3dSCy Schubert 	struct actsunit *up;
4612b15cb3dSCy Schubert 	struct refclockproc *pp;
4622b15cb3dSCy Schubert 	int	fd;
4632b15cb3dSCy Schubert 	char	device[20];
4642b15cb3dSCy Schubert 	char	lockfile[128], pidbuf[8];
4652b15cb3dSCy Schubert 
4662b15cb3dSCy Schubert 	/*
4672b15cb3dSCy Schubert 	 * The state machine is driven by messages from the modem,
4682b15cb3dSCy Schubert 	 * when first started and at timeout.
4692b15cb3dSCy Schubert 	 */
4702b15cb3dSCy Schubert 	pp = peer->procptr;
4712b15cb3dSCy Schubert 	up = pp->unitptr;
4722b15cb3dSCy Schubert 	switch (dstate) {
4732b15cb3dSCy Schubert 
4742b15cb3dSCy Schubert 	/*
4752b15cb3dSCy Schubert 	 * System poll event. Lock the modem port, open the device
4762b15cb3dSCy Schubert 	 * and send the setup command.
4772b15cb3dSCy Schubert 	 */
4782b15cb3dSCy Schubert 	case S_IDLE:
4792b15cb3dSCy Schubert 		if (-1 != pp->io.fd)
4802b15cb3dSCy Schubert 			return;		/* port is already open */
4812b15cb3dSCy Schubert 
4822b15cb3dSCy Schubert 		/*
4832b15cb3dSCy Schubert 		 * Lock the modem port. If busy, retry later. Note: if
4842b15cb3dSCy Schubert 		 * something fails between here and the close, the lock
4852b15cb3dSCy Schubert 		 * file may not be removed.
4862b15cb3dSCy Schubert 		 */
4872b15cb3dSCy Schubert 		if (pp->sloppyclockflag & CLK_FLAG2) {
4882b15cb3dSCy Schubert 			snprintf(lockfile, sizeof(lockfile), LOCKFILE,
4892b15cb3dSCy Schubert 			    up->unit);
4902b15cb3dSCy Schubert 			fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
4912b15cb3dSCy Schubert 			    0644);
4922b15cb3dSCy Schubert 			if (fd < 0) {
4932b15cb3dSCy Schubert 				report_event(PEVNT_CLOCK, peer, "acts: port busy");
4942b15cb3dSCy Schubert 				return;
4952b15cb3dSCy Schubert 			}
4962b15cb3dSCy Schubert 			snprintf(pidbuf, sizeof(pidbuf), "%d\n",
4972b15cb3dSCy Schubert 			    (u_int)getpid());
4982b15cb3dSCy Schubert 			if (write(fd, pidbuf, strlen(pidbuf)) < 0)
4992b15cb3dSCy Schubert 				msyslog(LOG_ERR, "acts: write lock fails %m");
5002b15cb3dSCy Schubert 			close(fd);
5012b15cb3dSCy Schubert 		}
5022b15cb3dSCy Schubert 
5032b15cb3dSCy Schubert 		/*
5042b15cb3dSCy Schubert 		 * Open the device in raw mode and link the I/O.
5052b15cb3dSCy Schubert 		 */
5062b15cb3dSCy Schubert 		snprintf(device, sizeof(device), DEVICE,
5072b15cb3dSCy Schubert 		    up->unit);
508a466cc55SCy Schubert 		fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_ACTS |
5092b15cb3dSCy Schubert 		    LDISC_RAW | LDISC_REMOTE);
5102b15cb3dSCy Schubert 		if (fd < 0) {
5112b15cb3dSCy Schubert 			msyslog(LOG_ERR, "acts: open fails %m");
5122b15cb3dSCy Schubert 			return;
5132b15cb3dSCy Schubert 		}
5142b15cb3dSCy Schubert 		pp->io.fd = fd;
5152b15cb3dSCy Schubert 		if (!io_addclock(&pp->io)) {
5162b15cb3dSCy Schubert 			msyslog(LOG_ERR, "acts: addclock fails");
5172b15cb3dSCy Schubert 			close(fd);
5182b15cb3dSCy Schubert 			pp->io.fd = -1;
5192b15cb3dSCy Schubert 			return;
5202b15cb3dSCy Schubert 		}
5212b15cb3dSCy Schubert 		up->msgcnt = 0;
5222b15cb3dSCy Schubert 		up->bufptr = up->buf;
5232b15cb3dSCy Schubert 
5242b15cb3dSCy Schubert 		/*
5252b15cb3dSCy Schubert 		 * If the port is directly connected to the device, skip
5262b15cb3dSCy Schubert 		 * the modem business and send 'T' for Spectrabum.
5272b15cb3dSCy Schubert 		 */
5282b15cb3dSCy Schubert 		if (sys_phone[up->retry] == NULL) {
529a466cc55SCy Schubert 			refclock_write(peer, "T", 1, "T");
5302b15cb3dSCy Schubert 			up->state = S_MSG;
5312b15cb3dSCy Schubert 			up->timer = TIMECODE;
5322b15cb3dSCy Schubert 			return;
5332b15cb3dSCy Schubert 		}
5342b15cb3dSCy Schubert 
5352b15cb3dSCy Schubert 		/*
5362b15cb3dSCy Schubert 		 * Initialize the modem. This works with Hayes-
5372b15cb3dSCy Schubert 		 * compatible modems.
5382b15cb3dSCy Schubert 		 */
5392b15cb3dSCy Schubert 		mprintf_event(PEVNT_CLOCK, peer, "SETUP %s",
5402b15cb3dSCy Schubert 			      modem_setup);
541a466cc55SCy Schubert 		refclock_write(peer, modem_setup, strlen(modem_setup),
542a466cc55SCy Schubert 			       "SETUP");
543a466cc55SCy Schubert 		refclock_write(peer, "\r", 1, "CR");
5442b15cb3dSCy Schubert 		up->state = S_SETUP;
5452b15cb3dSCy Schubert 		up->timer = SETUP;
5462b15cb3dSCy Schubert 		return;
5472b15cb3dSCy Schubert 
5482b15cb3dSCy Schubert 	/*
5492b15cb3dSCy Schubert 	 * In SETUP state the modem did not respond OK to setup string.
5502b15cb3dSCy Schubert 	 */
5512b15cb3dSCy Schubert 	case S_SETUP:
5522b15cb3dSCy Schubert 		report_event(PEVNT_CLOCK, peer, "no modem");
5532b15cb3dSCy Schubert 		break;
5542b15cb3dSCy Schubert 
5552b15cb3dSCy Schubert 	/*
5562b15cb3dSCy Schubert 	 * In CONNECT state the call did not complete. Abort the call.
5572b15cb3dSCy Schubert 	 */
5582b15cb3dSCy Schubert 	case S_CONNECT:
5592b15cb3dSCy Schubert 		report_event(PEVNT_CLOCK, peer, "no answer");
5602b15cb3dSCy Schubert 		break;
5612b15cb3dSCy Schubert 
5622b15cb3dSCy Schubert 	/*
5632b15cb3dSCy Schubert 	 * In MSG states no further timecodes are expected. If any
5642b15cb3dSCy Schubert 	 * timecodes have arrived, update the clock. In any case,
5652b15cb3dSCy Schubert 	 * terminate the call.
5662b15cb3dSCy Schubert 	 */
5672b15cb3dSCy Schubert 	case S_MSG:
5682b15cb3dSCy Schubert 		if (up->msgcnt == 0) {
5692b15cb3dSCy Schubert 			report_event(PEVNT_CLOCK, peer, "no timecodes");
5702b15cb3dSCy Schubert 		} else {
5712b15cb3dSCy Schubert 			pp->lastref = pp->lastrec;
5722b15cb3dSCy Schubert 			record_clock_stats(&peer->srcadr, pp->a_lastcode);
5732b15cb3dSCy Schubert 			refclock_receive(peer);
5742b15cb3dSCy Schubert 		}
5752b15cb3dSCy Schubert 		break;
5762b15cb3dSCy Schubert 	}
5772b15cb3dSCy Schubert 	acts_close(peer);
5782b15cb3dSCy Schubert }
5792b15cb3dSCy Schubert 
5802b15cb3dSCy Schubert 
5812b15cb3dSCy Schubert /*
5822b15cb3dSCy Schubert  * acts_close - close and prepare for next call.
5832b15cb3dSCy Schubert  *
5842b15cb3dSCy Schubert  * In ClOSE state no further protocol actions are required
5852b15cb3dSCy Schubert  * other than to close and release the device and prepare to
5862b15cb3dSCy Schubert  * dial the next number if necessary.
5872b15cb3dSCy Schubert  */
5882b15cb3dSCy Schubert void
5892b15cb3dSCy Schubert acts_close(
590ea906c41SOllivier Robert 	struct peer *peer
591ea906c41SOllivier Robert 	)
592ea906c41SOllivier Robert {
593ea906c41SOllivier Robert 	struct actsunit *up;
594ea906c41SOllivier Robert 	struct refclockproc *pp;
5952b15cb3dSCy Schubert 	char	lockfile[128];
5962b15cb3dSCy Schubert 	int	dtr;
5972b15cb3dSCy Schubert 
5982b15cb3dSCy Schubert 	pp = peer->procptr;
5992b15cb3dSCy Schubert 	up = pp->unitptr;
6002b15cb3dSCy Schubert 	if (pp->io.fd != -1) {
6012b15cb3dSCy Schubert 		report_event(PEVNT_CLOCK, peer, "close");
6022b15cb3dSCy Schubert 		dtr = TIOCM_DTR;
6032b15cb3dSCy Schubert 		if (ioctl(pp->io.fd, TIOCMBIC, &dtr) < 0)
6042b15cb3dSCy Schubert 			msyslog(LOG_ERR, "acts: ioctl(TIOCMBIC) failed: %m");
6052b15cb3dSCy Schubert 		io_closeclock(&pp->io);
6062b15cb3dSCy Schubert 		pp->io.fd = -1;
6072b15cb3dSCy Schubert 	}
6082b15cb3dSCy Schubert 	if (pp->sloppyclockflag & CLK_FLAG2) {
6092b15cb3dSCy Schubert 		snprintf(lockfile, sizeof(lockfile),
6102b15cb3dSCy Schubert 		    LOCKFILE, up->unit);
6112b15cb3dSCy Schubert 		unlink(lockfile);
6122b15cb3dSCy Schubert 	}
6132b15cb3dSCy Schubert 	if (up->msgcnt == 0 && up->retry > 0) {
6142b15cb3dSCy Schubert 		if (sys_phone[up->retry] != NULL) {
6152b15cb3dSCy Schubert 			up->state = S_IDLE;
6162b15cb3dSCy Schubert 			up->timer = REDIAL;
6172b15cb3dSCy Schubert 			return;
6182b15cb3dSCy Schubert 		}
6192b15cb3dSCy Schubert 	}
6202b15cb3dSCy Schubert 	up->state = S_IDLE;
6212b15cb3dSCy Schubert 	up->timer = 0;
6222b15cb3dSCy Schubert }
6232b15cb3dSCy Schubert 
624c0b746e5SOllivier Robert 
625ea906c41SOllivier Robert /*
6262b15cb3dSCy Schubert  * acts_poll - called by the transmit routine
6272b15cb3dSCy Schubert  */
6282b15cb3dSCy Schubert static void
6292b15cb3dSCy Schubert acts_poll(
6302b15cb3dSCy Schubert 	int	unit,
6312b15cb3dSCy Schubert 	struct peer *peer
6322b15cb3dSCy Schubert 	)
6332b15cb3dSCy Schubert {
6342b15cb3dSCy Schubert 	struct actsunit *up;
6352b15cb3dSCy Schubert 	struct refclockproc *pp;
6362b15cb3dSCy Schubert 
6372b15cb3dSCy Schubert 	/*
6382b15cb3dSCy Schubert 	 * This routine is called at every system poll. All it does is
6392b15cb3dSCy Schubert 	 * set flag1 under certain conditions. The real work is done by
6402b15cb3dSCy Schubert 	 * the timeout routine and state machine.
641ea906c41SOllivier Robert 	 */
642ea906c41SOllivier Robert 	pp = peer->procptr;
6432b15cb3dSCy Schubert 	up = pp->unitptr;
6442b15cb3dSCy Schubert 	switch (peer->ttl) {
645c0b746e5SOllivier Robert 
646c0b746e5SOllivier Robert 	/*
6472b15cb3dSCy Schubert 	 * In manual mode the calling program is activated by the ntpdc
6482b15cb3dSCy Schubert 	 * program using the enable flag (fudge flag1), either manually
6492b15cb3dSCy Schubert 	 * or by a cron job.
650c0b746e5SOllivier Robert 	 */
6512b15cb3dSCy Schubert 	case MODE_MANUAL:
652c0b746e5SOllivier Robert 		return;
653c0b746e5SOllivier Robert 
654ea906c41SOllivier Robert 	/*
6552b15cb3dSCy Schubert 	 * In automatic mode the calling program runs continuously at
6562b15cb3dSCy Schubert 	 * intervals determined by the poll event or specified timeout.
657ea906c41SOllivier Robert 	 */
6582b15cb3dSCy Schubert 	case MODE_AUTO:
659ea906c41SOllivier Robert 		break;
6602b15cb3dSCy Schubert 
6612b15cb3dSCy Schubert 	/*
6622b15cb3dSCy Schubert 	 * In backup mode the calling program runs continuously as long
6632b15cb3dSCy Schubert 	 * as either no peers are available or this peer is selected.
6642b15cb3dSCy Schubert 	 */
6652b15cb3dSCy Schubert 	case MODE_BACKUP:
6662b15cb3dSCy Schubert 		if (!(sys_peer == NULL || sys_peer == peer))
6672b15cb3dSCy Schubert 			return;
6682b15cb3dSCy Schubert 
6692b15cb3dSCy Schubert 		break;
6702b15cb3dSCy Schubert 	}
6712b15cb3dSCy Schubert 	pp->polls++;
6722b15cb3dSCy Schubert 	if (S_IDLE == up->state) {
6732b15cb3dSCy Schubert 		up->retry = 0;
6742b15cb3dSCy Schubert 		acts_timeout(peer, S_IDLE);
6752b15cb3dSCy Schubert 	}
6762b15cb3dSCy Schubert }
6772b15cb3dSCy Schubert 
6782b15cb3dSCy Schubert 
6792b15cb3dSCy Schubert /*
6802b15cb3dSCy Schubert  * acts_timer - called at one-second intervals
6812b15cb3dSCy Schubert  */
6822b15cb3dSCy Schubert static void
6832b15cb3dSCy Schubert acts_timer(
6842b15cb3dSCy Schubert 	int	unit,
6852b15cb3dSCy Schubert 	struct peer *peer
6862b15cb3dSCy Schubert 	)
6872b15cb3dSCy Schubert {
6882b15cb3dSCy Schubert 	struct actsunit *up;
6892b15cb3dSCy Schubert 	struct refclockproc *pp;
6902b15cb3dSCy Schubert 
6912b15cb3dSCy Schubert 	/*
6922b15cb3dSCy Schubert 	 * This routine implments a timeout which runs for a programmed
6932b15cb3dSCy Schubert 	 * interval. The counter is initialized by the state machine and
6942b15cb3dSCy Schubert 	 * counts down to zero. Upon reaching zero, the state machine is
6952b15cb3dSCy Schubert 	 * called. If flag1 is set while timer is zero, force a call.
6962b15cb3dSCy Schubert 	 */
6972b15cb3dSCy Schubert 	pp = peer->procptr;
6982b15cb3dSCy Schubert 	up = pp->unitptr;
6992b15cb3dSCy Schubert 	if (up->timer == 0) {
7002b15cb3dSCy Schubert 		if (pp->sloppyclockflag & CLK_FLAG1) {
7012b15cb3dSCy Schubert 			pp->sloppyclockflag &= ~CLK_FLAG1;
7022b15cb3dSCy Schubert 			acts_timeout(peer, S_IDLE);
7032b15cb3dSCy Schubert 		}
7042b15cb3dSCy Schubert 	} else {
7052b15cb3dSCy Schubert 		up->timer--;
7062b15cb3dSCy Schubert 		if (up->timer == 0)
7072b15cb3dSCy Schubert 			acts_timeout(peer, up->state);
708ea906c41SOllivier Robert 	}
709ea906c41SOllivier Robert }
710ea906c41SOllivier Robert 
711ea906c41SOllivier Robert /*
712ea906c41SOllivier Robert  * acts_timecode - identify the service and parse the timecode message
713ea906c41SOllivier Robert  */
714ea906c41SOllivier Robert void
715ea906c41SOllivier Robert acts_timecode(
716ea906c41SOllivier Robert 	struct peer *	peer,	/* peer structure pointer */
7172b15cb3dSCy Schubert 	const char *	str	/* timecode string */
718ea906c41SOllivier Robert 	)
719ea906c41SOllivier Robert {
720ea906c41SOllivier Robert 	struct actsunit *up;
721ea906c41SOllivier Robert 	struct refclockproc *pp;
722ea906c41SOllivier Robert 	int	day;		/* day of the month */
723ea906c41SOllivier Robert 	int	month;		/* month of the year */
724ea906c41SOllivier Robert 	u_long	mjd;		/* Modified Julian Day */
725ea906c41SOllivier Robert 	double	dut1;		/* DUT adjustment */
726ea906c41SOllivier Robert 
727ea906c41SOllivier Robert 	u_int	dst;		/* ACTS daylight/standard time */
728ea906c41SOllivier Robert 	u_int	leap;		/* ACTS leap indicator */
729ea906c41SOllivier Robert 	double	msADV;		/* ACTS transmit advance (ms) */
730ea906c41SOllivier Robert 	char	utc[10];	/* ACTS timescale */
731ea906c41SOllivier Robert 	char	flag;		/* ACTS on-time character (* or #) */
732ea906c41SOllivier Robert 
733ea906c41SOllivier Robert 	char	synchar;	/* WWVB synchronized indicator */
734ea906c41SOllivier Robert 	char	qualchar;	/* WWVB quality indicator */
735ea906c41SOllivier Robert 	char	leapchar;	/* WWVB leap indicator */
736ea906c41SOllivier Robert 	char	dstchar;	/* WWVB daylight/savings indicator */
737ea906c41SOllivier Robert 	int	tz;		/* WWVB timezone */
738ea906c41SOllivier Robert 
7392b15cb3dSCy Schubert 	int	leapmonth;	/* PTB/NPL month of leap */
740ea906c41SOllivier Robert 	char	leapdir;	/* PTB/NPL leap direction */
741ea906c41SOllivier Robert 
742ea906c41SOllivier Robert 	/*
743ea906c41SOllivier Robert 	 * The parser selects the modem format based on the message
744ea906c41SOllivier Robert 	 * length. Since the data are checked carefully, occasional
745ea906c41SOllivier Robert 	 * errors due noise are forgivable.
746ea906c41SOllivier Robert 	 */
747ea906c41SOllivier Robert 	pp = peer->procptr;
7482b15cb3dSCy Schubert 	up = pp->unitptr;
749ea906c41SOllivier Robert 	pp->nsec = 0;
750ea906c41SOllivier Robert 	switch (strlen(str)) {
751ea906c41SOllivier Robert 
752ea906c41SOllivier Robert 	/*
753ea906c41SOllivier Robert 	 * For USNO format on-time character '*', which is on a line by
754ea906c41SOllivier Robert 	 * itself. Be sure a timecode has been received.
755ea906c41SOllivier Robert 	 */
756c0b746e5SOllivier Robert 	case 1:
757ea906c41SOllivier Robert 		if (*str == '*' && up->msgcnt > 0)
758ea906c41SOllivier Robert 			break;
759c0b746e5SOllivier Robert 
760c0b746e5SOllivier Robert 		return;
761c0b746e5SOllivier Robert 
762c0b746e5SOllivier Robert 	/*
7632b15cb3dSCy Schubert 	 * ACTS format A: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
7642b15cb3dSCy Schubert 	 * UTC(NIST) *".
765c0b746e5SOllivier Robert 	 */
766ea906c41SOllivier Robert 	case LENACTS:
767ea906c41SOllivier Robert 		if (sscanf(str,
768ea906c41SOllivier Robert 		    "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
769ea906c41SOllivier Robert 		    &mjd, &pp->year, &month, &day, &pp->hour,
770ea906c41SOllivier Robert 		    &pp->minute, &pp->second, &dst, &leap, &dut1,
771ea906c41SOllivier Robert 		    &msADV, utc, &flag) != 13) {
772c0b746e5SOllivier Robert 			refclock_report(peer, CEVNT_BADREPLY);
773c0b746e5SOllivier Robert 			return;
774c0b746e5SOllivier Robert 		}
775ea906c41SOllivier Robert 		pp->day = ymd2yd(pp->year, month, day);
776ea906c41SOllivier Robert 		pp->leap = LEAP_NOWARNING;
777c0b746e5SOllivier Robert 		if (leap == 1)
778c0b746e5SOllivier Robert 			pp->leap = LEAP_ADDSECOND;
7792b15cb3dSCy Schubert 		else if (leap == 2)
780c0b746e5SOllivier Robert 			pp->leap = LEAP_DELSECOND;
781ea906c41SOllivier Robert 		memcpy(&pp->refid, REFACTS, 4);
782ea906c41SOllivier Robert 		up->msgcnt++;
7832b15cb3dSCy Schubert 		if (flag != '#' && up->msgcnt < 10)
7842b15cb3dSCy Schubert 			return;
7852b15cb3dSCy Schubert 
786ea906c41SOllivier Robert 		break;
787ea906c41SOllivier Robert 
788ea906c41SOllivier Robert 	/*
789ea906c41SOllivier Robert 	 * USNO format: "jjjjj nnn hhmmss UTC"
790ea906c41SOllivier Robert 	 */
791ea906c41SOllivier Robert 	case LENUSNO:
792ea906c41SOllivier Robert 		if (sscanf(str, "%5ld %3d %2d%2d%2d %3s",
793ea906c41SOllivier Robert 		    &mjd, &pp->day, &pp->hour, &pp->minute,
794ea906c41SOllivier Robert 		    &pp->second, utc) != 6) {
795ea906c41SOllivier Robert 			refclock_report(peer, CEVNT_BADREPLY);
796ea906c41SOllivier Robert 			return;
797ea906c41SOllivier Robert 		}
798ea906c41SOllivier Robert 
799ea906c41SOllivier Robert 		/*
800ea906c41SOllivier Robert 		 * Wait for the on-time character, which follows in a
801ea906c41SOllivier Robert 		 * separate message. There is no provision for leap
802ea906c41SOllivier Robert 		 * warning.
803ea906c41SOllivier Robert 		 */
804ea906c41SOllivier Robert 		pp->leap = LEAP_NOWARNING;
805ea906c41SOllivier Robert 		memcpy(&pp->refid, REFUSNO, 4);
806ea906c41SOllivier Robert 		up->msgcnt++;
8072b15cb3dSCy Schubert 		break;
808ea906c41SOllivier Robert 
809ea906c41SOllivier Robert 	/*
810ea906c41SOllivier Robert 	 * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ"
811ea906c41SOllivier Robert 	 */
812ea906c41SOllivier Robert 	case LENPTB:
813ea906c41SOllivier Robert 		if (sscanf(str,
814ea906c41SOllivier Robert 		    "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
815ea906c41SOllivier Robert 		    &pp->second, &pp->year, &month, &day, &pp->hour,
816ea906c41SOllivier Robert 		    &pp->minute, &mjd, &dut1, &leapdir, &leapmonth,
817ea906c41SOllivier Robert 		    &msADV, &flag) != 12) {
818ea906c41SOllivier Robert 			refclock_report(peer, CEVNT_BADREPLY);
819ea906c41SOllivier Robert 			return;
820ea906c41SOllivier Robert 		}
821ea906c41SOllivier Robert 		pp->leap = LEAP_NOWARNING;
822c0b746e5SOllivier Robert 		if (leapmonth == month) {
823c0b746e5SOllivier Robert 			if (leapdir == '+')
824c0b746e5SOllivier Robert 				pp->leap = LEAP_ADDSECOND;
825c0b746e5SOllivier Robert 			else if (leapdir == '-')
826c0b746e5SOllivier Robert 				pp->leap = LEAP_DELSECOND;
827c0b746e5SOllivier Robert 		}
828ea906c41SOllivier Robert 		pp->day = ymd2yd(pp->year, month, day);
829ea906c41SOllivier Robert 		memcpy(&pp->refid, REFPTB, 4);
830ea906c41SOllivier Robert 		up->msgcnt++;
831ea906c41SOllivier Robert 		break;
832ea906c41SOllivier Robert 
833c0b746e5SOllivier Robert 
834c0b746e5SOllivier Robert 	/*
835ea906c41SOllivier Robert 	 * WWVB format 0: "I  ddd hh:mm:ss DTZ=nn"
836c0b746e5SOllivier Robert 	 */
837ea906c41SOllivier Robert 	case LENWWVB0:
838ea906c41SOllivier Robert 		if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d",
839ea906c41SOllivier Robert 		    &synchar, &pp->day, &pp->hour, &pp->minute,
840ea906c41SOllivier Robert 		    &pp->second, &dstchar, &tz) != 7) {
841ea906c41SOllivier Robert 			refclock_report(peer, CEVNT_BADREPLY);
842c0b746e5SOllivier Robert 			return;
843ea906c41SOllivier Robert 		}
844ea906c41SOllivier Robert 		pp->leap = LEAP_NOWARNING;
845ea906c41SOllivier Robert 		if (synchar != ' ')
846ea906c41SOllivier Robert 			pp->leap = LEAP_NOTINSYNC;
847ea906c41SOllivier Robert 		memcpy(&pp->refid, REFWWVB, 4);
848ea906c41SOllivier Robert 		up->msgcnt++;
849ea906c41SOllivier Robert 		break;
850ea906c41SOllivier Robert 
851ea906c41SOllivier Robert 	/*
852ea906c41SOllivier Robert 	 * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
853ea906c41SOllivier Robert 	 */
854ea906c41SOllivier Robert 	case LENWWVB2:
855ea906c41SOllivier Robert 		if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
856ea906c41SOllivier Robert 		    &synchar, &qualchar, &pp->year, &pp->day,
857ea906c41SOllivier Robert 		    &pp->hour, &pp->minute, &pp->second, &pp->nsec,
858ea906c41SOllivier Robert 		    &dstchar, &leapchar, &dstchar) != 11) {
859ea906c41SOllivier Robert 			refclock_report(peer, CEVNT_BADREPLY);
860ea906c41SOllivier Robert 			return;
861ea906c41SOllivier Robert 		}
862ea906c41SOllivier Robert 		pp->nsec *= 1000000;
863ea906c41SOllivier Robert 		pp->leap = LEAP_NOWARNING;
864ea906c41SOllivier Robert 		if (synchar != ' ')
865ea906c41SOllivier Robert 			pp->leap = LEAP_NOTINSYNC;
866ea906c41SOllivier Robert 		else if (leapchar == 'L')
867ea906c41SOllivier Robert 			pp->leap = LEAP_ADDSECOND;
868ea906c41SOllivier Robert 		memcpy(&pp->refid, REFWWVB, 4);
869ea906c41SOllivier Robert 		up->msgcnt++;
870ea906c41SOllivier Robert 		break;
871ea906c41SOllivier Robert 
872ea906c41SOllivier Robert 	/*
873ea906c41SOllivier Robert 	 * None of the above. Just forget about it and wait for the next
874ea906c41SOllivier Robert 	 * message or timeout.
875ea906c41SOllivier Robert 	 */
876ea906c41SOllivier Robert 	default:
877c0b746e5SOllivier Robert 		return;
878c0b746e5SOllivier Robert 	}
879c0b746e5SOllivier Robert 
880c0b746e5SOllivier Robert 	/*
881ea906c41SOllivier Robert 	 * We have a valid timecode. The fudge time1 value is added to
882ea906c41SOllivier Robert 	 * each sample by the main line routines. Note that in current
883ea906c41SOllivier Robert 	 * telephone networks the propatation time can be different for
884ea906c41SOllivier Robert 	 * each call and can reach 200 ms for some calls.
885c0b746e5SOllivier Robert 	 */
886ea906c41SOllivier Robert 	peer->refid = pp->refid;
887ea906c41SOllivier Robert 	pp->lastrec = up->tstamp;
8882b15cb3dSCy Schubert 	if (up->msgcnt == 0)
8892b15cb3dSCy Schubert 		return;
8902b15cb3dSCy Schubert 
8912b15cb3dSCy Schubert 	strlcpy(pp->a_lastcode, str, sizeof(pp->a_lastcode));
8922b15cb3dSCy Schubert 	pp->lencode = strlen(pp->a_lastcode);
893ea906c41SOllivier Robert 	if (!refclock_process(pp)) {
894ea906c41SOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
895ea906c41SOllivier Robert 		return;
896ea906c41SOllivier Robert 	}
8979c2daa00SOllivier Robert 	pp->lastref = pp->lastrec;
898ea906c41SOllivier Robert }
899c0b746e5SOllivier Robert #else
900*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT
901c0b746e5SOllivier Robert #endif /* REFCLOCK */
902