154359Sroberto/* 254359Sroberto * refclock_dumbclock - clock driver for a unknown time distribution system 354359Sroberto * that only provides hh:mm:ss (in local time, yet!). 454359Sroberto */ 554359Sroberto 654359Sroberto/* 754359Sroberto * Must interpolate back to local time. Very annoying. 854359Sroberto */ 954359Sroberto#define GET_LOCALTIME 1054359Sroberto 1154359Sroberto#ifdef HAVE_CONFIG_H 1254359Sroberto#include <config.h> 1354359Sroberto#endif 1454359Sroberto 1554359Sroberto#if defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK) 1654359Sroberto 1754359Sroberto#include "ntpd.h" 1854359Sroberto#include "ntp_io.h" 1954359Sroberto#include "ntp_refclock.h" 2054359Sroberto#include "ntp_calendar.h" 2154359Sroberto#include "ntp_stdlib.h" 2254359Sroberto 2382498Sroberto#include <stdio.h> 2482498Sroberto#include <ctype.h> 2582498Sroberto 26200576Sroberto#ifdef SYS_WINNT 27200576Srobertoextern int async_write(int, const void *, unsigned int); 28200576Sroberto#undef write 29200576Sroberto#define write(fd, data, octets) async_write(fd, data, octets) 30200576Sroberto#endif 31200576Sroberto 3254359Sroberto/* 3354359Sroberto * This driver supports a generic dumb clock that only outputs hh:mm:ss, 3454359Sroberto * in local time, no less. 3554359Sroberto * 3654359Sroberto * Input format: 3754359Sroberto * 38285612Sdelphij * hh:mm:ss <cr> 3954359Sroberto * 4054359Sroberto * hh:mm:ss -- what you'd expect, with a 24 hour clock. (Heck, that's the only 4154359Sroberto * way it could get stupider.) We take time on the <cr>. 4254359Sroberto * 4354359Sroberto * The original source of this module was the WWVB module. 4454359Sroberto */ 4554359Sroberto 4654359Sroberto/* 4754359Sroberto * Interface definitions 4854359Sroberto */ 4954359Sroberto#define DEVICE "/dev/dumbclock%d" /* device name and unit */ 5054359Sroberto#define SPEED232 B9600 /* uart speed (9600 baud) */ 5154359Sroberto#define PRECISION (-13) /* precision assumed (about 100 us) */ 5254359Sroberto#define REFID "dumbclock" /* reference ID */ 5354359Sroberto#define DESCRIPTION "Dumb clock" /* WRU */ 5454359Sroberto 5554359Sroberto 5654359Sroberto/* 5754359Sroberto * Insanity check. Since the time is local, we need to make sure that during midnight 5854359Sroberto * transitions, we can convert back to Unix time. If the conversion results in some number 5954359Sroberto * worse than this number of seconds away, assume the next day and retry. 6054359Sroberto */ 6154359Sroberto#define INSANE_SECONDS 3600 6254359Sroberto 6354359Sroberto/* 6454359Sroberto * Dumb clock control structure 6554359Sroberto */ 6654359Srobertostruct dumbclock_unit { 67285612Sdelphij u_char tcswitch; /* timecode switch */ 68285612Sdelphij l_fp laststamp; /* last receive timestamp */ 69285612Sdelphij u_char lasthour; /* last hour (for monitor) */ 70285612Sdelphij u_char linect; /* count ignored lines (for monitor */ 71285612Sdelphij struct tm ymd; /* struct tm for y/m/d only */ 7254359Sroberto}; 7354359Sroberto 7454359Sroberto/* 7554359Sroberto * Function prototypes 7654359Sroberto */ 77285612Sdelphijstatic int dumbclock_start (int, struct peer *); 78285612Sdelphijstatic void dumbclock_shutdown (int, struct peer *); 79285612Sdelphijstatic void dumbclock_receive (struct recvbuf *); 8054359Sroberto#if 0 81285612Sdelphijstatic void dumbclock_poll (int, struct peer *); 8254359Sroberto#endif 8354359Sroberto 8454359Sroberto/* 8554359Sroberto * Transfer vector 8654359Sroberto */ 8754359Srobertostruct refclock refclock_dumbclock = { 8854359Sroberto dumbclock_start, /* start up driver */ 8954359Sroberto dumbclock_shutdown, /* shut down driver */ 9054359Sroberto noentry, /* poll the driver -- a nice fabrication */ 9154359Sroberto noentry, /* not used */ 9254359Sroberto noentry, /* not used */ 9354359Sroberto noentry, /* not used */ 9454359Sroberto NOFLAGS /* not used */ 9554359Sroberto}; 9654359Sroberto 9754359Sroberto 9854359Sroberto/* 9954359Sroberto * dumbclock_start - open the devices and initialize data for processing 10054359Sroberto */ 10154359Srobertostatic int 10254359Srobertodumbclock_start( 10354359Sroberto int unit, 10454359Sroberto struct peer *peer 10554359Sroberto ) 10654359Sroberto{ 10754359Sroberto register struct dumbclock_unit *up; 10854359Sroberto struct refclockproc *pp; 10954359Sroberto int fd; 11054359Sroberto char device[20]; 11154359Sroberto struct tm *tm_time_p; 11254359Sroberto time_t now; 11354359Sroberto 11454359Sroberto /* 11554359Sroberto * Open serial port. Don't bother with CLK line discipline, since 11654359Sroberto * it's not available. 11754359Sroberto */ 118285612Sdelphij snprintf(device, sizeof(device), DEVICE, unit); 11954359Sroberto#ifdef DEBUG 12054359Sroberto if (debug) 12154359Sroberto printf ("starting Dumbclock with device %s\n",device); 12254359Sroberto#endif 123132451Sroberto fd = refclock_open(device, SPEED232, 0); 124285612Sdelphij if (fd <= 0) 12554359Sroberto return (0); 12654359Sroberto 12754359Sroberto /* 12854359Sroberto * Allocate and initialize unit structure 12954359Sroberto */ 130285612Sdelphij up = emalloc_zero(sizeof(*up)); 13154359Sroberto pp = peer->procptr; 132285612Sdelphij pp->unitptr = up; 13354359Sroberto pp->io.clock_recv = dumbclock_receive; 134285612Sdelphij pp->io.srcclock = peer; 13554359Sroberto pp->io.datalen = 0; 13654359Sroberto pp->io.fd = fd; 13754359Sroberto if (!io_addclock(&pp->io)) { 138285612Sdelphij close(fd); 139285612Sdelphij pp->io.fd = -1; 14054359Sroberto free(up); 141285612Sdelphij pp->unitptr = NULL; 14254359Sroberto return (0); 14354359Sroberto } 14454359Sroberto 14554359Sroberto 14654359Sroberto time(&now); 14754359Sroberto#ifdef GET_LOCALTIME 14854359Sroberto tm_time_p = localtime(&now); 14954359Sroberto#else 15054359Sroberto tm_time_p = gmtime(&now); 15154359Sroberto#endif 15254359Sroberto if (tm_time_p) 153285612Sdelphij up->ymd = *tm_time_p; 15454359Sroberto else 155285612Sdelphij return 0; 156285612Sdelphij 15754359Sroberto /* 15854359Sroberto * Initialize miscellaneous variables 15954359Sroberto */ 16054359Sroberto peer->precision = PRECISION; 16154359Sroberto pp->clockdesc = DESCRIPTION; 16254359Sroberto memcpy((char *)&pp->refid, REFID, 4); 16354359Sroberto return (1); 16454359Sroberto} 16554359Sroberto 16654359Sroberto 16754359Sroberto/* 16854359Sroberto * dumbclock_shutdown - shut down the clock 16954359Sroberto */ 17054359Srobertostatic void 17154359Srobertodumbclock_shutdown( 17254359Sroberto int unit, 17354359Sroberto struct peer *peer 17454359Sroberto ) 17554359Sroberto{ 17654359Sroberto register struct dumbclock_unit *up; 17754359Sroberto struct refclockproc *pp; 17854359Sroberto 17954359Sroberto pp = peer->procptr; 180285612Sdelphij up = pp->unitptr; 181285612Sdelphij if (-1 != pp->io.fd) 182285612Sdelphij io_closeclock(&pp->io); 183285612Sdelphij if (NULL != up) 184285612Sdelphij free(up); 18554359Sroberto} 18654359Sroberto 18754359Sroberto 18854359Sroberto/* 18954359Sroberto * dumbclock_receive - receive data from the serial interface 19054359Sroberto */ 19154359Srobertostatic void 19254359Srobertodumbclock_receive( 19354359Sroberto struct recvbuf *rbufp 19454359Sroberto ) 19554359Sroberto{ 19654359Sroberto struct dumbclock_unit *up; 19754359Sroberto struct refclockproc *pp; 19854359Sroberto struct peer *peer; 19954359Sroberto 200285612Sdelphij l_fp trtmp; /* arrival timestamp */ 201285612Sdelphij int hours; /* hour-of-day */ 202285612Sdelphij int minutes; /* minutes-past-the-hour */ 203285612Sdelphij int seconds; /* seconds */ 204285612Sdelphij int temp; /* int temp */ 205285612Sdelphij int got_good; /* got a good time flag */ 20654359Sroberto 20754359Sroberto /* 20854359Sroberto * Initialize pointers and read the timecode and timestamp 20954359Sroberto */ 210285612Sdelphij peer = rbufp->recv_peer; 21154359Sroberto pp = peer->procptr; 212285612Sdelphij up = pp->unitptr; 21354359Sroberto temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); 21454359Sroberto 21554359Sroberto if (temp == 0) { 21654359Sroberto if (up->tcswitch == 0) { 21754359Sroberto up->tcswitch = 1; 21854359Sroberto up->laststamp = trtmp; 21954359Sroberto } else 220285612Sdelphij up->tcswitch = 0; 22154359Sroberto return; 22254359Sroberto } 223132451Sroberto pp->lencode = (u_short)temp; 22454359Sroberto pp->lastrec = up->laststamp; 22554359Sroberto up->laststamp = trtmp; 22654359Sroberto up->tcswitch = 1; 22754359Sroberto 22854359Sroberto#ifdef DEBUG 22954359Sroberto if (debug) 23054359Sroberto printf("dumbclock: timecode %d %s\n", 23154359Sroberto pp->lencode, pp->a_lastcode); 23254359Sroberto#endif 23354359Sroberto 23454359Sroberto /* 23554359Sroberto * We get down to business. Check the timecode format... 23654359Sroberto */ 23754359Sroberto got_good=0; 23854359Sroberto if (sscanf(pp->a_lastcode,"%02d:%02d:%02d", 23954359Sroberto &hours,&minutes,&seconds) == 3) 24054359Sroberto { 24154359Sroberto struct tm *gmtp; 24254359Sroberto struct tm *lt_p; 24354359Sroberto time_t asserted_time; /* the SPM time based on the composite time+date */ 24454359Sroberto struct tm asserted_tm; /* the struct tm of the same */ 24554359Sroberto int adjyear; 24654359Sroberto int adjmon; 247285612Sdelphij time_t reality_delta; 24854359Sroberto time_t now; 24954359Sroberto 25054359Sroberto 25154359Sroberto /* 25254359Sroberto * Convert to GMT for sites that distribute localtime. This 253285612Sdelphij * means we have to figure out what day it is. Easier said 25454359Sroberto * than done... 25554359Sroberto */ 25654359Sroberto 257285612Sdelphij memset(&asserted_tm, 0, sizeof(asserted_tm)); 258285612Sdelphij 25954359Sroberto asserted_tm.tm_year = up->ymd.tm_year; 26054359Sroberto asserted_tm.tm_mon = up->ymd.tm_mon; 26154359Sroberto asserted_tm.tm_mday = up->ymd.tm_mday; 26254359Sroberto asserted_tm.tm_hour = hours; 26354359Sroberto asserted_tm.tm_min = minutes; 26454359Sroberto asserted_tm.tm_sec = seconds; 26554359Sroberto asserted_tm.tm_isdst = -1; 26654359Sroberto 26754359Sroberto#ifdef GET_LOCALTIME 26854359Sroberto asserted_time = mktime (&asserted_tm); 26954359Sroberto time(&now); 27054359Sroberto#else 27154359Sroberto#include "GMT unsupported for dumbclock!" 27254359Sroberto#endif 27354359Sroberto reality_delta = asserted_time - now; 27454359Sroberto 27554359Sroberto /* 27654359Sroberto * We assume that if the time is grossly wrong, it's because we got the 27754359Sroberto * year/month/day wrong. 27854359Sroberto */ 27954359Sroberto if (reality_delta > INSANE_SECONDS) 28054359Sroberto { 28154359Sroberto asserted_time -= SECSPERDAY; /* local clock behind real time */ 28254359Sroberto } 28354359Sroberto else if (-reality_delta > INSANE_SECONDS) 28454359Sroberto { 28554359Sroberto asserted_time += SECSPERDAY; /* local clock ahead of real time */ 28654359Sroberto } 28754359Sroberto lt_p = localtime(&asserted_time); 28854359Sroberto if (lt_p) 28954359Sroberto { 29054359Sroberto up->ymd = *lt_p; 29154359Sroberto } 29254359Sroberto else 29354359Sroberto { 29454359Sroberto refclock_report (peer, CEVNT_FAULT); 29554359Sroberto return; 29654359Sroberto } 29754359Sroberto 29854359Sroberto if ((gmtp = gmtime (&asserted_time)) == NULL) 29954359Sroberto { 30054359Sroberto refclock_report (peer, CEVNT_FAULT); 30154359Sroberto return; 30254359Sroberto } 30354359Sroberto adjyear = gmtp->tm_year+1900; 30454359Sroberto adjmon = gmtp->tm_mon+1; 30554359Sroberto pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday); 30654359Sroberto pp->hour = gmtp->tm_hour; 30754359Sroberto pp->minute = gmtp->tm_min; 30854359Sroberto pp->second = gmtp->tm_sec; 30954359Sroberto#ifdef DEBUG 31054359Sroberto if (debug) 31154359Sroberto printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 31254359Sroberto adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute, 31354359Sroberto pp->second); 31454359Sroberto#endif 31554359Sroberto 31654359Sroberto got_good=1; 31754359Sroberto } 31854359Sroberto 31954359Sroberto if (!got_good) 32054359Sroberto { 32154359Sroberto if (up->linect > 0) 32254359Sroberto up->linect--; 32354359Sroberto else 32454359Sroberto refclock_report(peer, CEVNT_BADREPLY); 32554359Sroberto return; 32654359Sroberto } 32754359Sroberto 32854359Sroberto /* 32954359Sroberto * Process the new sample in the median filter and determine the 33054359Sroberto * timecode timestamp. 33154359Sroberto */ 33254359Sroberto if (!refclock_process(pp)) { 33354359Sroberto refclock_report(peer, CEVNT_BADTIME); 33454359Sroberto return; 33554359Sroberto } 336132451Sroberto pp->lastref = pp->lastrec; 337132451Sroberto refclock_receive(peer); 33854359Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 339132451Sroberto up->lasthour = (u_char)pp->hour; 34054359Sroberto} 34154359Sroberto 34254359Sroberto#if 0 34354359Sroberto/* 34454359Sroberto * dumbclock_poll - called by the transmit procedure 34554359Sroberto */ 34654359Srobertostatic void 34754359Srobertodumbclock_poll( 34854359Sroberto int unit, 34954359Sroberto struct peer *peer 35054359Sroberto ) 35154359Sroberto{ 35254359Sroberto register struct dumbclock_unit *up; 35354359Sroberto struct refclockproc *pp; 35454359Sroberto char pollchar; 35554359Sroberto 35654359Sroberto /* 35754359Sroberto * Time to poll the clock. The Chrono-log clock is supposed to 35854359Sroberto * respond to a 'T' by returning a timecode in the format(s) 35954359Sroberto * specified above. Ours does (can?) not, but this seems to be 36054359Sroberto * an installation-specific problem. This code is dyked out, 36154359Sroberto * but may be re-enabled if anyone ever finds a Chrono-log that 36254359Sroberto * actually listens to this command. 36354359Sroberto */ 36454359Sroberto#if 0 36554359Sroberto pp = peer->procptr; 366285612Sdelphij up = pp->unitptr; 36754359Sroberto if (peer->reach == 0) 36854359Sroberto refclock_report(peer, CEVNT_TIMEOUT); 36954359Sroberto if (up->linect > 0) 37054359Sroberto pollchar = 'R'; 37154359Sroberto else 37254359Sroberto pollchar = 'T'; 37354359Sroberto if (write(pp->io.fd, &pollchar, 1) != 1) 37454359Sroberto refclock_report(peer, CEVNT_FAULT); 37554359Sroberto else 37654359Sroberto pp->polls++; 37754359Sroberto#endif 37854359Sroberto} 37954359Sroberto#endif 38054359Sroberto 38154359Sroberto#else 38254359Srobertoint refclock_dumbclock_bs; 383285612Sdelphij#endif /* defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK) */ 384