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