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