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