remove-rx-2tier-freepacketq-20050403
[openafs.git] / src / ntp / read_psti.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 RCSID
14     ("$Header$");
15
16 #if     defined(REFCLOCK) && defined(PSTI)
17 #define ERR_RATE        60      /* Repeat errors once an hour */
18
19 /*
20  * read_psti.c
21  *     January 1988 -- orignal
22  *     January 1989 -- QU version
23  *
24  *   This module facilitates reading a Precision Time Standard, Inc.
25  *   WWV radio clock. We assume that clock is configured for 9600 baud,
26  *   no parity. Output is accepted in either 24 or 12 hour format.
27  *   Time is requested and processed in GMT.
28  *
29  *   This version is designed to make use of the QU command due to
30  *   additional information it provides (like date and flags).
31  *   Select is used to prevent hanging in a read when there are
32  *   no characters to read.  The line is run in cooked mode to
33  *   reduce overhead.
34  *
35  *   This requires a PSTI ROM revision later 4.01.000 or later.
36  *
37  *   Routines defined:
38  *      init_clock_psti(): Open the tty associated with the clock and
39  *                         set its tty mode bits. Returns fd on success
40  *                         and -1 on failure.
41  *      read_clock_psti(): Reads the clock and returns either 0 on success
42  *                         or non-zero on error.  On success, pointers are
43  *                         provided to the reference and local time.
44  */
45 #include <stdio.h>
46 #include <syslog.h>
47 #include <sys/time.h>
48 #include <sys/types.h>
49 #include <sys/ioctl.h>
50 #if defined(AFS_SUN_ENV)
51 #include <termio.h>
52 #endif
53
54 #ifdef  DEBUG
55 extern int debug;
56 #endif /* DEBUG */
57
58 static int nerrors = 0;
59 static char clockdata[32];
60 #define MIN_READ 13             /* for Rev 4.01.001 */
61
62 static double reltime();
63
64 #ifdef STANDALONE
65
66 #ifndef CLOCKDEV
67 #define CLOCKDEV "/dev/radioclock"
68 #endif
69
70 #define DEBUG   1
71 int debug = 1;
72
73 #include "AFS_component_version_number.c"
74
75 main(argc, argv)
76      int argc;
77      char **argv;
78 {
79     struct timeval *tvp, *otvp;
80
81     debug = argc;
82     if (openclock(CLOCKDEV))
83         do {
84             (void)readclock(&tvp, &otvp);
85             sleep(1);
86         } while (debug > 1);
87     exit(0);
88 }
89 #endif /* STANDALONE */
90
91
92 init_clock_psti(timesource)
93      char *timesource;
94 {
95     int cfd;
96 #ifdef TCSETA
97     struct termio tty;
98 #else
99     struct sgttyb tty;
100 #endif
101
102     if ((cfd = open(timesource, 2)) < 0) {
103 #ifdef DEBUG
104         if (debug)
105             perror(timesource);
106         else
107 #endif /* DEBUG */
108             syslog(LOG_ERR, "can't open %s: %m", timesource);
109         return (-1);
110     }
111
112     if (ioctl(cfd, TIOCEXCL, 0) < 0) {
113 #ifdef DEBUG
114         if (debug)
115             perror("TIOCEXCL on radioclock failed");
116         else
117 #endif /* DEBUG */
118             syslog(LOG_ERR, "TIOCEXCL on %s failed: %m", timesource);
119         return (-1);
120     }
121 #ifdef TCSETA
122     if (ioctl(cfd, TCGETA, &tty) < 0) {
123 #ifdef DEBUG
124         if (debug)
125             perror("ioctl on radioclock failed");
126         else
127 #endif /* DEBUG */
128             syslog(LOG_ERR, "ioctl on %s failed: %m", timesource);
129         return (-1);
130     }
131     tty.c_cflag = (B9600 << 16) | B9600 | CS8 | CLOCAL | CREAD;
132     tty.c_iflag = ICRNL;
133     tty.c_oflag = 0;
134     tty.c_lflag = 0;
135     memset((char *)tty.c_cc, 0, sizeof tty.c_cc);
136     tty.c_cc[VMIN] = MIN_READ;
137     tty.c_cc[VTIME] = 0;
138     if (ioctl(cfd, TCSETA, &tty) < 0) {
139 #else /* TCSETA  Use older Berkeley style IOCTL's */
140     memset((char *)&tty, 0, sizeof tty);
141     tty.sg_ispeed = tty.sg_ospeed = B9600;
142     tty.sg_flags = ANYP | CRMOD;
143     tty.sg_erase = tty.sg_kill = '\0';
144     if (ioctl(cfd, TIOCSETP, &tty) < 0) {
145 #endif /* TCSETA */
146 #ifdef DEBUG
147         if (debug)
148             perror("ioctl on radioclock failed");
149         else
150 #endif /* DEBUG */
151             syslog(LOG_ERR, "ioctl on %s failed: %m", timesource);
152         return (-1);
153     }
154     if (write(cfd, "xxxxxxsn\r", 9) != 9) {
155 #ifdef DEBUG
156         if (debug)
157             perror("init write to radioclock failed");
158         else
159 #endif /* DEBUG */
160             syslog(LOG_ERR, "init write to %s failed: %m", timesource);
161         return (-1);
162     }
163     return (cfd);               /* Succeeded in opening the clock */
164 }
165
166 /*
167  * read_clock_psti() -- Read the PSTI Radio Clock.
168  */
169 read_clock_psti(cfd, tvpp, otvpp)
170      int cfd;
171      struct timeval **tvpp, **otvpp;
172 {
173     static struct timeval radiotime;
174     static struct timeval mytime;
175     struct timeval timeout;
176     struct tm *mtm;
177     struct tm radio_tm, *rtm = &radio_tm;
178     register int i;
179     register int millis;
180     register double diff;
181     int stat1, stat2;
182     fd_set readfds;
183     char message[256];
184 #ifndef TCSETA
185     register char *cp;
186     int need;
187 #endif /* TCSETA */
188
189     FD_ZERO(&readfds);
190     FD_SET(cfd, &readfds);
191     timeout.tv_sec = 2;
192     timeout.tv_usec = 0;
193
194     (void)ioctl(cfd, TIOCFLUSH, 0);     /* scrap the I/O queues */
195
196     /* BEGIN TIME CRITICAL CODE SECTION!!!!!! */
197     /* EVERY CYCLE FROM THIS POINT OUT ADDS TO THE INACCURACY OF
198      * THE READ CLOCK VALUE!!!!! */
199     if (write(cfd, "\003qu0000", 7) != 7) {
200 #ifdef DEBUG
201         if (debug)
202             printf("radioclock write failed\n");
203         else
204 #endif /* DEBUG */
205         if ((nerrors++ % ERR_RATE) == 0)
206             syslog(LOG_ERR, "write to radioclock failed: %m");
207         return (1);
208     }
209     if (select(cfd + 1, &readfds, 0, 0, &timeout) != 1) {
210 #ifdef DEBUG
211         if (debug)
212             printf("radioclock poll timed out\n");
213         else
214 #endif /* DEBUG */
215         if ((nerrors++ % ERR_RATE) == 0)
216             syslog(LOG_ERR, "poll of radioclock failed: %m");
217         return (1);
218     }
219     if ((i = read(cfd, clockdata, sizeof clockdata)) < MIN_READ) {
220 #ifdef DEBUG
221         if (debug)
222             printf("radioclock read error (%d)\n", i);
223         else
224 #endif /* DEBUG */
225         if ((nerrors++ % ERR_RATE) == 0)
226             syslog(LOG_ERR, "radioclock read error (%d!=13): %m", i);
227         return (1);
228     }
229
230     (void)gettimeofday(&mytime, NULL);
231     /* END OF TIME CRITICAL CODE SECTION!!!! */
232
233     if (clockdata[i - 1] != '\n') {
234 #ifdef DEBUG
235         if (debug)
236             printf("radioclock format error1 (%.12s)(0x%x)\n", clockdata,
237                    clockdata[12]);
238         else
239 #endif /* DEBUG */
240         if ((nerrors++ % ERR_RATE) == 0)
241             syslog(LOG_ERR, "radioclock format error1 (%.12s)(0x%x)",
242                    clockdata, clockdata[12]);
243         return (1);
244     }
245     for (i = 0; i < 12; i++) {
246         if (clockdata[i] < '0' || clockdata[i] > 'o') {
247 #ifdef DEBUG
248             if (debug)
249                 printf("radioclock format error2\n");
250             else
251 #endif /* DEBUG */
252             if ((nerrors++ % ERR_RATE) == 0)
253                 syslog(LOG_ERR, "radioclock format error2\n");
254             return (1);
255         }
256     }
257     stat1 = clockdata[0] - '0';
258     stat2 = clockdata[1] - '0';
259     millis = ((clockdata[2] - '0') * 64) + (clockdata[3] - '0');
260     rtm->tm_sec = (clockdata[4] - '0');
261     rtm->tm_min = (clockdata[5] - '0');
262     rtm->tm_hour = (clockdata[6] - '0');
263     rtm->tm_yday = ((clockdata[7] - '0') * 64) + (clockdata[8] - '0') - 1;
264     rtm->tm_year = 86 + (clockdata[9] - '0');
265     /* byte 10 and 11 reserved */
266
267     /*
268      * Correct "hours" based on whether or not AM/PM mode is enabled.
269      * If clock is in 24 hour (military) mode then no correction is
270      * needed.
271      */
272     if (stat2 & 0x10) {         /* Map AM/PM time to Military */
273         if (stat2 & 0x8) {
274             if (rtm->tm_hour != 12)
275                 rtm->tm_hour += 12;
276         } else {
277             if (rtm->tm_hour == 12)
278                 rtm->tm_hour = 0;
279         }
280     }
281
282     if (stat1 != 0x4 && (nerrors++ % ERR_RATE) == 0) {
283 #ifdef DEBUG
284         if (debug)
285             printf("radioclock fault #%d 0x%x:%s%s%s%s%s%s\n", nerrors, stat1,
286                    stat1 & 0x20 ? " Out of Spec," : "",
287                    stat1 & 0x10 ? " Hardware Fault," : "",
288                    stat1 & 0x8 ? " Signal Fault," : "",
289                    stat1 & 0x4 ? " Time Avail," : "",
290                    stat1 & 0x2 ? " Year Mismatch," : "",
291                    stat1 & 0x1 ? " Clock Reset," : "");
292         else {
293 #endif /* DEBUG */
294             sprintf(message, "radioclock fault #%d 0x%x:%s%s%s%s%s%s\n",
295                     nerrors, stat1, stat1 & 0x20 ? " Out of Spec," : "",
296                     stat1 & 0x10 ? " Hardware Fault," : "",
297                     stat1 & 0x8 ? " Signal Fault," : "",
298                     stat1 & 0x4 ? " Time Avail," : "",
299                     stat1 & 0x2 ? " Year Mismatch," : "",
300                     stat1 & 0x1 ? " Clock Reset," : "");
301             syslog(LOG_ERR, message);
302         }
303     }
304     if (stat1 & 0x38)           /* Out of Spec, Hardware Fault, Signal Fault */
305         return (1);
306     if ((millis > 999 || rtm->tm_sec > 60 || rtm->tm_min > 60
307          || rtm->tm_hour > 23 || rtm->tm_yday > 365)
308         && (nerrors++ % ERR_RATE) == 0) {
309 #ifdef DEBUG
310         if (debug)
311             printf("radioclock bogon #%d: %dd %dh %dm %ds %dms\n", nerrors,
312                    rtm->tm_yday, rtm->tm_hour, rtm->tm_min, rtm->tm_sec,
313                    millis);
314         else
315 #endif /* DEBUG */
316             sprintf(message, "radioclock bogon #%d: %dd %dh %dm %ds %dms\n",
317                     nerrors, rtm->tm_yday, rtm->tm_hour, rtm->tm_min,
318                     rtm->tm_sec, millis);
319         syslog(LOG_ERR, message);
320         return (1);
321     }
322
323     mtm = gmtime(&mytime.tv_sec);
324     diff = reltime(rtm, millis * 1000) - reltime(mtm, mytime.tv_usec);
325 #ifdef DEBUG
326     if (debug > 1) {
327         char buf[256];
328         strftime(buf, 256, "Clock time:  %Y day %j %T", rtm);
329         strcat(buf, ".%03d diff %.3f\n", millis, diff);
330         printf(buf);
331     }
332 #endif /* DEBUG */
333
334     if (diff > (90 * 24 * 60 * 60.0) && (nerrors++ % ERR_RATE) == 0) {
335         char buf1[16], buf2[16];
336         strftime(buf1, 16, "%Y/%j", mtm);
337         strftime(buf2, 16, "%Y/%j", rtm);
338 #ifdef DEBUG
339         if (debug) {
340             printf("offset excessive (system %s, clock %s)\n", buf1, buf2);
341         } else
342 #endif /* DEBUG */
343             syslog(LOG_ERR, "offset excessive (system %s, clock %s)\n", buf1,
344                    buf2);
345         return (1);
346     }
347
348     diff += (double)mytime.tv_sec + ((double)mytime.tv_usec / 1000000.0);
349     radiotime.tv_sec = diff;
350     radiotime.tv_usec = (diff - (double)radiotime.tv_sec) * 1000000;
351 #ifdef DEBUG
352     if (debug > 1) {
353         char buf[256];
354         strftime(buf, 256, "%Y day %j %T", mtm);
355         printf("System time: %s.%03d\n", buf, mytime.tv_usec / 1000);
356         printf("stat1 0%o, stat2 0%o: ", stat1, stat2);
357         if (stat1 || stat2)
358             printf("%s%s%s%s%s%s%s%s%s%s%s%s",
359                    stat1 & 0x20 ? " Out of Spec," : "",
360                    stat1 & 0x10 ? " Hardware Fault," : "",
361                    stat1 & 0x8 ? " Signal Fault," : "",
362                    stat1 & 0x4 ? " Time Avail," : "",
363                    stat1 & 0x2 ? " Year Mismatch," : "",
364                    stat1 & 0x1 ? " Clock Reset," : "",
365                    stat2 & 0x20 ? " DST on," : "",
366                    stat2 & 0x10 ? " 12hr mode," : "",
367                    stat2 & 0x8 ? " PM," : "", stat2 & 0x4 ? " Spare?," : "",
368                    stat2 & 0x2 ? " DST??? +1," : "",
369                    stat2 & 0x1 ? " DST??? -1," : "");
370         printf("\n");
371     }
372 #endif /* DEBUG */
373     /* If necessary, acknowledge "Clock Reset" flag bit */
374     if (stat1 & 0x1) {
375         if (write(cfd, "si0", 3) != 3) {
376 #ifdef DEBUG
377             if (debug)
378                 printf("radioclock reset write failed\n");
379             else
380 #endif /* DEBUG */
381                 syslog(LOG_ERR, "reset write to radioclock failed: %m");
382             return (1);
383         }
384     }
385     if (nerrors && stat1 == 0x4) {
386         syslog(LOG_ERR, "radioclock OK (after %d errors)", nerrors);
387         nerrors = 0;
388     }
389     *tvpp = &radiotime;
390     *otvpp = &mytime;
391     return (0);
392 }
393
394 static double
395 reltime(tm, usec)
396      register struct tm *tm;
397      register int usec;
398 {
399     return (tm->tm_year * (366.0 * 24.0 * 60.0 * 60.0) +
400             tm->tm_yday * (24.0 * 60.0 * 60.0) + tm->tm_hour * (60.0 * 60.0) +
401             tm->tm_min * (60.0) + tm->tm_sec + usec / 1000000.0);
402 }
403 #endif