154359Sroberto/*
2182007Sroberto * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
3182007Sroberto *	Services
454359Sroberto */
554359Sroberto#ifdef HAVE_CONFIG_H
654359Sroberto#include <config.h>
754359Sroberto#endif
854359Sroberto
954359Sroberto#if defined(REFCLOCK) && (defined(CLOCK_ACTS) || defined(CLOCK_PTBACTS))
1054359Sroberto
1154359Sroberto#include "ntpd.h"
1254359Sroberto#include "ntp_io.h"
1354359Sroberto#include "ntp_unixtime.h"
1454359Sroberto#include "ntp_refclock.h"
1554359Sroberto#include "ntp_stdlib.h"
1654359Sroberto#include "ntp_control.h"
1754359Sroberto
1882498Sroberto#include <stdio.h>
1982498Sroberto#include <ctype.h>
2082498Sroberto#ifdef HAVE_SYS_IOCTL_H
2182498Sroberto# include <sys/ioctl.h>
2282498Sroberto#endif /* HAVE_SYS_IOCTL_H */
2382498Sroberto
2454359Sroberto/*
25182007Sroberto * This driver supports the US (NIST, USNO) and European (PTB, NPL,
26182007Sroberto * etc.) modem time services, as well as Spectracom GPS and WWVB
27182007Sroberto * receivers connected via a modem. The driver periodically dials a
28182007Sroberto * number from a telephone list, receives the timecode data and
29182007Sroberto * calculates the local clock correction. It is designed primarily for
30182007Sroberto * use as backup when neither a radio clock nor connectivity to Internet
31182007Sroberto * time servers is available.
3254359Sroberto *
33182007Sroberto * This driver requires a modem with a Hayes-compatible command set and
34182007Sroberto * control over the modem data terminal ready (DTR) control line. The
35182007Sroberto * modem setup string is hard-coded in the driver and may require
36182007Sroberto * changes for nonstandard modems or special circumstances. For reasons
37182007Sroberto * unrelated to this driver, the data set ready (DSR) control line
38182007Sroberto * should not be set when this driver is first started.
3954359Sroberto *
40182007Sroberto * The calling program is initiated by setting fudge flag1, either
41182007Sroberto * manually or automatically. When flag1 is set, the calling program
42182007Sroberto * dials the first number in the phone command of the configuration
43182007Sroberto * file. If that call fails, the calling program dials the second number
44182007Sroberto * and so on. The number is specified by the Hayes ATDT prefix followed
45182007Sroberto * by the number itself, including the prefix and long-distance digits
46182007Sroberto * and delay code, if necessary. The flag1 is reset and the calling
47182007Sroberto * program terminated if (a) a valid clock update has been determined,
48182007Sroberto * (b) no more numbers remain in the list, (c) a device fault or timeout
49182007Sroberto * occurs or (d) fudge flag1 is reset manually.
5054359Sroberto *
51182007Sroberto * The driver is transparent to each of the modem time services and
52182007Sroberto * Spectracom radios. It selects the parsing algorithm depending on the
53182007Sroberto * message length. There is some hazard should the message be corrupted.
54182007Sroberto * However, the data format is checked carefully and only if all checks
55182007Sroberto * succeed is the message accepted. Corrupted lines are discarded
56182007Sroberto * without complaint.
5754359Sroberto *
58182007Sroberto * Fudge controls
5954359Sroberto *
60182007Sroberto * flag1	force a call in manual mode
61182007Sroberto * flag2	enable port locking (not verified)
62182007Sroberto * flag3	no modem; port is directly connected to device
63182007Sroberto * flag4	not used
6454359Sroberto *
65182007Sroberto * time1	offset adjustment (s)
6654359Sroberto *
67182007Sroberto * Ordinarily, the serial port is connected to a modem; however, it can
68182007Sroberto * be connected directly to a device or another computer for testing and
69182007Sroberto * calibration. In this case set fudge flag3 and the driver will send a
70182007Sroberto * single character 'T' at each poll event. In principle, fudge flag2
71182007Sroberto * enables port locking, allowing the modem to be shared when not in use
72182007Sroberto * by this driver. At least on Solaris with the current NTP I/O
73182007Sroberto * routines, this results only in lots of ugly error messages.
7454359Sroberto */
7554359Sroberto/*
76182007Sroberto * National Institute of Science and Technology (NIST)
7754359Sroberto *
78182007Sroberto * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
7954359Sroberto *
80182007Sroberto * Data Format
81182007Sroberto *
8254359Sroberto * National Institute of Standards and Technology
8354359Sroberto * Telephone Time Service, Generator 3B
8454359Sroberto * Enter question mark "?" for HELP
8554359Sroberto *                         D  L D
8654359Sroberto *  MJD  YR MO DA H  M  S  ST S UT1 msADV        <OTM>
87182007Sroberto * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF>
88182007Sroberto * ...
8954359Sroberto *
90182007Sroberto * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is
91182007Sroberto * the on-time markers echoed by the driver and used by NIST to measure
92182007Sroberto * and correct for the propagation delay.
9354359Sroberto *
94182007Sroberto * US Naval Observatory (USNO)
9554359Sroberto *
96182007Sroberto * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
9754359Sroberto *
98182007Sroberto * Data Format (two lines, repeating at one-second intervals)
9954359Sroberto *
100182007Sroberto * jjjjj nnn hhmmss UTC<CR><LF>
101182007Sroberto * *<CR><LF>
10254359Sroberto *
103182007Sroberto * jjjjj	modified Julian day number (not used)
104182007Sroberto * nnn		day of year
105182007Sroberto * hhmmss	second of day
106182007Sroberto * *		on-time marker for previous timecode
107182007Sroberto * ...
10854359Sroberto *
109182007Sroberto * USNO does not correct for the propagation delay. A fudge time1 of
110182007Sroberto * about .06 s is advisable.
11154359Sroberto *
112182007Sroberto * European Services (PTB, NPL, etc.)
11354359Sroberto *
114182007Sroberto * PTB: +49 531 512038 (Germany)
115182007Sroberto * NPL: 0906 851 6333 (UK only)
11654359Sroberto *
117182007Sroberto * Data format (see the documentation for phone numbers and formats.)
11854359Sroberto *
119182007Sroberto * 1995-01-23 20:58:51 MEZ  10402303260219950123195849740+40000500<CR><LF>
12054359Sroberto *
121182007Sroberto * Spectracom GPS and WWVB Receivers
12254359Sroberto *
123182007Sroberto * If a modem is connected to a Spectracom receiver, this driver will
124182007Sroberto * call it up and retrieve the time in one of two formats. As this
125182007Sroberto * driver does not send anything, the radio will have to either be
126182007Sroberto * configured in continuous mode or be polled by another local driver.
12754359Sroberto */
12854359Sroberto/*
12954359Sroberto * Interface definitions
13054359Sroberto */
131182007Sroberto#define	DEVICE		"/dev/acts%d" /* device name and unit */
132182007Sroberto#define	SPEED232	B9600	/* uart speed (9600 baud) */
13354359Sroberto#define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
134182007Sroberto#define LOCKFILE	"/var/spool/locks/LCK..cua%d"
135182007Sroberto#define DESCRIPTION	"Automated Computer Time Service" /* WRU */
136182007Sroberto#define REFID		"NONE"	/* default reference ID */
137182007Sroberto#define MSGCNT		20	/* max message count */
138182007Sroberto#define SMAX		256	/* max clockstats line length */
139182007Sroberto
140182007Sroberto/*
141182007Sroberto * Calling program modes
142182007Sroberto */
14354359Sroberto#define MODE_AUTO	0	/* automatic mode */
14454359Sroberto#define MODE_BACKUP	1	/* backup mode */
14554359Sroberto#define MODE_MANUAL	2	/* manual mode */
14654359Sroberto
147182007Sroberto/*
148182007Sroberto * Service identifiers.
149182007Sroberto */
150182007Sroberto#define REFACTS		"NIST"	/* NIST reference ID */
151182007Sroberto#define LENACTS		50	/* NIST format */
152182007Sroberto#define REFUSNO		"USNO"	/* USNO reference ID */
153182007Sroberto#define LENUSNO		20	/* USNO */
154182007Sroberto#define REFPTB		"PTB\0"	/* PTB/NPL reference ID */
155182007Sroberto#define LENPTB		78	/* PTB/NPL format */
156182007Sroberto#define REFWWVB		"WWVB"	/* WWVB reference ID */
157182007Sroberto#define	LENWWVB0	22	/* WWVB format 0 */
158182007Sroberto#define	LENWWVB2	24	/* WWVB format 2 */
159182007Sroberto#define LF		0x0a	/* ASCII LF */
16054359Sroberto
16154359Sroberto/*
162182007Sroberto * Modem setup strings. These may have to be changed for some modems.
16354359Sroberto *
16454359Sroberto * AT	command prefix
165182007Sroberto * B1	US answer tone
166182007Sroberto * &C0	disable carrier detect
16754359Sroberto * &D2	hang up and return to command mode on DTR transition
16854359Sroberto * E0	modem command echo disabled
16954359Sroberto * l1	set modem speaker volume to low level
170182007Sroberto * M1	speaker enabled until carrier detect
17154359Sroberto * Q0	return result codes
17254359Sroberto * V1	return result codes as English words
17354359Sroberto */
174182007Sroberto#define MODEM_SETUP	"ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */
175182007Sroberto#define MODEM_HANGUP	"ATH\r"	/* modem disconnect */
17654359Sroberto
17754359Sroberto/*
178182007Sroberto * Timeouts (all in seconds)
17954359Sroberto */
180182007Sroberto#define SETUP		3	/* setup timeout */
181182007Sroberto#define	DTR		1	/* DTR timeout */
182182007Sroberto#define ANSWER		60	/* answer timeout */
183182007Sroberto#define CONNECT		20	/* first valid message timeout */
184182007Sroberto#define TIMECODE	30	/* all valid messages timeout */
18554359Sroberto
18654359Sroberto/*
187182007Sroberto * State machine codes
18854359Sroberto */
189182007Sroberto#define S_IDLE		0	/* wait for poll */
190182007Sroberto#define S_OK		1	/* wait for modem setup */
191182007Sroberto#define S_DTR		2	/* wait for modem DTR */
192182007Sroberto#define S_CONNECT	3	/* wait for answer*/
193182007Sroberto#define S_FIRST		4	/* wait for first valid message */
194182007Sroberto#define S_MSG		5	/* wait for all messages */
195182007Sroberto#define S_CLOSE		6	/* wait after sending disconnect */
19654359Sroberto
19754359Sroberto/*
19854359Sroberto * Unit control structure
19954359Sroberto */
20054359Srobertostruct actsunit {
201182007Sroberto	int	unit;		/* unit number */
20254359Sroberto	int	state;		/* the first one was Delaware */
203182007Sroberto	int	timer;		/* timeout counter */
204182007Sroberto	int	retry;		/* retry index */
205182007Sroberto	int	msgcnt;		/* count of messages received */
206182007Sroberto	l_fp	tstamp;		/* on-time timestamp */
207182007Sroberto	char	*bufptr;	/* buffer pointer */
20854359Sroberto};
20954359Sroberto
21054359Sroberto/*
21154359Sroberto * Function prototypes
21254359Sroberto */
21354359Srobertostatic	int	acts_start	P((int, struct peer *));
21454359Srobertostatic	void	acts_shutdown	P((int, struct peer *));
21554359Srobertostatic	void	acts_receive	P((struct recvbuf *));
216182007Srobertostatic	void	acts_message	P((struct peer *));
217182007Srobertostatic	void	acts_timecode	P((struct peer *, char *));
21854359Srobertostatic	void	acts_poll	P((int, struct peer *));
21954359Srobertostatic	void	acts_timeout	P((struct peer *));
22054359Srobertostatic	void	acts_disc	P((struct peer *));
221182007Srobertostatic	void	acts_timer	P((int, struct peer *));
22254359Sroberto
22354359Sroberto/*
22454359Sroberto * Transfer vector (conditional structure name)
22554359Sroberto */
226182007Srobertostruct	refclock refclock_acts = {
22754359Sroberto	acts_start,		/* start up driver */
22854359Sroberto	acts_shutdown,		/* shut down driver */
22954359Sroberto	acts_poll,		/* transmit poll message */
230182007Sroberto	noentry,		/* not used */
231182007Sroberto	noentry,		/* not used */
232182007Sroberto	noentry,		/* not used */
233182007Sroberto	acts_timer		/* housekeeping timer */
23454359Sroberto};
23554359Sroberto
236182007Srobertostruct	refclock refclock_ptb;
23754359Sroberto
23854359Sroberto/*
239182007Sroberto * Initialize data for processing
24054359Sroberto */
24154359Srobertostatic int
24254359Srobertoacts_start (
243182007Sroberto	int	unit,
24454359Sroberto	struct peer *peer
24554359Sroberto	)
24654359Sroberto{
247182007Sroberto	struct actsunit *up;
24854359Sroberto	struct refclockproc *pp;
24954359Sroberto
25054359Sroberto	/*
251182007Sroberto	 * Allocate and initialize unit structure
25254359Sroberto	 */
253182007Sroberto	up = emalloc(sizeof(struct actsunit));
254182007Sroberto	if (up == NULL)
25554359Sroberto		return (0);
25654359Sroberto
257182007Sroberto	memset(up, 0, sizeof(struct actsunit));
258182007Sroberto	up->unit = unit;
25954359Sroberto	pp = peer->procptr;
260182007Sroberto	pp->unitptr = (caddr_t)up;
26154359Sroberto	pp->io.clock_recv = acts_receive;
26254359Sroberto	pp->io.srcclock = (caddr_t)peer;
26354359Sroberto	pp->io.datalen = 0;
26454359Sroberto
26554359Sroberto	/*
26654359Sroberto	 * Initialize miscellaneous variables
26754359Sroberto	 */
26854359Sroberto	peer->precision = PRECISION;
26954359Sroberto	pp->clockdesc = DESCRIPTION;
27054359Sroberto	memcpy((char *)&pp->refid, REFID, 4);
27154359Sroberto	peer->sstclktype = CTL_SST_TS_TELEPHONE;
272182007Sroberto	peer->flags &= ~FLAG_FIXPOLL;
273182007Sroberto	up->bufptr = pp->a_lastcode;
27454359Sroberto	return (1);
27554359Sroberto}
27654359Sroberto
27754359Sroberto
27854359Sroberto/*
27954359Sroberto * acts_shutdown - shut down the clock
28054359Sroberto */
28154359Srobertostatic void
28254359Srobertoacts_shutdown (
283182007Sroberto	int	unit,
28454359Sroberto	struct peer *peer
28554359Sroberto	)
28654359Sroberto{
287182007Sroberto	struct actsunit *up;
28854359Sroberto	struct refclockproc *pp;
28954359Sroberto
290182007Sroberto	/*
291182007Sroberto	 * Warning: do this only when a call is not in progress.
292182007Sroberto	 */
29354359Sroberto	pp = peer->procptr;
29454359Sroberto	up = (struct actsunit *)pp->unitptr;
29554359Sroberto	free(up);
29654359Sroberto}
29754359Sroberto
29854359Sroberto
29954359Sroberto/*
30054359Sroberto * acts_receive - receive data from the serial interface
30154359Sroberto */
30254359Srobertostatic void
30354359Srobertoacts_receive (
30454359Sroberto	struct recvbuf *rbufp
30554359Sroberto	)
30654359Sroberto{
307182007Sroberto	struct actsunit *up;
30854359Sroberto	struct refclockproc *pp;
30954359Sroberto	struct peer *peer;
310182007Sroberto	char	tbuf[BMAX];
311182007Sroberto	char	*tptr;
312182007Sroberto
31354359Sroberto	/*
314182007Sroberto	 * Initialize pointers and read the timecode and timestamp. Note
315182007Sroberto	 * we are in raw mode and victim of whatever the terminal
316182007Sroberto	 * interface kicks up; so, we have to reassemble messages from
317182007Sroberto	 * arbitrary fragments. Capture the timecode at the beginning of
318182007Sroberto	 * the message and at the '*' and '#' on-time characters.
31954359Sroberto	 */
32054359Sroberto	peer = (struct peer *)rbufp->recv_srcclock;
32154359Sroberto	pp = peer->procptr;
32254359Sroberto	up = (struct actsunit *)pp->unitptr;
323182007Sroberto	pp->lencode = refclock_gtraw(rbufp, tbuf, BMAX - (up->bufptr -
324182007Sroberto	    pp->a_lastcode), &pp->lastrec);
325182007Sroberto	for (tptr = tbuf; *tptr != '\0'; tptr++) {
326182007Sroberto		if (*tptr == LF) {
327182007Sroberto			if (up->bufptr == pp->a_lastcode) {
328182007Sroberto				up->tstamp = pp->lastrec;
329182007Sroberto				continue;
330182007Sroberto
331182007Sroberto			} else {
332182007Sroberto				*up->bufptr = '\0';
333182007Sroberto				acts_message(peer);
334182007Sroberto				up->bufptr = pp->a_lastcode;
335182007Sroberto			}
336182007Sroberto		} else if (!iscntrl(*tptr)) {
337182007Sroberto			*up->bufptr++ = *tptr;
338182007Sroberto			if (*tptr == '*' || *tptr == '#') {
339182007Sroberto				up->tstamp = pp->lastrec;
340182007Sroberto				write(pp->io.fd, tptr, 1);
341182007Sroberto			}
342182007Sroberto		}
34354359Sroberto	}
344182007Sroberto}
345182007Sroberto
346182007Sroberto
347182007Sroberto/*
348182007Sroberto * acts_message - process message
349182007Sroberto */
350182007Srobertovoid
351182007Srobertoacts_message(
352182007Sroberto	struct peer *peer
353182007Sroberto	)
354182007Sroberto{
355182007Sroberto	struct actsunit *up;
356182007Sroberto	struct refclockproc *pp;
357182007Sroberto	int	dtr = TIOCM_DTR;
358182007Sroberto	char	tbuf[SMAX];
35954359Sroberto#ifdef DEBUG
360182007Sroberto	u_int	modem;
361182007Sroberto#endif
362182007Sroberto
363182007Sroberto	/*
364182007Sroberto	 * What to do depends on the state and the first token in the
365182007Sroberto	 * message. A NO token sends the message to the clockstats.
366182007Sroberto	 */
367182007Sroberto	pp = peer->procptr;
368182007Sroberto	up = (struct actsunit *)pp->unitptr;
369182007Sroberto#ifdef DEBUG
370182007Sroberto	ioctl(pp->io.fd, TIOCMGET, (char *)&modem);
371182007Sroberto	sprintf(tbuf, "acts: %04x (%d %d) %lu %s", modem, up->state,
372182007Sroberto	    up->timer, strlen(pp->a_lastcode), pp->a_lastcode);
37354359Sroberto	if (debug)
374182007Sroberto		printf("%s\n", tbuf);
37554359Sroberto#endif
376182007Sroberto	strncpy(tbuf, pp->a_lastcode, SMAX);
377182007Sroberto	strtok(tbuf, " ");
378182007Sroberto	if (strcmp(tbuf, "NO") == 0)
379182007Sroberto		record_clock_stats(&peer->srcadr, pp->a_lastcode);
380182007Sroberto	switch(up->state) {
38154359Sroberto
382182007Sroberto	/*
383182007Sroberto	 * We are waiting for the OK response to the modem setup
384182007Sroberto	 * command. When this happens, raise DTR and dial the number
385182007Sroberto	 * followed by \r.
386182007Sroberto	 */
387182007Sroberto	case S_OK:
388182007Sroberto		if (strcmp(tbuf, "OK") != 0) {
389182007Sroberto			msyslog(LOG_ERR, "acts: setup error %s",
390182007Sroberto			    pp->a_lastcode);
391182007Sroberto			acts_disc(peer);
392182007Sroberto			return;
393182007Sroberto		}
394182007Sroberto		ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr);
395182007Sroberto		up->state = S_DTR;
396182007Sroberto		up->timer = DTR;
39754359Sroberto		return;
39854359Sroberto
399182007Sroberto	/*
400182007Sroberto	 * We are waiting for the call to be answered. All we care about
401182007Sroberto	 * here is token CONNECT. Send the message to the clockstats.
402182007Sroberto	 */
403182007Sroberto	case S_CONNECT:
404182007Sroberto		record_clock_stats(&peer->srcadr, pp->a_lastcode);
405182007Sroberto		if (strcmp(tbuf, "CONNECT") != 0) {
40654359Sroberto			acts_disc(peer);
40754359Sroberto			return;
40854359Sroberto		}
409182007Sroberto		up->state = S_FIRST;
410182007Sroberto		up->timer = CONNECT;
41154359Sroberto		return;
41254359Sroberto
413182007Sroberto	/*
414182007Sroberto	 * We are waiting for a timecode. Pass it to the parser.
415182007Sroberto	 */
416182007Sroberto	case S_FIRST:
417182007Sroberto	case S_MSG:
418182007Sroberto		acts_timecode(peer, pp->a_lastcode);
419182007Sroberto		break;
42054359Sroberto	}
421182007Sroberto}
42254359Sroberto
423182007Sroberto/*
424182007Sroberto * acts_timecode - identify the service and parse the timecode message
425182007Sroberto */
426182007Srobertovoid
427182007Srobertoacts_timecode(
428182007Sroberto	struct peer *peer,	/* peer structure pointer */
429182007Sroberto	char	*str		/* timecode string */
430182007Sroberto	)
431182007Sroberto{
432182007Sroberto	struct actsunit *up;
433182007Sroberto	struct refclockproc *pp;
434182007Sroberto	int	day;		/* day of the month */
435182007Sroberto	int	month;		/* month of the year */
436182007Sroberto	u_long	mjd;		/* Modified Julian Day */
437182007Sroberto	double	dut1;		/* DUT adjustment */
438182007Sroberto
439182007Sroberto	u_int	dst;		/* ACTS daylight/standard time */
440182007Sroberto	u_int	leap;		/* ACTS leap indicator */
441182007Sroberto	double	msADV;		/* ACTS transmit advance (ms) */
442182007Sroberto	char	utc[10];	/* ACTS timescale */
443182007Sroberto	char	flag;		/* ACTS on-time character (* or #) */
444182007Sroberto
445182007Sroberto	char	synchar;	/* WWVB synchronized indicator */
446182007Sroberto	char	qualchar;	/* WWVB quality indicator */
447182007Sroberto	char	leapchar;	/* WWVB leap indicator */
448182007Sroberto	char	dstchar;	/* WWVB daylight/savings indicator */
449182007Sroberto	int	tz;		/* WWVB timezone */
450182007Sroberto
451182007Sroberto	u_int	leapmonth;	/* PTB/NPL month of leap */
452182007Sroberto	char	leapdir;	/* PTB/NPL leap direction */
453182007Sroberto
45454359Sroberto	/*
455182007Sroberto	 * The parser selects the modem format based on the message
456182007Sroberto	 * length. Since the data are checked carefully, occasional
457182007Sroberto	 * errors due noise are forgivable.
45854359Sroberto	 */
459182007Sroberto	pp = peer->procptr;
460182007Sroberto	up = (struct actsunit *)pp->unitptr;
461182007Sroberto	pp->nsec = 0;
462182007Sroberto	switch(strlen(str)) {
46354359Sroberto
46454359Sroberto	/*
465182007Sroberto	 * For USNO format on-time character '*', which is on a line by
466182007Sroberto	 * itself. Be sure a timecode has been received.
46754359Sroberto	 */
468182007Sroberto	case 1:
469182007Sroberto		if (*str == '*' && up->msgcnt > 0)
470182007Sroberto			break;
471182007Sroberto
47254359Sroberto		return;
473182007Sroberto
47454359Sroberto	/*
475182007Sroberto	 * ACTS format: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
476182007Sroberto	 * UTC(NIST) *"
47754359Sroberto	 */
478182007Sroberto	case LENACTS:
479182007Sroberto		if (sscanf(str,
480182007Sroberto		    "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
481182007Sroberto		    &mjd, &pp->year, &month, &day, &pp->hour,
482182007Sroberto		    &pp->minute, &pp->second, &dst, &leap, &dut1,
483182007Sroberto		    &msADV, utc, &flag) != 13) {
484182007Sroberto			refclock_report(peer, CEVNT_BADREPLY);
485182007Sroberto			return;
486182007Sroberto		}
487182007Sroberto
488182007Sroberto		/*
489182007Sroberto		 * Wait until ACTS has calculated the roundtrip delay.
490182007Sroberto		 * We don't need to do anything, as ACTS adjusts the
491182007Sroberto		 * on-time epoch.
492182007Sroberto		 */
493182007Sroberto		if (flag != '#')
494182007Sroberto			return;
495182007Sroberto
496182007Sroberto		pp->day = ymd2yd(pp->year, month, day);
497182007Sroberto		pp->leap = LEAP_NOWARNING;
498182007Sroberto		if (leap == 1)
499182007Sroberto	    		pp->leap = LEAP_ADDSECOND;
500182007Sroberto		else if (pp->leap == 2)
501182007Sroberto	    		pp->leap = LEAP_DELSECOND;
502182007Sroberto		memcpy(&pp->refid, REFACTS, 4);
503182007Sroberto		if (up->msgcnt == 0)
504182007Sroberto			record_clock_stats(&peer->srcadr, str);
505182007Sroberto		up->msgcnt++;
506182007Sroberto		break;
507182007Sroberto
50854359Sroberto	/*
509182007Sroberto	 * USNO format: "jjjjj nnn hhmmss UTC"
51054359Sroberto	 */
511182007Sroberto	case LENUSNO:
512182007Sroberto		if (sscanf(str, "%5ld %3d %2d%2d%2d %3s",
513182007Sroberto		    &mjd, &pp->day, &pp->hour, &pp->minute,
514182007Sroberto		    &pp->second, utc) != 6) {
515182007Sroberto			refclock_report(peer, CEVNT_BADREPLY);
516182007Sroberto			return;
517182007Sroberto		}
51854359Sroberto
519182007Sroberto		/*
520182007Sroberto		 * Wait for the on-time character, which follows in a
521182007Sroberto		 * separate message. There is no provision for leap
522182007Sroberto		 * warning.
523182007Sroberto		 */
524182007Sroberto		pp->leap = LEAP_NOWARNING;
525182007Sroberto		memcpy(&pp->refid, REFUSNO, 4);
526182007Sroberto		if (up->msgcnt == 0)
527182007Sroberto			record_clock_stats(&peer->srcadr, str);
528182007Sroberto		up->msgcnt++;
529182007Sroberto		return;
530182007Sroberto
53154359Sroberto	/*
532182007Sroberto	 * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ"
53354359Sroberto	 */
534182007Sroberto	case LENPTB:
535182007Sroberto		if (sscanf(str,
536182007Sroberto		    "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
537182007Sroberto		    &pp->second, &pp->year, &month, &day, &pp->hour,
538182007Sroberto		    &pp->minute, &mjd, &dut1, &leapdir, &leapmonth,
539182007Sroberto		    &msADV, &flag) != 12) {
540182007Sroberto			refclock_report(peer, CEVNT_BADREPLY);
541182007Sroberto			return;
542182007Sroberto		}
543182007Sroberto		pp->leap = LEAP_NOWARNING;
544182007Sroberto		if (leapmonth == month) {
545182007Sroberto			if (leapdir == '+')
546182007Sroberto		    		pp->leap = LEAP_ADDSECOND;
547182007Sroberto			else if (leapdir == '-')
548182007Sroberto		    		pp->leap = LEAP_DELSECOND;
549182007Sroberto		}
550182007Sroberto		pp->day = ymd2yd(pp->year, month, day);
551182007Sroberto		memcpy(&pp->refid, REFPTB, 4);
552182007Sroberto		if (up->msgcnt == 0)
553182007Sroberto			record_clock_stats(&peer->srcadr, str);
554182007Sroberto		up->msgcnt++;
555182007Sroberto		break;
55682498Sroberto
557182007Sroberto
55882498Sroberto	/*
559182007Sroberto	 * WWVB format 0: "I  ddd hh:mm:ss DTZ=nn"
56082498Sroberto	 */
561182007Sroberto	case LENWWVB0:
562182007Sroberto		if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d",
563182007Sroberto		    &synchar, &pp->day, &pp->hour, &pp->minute,
564182007Sroberto		    &pp->second, &dstchar, &tz) != 7) {
565182007Sroberto			refclock_report(peer, CEVNT_BADREPLY);
56654359Sroberto			return;
56754359Sroberto		}
568182007Sroberto		pp->leap = LEAP_NOWARNING;
569182007Sroberto		if (synchar != ' ')
570182007Sroberto			pp->leap = LEAP_NOTINSYNC;
571182007Sroberto		memcpy(&pp->refid, REFWWVB, 4);
572182007Sroberto		if (up->msgcnt == 0)
573182007Sroberto			record_clock_stats(&peer->srcadr, str);
574182007Sroberto		up->msgcnt++;
575182007Sroberto		break;
576182007Sroberto
577182007Sroberto	/*
578182007Sroberto	 * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
579182007Sroberto	 */
580182007Sroberto	case LENWWVB2:
581182007Sroberto		if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
582182007Sroberto		    &synchar, &qualchar, &pp->year, &pp->day,
583182007Sroberto		    &pp->hour, &pp->minute, &pp->second, &pp->nsec,
584182007Sroberto		    &dstchar, &leapchar, &dstchar) != 11) {
585182007Sroberto			refclock_report(peer, CEVNT_BADREPLY);
58654359Sroberto			return;
58754359Sroberto		}
588182007Sroberto		pp->nsec *= 1000000;
589182007Sroberto		pp->leap = LEAP_NOWARNING;
590182007Sroberto		if (synchar != ' ')
591182007Sroberto			pp->leap = LEAP_NOTINSYNC;
592182007Sroberto		else if (leapchar == 'L')
593182007Sroberto			pp->leap = LEAP_ADDSECOND;
594182007Sroberto		memcpy(&pp->refid, REFWWVB, 4);
595182007Sroberto		if (up->msgcnt == 0)
596182007Sroberto			record_clock_stats(&peer->srcadr, str);
597182007Sroberto		up->msgcnt++;
598182007Sroberto		break;
59954359Sroberto
60054359Sroberto	/*
601182007Sroberto	 * None of the above. Just forget about it and wait for the next
602182007Sroberto	 * message or timeout.
60354359Sroberto	 */
604182007Sroberto	default:
605182007Sroberto		return;
60654359Sroberto	}
60754359Sroberto
60854359Sroberto	/*
609182007Sroberto	 * We have a valid timecode. The fudge time1 value is added to
610182007Sroberto	 * each sample by the main line routines. Note that in current
611182007Sroberto	 * telephone networks the propatation time can be different for
612182007Sroberto	 * each call and can reach 200 ms for some calls.
61354359Sroberto	 */
614182007Sroberto	peer->refid = pp->refid;
615182007Sroberto	pp->lastrec = up->tstamp;
616182007Sroberto	if (!refclock_process(pp)) {
617182007Sroberto		refclock_report(peer, CEVNT_BADTIME);
618182007Sroberto		return;
619182007Sroberto			}
620132451Sroberto	pp->lastref = pp->lastrec;
621182007Sroberto	if (peer->disp > MAXDISTANCE)
622182007Sroberto		refclock_receive(peer);
623182007Sroberto	if (up->state != S_MSG) {
624182007Sroberto		up->state = S_MSG;
625182007Sroberto		up->timer = TIMECODE;
626182007Sroberto	}
62754359Sroberto}
62854359Sroberto
62954359Sroberto
63054359Sroberto/*
63154359Sroberto * acts_poll - called by the transmit routine
63254359Sroberto */
63354359Srobertostatic void
63454359Srobertoacts_poll (
635182007Sroberto	int	unit,
63654359Sroberto	struct peer *peer
63754359Sroberto	)
63854359Sroberto{
639182007Sroberto	struct actsunit *up;
64054359Sroberto	struct refclockproc *pp;
64154359Sroberto
64254359Sroberto	/*
643182007Sroberto	 * This routine is called at every system poll. All it does is
644182007Sroberto	 * set flag1 under certain conditions. The real work is done by
645182007Sroberto	 * the timeout routine and state machine.
64654359Sroberto	 */
64754359Sroberto	pp = peer->procptr;
64854359Sroberto	up = (struct actsunit *)pp->unitptr;
649182007Sroberto	switch (peer->ttl) {
65054359Sroberto
651182007Sroberto	/*
652182007Sroberto	 * In manual mode the calling program is activated by the ntpdc
653182007Sroberto	 * program using the enable flag (fudge flag1), either manually
654182007Sroberto	 * or by a cron job.
655182007Sroberto	 */
656182007Sroberto	case MODE_MANUAL:
657182007Sroberto		/* fall through */
658182007Sroberto		break;
659182007Sroberto
660182007Sroberto	/*
661182007Sroberto	 * In automatic mode the calling program runs continuously at
662182007Sroberto	 * intervals determined by the poll event or specified timeout.
663182007Sroberto	 */
664182007Sroberto	case MODE_AUTO:
66554359Sroberto		pp->sloppyclockflag |= CLK_FLAG1;
666182007Sroberto		break;
667182007Sroberto
668182007Sroberto	/*
669182007Sroberto	 * In backup mode the calling program runs continuously as long
670182007Sroberto	 * as either no peers are available or this peer is selected.
671182007Sroberto	 */
672182007Sroberto	case MODE_BACKUP:
673182007Sroberto		if (sys_peer == NULL || sys_peer == peer)
674182007Sroberto			pp->sloppyclockflag |= CLK_FLAG1;
675182007Sroberto		break;
67654359Sroberto	}
67754359Sroberto}
67854359Sroberto
67954359Sroberto
68054359Sroberto/*
681182007Sroberto * acts_timer - called at one-second intervals
68254359Sroberto */
68354359Srobertostatic void
684182007Srobertoacts_timer(
685182007Sroberto	int	unit,
68654359Sroberto	struct peer *peer
68754359Sroberto	)
68854359Sroberto{
689182007Sroberto	struct actsunit *up;
69054359Sroberto	struct refclockproc *pp;
69154359Sroberto
69254359Sroberto	/*
693182007Sroberto	 * This routine implments a timeout which runs for a programmed
694182007Sroberto	 * interval. The counter is initialized by the state machine and
695182007Sroberto	 * counts down to zero. Upon reaching zero, the state machine is
696182007Sroberto	 * called. If flag1 is set while in S_IDLE state, force a
697182007Sroberto	 * timeout.
69854359Sroberto	 */
69954359Sroberto	pp = peer->procptr;
70054359Sroberto	up = (struct actsunit *)pp->unitptr;
701182007Sroberto	if (pp->sloppyclockflag & CLK_FLAG1 && up->state == S_IDLE) {
702182007Sroberto		acts_timeout(peer);
70354359Sroberto		return;
70454359Sroberto	}
705182007Sroberto	if (up->timer == 0)
706182007Sroberto		return;
70754359Sroberto
708182007Sroberto	up->timer--;
709182007Sroberto	if (up->timer == 0)
710182007Sroberto		acts_timeout(peer);
711182007Sroberto}
712182007Sroberto
713182007Sroberto
714182007Sroberto/*
715182007Sroberto * acts_timeout - called on timeout
716182007Sroberto */
717182007Srobertostatic void
718182007Srobertoacts_timeout(
719182007Sroberto	struct peer *peer
720182007Sroberto	)
721182007Sroberto{
722182007Sroberto	struct actsunit *up;
723182007Sroberto	struct refclockproc *pp;
724182007Sroberto	int	fd;
725182007Sroberto	char	device[20];
726182007Sroberto	char	lockfile[128], pidbuf[8];
727182007Sroberto	char	tbuf[BMAX];
728182007Sroberto
729182007Sroberto	/*
730182007Sroberto	 * The state machine is driven by messages from the modem, when
731182007Sroberto	 * first stated and at timeout.
732182007Sroberto	 */
733182007Sroberto	pp = peer->procptr;
734182007Sroberto	up = (struct actsunit *)pp->unitptr;
735182007Sroberto	pp->sloppyclockflag &= ~CLK_FLAG1;
736182007Sroberto	if (sys_phone[up->retry] == NULL && !(pp->sloppyclockflag &
737182007Sroberto	    CLK_FLAG3)) {
738182007Sroberto		msyslog(LOG_ERR, "acts: no phones");
739182007Sroberto		return;
740182007Sroberto	}
741182007Sroberto	switch(up->state) {
742182007Sroberto
743182007Sroberto	/*
744182007Sroberto	 * System poll event. Lock the modem port and open the device.
745182007Sroberto	 */
746182007Sroberto	case S_IDLE:
747182007Sroberto
74854359Sroberto		/*
749182007Sroberto		 * Lock the modem port. If busy, retry later. Note: if
750182007Sroberto		 * something fails between here and the close, the lock
751182007Sroberto		 * file may not be removed.
75254359Sroberto		 */
753182007Sroberto		if (pp->sloppyclockflag & CLK_FLAG2) {
754182007Sroberto			sprintf(lockfile, LOCKFILE, up->unit);
755182007Sroberto			fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
756182007Sroberto			    0644);
757182007Sroberto			if (fd < 0) {
758182007Sroberto				msyslog(LOG_ERR, "acts: port busy");
759182007Sroberto				return;
760182007Sroberto			}
761182007Sroberto			sprintf(pidbuf, "%d\n", (u_int)getpid());
762182007Sroberto			write(fd, pidbuf, strlen(pidbuf));
763182007Sroberto			close(fd);
764182007Sroberto		}
76554359Sroberto
76654359Sroberto		/*
767182007Sroberto		 * Open the device in raw mode and link the I/O.
76854359Sroberto		 */
769182007Sroberto		if (!pp->io.fd) {
770182007Sroberto			sprintf(device, DEVICE, up->unit);
771182007Sroberto			fd = refclock_open(device, SPEED232,
772182007Sroberto			    LDISC_ACTS | LDISC_RAW | LDISC_REMOTE);
773182007Sroberto			if (fd == 0) {
774182007Sroberto				return;
775182007Sroberto			}
776182007Sroberto			pp->io.fd = fd;
777182007Sroberto			if (!io_addclock(&pp->io)) {
778182007Sroberto				msyslog(LOG_ERR,
779182007Sroberto				    "acts: addclock fails");
780182007Sroberto				close(fd);
781182007Sroberto				pp->io.fd = 0;
782182007Sroberto				return;
783182007Sroberto			}
784182007Sroberto		}
78554359Sroberto
78654359Sroberto		/*
787182007Sroberto		 * If the port is directly connected to the device, skip
788182007Sroberto		 * the modem business and send 'T' for Spectrabum.
78954359Sroberto		 */
790182007Sroberto		if (pp->sloppyclockflag & CLK_FLAG3) {
791182007Sroberto			if (write(pp->io.fd, "T", 1) < 0) {
792182007Sroberto				msyslog(LOG_ERR, "acts: write %m");
793182007Sroberto				return;
79454359Sroberto			}
795182007Sroberto			up->state = S_FIRST;
796182007Sroberto			up->timer = CONNECT;
797182007Sroberto			return;
79854359Sroberto		}
79954359Sroberto
800182007Sroberto		/*
801182007Sroberto		 * Initialize the modem. This works with Hayes commands.
802182007Sroberto		 */
803182007Sroberto#ifdef DEBUG
804182007Sroberto		if (debug)
805182007Sroberto			printf("acts: setup %s\n", MODEM_SETUP);
806182007Sroberto#endif
807182007Sroberto		if (write(pp->io.fd, MODEM_SETUP, strlen(MODEM_SETUP)) <
808182007Sroberto		    0) {
809182007Sroberto			msyslog(LOG_ERR, "acts: write %m");
810182007Sroberto			return;
811182007Sroberto		}
812182007Sroberto		up->state = S_OK;
813182007Sroberto		up->timer = SETUP;
814182007Sroberto		return;
81554359Sroberto
81654359Sroberto	/*
817182007Sroberto	 * In OK state the modem did not respond to setup.
81854359Sroberto	 */
819182007Sroberto	case S_OK:
820182007Sroberto		msyslog(LOG_ERR, "acts: no modem");
821182007Sroberto		break;
82254359Sroberto
82354359Sroberto	/*
824182007Sroberto	 * In DTR state we are waiting for the modem to settle down
825182007Sroberto	 * before hammering it with a dial command.
82654359Sroberto	 */
827182007Sroberto	case S_DTR:
828182007Sroberto		sprintf(tbuf, "DIAL #%d %s", up->retry,
829182007Sroberto		    sys_phone[up->retry]);
830182007Sroberto		record_clock_stats(&peer->srcadr, tbuf);
83154359Sroberto#ifdef DEBUG
83254359Sroberto		if (debug)
833182007Sroberto			printf("%s\n", tbuf);
83454359Sroberto#endif
835182007Sroberto		write(pp->io.fd, sys_phone[up->retry],
836182007Sroberto		    strlen(sys_phone[up->retry]));
837182007Sroberto		write(pp->io.fd, "\r", 1);
838182007Sroberto		up->state = S_CONNECT;
839182007Sroberto		up->timer = ANSWER;
84054359Sroberto		return;
84154359Sroberto
84254359Sroberto	/*
843182007Sroberto	 * In CONNECT state the call did not complete.
84454359Sroberto	 */
845182007Sroberto	case S_CONNECT:
846182007Sroberto		msyslog(LOG_ERR, "acts: no answer");
847182007Sroberto		break;
84854359Sroberto
849182007Sroberto	/*
850182007Sroberto	 * In FIRST state no messages were received.
851182007Sroberto	 */
852182007Sroberto	case S_FIRST:
853182007Sroberto		msyslog(LOG_ERR, "acts: no messages");
854182007Sroberto		break;
85554359Sroberto
85654359Sroberto	/*
857182007Sroberto	 * In CLOSE state hangup is complete. Close the doors and
858182007Sroberto	 * windows and get some air.
85954359Sroberto	 */
860182007Sroberto	case S_CLOSE:
861182007Sroberto
862182007Sroberto		/*
863182007Sroberto		 * Close the device and unlock a shared modem.
864182007Sroberto		 */
865182007Sroberto		if (pp->io.fd) {
866182007Sroberto			io_closeclock(&pp->io);
867182007Sroberto			close(pp->io.fd);
868182007Sroberto			if (pp->sloppyclockflag & CLK_FLAG2) {
869182007Sroberto				sprintf(lockfile, LOCKFILE, up->unit);
870182007Sroberto				unlink(lockfile);
871182007Sroberto			}
872182007Sroberto			pp->io.fd = 0;
873182007Sroberto		}
874182007Sroberto
875182007Sroberto		/*
876182007Sroberto		 * If messages were received, fold the tent and wait for
877182007Sroberto		 * the next poll. If no messages and there are more
878182007Sroberto		 * numbers to dial, retry after a short wait.
879182007Sroberto		 */
880182007Sroberto		up->bufptr = pp->a_lastcode;
881182007Sroberto		up->timer = 0;
882182007Sroberto		up->state = S_IDLE;
883182007Sroberto		if ( up->msgcnt == 0) {
884182007Sroberto			up->retry++;
885182007Sroberto			if (sys_phone[up->retry] == NULL)
886182007Sroberto				up->retry = 0;
887182007Sroberto			else
888182007Sroberto				up->timer = SETUP;
889182007Sroberto		} else {
890182007Sroberto			up->retry = 0;
891182007Sroberto		}
892182007Sroberto		up->msgcnt = 0;
893182007Sroberto		return;
89454359Sroberto	}
895182007Sroberto	acts_disc(peer);
89654359Sroberto}
89754359Sroberto
89854359Sroberto
89954359Sroberto/*
900182007Sroberto * acts_disc - disconnect the call and clean the place up.
90154359Sroberto */
902182007Srobertostatic void
903182007Srobertoacts_disc (
904182007Sroberto	struct peer *peer
90554359Sroberto	)
90654359Sroberto{
907182007Sroberto	struct actsunit *up;
90854359Sroberto	struct refclockproc *pp;
909182007Sroberto	int	dtr = TIOCM_DTR;
91054359Sroberto
91154359Sroberto	/*
912182007Sroberto	 * We get here if the call terminated successfully or if an
913182007Sroberto	 * error occured. If the median filter has something in it,feed
914182007Sroberto	 * the data to the clock filter. If a modem port, drop DTR to
915182007Sroberto	 * force command mode and send modem hangup.
91654359Sroberto	 */
91754359Sroberto	pp = peer->procptr;
91854359Sroberto	up = (struct actsunit *)pp->unitptr;
919182007Sroberto	if (up->msgcnt > 0)
920182007Sroberto		refclock_receive(peer);
921182007Sroberto	if (!(pp->sloppyclockflag & CLK_FLAG3)) {
922182007Sroberto		ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr);
923182007Sroberto		write(pp->io.fd, MODEM_HANGUP, strlen(MODEM_HANGUP));
924182007Sroberto	}
925182007Sroberto	up->timer = SETUP;
926182007Sroberto	up->state = S_CLOSE;
92754359Sroberto}
92854359Sroberto
92954359Sroberto#else
93054359Srobertoint refclock_acts_bs;
93154359Sroberto#endif /* REFCLOCK */
932