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