refclock_acts.c revision 1.7 1 1.7 christos /* $NetBSD: refclock_acts.c,v 1.7 2014/01/01 19:52:58 christos Exp $ */
2 1.1 kardel
3 1.1 kardel /*
4 1.1 kardel * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
5 1.1 kardel * Services
6 1.1 kardel */
7 1.1 kardel #ifdef HAVE_CONFIG_H
8 1.1 kardel #include <config.h>
9 1.1 kardel #endif
10 1.1 kardel
11 1.6 christos #if defined(REFCLOCK) && defined(CLOCK_ACTS)
12 1.1 kardel
13 1.1 kardel #include "ntpd.h"
14 1.1 kardel #include "ntp_io.h"
15 1.1 kardel #include "ntp_unixtime.h"
16 1.1 kardel #include "ntp_refclock.h"
17 1.1 kardel #include "ntp_stdlib.h"
18 1.1 kardel #include "ntp_control.h"
19 1.1 kardel
20 1.1 kardel #include <stdio.h>
21 1.1 kardel #include <ctype.h>
22 1.1 kardel #ifdef HAVE_SYS_IOCTL_H
23 1.1 kardel # include <sys/ioctl.h>
24 1.1 kardel #endif /* HAVE_SYS_IOCTL_H */
25 1.1 kardel
26 1.6 christos #ifdef SYS_WINNT
27 1.6 christos #undef write /* ports/winnt/include/config.h: #define write _write */
28 1.6 christos extern int async_write(int, const void *, unsigned int);
29 1.6 christos #define write(fd, data, octets) async_write(fd, data, octets)
30 1.6 christos #endif
31 1.6 christos
32 1.1 kardel /*
33 1.1 kardel * This driver supports the US (NIST, USNO) and European (PTB, NPL,
34 1.1 kardel * etc.) modem time services, as well as Spectracom GPS and WWVB
35 1.1 kardel * receivers connected via a modem. The driver periodically dials a
36 1.1 kardel * number from a telephone list, receives the timecode data and
37 1.1 kardel * calculates the local clock correction. It is designed primarily for
38 1.1 kardel * use as backup when neither a radio clock nor connectivity to Internet
39 1.1 kardel * time servers is available.
40 1.1 kardel *
41 1.1 kardel * This driver requires a modem with a Hayes-compatible command set and
42 1.1 kardel * control over the modem data terminal ready (DTR) control line. The
43 1.1 kardel * modem setup string is hard-coded in the driver and may require
44 1.6 christos * changes for nonstandard modems or special circumstances.
45 1.6 christos *
46 1.6 christos * When enabled, the calling program dials the first number in the
47 1.6 christos * phones file. If that call fails, it dials the second number and
48 1.6 christos * so on. The phone number is specified by the Hayes ATDT prefix
49 1.6 christos * followed by the number itself, including the long-distance prefix
50 1.6 christos * and delay code, if necessary. The calling program is enabled
51 1.6 christos * when (a) fudge flag1 is set by ntpdc, (b) at each poll interval
52 1.6 christos * when no other synchronization sources are present, and (c) at each
53 1.6 christos * poll interval whether or not other synchronization sources are
54 1.6 christos * present. The calling program disconnects if (a) the called party
55 1.6 christos * is busy or does not answer, (b) the called party disconnects
56 1.6 christos * before a sufficient nuimber of timecodes have been received.
57 1.1 kardel *
58 1.1 kardel * The driver is transparent to each of the modem time services and
59 1.1 kardel * Spectracom radios. It selects the parsing algorithm depending on the
60 1.1 kardel * message length. There is some hazard should the message be corrupted.
61 1.1 kardel * However, the data format is checked carefully and only if all checks
62 1.1 kardel * succeed is the message accepted. Corrupted lines are discarded
63 1.1 kardel * without complaint.
64 1.1 kardel *
65 1.1 kardel * Fudge controls
66 1.1 kardel *
67 1.1 kardel * flag1 force a call in manual mode
68 1.1 kardel * flag2 enable port locking (not verified)
69 1.6 christos * flag3 not used
70 1.1 kardel * flag4 not used
71 1.1 kardel *
72 1.1 kardel * time1 offset adjustment (s)
73 1.1 kardel *
74 1.6 christos * Ordinarily, the serial port is connected to a modem and the phones
75 1.6 christos * list is defined. If no phones list is defined, the port can be
76 1.6 christos * connected directly to a device or another computer. In this case the
77 1.6 christos * driver will send a single character 'T' at each poll event. If
78 1.6 christos * fudge flag2 is enabled, port locking allows the modem to be shared
79 1.6 christos * when not in use by this driver.
80 1.1 kardel */
81 1.1 kardel /*
82 1.1 kardel * National Institute of Science and Technology (NIST)
83 1.1 kardel *
84 1.1 kardel * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
85 1.1 kardel *
86 1.1 kardel * Data Format
87 1.1 kardel *
88 1.1 kardel * National Institute of Standards and Technology
89 1.1 kardel * Telephone Time Service, Generator 3B
90 1.1 kardel * Enter question mark "?" for HELP
91 1.1 kardel * D L D
92 1.1 kardel * MJD YR MO DA H M S ST S UT1 msADV <OTM>
93 1.1 kardel * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF>
94 1.1 kardel * ...
95 1.1 kardel *
96 1.1 kardel * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is
97 1.1 kardel * the on-time markers echoed by the driver and used by NIST to measure
98 1.6 christos * and correct for the propagation delay. Note: the ACTS timecode has
99 1.6 christos * recently been changed to eliminate the * on-time indicator. The
100 1.6 christos * reason for this and the long term implications are not clear.
101 1.1 kardel *
102 1.1 kardel * US Naval Observatory (USNO)
103 1.1 kardel *
104 1.1 kardel * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
105 1.1 kardel *
106 1.1 kardel * Data Format (two lines, repeating at one-second intervals)
107 1.1 kardel *
108 1.1 kardel * jjjjj nnn hhmmss UTC<CR><LF>
109 1.1 kardel * *<CR><LF>
110 1.1 kardel *
111 1.1 kardel * jjjjj modified Julian day number (not used)
112 1.1 kardel * nnn day of year
113 1.1 kardel * hhmmss second of day
114 1.1 kardel * * on-time marker for previous timecode
115 1.1 kardel * ...
116 1.1 kardel *
117 1.1 kardel * USNO does not correct for the propagation delay. A fudge time1 of
118 1.1 kardel * about .06 s is advisable.
119 1.1 kardel *
120 1.1 kardel * European Services (PTB, NPL, etc.)
121 1.1 kardel *
122 1.1 kardel * PTB: +49 531 512038 (Germany)
123 1.1 kardel * NPL: 0906 851 6333 (UK only)
124 1.1 kardel *
125 1.1 kardel * Data format (see the documentation for phone numbers and formats.)
126 1.1 kardel *
127 1.1 kardel * 1995-01-23 20:58:51 MEZ 10402303260219950123195849740+40000500<CR><LF>
128 1.1 kardel *
129 1.1 kardel * Spectracom GPS and WWVB Receivers
130 1.1 kardel *
131 1.1 kardel * If a modem is connected to a Spectracom receiver, this driver will
132 1.1 kardel * call it up and retrieve the time in one of two formats. As this
133 1.1 kardel * driver does not send anything, the radio will have to either be
134 1.1 kardel * configured in continuous mode or be polled by another local driver.
135 1.1 kardel */
136 1.1 kardel /*
137 1.1 kardel * Interface definitions
138 1.1 kardel */
139 1.1 kardel #define DEVICE "/dev/acts%d" /* device name and unit */
140 1.6 christos #define SPEED232 B19200 /* uart speed (19200 bps) */
141 1.1 kardel #define PRECISION (-10) /* precision assumed (about 1 ms) */
142 1.6 christos #define LOCKFILE "/var/spool/lock/LCK..cua%d"
143 1.1 kardel #define DESCRIPTION "Automated Computer Time Service" /* WRU */
144 1.1 kardel #define REFID "NONE" /* default reference ID */
145 1.1 kardel #define MSGCNT 20 /* max message count */
146 1.1 kardel #define MAXPHONE 10 /* max number of phone numbers */
147 1.1 kardel
148 1.1 kardel /*
149 1.6 christos * Calling program modes (mode)
150 1.1 kardel */
151 1.6 christos #define MODE_BACKUP 0 /* backup mode */
152 1.6 christos #define MODE_AUTO 1 /* automatic mode */
153 1.1 kardel #define MODE_MANUAL 2 /* manual mode */
154 1.1 kardel
155 1.1 kardel /*
156 1.6 christos * Service identifiers (message length)
157 1.1 kardel */
158 1.1 kardel #define REFACTS "NIST" /* NIST reference ID */
159 1.6 christos #define LENACTS 50 /* NIST format A */
160 1.1 kardel #define REFUSNO "USNO" /* USNO reference ID */
161 1.1 kardel #define LENUSNO 20 /* USNO */
162 1.1 kardel #define REFPTB "PTB\0" /* PTB/NPL reference ID */
163 1.1 kardel #define LENPTB 78 /* PTB/NPL format */
164 1.1 kardel #define REFWWVB "WWVB" /* WWVB reference ID */
165 1.1 kardel #define LENWWVB0 22 /* WWVB format 0 */
166 1.1 kardel #define LENWWVB2 24 /* WWVB format 2 */
167 1.1 kardel #define LF 0x0a /* ASCII LF */
168 1.1 kardel
169 1.1 kardel /*
170 1.6 christos * Modem setup strings. These may have to be changed for
171 1.6 christos * some modems.
172 1.1 kardel *
173 1.1 kardel * AT command prefix
174 1.1 kardel * B1 US answer tone
175 1.1 kardel * &C0 disable carrier detect
176 1.1 kardel * &D2 hang up and return to command mode on DTR transition
177 1.1 kardel * E0 modem command echo disabled
178 1.6 christos * L1 set modem speaker volume to low level
179 1.1 kardel * M1 speaker enabled until carrier detect
180 1.1 kardel * Q0 return result codes
181 1.1 kardel * V1 return result codes as English words
182 1.6 christos * Y1 enable long-space disconnect
183 1.1 kardel */
184 1.6 christos const char def_modem_setup[] = "ATB1&C0&D2E0L1M1Q0V1Y1";
185 1.6 christos const char *modem_setup = def_modem_setup;
186 1.1 kardel
187 1.1 kardel /*
188 1.1 kardel * Timeouts (all in seconds)
189 1.1 kardel */
190 1.1 kardel #define SETUP 3 /* setup timeout */
191 1.6 christos #define REDIAL 30 /* redial timeout */
192 1.1 kardel #define ANSWER 60 /* answer timeout */
193 1.6 christos #define TIMECODE 60 /* message timeout */
194 1.6 christos #define MAXCODE 20 /* max timecodes */
195 1.1 kardel
196 1.1 kardel /*
197 1.1 kardel * State machine codes
198 1.1 kardel */
199 1.6 christos typedef enum {
200 1.6 christos S_IDLE, /* wait for poll */
201 1.6 christos S_SETUP, /* send modem setup */
202 1.6 christos S_CONNECT, /* wait for answer */
203 1.6 christos S_MSG /* wait for timecode */
204 1.6 christos } teModemState;
205 1.1 kardel
206 1.1 kardel /*
207 1.1 kardel * Unit control structure
208 1.1 kardel */
209 1.1 kardel struct actsunit {
210 1.1 kardel int unit; /* unit number */
211 1.1 kardel int state; /* the first one was Delaware */
212 1.1 kardel int timer; /* timeout counter */
213 1.1 kardel int retry; /* retry index */
214 1.1 kardel int msgcnt; /* count of messages received */
215 1.1 kardel l_fp tstamp; /* on-time timestamp */
216 1.6 christos char *bufptr; /* next incoming char stored here */
217 1.6 christos char buf[BMAX]; /* bufptr roams within buf[] */
218 1.1 kardel };
219 1.1 kardel
220 1.1 kardel /*
221 1.1 kardel * Function prototypes
222 1.1 kardel */
223 1.1 kardel static int acts_start (int, struct peer *);
224 1.1 kardel static void acts_shutdown (int, struct peer *);
225 1.1 kardel static void acts_receive (struct recvbuf *);
226 1.6 christos static void acts_message (struct peer *, const char *);
227 1.6 christos static void acts_timecode (struct peer *, const char *);
228 1.1 kardel static void acts_poll (int, struct peer *);
229 1.6 christos static void acts_timeout (struct peer *, teModemState);
230 1.1 kardel static void acts_timer (int, struct peer *);
231 1.6 christos static void acts_close (struct peer *);
232 1.1 kardel
233 1.1 kardel /*
234 1.1 kardel * Transfer vector (conditional structure name)
235 1.1 kardel */
236 1.6 christos struct refclock refclock_acts = {
237 1.1 kardel acts_start, /* start up driver */
238 1.1 kardel acts_shutdown, /* shut down driver */
239 1.1 kardel acts_poll, /* transmit poll message */
240 1.1 kardel noentry, /* not used */
241 1.1 kardel noentry, /* not used */
242 1.1 kardel noentry, /* not used */
243 1.1 kardel acts_timer /* housekeeping timer */
244 1.1 kardel };
245 1.1 kardel
246 1.1 kardel /*
247 1.1 kardel * Initialize data for processing
248 1.1 kardel */
249 1.1 kardel static int
250 1.6 christos acts_start(
251 1.1 kardel int unit,
252 1.1 kardel struct peer *peer
253 1.1 kardel )
254 1.1 kardel {
255 1.1 kardel struct actsunit *up;
256 1.1 kardel struct refclockproc *pp;
257 1.6 christos const char *setup;
258 1.1 kardel
259 1.1 kardel /*
260 1.1 kardel * Allocate and initialize unit structure
261 1.1 kardel */
262 1.6 christos up = emalloc_zero(sizeof(struct actsunit));
263 1.1 kardel up->unit = unit;
264 1.1 kardel pp = peer->procptr;
265 1.6 christos pp->unitptr = up;
266 1.1 kardel pp->io.clock_recv = acts_receive;
267 1.6 christos pp->io.srcclock = peer;
268 1.1 kardel pp->io.datalen = 0;
269 1.6 christos pp->io.fd = -1;
270 1.1 kardel
271 1.1 kardel /*
272 1.1 kardel * Initialize miscellaneous variables
273 1.1 kardel */
274 1.1 kardel peer->precision = PRECISION;
275 1.1 kardel pp->clockdesc = DESCRIPTION;
276 1.6 christos memcpy(&pp->refid, REFID, 4);
277 1.1 kardel peer->sstclktype = CTL_SST_TS_TELEPHONE;
278 1.6 christos up->bufptr = up->buf;
279 1.6 christos if (def_modem_setup == modem_setup) {
280 1.6 christos setup = get_ext_sys_var("modemsetup");
281 1.6 christos if (setup != NULL)
282 1.6 christos modem_setup = estrdup(setup);
283 1.6 christos }
284 1.6 christos
285 1.1 kardel return (1);
286 1.1 kardel }
287 1.1 kardel
288 1.1 kardel
289 1.1 kardel /*
290 1.1 kardel * acts_shutdown - shut down the clock
291 1.1 kardel */
292 1.1 kardel static void
293 1.6 christos acts_shutdown(
294 1.1 kardel int unit,
295 1.1 kardel struct peer *peer
296 1.1 kardel )
297 1.1 kardel {
298 1.1 kardel struct actsunit *up;
299 1.1 kardel struct refclockproc *pp;
300 1.1 kardel
301 1.1 kardel /*
302 1.1 kardel * Warning: do this only when a call is not in progress.
303 1.1 kardel */
304 1.1 kardel pp = peer->procptr;
305 1.6 christos up = pp->unitptr;
306 1.6 christos acts_close(peer);
307 1.1 kardel free(up);
308 1.1 kardel }
309 1.1 kardel
310 1.1 kardel
311 1.1 kardel /*
312 1.1 kardel * acts_receive - receive data from the serial interface
313 1.1 kardel */
314 1.1 kardel static void
315 1.6 christos acts_receive(
316 1.1 kardel struct recvbuf *rbufp
317 1.1 kardel )
318 1.1 kardel {
319 1.1 kardel struct actsunit *up;
320 1.1 kardel struct refclockproc *pp;
321 1.1 kardel struct peer *peer;
322 1.6 christos char tbuf[sizeof(up->buf)];
323 1.6 christos char * tptr;
324 1.6 christos int octets;
325 1.1 kardel
326 1.1 kardel /*
327 1.1 kardel * Initialize pointers and read the timecode and timestamp. Note
328 1.1 kardel * we are in raw mode and victim of whatever the terminal
329 1.1 kardel * interface kicks up; so, we have to reassemble messages from
330 1.1 kardel * arbitrary fragments. Capture the timecode at the beginning of
331 1.1 kardel * the message and at the '*' and '#' on-time characters.
332 1.1 kardel */
333 1.6 christos peer = rbufp->recv_peer;
334 1.1 kardel pp = peer->procptr;
335 1.6 christos up = pp->unitptr;
336 1.6 christos octets = sizeof(up->buf) - (up->bufptr - up->buf);
337 1.6 christos refclock_gtraw(rbufp, tbuf, octets, &pp->lastrec);
338 1.1 kardel for (tptr = tbuf; *tptr != '\0'; tptr++) {
339 1.1 kardel if (*tptr == LF) {
340 1.6 christos if (up->bufptr == up->buf) {
341 1.1 kardel up->tstamp = pp->lastrec;
342 1.1 kardel continue;
343 1.1 kardel } else {
344 1.1 kardel *up->bufptr = '\0';
345 1.6 christos up->bufptr = up->buf;
346 1.6 christos acts_message(peer, up->buf);
347 1.1 kardel }
348 1.2 christos } else if (!iscntrl((unsigned char)*tptr)) {
349 1.1 kardel *up->bufptr++ = *tptr;
350 1.1 kardel if (*tptr == '*' || *tptr == '#') {
351 1.1 kardel up->tstamp = pp->lastrec;
352 1.6 christos if (write(pp->io.fd, tptr, 1) < 0)
353 1.6 christos msyslog(LOG_ERR, "acts: write echo fails %m");
354 1.1 kardel }
355 1.1 kardel }
356 1.1 kardel }
357 1.1 kardel }
358 1.1 kardel
359 1.1 kardel
360 1.1 kardel /*
361 1.1 kardel * acts_message - process message
362 1.1 kardel */
363 1.1 kardel void
364 1.1 kardel acts_message(
365 1.6 christos struct peer *peer,
366 1.6 christos const char *msg
367 1.1 kardel )
368 1.1 kardel {
369 1.1 kardel struct actsunit *up;
370 1.1 kardel struct refclockproc *pp;
371 1.6 christos char tbuf[BMAX];
372 1.6 christos int dtr = TIOCM_DTR;
373 1.6 christos
374 1.6 christos DPRINTF(1, ("acts: %d %s\n", (int)strlen(msg), msg));
375 1.1 kardel
376 1.1 kardel /*
377 1.1 kardel * What to do depends on the state and the first token in the
378 1.6 christos * message.
379 1.6 christos */
380 1.1 kardel pp = peer->procptr;
381 1.6 christos up = pp->unitptr;
382 1.1 kardel
383 1.1 kardel /*
384 1.6 christos * Extract the first token in the line.
385 1.1 kardel */
386 1.6 christos strlcpy(tbuf, msg, sizeof(tbuf));
387 1.1 kardel strtok(tbuf, " ");
388 1.6 christos switch (up->state) {
389 1.6 christos
390 1.6 christos /*
391 1.6 christos * We are waiting for the OK response to the modem setup
392 1.6 christos * command. When this happens, dial the number followed.
393 1.6 christos * If anything other than OK is received, just ignore it
394 1.6 christos * and wait for timeoue.
395 1.6 christos */
396 1.6 christos case S_SETUP:
397 1.6 christos if (strcmp(tbuf, "OK") != 0) {
398 1.6 christos /*
399 1.6 christos * We disable echo with MODEM_SETUP's E0 but
400 1.6 christos * if the modem was previously E1, we will
401 1.6 christos * see MODEM_SETUP echoed before the OK/ERROR.
402 1.6 christos * Ignore it.
403 1.6 christos */
404 1.6 christos if (!strcmp(tbuf, modem_setup))
405 1.6 christos return;
406 1.6 christos break;
407 1.6 christos }
408 1.6 christos
409 1.6 christos mprintf_event(PEVNT_CLOCK, peer, "DIAL #%d %s",
410 1.6 christos up->retry, sys_phone[up->retry]);
411 1.6 christos if (ioctl(pp->io.fd, TIOCMBIS, &dtr) < 0)
412 1.6 christos msyslog(LOG_ERR, "acts: ioctl(TIOCMBIS) failed: %m");
413 1.6 christos if (write(pp->io.fd, sys_phone[up->retry],
414 1.6 christos strlen(sys_phone[up->retry])) < 0)
415 1.6 christos msyslog(LOG_ERR, "acts: write DIAL fails %m");
416 1.6 christos write(pp->io.fd, "\r", 1);
417 1.6 christos up->retry++;
418 1.6 christos up->state = S_CONNECT;
419 1.6 christos up->timer = ANSWER;
420 1.6 christos return;
421 1.6 christos
422 1.6 christos /*
423 1.6 christos * We are waiting for the CONNECT response to the dial
424 1.6 christos * command. When this happens, listen for timecodes. If
425 1.6 christos * somthing other than CONNECT is received, like BUSY
426 1.6 christos * or NO CARRIER, abort the call.
427 1.6 christos */
428 1.6 christos case S_CONNECT:
429 1.6 christos if (strcmp(tbuf, "CONNECT") != 0)
430 1.6 christos break;
431 1.6 christos
432 1.6 christos report_event(PEVNT_CLOCK, peer, msg);
433 1.6 christos up->state = S_MSG;
434 1.6 christos up->timer = TIMECODE;
435 1.6 christos return;
436 1.6 christos
437 1.6 christos /*
438 1.6 christos * We are waiting for a timecode response. Pass it to
439 1.6 christos * the parser. If NO CARRIER is received, save the
440 1.6 christos * messages and abort the call.
441 1.6 christos */
442 1.6 christos case S_MSG:
443 1.6 christos if (strcmp(tbuf, "NO") == 0)
444 1.6 christos report_event(PEVNT_CLOCK, peer, msg);
445 1.6 christos if (up->msgcnt < MAXCODE)
446 1.6 christos acts_timecode(peer, msg);
447 1.6 christos else
448 1.6 christos acts_timeout(peer, S_MSG);
449 1.1 kardel return;
450 1.1 kardel }
451 1.1 kardel
452 1.1 kardel /*
453 1.6 christos * Other response. Tell us about it.
454 1.6 christos */
455 1.6 christos report_event(PEVNT_CLOCK, peer, msg);
456 1.6 christos acts_close(peer);
457 1.6 christos }
458 1.6 christos
459 1.6 christos
460 1.6 christos /*
461 1.6 christos * acts_timeout - called on timeout
462 1.6 christos */
463 1.6 christos static void
464 1.6 christos acts_timeout(
465 1.6 christos struct peer *peer,
466 1.6 christos teModemState dstate
467 1.6 christos )
468 1.6 christos {
469 1.6 christos struct actsunit *up;
470 1.6 christos struct refclockproc *pp;
471 1.6 christos int fd;
472 1.6 christos int rc;
473 1.6 christos char device[20];
474 1.6 christos char lockfile[128], pidbuf[8];
475 1.6 christos
476 1.6 christos /*
477 1.6 christos * The state machine is driven by messages from the modem,
478 1.6 christos * when first started and at timeout.
479 1.6 christos */
480 1.6 christos pp = peer->procptr;
481 1.6 christos up = pp->unitptr;
482 1.6 christos switch (dstate) {
483 1.6 christos
484 1.6 christos /*
485 1.6 christos * System poll event. Lock the modem port, open the device
486 1.6 christos * and send the setup command.
487 1.1 kardel */
488 1.6 christos case S_IDLE:
489 1.6 christos if (-1 != pp->io.fd)
490 1.6 christos return; /* port is already open */
491 1.6 christos
492 1.6 christos /*
493 1.6 christos * Lock the modem port. If busy, retry later. Note: if
494 1.6 christos * something fails between here and the close, the lock
495 1.6 christos * file may not be removed.
496 1.6 christos */
497 1.6 christos if (pp->sloppyclockflag & CLK_FLAG2) {
498 1.6 christos snprintf(lockfile, sizeof(lockfile), LOCKFILE,
499 1.6 christos up->unit);
500 1.6 christos fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
501 1.6 christos 0644);
502 1.6 christos if (fd < 0) {
503 1.6 christos report_event(PEVNT_CLOCK, peer, "acts: port busy");
504 1.6 christos return;
505 1.6 christos }
506 1.6 christos snprintf(pidbuf, sizeof(pidbuf), "%d\n",
507 1.6 christos (u_int)getpid());
508 1.6 christos if (write(fd, pidbuf, strlen(pidbuf)) < 0)
509 1.6 christos msyslog(LOG_ERR, "acts: write lock fails %m");
510 1.6 christos close(fd);
511 1.6 christos }
512 1.6 christos
513 1.6 christos /*
514 1.6 christos * Open the device in raw mode and link the I/O.
515 1.6 christos */
516 1.6 christos snprintf(device, sizeof(device), DEVICE,
517 1.6 christos up->unit);
518 1.6 christos fd = refclock_open(device, SPEED232, LDISC_ACTS |
519 1.6 christos LDISC_RAW | LDISC_REMOTE);
520 1.7 christos if (fd < 0) {
521 1.6 christos msyslog(LOG_ERR, "acts: open fails %m");
522 1.6 christos return;
523 1.6 christos }
524 1.6 christos pp->io.fd = fd;
525 1.6 christos if (!io_addclock(&pp->io)) {
526 1.6 christos msyslog(LOG_ERR, "acts: addclock fails");
527 1.6 christos close(fd);
528 1.6 christos pp->io.fd = -1;
529 1.6 christos return;
530 1.6 christos }
531 1.6 christos up->msgcnt = 0;
532 1.6 christos up->bufptr = up->buf;
533 1.6 christos
534 1.6 christos /*
535 1.6 christos * If the port is directly connected to the device, skip
536 1.6 christos * the modem business and send 'T' for Spectrabum.
537 1.6 christos */
538 1.6 christos if (sys_phone[up->retry] == NULL) {
539 1.6 christos if (write(pp->io.fd, "T", 1) < 0)
540 1.6 christos msyslog(LOG_ERR, "acts: write T fails %m");
541 1.6 christos up->state = S_MSG;
542 1.6 christos up->timer = TIMECODE;
543 1.1 kardel return;
544 1.1 kardel }
545 1.6 christos
546 1.6 christos /*
547 1.6 christos * Initialize the modem. This works with Hayes-
548 1.6 christos * compatible modems.
549 1.6 christos */
550 1.6 christos mprintf_event(PEVNT_CLOCK, peer, "SETUP %s",
551 1.6 christos modem_setup);
552 1.6 christos rc = write(pp->io.fd, modem_setup, strlen(modem_setup));
553 1.6 christos if (rc < 0)
554 1.6 christos msyslog(LOG_ERR, "acts: write SETUP fails %m");
555 1.6 christos write(pp->io.fd, "\r", 1);
556 1.6 christos up->state = S_SETUP;
557 1.6 christos up->timer = SETUP;
558 1.1 kardel return;
559 1.1 kardel
560 1.1 kardel /*
561 1.6 christos * In SETUP state the modem did not respond OK to setup string.
562 1.6 christos */
563 1.6 christos case S_SETUP:
564 1.6 christos report_event(PEVNT_CLOCK, peer, "no modem");
565 1.6 christos break;
566 1.6 christos
567 1.6 christos /*
568 1.6 christos * In CONNECT state the call did not complete. Abort the call.
569 1.1 kardel */
570 1.1 kardel case S_CONNECT:
571 1.6 christos report_event(PEVNT_CLOCK, peer, "no answer");
572 1.6 christos break;
573 1.6 christos
574 1.6 christos /*
575 1.6 christos * In MSG states no further timecodes are expected. If any
576 1.6 christos * timecodes have arrived, update the clock. In any case,
577 1.6 christos * terminate the call.
578 1.6 christos */
579 1.6 christos case S_MSG:
580 1.6 christos if (up->msgcnt == 0) {
581 1.6 christos report_event(PEVNT_CLOCK, peer, "no timecodes");
582 1.6 christos } else {
583 1.6 christos pp->lastref = pp->lastrec;
584 1.6 christos record_clock_stats(&peer->srcadr, pp->a_lastcode);
585 1.6 christos refclock_receive(peer);
586 1.6 christos }
587 1.6 christos break;
588 1.6 christos }
589 1.6 christos acts_close(peer);
590 1.6 christos }
591 1.6 christos
592 1.6 christos
593 1.6 christos /*
594 1.6 christos * acts_close - close and prepare for next call.
595 1.6 christos *
596 1.6 christos * In ClOSE state no further protocol actions are required
597 1.6 christos * other than to close and release the device and prepare to
598 1.6 christos * dial the next number if necessary.
599 1.6 christos */
600 1.6 christos void
601 1.6 christos acts_close(
602 1.6 christos struct peer *peer
603 1.6 christos )
604 1.6 christos {
605 1.6 christos struct actsunit *up;
606 1.6 christos struct refclockproc *pp;
607 1.6 christos char lockfile[128];
608 1.6 christos int dtr;
609 1.6 christos
610 1.6 christos pp = peer->procptr;
611 1.6 christos up = pp->unitptr;
612 1.6 christos if (pp->io.fd != -1) {
613 1.6 christos report_event(PEVNT_CLOCK, peer, "close");
614 1.6 christos dtr = TIOCM_DTR;
615 1.6 christos if (ioctl(pp->io.fd, TIOCMBIC, &dtr) < 0)
616 1.6 christos msyslog(LOG_ERR, "acts: ioctl(TIOCMBIC) failed: %m");
617 1.6 christos io_closeclock(&pp->io);
618 1.6 christos pp->io.fd = -1;
619 1.6 christos }
620 1.6 christos if (pp->sloppyclockflag & CLK_FLAG2) {
621 1.6 christos snprintf(lockfile, sizeof(lockfile),
622 1.6 christos LOCKFILE, up->unit);
623 1.6 christos unlink(lockfile);
624 1.6 christos }
625 1.6 christos if (up->msgcnt == 0 && up->retry > 0) {
626 1.6 christos if (sys_phone[up->retry] != NULL) {
627 1.6 christos up->state = S_IDLE;
628 1.6 christos up->timer = REDIAL;
629 1.1 kardel return;
630 1.1 kardel }
631 1.6 christos }
632 1.6 christos up->state = S_IDLE;
633 1.6 christos up->timer = 0;
634 1.6 christos }
635 1.6 christos
636 1.6 christos
637 1.6 christos /*
638 1.6 christos * acts_poll - called by the transmit routine
639 1.6 christos */
640 1.6 christos static void
641 1.6 christos acts_poll(
642 1.6 christos int unit,
643 1.6 christos struct peer *peer
644 1.6 christos )
645 1.6 christos {
646 1.6 christos struct actsunit *up;
647 1.6 christos struct refclockproc *pp;
648 1.6 christos
649 1.6 christos /*
650 1.6 christos * This routine is called at every system poll. All it does is
651 1.6 christos * set flag1 under certain conditions. The real work is done by
652 1.6 christos * the timeout routine and state machine.
653 1.6 christos */
654 1.6 christos pp = peer->procptr;
655 1.6 christos up = pp->unitptr;
656 1.6 christos switch (peer->ttl) {
657 1.6 christos
658 1.6 christos /*
659 1.6 christos * In manual mode the calling program is activated by the ntpdc
660 1.6 christos * program using the enable flag (fudge flag1), either manually
661 1.6 christos * or by a cron job.
662 1.6 christos */
663 1.6 christos case MODE_MANUAL:
664 1.1 kardel return;
665 1.1 kardel
666 1.1 kardel /*
667 1.6 christos * In automatic mode the calling program runs continuously at
668 1.6 christos * intervals determined by the poll event or specified timeout.
669 1.1 kardel */
670 1.6 christos case MODE_AUTO:
671 1.6 christos break;
672 1.6 christos
673 1.6 christos /*
674 1.6 christos * In backup mode the calling program runs continuously as long
675 1.6 christos * as either no peers are available or this peer is selected.
676 1.6 christos */
677 1.6 christos case MODE_BACKUP:
678 1.6 christos if (!(sys_peer == NULL || sys_peer == peer))
679 1.6 christos return;
680 1.6 christos
681 1.1 kardel break;
682 1.1 kardel }
683 1.6 christos pp->polls++;
684 1.6 christos if (S_IDLE == up->state) {
685 1.6 christos up->retry = 0;
686 1.6 christos acts_timeout(peer, S_IDLE);
687 1.6 christos }
688 1.6 christos }
689 1.6 christos
690 1.6 christos
691 1.6 christos /*
692 1.6 christos * acts_timer - called at one-second intervals
693 1.6 christos */
694 1.6 christos static void
695 1.6 christos acts_timer(
696 1.6 christos int unit,
697 1.6 christos struct peer *peer
698 1.6 christos )
699 1.6 christos {
700 1.6 christos struct actsunit *up;
701 1.6 christos struct refclockproc *pp;
702 1.6 christos
703 1.6 christos /*
704 1.6 christos * This routine implments a timeout which runs for a programmed
705 1.6 christos * interval. The counter is initialized by the state machine and
706 1.6 christos * counts down to zero. Upon reaching zero, the state machine is
707 1.6 christos * called. If flag1 is set while timer is zero, force a call.
708 1.6 christos */
709 1.6 christos pp = peer->procptr;
710 1.6 christos up = pp->unitptr;
711 1.6 christos if (up->timer == 0) {
712 1.6 christos if (pp->sloppyclockflag & CLK_FLAG1) {
713 1.6 christos pp->sloppyclockflag &= ~CLK_FLAG1;
714 1.6 christos acts_timeout(peer, S_IDLE);
715 1.6 christos }
716 1.6 christos } else {
717 1.6 christos up->timer--;
718 1.6 christos if (up->timer == 0)
719 1.6 christos acts_timeout(peer, up->state);
720 1.6 christos }
721 1.1 kardel }
722 1.1 kardel
723 1.1 kardel /*
724 1.1 kardel * acts_timecode - identify the service and parse the timecode message
725 1.1 kardel */
726 1.1 kardel void
727 1.1 kardel acts_timecode(
728 1.6 christos struct peer * peer, /* peer structure pointer */
729 1.6 christos const char * str /* timecode string */
730 1.1 kardel )
731 1.1 kardel {
732 1.1 kardel struct actsunit *up;
733 1.1 kardel struct refclockproc *pp;
734 1.1 kardel int day; /* day of the month */
735 1.1 kardel int month; /* month of the year */
736 1.1 kardel u_long mjd; /* Modified Julian Day */
737 1.1 kardel double dut1; /* DUT adjustment */
738 1.1 kardel
739 1.1 kardel u_int dst; /* ACTS daylight/standard time */
740 1.1 kardel u_int leap; /* ACTS leap indicator */
741 1.1 kardel double msADV; /* ACTS transmit advance (ms) */
742 1.1 kardel char utc[10]; /* ACTS timescale */
743 1.1 kardel char flag; /* ACTS on-time character (* or #) */
744 1.1 kardel
745 1.1 kardel char synchar; /* WWVB synchronized indicator */
746 1.1 kardel char qualchar; /* WWVB quality indicator */
747 1.1 kardel char leapchar; /* WWVB leap indicator */
748 1.1 kardel char dstchar; /* WWVB daylight/savings indicator */
749 1.1 kardel int tz; /* WWVB timezone */
750 1.1 kardel
751 1.3 christos int leapmonth; /* PTB/NPL month of leap */
752 1.1 kardel char leapdir; /* PTB/NPL leap direction */
753 1.1 kardel
754 1.1 kardel /*
755 1.1 kardel * The parser selects the modem format based on the message
756 1.1 kardel * length. Since the data are checked carefully, occasional
757 1.1 kardel * errors due noise are forgivable.
758 1.1 kardel */
759 1.1 kardel pp = peer->procptr;
760 1.6 christos up = pp->unitptr;
761 1.1 kardel pp->nsec = 0;
762 1.6 christos switch (strlen(str)) {
763 1.1 kardel
764 1.1 kardel /*
765 1.1 kardel * For USNO format on-time character '*', which is on a line by
766 1.1 kardel * itself. Be sure a timecode has been received.
767 1.1 kardel */
768 1.1 kardel case 1:
769 1.1 kardel if (*str == '*' && up->msgcnt > 0)
770 1.1 kardel break;
771 1.1 kardel
772 1.1 kardel return;
773 1.1 kardel
774 1.1 kardel /*
775 1.6 christos * ACTS format A: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
776 1.6 christos * UTC(NIST) *".
777 1.1 kardel */
778 1.1 kardel case LENACTS:
779 1.1 kardel if (sscanf(str,
780 1.1 kardel "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
781 1.1 kardel &mjd, &pp->year, &month, &day, &pp->hour,
782 1.1 kardel &pp->minute, &pp->second, &dst, &leap, &dut1,
783 1.1 kardel &msADV, utc, &flag) != 13) {
784 1.1 kardel refclock_report(peer, CEVNT_BADREPLY);
785 1.1 kardel return;
786 1.1 kardel }
787 1.1 kardel pp->day = ymd2yd(pp->year, month, day);
788 1.1 kardel pp->leap = LEAP_NOWARNING;
789 1.1 kardel if (leap == 1)
790 1.6 christos pp->leap = LEAP_ADDSECOND;
791 1.6 christos else if (leap == 2)
792 1.6 christos pp->leap = LEAP_DELSECOND;
793 1.1 kardel memcpy(&pp->refid, REFACTS, 4);
794 1.1 kardel up->msgcnt++;
795 1.6 christos if (flag != '#' && up->msgcnt < 10)
796 1.6 christos return;
797 1.6 christos
798 1.1 kardel break;
799 1.1 kardel
800 1.1 kardel /*
801 1.1 kardel * USNO format: "jjjjj nnn hhmmss UTC"
802 1.1 kardel */
803 1.1 kardel case LENUSNO:
804 1.1 kardel if (sscanf(str, "%5ld %3d %2d%2d%2d %3s",
805 1.1 kardel &mjd, &pp->day, &pp->hour, &pp->minute,
806 1.1 kardel &pp->second, utc) != 6) {
807 1.1 kardel refclock_report(peer, CEVNT_BADREPLY);
808 1.1 kardel return;
809 1.1 kardel }
810 1.1 kardel
811 1.1 kardel /*
812 1.1 kardel * Wait for the on-time character, which follows in a
813 1.1 kardel * separate message. There is no provision for leap
814 1.1 kardel * warning.
815 1.1 kardel */
816 1.1 kardel pp->leap = LEAP_NOWARNING;
817 1.1 kardel memcpy(&pp->refid, REFUSNO, 4);
818 1.1 kardel up->msgcnt++;
819 1.6 christos break;
820 1.1 kardel
821 1.1 kardel /*
822 1.1 kardel * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ"
823 1.1 kardel */
824 1.1 kardel case LENPTB:
825 1.1 kardel if (sscanf(str,
826 1.1 kardel "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
827 1.1 kardel &pp->second, &pp->year, &month, &day, &pp->hour,
828 1.1 kardel &pp->minute, &mjd, &dut1, &leapdir, &leapmonth,
829 1.1 kardel &msADV, &flag) != 12) {
830 1.1 kardel refclock_report(peer, CEVNT_BADREPLY);
831 1.1 kardel return;
832 1.1 kardel }
833 1.1 kardel pp->leap = LEAP_NOWARNING;
834 1.1 kardel if (leapmonth == month) {
835 1.1 kardel if (leapdir == '+')
836 1.6 christos pp->leap = LEAP_ADDSECOND;
837 1.1 kardel else if (leapdir == '-')
838 1.6 christos pp->leap = LEAP_DELSECOND;
839 1.1 kardel }
840 1.1 kardel pp->day = ymd2yd(pp->year, month, day);
841 1.1 kardel memcpy(&pp->refid, REFPTB, 4);
842 1.1 kardel up->msgcnt++;
843 1.1 kardel break;
844 1.1 kardel
845 1.1 kardel
846 1.1 kardel /*
847 1.1 kardel * WWVB format 0: "I ddd hh:mm:ss DTZ=nn"
848 1.1 kardel */
849 1.1 kardel case LENWWVB0:
850 1.1 kardel if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d",
851 1.1 kardel &synchar, &pp->day, &pp->hour, &pp->minute,
852 1.1 kardel &pp->second, &dstchar, &tz) != 7) {
853 1.1 kardel refclock_report(peer, CEVNT_BADREPLY);
854 1.1 kardel return;
855 1.1 kardel }
856 1.1 kardel pp->leap = LEAP_NOWARNING;
857 1.1 kardel if (synchar != ' ')
858 1.1 kardel pp->leap = LEAP_NOTINSYNC;
859 1.1 kardel memcpy(&pp->refid, REFWWVB, 4);
860 1.1 kardel up->msgcnt++;
861 1.1 kardel break;
862 1.1 kardel
863 1.1 kardel /*
864 1.1 kardel * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
865 1.1 kardel */
866 1.1 kardel case LENWWVB2:
867 1.1 kardel if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
868 1.1 kardel &synchar, &qualchar, &pp->year, &pp->day,
869 1.1 kardel &pp->hour, &pp->minute, &pp->second, &pp->nsec,
870 1.1 kardel &dstchar, &leapchar, &dstchar) != 11) {
871 1.1 kardel refclock_report(peer, CEVNT_BADREPLY);
872 1.1 kardel return;
873 1.1 kardel }
874 1.1 kardel pp->nsec *= 1000000;
875 1.1 kardel pp->leap = LEAP_NOWARNING;
876 1.1 kardel if (synchar != ' ')
877 1.1 kardel pp->leap = LEAP_NOTINSYNC;
878 1.1 kardel else if (leapchar == 'L')
879 1.1 kardel pp->leap = LEAP_ADDSECOND;
880 1.1 kardel memcpy(&pp->refid, REFWWVB, 4);
881 1.1 kardel up->msgcnt++;
882 1.1 kardel break;
883 1.1 kardel
884 1.1 kardel /*
885 1.1 kardel * None of the above. Just forget about it and wait for the next
886 1.1 kardel * message or timeout.
887 1.1 kardel */
888 1.1 kardel default:
889 1.1 kardel return;
890 1.1 kardel }
891 1.1 kardel
892 1.1 kardel /*
893 1.1 kardel * We have a valid timecode. The fudge time1 value is added to
894 1.1 kardel * each sample by the main line routines. Note that in current
895 1.1 kardel * telephone networks the propatation time can be different for
896 1.1 kardel * each call and can reach 200 ms for some calls.
897 1.1 kardel */
898 1.1 kardel peer->refid = pp->refid;
899 1.1 kardel pp->lastrec = up->tstamp;
900 1.6 christos if (up->msgcnt == 0)
901 1.6 christos return;
902 1.6 christos
903 1.6 christos strlcpy(pp->a_lastcode, str, sizeof(pp->a_lastcode));
904 1.6 christos pp->lencode = strlen(pp->a_lastcode);
905 1.1 kardel if (!refclock_process(pp)) {
906 1.1 kardel refclock_report(peer, CEVNT_BADTIME);
907 1.1 kardel return;
908 1.1 kardel }
909 1.1 kardel pp->lastref = pp->lastrec;
910 1.1 kardel }
911 1.1 kardel #else
912 1.1 kardel int refclock_acts_bs;
913 1.1 kardel #endif /* REFCLOCK */
914