]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ntp/ntpd/refclock_usno.c
This commit was generated by cvs2svn to compensate for changes in r58310,
[FreeBSD/FreeBSD.git] / contrib / ntp / 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 <stdio.h>
13 #include <ctype.h>
14 #include <sys/time.h>
15 #ifdef HAVE_SYS_IOCTL_H
16 # include <sys/ioctl.h>
17 #endif /* HAVE_SYS_IOCTL_H */
18
19 #include "ntpd.h"
20 #include "ntp_io.h"
21 #include "ntp_unixtime.h"
22 #include "ntp_refclock.h"
23 #include "ntp_stdlib.h"
24 #include "ntp_control.h"
25
26 /*
27  * This driver supports the Naval Observatory dialup at +1 202 653 0351.
28  * It is a hacked-up version of the ACTS driver.
29  *
30  * This driver does not support the `phone' configuration because that
31  * is needlessly global; it would clash with the ACTS driver.
32  *
33  * The Naval Observatory does not support the echo-delay measurement scheme.
34  *
35  * However, this driver *does* support UUCP port locking, allowing the
36  * line to be shared with other processes when not actually dialing
37  * for time.
38  */
39
40 /*
41  * Interface definitions
42  */
43
44 #define DEVICE          "/dev/cua%d" /* device name and unit */
45 #define LOCKFILE        "/var/lock/LCK..cua%d"
46 /* #define LOCKFILE     "/usr/spool/uucp/LCK..cua%d" */
47
48 #define PHONE           "atdt 202 653 0351"
49 /* #define PHONE        "atdt 1 202 653 0351" */
50
51 #define SPEED232        B1200   /* uart speed (1200 cowardly baud) */
52 #define PRECISION       (-10)   /* precision assumed (about 1 ms) */
53 #define REFID           "USNO"  /* reference ID */
54 #define DESCRIPTION     "Naval Observatory dialup"
55
56 #define MODE_AUTO       0       /* automatic mode */
57 #define MODE_BACKUP     1       /* backup mode */
58 #define MODE_MANUAL     2       /* manual mode */
59
60 #define MSGCNT          10      /* we need this many time messages */
61 #define SMAX            80      /* max token string length */
62 #define LENCODE         20      /* length of valid timecode string */
63 #define USNO_MINPOLL    10      /* log2 min poll interval (1024 s) */
64 #define USNO_MAXPOLL    14      /* log2 max poll interval (16384 s) */
65 #define MAXOUTAGE       3600    /* max before USNO kicks in (s) */
66
67 /*
68  * Modem control strings. These may have to be changed for some modems.
69  *
70  * AT   command prefix
71  * B1   initiate call negotiation using Bell 212A
72  * &C1  enable carrier detect
73  * &D2  hang up and return to command mode on DTR transition
74  * E0   modem command echo disabled
75  * l1   set modem speaker volume to low level
76  * M1   speaker enabled untill carrier detect
77  * Q0   return result codes
78  * V1   return result codes as English words
79  */
80 #define MODEM_SETUP     "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */
81 #define MODEM_HANGUP    "ATH"   /* modem disconnect */
82
83 /*
84  * Timeouts
85  */
86 #define IDLE            60      /* idle timeout (s) */
87 #define WAIT            2       /* wait timeout (s) */
88 #define ANSWER          30      /* answer timeout (s) */
89 #define CONNECT         10      /* connect timeout (s) */
90 #define TIMECODE        (MSGCNT+16)     /* timecode timeout (s) */
91
92 /*
93  * Unit control structure
94  */
95 struct usnounit {
96         int     pollcnt;        /* poll message counter */
97
98         int     state;          /* the first one was Delaware */
99         int     run;            /* call program run switch */
100         int     msgcnt;         /* count of time messages received */
101         long    redial;         /* interval to next automatic call */
102         int     unit;           /* unit number (= port) */
103 };
104
105 /*
106  * Function prototypes
107  */
108 static  int     usno_start      P((int, struct peer *));
109 static  void    usno_shutdown   P((int, struct peer *));
110 static  void    usno_poll       P((int, struct peer *));
111 static  void    usno_disc       P((struct peer *));
112 #if 0
113 static  void    usno_timeout    P((struct peer *));
114 static  void    usno_receive    P((struct recvbuf *));
115 static  int     usno_write      P((struct peer *, const char *));
116 #endif /* 0 */
117
118 /*
119  * Transfer vector
120  */
121 struct  refclock refclock_usno = {
122         usno_start,             /* start up driver */
123         usno_shutdown,          /* shut down driver */
124         usno_poll,              /* transmit poll message */
125         noentry,                /* not used (usno_control) */
126         noentry,                /* not used (usno_init) */
127         noentry,                /* not used (usno_buginfo) */
128         NOFLAGS                 /* not used */
129 };
130
131
132 /*
133  * usno_start - open the devices and initialize data for processing
134  */
135 static int
136 usno_start(
137         int unit,
138         struct peer *peer
139         )
140 {
141         register struct usnounit *up;
142         struct refclockproc *pp;
143
144         /*
145          * Initialize miscellaneous variables
146          */
147         pp = peer->procptr;
148         peer->precision = PRECISION;
149         pp->clockdesc = DESCRIPTION;
150         memcpy((char *)&pp->refid, REFID, 4);
151         peer->minpoll = USNO_MINPOLL;
152         peer->maxpoll = USNO_MAXPOLL;
153         peer->sstclktype = CTL_SST_TS_TELEPHONE;
154
155         /*
156          * Allocate and initialize unit structure
157          */
158         if (!(up = (struct usnounit *)
159               emalloc(sizeof(struct usnounit))))
160             return (0);
161         memset((char *)up, 0, sizeof(struct usnounit));
162         up->unit = unit;
163         pp->unitptr = (caddr_t)up;
164
165         /*
166          * Set up the driver timeout
167          */
168         peer->nextdate = current_time + WAIT;
169         return (1);
170 }
171
172
173 /*
174  * usno_shutdown - shut down the clock
175  */
176 static void
177 usno_shutdown(
178         int unit,
179         struct peer *peer
180         )
181 {
182         register struct usnounit *up;
183         struct refclockproc *pp;
184
185 #ifdef DEBUG
186         if (debug)
187             printf("usno: clock %s shutting down\n", ntoa(&peer->srcadr));
188 #endif
189         pp = peer->procptr;
190         up = (struct usnounit *)pp->unitptr;
191         usno_disc(peer);
192         free(up);
193 }
194
195
196 #if 0
197 /*
198  * usno_receive - receive data from the serial interface
199  */
200 static void
201 usno_receive(
202         struct recvbuf *rbufp
203         )
204 {
205         register struct usnounit *up;
206         struct refclockproc *pp;
207         struct peer *peer;
208         char str[SMAX];
209         u_long mjd;             /* Modified Julian Day */
210         static int day, hour, minute, second;
211
212         /*
213          * Initialize pointers and read the timecode and timestamp. If
214          * the OK modem status code, leave it where folks can find it.
215          */
216         peer = (struct peer *)rbufp->recv_srcclock;
217         pp = peer->procptr;
218         up = (struct usnounit *)pp->unitptr;
219         pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
220                                      &pp->lastrec);
221         if (pp->lencode == 0) {
222                 if (strcmp(pp->a_lastcode, "OK") == 0)
223                     pp->lencode = 2;
224                 return;
225         }
226 #ifdef DEBUG
227         if (debug)
228             printf("usno: timecode %d %s\n", pp->lencode,
229                    pp->a_lastcode);
230 #endif
231
232         switch (up->state) {
233
234             case 0:
235
236                 /*
237                  * State 0. We are not expecting anything. Probably
238                  * modem disconnect noise. Go back to sleep.
239                  */
240                 return;
241
242             case 1:
243
244                 /*
245                  * State 1. We are about to dial. Just drop it.
246                  */
247                 return;
248
249             case 2:
250
251                 /*
252                  * State 2. We are waiting for the call to be answered.
253                  * All we care about here is CONNECT as the first token
254                  * in the string. If the modem signals BUSY, ERROR, NO
255                  * ANSWER, NO CARRIER or NO DIALTONE, we immediately
256                  * hang up the phone. If CONNECT doesn't happen after
257                  * ANSWER seconds, hang up the phone. If everything is
258                  * okay, start the connect timeout and slide into state
259                  * 3.
260                  */
261                 (void)strncpy(str, strtok(pp->a_lastcode, " "), SMAX);
262                 if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") ==
263                     0 || strcmp(str, "NO") == 0) {
264                         NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
265                                 msyslog(LOG_NOTICE,
266                                         "clock %s USNO modem status %s",
267                                         ntoa(&peer->srcadr), pp->a_lastcode);
268                         usno_disc(peer);
269                 } else if (strcmp(str, "CONNECT") == 0) {
270                         peer->nextdate = current_time + CONNECT;
271                         up->msgcnt = 0;
272                         up->state++;
273                 } else {
274                         NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
275                                 msyslog(LOG_WARNING,
276                                         "clock %s USNO unknown modem status %s",
277                                         ntoa(&peer->srcadr), pp->a_lastcode);
278                 }
279                 return;
280
281             case 3:
282
283                 /*
284                  * State 3. The call has been answered and we are
285                  * waiting for the first message. If this doesn't
286                  * happen within the timecode timeout, hang up the
287                  * phone. We probably got a wrong number or they are
288                  * down.
289                  */
290                 peer->nextdate = current_time + TIMECODE;
291                 up->state++;
292                 return;
293
294             case 4:
295
296                 /*
297                  * State 4. We are reading a timecode.  It's an actual
298                  * timecode, or it's the `*' OTM.
299                  *
300                  * jjjjj nnn hhmmss UTC
301                  */
302                 if (pp->lencode == LENCODE) {
303                         if (sscanf(pp->a_lastcode, "%5ld %3d %2d%2d%2d UTC",
304                                    &mjd, &day, &hour, &minute, &second) != 5) {
305 #ifdef DEBUG
306                                 if (debug)
307                                     printf("usno: bad timecode format\n");
308 #endif
309                                 refclock_report(peer, CEVNT_BADREPLY);
310                         } else
311                             up->msgcnt++;
312                         return;
313                 } else if (pp->lencode != 1 || !up->msgcnt)
314                     return;
315                 /* else, OTM; drop out of switch */
316         }
317
318         pp->leap = LEAP_NOWARNING;
319         pp->day = day;
320         pp->hour = hour;
321         pp->minute = minute;
322         pp->second = second;
323
324         /*
325          * Colossal hack here. We process each sample in a trimmed-mean
326          * filter and determine the reference clock offset and
327          * dispersion. The fudge time1 value is added to each sample as
328          * received.
329          */
330         if (!refclock_process(pp)) {
331 #ifdef DEBUG
332                 if (debug)
333                     printf("usno: time rejected\n");
334 #endif
335                 refclock_report(peer, CEVNT_BADTIME);
336                 return;
337         } else if (up->msgcnt < MSGCNT)
338             return;
339
340         /*
341          * We have a filtered sample offset ready for peer processing.
342          * We use lastrec as both the reference time and receive time in
343          * order to avoid being cute, like setting the reference time
344          * later than the receive time, which may cause a paranoid
345          * protocol module to chuck out the data. Finaly, we unhook the
346          * timeout, arm for the next call, fold the tent and go home.
347          */
348         record_clock_stats(&peer->srcadr, pp->a_lastcode);
349         refclock_receive(peer);
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 */