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>