/contrib/ntp/ntpd/refclock_acts.c
https://bitbucket.org/freebsd/freebsd-head/ · C · 931 lines · 526 code · 77 blank · 328 comment · 110 complexity · 17742ab2a6ffd85797f4e173885fc332 MD5 · raw file
- /*
- * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
- * Services
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #if defined(REFCLOCK) && (defined(CLOCK_ACTS) || defined(CLOCK_PTBACTS))
- #include "ntpd.h"
- #include "ntp_io.h"
- #include "ntp_unixtime.h"
- #include "ntp_refclock.h"
- #include "ntp_stdlib.h"
- #include "ntp_control.h"
- #include <stdio.h>
- #include <ctype.h>
- #ifdef HAVE_SYS_IOCTL_H
- # include <sys/ioctl.h>
- #endif /* HAVE_SYS_IOCTL_H */
- /*
- * This driver supports the US (NIST, USNO) and European (PTB, NPL,
- * etc.) modem time services, as well as Spectracom GPS and WWVB
- * receivers connected via a modem. The driver periodically dials a
- * number from a telephone list, receives the timecode data and
- * calculates the local clock correction. It is designed primarily for
- * use as backup when neither a radio clock nor connectivity to Internet
- * time servers is available.
- *
- * This driver requires a modem with a Hayes-compatible command set and
- * control over the modem data terminal ready (DTR) control line. The
- * modem setup string is hard-coded in the driver and may require
- * changes for nonstandard modems or special circumstances. For reasons
- * unrelated to this driver, the data set ready (DSR) control line
- * should not be set when this driver is first started.
- *
- * The calling program is initiated by setting fudge flag1, either
- * manually or automatically. When flag1 is set, the calling program
- * dials the first number in the phone command of the configuration
- * file. If that call fails, the calling program dials the second number
- * and so on. The number is specified by the Hayes ATDT prefix followed
- * by the number itself, including the prefix and long-distance digits
- * and delay code, if necessary. The flag1 is reset and the calling
- * program terminated if (a) a valid clock update has been determined,
- * (b) no more numbers remain in the list, (c) a device fault or timeout
- * occurs or (d) fudge flag1 is reset manually.
- *
- * The driver is transparent to each of the modem time services and
- * Spectracom radios. It selects the parsing algorithm depending on the
- * message length. There is some hazard should the message be corrupted.
- * However, the data format is checked carefully and only if all checks
- * succeed is the message accepted. Corrupted lines are discarded
- * without complaint.
- *
- * Fudge controls
- *
- * flag1 force a call in manual mode
- * flag2 enable port locking (not verified)
- * flag3 no modem; port is directly connected to device
- * flag4 not used
- *
- * time1 offset adjustment (s)
- *
- * Ordinarily, the serial port is connected to a modem; however, it can
- * be connected directly to a device or another computer for testing and
- * calibration. In this case set fudge flag3 and the driver will send a
- * single character 'T' at each poll event. In principle, fudge flag2
- * enables port locking, allowing the modem to be shared when not in use
- * by this driver. At least on Solaris with the current NTP I/O
- * routines, this results only in lots of ugly error messages.
- */
- /*
- * National Institute of Science and Technology (NIST)
- *
- * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
- *
- * Data Format
- *
- * National Institute of Standards and Technology
- * Telephone Time Service, Generator 3B
- * Enter question mark "?" for HELP
- * D L D
- * MJD YR MO DA H M S ST S UT1 msADV <OTM>
- * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF>
- * ...
- *
- * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is
- * the on-time markers echoed by the driver and used by NIST to measure
- * and correct for the propagation delay.
- *
- * US Naval Observatory (USNO)
- *
- * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
- *
- * Data Format (two lines, repeating at one-second intervals)
- *
- * jjjjj nnn hhmmss UTC<CR><LF>
- * *<CR><LF>
- *
- * jjjjj modified Julian day number (not used)
- * nnn day of year
- * hhmmss second of day
- * * on-time marker for previous timecode
- * ...
- *
- * USNO does not correct for the propagation delay. A fudge time1 of
- * about .06 s is advisable.
- *
- * European Services (PTB, NPL, etc.)
- *
- * PTB: +49 531 512038 (Germany)
- * NPL: 0906 851 6333 (UK only)
- *
- * Data format (see the documentation for phone numbers and formats.)
- *
- * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF>
- *
- * Spectracom GPS and WWVB Receivers
- *
- * If a modem is connected to a Spectracom receiver, this driver will
- * call it up and retrieve the time in one of two formats. As this
- * driver does not send anything, the radio will have to either be
- * configured in continuous mode or be polled by another local driver.
- */
- /*
- * Interface definitions
- */
- #define DEVICE "/dev/acts%d" /* device name and unit */
- #define SPEED232 B9600 /* uart speed (9600 baud) */
- #define PRECISION (-10) /* precision assumed (about 1 ms) */
- #define LOCKFILE "/var/spool/locks/LCK..cua%d"
- #define DESCRIPTION "Automated Computer Time Service" /* WRU */
- #define REFID "NONE" /* default reference ID */
- #define MSGCNT 20 /* max message count */
- #define SMAX 256 /* max clockstats line length */
- /*
- * Calling program modes
- */
- #define MODE_AUTO 0 /* automatic mode */
- #define MODE_BACKUP 1 /* backup mode */
- #define MODE_MANUAL 2 /* manual mode */
- /*
- * Service identifiers.
- */
- #define REFACTS "NIST" /* NIST reference ID */
- #define LENACTS 50 /* NIST format */
- #define REFUSNO "USNO" /* USNO reference ID */
- #define LENUSNO 20 /* USNO */
- #define REFPTB "PTB\0" /* PTB/NPL reference ID */
- #define LENPTB 78 /* PTB/NPL format */
- #define REFWWVB "WWVB" /* WWVB reference ID */
- #define LENWWVB0 22 /* WWVB format 0 */
- #define LENWWVB2 24 /* WWVB format 2 */
- #define LF 0x0a /* ASCII LF */
- /*
- * Modem setup strings. These may have to be changed for some modems.
- *
- * AT command prefix
- * B1 US answer tone
- * &C0 disable carrier detect
- * &D2 hang up and return to command mode on DTR transition
- * E0 modem command echo disabled
- * l1 set modem speaker volume to low level
- * M1 speaker enabled until carrier detect
- * Q0 return result codes
- * V1 return result codes as English words
- */
- #define MODEM_SETUP "ATB1&C0&D2E0L1M1Q0V1\r" /* modem setup */
- #define MODEM_HANGUP "ATH\r" /* modem disconnect */
- /*
- * Timeouts (all in seconds)
- */
- #define SETUP 3 /* setup timeout */
- #define DTR 1 /* DTR timeout */
- #define ANSWER 60 /* answer timeout */
- #define CONNECT 20 /* first valid message timeout */
- #define TIMECODE 30 /* all valid messages timeout */
- /*
- * State machine codes
- */
- #define S_IDLE 0 /* wait for poll */
- #define S_OK 1 /* wait for modem setup */
- #define S_DTR 2 /* wait for modem DTR */
- #define S_CONNECT 3 /* wait for answer*/
- #define S_FIRST 4 /* wait for first valid message */
- #define S_MSG 5 /* wait for all messages */
- #define S_CLOSE 6 /* wait after sending disconnect */
- /*
- * Unit control structure
- */
- struct actsunit {
- int unit; /* unit number */
- int state; /* the first one was Delaware */
- int timer; /* timeout counter */
- int retry; /* retry index */
- int msgcnt; /* count of messages received */
- l_fp tstamp; /* on-time timestamp */
- char *bufptr; /* buffer pointer */
- };
- /*
- * Function prototypes
- */
- static int acts_start P((int, struct peer *));
- static void acts_shutdown P((int, struct peer *));
- static void acts_receive P((struct recvbuf *));
- static void acts_message P((struct peer *));
- static void acts_timecode P((struct peer *, char *));
- static void acts_poll P((int, struct peer *));
- static void acts_timeout P((struct peer *));
- static void acts_disc P((struct peer *));
- static void acts_timer P((int, struct peer *));
- /*
- * Transfer vector (conditional structure name)
- */
- struct refclock refclock_acts = {
- acts_start, /* start up driver */
- acts_shutdown, /* shut down driver */
- acts_poll, /* transmit poll message */
- noentry, /* not used */
- noentry, /* not used */
- noentry, /* not used */
- acts_timer /* housekeeping timer */
- };
- struct refclock refclock_ptb;
- /*
- * Initialize data for processing
- */
- static int
- acts_start (
- int unit,
- struct peer *peer
- )
- {
- struct actsunit *up;
- struct refclockproc *pp;
- /*
- * Allocate and initialize unit structure
- */
- up = emalloc(sizeof(struct actsunit));
- if (up == NULL)
- return (0);
- memset(up, 0, sizeof(struct actsunit));
- up->unit = unit;
- pp = peer->procptr;
- pp->unitptr = (caddr_t)up;
- pp->io.clock_recv = acts_receive;
- pp->io.srcclock = (caddr_t)peer;
- pp->io.datalen = 0;
- /*
- * Initialize miscellaneous variables
- */
- peer->precision = PRECISION;
- pp->clockdesc = DESCRIPTION;
- memcpy((char *)&pp->refid, REFID, 4);
- peer->sstclktype = CTL_SST_TS_TELEPHONE;
- peer->flags &= ~FLAG_FIXPOLL;
- up->bufptr = pp->a_lastcode;
- return (1);
- }
- /*
- * acts_shutdown - shut down the clock
- */
- static void
- acts_shutdown (
- int unit,
- struct peer *peer
- )
- {
- struct actsunit *up;
- struct refclockproc *pp;
- /*
- * Warning: do this only when a call is not in progress.
- */
- pp = peer->procptr;
- up = (struct actsunit *)pp->unitptr;
- free(up);
- }
- /*
- * acts_receive - receive data from the serial interface
- */
- static void
- acts_receive (
- struct recvbuf *rbufp
- )
- {
- struct actsunit *up;
- struct refclockproc *pp;
- struct peer *peer;
- char tbuf[BMAX];
- char *tptr;
- /*
- * Initialize pointers and read the timecode and timestamp. Note
- * we are in raw mode and victim of whatever the terminal
- * interface kicks up; so, we have to reassemble messages from
- * arbitrary fragments. Capture the timecode at the beginning of
- * the message and at the '*' and '#' on-time characters.
- */
- peer = (struct peer *)rbufp->recv_srcclock;
- pp = peer->procptr;
- up = (struct actsunit *)pp->unitptr;
- pp->lencode = refclock_gtraw(rbufp, tbuf, BMAX - (up->bufptr -
- pp->a_lastcode), &pp->lastrec);
- for (tptr = tbuf; *tptr != '\0'; tptr++) {
- if (*tptr == LF) {
- if (up->bufptr == pp->a_lastcode) {
- up->tstamp = pp->lastrec;
- continue;
- } else {
- *up->bufptr = '\0';
- acts_message(peer);
- up->bufptr = pp->a_lastcode;
- }
- } else if (!iscntrl(*tptr)) {
- *up->bufptr++ = *tptr;
- if (*tptr == '*' || *tptr == '#') {
- up->tstamp = pp->lastrec;
- write(pp->io.fd, tptr, 1);
- }
- }
- }
- }
- /*
- * acts_message - process message
- */
- void
- acts_message(
- struct peer *peer
- )
- {
- struct actsunit *up;
- struct refclockproc *pp;
- int dtr = TIOCM_DTR;
- char tbuf[SMAX];
- #ifdef DEBUG
- u_int modem;
- #endif
- /*
- * What to do depends on the state and the first token in the
- * message. A NO token sends the message to the clockstats.
- */
- pp = peer->procptr;
- up = (struct actsunit *)pp->unitptr;
- #ifdef DEBUG
- ioctl(pp->io.fd, TIOCMGET, (char *)&modem);
- sprintf(tbuf, "acts: %04x (%d %d) %lu %s", modem, up->state,
- up->timer, strlen(pp->a_lastcode), pp->a_lastcode);
- if (debug)
- printf("%s\n", tbuf);
- #endif
- strncpy(tbuf, pp->a_lastcode, SMAX);
- strtok(tbuf, " ");
- if (strcmp(tbuf, "NO") == 0)
- record_clock_stats(&peer->srcadr, pp->a_lastcode);
- switch(up->state) {
- /*
- * We are waiting for the OK response to the modem setup
- * command. When this happens, raise DTR and dial the number
- * followed by \r.
- */
- case S_OK:
- if (strcmp(tbuf, "OK") != 0) {
- msyslog(LOG_ERR, "acts: setup error %s",
- pp->a_lastcode);
- acts_disc(peer);
- return;
- }
- ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr);
- up->state = S_DTR;
- up->timer = DTR;
- return;
- /*
- * We are waiting for the call to be answered. All we care about
- * here is token CONNECT. Send the message to the clockstats.
- */
- case S_CONNECT:
- record_clock_stats(&peer->srcadr, pp->a_lastcode);
- if (strcmp(tbuf, "CONNECT") != 0) {
- acts_disc(peer);
- return;
- }
- up->state = S_FIRST;
- up->timer = CONNECT;
- return;
- /*
- * We are waiting for a timecode. Pass it to the parser.
- */
- case S_FIRST:
- case S_MSG:
- acts_timecode(peer, pp->a_lastcode);
- break;
- }
- }
- /*
- * acts_timecode - identify the service and parse the timecode message
- */
- void
- acts_timecode(
- struct peer *peer, /* peer structure pointer */
- char *str /* timecode string */
- )
- {
- struct actsunit *up;
- struct refclockproc *pp;
- int day; /* day of the month */
- int month; /* month of the year */
- u_long mjd; /* Modified Julian Day */
- double dut1; /* DUT adjustment */
- u_int dst; /* ACTS daylight/standard time */
- u_int leap; /* ACTS leap indicator */
- double msADV; /* ACTS transmit advance (ms) */
- char utc[10]; /* ACTS timescale */
- char flag; /* ACTS on-time character (* or #) */
- char synchar; /* WWVB synchronized indicator */
- char qualchar; /* WWVB quality indicator */
- char leapchar; /* WWVB leap indicator */
- char dstchar; /* WWVB daylight/savings indicator */
- int tz; /* WWVB timezone */
- u_int leapmonth; /* PTB/NPL month of leap */
- char leapdir; /* PTB/NPL leap direction */
- /*
- * The parser selects the modem format based on the message
- * length. Since the data are checked carefully, occasional
- * errors due noise are forgivable.
- */
- pp = peer->procptr;
- up = (struct actsunit *)pp->unitptr;
- pp->nsec = 0;
- switch(strlen(str)) {
- /*
- * For USNO format on-time character '*', which is on a line by
- * itself. Be sure a timecode has been received.
- */
- case 1:
- if (*str == '*' && up->msgcnt > 0)
- break;
- return;
-
- /*
- * ACTS format: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
- * UTC(NIST) *"
- */
- case LENACTS:
- if (sscanf(str,
- "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
- &mjd, &pp->year, &month, &day, &pp->hour,
- &pp->minute, &pp->second, &dst, &leap, &dut1,
- &msADV, utc, &flag) != 13) {
- refclock_report(peer, CEVNT_BADREPLY);
- return;
- }
- /*
- * Wait until ACTS has calculated the roundtrip delay.
- * We don't need to do anything, as ACTS adjusts the
- * on-time epoch.
- */
- if (flag != '#')
- return;
- pp->day = ymd2yd(pp->year, month, day);
- pp->leap = LEAP_NOWARNING;
- if (leap == 1)
- pp->leap = LEAP_ADDSECOND;
- else if (pp->leap == 2)
- pp->leap = LEAP_DELSECOND;
- memcpy(&pp->refid, REFACTS, 4);
- if (up->msgcnt == 0)
- record_clock_stats(&peer->srcadr, str);
- up->msgcnt++;
- break;
- /*
- * USNO format: "jjjjj nnn hhmmss UTC"
- */
- case LENUSNO:
- if (sscanf(str, "%5ld %3d %2d%2d%2d %3s",
- &mjd, &pp->day, &pp->hour, &pp->minute,
- &pp->second, utc) != 6) {
- refclock_report(peer, CEVNT_BADREPLY);
- return;
- }
- /*
- * Wait for the on-time character, which follows in a
- * separate message. There is no provision for leap
- * warning.
- */
- pp->leap = LEAP_NOWARNING;
- memcpy(&pp->refid, REFUSNO, 4);
- if (up->msgcnt == 0)
- record_clock_stats(&peer->srcadr, str);
- up->msgcnt++;
- return;
- /*
- * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ"
- */
- case LENPTB:
- if (sscanf(str,
- "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
- &pp->second, &pp->year, &month, &day, &pp->hour,
- &pp->minute, &mjd, &dut1, &leapdir, &leapmonth,
- &msADV, &flag) != 12) {
- refclock_report(peer, CEVNT_BADREPLY);
- return;
- }
- pp->leap = LEAP_NOWARNING;
- if (leapmonth == month) {
- if (leapdir == '+')
- pp->leap = LEAP_ADDSECOND;
- else if (leapdir == '-')
- pp->leap = LEAP_DELSECOND;
- }
- pp->day = ymd2yd(pp->year, month, day);
- memcpy(&pp->refid, REFPTB, 4);
- if (up->msgcnt == 0)
- record_clock_stats(&peer->srcadr, str);
- up->msgcnt++;
- break;
- /*
- * WWVB format 0: "I ddd hh:mm:ss DTZ=nn"
- */
- case LENWWVB0:
- if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d",
- &synchar, &pp->day, &pp->hour, &pp->minute,
- &pp->second, &dstchar, &tz) != 7) {
- refclock_report(peer, CEVNT_BADREPLY);
- return;
- }
- pp->leap = LEAP_NOWARNING;
- if (synchar != ' ')
- pp->leap = LEAP_NOTINSYNC;
- memcpy(&pp->refid, REFWWVB, 4);
- if (up->msgcnt == 0)
- record_clock_stats(&peer->srcadr, str);
- up->msgcnt++;
- break;
- /*
- * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
- */
- case LENWWVB2:
- if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
- &synchar, &qualchar, &pp->year, &pp->day,
- &pp->hour, &pp->minute, &pp->second, &pp->nsec,
- &dstchar, &leapchar, &dstchar) != 11) {
- refclock_report(peer, CEVNT_BADREPLY);
- return;
- }
- pp->nsec *= 1000000;
- pp->leap = LEAP_NOWARNING;
- if (synchar != ' ')
- pp->leap = LEAP_NOTINSYNC;
- else if (leapchar == 'L')
- pp->leap = LEAP_ADDSECOND;
- memcpy(&pp->refid, REFWWVB, 4);
- if (up->msgcnt == 0)
- record_clock_stats(&peer->srcadr, str);
- up->msgcnt++;
- break;
- /*
- * None of the above. Just forget about it and wait for the next
- * message or timeout.
- */
- default:
- return;
- }
- /*
- * We have a valid timecode. The fudge time1 value is added to
- * each sample by the main line routines. Note that in current
- * telephone networks the propatation time can be different for
- * each call and can reach 200 ms for some calls.
- */
- peer->refid = pp->refid;
- pp->lastrec = up->tstamp;
- if (!refclock_process(pp)) {
- refclock_report(peer, CEVNT_BADTIME);
- return;
- }
- pp->lastref = pp->lastrec;
- if (peer->disp > MAXDISTANCE)
- refclock_receive(peer);
- if (up->state != S_MSG) {
- up->state = S_MSG;
- up->timer = TIMECODE;
- }
- }
- /*
- * acts_poll - called by the transmit routine
- */
- static void
- acts_poll (
- int unit,
- struct peer *peer
- )
- {
- struct actsunit *up;
- struct refclockproc *pp;
- /*
- * This routine is called at every system poll. All it does is
- * set flag1 under certain conditions. The real work is done by
- * the timeout routine and state machine.
- */
- pp = peer->procptr;
- up = (struct actsunit *)pp->unitptr;
- switch (peer->ttl) {
- /*
- * In manual mode the calling program is activated by the ntpdc
- * program using the enable flag (fudge flag1), either manually
- * or by a cron job.
- */
- case MODE_MANUAL:
- /* fall through */
- break;
- /*
- * In automatic mode the calling program runs continuously at
- * intervals determined by the poll event or specified timeout.
- */
- case MODE_AUTO:
- pp->sloppyclockflag |= CLK_FLAG1;
- break;
- /*
- * In backup mode the calling program runs continuously as long
- * as either no peers are available or this peer is selected.
- */
- case MODE_BACKUP:
- if (sys_peer == NULL || sys_peer == peer)
- pp->sloppyclockflag |= CLK_FLAG1;
- break;
- }
- }
- /*
- * acts_timer - called at one-second intervals
- */
- static void
- acts_timer(
- int unit,
- struct peer *peer
- )
- {
- struct actsunit *up;
- struct refclockproc *pp;
- /*
- * This routine implments a timeout which runs for a programmed
- * interval. The counter is initialized by the state machine and
- * counts down to zero. Upon reaching zero, the state machine is
- * called. If flag1 is set while in S_IDLE state, force a
- * timeout.
- */
- pp = peer->procptr;
- up = (struct actsunit *)pp->unitptr;
- if (pp->sloppyclockflag & CLK_FLAG1 && up->state == S_IDLE) {
- acts_timeout(peer);
- return;
- }
- if (up->timer == 0)
- return;
- up->timer--;
- if (up->timer == 0)
- acts_timeout(peer);
- }
- /*
- * acts_timeout - called on timeout
- */
- static void
- acts_timeout(
- struct peer *peer
- )
- {
- struct actsunit *up;
- struct refclockproc *pp;
- int fd;
- char device[20];
- char lockfile[128], pidbuf[8];
- char tbuf[BMAX];
- /*
- * The state machine is driven by messages from the modem, when
- * first stated and at timeout.
- */
- pp = peer->procptr;
- up = (struct actsunit *)pp->unitptr;
- pp->sloppyclockflag &= ~CLK_FLAG1;
- if (sys_phone[up->retry] == NULL && !(pp->sloppyclockflag &
- CLK_FLAG3)) {
- msyslog(LOG_ERR, "acts: no phones");
- return;
- }
- switch(up->state) {
- /*
- * System poll event. Lock the modem port and open the device.
- */
- case S_IDLE:
- /*
- * Lock the modem port. If busy, retry later. Note: if
- * something fails between here and the close, the lock
- * file may not be removed.
- */
- if (pp->sloppyclockflag & CLK_FLAG2) {
- sprintf(lockfile, LOCKFILE, up->unit);
- fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
- 0644);
- if (fd < 0) {
- msyslog(LOG_ERR, "acts: port busy");
- return;
- }
- sprintf(pidbuf, "%d\n", (u_int)getpid());
- write(fd, pidbuf, strlen(pidbuf));
- close(fd);
- }
- /*
- * Open the device in raw mode and link the I/O.
- */
- if (!pp->io.fd) {
- sprintf(device, DEVICE, up->unit);
- fd = refclock_open(device, SPEED232,
- LDISC_ACTS | LDISC_RAW | LDISC_REMOTE);
- if (fd == 0) {
- return;
- }
- pp->io.fd = fd;
- if (!io_addclock(&pp->io)) {
- msyslog(LOG_ERR,
- "acts: addclock fails");
- close(fd);
- pp->io.fd = 0;
- return;
- }
- }
- /*
- * If the port is directly connected to the device, skip
- * the modem business and send 'T' for Spectrabum.
- */
- if (pp->sloppyclockflag & CLK_FLAG3) {
- if (write(pp->io.fd, "T", 1) < 0) {
- msyslog(LOG_ERR, "acts: write %m");
- return;
- }
- up->state = S_FIRST;
- up->timer = CONNECT;
- return;
- }
- /*
- * Initialize the modem. This works with Hayes commands.
- */
- #ifdef DEBUG
- if (debug)
- printf("acts: setup %s\n", MODEM_SETUP);
- #endif
- if (write(pp->io.fd, MODEM_SETUP, strlen(MODEM_SETUP)) <
- 0) {
- msyslog(LOG_ERR, "acts: write %m");
- return;
- }
- up->state = S_OK;
- up->timer = SETUP;
- return;
- /*
- * In OK state the modem did not respond to setup.
- */
- case S_OK:
- msyslog(LOG_ERR, "acts: no modem");
- break;
- /*
- * In DTR state we are waiting for the modem to settle down
- * before hammering it with a dial command.
- */
- case S_DTR:
- sprintf(tbuf, "DIAL #%d %s", up->retry,
- sys_phone[up->retry]);
- record_clock_stats(&peer->srcadr, tbuf);
- #ifdef DEBUG
- if (debug)
- printf("%s\n", tbuf);
- #endif
- write(pp->io.fd, sys_phone[up->retry],
- strlen(sys_phone[up->retry]));
- write(pp->io.fd, "\r", 1);
- up->state = S_CONNECT;
- up->timer = ANSWER;
- return;
- /*
- * In CONNECT state the call did not complete.
- */
- case S_CONNECT:
- msyslog(LOG_ERR, "acts: no answer");
- break;
- /*
- * In FIRST state no messages were received.
- */
- case S_FIRST:
- msyslog(LOG_ERR, "acts: no messages");
- break;
- /*
- * In CLOSE state hangup is complete. Close the doors and
- * windows and get some air.
- */
- case S_CLOSE:
- /*
- * Close the device and unlock a shared modem.
- */
- if (pp->io.fd) {
- io_closeclock(&pp->io);
- close(pp->io.fd);
- if (pp->sloppyclockflag & CLK_FLAG2) {
- sprintf(lockfile, LOCKFILE, up->unit);
- unlink(lockfile);
- }
- pp->io.fd = 0;
- }
- /*
- * If messages were received, fold the tent and wait for
- * the next poll. If no messages and there are more
- * numbers to dial, retry after a short wait.
- */
- up->bufptr = pp->a_lastcode;
- up->timer = 0;
- up->state = S_IDLE;
- if ( up->msgcnt == 0) {
- up->retry++;
- if (sys_phone[up->retry] == NULL)
- up->retry = 0;
- else
- up->timer = SETUP;
- } else {
- up->retry = 0;
- }
- up->msgcnt = 0;
- return;
- }
- acts_disc(peer);
- }
- /*
- * acts_disc - disconnect the call and clean the place up.
- */
- static void
- acts_disc (
- struct peer *peer
- )
- {
- struct actsunit *up;
- struct refclockproc *pp;
- int dtr = TIOCM_DTR;
- /*
- * We get here if the call terminated successfully or if an
- * error occured. If the median filter has something in it,feed
- * the data to the clock filter. If a modem port, drop DTR to
- * force command mode and send modem hangup.
- */
- pp = peer->procptr;
- up = (struct actsunit *)pp->unitptr;
- if (up->msgcnt > 0)
- refclock_receive(peer);
- if (!(pp->sloppyclockflag & CLK_FLAG3)) {
- ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr);
- write(pp->io.fd, MODEM_HANGUP, strlen(MODEM_HANGUP));
- }
- up->timer = SETUP;
- up->state = S_CLOSE;
- }
- #else
- int refclock_acts_bs;
- #endif /* REFCLOCK */