PageRenderTime 84ms CodeModel.GetById 12ms app.highlight 64ms RepoModel.GetById 2ms app.codeStats 0ms

/contrib/ntp/ntpd/refclock_acts.c

https://bitbucket.org/freebsd/freebsd-head/
C | 931 lines | 526 code | 77 blank | 328 comment | 107 complexity | 17742ab2a6ffd85797f4e173885fc332 MD5 | raw file
  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
140/*
141 * Calling program modes
142 */
143#define MODE_AUTO	0	/* automatic mode */
144#define MODE_BACKUP	1	/* backup mode */
145#define MODE_MANUAL	2	/* manual mode */
146
147/*
148 * Service identifiers.
149 */
150#define REFACTS		"NIST"	/* NIST reference ID */
151#define LENACTS		50	/* NIST format */
152#define REFUSNO		"USNO"	/* USNO reference ID */
153#define LENUSNO		20	/* USNO */
154#define REFPTB		"PTB\0"	/* PTB/NPL reference ID */
155#define LENPTB		78	/* PTB/NPL format */
156#define REFWWVB		"WWVB"	/* WWVB reference ID */
157#define	LENWWVB0	22	/* WWVB format 0 */
158#define	LENWWVB2	24	/* WWVB format 2 */
159#define LF		0x0a	/* ASCII LF */
160
161/*
162 * Modem setup strings. These may have to be changed for some modems.
163 *
164 * AT	command prefix
165 * B1	US answer tone
166 * &C0	disable carrier detect
167 * &D2	hang up and return to command mode on DTR transition
168 * E0	modem command echo disabled
169 * l1	set modem speaker volume to low level
170 * M1	speaker enabled until carrier detect
171 * Q0	return result codes
172 * V1	return result codes as English words
173 */
174#define MODEM_SETUP	"ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */
175#define MODEM_HANGUP	"ATH\r"	/* modem disconnect */
176
177/*
178 * Timeouts (all in seconds)
179 */
180#define SETUP		3	/* setup timeout */
181#define	DTR		1	/* DTR timeout */
182#define ANSWER		60	/* answer timeout */
183#define CONNECT		20	/* first valid message timeout */
184#define TIMECODE	30	/* all valid messages timeout */
185
186/*
187 * State machine codes
188 */
189#define S_IDLE		0	/* wait for poll */
190#define S_OK		1	/* wait for modem setup */
191#define S_DTR		2	/* wait for modem DTR */
192#define S_CONNECT	3	/* wait for answer*/
193#define S_FIRST		4	/* wait for first valid message */
194#define S_MSG		5	/* wait for all messages */
195#define S_CLOSE		6	/* wait after sending disconnect */
196
197/*
198 * Unit control structure
199 */
200struct actsunit {
201	int	unit;		/* unit number */
202	int	state;		/* the first one was Delaware */
203	int	timer;		/* timeout counter */
204	int	retry;		/* retry index */
205	int	msgcnt;		/* count of messages received */
206	l_fp	tstamp;		/* on-time timestamp */
207	char	*bufptr;	/* buffer pointer */
208};
209
210/*
211 * Function prototypes
212 */
213static	int	acts_start	P((int, struct peer *));
214static	void	acts_shutdown	P((int, struct peer *));
215static	void	acts_receive	P((struct recvbuf *));
216static	void	acts_message	P((struct peer *));
217static	void	acts_timecode	P((struct peer *, char *));
218static	void	acts_poll	P((int, struct peer *));
219static	void	acts_timeout	P((struct peer *));
220static	void	acts_disc	P((struct peer *));
221static	void	acts_timer	P((int, struct peer *));
222
223/*
224 * Transfer vector (conditional structure name)
225 */
226struct	refclock refclock_acts = {
227	acts_start,		/* start up driver */
228	acts_shutdown,		/* shut down driver */
229	acts_poll,		/* transmit poll message */
230	noentry,		/* not used */
231	noentry,		/* not used */
232	noentry,		/* not used */
233	acts_timer		/* housekeeping timer */
234};
235
236struct	refclock refclock_ptb;
237
238/*
239 * Initialize data for processing
240 */
241static int
242acts_start (
243	int	unit,
244	struct peer *peer
245	)
246{
247	struct actsunit *up;
248	struct refclockproc *pp;
249
250	/*
251	 * Allocate and initialize unit structure
252	 */
253	up = emalloc(sizeof(struct actsunit));
254	if (up == NULL)
255		return (0);
256
257	memset(up, 0, sizeof(struct actsunit));
258	up->unit = unit;
259	pp = peer->procptr;
260	pp->unitptr = (caddr_t)up;
261	pp->io.clock_recv = acts_receive;
262	pp->io.srcclock = (caddr_t)peer;
263	pp->io.datalen = 0;
264
265	/*
266	 * Initialize miscellaneous variables
267	 */
268	peer->precision = PRECISION;
269	pp->clockdesc = DESCRIPTION;
270	memcpy((char *)&pp->refid, REFID, 4);
271	peer->sstclktype = CTL_SST_TS_TELEPHONE;
272	peer->flags &= ~FLAG_FIXPOLL;
273	up->bufptr = pp->a_lastcode;
274	return (1);
275}
276
277
278/*
279 * acts_shutdown - shut down the clock
280 */
281static void
282acts_shutdown (
283	int	unit,
284	struct peer *peer
285	)
286{
287	struct actsunit *up;
288	struct refclockproc *pp;
289
290	/*
291	 * Warning: do this only when a call is not in progress.
292	 */
293	pp = peer->procptr;
294	up = (struct actsunit *)pp->unitptr;
295	free(up);
296}
297
298
299/*
300 * acts_receive - receive data from the serial interface
301 */
302static void
303acts_receive (
304	struct recvbuf *rbufp
305	)
306{
307	struct actsunit *up;
308	struct refclockproc *pp;
309	struct peer *peer;
310	char	tbuf[BMAX];
311	char	*tptr;
312
313	/*
314	 * Initialize pointers and read the timecode and timestamp. Note
315	 * we are in raw mode and victim of whatever the terminal
316	 * interface kicks up; so, we have to reassemble messages from
317	 * arbitrary fragments. Capture the timecode at the beginning of
318	 * the message and at the '*' and '#' on-time characters.
319	 */
320	peer = (struct peer *)rbufp->recv_srcclock;
321	pp = peer->procptr;
322	up = (struct actsunit *)pp->unitptr;
323	pp->lencode = refclock_gtraw(rbufp, tbuf, BMAX - (up->bufptr -
324	    pp->a_lastcode), &pp->lastrec);
325	for (tptr = tbuf; *tptr != '\0'; tptr++) {
326		if (*tptr == LF) {
327			if (up->bufptr == pp->a_lastcode) {
328				up->tstamp = pp->lastrec;
329				continue;
330
331			} else {
332				*up->bufptr = '\0';
333				acts_message(peer);
334				up->bufptr = pp->a_lastcode;
335			}
336		} else if (!iscntrl(*tptr)) {
337			*up->bufptr++ = *tptr;
338			if (*tptr == '*' || *tptr == '#') {
339				up->tstamp = pp->lastrec;
340				write(pp->io.fd, tptr, 1);
341			}
342		}
343	}
344}
345
346
347/*
348 * acts_message - process message
349 */
350void
351acts_message(
352	struct peer *peer
353	)
354{
355	struct actsunit *up;
356	struct refclockproc *pp;
357	int	dtr = TIOCM_DTR;
358	char	tbuf[SMAX];
359#ifdef DEBUG
360	u_int	modem;
361#endif
362
363	/*
364	 * What to do depends on the state and the first token in the
365	 * message. A NO token sends the message to the clockstats.
366	 */
367	pp = peer->procptr;
368	up = (struct actsunit *)pp->unitptr;
369#ifdef DEBUG
370	ioctl(pp->io.fd, TIOCMGET, (char *)&modem);
371	sprintf(tbuf, "acts: %04x (%d %d) %lu %s", modem, up->state,
372	    up->timer, strlen(pp->a_lastcode), pp->a_lastcode);
373	if (debug)
374		printf("%s\n", tbuf);
375#endif
376	strncpy(tbuf, pp->a_lastcode, SMAX);
377	strtok(tbuf, " ");
378	if (strcmp(tbuf, "NO") == 0)
379		record_clock_stats(&peer->srcadr, pp->a_lastcode);
380	switch(up->state) {
381
382	/*
383	 * We are waiting for the OK response to the modem setup
384	 * command. When this happens, raise DTR and dial the number
385	 * followed by \r.
386	 */
387	case S_OK:
388		if (strcmp(tbuf, "OK") != 0) {
389			msyslog(LOG_ERR, "acts: setup error %s",
390			    pp->a_lastcode);
391			acts_disc(peer);
392			return;
393		}
394		ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr);
395		up->state = S_DTR;
396		up->timer = DTR;
397		return;
398
399	/*
400	 * We are waiting for the call to be answered. All we care about
401	 * here is token CONNECT. Send the message to the clockstats.
402	 */
403	case S_CONNECT:
404		record_clock_stats(&peer->srcadr, pp->a_lastcode);
405		if (strcmp(tbuf, "CONNECT") != 0) {
406			acts_disc(peer);
407			return;
408		}
409		up->state = S_FIRST;
410		up->timer = CONNECT;
411		return;
412
413	/*
414	 * We are waiting for a timecode. Pass it to the parser.
415	 */
416	case S_FIRST:
417	case S_MSG:
418		acts_timecode(peer, pp->a_lastcode);
419		break;
420	}
421}
422
423/*
424 * acts_timecode - identify the service and parse the timecode message
425 */
426void
427acts_timecode(
428	struct peer *peer,	/* peer structure pointer */
429	char	*str		/* timecode string */
430	)
431{
432	struct actsunit *up;
433	struct refclockproc *pp;
434	int	day;		/* day of the month */
435	int	month;		/* month of the year */
436	u_long	mjd;		/* Modified Julian Day */
437	double	dut1;		/* DUT adjustment */
438
439	u_int	dst;		/* ACTS daylight/standard time */
440	u_int	leap;		/* ACTS leap indicator */
441	double	msADV;		/* ACTS transmit advance (ms) */
442	char	utc[10];	/* ACTS timescale */
443	char	flag;		/* ACTS on-time character (* or #) */
444
445	char	synchar;	/* WWVB synchronized indicator */
446	char	qualchar;	/* WWVB quality indicator */
447	char	leapchar;	/* WWVB leap indicator */
448	char	dstchar;	/* WWVB daylight/savings indicator */
449	int	tz;		/* WWVB timezone */
450
451	u_int	leapmonth;	/* PTB/NPL month of leap */
452	char	leapdir;	/* PTB/NPL leap direction */
453
454	/*
455	 * The parser selects the modem format based on the message
456	 * length. Since the data are checked carefully, occasional
457	 * errors due noise are forgivable.
458	 */
459	pp = peer->procptr;
460	up = (struct actsunit *)pp->unitptr;
461	pp->nsec = 0;
462	switch(strlen(str)) {
463
464	/*
465	 * For USNO format on-time character '*', which is on a line by
466	 * itself. Be sure a timecode has been received.
467	 */
468	case 1:
469		if (*str == '*' && up->msgcnt > 0) 
470			break;
471
472		return;
473	
474	/*
475	 * ACTS format: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
476	 * UTC(NIST) *"
477	 */
478	case LENACTS:
479		if (sscanf(str,
480		    "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
481		    &mjd, &pp->year, &month, &day, &pp->hour,
482		    &pp->minute, &pp->second, &dst, &leap, &dut1,
483		    &msADV, utc, &flag) != 13) {
484			refclock_report(peer, CEVNT_BADREPLY);
485			return;
486		}
487
488		/*
489		 * Wait until ACTS has calculated the roundtrip delay.
490		 * We don't need to do anything, as ACTS adjusts the
491		 * on-time epoch.
492		 */
493		if (flag != '#')
494			return;
495
496		pp->day = ymd2yd(pp->year, month, day);
497		pp->leap = LEAP_NOWARNING;
498		if (leap == 1)
499	    		pp->leap = LEAP_ADDSECOND;
500		else if (pp->leap == 2)
501	    		pp->leap = LEAP_DELSECOND;
502		memcpy(&pp->refid, REFACTS, 4);
503		if (up->msgcnt == 0)
504			record_clock_stats(&peer->srcadr, str);
505		up->msgcnt++;
506		break;
507
508	/*
509	 * USNO format: "jjjjj nnn hhmmss UTC"
510	 */
511	case LENUSNO:
512		if (sscanf(str, "%5ld %3d %2d%2d%2d %3s",
513		    &mjd, &pp->day, &pp->hour, &pp->minute,
514		    &pp->second, utc) != 6) {
515			refclock_report(peer, CEVNT_BADREPLY);
516			return;
517		}
518
519		/*
520		 * Wait for the on-time character, which follows in a
521		 * separate message. There is no provision for leap
522		 * warning.
523		 */
524		pp->leap = LEAP_NOWARNING;
525		memcpy(&pp->refid, REFUSNO, 4);
526		if (up->msgcnt == 0)
527			record_clock_stats(&peer->srcadr, str);
528		up->msgcnt++;
529		return;
530
531	/*
532	 * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ" 
533	 */
534	case LENPTB:
535		if (sscanf(str,
536		    "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
537		    &pp->second, &pp->year, &month, &day, &pp->hour,
538		    &pp->minute, &mjd, &dut1, &leapdir, &leapmonth,
539		    &msADV, &flag) != 12) {
540			refclock_report(peer, CEVNT_BADREPLY);
541			return;
542		}
543		pp->leap = LEAP_NOWARNING;
544		if (leapmonth == month) {
545			if (leapdir == '+')
546		    		pp->leap = LEAP_ADDSECOND;
547			else if (leapdir == '-')
548		    		pp->leap = LEAP_DELSECOND;
549		}
550		pp->day = ymd2yd(pp->year, month, day);
551		memcpy(&pp->refid, REFPTB, 4);
552		if (up->msgcnt == 0)
553			record_clock_stats(&peer->srcadr, str);
554		up->msgcnt++;
555		break;
556
557
558	/*
559	 * WWVB format 0: "I  ddd hh:mm:ss DTZ=nn"
560	 */
561	case LENWWVB0:
562		if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d",
563		    &synchar, &pp->day, &pp->hour, &pp->minute,
564		    &pp->second, &dstchar, &tz) != 7) {
565			refclock_report(peer, CEVNT_BADREPLY);
566			return;
567		}
568		pp->leap = LEAP_NOWARNING;
569		if (synchar != ' ')
570			pp->leap = LEAP_NOTINSYNC;
571		memcpy(&pp->refid, REFWWVB, 4);
572		if (up->msgcnt == 0)
573			record_clock_stats(&peer->srcadr, str);
574		up->msgcnt++;
575		break;
576
577	/*
578	 * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
579	 */
580	case LENWWVB2:
581		if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
582		    &synchar, &qualchar, &pp->year, &pp->day,
583		    &pp->hour, &pp->minute, &pp->second, &pp->nsec,
584		    &dstchar, &leapchar, &dstchar) != 11) {
585			refclock_report(peer, CEVNT_BADREPLY);
586			return;
587		}
588		pp->nsec *= 1000000;
589		pp->leap = LEAP_NOWARNING;
590		if (synchar != ' ')
591			pp->leap = LEAP_NOTINSYNC;
592		else if (leapchar == 'L')
593			pp->leap = LEAP_ADDSECOND;
594		memcpy(&pp->refid, REFWWVB, 4);
595		if (up->msgcnt == 0)
596			record_clock_stats(&peer->srcadr, str);
597		up->msgcnt++;
598		break;
599
600	/*
601	 * None of the above. Just forget about it and wait for the next
602	 * message or timeout.
603	 */
604	default:
605		return;
606	}
607
608	/*
609	 * We have a valid timecode. The fudge time1 value is added to
610	 * each sample by the main line routines. Note that in current
611	 * telephone networks the propatation time can be different for
612	 * each call and can reach 200 ms for some calls.
613	 */
614	peer->refid = pp->refid;
615	pp->lastrec = up->tstamp;
616	if (!refclock_process(pp)) {
617		refclock_report(peer, CEVNT_BADTIME);
618		return;
619			}
620	pp->lastref = pp->lastrec;
621	if (peer->disp > MAXDISTANCE)
622		refclock_receive(peer);
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[BMAX];
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			sprintf(lockfile, LOCKFILE, up->unit);
755			fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
756			    0644);
757			if (fd < 0) {
758				msyslog(LOG_ERR, "acts: port busy");
759				return;
760			}
761			sprintf(pidbuf, "%d\n", (u_int)getpid());
762			write(fd, pidbuf, strlen(pidbuf));
763			close(fd);
764		}
765
766		/*
767		 * Open the device in raw mode and link the I/O.
768		 */
769		if (!pp->io.fd) {
770			sprintf(device, DEVICE, up->unit);
771			fd = refclock_open(device, SPEED232,
772			    LDISC_ACTS | LDISC_RAW | LDISC_REMOTE);
773			if (fd == 0) {
774				return;
775			}
776			pp->io.fd = fd;
777			if (!io_addclock(&pp->io)) {
778				msyslog(LOG_ERR,
779				    "acts: addclock fails");
780				close(fd);
781				pp->io.fd = 0;
782				return;
783			}
784		}
785
786		/*
787		 * If the port is directly connected to the device, skip
788		 * the modem business and send 'T' for Spectrabum.
789		 */
790		if (pp->sloppyclockflag & CLK_FLAG3) {
791			if (write(pp->io.fd, "T", 1) < 0) {
792				msyslog(LOG_ERR, "acts: write %m");
793				return;
794			}
795			up->state = S_FIRST;
796			up->timer = CONNECT;
797			return;
798		}
799
800		/*
801		 * Initialize the modem. This works with Hayes commands.
802		 */
803#ifdef DEBUG
804		if (debug)
805			printf("acts: setup %s\n", MODEM_SETUP);
806#endif
807		if (write(pp->io.fd, MODEM_SETUP, strlen(MODEM_SETUP)) <
808		    0) {
809			msyslog(LOG_ERR, "acts: write %m");
810			return;
811		}
812		up->state = S_OK;
813		up->timer = SETUP;
814		return;
815
816	/*
817	 * In OK state the modem did not respond to setup.
818	 */
819	case S_OK:
820		msyslog(LOG_ERR, "acts: no modem");
821		break;
822
823	/*
824	 * In DTR state we are waiting for the modem to settle down
825	 * before hammering it with a dial command.
826	 */
827	case S_DTR:
828		sprintf(tbuf, "DIAL #%d %s", up->retry,
829		    sys_phone[up->retry]);
830		record_clock_stats(&peer->srcadr, tbuf);
831#ifdef DEBUG
832		if (debug)
833			printf("%s\n", tbuf);
834#endif
835		write(pp->io.fd, sys_phone[up->retry],
836		    strlen(sys_phone[up->retry]));
837		write(pp->io.fd, "\r", 1);
838		up->state = S_CONNECT;
839		up->timer = ANSWER;
840		return;
841
842	/*
843	 * In CONNECT state the call did not complete.
844	 */
845	case S_CONNECT:
846		msyslog(LOG_ERR, "acts: no answer");
847		break;
848
849	/*
850	 * In FIRST state no messages were received.
851	 */
852	case S_FIRST:
853		msyslog(LOG_ERR, "acts: no messages");
854		break;
855
856	/*
857	 * In CLOSE state hangup is complete. Close the doors and
858	 * windows and get some air.
859	 */
860	case S_CLOSE:
861
862		/*
863		 * Close the device and unlock a shared modem.
864		 */
865		if (pp->io.fd) {
866			io_closeclock(&pp->io);
867			close(pp->io.fd);
868			if (pp->sloppyclockflag & CLK_FLAG2) {
869				sprintf(lockfile, LOCKFILE, up->unit);
870				unlink(lockfile);
871			}
872			pp->io.fd = 0;
873		}
874
875		/*
876		 * If messages were received, fold the tent and wait for
877		 * the next poll. If no messages and there are more
878		 * numbers to dial, retry after a short wait.
879		 */
880		up->bufptr = pp->a_lastcode;
881		up->timer = 0;
882		up->state = S_IDLE;
883		if ( up->msgcnt == 0) {
884			up->retry++;
885			if (sys_phone[up->retry] == NULL)
886				up->retry = 0;
887			else
888				up->timer = SETUP;
889		} else {
890			up->retry = 0;
891		}
892		up->msgcnt = 0;
893		return;
894	}
895	acts_disc(peer);
896}
897
898
899/*
900 * acts_disc - disconnect the call and clean the place up.
901 */
902static void
903acts_disc (
904	struct peer *peer
905	)
906{
907	struct actsunit *up;
908	struct refclockproc *pp;
909	int	dtr = TIOCM_DTR;
910
911	/*
912	 * We get here if the call terminated successfully or if an
913	 * error occured. If the median filter has something in it,feed
914	 * the data to the clock filter. If a modem port, drop DTR to
915	 * force command mode and send modem hangup.
916	 */
917	pp = peer->procptr;
918	up = (struct actsunit *)pp->unitptr;
919	if (up->msgcnt > 0)
920		refclock_receive(peer);
921	if (!(pp->sloppyclockflag & CLK_FLAG3)) {
922		ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr);
923		write(pp->io.fd, MODEM_HANGUP, strlen(MODEM_HANGUP));
924	}
925	up->timer = SETUP;
926	up->state = S_CLOSE;
927}
928
929#else
930int refclock_acts_bs;
931#endif /* REFCLOCK */