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