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