Annotation of embedaddon/ntp/ntpd/refclock_zyfer.c, revision 1.1
1.1 ! misho 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>