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