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