2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afs/param.h>
11 #include <afsconfig.h>
15 #if defined(REFCLOCK) && defined(PSTI)
16 #define ERR_RATE 60 /* Repeat errors once an hour */
20 * January 1988 -- orignal
21 * January 1989 -- QU version
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.
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
34 * This requires a PSTI ROM revision later 4.01.000 or later.
37 * init_clock_psti(): Open the tty associated with the clock and
38 * set its tty mode bits. Returns fd on success
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.
47 #include <sys/types.h>
48 #include <sys/ioctl.h>
49 #if defined(AFS_SUN_ENV)
57 static int nerrors = 0;
58 static char clockdata[32];
59 #define MIN_READ 13 /* for Rev 4.01.001 */
61 static double reltime();
66 #define CLOCKDEV "/dev/radioclock"
72 #include "AFS_component_version_number.c"
78 struct timeval *tvp, *otvp;
81 if (openclock(CLOCKDEV))
83 (void)readclock(&tvp, &otvp);
88 #endif /* STANDALONE */
91 init_clock_psti(timesource)
101 if ((cfd = open(timesource, 2)) < 0) {
103 if (debug) perror(timesource); else
105 syslog(LOG_ERR, "can't open %s: %m", timesource);
109 if (ioctl(cfd, TIOCEXCL, 0) < 0) {
111 if (debug) perror("TIOCEXCL on radioclock failed"); else
113 syslog(LOG_ERR, "TIOCEXCL on %s failed: %m", timesource);
118 if (ioctl(cfd, TCGETA, &tty) < 0) {
120 if (debug) perror("ioctl on radioclock failed"); else
122 syslog(LOG_ERR, "ioctl on %s failed: %m", timesource);
125 tty.c_cflag = (B9600<<16)|B9600|CS8|CLOCAL|CREAD;
129 bzero((char *)tty.c_cc, sizeof tty.c_cc);
130 tty.c_cc[VMIN] = MIN_READ;
132 if (ioctl(cfd, TCSETA, &tty) < 0) {
133 #else /* TCSETA Use older Berkeley style IOCTL's */
134 bzero((char *)&tty, 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) {
141 if (debug) perror("ioctl on radioclock failed"); else
143 syslog(LOG_ERR, "ioctl on %s failed: %m", timesource);
146 if (write(cfd, "xxxxxxsn\r", 9) != 9) {
148 if (debug) perror("init write to radioclock failed"); else
150 syslog(LOG_ERR, "init write to %s failed: %m", timesource);
153 return(cfd); /* Succeeded in opening the clock */
157 * read_clock_psti() -- Read the PSTI Radio Clock.
159 read_clock_psti(cfd, tvpp, otvpp)
161 struct timeval **tvpp, **otvpp;
163 static struct timeval radiotime;
164 static struct timeval mytime;
165 struct timeval timeout;
167 struct tm radio_tm, *rtm = &radio_tm;
170 register double diff;
180 FD_SET(cfd, &readfds);
184 (void) ioctl(cfd, TIOCFLUSH, 0); /* scrap the I/O queues */
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) {
191 if (debug) printf("radioclock write failed\n"); else
193 if ((nerrors++%ERR_RATE) == 0)
194 syslog(LOG_ERR, "write to radioclock failed: %m");
197 if(select(cfd+1, &readfds, 0, 0, &timeout) != 1) {
199 if (debug) printf("radioclock poll timed out\n"); else
201 if ((nerrors++%ERR_RATE) == 0)
202 syslog(LOG_ERR, "poll of radioclock failed: %m");
205 if ((i = read(cfd, clockdata, sizeof clockdata)) < MIN_READ) {
207 if (debug) printf("radioclock read error (%d)\n", i); else
209 if ((nerrors++%ERR_RATE) == 0)
210 syslog(LOG_ERR, "radioclock read error (%d!=13): %m", i);
214 (void) gettimeofday(&mytime, (struct timezone *)0);
215 /* END OF TIME CRITICAL CODE SECTION!!!! */
217 if (clockdata[i-1] != '\n') {
219 if (debug) printf("radioclock format error1 (%.12s)(0x%x)\n",
220 clockdata, clockdata[12]); else
222 if ((nerrors++%ERR_RATE) == 0)
223 syslog(LOG_ERR, "radioclock format error1 (%.12s)(0x%x)",
224 clockdata, clockdata[12]);
227 for (i = 0; i < 12; i++) {
228 if (clockdata[i] < '0' || clockdata[i] > 'o') {
230 if (debug) printf("radioclock format error2\n"); else
232 if ((nerrors++%ERR_RATE) == 0)
233 syslog(LOG_ERR, "radioclock format error2\n");
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 */
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
252 if(stat2&0x10) { /* Map AM/PM time to Military */
254 if (rtm->tm_hour != 12) rtm->tm_hour += 12;
256 if (rtm->tm_hour == 12) rtm->tm_hour = 0;
260 if (stat1 != 0x4 && (nerrors++%ERR_RATE)==0) {
262 if (debug) printf("radioclock fault #%d 0x%x:%s%s%s%s%s%s\n",
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,":"");
272 sprintf(message, "radioclock fault #%d 0x%x:%s%s%s%s%s%s\n",
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);
283 if (stat1&0x38) /* Out of Spec, Hardware Fault, Signal Fault */
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) {
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);
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);
300 mtm = gmtime(&mytime.tv_sec);
301 diff = reltime(rtm, millis*1000) - reltime(mtm, mytime.tv_usec);
305 strftime (buf, 256, "Clock time: %Y day %j %T", rtm);
306 strcat (buf, ".%03d diff %.3f\n", millis, diff);
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);
317 printf("offset excessive (system %s, clock %s)\n",
322 syslog(LOG_ERR, "offset excessive (system %s, clock %s)\n",
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;
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);
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,":"",
347 stat2&0x4?" Spare?,":"",
348 stat2&0x2?" DST??? +1,":"",
349 stat2&0x1?" DST??? -1,":"");
353 /* If necessary, acknowledge "Clock Reset" flag bit */
355 if (write(cfd, "si0", 3) != 3) {
357 if (debug) printf("radioclock reset write failed\n"); else
359 syslog(LOG_ERR, "reset write to radioclock failed: %m");
363 if (nerrors && stat1==0x4) {
364 syslog(LOG_ERR, "radioclock OK (after %d errors)", nerrors);
374 register struct tm *tm;
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) +