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