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 27 extern 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 */ 66 struct 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 */ 77 static int dumbclock_start (int, struct peer *); 78 static void dumbclock_shutdown (int, struct peer *); 79 static void dumbclock_receive (struct recvbuf *); 80 #if 0 81 static void dumbclock_poll (int, struct peer *); 82 #endif 83 84 /* 85 * Transfer vector 86 */ 87 struct 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 */ 101 static int 102 dumbclock_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 snprintf(device, sizeof(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 = emalloc_zero(sizeof(*up)); 131 pp = peer->procptr; 132 pp->unitptr = up; 133 pp->io.clock_recv = dumbclock_receive; 134 pp->io.srcclock = peer; 135 pp->io.datalen = 0; 136 pp->io.fd = fd; 137 if (!io_addclock(&pp->io)) { 138 close(fd); 139 pp->io.fd = -1; 140 free(up); 141 pp->unitptr = NULL; 142 return (0); 143 } 144 145 146 time(&now); 147 #ifdef GET_LOCALTIME 148 tm_time_p = localtime(&now); 149 #else 150 tm_time_p = gmtime(&now); 151 #endif 152 if (tm_time_p) 153 up->ymd = *tm_time_p; 154 else 155 return 0; 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 = pp->unitptr; 181 if (-1 != pp->io.fd) 182 io_closeclock(&pp->io); 183 if (NULL != up) 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 = rbufp->recv_peer; 211 pp = peer->procptr; 212 up = 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 = (u_short)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 got_good=0; 238 if (sscanf(pp->a_lastcode,"%02d:%02d:%02d", 239 &hours,&minutes,&seconds) == 3) 240 { 241 struct tm *gmtp; 242 struct tm *lt_p; 243 time_t asserted_time; /* the SPM time based on the composite time+date */ 244 struct tm asserted_tm; /* the struct tm of the same */ 245 int adjyear; 246 int adjmon; 247 time_t reality_delta; 248 time_t now; 249 250 251 /* 252 * Convert to GMT for sites that distribute localtime. This 253 * means we have to figure out what day it is. Easier said 254 * than done... 255 */ 256 257 memset(&asserted_tm, 0, sizeof(asserted_tm)); 258 259 asserted_tm.tm_year = up->ymd.tm_year; 260 asserted_tm.tm_mon = up->ymd.tm_mon; 261 asserted_tm.tm_mday = up->ymd.tm_mday; 262 asserted_tm.tm_hour = hours; 263 asserted_tm.tm_min = minutes; 264 asserted_tm.tm_sec = seconds; 265 asserted_tm.tm_isdst = -1; 266 267 #ifdef GET_LOCALTIME 268 asserted_time = mktime (&asserted_tm); 269 time(&now); 270 #else 271 #include "GMT unsupported for dumbclock!" 272 #endif 273 reality_delta = asserted_time - now; 274 275 /* 276 * We assume that if the time is grossly wrong, it's because we got the 277 * year/month/day wrong. 278 */ 279 if (reality_delta > INSANE_SECONDS) 280 { 281 asserted_time -= SECSPERDAY; /* local clock behind real time */ 282 } 283 else if (-reality_delta > INSANE_SECONDS) 284 { 285 asserted_time += SECSPERDAY; /* local clock ahead of real time */ 286 } 287 lt_p = localtime(&asserted_time); 288 if (lt_p) 289 { 290 up->ymd = *lt_p; 291 } 292 else 293 { 294 refclock_report (peer, CEVNT_FAULT); 295 return; 296 } 297 298 if ((gmtp = gmtime (&asserted_time)) == NULL) 299 { 300 refclock_report (peer, CEVNT_FAULT); 301 return; 302 } 303 adjyear = gmtp->tm_year+1900; 304 adjmon = gmtp->tm_mon+1; 305 pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday); 306 pp->hour = gmtp->tm_hour; 307 pp->minute = gmtp->tm_min; 308 pp->second = gmtp->tm_sec; 309 #ifdef DEBUG 310 if (debug) 311 printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 312 adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute, 313 pp->second); 314 #endif 315 316 got_good=1; 317 } 318 319 if (!got_good) 320 { 321 if (up->linect > 0) 322 up->linect--; 323 else 324 refclock_report(peer, CEVNT_BADREPLY); 325 return; 326 } 327 328 /* 329 * Process the new sample in the median filter and determine the 330 * timecode timestamp. 331 */ 332 if (!refclock_process(pp)) { 333 refclock_report(peer, CEVNT_BADTIME); 334 return; 335 } 336 pp->lastref = pp->lastrec; 337 refclock_receive(peer); 338 record_clock_stats(&peer->srcadr, pp->a_lastcode); 339 up->lasthour = (u_char)pp->hour; 340 } 341 342 #if 0 343 /* 344 * dumbclock_poll - called by the transmit procedure 345 */ 346 static void 347 dumbclock_poll( 348 int unit, 349 struct peer *peer 350 ) 351 { 352 register struct dumbclock_unit *up; 353 struct refclockproc *pp; 354 char pollchar; 355 356 /* 357 * Time to poll the clock. The Chrono-log clock is supposed to 358 * respond to a 'T' by returning a timecode in the format(s) 359 * specified above. Ours does (can?) not, but this seems to be 360 * an installation-specific problem. This code is dyked out, 361 * but may be re-enabled if anyone ever finds a Chrono-log that 362 * actually listens to this command. 363 */ 364 #if 0 365 pp = peer->procptr; 366 up = pp->unitptr; 367 if (peer->reach == 0) 368 refclock_report(peer, CEVNT_TIMEOUT); 369 if (up->linect > 0) 370 pollchar = 'R'; 371 else 372 pollchar = 'T'; 373 if (write(pp->io.fd, &pollchar, 1) != 1) 374 refclock_report(peer, CEVNT_FAULT); 375 else 376 pp->polls++; 377 #endif 378 } 379 #endif 380 381 #else 382 int refclock_dumbclock_bs; 383 #endif /* defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK) */ 384