1/*	$NetBSD: refclock_acts.c,v 1.1.1.2 2012/01/31 21:25:35 kardel Exp $	*/
2
3/*
4 * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
5 *	Services
6 */
7#ifdef HAVE_CONFIG_H
8#include <config.h>
9#endif
10
11#if defined(REFCLOCK) && (defined(CLOCK_ACTS) || defined(CLOCK_PTBACTS))
12
13#include "ntpd.h"
14#include "ntp_io.h"
15#include "ntp_unixtime.h"
16#include "ntp_refclock.h"
17#include "ntp_stdlib.h"
18#include "ntp_control.h"
19
20#include <stdio.h>
21#include <ctype.h>
22#ifdef HAVE_SYS_IOCTL_H
23# include <sys/ioctl.h>
24#endif /* HAVE_SYS_IOCTL_H */
25
26/*
27 * This driver supports the US (NIST, USNO) and European (PTB, NPL,
28 * etc.) modem time services, as well as Spectracom GPS and WWVB
29 * receivers connected via a modem. The driver periodically dials a
30 * number from a telephone list, receives the timecode data and
31 * calculates the local clock correction. It is designed primarily for
32 * use as backup when neither a radio clock nor connectivity to Internet
33 * time servers is available.
34 *
35 * This driver requires a modem with a Hayes-compatible command set and
36 * control over the modem data terminal ready (DTR) control line. The
37 * modem setup string is hard-coded in the driver and may require
38 * changes for nonstandard modems or special circumstances. For reasons
39 * unrelated to this driver, the data set ready (DSR) control line
40 * should not be set when this driver is first started.
41 *
42 * The calling program is initiated by setting fudge flag1, either
43 * manually or automatically. When flag1 is set, the calling program
44 * dials the first number in the phone command of the configuration
45 * file. If that call fails, the calling program dials the second number
46 * and so on. The number is specified by the Hayes ATDT prefix followed
47 * by the number itself, including the prefix and long-distance digits
48 * and delay code, if necessary. The flag1 is reset and the calling
49 * program terminated if (a) a valid clock update has been determined,
50 * (b) no more numbers remain in the list, (c) a device fault or timeout
51 * occurs or (d) fudge flag1 is reset manually.
52 *
53 * The driver is transparent to each of the modem time services and
54 * Spectracom radios. It selects the parsing algorithm depending on the
55 * message length. There is some hazard should the message be corrupted.
56 * However, the data format is checked carefully and only if all checks
57 * succeed is the message accepted. Corrupted lines are discarded
58 * without complaint.
59 *
60 * Fudge controls
61 *
62 * flag1	force a call in manual mode
63 * flag2	enable port locking (not verified)
64 * flag3	no modem; port is directly connected to device
65 * flag4	not used
66 *
67 * time1	offset adjustment (s)
68 *
69 * Ordinarily, the serial port is connected to a modem; however, it can
70 * be connected directly to a device or another computer for testing and
71 * calibration. In this case set fudge flag3 and the driver will send a
72 * single character 'T' at each poll event. In principle, fudge flag2
73 * enables port locking, allowing the modem to be shared when not in use
74 * by this driver. At least on Solaris with the current NTP I/O
75 * routines, this results only in lots of ugly error messages.
76 */
77/*
78 * National Institute of Science and Technology (NIST)
79 *
80 * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
81 *
82 * Data Format
83 *
84 * National Institute of Standards and Technology
85 * Telephone Time Service, Generator 3B
86 * Enter question mark "?" for HELP
87 *                         D  L D
88 *  MJD  YR MO DA H  M  S  ST S UT1 msADV        <OTM>
89 * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF>
90 * ...
91 *
92 * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is
93 * the on-time markers echoed by the driver and used by NIST to measure
94 * and correct for the propagation delay.
95 *
96 * US Naval Observatory (USNO)
97 *
98 * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
99 *
100 * Data Format (two lines, repeating at one-second intervals)
101 *
102 * jjjjj nnn hhmmss UTC<CR><LF>
103 * *<CR><LF>
104 *
105 * jjjjj	modified Julian day number (not used)
106 * nnn		day of year
107 * hhmmss	second of day
108 * *		on-time marker for previous timecode
109 * ...
110 *
111 * USNO does not correct for the propagation delay. A fudge time1 of
112 * about .06 s is advisable.
113 *
114 * European Services (PTB, NPL, etc.)
115 *
116 * PTB: +49 531 512038 (Germany)
117 * NPL: 0906 851 6333 (UK only)
118 *
119 * Data format (see the documentation for phone numbers and formats.)
120 *
121 * 1995-01-23 20:58:51 MEZ  10402303260219950123195849740+40000500<CR><LF>
122 *
123 * Spectracom GPS and WWVB Receivers
124 *
125 * If a modem is connected to a Spectracom receiver, this driver will
126 * call it up and retrieve the time in one of two formats. As this
127 * driver does not send anything, the radio will have to either be
128 * configured in continuous mode or be polled by another local driver.
129 */
130/*
131 * Interface definitions
132 */
133#define	DEVICE		"/dev/acts%d" /* device name and unit */
134#define	SPEED232	B9600	/* uart speed (9600 baud) */
135#define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
136#define LOCKFILE	"/var/spool/locks/LCK..cua%d"
137#define DESCRIPTION	"Automated Computer Time Service" /* WRU */
138#define REFID		"NONE"	/* default reference ID */
139#define MSGCNT		20	/* max message count */
140#define SMAX		256	/* max clockstats line length */
141#define	MAXPHONE	10	/* max number of phone numbers */
142
143/*
144 * Calling program modes
145 */
146#define MODE_AUTO	0	/* automatic mode */
147#define MODE_BACKUP	1	/* backup mode */
148#define MODE_MANUAL	2	/* manual mode */
149
150/*
151 * Service identifiers.
152 */
153#define REFACTS		"NIST"	/* NIST reference ID */
154#define LENACTS		50	/* NIST format */
155#define REFUSNO		"USNO"	/* USNO reference ID */
156#define LENUSNO		20	/* USNO */
157#define REFPTB		"PTB\0"	/* PTB/NPL reference ID */
158#define LENPTB		78	/* PTB/NPL format */
159#define REFWWVB		"WWVB"	/* WWVB reference ID */
160#define	LENWWVB0	22	/* WWVB format 0 */
161#define	LENWWVB2	24	/* WWVB format 2 */
162#define LF		0x0a	/* ASCII LF */
163
164/*
165 * Modem setup strings. These may have to be changed for some modems.
166 *
167 * AT	command prefix
168 * B1	US answer tone
169 * &C0	disable carrier detect
170 * &D2	hang up and return to command mode on DTR transition
171 * E0	modem command echo disabled
172 * l1	set modem speaker volume to low level
173 * M1	speaker enabled until carrier detect
174 * Q0	return result codes
175 * V1	return result codes as English words
176 */
177#define MODEM_SETUP	"ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */
178#define MODEM_HANGUP	"ATH\r"	/* modem disconnect */
179
180/*
181 * Timeouts (all in seconds)
182 */
183#define SETUP		3	/* setup timeout */
184#define	DTR		1	/* DTR timeout */
185#define ANSWER		60	/* answer timeout */
186#define CONNECT		20	/* first valid message timeout */
187#define TIMECODE	30	/* all valid messages timeout */
188
189/*
190 * State machine codes
191 */
192#define S_IDLE		0	/* wait for poll */
193#define S_OK		1	/* wait for modem setup */
194#define S_DTR		2	/* wait for modem DTR */
195#define S_CONNECT	3	/* wait for answer*/
196#define S_FIRST		4	/* wait for first valid message */
197#define S_MSG		5	/* wait for all messages */
198#define S_CLOSE		6	/* wait after sending disconnect */
199
200/*
201 * Unit control structure
202 */
203struct actsunit {
204	int	unit;		/* unit number */
205	int	state;		/* the first one was Delaware */
206	int	timer;		/* timeout counter */
207	int	retry;		/* retry index */
208	int	msgcnt;		/* count of messages received */
209	l_fp	tstamp;		/* on-time timestamp */
210	char	*bufptr;	/* buffer pointer */
211};
212
213/*
214 * Function prototypes
215 */
216static	int	acts_start	(int, struct peer *);
217static	void	acts_shutdown	(int, struct peer *);
218static	void	acts_receive	(struct recvbuf *);
219static	void	acts_message	(struct peer *);
220static	void	acts_timecode	(struct peer *, char *);
221static	void	acts_poll	(int, struct peer *);
222static	void	acts_timeout	(struct peer *);
223static	void	acts_disc	(struct peer *);
224static	void	acts_timer	(int, struct peer *);
225
226/*
227 * Transfer vector (conditional structure name)
228 */
229struct	refclock refclock_acts = {
230	acts_start,		/* start up driver */
231	acts_shutdown,		/* shut down driver */
232	acts_poll,		/* transmit poll message */
233	noentry,		/* not used */
234	noentry,		/* not used */
235	noentry,		/* not used */
236	acts_timer		/* housekeeping timer */
237};
238
239/*
240 * Initialize data for processing
241 */
242static int
243acts_start (
244	int	unit,
245	struct peer *peer
246	)
247{
248	struct actsunit *up;
249	struct refclockproc *pp;
250
251	/*
252	 * Allocate and initialize unit structure
253	 */
254	up = emalloc(sizeof(struct actsunit));
255	memset(up, 0, sizeof(struct actsunit));
256	up->unit = unit;
257	pp = peer->procptr;
258	pp->unitptr = (caddr_t)up;
259	pp->io.clock_recv = acts_receive;
260	pp->io.srcclock = (caddr_t)peer;
261	pp->io.datalen = 0;
262
263	/*
264	 * Initialize miscellaneous variables
265	 */
266	peer->precision = PRECISION;
267	pp->clockdesc = DESCRIPTION;
268	memcpy((char *)&pp->refid, REFID, 4);
269	peer->sstclktype = CTL_SST_TS_TELEPHONE;
270	up->bufptr = pp->a_lastcode;
271	return (1);
272}
273
274
275/*
276 * acts_shutdown - shut down the clock
277 */
278static void
279acts_shutdown (
280	int	unit,
281	struct peer *peer
282	)
283{
284	struct actsunit *up;
285	struct refclockproc *pp;
286
287	/*
288	 * Warning: do this only when a call is not in progress.
289	 */
290	pp = peer->procptr;
291	up = (struct actsunit *)pp->unitptr;
292	free(up);
293}
294
295
296/*
297 * acts_receive - receive data from the serial interface
298 */
299static void
300acts_receive (
301	struct recvbuf *rbufp
302	)
303{
304	struct actsunit *up;
305	struct refclockproc *pp;
306	struct peer *peer;
307	char	tbuf[BMAX];
308	char	*tptr;
309
310	/*
311	 * Initialize pointers and read the timecode and timestamp. Note
312	 * we are in raw mode and victim of whatever the terminal
313	 * interface kicks up; so, we have to reassemble messages from
314	 * arbitrary fragments. Capture the timecode at the beginning of
315	 * the message and at the '*' and '#' on-time characters.
316	 */
317	peer = (struct peer *)rbufp->recv_srcclock;
318	pp = peer->procptr;
319	up = (struct actsunit *)pp->unitptr;
320	pp->lencode = refclock_gtraw(rbufp, tbuf, BMAX - (up->bufptr -
321	    pp->a_lastcode), &pp->lastrec);
322	for (tptr = tbuf; *tptr != '\0'; tptr++) {
323		if (*tptr == LF) {
324			if (up->bufptr == pp->a_lastcode) {
325				up->tstamp = pp->lastrec;
326				continue;
327
328			} else {
329				*up->bufptr = '\0';
330				acts_message(peer);
331				up->bufptr = pp->a_lastcode;
332			}
333		} else if (!iscntrl((unsigned char)*tptr)) {
334			*up->bufptr++ = *tptr;
335			if (*tptr == '*' || *tptr == '#') {
336				up->tstamp = pp->lastrec;
337				write(pp->io.fd, tptr, 1);
338			}
339		}
340	}
341}
342
343
344/*
345 * acts_message - process message
346 */
347void
348acts_message(
349	struct peer *peer
350	)
351{
352	struct actsunit *up;
353	struct refclockproc *pp;
354	int	dtr = TIOCM_DTR;
355	char	tbuf[SMAX];
356#ifdef DEBUG
357	u_int	modem;
358#endif
359
360	/*
361	 * What to do depends on the state and the first token in the
362	 * message.	 */
363	pp = peer->procptr;
364	up = (struct actsunit *)pp->unitptr;
365#ifdef DEBUG
366	ioctl(pp->io.fd, TIOCMGET, (char *)&modem);
367	snprintf(tbuf, sizeof(tbuf), "acts: %04x (%d %d) %zu %s", modem,
368	    up->state, up->timer, strlen(pp->a_lastcode),
369	    pp->a_lastcode);
370	if (debug)
371		printf("%s\n", tbuf);
372#endif
373
374	/*
375	 * Extract the first token in the line. A NO token sends the
376	 * message to the clockstats.
377	 */
378	strncpy(tbuf, pp->a_lastcode, SMAX);
379	strtok(tbuf, " ");
380	if (strcmp(tbuf, "NO") == 0) {
381		report_event(PEVNT_CLOCK, peer, pp->a_lastcode);
382		return;
383	}
384	switch(up->state) {
385
386	/*
387	 * We are waiting for the OK response to the modem setup
388	 * command. When this happens, raise DTR and dial the number
389	 * followed by \r.
390	 */
391	case S_OK:
392		if (strcmp(tbuf, "OK") != 0) {
393			msyslog(LOG_ERR, "acts: setup error %s",
394			    pp->a_lastcode);
395			acts_disc(peer);
396			return;
397		}
398		ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr);
399		up->state = S_DTR;
400		up->timer = DTR;
401		return;
402
403	/*
404	 * We are waiting for the call to be answered. All we care about
405	 * here is token CONNECT. Send the message to the clockstats.
406	 */
407	case S_CONNECT:
408		report_event(PEVNT_CLOCK, peer, pp->a_lastcode);
409		if (strcmp(tbuf, "CONNECT") != 0) {
410			acts_disc(peer);
411			return;
412		}
413		up->state = S_FIRST;
414		up->timer = CONNECT;
415		return;
416
417	/*
418	 * We are waiting for a timecode. Pass it to the parser.
419	 */
420	case S_FIRST:
421	case S_MSG:
422		acts_timecode(peer, pp->a_lastcode);
423		break;
424	}
425}
426
427/*
428 * acts_timecode - identify the service and parse the timecode message
429 */
430void
431acts_timecode(
432	struct peer *peer,	/* peer structure pointer */
433	char	*str		/* timecode string */
434	)
435{
436	struct actsunit *up;
437	struct refclockproc *pp;
438	int	day;		/* day of the month */
439	int	month;		/* month of the year */
440	u_long	mjd;		/* Modified Julian Day */
441	double	dut1;		/* DUT adjustment */
442
443	u_int	dst;		/* ACTS daylight/standard time */
444	u_int	leap;		/* ACTS leap indicator */
445	double	msADV;		/* ACTS transmit advance (ms) */
446	char	utc[10];	/* ACTS timescale */
447	char	flag;		/* ACTS on-time character (* or #) */
448
449	char	synchar;	/* WWVB synchronized indicator */
450	char	qualchar;	/* WWVB quality indicator */
451	char	leapchar;	/* WWVB leap indicator */
452	char	dstchar;	/* WWVB daylight/savings indicator */
453	int	tz;		/* WWVB timezone */
454
455	int	leapmonth;	/* PTB/NPL month of leap */
456	char	leapdir;	/* PTB/NPL leap direction */
457
458	/*
459	 * The parser selects the modem format based on the message
460	 * length. Since the data are checked carefully, occasional
461	 * errors due noise are forgivable.
462	 */
463	pp = peer->procptr;
464	up = (struct actsunit *)pp->unitptr;
465	pp->nsec = 0;
466	switch(strlen(str)) {
467
468	/*
469	 * For USNO format on-time character '*', which is on a line by
470	 * itself. Be sure a timecode has been received.
471	 */
472	case 1:
473		if (*str == '*' && up->msgcnt > 0)
474			break;
475
476		return;
477
478	/*
479	 * ACTS format: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
480	 * UTC(NIST) *"
481	 */
482	case LENACTS:
483		if (sscanf(str,
484		    "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
485		    &mjd, &pp->year, &month, &day, &pp->hour,
486		    &pp->minute, &pp->second, &dst, &leap, &dut1,
487		    &msADV, utc, &flag) != 13) {
488			refclock_report(peer, CEVNT_BADREPLY);
489			return;
490		}
491
492		/*
493		 * Wait until ACTS has calculated the roundtrip delay.
494		 * We don't need to do anything, as ACTS adjusts the
495		 * on-time epoch.
496		 */
497		if (flag != '#')
498			return;
499
500		pp->day = ymd2yd(pp->year, month, day);
501		pp->leap = LEAP_NOWARNING;
502		if (leap == 1)
503	    		pp->leap = LEAP_ADDSECOND;
504		else if (pp->leap == 2)
505	    		pp->leap = LEAP_DELSECOND;
506		memcpy(&pp->refid, REFACTS, 4);
507		if (up->msgcnt == 0)
508			record_clock_stats(&peer->srcadr, str);
509		up->msgcnt++;
510		break;
511
512	/*
513	 * USNO format: "jjjjj nnn hhmmss UTC"
514	 */
515	case LENUSNO:
516		if (sscanf(str, "%5ld %3d %2d%2d%2d %3s",
517		    &mjd, &pp->day, &pp->hour, &pp->minute,
518		    &pp->second, utc) != 6) {
519			refclock_report(peer, CEVNT_BADREPLY);
520			return;
521		}
522
523		/*
524		 * Wait for the on-time character, which follows in a
525		 * separate message. There is no provision for leap
526		 * warning.
527		 */
528		pp->leap = LEAP_NOWARNING;
529		memcpy(&pp->refid, REFUSNO, 4);
530		if (up->msgcnt == 0)
531			record_clock_stats(&peer->srcadr, str);
532		up->msgcnt++;
533		return;
534
535	/*
536	 * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ"
537	 */
538	case LENPTB:
539		if (sscanf(str,
540		    "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
541		    &pp->second, &pp->year, &month, &day, &pp->hour,
542		    &pp->minute, &mjd, &dut1, &leapdir, &leapmonth,
543		    &msADV, &flag) != 12) {
544			refclock_report(peer, CEVNT_BADREPLY);
545			return;
546		}
547		pp->leap = LEAP_NOWARNING;
548		if (leapmonth == month) {
549			if (leapdir == '+')
550		    		pp->leap = LEAP_ADDSECOND;
551			else if (leapdir == '-')
552		    		pp->leap = LEAP_DELSECOND;
553		}
554		pp->day = ymd2yd(pp->year, month, day);
555		memcpy(&pp->refid, REFPTB, 4);
556		if (up->msgcnt == 0)
557			record_clock_stats(&peer->srcadr, str);
558		up->msgcnt++;
559		break;
560
561
562	/*
563	 * WWVB format 0: "I  ddd hh:mm:ss DTZ=nn"
564	 */
565	case LENWWVB0:
566		if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d",
567		    &synchar, &pp->day, &pp->hour, &pp->minute,
568		    &pp->second, &dstchar, &tz) != 7) {
569			refclock_report(peer, CEVNT_BADREPLY);
570			return;
571		}
572		pp->leap = LEAP_NOWARNING;
573		if (synchar != ' ')
574			pp->leap = LEAP_NOTINSYNC;
575		memcpy(&pp->refid, REFWWVB, 4);
576		if (up->msgcnt == 0)
577			record_clock_stats(&peer->srcadr, str);
578		up->msgcnt++;
579		break;
580
581	/*
582	 * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
583	 */
584	case LENWWVB2:
585		if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
586		    &synchar, &qualchar, &pp->year, &pp->day,
587		    &pp->hour, &pp->minute, &pp->second, &pp->nsec,
588		    &dstchar, &leapchar, &dstchar) != 11) {
589			refclock_report(peer, CEVNT_BADREPLY);
590			return;
591		}
592		pp->nsec *= 1000000;
593		pp->leap = LEAP_NOWARNING;
594		if (synchar != ' ')
595			pp->leap = LEAP_NOTINSYNC;
596		else if (leapchar == 'L')
597			pp->leap = LEAP_ADDSECOND;
598		memcpy(&pp->refid, REFWWVB, 4);
599		if (up->msgcnt == 0)
600			record_clock_stats(&peer->srcadr, str);
601		up->msgcnt++;
602		break;
603
604	/*
605	 * None of the above. Just forget about it and wait for the next
606	 * message or timeout.
607	 */
608	default:
609		return;
610	}
611
612	/*
613	 * We have a valid timecode. The fudge time1 value is added to
614	 * each sample by the main line routines. Note that in current
615	 * telephone networks the propatation time can be different for
616	 * each call and can reach 200 ms for some calls.
617	 */
618	peer->refid = pp->refid;
619	pp->lastrec = up->tstamp;
620	if (!refclock_process(pp)) {
621		refclock_report(peer, CEVNT_BADTIME);
622		return;
623	}
624	pp->lastref = pp->lastrec;
625	if (up->state != S_MSG) {
626		up->state = S_MSG;
627		up->timer = TIMECODE;
628	}
629}
630
631
632/*
633 * acts_poll - called by the transmit routine
634 */
635static void
636acts_poll (
637	int	unit,
638	struct peer *peer
639	)
640{
641	struct actsunit *up;
642	struct refclockproc *pp;
643
644	/*
645	 * This routine is called at every system poll. All it does is
646	 * set flag1 under certain conditions. The real work is done by
647	 * the timeout routine and state machine.
648	 */
649	pp = peer->procptr;
650	up = (struct actsunit *)pp->unitptr;
651	switch (peer->ttl) {
652
653	/*
654	 * In manual mode the calling program is activated by the ntpdc
655	 * program using the enable flag (fudge flag1), either manually
656	 * or by a cron job.
657	 */
658	case MODE_MANUAL:
659		/* fall through */
660		break;
661
662	/*
663	 * In automatic mode the calling program runs continuously at
664	 * intervals determined by the poll event or specified timeout.
665	 */
666	case MODE_AUTO:
667		pp->sloppyclockflag |= CLK_FLAG1;
668		break;
669
670	/*
671	 * In backup mode the calling program runs continuously as long
672	 * as either no peers are available or this peer is selected.
673	 */
674	case MODE_BACKUP:
675		if (sys_peer == NULL || sys_peer == peer)
676			pp->sloppyclockflag |= CLK_FLAG1;
677		break;
678	}
679}
680
681
682/*
683 * acts_timer - called at one-second intervals
684 */
685static void
686acts_timer(
687	int	unit,
688	struct peer *peer
689	)
690{
691	struct actsunit *up;
692	struct refclockproc *pp;
693
694	/*
695	 * This routine implments a timeout which runs for a programmed
696	 * interval. The counter is initialized by the state machine and
697	 * counts down to zero. Upon reaching zero, the state machine is
698	 * called. If flag1 is set while in S_IDLE state, force a
699	 * timeout.
700	 */
701	pp = peer->procptr;
702	up = (struct actsunit *)pp->unitptr;
703	if (pp->sloppyclockflag & CLK_FLAG1 && up->state == S_IDLE) {
704		acts_timeout(peer);
705		return;
706	}
707	if (up->timer == 0)
708		return;
709
710	up->timer--;
711	if (up->timer == 0)
712		acts_timeout(peer);
713}
714
715
716/*
717 * acts_timeout - called on timeout
718 */
719static void
720acts_timeout(
721	struct peer *peer
722	)
723{
724	struct actsunit *up;
725	struct refclockproc *pp;
726	int	fd;
727	char	device[20];
728	char	lockfile[128], pidbuf[8];
729	char	tbuf[SMAX];
730
731	/*
732	 * The state machine is driven by messages from the modem, when
733	 * first stated and at timeout.
734	 */
735	pp = peer->procptr;
736	up = (struct actsunit *)pp->unitptr;
737	pp->sloppyclockflag &= ~CLK_FLAG1;
738	if (sys_phone[up->retry] == NULL && !(pp->sloppyclockflag &
739	    CLK_FLAG3)) {
740		msyslog(LOG_ERR, "acts: no phones");
741		return;
742	}
743	switch(up->state) {
744
745	/*
746	 * System poll event. Lock the modem port and open the device.
747	 */
748	case S_IDLE:
749
750		/*
751		 * Lock the modem port. If busy, retry later. Note: if
752		 * something fails between here and the close, the lock
753		 * file may not be removed.
754		 */
755		if (pp->sloppyclockflag & CLK_FLAG2) {
756			snprintf(lockfile, sizeof(lockfile), LOCKFILE,
757			    up->unit);
758			fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
759			    0644);
760			if (fd < 0) {
761				msyslog(LOG_ERR, "acts: port busy");
762				return;
763			}
764			snprintf(pidbuf, sizeof(pidbuf), "%d\n",
765			    (u_int)getpid());
766			write(fd, pidbuf, strlen(pidbuf));
767			close(fd);
768		}
769
770		/*
771		 * Open the device in raw mode and link the I/O.
772		 */
773		if (!pp->io.fd) {
774			snprintf(device, sizeof(device), DEVICE,
775			    up->unit);
776			fd = refclock_open(device, SPEED232,
777			    LDISC_ACTS | LDISC_RAW | LDISC_REMOTE);
778			if (fd == 0) {
779				msyslog(LOG_ERR,
780				    "acts: open fails");
781				return;
782			}
783			pp->io.fd = fd;
784			if (!io_addclock(&pp->io)) {
785				msyslog(LOG_ERR,
786				    "acts: addclock fails");
787				close(fd);
788				pp->io.fd = 0;
789				return;
790			}
791		}
792
793		/*
794		 * If the port is directly connected to the device, skip
795		 * the modem business and send 'T' for Spectrabum.
796		 */
797		if (pp->sloppyclockflag & CLK_FLAG3) {
798			if (write(pp->io.fd, "T", 1) < 0) {
799				msyslog(LOG_ERR, "acts: write %m");
800				return;
801			}
802			up->state = S_FIRST;
803			up->timer = CONNECT;
804			return;
805		}
806
807		/*
808		 * Initialize the modem. This works with Hayes commands.
809		 */
810#ifdef DEBUG
811		if (debug)
812			printf("acts: setup %s\n", MODEM_SETUP);
813#endif
814		if (write(pp->io.fd, MODEM_SETUP, strlen(MODEM_SETUP)) <
815		    0) {
816			msyslog(LOG_ERR, "acts: write %m");
817			return;
818		}
819		up->state = S_OK;
820		up->timer = SETUP;
821		return;
822
823	/*
824	 * In OK state the modem did not respond to setup.
825	 */
826	case S_OK:
827		msyslog(LOG_ERR, "acts: no modem");
828		break;
829
830	/*
831	 * In DTR state we are waiting for the modem to settle down
832	 * before hammering it with a dial command.
833	 */
834	case S_DTR:
835		snprintf(tbuf, sizeof(tbuf), "DIAL #%d %s", up->retry,
836		    sys_phone[up->retry]);
837		report_event(PEVNT_CLOCK, peer, tbuf);
838#ifdef DEBUG
839		if (debug)
840			printf("%s\n", tbuf);
841#endif
842		write(pp->io.fd, sys_phone[up->retry],
843		    strlen(sys_phone[up->retry]));
844		write(pp->io.fd, "\r", 1);
845		up->state = S_CONNECT;
846		up->timer = ANSWER;
847		return;
848
849	/*
850	 * In CONNECT state the call did not complete.
851	 */
852	case S_CONNECT:
853		msyslog(LOG_ERR, "acts: no answer");
854		break;
855
856	/*
857	 * In FIRST state no messages were received.
858	 */
859	case S_FIRST:
860		msyslog(LOG_ERR, "acts: no messages");
861		break;
862
863	/*
864	 * In CLOSE state hangup is complete. Close the doors and
865	 * windows and get some air.
866	 */
867	case S_CLOSE:
868
869		/*
870		 * Close the device and unlock a shared modem.
871		 */
872		if (pp->io.fd) {
873			io_closeclock(&pp->io);
874			close(pp->io.fd);
875			if (pp->sloppyclockflag & CLK_FLAG2) {
876				snprintf(lockfile, sizeof(lockfile),
877				    LOCKFILE, up->unit);
878				unlink(lockfile);
879			}
880			pp->io.fd = 0;
881		}
882
883		/*
884		 * If messages were received, fold the tent and wait for
885		 * the next poll. If no messages and there are more
886		 * numbers to dial, retry after a short wait.
887		 */
888		up->bufptr = pp->a_lastcode;
889		up->timer = 0;
890		up->state = S_IDLE;
891		if ( up->msgcnt == 0) {
892			up->retry++;
893			if (sys_phone[up->retry] == NULL)
894				up->retry = 0;
895			else
896				up->timer = SETUP;
897		} else {
898			up->retry = 0;
899		}
900		up->msgcnt = 0;
901		return;
902	}
903	acts_disc(peer);
904}
905
906
907/*
908 * acts_disc - disconnect the call and clean the place up.
909 */
910static void
911acts_disc (
912	struct peer *peer
913	)
914{
915	struct actsunit *up;
916	struct refclockproc *pp;
917	int	dtr = TIOCM_DTR;
918
919	/*
920	 * We get here if the call terminated successfully or if an
921	 * error occured. If the median filter has something in it,
922	 * feed the data to the clock filter. If a modem port, drop DTR
923	 * to force command mode and send modem hangup.
924	 */
925	pp = peer->procptr;
926	up = (struct actsunit *)pp->unitptr;
927	if (up->msgcnt > 0)
928		refclock_receive(peer);
929	if (!(pp->sloppyclockflag & CLK_FLAG3)) {
930		ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr);
931		write(pp->io.fd, MODEM_HANGUP, strlen(MODEM_HANGUP));
932	}
933	up->timer = SETUP;
934	up->state = S_CLOSE;
935}
936#else
937int refclock_acts_bs;
938#endif /* REFCLOCK */
939