154359Sroberto/* 2182007Sroberto * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time 3182007Sroberto * Services 454359Sroberto */ 554359Sroberto#ifdef HAVE_CONFIG_H 654359Sroberto#include <config.h> 754359Sroberto#endif 854359Sroberto 954359Sroberto#if defined(REFCLOCK) && (defined(CLOCK_ACTS) || defined(CLOCK_PTBACTS)) 1054359Sroberto 1154359Sroberto#include "ntpd.h" 1254359Sroberto#include "ntp_io.h" 1354359Sroberto#include "ntp_unixtime.h" 1454359Sroberto#include "ntp_refclock.h" 1554359Sroberto#include "ntp_stdlib.h" 1654359Sroberto#include "ntp_control.h" 1754359Sroberto 1882498Sroberto#include <stdio.h> 1982498Sroberto#include <ctype.h> 2082498Sroberto#ifdef HAVE_SYS_IOCTL_H 2182498Sroberto# include <sys/ioctl.h> 2282498Sroberto#endif /* HAVE_SYS_IOCTL_H */ 2382498Sroberto 2454359Sroberto/* 25182007Sroberto * This driver supports the US (NIST, USNO) and European (PTB, NPL, 26182007Sroberto * etc.) modem time services, as well as Spectracom GPS and WWVB 27182007Sroberto * receivers connected via a modem. The driver periodically dials a 28182007Sroberto * number from a telephone list, receives the timecode data and 29182007Sroberto * calculates the local clock correction. It is designed primarily for 30182007Sroberto * use as backup when neither a radio clock nor connectivity to Internet 31182007Sroberto * time servers is available. 3254359Sroberto * 33182007Sroberto * This driver requires a modem with a Hayes-compatible command set and 34182007Sroberto * control over the modem data terminal ready (DTR) control line. The 35182007Sroberto * modem setup string is hard-coded in the driver and may require 36182007Sroberto * changes for nonstandard modems or special circumstances. For reasons 37182007Sroberto * unrelated to this driver, the data set ready (DSR) control line 38182007Sroberto * should not be set when this driver is first started. 3954359Sroberto * 40182007Sroberto * The calling program is initiated by setting fudge flag1, either 41182007Sroberto * manually or automatically. When flag1 is set, the calling program 42182007Sroberto * dials the first number in the phone command of the configuration 43182007Sroberto * file. If that call fails, the calling program dials the second number 44182007Sroberto * and so on. The number is specified by the Hayes ATDT prefix followed 45182007Sroberto * by the number itself, including the prefix and long-distance digits 46182007Sroberto * and delay code, if necessary. The flag1 is reset and the calling 47182007Sroberto * program terminated if (a) a valid clock update has been determined, 48182007Sroberto * (b) no more numbers remain in the list, (c) a device fault or timeout 49182007Sroberto * occurs or (d) fudge flag1 is reset manually. 5054359Sroberto * 51182007Sroberto * The driver is transparent to each of the modem time services and 52182007Sroberto * Spectracom radios. It selects the parsing algorithm depending on the 53182007Sroberto * message length. There is some hazard should the message be corrupted. 54182007Sroberto * However, the data format is checked carefully and only if all checks 55182007Sroberto * succeed is the message accepted. Corrupted lines are discarded 56182007Sroberto * without complaint. 5754359Sroberto * 58182007Sroberto * Fudge controls 5954359Sroberto * 60182007Sroberto * flag1 force a call in manual mode 61182007Sroberto * flag2 enable port locking (not verified) 62182007Sroberto * flag3 no modem; port is directly connected to device 63182007Sroberto * flag4 not used 6454359Sroberto * 65182007Sroberto * time1 offset adjustment (s) 6654359Sroberto * 67182007Sroberto * Ordinarily, the serial port is connected to a modem; however, it can 68182007Sroberto * be connected directly to a device or another computer for testing and 69182007Sroberto * calibration. In this case set fudge flag3 and the driver will send a 70182007Sroberto * single character 'T' at each poll event. In principle, fudge flag2 71182007Sroberto * enables port locking, allowing the modem to be shared when not in use 72182007Sroberto * by this driver. At least on Solaris with the current NTP I/O 73182007Sroberto * routines, this results only in lots of ugly error messages. 7454359Sroberto */ 7554359Sroberto/* 76182007Sroberto * National Institute of Science and Technology (NIST) 7754359Sroberto * 78182007Sroberto * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii) 7954359Sroberto * 80182007Sroberto * Data Format 81182007Sroberto * 8254359Sroberto * National Institute of Standards and Technology 8354359Sroberto * Telephone Time Service, Generator 3B 8454359Sroberto * Enter question mark "?" for HELP 8554359Sroberto * D L D 8654359Sroberto * MJD YR MO DA H M S ST S UT1 msADV <OTM> 87182007Sroberto * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF> 88182007Sroberto * ... 8954359Sroberto * 90182007Sroberto * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is 91182007Sroberto * the on-time markers echoed by the driver and used by NIST to measure 92182007Sroberto * and correct for the propagation delay. 9354359Sroberto * 94182007Sroberto * US Naval Observatory (USNO) 9554359Sroberto * 96182007Sroberto * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO) 9754359Sroberto * 98182007Sroberto * Data Format (two lines, repeating at one-second intervals) 9954359Sroberto * 100182007Sroberto * jjjjj nnn hhmmss UTC<CR><LF> 101182007Sroberto * *<CR><LF> 10254359Sroberto * 103182007Sroberto * jjjjj modified Julian day number (not used) 104182007Sroberto * nnn day of year 105182007Sroberto * hhmmss second of day 106182007Sroberto * * on-time marker for previous timecode 107182007Sroberto * ... 10854359Sroberto * 109182007Sroberto * USNO does not correct for the propagation delay. A fudge time1 of 110182007Sroberto * about .06 s is advisable. 11154359Sroberto * 112182007Sroberto * European Services (PTB, NPL, etc.) 11354359Sroberto * 114182007Sroberto * PTB: +49 531 512038 (Germany) 115182007Sroberto * NPL: 0906 851 6333 (UK only) 11654359Sroberto * 117182007Sroberto * Data format (see the documentation for phone numbers and formats.) 11854359Sroberto * 119182007Sroberto * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF> 12054359Sroberto * 121182007Sroberto * Spectracom GPS and WWVB Receivers 12254359Sroberto * 123182007Sroberto * If a modem is connected to a Spectracom receiver, this driver will 124182007Sroberto * call it up and retrieve the time in one of two formats. As this 125182007Sroberto * driver does not send anything, the radio will have to either be 126182007Sroberto * configured in continuous mode or be polled by another local driver. 12754359Sroberto */ 12854359Sroberto/* 12954359Sroberto * Interface definitions 13054359Sroberto */ 131182007Sroberto#define DEVICE "/dev/acts%d" /* device name and unit */ 132182007Sroberto#define SPEED232 B9600 /* uart speed (9600 baud) */ 13354359Sroberto#define PRECISION (-10) /* precision assumed (about 1 ms) */ 134182007Sroberto#define LOCKFILE "/var/spool/locks/LCK..cua%d" 135182007Sroberto#define DESCRIPTION "Automated Computer Time Service" /* WRU */ 136182007Sroberto#define REFID "NONE" /* default reference ID */ 137182007Sroberto#define MSGCNT 20 /* max message count */ 138182007Sroberto#define SMAX 256 /* max clockstats line length */ 139182007Sroberto 140182007Sroberto/* 141182007Sroberto * Calling program modes 142182007Sroberto */ 14354359Sroberto#define MODE_AUTO 0 /* automatic mode */ 14454359Sroberto#define MODE_BACKUP 1 /* backup mode */ 14554359Sroberto#define MODE_MANUAL 2 /* manual mode */ 14654359Sroberto 147182007Sroberto/* 148182007Sroberto * Service identifiers. 149182007Sroberto */ 150182007Sroberto#define REFACTS "NIST" /* NIST reference ID */ 151182007Sroberto#define LENACTS 50 /* NIST format */ 152182007Sroberto#define REFUSNO "USNO" /* USNO reference ID */ 153182007Sroberto#define LENUSNO 20 /* USNO */ 154182007Sroberto#define REFPTB "PTB\0" /* PTB/NPL reference ID */ 155182007Sroberto#define LENPTB 78 /* PTB/NPL format */ 156182007Sroberto#define REFWWVB "WWVB" /* WWVB reference ID */ 157182007Sroberto#define LENWWVB0 22 /* WWVB format 0 */ 158182007Sroberto#define LENWWVB2 24 /* WWVB format 2 */ 159182007Sroberto#define LF 0x0a /* ASCII LF */ 16054359Sroberto 16154359Sroberto/* 162182007Sroberto * Modem setup strings. These may have to be changed for some modems. 16354359Sroberto * 16454359Sroberto * AT command prefix 165182007Sroberto * B1 US answer tone 166182007Sroberto * &C0 disable carrier detect 16754359Sroberto * &D2 hang up and return to command mode on DTR transition 16854359Sroberto * E0 modem command echo disabled 16954359Sroberto * l1 set modem speaker volume to low level 170182007Sroberto * M1 speaker enabled until carrier detect 17154359Sroberto * Q0 return result codes 17254359Sroberto * V1 return result codes as English words 17354359Sroberto */ 174182007Sroberto#define MODEM_SETUP "ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */ 175182007Sroberto#define MODEM_HANGUP "ATH\r" /* modem disconnect */ 17654359Sroberto 17754359Sroberto/* 178182007Sroberto * Timeouts (all in seconds) 17954359Sroberto */ 180182007Sroberto#define SETUP 3 /* setup timeout */ 181182007Sroberto#define DTR 1 /* DTR timeout */ 182182007Sroberto#define ANSWER 60 /* answer timeout */ 183182007Sroberto#define CONNECT 20 /* first valid message timeout */ 184182007Sroberto#define TIMECODE 30 /* all valid messages timeout */ 18554359Sroberto 18654359Sroberto/* 187182007Sroberto * State machine codes 18854359Sroberto */ 189182007Sroberto#define S_IDLE 0 /* wait for poll */ 190182007Sroberto#define S_OK 1 /* wait for modem setup */ 191182007Sroberto#define S_DTR 2 /* wait for modem DTR */ 192182007Sroberto#define S_CONNECT 3 /* wait for answer*/ 193182007Sroberto#define S_FIRST 4 /* wait for first valid message */ 194182007Sroberto#define S_MSG 5 /* wait for all messages */ 195182007Sroberto#define S_CLOSE 6 /* wait after sending disconnect */ 19654359Sroberto 19754359Sroberto/* 19854359Sroberto * Unit control structure 19954359Sroberto */ 20054359Srobertostruct actsunit { 201182007Sroberto int unit; /* unit number */ 20254359Sroberto int state; /* the first one was Delaware */ 203182007Sroberto int timer; /* timeout counter */ 204182007Sroberto int retry; /* retry index */ 205182007Sroberto int msgcnt; /* count of messages received */ 206182007Sroberto l_fp tstamp; /* on-time timestamp */ 207182007Sroberto char *bufptr; /* buffer pointer */ 20854359Sroberto}; 20954359Sroberto 21054359Sroberto/* 21154359Sroberto * Function prototypes 21254359Sroberto */ 21354359Srobertostatic int acts_start P((int, struct peer *)); 21454359Srobertostatic void acts_shutdown P((int, struct peer *)); 21554359Srobertostatic void acts_receive P((struct recvbuf *)); 216182007Srobertostatic void acts_message P((struct peer *)); 217182007Srobertostatic void acts_timecode P((struct peer *, char *)); 21854359Srobertostatic void acts_poll P((int, struct peer *)); 21954359Srobertostatic void acts_timeout P((struct peer *)); 22054359Srobertostatic void acts_disc P((struct peer *)); 221182007Srobertostatic void acts_timer P((int, struct peer *)); 22254359Sroberto 22354359Sroberto/* 22454359Sroberto * Transfer vector (conditional structure name) 22554359Sroberto */ 226182007Srobertostruct refclock refclock_acts = { 22754359Sroberto acts_start, /* start up driver */ 22854359Sroberto acts_shutdown, /* shut down driver */ 22954359Sroberto acts_poll, /* transmit poll message */ 230182007Sroberto noentry, /* not used */ 231182007Sroberto noentry, /* not used */ 232182007Sroberto noentry, /* not used */ 233182007Sroberto acts_timer /* housekeeping timer */ 23454359Sroberto}; 23554359Sroberto 236182007Srobertostruct refclock refclock_ptb; 23754359Sroberto 23854359Sroberto/* 239182007Sroberto * Initialize data for processing 24054359Sroberto */ 24154359Srobertostatic int 24254359Srobertoacts_start ( 243182007Sroberto int unit, 24454359Sroberto struct peer *peer 24554359Sroberto ) 24654359Sroberto{ 247182007Sroberto struct actsunit *up; 24854359Sroberto struct refclockproc *pp; 24954359Sroberto 25054359Sroberto /* 251182007Sroberto * Allocate and initialize unit structure 25254359Sroberto */ 253182007Sroberto up = emalloc(sizeof(struct actsunit)); 254182007Sroberto if (up == NULL) 25554359Sroberto return (0); 25654359Sroberto 257182007Sroberto memset(up, 0, sizeof(struct actsunit)); 258182007Sroberto up->unit = unit; 25954359Sroberto pp = peer->procptr; 260182007Sroberto pp->unitptr = (caddr_t)up; 26154359Sroberto pp->io.clock_recv = acts_receive; 26254359Sroberto pp->io.srcclock = (caddr_t)peer; 26354359Sroberto pp->io.datalen = 0; 26454359Sroberto 26554359Sroberto /* 26654359Sroberto * Initialize miscellaneous variables 26754359Sroberto */ 26854359Sroberto peer->precision = PRECISION; 26954359Sroberto pp->clockdesc = DESCRIPTION; 27054359Sroberto memcpy((char *)&pp->refid, REFID, 4); 27154359Sroberto peer->sstclktype = CTL_SST_TS_TELEPHONE; 272182007Sroberto peer->flags &= ~FLAG_FIXPOLL; 273182007Sroberto up->bufptr = pp->a_lastcode; 27454359Sroberto return (1); 27554359Sroberto} 27654359Sroberto 27754359Sroberto 27854359Sroberto/* 27954359Sroberto * acts_shutdown - shut down the clock 28054359Sroberto */ 28154359Srobertostatic void 28254359Srobertoacts_shutdown ( 283182007Sroberto int unit, 28454359Sroberto struct peer *peer 28554359Sroberto ) 28654359Sroberto{ 287182007Sroberto struct actsunit *up; 28854359Sroberto struct refclockproc *pp; 28954359Sroberto 290182007Sroberto /* 291182007Sroberto * Warning: do this only when a call is not in progress. 292182007Sroberto */ 29354359Sroberto pp = peer->procptr; 29454359Sroberto up = (struct actsunit *)pp->unitptr; 29554359Sroberto free(up); 29654359Sroberto} 29754359Sroberto 29854359Sroberto 29954359Sroberto/* 30054359Sroberto * acts_receive - receive data from the serial interface 30154359Sroberto */ 30254359Srobertostatic void 30354359Srobertoacts_receive ( 30454359Sroberto struct recvbuf *rbufp 30554359Sroberto ) 30654359Sroberto{ 307182007Sroberto struct actsunit *up; 30854359Sroberto struct refclockproc *pp; 30954359Sroberto struct peer *peer; 310182007Sroberto char tbuf[BMAX]; 311182007Sroberto char *tptr; 312182007Sroberto 31354359Sroberto /* 314182007Sroberto * Initialize pointers and read the timecode and timestamp. Note 315182007Sroberto * we are in raw mode and victim of whatever the terminal 316182007Sroberto * interface kicks up; so, we have to reassemble messages from 317182007Sroberto * arbitrary fragments. Capture the timecode at the beginning of 318182007Sroberto * the message and at the '*' and '#' on-time characters. 31954359Sroberto */ 32054359Sroberto peer = (struct peer *)rbufp->recv_srcclock; 32154359Sroberto pp = peer->procptr; 32254359Sroberto up = (struct actsunit *)pp->unitptr; 323182007Sroberto pp->lencode = refclock_gtraw(rbufp, tbuf, BMAX - (up->bufptr - 324182007Sroberto pp->a_lastcode), &pp->lastrec); 325182007Sroberto for (tptr = tbuf; *tptr != '\0'; tptr++) { 326182007Sroberto if (*tptr == LF) { 327182007Sroberto if (up->bufptr == pp->a_lastcode) { 328182007Sroberto up->tstamp = pp->lastrec; 329182007Sroberto continue; 330182007Sroberto 331182007Sroberto } else { 332182007Sroberto *up->bufptr = '\0'; 333182007Sroberto acts_message(peer); 334182007Sroberto up->bufptr = pp->a_lastcode; 335182007Sroberto } 336182007Sroberto } else if (!iscntrl(*tptr)) { 337182007Sroberto *up->bufptr++ = *tptr; 338182007Sroberto if (*tptr == '*' || *tptr == '#') { 339182007Sroberto up->tstamp = pp->lastrec; 340182007Sroberto write(pp->io.fd, tptr, 1); 341182007Sroberto } 342182007Sroberto } 34354359Sroberto } 344182007Sroberto} 345182007Sroberto 346182007Sroberto 347182007Sroberto/* 348182007Sroberto * acts_message - process message 349182007Sroberto */ 350182007Srobertovoid 351182007Srobertoacts_message( 352182007Sroberto struct peer *peer 353182007Sroberto ) 354182007Sroberto{ 355182007Sroberto struct actsunit *up; 356182007Sroberto struct refclockproc *pp; 357182007Sroberto int dtr = TIOCM_DTR; 358182007Sroberto char tbuf[SMAX]; 35954359Sroberto#ifdef DEBUG 360182007Sroberto u_int modem; 361182007Sroberto#endif 362182007Sroberto 363182007Sroberto /* 364182007Sroberto * What to do depends on the state and the first token in the 365182007Sroberto * message. A NO token sends the message to the clockstats. 366182007Sroberto */ 367182007Sroberto pp = peer->procptr; 368182007Sroberto up = (struct actsunit *)pp->unitptr; 369182007Sroberto#ifdef DEBUG 370182007Sroberto ioctl(pp->io.fd, TIOCMGET, (char *)&modem); 371182007Sroberto sprintf(tbuf, "acts: %04x (%d %d) %lu %s", modem, up->state, 372182007Sroberto up->timer, strlen(pp->a_lastcode), pp->a_lastcode); 37354359Sroberto if (debug) 374182007Sroberto printf("%s\n", tbuf); 37554359Sroberto#endif 376182007Sroberto strncpy(tbuf, pp->a_lastcode, SMAX); 377182007Sroberto strtok(tbuf, " "); 378182007Sroberto if (strcmp(tbuf, "NO") == 0) 379182007Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 380182007Sroberto switch(up->state) { 38154359Sroberto 382182007Sroberto /* 383182007Sroberto * We are waiting for the OK response to the modem setup 384182007Sroberto * command. When this happens, raise DTR and dial the number 385182007Sroberto * followed by \r. 386182007Sroberto */ 387182007Sroberto case S_OK: 388182007Sroberto if (strcmp(tbuf, "OK") != 0) { 389182007Sroberto msyslog(LOG_ERR, "acts: setup error %s", 390182007Sroberto pp->a_lastcode); 391182007Sroberto acts_disc(peer); 392182007Sroberto return; 393182007Sroberto } 394182007Sroberto ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr); 395182007Sroberto up->state = S_DTR; 396182007Sroberto up->timer = DTR; 39754359Sroberto return; 39854359Sroberto 399182007Sroberto /* 400182007Sroberto * We are waiting for the call to be answered. All we care about 401182007Sroberto * here is token CONNECT. Send the message to the clockstats. 402182007Sroberto */ 403182007Sroberto case S_CONNECT: 404182007Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 405182007Sroberto if (strcmp(tbuf, "CONNECT") != 0) { 40654359Sroberto acts_disc(peer); 40754359Sroberto return; 40854359Sroberto } 409182007Sroberto up->state = S_FIRST; 410182007Sroberto up->timer = CONNECT; 41154359Sroberto return; 41254359Sroberto 413182007Sroberto /* 414182007Sroberto * We are waiting for a timecode. Pass it to the parser. 415182007Sroberto */ 416182007Sroberto case S_FIRST: 417182007Sroberto case S_MSG: 418182007Sroberto acts_timecode(peer, pp->a_lastcode); 419182007Sroberto break; 42054359Sroberto } 421182007Sroberto} 42254359Sroberto 423182007Sroberto/* 424182007Sroberto * acts_timecode - identify the service and parse the timecode message 425182007Sroberto */ 426182007Srobertovoid 427182007Srobertoacts_timecode( 428182007Sroberto struct peer *peer, /* peer structure pointer */ 429182007Sroberto char *str /* timecode string */ 430182007Sroberto ) 431182007Sroberto{ 432182007Sroberto struct actsunit *up; 433182007Sroberto struct refclockproc *pp; 434182007Sroberto int day; /* day of the month */ 435182007Sroberto int month; /* month of the year */ 436182007Sroberto u_long mjd; /* Modified Julian Day */ 437182007Sroberto double dut1; /* DUT adjustment */ 438182007Sroberto 439182007Sroberto u_int dst; /* ACTS daylight/standard time */ 440182007Sroberto u_int leap; /* ACTS leap indicator */ 441182007Sroberto double msADV; /* ACTS transmit advance (ms) */ 442182007Sroberto char utc[10]; /* ACTS timescale */ 443182007Sroberto char flag; /* ACTS on-time character (* or #) */ 444182007Sroberto 445182007Sroberto char synchar; /* WWVB synchronized indicator */ 446182007Sroberto char qualchar; /* WWVB quality indicator */ 447182007Sroberto char leapchar; /* WWVB leap indicator */ 448182007Sroberto char dstchar; /* WWVB daylight/savings indicator */ 449182007Sroberto int tz; /* WWVB timezone */ 450182007Sroberto 451182007Sroberto u_int leapmonth; /* PTB/NPL month of leap */ 452182007Sroberto char leapdir; /* PTB/NPL leap direction */ 453182007Sroberto 45454359Sroberto /* 455182007Sroberto * The parser selects the modem format based on the message 456182007Sroberto * length. Since the data are checked carefully, occasional 457182007Sroberto * errors due noise are forgivable. 45854359Sroberto */ 459182007Sroberto pp = peer->procptr; 460182007Sroberto up = (struct actsunit *)pp->unitptr; 461182007Sroberto pp->nsec = 0; 462182007Sroberto switch(strlen(str)) { 46354359Sroberto 46454359Sroberto /* 465182007Sroberto * For USNO format on-time character '*', which is on a line by 466182007Sroberto * itself. Be sure a timecode has been received. 46754359Sroberto */ 468182007Sroberto case 1: 469182007Sroberto if (*str == '*' && up->msgcnt > 0) 470182007Sroberto break; 471182007Sroberto 47254359Sroberto return; 473182007Sroberto 47454359Sroberto /* 475182007Sroberto * ACTS format: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa 476182007Sroberto * UTC(NIST) *" 47754359Sroberto */ 478182007Sroberto case LENACTS: 479182007Sroberto if (sscanf(str, 480182007Sroberto "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c", 481182007Sroberto &mjd, &pp->year, &month, &day, &pp->hour, 482182007Sroberto &pp->minute, &pp->second, &dst, &leap, &dut1, 483182007Sroberto &msADV, utc, &flag) != 13) { 484182007Sroberto refclock_report(peer, CEVNT_BADREPLY); 485182007Sroberto return; 486182007Sroberto } 487182007Sroberto 488182007Sroberto /* 489182007Sroberto * Wait until ACTS has calculated the roundtrip delay. 490182007Sroberto * We don't need to do anything, as ACTS adjusts the 491182007Sroberto * on-time epoch. 492182007Sroberto */ 493182007Sroberto if (flag != '#') 494182007Sroberto return; 495182007Sroberto 496182007Sroberto pp->day = ymd2yd(pp->year, month, day); 497182007Sroberto pp->leap = LEAP_NOWARNING; 498182007Sroberto if (leap == 1) 499182007Sroberto pp->leap = LEAP_ADDSECOND; 500182007Sroberto else if (pp->leap == 2) 501182007Sroberto pp->leap = LEAP_DELSECOND; 502182007Sroberto memcpy(&pp->refid, REFACTS, 4); 503182007Sroberto if (up->msgcnt == 0) 504182007Sroberto record_clock_stats(&peer->srcadr, str); 505182007Sroberto up->msgcnt++; 506182007Sroberto break; 507182007Sroberto 50854359Sroberto /* 509182007Sroberto * USNO format: "jjjjj nnn hhmmss UTC" 51054359Sroberto */ 511182007Sroberto case LENUSNO: 512182007Sroberto if (sscanf(str, "%5ld %3d %2d%2d%2d %3s", 513182007Sroberto &mjd, &pp->day, &pp->hour, &pp->minute, 514182007Sroberto &pp->second, utc) != 6) { 515182007Sroberto refclock_report(peer, CEVNT_BADREPLY); 516182007Sroberto return; 517182007Sroberto } 51854359Sroberto 519182007Sroberto /* 520182007Sroberto * Wait for the on-time character, which follows in a 521182007Sroberto * separate message. There is no provision for leap 522182007Sroberto * warning. 523182007Sroberto */ 524182007Sroberto pp->leap = LEAP_NOWARNING; 525182007Sroberto memcpy(&pp->refid, REFUSNO, 4); 526182007Sroberto if (up->msgcnt == 0) 527182007Sroberto record_clock_stats(&peer->srcadr, str); 528182007Sroberto up->msgcnt++; 529182007Sroberto return; 530182007Sroberto 53154359Sroberto /* 532182007Sroberto * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ" 53354359Sroberto */ 534182007Sroberto case LENPTB: 535182007Sroberto if (sscanf(str, 536182007Sroberto "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c", 537182007Sroberto &pp->second, &pp->year, &month, &day, &pp->hour, 538182007Sroberto &pp->minute, &mjd, &dut1, &leapdir, &leapmonth, 539182007Sroberto &msADV, &flag) != 12) { 540182007Sroberto refclock_report(peer, CEVNT_BADREPLY); 541182007Sroberto return; 542182007Sroberto } 543182007Sroberto pp->leap = LEAP_NOWARNING; 544182007Sroberto if (leapmonth == month) { 545182007Sroberto if (leapdir == '+') 546182007Sroberto pp->leap = LEAP_ADDSECOND; 547182007Sroberto else if (leapdir == '-') 548182007Sroberto pp->leap = LEAP_DELSECOND; 549182007Sroberto } 550182007Sroberto pp->day = ymd2yd(pp->year, month, day); 551182007Sroberto memcpy(&pp->refid, REFPTB, 4); 552182007Sroberto if (up->msgcnt == 0) 553182007Sroberto record_clock_stats(&peer->srcadr, str); 554182007Sroberto up->msgcnt++; 555182007Sroberto break; 55682498Sroberto 557182007Sroberto 55882498Sroberto /* 559182007Sroberto * WWVB format 0: "I ddd hh:mm:ss DTZ=nn" 56082498Sroberto */ 561182007Sroberto case LENWWVB0: 562182007Sroberto if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d", 563182007Sroberto &synchar, &pp->day, &pp->hour, &pp->minute, 564182007Sroberto &pp->second, &dstchar, &tz) != 7) { 565182007Sroberto refclock_report(peer, CEVNT_BADREPLY); 56654359Sroberto return; 56754359Sroberto } 568182007Sroberto pp->leap = LEAP_NOWARNING; 569182007Sroberto if (synchar != ' ') 570182007Sroberto pp->leap = LEAP_NOTINSYNC; 571182007Sroberto memcpy(&pp->refid, REFWWVB, 4); 572182007Sroberto if (up->msgcnt == 0) 573182007Sroberto record_clock_stats(&peer->srcadr, str); 574182007Sroberto up->msgcnt++; 575182007Sroberto break; 576182007Sroberto 577182007Sroberto /* 578182007Sroberto * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD" 579182007Sroberto */ 580182007Sroberto case LENWWVB2: 581182007Sroberto if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c", 582182007Sroberto &synchar, &qualchar, &pp->year, &pp->day, 583182007Sroberto &pp->hour, &pp->minute, &pp->second, &pp->nsec, 584182007Sroberto &dstchar, &leapchar, &dstchar) != 11) { 585182007Sroberto refclock_report(peer, CEVNT_BADREPLY); 58654359Sroberto return; 58754359Sroberto } 588182007Sroberto pp->nsec *= 1000000; 589182007Sroberto pp->leap = LEAP_NOWARNING; 590182007Sroberto if (synchar != ' ') 591182007Sroberto pp->leap = LEAP_NOTINSYNC; 592182007Sroberto else if (leapchar == 'L') 593182007Sroberto pp->leap = LEAP_ADDSECOND; 594182007Sroberto memcpy(&pp->refid, REFWWVB, 4); 595182007Sroberto if (up->msgcnt == 0) 596182007Sroberto record_clock_stats(&peer->srcadr, str); 597182007Sroberto up->msgcnt++; 598182007Sroberto break; 59954359Sroberto 60054359Sroberto /* 601182007Sroberto * None of the above. Just forget about it and wait for the next 602182007Sroberto * message or timeout. 60354359Sroberto */ 604182007Sroberto default: 605182007Sroberto return; 60654359Sroberto } 60754359Sroberto 60854359Sroberto /* 609182007Sroberto * We have a valid timecode. The fudge time1 value is added to 610182007Sroberto * each sample by the main line routines. Note that in current 611182007Sroberto * telephone networks the propatation time can be different for 612182007Sroberto * each call and can reach 200 ms for some calls. 61354359Sroberto */ 614182007Sroberto peer->refid = pp->refid; 615182007Sroberto pp->lastrec = up->tstamp; 616182007Sroberto if (!refclock_process(pp)) { 617182007Sroberto refclock_report(peer, CEVNT_BADTIME); 618182007Sroberto return; 619182007Sroberto } 620132451Sroberto pp->lastref = pp->lastrec; 621182007Sroberto if (peer->disp > MAXDISTANCE) 622182007Sroberto refclock_receive(peer); 623182007Sroberto if (up->state != S_MSG) { 624182007Sroberto up->state = S_MSG; 625182007Sroberto up->timer = TIMECODE; 626182007Sroberto } 62754359Sroberto} 62854359Sroberto 62954359Sroberto 63054359Sroberto/* 63154359Sroberto * acts_poll - called by the transmit routine 63254359Sroberto */ 63354359Srobertostatic void 63454359Srobertoacts_poll ( 635182007Sroberto int unit, 63654359Sroberto struct peer *peer 63754359Sroberto ) 63854359Sroberto{ 639182007Sroberto struct actsunit *up; 64054359Sroberto struct refclockproc *pp; 64154359Sroberto 64254359Sroberto /* 643182007Sroberto * This routine is called at every system poll. All it does is 644182007Sroberto * set flag1 under certain conditions. The real work is done by 645182007Sroberto * the timeout routine and state machine. 64654359Sroberto */ 64754359Sroberto pp = peer->procptr; 64854359Sroberto up = (struct actsunit *)pp->unitptr; 649182007Sroberto switch (peer->ttl) { 65054359Sroberto 651182007Sroberto /* 652182007Sroberto * In manual mode the calling program is activated by the ntpdc 653182007Sroberto * program using the enable flag (fudge flag1), either manually 654182007Sroberto * or by a cron job. 655182007Sroberto */ 656182007Sroberto case MODE_MANUAL: 657182007Sroberto /* fall through */ 658182007Sroberto break; 659182007Sroberto 660182007Sroberto /* 661182007Sroberto * In automatic mode the calling program runs continuously at 662182007Sroberto * intervals determined by the poll event or specified timeout. 663182007Sroberto */ 664182007Sroberto case MODE_AUTO: 66554359Sroberto pp->sloppyclockflag |= CLK_FLAG1; 666182007Sroberto break; 667182007Sroberto 668182007Sroberto /* 669182007Sroberto * In backup mode the calling program runs continuously as long 670182007Sroberto * as either no peers are available or this peer is selected. 671182007Sroberto */ 672182007Sroberto case MODE_BACKUP: 673182007Sroberto if (sys_peer == NULL || sys_peer == peer) 674182007Sroberto pp->sloppyclockflag |= CLK_FLAG1; 675182007Sroberto break; 67654359Sroberto } 67754359Sroberto} 67854359Sroberto 67954359Sroberto 68054359Sroberto/* 681182007Sroberto * acts_timer - called at one-second intervals 68254359Sroberto */ 68354359Srobertostatic void 684182007Srobertoacts_timer( 685182007Sroberto int unit, 68654359Sroberto struct peer *peer 68754359Sroberto ) 68854359Sroberto{ 689182007Sroberto struct actsunit *up; 69054359Sroberto struct refclockproc *pp; 69154359Sroberto 69254359Sroberto /* 693182007Sroberto * This routine implments a timeout which runs for a programmed 694182007Sroberto * interval. The counter is initialized by the state machine and 695182007Sroberto * counts down to zero. Upon reaching zero, the state machine is 696182007Sroberto * called. If flag1 is set while in S_IDLE state, force a 697182007Sroberto * timeout. 69854359Sroberto */ 69954359Sroberto pp = peer->procptr; 70054359Sroberto up = (struct actsunit *)pp->unitptr; 701182007Sroberto if (pp->sloppyclockflag & CLK_FLAG1 && up->state == S_IDLE) { 702182007Sroberto acts_timeout(peer); 70354359Sroberto return; 70454359Sroberto } 705182007Sroberto if (up->timer == 0) 706182007Sroberto return; 70754359Sroberto 708182007Sroberto up->timer--; 709182007Sroberto if (up->timer == 0) 710182007Sroberto acts_timeout(peer); 711182007Sroberto} 712182007Sroberto 713182007Sroberto 714182007Sroberto/* 715182007Sroberto * acts_timeout - called on timeout 716182007Sroberto */ 717182007Srobertostatic void 718182007Srobertoacts_timeout( 719182007Sroberto struct peer *peer 720182007Sroberto ) 721182007Sroberto{ 722182007Sroberto struct actsunit *up; 723182007Sroberto struct refclockproc *pp; 724182007Sroberto int fd; 725182007Sroberto char device[20]; 726182007Sroberto char lockfile[128], pidbuf[8]; 727182007Sroberto char tbuf[BMAX]; 728182007Sroberto 729182007Sroberto /* 730182007Sroberto * The state machine is driven by messages from the modem, when 731182007Sroberto * first stated and at timeout. 732182007Sroberto */ 733182007Sroberto pp = peer->procptr; 734182007Sroberto up = (struct actsunit *)pp->unitptr; 735182007Sroberto pp->sloppyclockflag &= ~CLK_FLAG1; 736182007Sroberto if (sys_phone[up->retry] == NULL && !(pp->sloppyclockflag & 737182007Sroberto CLK_FLAG3)) { 738182007Sroberto msyslog(LOG_ERR, "acts: no phones"); 739182007Sroberto return; 740182007Sroberto } 741182007Sroberto switch(up->state) { 742182007Sroberto 743182007Sroberto /* 744182007Sroberto * System poll event. Lock the modem port and open the device. 745182007Sroberto */ 746182007Sroberto case S_IDLE: 747182007Sroberto 74854359Sroberto /* 749182007Sroberto * Lock the modem port. If busy, retry later. Note: if 750182007Sroberto * something fails between here and the close, the lock 751182007Sroberto * file may not be removed. 75254359Sroberto */ 753182007Sroberto if (pp->sloppyclockflag & CLK_FLAG2) { 754182007Sroberto sprintf(lockfile, LOCKFILE, up->unit); 755182007Sroberto fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, 756182007Sroberto 0644); 757182007Sroberto if (fd < 0) { 758182007Sroberto msyslog(LOG_ERR, "acts: port busy"); 759182007Sroberto return; 760182007Sroberto } 761182007Sroberto sprintf(pidbuf, "%d\n", (u_int)getpid()); 762182007Sroberto write(fd, pidbuf, strlen(pidbuf)); 763182007Sroberto close(fd); 764182007Sroberto } 76554359Sroberto 76654359Sroberto /* 767182007Sroberto * Open the device in raw mode and link the I/O. 76854359Sroberto */ 769182007Sroberto if (!pp->io.fd) { 770182007Sroberto sprintf(device, DEVICE, up->unit); 771182007Sroberto fd = refclock_open(device, SPEED232, 772182007Sroberto LDISC_ACTS | LDISC_RAW | LDISC_REMOTE); 773182007Sroberto if (fd == 0) { 774182007Sroberto return; 775182007Sroberto } 776182007Sroberto pp->io.fd = fd; 777182007Sroberto if (!io_addclock(&pp->io)) { 778182007Sroberto msyslog(LOG_ERR, 779182007Sroberto "acts: addclock fails"); 780182007Sroberto close(fd); 781182007Sroberto pp->io.fd = 0; 782182007Sroberto return; 783182007Sroberto } 784182007Sroberto } 78554359Sroberto 78654359Sroberto /* 787182007Sroberto * If the port is directly connected to the device, skip 788182007Sroberto * the modem business and send 'T' for Spectrabum. 78954359Sroberto */ 790182007Sroberto if (pp->sloppyclockflag & CLK_FLAG3) { 791182007Sroberto if (write(pp->io.fd, "T", 1) < 0) { 792182007Sroberto msyslog(LOG_ERR, "acts: write %m"); 793182007Sroberto return; 79454359Sroberto } 795182007Sroberto up->state = S_FIRST; 796182007Sroberto up->timer = CONNECT; 797182007Sroberto return; 79854359Sroberto } 79954359Sroberto 800182007Sroberto /* 801182007Sroberto * Initialize the modem. This works with Hayes commands. 802182007Sroberto */ 803182007Sroberto#ifdef DEBUG 804182007Sroberto if (debug) 805182007Sroberto printf("acts: setup %s\n", MODEM_SETUP); 806182007Sroberto#endif 807182007Sroberto if (write(pp->io.fd, MODEM_SETUP, strlen(MODEM_SETUP)) < 808182007Sroberto 0) { 809182007Sroberto msyslog(LOG_ERR, "acts: write %m"); 810182007Sroberto return; 811182007Sroberto } 812182007Sroberto up->state = S_OK; 813182007Sroberto up->timer = SETUP; 814182007Sroberto return; 81554359Sroberto 81654359Sroberto /* 817182007Sroberto * In OK state the modem did not respond to setup. 81854359Sroberto */ 819182007Sroberto case S_OK: 820182007Sroberto msyslog(LOG_ERR, "acts: no modem"); 821182007Sroberto break; 82254359Sroberto 82354359Sroberto /* 824182007Sroberto * In DTR state we are waiting for the modem to settle down 825182007Sroberto * before hammering it with a dial command. 82654359Sroberto */ 827182007Sroberto case S_DTR: 828182007Sroberto sprintf(tbuf, "DIAL #%d %s", up->retry, 829182007Sroberto sys_phone[up->retry]); 830182007Sroberto record_clock_stats(&peer->srcadr, tbuf); 83154359Sroberto#ifdef DEBUG 83254359Sroberto if (debug) 833182007Sroberto printf("%s\n", tbuf); 83454359Sroberto#endif 835182007Sroberto write(pp->io.fd, sys_phone[up->retry], 836182007Sroberto strlen(sys_phone[up->retry])); 837182007Sroberto write(pp->io.fd, "\r", 1); 838182007Sroberto up->state = S_CONNECT; 839182007Sroberto up->timer = ANSWER; 84054359Sroberto return; 84154359Sroberto 84254359Sroberto /* 843182007Sroberto * In CONNECT state the call did not complete. 84454359Sroberto */ 845182007Sroberto case S_CONNECT: 846182007Sroberto msyslog(LOG_ERR, "acts: no answer"); 847182007Sroberto break; 84854359Sroberto 849182007Sroberto /* 850182007Sroberto * In FIRST state no messages were received. 851182007Sroberto */ 852182007Sroberto case S_FIRST: 853182007Sroberto msyslog(LOG_ERR, "acts: no messages"); 854182007Sroberto break; 85554359Sroberto 85654359Sroberto /* 857182007Sroberto * In CLOSE state hangup is complete. Close the doors and 858182007Sroberto * windows and get some air. 85954359Sroberto */ 860182007Sroberto case S_CLOSE: 861182007Sroberto 862182007Sroberto /* 863182007Sroberto * Close the device and unlock a shared modem. 864182007Sroberto */ 865182007Sroberto if (pp->io.fd) { 866182007Sroberto io_closeclock(&pp->io); 867182007Sroberto close(pp->io.fd); 868182007Sroberto if (pp->sloppyclockflag & CLK_FLAG2) { 869182007Sroberto sprintf(lockfile, LOCKFILE, up->unit); 870182007Sroberto unlink(lockfile); 871182007Sroberto } 872182007Sroberto pp->io.fd = 0; 873182007Sroberto } 874182007Sroberto 875182007Sroberto /* 876182007Sroberto * If messages were received, fold the tent and wait for 877182007Sroberto * the next poll. If no messages and there are more 878182007Sroberto * numbers to dial, retry after a short wait. 879182007Sroberto */ 880182007Sroberto up->bufptr = pp->a_lastcode; 881182007Sroberto up->timer = 0; 882182007Sroberto up->state = S_IDLE; 883182007Sroberto if ( up->msgcnt == 0) { 884182007Sroberto up->retry++; 885182007Sroberto if (sys_phone[up->retry] == NULL) 886182007Sroberto up->retry = 0; 887182007Sroberto else 888182007Sroberto up->timer = SETUP; 889182007Sroberto } else { 890182007Sroberto up->retry = 0; 891182007Sroberto } 892182007Sroberto up->msgcnt = 0; 893182007Sroberto return; 89454359Sroberto } 895182007Sroberto acts_disc(peer); 89654359Sroberto} 89754359Sroberto 89854359Sroberto 89954359Sroberto/* 900182007Sroberto * acts_disc - disconnect the call and clean the place up. 90154359Sroberto */ 902182007Srobertostatic void 903182007Srobertoacts_disc ( 904182007Sroberto struct peer *peer 90554359Sroberto ) 90654359Sroberto{ 907182007Sroberto struct actsunit *up; 90854359Sroberto struct refclockproc *pp; 909182007Sroberto int dtr = TIOCM_DTR; 91054359Sroberto 91154359Sroberto /* 912182007Sroberto * We get here if the call terminated successfully or if an 913182007Sroberto * error occured. If the median filter has something in it,feed 914182007Sroberto * the data to the clock filter. If a modem port, drop DTR to 915182007Sroberto * force command mode and send modem hangup. 91654359Sroberto */ 91754359Sroberto pp = peer->procptr; 91854359Sroberto up = (struct actsunit *)pp->unitptr; 919182007Sroberto if (up->msgcnt > 0) 920182007Sroberto refclock_receive(peer); 921182007Sroberto if (!(pp->sloppyclockflag & CLK_FLAG3)) { 922182007Sroberto ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr); 923182007Sroberto write(pp->io.fd, MODEM_HANGUP, strlen(MODEM_HANGUP)); 924182007Sroberto } 925182007Sroberto up->timer = SETUP; 926182007Sroberto up->state = S_CLOSE; 92754359Sroberto} 92854359Sroberto 92954359Sroberto#else 93054359Srobertoint refclock_acts_bs; 93154359Sroberto#endif /* REFCLOCK */ 932