Annotation of embedaddon/ntp/ntpd/refclock_acts.c, revision 1.1

1.1     ! misho       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>