]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - ntpd/refclock_usno.c
Fix compilation with gcc 4.1. This is imported on the vendor branch as it
[FreeBSD/FreeBSD.git] / ntpd / refclock_usno.c
1 /*
2  * refclock_usno - clock driver for the Naval Observatory dialup
3  * Michael Shields <shields@tembel.org> 1995/02/25
4  */
5
6 #ifdef HAVE_CONFIG_H
7 #include <config.h>
8 #endif
9
10 #if defined(REFCLOCK) && defined(CLOCK_USNO)
11
12 #include "ntpd.h"
13 #include "ntp_io.h"
14 #include "ntp_unixtime.h"
15 #include "ntp_refclock.h"
16 #include "ntp_stdlib.h"
17 #include "ntp_control.h"
18
19 #include <stdio.h>
20 #include <ctype.h>
21 #ifdef HAVE_SYS_IOCTL_H
22 # include <sys/ioctl.h>
23 #endif /* HAVE_SYS_IOCTL_H */
24
25 /*
26  * This driver supports the Naval Observatory dialup at +1 202 653 0351.
27  * It is a hacked-up version of the ACTS driver.
28  *
29  * This driver does not support the `phone' configuration because that
30  * is needlessly global; it would clash with the ACTS driver.
31  *
32  * The Naval Observatory does not support the echo-delay measurement scheme.
33  *
34  * However, this driver *does* support UUCP port locking, allowing the
35  * line to be shared with other processes when not actually dialing
36  * for time.
37  */
38
39 /*
40  * Interface definitions
41  */
42
43 #define DEVICE          "/dev/cua%d" /* device name and unit */
44 #define LOCKFILE        "/var/lock/LCK..cua%d"
45 /* #define LOCKFILE     "/usr/spool/uucp/LCK..cua%d" */
46
47 #define PHONE           "atdt 202 653 0351"
48 /* #define PHONE        "atdt 1 202 653 0351" */
49
50 #define SPEED232        B1200   /* uart speed (1200 cowardly baud) */
51 #define PRECISION       (-10)   /* precision assumed (about 1 ms) */
52 #define REFID           "USNO"  /* reference ID */
53 #define DESCRIPTION     "Naval Observatory dialup"
54
55 #define MODE_AUTO       0       /* automatic mode */
56 #define MODE_BACKUP     1       /* backup mode */
57 #define MODE_MANUAL     2       /* manual mode */
58
59 #define MSGCNT          10      /* we need this many time messages */
60 #define SMAX            80      /* max token string length */
61 #define LENCODE         20      /* length of valid timecode string */
62 #define USNO_MINPOLL    10      /* log2 min poll interval (1024 s) */
63 #define USNO_MAXPOLL    14      /* log2 max poll interval (16384 s) */
64 #define MAXOUTAGE       3600    /* max before USNO kicks in (s) */
65
66 /*
67  * Modem control strings. These may have to be changed for some modems.
68  *
69  * AT   command prefix
70  * B1   initiate call negotiation using Bell 212A
71  * &C1  enable carrier detect
72  * &D2  hang up and return to command mode on DTR transition
73  * E0   modem command echo disabled
74  * l1   set modem speaker volume to low level
75  * M1   speaker enabled untill carrier detect
76  * Q0   return result codes
77  * V1   return result codes as English words
78  */
79 #define MODEM_SETUP     "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */
80 #define MODEM_HANGUP    "ATH"   /* modem disconnect */
81
82 /*
83  * Timeouts
84  */
85 #define IDLE            60      /* idle timeout (s) */
86 #define WAIT            2       /* wait timeout (s) */
87 #define ANSWER          30      /* answer timeout (s) */
88 #define CONNECT         10      /* connect timeout (s) */
89 #define TIMECODE        (MSGCNT+16)     /* timecode timeout (s) */
90
91 /*
92  * Unit control structure
93  */
94 struct usnounit {
95         int     pollcnt;        /* poll message counter */
96
97         int     state;          /* the first one was Delaware */
98         int     run;            /* call program run switch */
99         int     msgcnt;         /* count of time messages received */
100         long    redial;         /* interval to next automatic call */
101         int     unit;           /* unit number (= port) */
102 };
103
104 /*
105  * Function prototypes
106  */
107 static  int     usno_start      P((int, struct peer *));
108 static  void    usno_shutdown   P((int, struct peer *));
109 static  void    usno_poll       P((int, struct peer *));
110 static  void    usno_disc       P((struct peer *));
111 #if 0
112 static  void    usno_timeout    P((struct peer *));
113 static  void    usno_receive    P((struct recvbuf *));
114 static  int     usno_write      P((struct peer *, const char *));
115 #endif /* 0 */
116
117 /*
118  * Transfer vector
119  */
120 struct  refclock refclock_usno = {
121         usno_start,             /* start up driver */
122         usno_shutdown,          /* shut down driver */
123         usno_poll,              /* transmit poll message */
124         noentry,                /* not used (usno_control) */
125         noentry,                /* not used (usno_init) */
126         noentry,                /* not used (usno_buginfo) */
127         NOFLAGS                 /* not used */
128 };
129
130
131 /*
132  * usno_start - open the devices and initialize data for processing
133  */
134 static int
135 usno_start(
136         int unit,
137         struct peer *peer
138         )
139 {
140         register struct usnounit *up;
141         struct refclockproc *pp;
142
143         /*
144          * Initialize miscellaneous variables
145          */
146         pp = peer->procptr;
147         peer->precision = PRECISION;
148         pp->clockdesc = DESCRIPTION;
149         memcpy((char *)&pp->refid, REFID, 4);
150         peer->minpoll = USNO_MINPOLL;
151         peer->maxpoll = USNO_MAXPOLL;
152         peer->sstclktype = CTL_SST_TS_TELEPHONE;
153
154         /*
155          * Allocate and initialize unit structure
156          */
157         if (!(up = (struct usnounit *)
158               emalloc(sizeof(struct usnounit))))
159             return (0);
160         memset((char *)up, 0, sizeof(struct usnounit));
161         up->unit = unit;
162         pp->unitptr = (caddr_t)up;
163
164         /*
165          * Set up the driver timeout
166          */
167         peer->nextdate = current_time + WAIT;
168         return (1);
169 }
170
171
172 /*
173  * usno_shutdown - shut down the clock
174  */
175 static void
176 usno_shutdown(
177         int unit,
178         struct peer *peer
179         )
180 {
181         register struct usnounit *up;
182         struct refclockproc *pp;
183
184 #ifdef DEBUG
185         if (debug)
186             printf("usno: clock %s shutting down\n", ntoa(&peer->srcadr));
187 #endif
188         pp = peer->procptr;
189         up = (struct usnounit *)pp->unitptr;
190         usno_disc(peer);
191         free(up);
192 }
193
194
195 #if 0
196 /*
197  * usno_receive - receive data from the serial interface
198  */
199 static void
200 usno_receive(
201         struct recvbuf *rbufp
202         )
203 {
204         register struct usnounit *up;
205         struct refclockproc *pp;
206         struct peer *peer;
207         char str[SMAX];
208         u_long mjd;             /* Modified Julian Day */
209         static int day, hour, minute, second;
210
211         /*
212          * Initialize pointers and read the timecode and timestamp. If
213          * the OK modem status code, leave it where folks can find it.
214          */
215         peer = (struct peer *)rbufp->recv_srcclock;
216         pp = peer->procptr;
217         up = (struct usnounit *)pp->unitptr;
218         pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
219                                      &pp->lastrec);
220         if (pp->lencode == 0) {
221                 if (strcmp(pp->a_lastcode, "OK") == 0)
222                     pp->lencode = 2;
223                 return;
224         }
225 #ifdef DEBUG
226         if (debug)
227             printf("usno: timecode %d %s\n", pp->lencode,
228                    pp->a_lastcode);
229 #endif
230
231         switch (up->state) {
232
233             case 0:
234
235                 /*
236                  * State 0. We are not expecting anything. Probably
237                  * modem disconnect noise. Go back to sleep.
238                  */
239                 return;
240
241             case 1:
242
243                 /*
244                  * State 1. We are about to dial. Just drop it.
245                  */
246                 return;
247
248             case 2:
249
250                 /*
251                  * State 2. We are waiting for the call to be answered.
252                  * All we care about here is CONNECT as the first token
253                  * in the string. If the modem signals BUSY, ERROR, NO
254                  * ANSWER, NO CARRIER or NO DIALTONE, we immediately
255                  * hang up the phone. If CONNECT doesn't happen after
256                  * ANSWER seconds, hang up the phone. If everything is
257                  * okay, start the connect timeout and slide into state
258                  * 3.
259                  */
260                 (void)strncpy(str, strtok(pp->a_lastcode, " "), SMAX);
261                 if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") ==
262                     0 || strcmp(str, "NO") == 0) {
263                         NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
264                                 msyslog(LOG_NOTICE,
265                                         "clock %s USNO modem status %s",
266                                         ntoa(&peer->srcadr), pp->a_lastcode);
267                         usno_disc(peer);
268                 } else if (strcmp(str, "CONNECT") == 0) {
269                         peer->nextdate = current_time + CONNECT;
270                         up->msgcnt = 0;
271                         up->state++;
272                 } else {
273                         NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
274                                 msyslog(LOG_WARNING,
275                                         "clock %s USNO unknown modem status %s",
276                                         ntoa(&peer->srcadr), pp->a_lastcode);
277                 }
278                 return;
279
280             case 3:
281
282                 /*
283                  * State 3. The call has been answered and we are
284                  * waiting for the first message. If this doesn't
285                  * happen within the timecode timeout, hang up the
286                  * phone. We probably got a wrong number or they are
287                  * down.
288                  */
289                 peer->nextdate = current_time + TIMECODE;
290                 up->state++;
291                 return;
292
293             case 4:
294
295                 /*
296                  * State 4. We are reading a timecode.  It's an actual
297                  * timecode, or it's the `*' OTM.
298                  *
299                  * jjjjj nnn hhmmss UTC
300                  */
301                 if (pp->lencode == LENCODE) {
302                         if (sscanf(pp->a_lastcode, "%5ld %3d %2d%2d%2d UTC",
303                                    &mjd, &day, &hour, &minute, &second) != 5) {
304 #ifdef DEBUG
305                                 if (debug)
306                                     printf("usno: bad timecode format\n");
307 #endif
308                                 refclock_report(peer, CEVNT_BADREPLY);
309                         } else
310                             up->msgcnt++;
311                         return;
312                 } else if (pp->lencode != 1 || !up->msgcnt)
313                     return;
314                 /* else, OTM; drop out of switch */
315         }
316
317         pp->leap = LEAP_NOWARNING;
318         pp->day = day;
319         pp->hour = hour;
320         pp->minute = minute;
321         pp->second = second;
322
323         /*
324          * Colossal hack here. We process each sample in a trimmed-mean
325          * filter and determine the reference clock offset and
326          * dispersion. The fudge time1 value is added to each sample as
327          * received.
328          */
329         if (!refclock_process(pp)) {
330 #ifdef DEBUG
331                 if (debug)
332                     printf("usno: time rejected\n");
333 #endif
334                 refclock_report(peer, CEVNT_BADTIME);
335                 return;
336         } else if (up->msgcnt < MSGCNT)
337             return;
338
339         /*
340          * We have a filtered sample offset ready for peer processing.
341          * We use lastrec as both the reference time and receive time in
342          * order to avoid being cute, like setting the reference time
343          * later than the receive time, which may cause a paranoid
344          * protocol module to chuck out the data. Finaly, we unhook the
345          * timeout, arm for the next call, fold the tent and go home.
346          */
347         pp->lastref = pp->lastrec;
348         refclock_receive(peer);
349         record_clock_stats(&peer->srcadr, pp->a_lastcode);
350         pp->sloppyclockflag &= ~CLK_FLAG1;
351         up->pollcnt = 0;
352         up->state = 0;
353         usno_disc(peer);
354 }
355 #endif /* 0 */
356
357
358 /*
359  * usno_poll - called by the transmit routine
360  */
361 static void
362 usno_poll(
363         int unit,
364         struct peer *peer
365         )
366 {
367         register struct usnounit *up;
368         struct refclockproc *pp;
369
370         /*
371          * If the driver is running, we set the enable flag (fudge
372          * flag1), which causes the driver timeout routine to initiate a
373          * call. If not, the enable flag can be set using
374          * ntpdc. If this is the sustem peer, then follow the system
375          * poll interval.
376          */
377         pp = peer->procptr;
378         up = (struct usnounit *)pp->unitptr;
379         if (up->run) {
380                 pp->sloppyclockflag |= CLK_FLAG1;
381                 if (peer == sys_peer)
382                     peer->hpoll = sys_poll;
383                 else
384                     peer->hpoll = peer->minpoll;
385         }
386 }
387
388
389 #if 0
390 /*
391  * usno_timeout - called by the timer interrupt
392  */
393 static void
394 usno_timeout(
395         struct peer *peer
396         )
397 {
398         register struct usnounit *up;
399         struct refclockproc *pp;
400         int fd;
401         char device[20];
402         char lockfile[128], pidbuf[8];
403         int dtr = TIOCM_DTR;
404
405         /*
406          * If a timeout occurs in other than state 0, the call has
407          * failed. If in state 0, we just see if there is other work to
408          * do.
409          */
410         pp = peer->procptr;
411         up = (struct usnounit *)pp->unitptr;
412         if (up->state) {
413                 if (up->state != 1) {
414                         usno_disc(peer);
415                         return;
416                 }
417                 /*
418                  * Call, and start the answer timeout. We think it
419                  * strange if the OK status has not been received from
420                  * the modem, but plow ahead anyway.
421                  *
422                  * This code is *here* because we had to stick in a brief
423                  * delay to let the modem settle down after raising DTR,
424                  * and for the OK to be received.  State machines are
425                  * contorted.
426                  */
427                 if (strcmp(pp->a_lastcode, "OK") != 0)
428                     NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
429                             msyslog(LOG_NOTICE, "clock %s USNO no modem status",
430                                     ntoa(&peer->srcadr));
431                 (void)usno_write(peer, PHONE);
432                 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
433                         msyslog(LOG_NOTICE, "clock %s USNO calling %s\n",
434                                 ntoa(&peer->srcadr), PHONE);
435                 up->state = 2;
436                 up->pollcnt++;
437                 pp->polls++;
438                 peer->nextdate = current_time + ANSWER;
439                 return;
440         }
441         switch (peer->ttl) {
442
443                 /*
444                  * In manual mode the calling program is activated
445                  * by the ntpdc program using the enable flag (fudge
446                  * flag1), either manually or by a cron job.
447                  */
448             case MODE_MANUAL:
449                 up->run = 0;
450                 break;
451
452                 /*
453                  * In automatic mode the calling program runs
454                  * continuously at intervals determined by the sys_poll
455                  * variable.
456                  */
457             case MODE_AUTO:
458                 if (!up->run)
459                     pp->sloppyclockflag |= CLK_FLAG1;
460                 up->run = 1;
461                 break;
462
463                 /*
464                  * In backup mode the calling program is disabled,
465                  * unless no system peer has been selected for MAXOUTAGE
466                  * (3600 s). Once enabled, it runs until some other NTP
467                  * peer shows up.
468                  */
469             case MODE_BACKUP:
470                 if (!up->run && sys_peer == 0) {
471                         if (current_time - last_time > MAXOUTAGE) {
472                                 up->run = 1;
473                                 peer->hpoll = peer->minpoll;
474                                 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
475                                         msyslog(LOG_NOTICE,
476                                                 "clock %s USNO backup started ",
477                                                 ntoa(&peer->srcadr));
478                         }
479                 } else if (up->run && sys_peer->sstclktype != CTL_SST_TS_TELEPHONE) {
480                         peer->hpoll = peer->minpoll;
481                         up->run = 0;
482                         NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
483                                 msyslog(LOG_NOTICE,
484                                         "clock %s USNO backup stopped",
485                                         ntoa(&peer->srcadr));
486                 }
487                 break;
488
489             default:
490                 msyslog(LOG_ERR,
491                         "clock %s USNO invalid mode", ntoa(&peer->srcadr));
492                 
493         }
494
495         /*
496          * The fudge flag1 is used as an enable/disable; if set either
497          * by the code or via ntpdc, the calling program is
498          * started; if reset, the phones stop ringing.
499          */
500         if (!(pp->sloppyclockflag & CLK_FLAG1)) {
501                 up->pollcnt = 0;
502                 peer->nextdate = current_time + IDLE;
503                 return;
504         }
505
506         /*
507          * Lock the port.
508          */
509         (void)sprintf(lockfile, LOCKFILE, up->unit);
510         fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644);
511         if (fd < 0) {
512                 msyslog(LOG_ERR, "clock %s USNO port busy",
513                         ntoa(&peer->srcadr));
514                 return;
515         }
516         sprintf(pidbuf, "%d\n", (int) getpid());
517         write(fd, pidbuf, strlen(pidbuf));
518         close(fd);
519
520         /*
521          * Open serial port. Use ACTS line discipline, if available. It
522          * pumps a timestamp into the data stream at every on-time
523          * character '*' found. Note: the port must have modem control
524          * or deep pockets for the phone bill. HP-UX 9.03 users should
525          * have very deep pockets.
526          */
527         (void)sprintf(device, DEVICE, up->unit);
528         if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS))) {
529                 unlink(lockfile);
530                 return;
531         }
532         if (ioctl(fd, TIOCMBIC, (char *)&dtr) < 0)
533             msyslog(LOG_WARNING, "usno_timeout: clock %s: couldn't clear DTR: %m",
534                     ntoa(&peer->srcadr));
535
536         pp->io.clock_recv = usno_receive;
537         pp->io.srcclock = (caddr_t)peer;
538         pp->io.datalen = 0;
539         pp->io.fd = fd;
540         if (!io_addclock(&pp->io)) {
541                 (void) close(fd);
542                 unlink(lockfile);
543                 free(up);
544                 return;
545         }
546
547         /*
548          * Initialize modem and kill DTR. We skedaddle if this comes
549          * bum.
550          */
551         if (!usno_write(peer, MODEM_SETUP)) {
552                 msyslog(LOG_ERR, "clock %s USNO couldn't write",
553                         ntoa(&peer->srcadr));
554                 io_closeclock(&pp->io);
555                 unlink(lockfile);
556                 free(up);
557                 return;
558         }
559
560         /*
561          * Initiate a call to the Observatory. If we wind up here in
562          * other than state 0, a successful call could not be completed
563          * within minpoll seconds.
564          */
565         if (up->pollcnt) {
566                 refclock_report(peer, CEVNT_TIMEOUT);
567                 NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
568                         msyslog(LOG_NOTICE,
569                                 "clock %s USNO calling program terminated",
570                                 ntoa(&peer->srcadr));
571                 pp->sloppyclockflag &= ~CLK_FLAG1;
572                 up->pollcnt = 0;
573 #ifdef DEBUG
574                 if (debug)
575                     printf("usno: calling program terminated\n");
576 #endif
577                 usno_disc(peer);
578                 return;
579         }
580
581         /*
582          * Raise DTR, and let the modem settle.  Then we'll dial.
583          */
584         if (ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr) < -1)
585             msyslog(LOG_INFO, "usno_timeout: clock %s: couldn't set DTR: %m",
586                     ntoa(&peer->srcadr));
587         up->state = 1;
588         peer->nextdate = current_time + WAIT;
589 }
590 #endif /* 0 */
591
592
593 /*
594  * usno_disc - disconnect the call and wait for the ruckus to cool
595  */
596 static void
597 usno_disc(
598         struct peer *peer
599         )
600 {
601         register struct usnounit *up;
602         struct refclockproc *pp;
603         int dtr = TIOCM_DTR;
604         char lockfile[128];
605
606         /*
607          * We should never get here other than in state 0, unless a call
608          * has timed out. We drop DTR, which will reliably get the modem
609          * off the air, even while the modem is hammering away full tilt.
610          */
611         pp = peer->procptr;
612         up = (struct usnounit *)pp->unitptr;
613
614         if (ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr) < 0)
615             msyslog(LOG_INFO, "usno_disc: clock %s: couldn't clear DTR: %m",
616                     ntoa(&peer->srcadr));
617
618         if (up->state > 0) {
619                 up->state = 0;
620                 msyslog(LOG_NOTICE, "clock %s USNO call failed %d",
621                         ntoa(&peer->srcadr), up->state);
622 #ifdef DEBUG
623                 if (debug)
624                     printf("usno: call failed %d\n", up->state);
625 #endif
626         }
627
628         io_closeclock(&pp->io);
629         sprintf(lockfile, LOCKFILE, up->unit);
630         unlink(lockfile);
631
632         peer->nextdate = current_time + WAIT;
633 }
634
635
636 #if 0
637 /*
638  * usno_write - write a message to the serial port
639  */
640 static int
641 usno_write(
642         struct peer *peer,
643         const char *str
644         )
645 {
646         register struct usnounit *up;
647         struct refclockproc *pp;
648         int len;
649         int code;
650         char cr = '\r';
651
652         /*
653          * Not much to do here, other than send the message, handle
654          * debug and report faults.
655          */
656         pp = peer->procptr;
657         up = (struct usnounit *)pp->unitptr;
658         len = strlen(str);
659 #ifdef DEBUG
660         if (debug)
661             printf("usno: state %d send %d %s\n", up->state, len,
662                    str);
663 #endif
664         code = write(pp->io.fd, str, (unsigned)len) == len;
665         code |= write(pp->io.fd, &cr, 1) == 1;
666         if (!code)
667             refclock_report(peer, CEVNT_FAULT);
668         return (code);
669 }
670 #endif /* 0 */
671
672 #else
673 int refclock_usno_bs;
674 #endif /* REFCLOCK */