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