File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / ntpd / refclock_acts.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:08:37 2012 UTC (12 years, 2 months ago) by misho
Branches: ntp, MAIN
CVS tags: v4_2_6p5p0, v4_2_6p5, HEAD
ntp 4.2.6p5

    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:  */
  201: struct 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:  */
  214: static	int	acts_start	(int, struct peer *);
  215: static	void	acts_shutdown	(int, struct peer *);
  216: static	void	acts_receive	(struct recvbuf *);
  217: static	void	acts_message	(struct peer *);
  218: static	void	acts_timecode	(struct peer *, char *);
  219: static	void	acts_poll	(int, struct peer *);
  220: static	void	acts_timeout	(struct peer *);
  221: static	void	acts_disc	(struct peer *);
  222: static	void	acts_timer	(int, struct peer *);
  223: 
  224: /*
  225:  * Transfer vector (conditional structure name)
  226:  */
  227: struct	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:  */
  240: static int
  241: acts_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:  */
  276: static void
  277: acts_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:  */
  297: static void
  298: acts_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:  */
  345: void
  346: acts_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) %lu %s", modem,
  366: 		 up->state, up->timer, (u_long)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:  */
  428: void
  429: acts_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:  */
  633: static void
  634: acts_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:  */
  683: static void
  684: acts_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:  */
  717: static void
  718: acts_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:  */
  908: static void
  909: acts_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
  935: int refclock_acts_bs;
  936: #endif /* REFCLOCK */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>