File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / ntpd / refclock_zyfer.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:08:38 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_zyfer - clock driver for the Zyfer GPSTarplus Clock
    3:  *
    4:  * Harlan Stenn, Jan 2002
    5:  */
    6: 
    7: #ifdef HAVE_CONFIG_H
    8: #include <config.h>
    9: #endif
   10: 
   11: #if defined(REFCLOCK) && defined(CLOCK_ZYFER)
   12: 
   13: #include "ntpd.h"
   14: #include "ntp_io.h"
   15: #include "ntp_refclock.h"
   16: #include "ntp_stdlib.h"
   17: #include "ntp_unixtime.h"
   18: 
   19: #include <stdio.h>
   20: #include <ctype.h>
   21: 
   22: #ifdef HAVE_SYS_TERMIOS_H
   23: # include <sys/termios.h>
   24: #endif
   25: #ifdef HAVE_SYS_PPSCLOCK_H
   26: # include <sys/ppsclock.h>
   27: #endif
   28: 
   29: /*
   30:  * This driver provides support for the TOD serial port of a Zyfer GPStarplus.
   31:  * This clock also provides PPS as well as IRIG outputs.
   32:  * Precision is limited by the serial driver, etc.
   33:  *
   34:  * If I was really brave I'd hack/generalize the serial driver to deal
   35:  * with arbitrary on-time characters.  This clock *begins* the stream with
   36:  * `!`, the on-time character, and the string is *not* EOL-terminated.
   37:  *
   38:  * Configure the beast for 9600, 8N1.  While I see leap-second stuff
   39:  * in the documentation, the published specs on the TOD format only show
   40:  * the seconds going to '59'.  I see no leap warning in the TOD format.
   41:  *
   42:  * The clock sends the following message once per second:
   43:  *
   44:  *	!TIME,2002,017,07,59,32,2,4,1
   45:  *	      YYYY DDD HH MM SS m T O
   46:  *
   47:  *	!		On-time character
   48:  *	YYYY		Year
   49:  *	DDD	001-366	Day of Year
   50:  *	HH	00-23	Hour
   51:  *	MM	00-59	Minute
   52:  *	SS	00-59	Second (probably 00-60)
   53:  *	m	1-5	Time Mode:
   54:  *			1 = GPS time
   55:  *			2 = UTC time
   56:  *			3 = LGPS time (Local GPS)
   57:  *			4 = LUTC time (Local UTC)
   58:  *			5 = Manual time
   59:  *	T	4-9	Time Figure Of Merit:
   60:  *			4         x <= 1us
   61:  *			5   1us < x <= 10 us
   62:  *			6  10us < x <= 100us
   63:  *			7 100us < x <= 1ms
   64:  *			8   1ms < x <= 10ms
   65:  *			9  10ms < x
   66:  *	O	0-4	Operation Mode:
   67:  *			0 Warm-up
   68:  *			1 Time Locked
   69:  *			2 Coasting
   70:  *			3 Recovering
   71:  *			4 Manual
   72:  *
   73:  */
   74: 
   75: /*
   76:  * Interface definitions
   77:  */
   78: #define	DEVICE		"/dev/zyfer%d" /* device name and unit */
   79: #define	SPEED232	B9600	/* uart speed (9600 baud) */
   80: #define	PRECISION	(-20)	/* precision assumed (about 1 us) */
   81: #define	REFID		"GPS\0"	/* reference ID */
   82: #define	DESCRIPTION	"Zyfer GPStarplus" /* WRU */
   83: 
   84: #define	LENZYFER	29	/* timecode length */
   85: 
   86: /*
   87:  * Unit control structure
   88:  */
   89: struct zyferunit {
   90: 	u_char	Rcvbuf[LENZYFER + 1];
   91: 	u_char	polled;		/* poll message flag */
   92: 	int	pollcnt;
   93: 	l_fp    tstamp;         /* timestamp of last poll */
   94: 	int	Rcvptr;
   95: };
   96: 
   97: /*
   98:  * Function prototypes
   99:  */
  100: static	int	zyfer_start	(int, struct peer *);
  101: static	void	zyfer_shutdown	(int, struct peer *);
  102: static	void	zyfer_receive	(struct recvbuf *);
  103: static	void	zyfer_poll	(int, struct peer *);
  104: 
  105: /*
  106:  * Transfer vector
  107:  */
  108: struct	refclock refclock_zyfer = {
  109: 	zyfer_start,		/* start up driver */
  110: 	zyfer_shutdown,		/* shut down driver */
  111: 	zyfer_poll,		/* transmit poll message */
  112: 	noentry,		/* not used (old zyfer_control) */
  113: 	noentry,		/* initialize driver (not used) */
  114: 	noentry,		/* not used (old zyfer_buginfo) */
  115: 	NOFLAGS			/* not used */
  116: };
  117: 
  118: 
  119: /*
  120:  * zyfer_start - open the devices and initialize data for processing
  121:  */
  122: static int
  123: zyfer_start(
  124: 	int unit,
  125: 	struct peer *peer
  126: 	)
  127: {
  128: 	register struct zyferunit *up;
  129: 	struct refclockproc *pp;
  130: 	int fd;
  131: 	char device[20];
  132: 
  133: 	/*
  134: 	 * Open serial port.
  135: 	 * Something like LDISC_ACTS that looked for ! would be nice...
  136: 	 */
  137: 	(void)sprintf(device, DEVICE, unit);
  138: 	if ( !(fd = refclock_open(device, SPEED232, LDISC_RAW)) )
  139: 	    return (0);
  140: 
  141: 	msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
  142: 
  143: 	/*
  144: 	 * Allocate and initialize unit structure
  145: 	 */
  146: 	if (!(up = (struct zyferunit *)
  147: 	      emalloc(sizeof(struct zyferunit)))) {
  148: 		(void) close(fd);
  149: 		return (0);
  150: 	}
  151: 	memset((char *)up, 0, sizeof(struct zyferunit));
  152: 	pp = peer->procptr;
  153: 	pp->io.clock_recv = zyfer_receive;
  154: 	pp->io.srcclock = (caddr_t)peer;
  155: 	pp->io.datalen = 0;
  156: 	pp->io.fd = fd;
  157: 	if (!io_addclock(&pp->io)) {
  158: 		(void) close(fd);
  159: 		free(up);
  160: 		return (0);
  161: 	}
  162: 	pp->unitptr = (caddr_t)up;
  163: 
  164: 	/*
  165: 	 * Initialize miscellaneous variables
  166: 	 */
  167: 	peer->precision = PRECISION;
  168: 	pp->clockdesc = DESCRIPTION;
  169: 	memcpy((char *)&pp->refid, REFID, 4);
  170: 	up->pollcnt = 2;
  171: 	up->polled = 0;		/* May not be needed... */
  172: 
  173: 	return (1);
  174: }
  175: 
  176: 
  177: /*
  178:  * zyfer_shutdown - shut down the clock
  179:  */
  180: static void
  181: zyfer_shutdown(
  182: 	int unit,
  183: 	struct peer *peer
  184: 	)
  185: {
  186: 	register struct zyferunit *up;
  187: 	struct refclockproc *pp;
  188: 
  189: 	pp = peer->procptr;
  190: 	up = (struct zyferunit *)pp->unitptr;
  191: 	io_closeclock(&pp->io);
  192: 	free(up);
  193: }
  194: 
  195: 
  196: /*
  197:  * zyfer_receive - receive data from the serial interface
  198:  */
  199: static void
  200: zyfer_receive(
  201: 	struct recvbuf *rbufp
  202: 	)
  203: {
  204: 	register struct zyferunit *up;
  205: 	struct refclockproc *pp;
  206: 	struct peer *peer;
  207: 	int tmode;		/* Time mode */
  208: 	int tfom;		/* Time Figure Of Merit */
  209: 	int omode;		/* Operation mode */
  210: 	u_char *p;
  211: #ifdef PPS
  212: 	struct ppsclockev ppsev;
  213: 	int request;
  214: #ifdef HAVE_CIOGETEV
  215:         request = CIOGETEV;
  216: #endif
  217: #ifdef HAVE_TIOCGPPSEV
  218:         request = TIOCGPPSEV;
  219: #endif
  220: #endif /* PPS */
  221: 
  222: 	peer = (struct peer *)rbufp->recv_srcclock;
  223: 	pp = peer->procptr;
  224: 	up = (struct zyferunit *)pp->unitptr;
  225: 	p = (u_char *) &rbufp->recv_space;
  226: 	/*
  227: 	 * If lencode is 0:
  228: 	 * - if *rbufp->recv_space is !
  229: 	 * - - call refclock_gtlin to get things going
  230: 	 * - else flush
  231: 	 * else stuff it on the end of lastcode
  232: 	 * If we don't have LENZYFER bytes
  233: 	 * - wait for more data
  234: 	 * Crack the beast, and if it's OK, process it.
  235: 	 *
  236: 	 * We use refclock_gtlin() because we might use LDISC_CLK.
  237: 	 *
  238: 	 * Under FreeBSD, we get the ! followed by two 14-byte packets.
  239: 	 */
  240: 
  241: 	if (pp->lencode >= LENZYFER)
  242: 		pp->lencode = 0;
  243: 
  244: 	if (!pp->lencode) {
  245: 		if (*p == '!')
  246: 			pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode,
  247: 						     BMAX, &pp->lastrec);
  248: 		else
  249: 			return;
  250: 	} else {
  251: 		memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length);
  252: 		pp->lencode += rbufp->recv_length;
  253: 		pp->a_lastcode[pp->lencode] = '\0';
  254: 	}
  255: 
  256: 	if (pp->lencode < LENZYFER)
  257: 		return;
  258: 
  259: 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
  260: 
  261: 	/*
  262: 	 * We get down to business, check the timecode format and decode
  263: 	 * its contents. If the timecode has invalid length or is not in
  264: 	 * proper format, we declare bad format and exit.
  265: 	 */
  266: 
  267: 	if (pp->lencode != LENZYFER) {
  268: 		refclock_report(peer, CEVNT_BADTIME);
  269: 		return;
  270: 	}
  271: 
  272: 	/*
  273: 	 * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
  274: 	 */
  275: 	if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
  276: 		   &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second,
  277: 		   &tmode, &tfom, &omode) != 8) {
  278: 		refclock_report(peer, CEVNT_BADREPLY);
  279: 		return;
  280: 	}
  281: 
  282: 	if (tmode != 2) {
  283: 		refclock_report(peer, CEVNT_BADTIME);
  284: 		return;
  285: 	}
  286: 
  287: 	/* Should we make sure tfom is 4? */
  288: 
  289: 	if (omode != 1) {
  290: 		pp->leap = LEAP_NOTINSYNC;
  291: 		return;
  292: 	}
  293: #ifdef PPS
  294: 	if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
  295: 		ppsev.tv.tv_sec += (u_int32) JAN_1970;
  296: 		TVTOTS(&ppsev.tv,&up->tstamp);
  297: 	}
  298: 	/* record the last ppsclock event time stamp */
  299: 	pp->lastrec = up->tstamp;
  300: #endif /* PPS */
  301: 	if (!refclock_process(pp)) {
  302: 		refclock_report(peer, CEVNT_BADTIME);
  303: 		return;
  304:         }
  305: 
  306: 	/*
  307: 	 * Good place for record_clock_stats()
  308: 	 */
  309: 	up->pollcnt = 2;
  310: 
  311: 	if (up->polled) {
  312: 		up->polled = 0;
  313: 		refclock_receive(peer);
  314: 	}
  315: }
  316: 
  317: 
  318: /*
  319:  * zyfer_poll - called by the transmit procedure
  320:  */
  321: static void
  322: zyfer_poll(
  323: 	int unit,
  324: 	struct peer *peer
  325: 	)
  326: {
  327: 	register struct zyferunit *up;
  328: 	struct refclockproc *pp;
  329: 
  330: 	/*
  331: 	 * We don't really do anything here, except arm the receiving
  332: 	 * side to capture a sample and check for timeouts.
  333: 	 */
  334: 	pp = peer->procptr;
  335: 	up = (struct zyferunit *)pp->unitptr;
  336: 	if (!up->pollcnt)
  337: 		refclock_report(peer, CEVNT_TIMEOUT);
  338: 	else
  339: 		up->pollcnt--;
  340: 	pp->polls++;
  341: 	up->polled = 1;
  342: }
  343: 
  344: #else
  345: int refclock_zyfer_bs;
  346: #endif /* REFCLOCK */

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