xref: /netbsd-src/external/bsd/ntp/dist/ntpd/refclock_dumbclock.c (revision 7788a0781fe6ff2cce37368b4578a7ade0850cb1)
1 /*	$NetBSD: refclock_dumbclock.c,v 1.1.1.2 2012/01/31 21:26:00 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 	snprintf(device, sizeof(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)
127 		return (0);
128 
129 	/*
130 	 * Allocate and initialize unit structure
131 	 */
132 	up = emalloc(sizeof(*up));
133 	memset(up, 0, sizeof(*up));
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 		close(fd);
142 		pp->io.fd = -1;
143 		free(up);
144 		pp->unitptr = NULL;
145 		return (0);
146 	}
147 
148 
149 	time(&now);
150 #ifdef GET_LOCALTIME
151 	tm_time_p = localtime(&now);
152 #else
153 	tm_time_p = gmtime(&now);
154 #endif
155 	if (tm_time_p)
156 		up->ymd = *tm_time_p;
157 	else
158 		return 0;
159 
160 	/*
161 	 * Initialize miscellaneous variables
162 	 */
163 	peer->precision = PRECISION;
164 	pp->clockdesc = DESCRIPTION;
165 	memcpy((char *)&pp->refid, REFID, 4);
166 	return (1);
167 }
168 
169 
170 /*
171  * dumbclock_shutdown - shut down the clock
172  */
173 static void
174 dumbclock_shutdown(
175 	int unit,
176 	struct peer *peer
177 	)
178 {
179 	register struct dumbclock_unit *up;
180 	struct refclockproc *pp;
181 
182 	pp = peer->procptr;
183 	up = (struct dumbclock_unit *)pp->unitptr;
184 	if (-1 != pp->io.fd)
185 		io_closeclock(&pp->io);
186 	if (NULL != up)
187 		free(up);
188 }
189 
190 
191 /*
192  * dumbclock_receive - receive data from the serial interface
193  */
194 static void
195 dumbclock_receive(
196 	struct recvbuf *rbufp
197 	)
198 {
199 	struct dumbclock_unit *up;
200 	struct refclockproc *pp;
201 	struct peer *peer;
202 
203 	l_fp	trtmp;		/* arrival timestamp */
204 	int	hours;		/* hour-of-day */
205 	int	minutes;	/* minutes-past-the-hour */
206 	int	seconds;	/* seconds */
207 	int	temp;		/* int temp */
208 	int	got_good;	/* got a good time flag */
209 
210 	/*
211 	 * Initialize pointers and read the timecode and timestamp
212 	 */
213 	peer = (struct peer *)rbufp->recv_srcclock;
214 	pp = peer->procptr;
215 	up = (struct dumbclock_unit *)pp->unitptr;
216 	temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
217 
218 	if (temp == 0) {
219 		if (up->tcswitch == 0) {
220 			up->tcswitch = 1;
221 			up->laststamp = trtmp;
222 		} else
223 			up->tcswitch = 0;
224 		return;
225 	}
226 	pp->lencode = (u_short)temp;
227 	pp->lastrec = up->laststamp;
228 	up->laststamp = trtmp;
229 	up->tcswitch = 1;
230 
231 #ifdef DEBUG
232 	if (debug)
233 		printf("dumbclock: timecode %d %s\n",
234 		       pp->lencode, pp->a_lastcode);
235 #endif
236 
237 	/*
238 	 * We get down to business. Check the timecode format...
239 	 */
240 	got_good=0;
241 	if (sscanf(pp->a_lastcode,"%02d:%02d:%02d",
242 		   &hours,&minutes,&seconds) == 3)
243 	{
244 	    struct tm *gmtp;
245 	    struct tm *lt_p;
246 	    time_t     asserted_time;	     /* the SPM time based on the composite time+date */
247 	    struct tm  asserted_tm;	     /* the struct tm of the same */
248 	    int        adjyear;
249 	    int        adjmon;
250 	    time_t     reality_delta;
251 	    time_t     now;
252 
253 
254 	    /*
255 	     * Convert to GMT for sites that distribute localtime.  This
256 	     * means we have to figure out what day it is.  Easier said
257 	     * than done...
258 	     */
259 
260 	    memset(&asserted_tm, 0, sizeof(asserted_tm));
261 
262 	    asserted_tm.tm_year  = up->ymd.tm_year;
263 	    asserted_tm.tm_mon   = up->ymd.tm_mon;
264 	    asserted_tm.tm_mday  = up->ymd.tm_mday;
265 	    asserted_tm.tm_hour  = hours;
266 	    asserted_tm.tm_min   = minutes;
267 	    asserted_tm.tm_sec   = seconds;
268 	    asserted_tm.tm_isdst = -1;
269 
270 #ifdef GET_LOCALTIME
271 	    asserted_time = mktime (&asserted_tm);
272 	    time(&now);
273 #else
274 #include "GMT unsupported for dumbclock!"
275 #endif
276 	    reality_delta = asserted_time - now;
277 
278 	    /*
279 	     * We assume that if the time is grossly wrong, it's because we got the
280 	     * year/month/day wrong.
281 	     */
282 	    if (reality_delta > INSANE_SECONDS)
283 	    {
284 		asserted_time -= SECSPERDAY; /* local clock behind real time */
285 	    }
286 	    else if (-reality_delta > INSANE_SECONDS)
287 	    {
288 		asserted_time += SECSPERDAY; /* local clock ahead of real time */
289 	    }
290 	    lt_p = localtime(&asserted_time);
291 	    if (lt_p)
292 	    {
293 		up->ymd = *lt_p;
294 	    }
295 	    else
296 	    {
297 		refclock_report (peer, CEVNT_FAULT);
298 		return;
299 	    }
300 
301 	    if ((gmtp = gmtime (&asserted_time)) == NULL)
302 	    {
303 		refclock_report (peer, CEVNT_FAULT);
304 		return;
305 	    }
306 	    adjyear = gmtp->tm_year+1900;
307 	    adjmon  = gmtp->tm_mon+1;
308 	    pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
309 	    pp->hour   = gmtp->tm_hour;
310 	    pp->minute = gmtp->tm_min;
311 	    pp->second = gmtp->tm_sec;
312 #ifdef DEBUG
313 	    if (debug)
314 		printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
315 			adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
316 			pp->second);
317 #endif
318 
319 	    got_good=1;
320 	}
321 
322 	if (!got_good)
323 	{
324 	    if (up->linect > 0)
325 	    	up->linect--;
326 	    else
327 	    	refclock_report(peer, CEVNT_BADREPLY);
328 	    return;
329 	}
330 
331 	/*
332 	 * Process the new sample in the median filter and determine the
333 	 * timecode timestamp.
334 	 */
335 	if (!refclock_process(pp)) {
336 		refclock_report(peer, CEVNT_BADTIME);
337 		return;
338 	}
339 	pp->lastref = pp->lastrec;
340 	refclock_receive(peer);
341 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
342 	up->lasthour = (u_char)pp->hour;
343 }
344 
345 #if 0
346 /*
347  * dumbclock_poll - called by the transmit procedure
348  */
349 static void
350 dumbclock_poll(
351 	int unit,
352 	struct peer *peer
353 	)
354 {
355 	register struct dumbclock_unit *up;
356 	struct refclockproc *pp;
357 	char pollchar;
358 
359 	/*
360 	 * Time to poll the clock. The Chrono-log clock is supposed to
361 	 * respond to a 'T' by returning a timecode in the format(s)
362 	 * specified above.  Ours does (can?) not, but this seems to be
363 	 * an installation-specific problem.  This code is dyked out,
364 	 * but may be re-enabled if anyone ever finds a Chrono-log that
365 	 * actually listens to this command.
366 	 */
367 #if 0
368 	pp = peer->procptr;
369 	up = (struct dumbclock_unit *)pp->unitptr;
370 	if (peer->reach == 0)
371 		refclock_report(peer, CEVNT_TIMEOUT);
372 	if (up->linect > 0)
373 		pollchar = 'R';
374 	else
375 		pollchar = 'T';
376 	if (write(pp->io.fd, &pollchar, 1) != 1)
377 		refclock_report(peer, CEVNT_FAULT);
378 	else
379 		pp->polls++;
380 #endif
381 }
382 #endif
383 
384 #else
385 int refclock_dumbclock_bs;
386 #endif	/* defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK) */
387