1c0b746e5SOllivier Robert /* 2c0b746e5SOllivier Robert * refclock_dumbclock - clock driver for a unknown time distribution system 3c0b746e5SOllivier Robert * that only provides hh:mm:ss (in local time, yet!). 4c0b746e5SOllivier Robert */ 5c0b746e5SOllivier Robert 6c0b746e5SOllivier Robert /* 7c0b746e5SOllivier Robert * Must interpolate back to local time. Very annoying. 8c0b746e5SOllivier Robert */ 9c0b746e5SOllivier Robert #define GET_LOCALTIME 10c0b746e5SOllivier Robert 11c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 12c0b746e5SOllivier Robert #include <config.h> 13c0b746e5SOllivier Robert #endif 14c0b746e5SOllivier Robert 15c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK) 16c0b746e5SOllivier Robert 17c0b746e5SOllivier Robert #include <stdio.h> 18c0b746e5SOllivier Robert #include <ctype.h> 19c0b746e5SOllivier Robert #include <sys/time.h> 20c0b746e5SOllivier Robert #include <time.h> 21c0b746e5SOllivier Robert 22c0b746e5SOllivier Robert #include "ntpd.h" 23c0b746e5SOllivier Robert #include "ntp_io.h" 24c0b746e5SOllivier Robert #include "ntp_refclock.h" 25c0b746e5SOllivier Robert #include "ntp_calendar.h" 26c0b746e5SOllivier Robert #include "ntp_stdlib.h" 27c0b746e5SOllivier Robert 28c0b746e5SOllivier Robert /* 29c0b746e5SOllivier Robert * This driver supports a generic dumb clock that only outputs hh:mm:ss, 30c0b746e5SOllivier Robert * in local time, no less. 31c0b746e5SOllivier Robert * 32c0b746e5SOllivier Robert * Input format: 33c0b746e5SOllivier Robert * 34c0b746e5SOllivier Robert * hh:mm:ss <cr> 35c0b746e5SOllivier Robert * 36c0b746e5SOllivier Robert * hh:mm:ss -- what you'd expect, with a 24 hour clock. (Heck, that's the only 37c0b746e5SOllivier Robert * way it could get stupider.) We take time on the <cr>. 38c0b746e5SOllivier Robert * 39c0b746e5SOllivier Robert * The original source of this module was the WWVB module. 40c0b746e5SOllivier Robert */ 41c0b746e5SOllivier Robert 42c0b746e5SOllivier Robert /* 43c0b746e5SOllivier Robert * Interface definitions 44c0b746e5SOllivier Robert */ 45c0b746e5SOllivier Robert #define DEVICE "/dev/dumbclock%d" /* device name and unit */ 46c0b746e5SOllivier Robert #define SPEED232 B9600 /* uart speed (9600 baud) */ 47c0b746e5SOllivier Robert #define PRECISION (-13) /* precision assumed (about 100 us) */ 48c0b746e5SOllivier Robert #define REFID "dumbclock" /* reference ID */ 49c0b746e5SOllivier Robert #define DESCRIPTION "Dumb clock" /* WRU */ 50c0b746e5SOllivier Robert 51c0b746e5SOllivier Robert 52c0b746e5SOllivier Robert /* 53c0b746e5SOllivier Robert * Insanity check. Since the time is local, we need to make sure that during midnight 54c0b746e5SOllivier Robert * transitions, we can convert back to Unix time. If the conversion results in some number 55c0b746e5SOllivier Robert * worse than this number of seconds away, assume the next day and retry. 56c0b746e5SOllivier Robert */ 57c0b746e5SOllivier Robert #define INSANE_SECONDS 3600 58c0b746e5SOllivier Robert 59c0b746e5SOllivier Robert /* 60c0b746e5SOllivier Robert * Dumb clock control structure 61c0b746e5SOllivier Robert */ 62c0b746e5SOllivier Robert struct dumbclock_unit { 63c0b746e5SOllivier Robert u_char tcswitch; /* timecode switch */ 64c0b746e5SOllivier Robert l_fp laststamp; /* last receive timestamp */ 65c0b746e5SOllivier Robert u_char lasthour; /* last hour (for monitor) */ 66c0b746e5SOllivier Robert u_char linect; /* count ignored lines (for monitor */ 67c0b746e5SOllivier Robert struct tm ymd; /* struct tm for y/m/d only */ 68c0b746e5SOllivier Robert }; 69c0b746e5SOllivier Robert 70c0b746e5SOllivier Robert /* 71c0b746e5SOllivier Robert * Function prototypes 72c0b746e5SOllivier Robert */ 73c0b746e5SOllivier Robert static int dumbclock_start P((int, struct peer *)); 74c0b746e5SOllivier Robert static void dumbclock_shutdown P((int, struct peer *)); 75c0b746e5SOllivier Robert static void dumbclock_receive P((struct recvbuf *)); 76c0b746e5SOllivier Robert #if 0 77c0b746e5SOllivier Robert static void dumbclock_poll P((int, struct peer *)); 78c0b746e5SOllivier Robert #endif 79c0b746e5SOllivier Robert 80c0b746e5SOllivier Robert /* 81c0b746e5SOllivier Robert * Transfer vector 82c0b746e5SOllivier Robert */ 83c0b746e5SOllivier Robert struct refclock refclock_dumbclock = { 84c0b746e5SOllivier Robert dumbclock_start, /* start up driver */ 85c0b746e5SOllivier Robert dumbclock_shutdown, /* shut down driver */ 86c0b746e5SOllivier Robert noentry, /* poll the driver -- a nice fabrication */ 87c0b746e5SOllivier Robert noentry, /* not used */ 88c0b746e5SOllivier Robert noentry, /* not used */ 89c0b746e5SOllivier Robert noentry, /* not used */ 90c0b746e5SOllivier Robert NOFLAGS /* not used */ 91c0b746e5SOllivier Robert }; 92c0b746e5SOllivier Robert 93c0b746e5SOllivier Robert 94c0b746e5SOllivier Robert /* 95c0b746e5SOllivier Robert * dumbclock_start - open the devices and initialize data for processing 96c0b746e5SOllivier Robert */ 97c0b746e5SOllivier Robert static int 98c0b746e5SOllivier Robert dumbclock_start( 99c0b746e5SOllivier Robert int unit, 100c0b746e5SOllivier Robert struct peer *peer 101c0b746e5SOllivier Robert ) 102c0b746e5SOllivier Robert { 103c0b746e5SOllivier Robert register struct dumbclock_unit *up; 104c0b746e5SOllivier Robert struct refclockproc *pp; 105c0b746e5SOllivier Robert int fd; 106c0b746e5SOllivier Robert char device[20]; 107c0b746e5SOllivier Robert struct tm *tm_time_p; 108c0b746e5SOllivier Robert time_t now; 109c0b746e5SOllivier Robert 110c0b746e5SOllivier Robert /* 111c0b746e5SOllivier Robert * Open serial port. Don't bother with CLK line discipline, since 112c0b746e5SOllivier Robert * it's not available. 113c0b746e5SOllivier Robert */ 114c0b746e5SOllivier Robert (void)sprintf(device, DEVICE, unit); 115c0b746e5SOllivier Robert #ifdef DEBUG 116c0b746e5SOllivier Robert if (debug) 117c0b746e5SOllivier Robert printf ("starting Dumbclock with device %s\n",device); 118c0b746e5SOllivier Robert #endif 119c0b746e5SOllivier Robert if (!(fd = refclock_open(device, SPEED232, 0))) 120c0b746e5SOllivier Robert return (0); 121c0b746e5SOllivier Robert 122c0b746e5SOllivier Robert /* 123c0b746e5SOllivier Robert * Allocate and initialize unit structure 124c0b746e5SOllivier Robert */ 125c0b746e5SOllivier Robert if (!(up = (struct dumbclock_unit *) 126c0b746e5SOllivier Robert emalloc(sizeof(struct dumbclock_unit)))) { 127c0b746e5SOllivier Robert (void) close(fd); 128c0b746e5SOllivier Robert return (0); 129c0b746e5SOllivier Robert } 130c0b746e5SOllivier Robert memset((char *)up, 0, sizeof(struct dumbclock_unit)); 131c0b746e5SOllivier Robert pp = peer->procptr; 132c0b746e5SOllivier Robert pp->unitptr = (caddr_t)up; 133c0b746e5SOllivier Robert pp->io.clock_recv = dumbclock_receive; 134c0b746e5SOllivier Robert pp->io.srcclock = (caddr_t)peer; 135c0b746e5SOllivier Robert pp->io.datalen = 0; 136c0b746e5SOllivier Robert pp->io.fd = fd; 137c0b746e5SOllivier Robert if (!io_addclock(&pp->io)) { 138c0b746e5SOllivier Robert (void) close(fd); 139c0b746e5SOllivier Robert free(up); 140c0b746e5SOllivier Robert return (0); 141c0b746e5SOllivier Robert } 142c0b746e5SOllivier Robert 143c0b746e5SOllivier Robert 144c0b746e5SOllivier Robert time(&now); 145c0b746e5SOllivier Robert #ifdef GET_LOCALTIME 146c0b746e5SOllivier Robert tm_time_p = localtime(&now); 147c0b746e5SOllivier Robert #else 148c0b746e5SOllivier Robert tm_time_p = gmtime(&now); 149c0b746e5SOllivier Robert #endif 150c0b746e5SOllivier Robert if (tm_time_p) 151c0b746e5SOllivier Robert { 152c0b746e5SOllivier Robert up->ymd = *tm_time_p; 153c0b746e5SOllivier Robert } 154c0b746e5SOllivier Robert else 155c0b746e5SOllivier Robert { 156c0b746e5SOllivier Robert return 0; 157c0b746e5SOllivier Robert } 158c0b746e5SOllivier Robert 159c0b746e5SOllivier Robert /* 160c0b746e5SOllivier Robert * Initialize miscellaneous variables 161c0b746e5SOllivier Robert */ 162c0b746e5SOllivier Robert peer->precision = PRECISION; 163c0b746e5SOllivier Robert pp->clockdesc = DESCRIPTION; 164c0b746e5SOllivier Robert memcpy((char *)&pp->refid, REFID, 4); 165c0b746e5SOllivier Robert return (1); 166c0b746e5SOllivier Robert } 167c0b746e5SOllivier Robert 168c0b746e5SOllivier Robert 169c0b746e5SOllivier Robert /* 170c0b746e5SOllivier Robert * dumbclock_shutdown - shut down the clock 171c0b746e5SOllivier Robert */ 172c0b746e5SOllivier Robert static void 173c0b746e5SOllivier Robert dumbclock_shutdown( 174c0b746e5SOllivier Robert int unit, 175c0b746e5SOllivier Robert struct peer *peer 176c0b746e5SOllivier Robert ) 177c0b746e5SOllivier Robert { 178c0b746e5SOllivier Robert register struct dumbclock_unit *up; 179c0b746e5SOllivier Robert struct refclockproc *pp; 180c0b746e5SOllivier Robert 181c0b746e5SOllivier Robert pp = peer->procptr; 182c0b746e5SOllivier Robert up = (struct dumbclock_unit *)pp->unitptr; 183c0b746e5SOllivier Robert io_closeclock(&pp->io); 184c0b746e5SOllivier Robert free(up); 185c0b746e5SOllivier Robert } 186c0b746e5SOllivier Robert 187c0b746e5SOllivier Robert 188c0b746e5SOllivier Robert /* 189c0b746e5SOllivier Robert * dumbclock_receive - receive data from the serial interface 190c0b746e5SOllivier Robert */ 191c0b746e5SOllivier Robert static void 192c0b746e5SOllivier Robert dumbclock_receive( 193c0b746e5SOllivier Robert struct recvbuf *rbufp 194c0b746e5SOllivier Robert ) 195c0b746e5SOllivier Robert { 196c0b746e5SOllivier Robert struct dumbclock_unit *up; 197c0b746e5SOllivier Robert struct refclockproc *pp; 198c0b746e5SOllivier Robert struct peer *peer; 199c0b746e5SOllivier Robert 200c0b746e5SOllivier Robert l_fp trtmp; /* arrival timestamp */ 201c0b746e5SOllivier Robert int hours; /* hour-of-day */ 202c0b746e5SOllivier Robert int minutes; /* minutes-past-the-hour */ 203c0b746e5SOllivier Robert int seconds; /* seconds */ 204c0b746e5SOllivier Robert int temp; /* int temp */ 205c0b746e5SOllivier Robert int got_good; /* got a good time flag */ 206c0b746e5SOllivier Robert 207c0b746e5SOllivier Robert /* 208c0b746e5SOllivier Robert * Initialize pointers and read the timecode and timestamp 209c0b746e5SOllivier Robert */ 210c0b746e5SOllivier Robert peer = (struct peer *)rbufp->recv_srcclock; 211c0b746e5SOllivier Robert pp = peer->procptr; 212c0b746e5SOllivier Robert up = (struct dumbclock_unit *)pp->unitptr; 213c0b746e5SOllivier Robert temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); 214c0b746e5SOllivier Robert 215c0b746e5SOllivier Robert if (temp == 0) { 216c0b746e5SOllivier Robert if (up->tcswitch == 0) { 217c0b746e5SOllivier Robert up->tcswitch = 1; 218c0b746e5SOllivier Robert up->laststamp = trtmp; 219c0b746e5SOllivier Robert } else 220c0b746e5SOllivier Robert up->tcswitch = 0; 221c0b746e5SOllivier Robert return; 222c0b746e5SOllivier Robert } 223c0b746e5SOllivier Robert pp->lencode = temp; 224c0b746e5SOllivier Robert pp->lastrec = up->laststamp; 225c0b746e5SOllivier Robert up->laststamp = trtmp; 226c0b746e5SOllivier Robert up->tcswitch = 1; 227c0b746e5SOllivier Robert 228c0b746e5SOllivier Robert #ifdef DEBUG 229c0b746e5SOllivier Robert if (debug) 230c0b746e5SOllivier Robert printf("dumbclock: timecode %d %s\n", 231c0b746e5SOllivier Robert pp->lencode, pp->a_lastcode); 232c0b746e5SOllivier Robert #endif 233c0b746e5SOllivier Robert 234c0b746e5SOllivier Robert /* 235c0b746e5SOllivier Robert * We get down to business. Check the timecode format... 236c0b746e5SOllivier Robert */ 237c0b746e5SOllivier Robert pp->msec = 0; 238c0b746e5SOllivier Robert got_good=0; 239c0b746e5SOllivier Robert if (sscanf(pp->a_lastcode,"%02d:%02d:%02d", 240c0b746e5SOllivier Robert &hours,&minutes,&seconds) == 3) 241c0b746e5SOllivier Robert { 242c0b746e5SOllivier Robert struct tm *gmtp; 243c0b746e5SOllivier Robert struct tm *lt_p; 244c0b746e5SOllivier Robert time_t asserted_time; /* the SPM time based on the composite time+date */ 245c0b746e5SOllivier Robert struct tm asserted_tm; /* the struct tm of the same */ 246c0b746e5SOllivier Robert int adjyear; 247c0b746e5SOllivier Robert int adjmon; 248c0b746e5SOllivier Robert int reality_delta; 249c0b746e5SOllivier Robert time_t now; 250c0b746e5SOllivier Robert 251c0b746e5SOllivier Robert 252c0b746e5SOllivier Robert /* 253c0b746e5SOllivier Robert * Convert to GMT for sites that distribute localtime. This 254c0b746e5SOllivier Robert * means we have to figure out what day it is. Easier said 255c0b746e5SOllivier Robert * than done... 256c0b746e5SOllivier Robert */ 257c0b746e5SOllivier Robert 258c0b746e5SOllivier Robert asserted_tm.tm_year = up->ymd.tm_year; 259c0b746e5SOllivier Robert asserted_tm.tm_mon = up->ymd.tm_mon; 260c0b746e5SOllivier Robert asserted_tm.tm_mday = up->ymd.tm_mday; 261c0b746e5SOllivier Robert asserted_tm.tm_hour = hours; 262c0b746e5SOllivier Robert asserted_tm.tm_min = minutes; 263c0b746e5SOllivier Robert asserted_tm.tm_sec = seconds; 264c0b746e5SOllivier Robert asserted_tm.tm_isdst = -1; 265c0b746e5SOllivier Robert 266c0b746e5SOllivier Robert #ifdef GET_LOCALTIME 267c0b746e5SOllivier Robert asserted_time = mktime (&asserted_tm); 268c0b746e5SOllivier Robert time(&now); 269c0b746e5SOllivier Robert #else 270c0b746e5SOllivier Robert #include "GMT unsupported for dumbclock!" 271c0b746e5SOllivier Robert #endif 272c0b746e5SOllivier Robert reality_delta = asserted_time - now; 273c0b746e5SOllivier Robert 274c0b746e5SOllivier Robert /* 275c0b746e5SOllivier Robert * We assume that if the time is grossly wrong, it's because we got the 276c0b746e5SOllivier Robert * year/month/day wrong. 277c0b746e5SOllivier Robert */ 278c0b746e5SOllivier Robert if (reality_delta > INSANE_SECONDS) 279c0b746e5SOllivier Robert { 280c0b746e5SOllivier Robert asserted_time -= SECSPERDAY; /* local clock behind real time */ 281c0b746e5SOllivier Robert } 282c0b746e5SOllivier Robert else if (-reality_delta > INSANE_SECONDS) 283c0b746e5SOllivier Robert { 284c0b746e5SOllivier Robert asserted_time += SECSPERDAY; /* local clock ahead of real time */ 285c0b746e5SOllivier Robert } 286c0b746e5SOllivier Robert lt_p = localtime(&asserted_time); 287c0b746e5SOllivier Robert if (lt_p) 288c0b746e5SOllivier Robert { 289c0b746e5SOllivier Robert up->ymd = *lt_p; 290c0b746e5SOllivier Robert } 291c0b746e5SOllivier Robert else 292c0b746e5SOllivier Robert { 293c0b746e5SOllivier Robert refclock_report (peer, CEVNT_FAULT); 294c0b746e5SOllivier Robert return; 295c0b746e5SOllivier Robert } 296c0b746e5SOllivier Robert 297c0b746e5SOllivier Robert if ((gmtp = gmtime (&asserted_time)) == NULL) 298c0b746e5SOllivier Robert { 299c0b746e5SOllivier Robert refclock_report (peer, CEVNT_FAULT); 300c0b746e5SOllivier Robert return; 301c0b746e5SOllivier Robert } 302c0b746e5SOllivier Robert adjyear = gmtp->tm_year+1900; 303c0b746e5SOllivier Robert adjmon = gmtp->tm_mon+1; 304c0b746e5SOllivier Robert pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday); 305c0b746e5SOllivier Robert pp->hour = gmtp->tm_hour; 306c0b746e5SOllivier Robert pp->minute = gmtp->tm_min; 307c0b746e5SOllivier Robert pp->second = gmtp->tm_sec; 308c0b746e5SOllivier Robert #ifdef DEBUG 309c0b746e5SOllivier Robert if (debug) 310c0b746e5SOllivier Robert printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 311c0b746e5SOllivier Robert adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute, 312c0b746e5SOllivier Robert pp->second); 313c0b746e5SOllivier Robert #endif 314c0b746e5SOllivier Robert 315c0b746e5SOllivier Robert got_good=1; 316c0b746e5SOllivier Robert } 317c0b746e5SOllivier Robert 318c0b746e5SOllivier Robert if (!got_good) 319c0b746e5SOllivier Robert { 320c0b746e5SOllivier Robert if (up->linect > 0) 321c0b746e5SOllivier Robert up->linect--; 322c0b746e5SOllivier Robert else 323c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 324c0b746e5SOllivier Robert return; 325c0b746e5SOllivier Robert } 326c0b746e5SOllivier Robert 327c0b746e5SOllivier Robert /* 328c0b746e5SOllivier Robert * Process the new sample in the median filter and determine the 329c0b746e5SOllivier Robert * timecode timestamp. 330c0b746e5SOllivier Robert */ 331c0b746e5SOllivier Robert if (!refclock_process(pp)) { 332c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADTIME); 333c0b746e5SOllivier Robert return; 334c0b746e5SOllivier Robert } 335c0b746e5SOllivier Robert record_clock_stats(&peer->srcadr, pp->a_lastcode); 336c0b746e5SOllivier Robert refclock_receive(peer); 337c0b746e5SOllivier Robert up->lasthour = pp->hour; 338c0b746e5SOllivier Robert } 339c0b746e5SOllivier Robert 340c0b746e5SOllivier Robert #if 0 341c0b746e5SOllivier Robert /* 342c0b746e5SOllivier Robert * dumbclock_poll - called by the transmit procedure 343c0b746e5SOllivier Robert */ 344c0b746e5SOllivier Robert static void 345c0b746e5SOllivier Robert dumbclock_poll( 346c0b746e5SOllivier Robert int unit, 347c0b746e5SOllivier Robert struct peer *peer 348c0b746e5SOllivier Robert ) 349c0b746e5SOllivier Robert { 350c0b746e5SOllivier Robert register struct dumbclock_unit *up; 351c0b746e5SOllivier Robert struct refclockproc *pp; 352c0b746e5SOllivier Robert char pollchar; 353c0b746e5SOllivier Robert 354c0b746e5SOllivier Robert /* 355c0b746e5SOllivier Robert * Time to poll the clock. The Chrono-log clock is supposed to 356c0b746e5SOllivier Robert * respond to a 'T' by returning a timecode in the format(s) 357c0b746e5SOllivier Robert * specified above. Ours does (can?) not, but this seems to be 358c0b746e5SOllivier Robert * an installation-specific problem. This code is dyked out, 359c0b746e5SOllivier Robert * but may be re-enabled if anyone ever finds a Chrono-log that 360c0b746e5SOllivier Robert * actually listens to this command. 361c0b746e5SOllivier Robert */ 362c0b746e5SOllivier Robert #if 0 363c0b746e5SOllivier Robert pp = peer->procptr; 364c0b746e5SOllivier Robert up = (struct dumbclock_unit *)pp->unitptr; 365c0b746e5SOllivier Robert if (peer->reach == 0) 366c0b746e5SOllivier Robert refclock_report(peer, CEVNT_TIMEOUT); 367c0b746e5SOllivier Robert if (up->linect > 0) 368c0b746e5SOllivier Robert pollchar = 'R'; 369c0b746e5SOllivier Robert else 370c0b746e5SOllivier Robert pollchar = 'T'; 371c0b746e5SOllivier Robert if (write(pp->io.fd, &pollchar, 1) != 1) 372c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 373c0b746e5SOllivier Robert else 374c0b746e5SOllivier Robert pp->polls++; 375c0b746e5SOllivier Robert #endif 376c0b746e5SOllivier Robert } 377c0b746e5SOllivier Robert #endif 378c0b746e5SOllivier Robert 379c0b746e5SOllivier Robert #else 380c0b746e5SOllivier Robert int refclock_dumbclock_bs; 381c0b746e5SOllivier Robert #endif /* REFCLOCK */ 382