xref: /freebsd/contrib/ntp/ntpd/refclock_pcf.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1a151a66cSOllivier Robert /*
2a151a66cSOllivier Robert  * refclock_pcf - clock driver for the Conrad parallel port radio clock
3a151a66cSOllivier Robert  */
4a151a66cSOllivier Robert 
5a151a66cSOllivier Robert #ifdef HAVE_CONFIG_H
6a151a66cSOllivier Robert # include <config.h>
7a151a66cSOllivier Robert #endif
8a151a66cSOllivier Robert 
9a151a66cSOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_PCF)
10a151a66cSOllivier Robert 
11a151a66cSOllivier Robert #include "ntpd.h"
12a151a66cSOllivier Robert #include "ntp_io.h"
13a151a66cSOllivier Robert #include "ntp_refclock.h"
14a151a66cSOllivier Robert #include "ntp_calendar.h"
15a151a66cSOllivier Robert #include "ntp_stdlib.h"
16a151a66cSOllivier Robert 
17a151a66cSOllivier Robert /*
18224ba2bdSOllivier Robert  * This driver supports the parallel port radio clock sold by Conrad
19a151a66cSOllivier Robert  * Electronic under order numbers 967602 and 642002.
20a151a66cSOllivier Robert  *
21a151a66cSOllivier Robert  * It requires that the local timezone be CET/CEST and that the pcfclock
22224ba2bdSOllivier Robert  * device driver be installed.  A device driver for Linux is available at
23224ba2bdSOllivier Robert  * http://home.pages.de/~voegele/pcf.html.  Information about a FreeBSD
24224ba2bdSOllivier Robert  * driver is available at http://schumann.cx/pcfclock/.
25a151a66cSOllivier Robert  */
26a151a66cSOllivier Robert 
27a151a66cSOllivier Robert /*
28a151a66cSOllivier Robert  * Interface definitions
29a151a66cSOllivier Robert  */
30224ba2bdSOllivier Robert #define	DEVICE		"/dev/pcfclocks/%d"
31224ba2bdSOllivier Robert #define	OLDDEVICE	"/dev/pcfclock%d"
32a151a66cSOllivier Robert #define	PRECISION	(-1)	/* precision assumed (about 0.5 s) */
33a151a66cSOllivier Robert #define REFID		"PCF"
34a151a66cSOllivier Robert #define DESCRIPTION	"Conrad parallel port radio clock"
35a151a66cSOllivier Robert 
36a151a66cSOllivier Robert #define LENPCF		18	/* timecode length */
37a151a66cSOllivier Robert 
38a151a66cSOllivier Robert /*
39a151a66cSOllivier Robert  * Function prototypes
40a151a66cSOllivier Robert  */
412b15cb3dSCy Schubert static	int 	pcf_start 		(int, struct peer *);
422b15cb3dSCy Schubert static	void	pcf_shutdown		(int, struct peer *);
432b15cb3dSCy Schubert static	void	pcf_poll		(int, struct peer *);
44a151a66cSOllivier Robert 
45a151a66cSOllivier Robert /*
46a151a66cSOllivier Robert  * Transfer vector
47a151a66cSOllivier Robert  */
48a151a66cSOllivier Robert struct  refclock refclock_pcf = {
49a151a66cSOllivier Robert 	pcf_start,              /* start up driver */
50a151a66cSOllivier Robert 	pcf_shutdown,           /* shut down driver */
51a151a66cSOllivier Robert 	pcf_poll,               /* transmit poll message */
52a151a66cSOllivier Robert 	noentry,                /* not used */
53a151a66cSOllivier Robert 	noentry,                /* initialize driver (not used) */
54a151a66cSOllivier Robert 	noentry,                /* not used */
55a151a66cSOllivier Robert 	NOFLAGS                 /* not used */
56a151a66cSOllivier Robert };
57a151a66cSOllivier Robert 
58a151a66cSOllivier Robert 
59a151a66cSOllivier Robert /*
60a151a66cSOllivier Robert  * pcf_start - open the device and initialize data for processing
61a151a66cSOllivier Robert  */
62a151a66cSOllivier Robert static int
63a151a66cSOllivier Robert pcf_start(
64a151a66cSOllivier Robert      	int unit,
65a151a66cSOllivier Robert 	struct peer *peer
66a151a66cSOllivier Robert 	)
67a151a66cSOllivier Robert {
68a151a66cSOllivier Robert 	struct refclockproc *pp;
69a151a66cSOllivier Robert 	int fd;
70224ba2bdSOllivier Robert 	char device[128];
71a151a66cSOllivier Robert 
72a151a66cSOllivier Robert 	/*
73a151a66cSOllivier Robert 	 * Open device file for reading.
74a151a66cSOllivier Robert 	 */
752b15cb3dSCy Schubert 	snprintf(device, sizeof(device), DEVICE, unit);
76224ba2bdSOllivier Robert 	fd = open(device, O_RDONLY);
77224ba2bdSOllivier Robert 	if (fd == -1) {
782b15cb3dSCy Schubert 		snprintf(device, sizeof(device), OLDDEVICE, unit);
79224ba2bdSOllivier Robert 		fd = open(device, O_RDONLY);
80224ba2bdSOllivier Robert 	}
81a151a66cSOllivier Robert #ifdef DEBUG
82a151a66cSOllivier Robert 	if (debug)
83a151a66cSOllivier Robert 		printf ("starting PCF with device %s\n",device);
84a151a66cSOllivier Robert #endif
85224ba2bdSOllivier Robert 	if (fd == -1) {
86a151a66cSOllivier Robert 		return (0);
87a151a66cSOllivier Robert 	}
88a151a66cSOllivier Robert 
89a151a66cSOllivier Robert 	pp = peer->procptr;
90a151a66cSOllivier Robert 	pp->io.clock_recv = noentry;
912b15cb3dSCy Schubert 	pp->io.srcclock = peer;
92a151a66cSOllivier Robert 	pp->io.datalen = 0;
93a151a66cSOllivier Robert 	pp->io.fd = fd;
94a151a66cSOllivier Robert 
95a151a66cSOllivier Robert 	/*
96a151a66cSOllivier Robert 	 * Initialize miscellaneous variables
97a151a66cSOllivier Robert 	 */
98a151a66cSOllivier Robert 	peer->precision = PRECISION;
99a151a66cSOllivier Robert 	pp->clockdesc = DESCRIPTION;
100224ba2bdSOllivier Robert 	/* one transmission takes 172.5 milliseconds since the radio clock
101224ba2bdSOllivier Robert 	   transmits 69 bits with a period of 2.5 milliseconds per bit */
102224ba2bdSOllivier Robert 	pp->fudgetime1 = 0.1725;
103a151a66cSOllivier Robert 	memcpy((char *)&pp->refid, REFID, 4);
104a151a66cSOllivier Robert 
105a151a66cSOllivier Robert 	return (1);
106a151a66cSOllivier Robert }
107a151a66cSOllivier Robert 
108a151a66cSOllivier Robert 
109a151a66cSOllivier Robert /*
110a151a66cSOllivier Robert  * pcf_shutdown - shut down the clock
111a151a66cSOllivier Robert  */
112a151a66cSOllivier Robert static void
113a151a66cSOllivier Robert pcf_shutdown(
114a151a66cSOllivier Robert 	int unit,
115a151a66cSOllivier Robert 	struct peer *peer
116a151a66cSOllivier Robert 	)
117a151a66cSOllivier Robert {
118a151a66cSOllivier Robert 	struct refclockproc *pp;
119a151a66cSOllivier Robert 
120a151a66cSOllivier Robert 	pp = peer->procptr;
1212b15cb3dSCy Schubert 	if (NULL != pp)
1222b15cb3dSCy Schubert 		close(pp->io.fd);
123a151a66cSOllivier Robert }
124a151a66cSOllivier Robert 
125a151a66cSOllivier Robert 
126a151a66cSOllivier Robert /*
127a151a66cSOllivier Robert  * pcf_poll - called by the transmit procedure
128a151a66cSOllivier Robert  */
129a151a66cSOllivier Robert static void
130a151a66cSOllivier Robert pcf_poll(
131a151a66cSOllivier Robert 	int unit,
132a151a66cSOllivier Robert 	struct peer *peer
133a151a66cSOllivier Robert 	)
134a151a66cSOllivier Robert {
135a151a66cSOllivier Robert 	struct refclockproc *pp;
136a151a66cSOllivier Robert 	char buf[LENPCF];
137a151a66cSOllivier Robert 	struct tm tm, *tp;
138a151a66cSOllivier Robert 	time_t t;
139a151a66cSOllivier Robert 
140a151a66cSOllivier Robert 	pp = peer->procptr;
141a151a66cSOllivier Robert 
142a151a66cSOllivier Robert 	buf[0] = 0;
1432b15cb3dSCy Schubert 	if (read(pp->io.fd, buf, sizeof(buf)) < (ssize_t)sizeof(buf) || buf[0] != 9) {
144a151a66cSOllivier Robert 		refclock_report(peer, CEVNT_FAULT);
145a151a66cSOllivier Robert 		return;
146a151a66cSOllivier Robert 	}
147a151a66cSOllivier Robert 
1482b15cb3dSCy Schubert 	ZERO(tm);
1492b15cb3dSCy Schubert 
150a151a66cSOllivier Robert 	tm.tm_mday = buf[11] * 10 + buf[10];
151a151a66cSOllivier Robert 	tm.tm_mon = buf[13] * 10 + buf[12] - 1;
152a151a66cSOllivier Robert 	tm.tm_year = buf[15] * 10 + buf[14];
153a151a66cSOllivier Robert 	tm.tm_hour = buf[7] * 10 + buf[6];
154a151a66cSOllivier Robert 	tm.tm_min = buf[5] * 10 + buf[4];
155a151a66cSOllivier Robert 	tm.tm_sec = buf[3] * 10 + buf[2];
156224ba2bdSOllivier Robert 	tm.tm_isdst = (buf[8] & 1) ? 1 : (buf[8] & 2) ? 0 : -1;
157a151a66cSOllivier Robert 
158a151a66cSOllivier Robert 	/*
159a151a66cSOllivier Robert 	 * Y2K convert the 2-digit year
160a151a66cSOllivier Robert 	 */
161a151a66cSOllivier Robert 	if (tm.tm_year < 99)
162a151a66cSOllivier Robert 		tm.tm_year += 100;
163a151a66cSOllivier Robert 
164a151a66cSOllivier Robert 	t = mktime(&tm);
165a151a66cSOllivier Robert 	if (t == (time_t) -1) {
166a151a66cSOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
167a151a66cSOllivier Robert 		return;
168a151a66cSOllivier Robert 	}
169a151a66cSOllivier Robert 
170a151a66cSOllivier Robert #if defined(__GLIBC__) && defined(_BSD_SOURCE)
171a151a66cSOllivier Robert 	if ((tm.tm_isdst > 0 && tm.tm_gmtoff != 7200)
172a151a66cSOllivier Robert 	    || (tm.tm_isdst == 0 && tm.tm_gmtoff != 3600)
173a151a66cSOllivier Robert 	    || tm.tm_isdst < 0) {
174a151a66cSOllivier Robert #ifdef DEBUG
175a151a66cSOllivier Robert 		if (debug)
176a151a66cSOllivier Robert 			printf ("local time zone not set to CET/CEST\n");
177a151a66cSOllivier Robert #endif
178a151a66cSOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
179a151a66cSOllivier Robert 		return;
180a151a66cSOllivier Robert 	}
181a151a66cSOllivier Robert #endif
182a151a66cSOllivier Robert 
183a151a66cSOllivier Robert 	pp->lencode = strftime(pp->a_lastcode, BMAX, "%Y %m %d %H %M %S", &tm);
184a151a66cSOllivier Robert 
185a151a66cSOllivier Robert #if defined(_REENTRANT) || defined(_THREAD_SAFE)
186a151a66cSOllivier Robert 	tp = gmtime_r(&t, &tm);
187a151a66cSOllivier Robert #else
188a151a66cSOllivier Robert 	tp = gmtime(&t);
189a151a66cSOllivier Robert #endif
190a151a66cSOllivier Robert 	if (!tp) {
191a151a66cSOllivier Robert 		refclock_report(peer, CEVNT_FAULT);
192a151a66cSOllivier Robert 		return;
193a151a66cSOllivier Robert 	}
194a151a66cSOllivier Robert 
195a151a66cSOllivier Robert 	get_systime(&pp->lastrec);
196a151a66cSOllivier Robert 	pp->polls++;
197a151a66cSOllivier Robert 	pp->year = tp->tm_year + 1900;
198a151a66cSOllivier Robert 	pp->day = tp->tm_yday + 1;
199a151a66cSOllivier Robert 	pp->hour = tp->tm_hour;
200a151a66cSOllivier Robert 	pp->minute = tp->tm_min;
201a151a66cSOllivier Robert 	pp->second = tp->tm_sec;
2029c2daa00SOllivier Robert 	pp->nsec = buf[16] * 31250000;
203a151a66cSOllivier Robert 	if (buf[17] & 1)
2049c2daa00SOllivier Robert 		pp->nsec += 500000000;
205a151a66cSOllivier Robert 
206a151a66cSOllivier Robert #ifdef DEBUG
207a151a66cSOllivier Robert 	if (debug)
208a151a66cSOllivier Robert 		printf ("pcf%d: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
209a151a66cSOllivier Robert 			unit, pp->year, tp->tm_mon + 1, tp->tm_mday, pp->hour,
210a151a66cSOllivier Robert 			pp->minute, pp->second);
211a151a66cSOllivier Robert #endif
212a151a66cSOllivier Robert 
213a151a66cSOllivier Robert 	if (!refclock_process(pp)) {
214a151a66cSOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
215a151a66cSOllivier Robert 		return;
216a151a66cSOllivier Robert 	}
217a151a66cSOllivier Robert 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
218224ba2bdSOllivier Robert 	if ((buf[1] & 1) && !(pp->sloppyclockflag & CLK_FLAG2))
219a151a66cSOllivier Robert 		pp->leap = LEAP_NOTINSYNC;
220a151a66cSOllivier Robert 	else
221a151a66cSOllivier Robert 		pp->leap = LEAP_NOWARNING;
2229c2daa00SOllivier Robert 	pp->lastref = pp->lastrec;
223a151a66cSOllivier Robert 	refclock_receive(peer);
224a151a66cSOllivier Robert }
225a151a66cSOllivier Robert #else
226*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT
227a151a66cSOllivier Robert #endif /* REFCLOCK */
228