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>