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