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