156746Sroberto/*
256746Sroberto * refclock_pcf - clock driver for the Conrad parallel port radio clock
356746Sroberto */
456746Sroberto
556746Sroberto#ifdef HAVE_CONFIG_H
656746Sroberto# include <config.h>
756746Sroberto#endif
856746Sroberto
956746Sroberto#if defined(REFCLOCK) && defined(CLOCK_PCF)
1056746Sroberto
1156746Sroberto#include "ntpd.h"
1256746Sroberto#include "ntp_io.h"
1356746Sroberto#include "ntp_refclock.h"
1456746Sroberto#include "ntp_calendar.h"
1556746Sroberto#include "ntp_stdlib.h"
1656746Sroberto
1756746Sroberto/*
1882498Sroberto * This driver supports the parallel port radio clock sold by Conrad
1956746Sroberto * Electronic under order numbers 967602 and 642002.
2056746Sroberto *
2156746Sroberto * It requires that the local timezone be CET/CEST and that the pcfclock
2282498Sroberto * device driver be installed.  A device driver for Linux is available at
2382498Sroberto * http://home.pages.de/~voegele/pcf.html.  Information about a FreeBSD
2482498Sroberto * driver is available at http://schumann.cx/pcfclock/.
2556746Sroberto */
2656746Sroberto
2756746Sroberto/*
2856746Sroberto * Interface definitions
2956746Sroberto */
3082498Sroberto#define	DEVICE		"/dev/pcfclocks/%d"
3182498Sroberto#define	OLDDEVICE	"/dev/pcfclock%d"
3256746Sroberto#define	PRECISION	(-1)	/* precision assumed (about 0.5 s) */
3356746Sroberto#define REFID		"PCF"
3456746Sroberto#define DESCRIPTION	"Conrad parallel port radio clock"
3556746Sroberto
3656746Sroberto#define LENPCF		18	/* timecode length */
3756746Sroberto
3856746Sroberto/*
3956746Sroberto * Function prototypes
4056746Sroberto */
41280849Scystatic	int 	pcf_start 		(int, struct peer *);
42280849Scystatic	void	pcf_shutdown		(int, struct peer *);
43280849Scystatic	void	pcf_poll		(int, struct peer *);
4456746Sroberto
4556746Sroberto/*
4656746Sroberto * Transfer vector
4756746Sroberto */
4856746Srobertostruct  refclock refclock_pcf = {
4956746Sroberto	pcf_start,              /* start up driver */
5056746Sroberto	pcf_shutdown,           /* shut down driver */
5156746Sroberto	pcf_poll,               /* transmit poll message */
5256746Sroberto	noentry,                /* not used */
5356746Sroberto	noentry,                /* initialize driver (not used) */
5456746Sroberto	noentry,                /* not used */
5556746Sroberto	NOFLAGS                 /* not used */
5656746Sroberto};
5756746Sroberto
5856746Sroberto
5956746Sroberto/*
6056746Sroberto * pcf_start - open the device and initialize data for processing
6156746Sroberto */
6256746Srobertostatic int
6356746Srobertopcf_start(
6456746Sroberto     	int unit,
6556746Sroberto	struct peer *peer
6656746Sroberto	)
6756746Sroberto{
6856746Sroberto	struct refclockproc *pp;
6956746Sroberto	int fd;
7082498Sroberto	char device[128];
7156746Sroberto
7256746Sroberto	/*
7356746Sroberto	 * Open device file for reading.
7456746Sroberto	 */
75280849Scy	snprintf(device, sizeof(device), DEVICE, unit);
7682498Sroberto	fd = open(device, O_RDONLY);
7782498Sroberto	if (fd == -1) {
78280849Scy		snprintf(device, sizeof(device), OLDDEVICE, unit);
7982498Sroberto		fd = open(device, O_RDONLY);
8082498Sroberto	}
8156746Sroberto#ifdef DEBUG
8256746Sroberto	if (debug)
8356746Sroberto		printf ("starting PCF with device %s\n",device);
8456746Sroberto#endif
8582498Sroberto	if (fd == -1) {
8656746Sroberto		return (0);
8756746Sroberto	}
8856746Sroberto
8956746Sroberto	pp = peer->procptr;
9056746Sroberto	pp->io.clock_recv = noentry;
91280849Scy	pp->io.srcclock = peer;
9256746Sroberto	pp->io.datalen = 0;
9356746Sroberto	pp->io.fd = fd;
9456746Sroberto
9556746Sroberto	/*
9656746Sroberto	 * Initialize miscellaneous variables
9756746Sroberto	 */
9856746Sroberto	peer->precision = PRECISION;
9956746Sroberto	pp->clockdesc = DESCRIPTION;
10082498Sroberto	/* one transmission takes 172.5 milliseconds since the radio clock
10182498Sroberto	   transmits 69 bits with a period of 2.5 milliseconds per bit */
10282498Sroberto	pp->fudgetime1 = 0.1725;
10356746Sroberto	memcpy((char *)&pp->refid, REFID, 4);
10456746Sroberto
10556746Sroberto	return (1);
10656746Sroberto}
10756746Sroberto
10856746Sroberto
10956746Sroberto/*
11056746Sroberto * pcf_shutdown - shut down the clock
11156746Sroberto */
11256746Srobertostatic void
11356746Srobertopcf_shutdown(
11456746Sroberto	int unit,
11556746Sroberto	struct peer *peer
11656746Sroberto	)
11756746Sroberto{
11856746Sroberto	struct refclockproc *pp;
11956746Sroberto
12056746Sroberto	pp = peer->procptr;
121280849Scy	if (NULL != pp)
122280849Scy		close(pp->io.fd);
12356746Sroberto}
12456746Sroberto
12556746Sroberto
12656746Sroberto/*
12756746Sroberto * pcf_poll - called by the transmit procedure
12856746Sroberto */
12956746Srobertostatic void
13056746Srobertopcf_poll(
13156746Sroberto	int unit,
13256746Sroberto	struct peer *peer
13356746Sroberto	)
13456746Sroberto{
13556746Sroberto	struct refclockproc *pp;
13656746Sroberto	char buf[LENPCF];
13756746Sroberto	struct tm tm, *tp;
13856746Sroberto	time_t t;
13956746Sroberto
14056746Sroberto	pp = peer->procptr;
14156746Sroberto
14256746Sroberto	buf[0] = 0;
143280849Scy	if (read(pp->io.fd, buf, sizeof(buf)) < (ssize_t)sizeof(buf) || buf[0] != 9) {
14456746Sroberto		refclock_report(peer, CEVNT_FAULT);
14556746Sroberto		return;
14656746Sroberto	}
14756746Sroberto
148280849Scy	ZERO(tm);
149280849Scy
15056746Sroberto	tm.tm_mday = buf[11] * 10 + buf[10];
15156746Sroberto	tm.tm_mon = buf[13] * 10 + buf[12] - 1;
15256746Sroberto	tm.tm_year = buf[15] * 10 + buf[14];
15356746Sroberto	tm.tm_hour = buf[7] * 10 + buf[6];
15456746Sroberto	tm.tm_min = buf[5] * 10 + buf[4];
15556746Sroberto	tm.tm_sec = buf[3] * 10 + buf[2];
15682498Sroberto	tm.tm_isdst = (buf[8] & 1) ? 1 : (buf[8] & 2) ? 0 : -1;
15756746Sroberto
15856746Sroberto	/*
15956746Sroberto	 * Y2K convert the 2-digit year
16056746Sroberto	 */
16156746Sroberto	if (tm.tm_year < 99)
16256746Sroberto		tm.tm_year += 100;
16356746Sroberto
16456746Sroberto	t = mktime(&tm);
16556746Sroberto	if (t == (time_t) -1) {
16656746Sroberto		refclock_report(peer, CEVNT_BADTIME);
16756746Sroberto		return;
16856746Sroberto	}
16956746Sroberto
17056746Sroberto#if defined(__GLIBC__) && defined(_BSD_SOURCE)
17156746Sroberto	if ((tm.tm_isdst > 0 && tm.tm_gmtoff != 7200)
17256746Sroberto	    || (tm.tm_isdst == 0 && tm.tm_gmtoff != 3600)
17356746Sroberto	    || tm.tm_isdst < 0) {
17456746Sroberto#ifdef DEBUG
17556746Sroberto		if (debug)
17656746Sroberto			printf ("local time zone not set to CET/CEST\n");
17756746Sroberto#endif
17856746Sroberto		refclock_report(peer, CEVNT_BADTIME);
17956746Sroberto		return;
18056746Sroberto	}
18156746Sroberto#endif
18256746Sroberto
18356746Sroberto	pp->lencode = strftime(pp->a_lastcode, BMAX, "%Y %m %d %H %M %S", &tm);
18456746Sroberto
18556746Sroberto#if defined(_REENTRANT) || defined(_THREAD_SAFE)
18656746Sroberto	tp = gmtime_r(&t, &tm);
18756746Sroberto#else
18856746Sroberto	tp = gmtime(&t);
18956746Sroberto#endif
19056746Sroberto	if (!tp) {
19156746Sroberto		refclock_report(peer, CEVNT_FAULT);
19256746Sroberto		return;
19356746Sroberto	}
19456746Sroberto
19556746Sroberto	get_systime(&pp->lastrec);
19656746Sroberto	pp->polls++;
19756746Sroberto	pp->year = tp->tm_year + 1900;
19856746Sroberto	pp->day = tp->tm_yday + 1;
19956746Sroberto	pp->hour = tp->tm_hour;
20056746Sroberto	pp->minute = tp->tm_min;
20156746Sroberto	pp->second = tp->tm_sec;
202132451Sroberto	pp->nsec = buf[16] * 31250000;
20356746Sroberto	if (buf[17] & 1)
204132451Sroberto		pp->nsec += 500000000;
20556746Sroberto
20656746Sroberto#ifdef DEBUG
20756746Sroberto	if (debug)
20856746Sroberto		printf ("pcf%d: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
20956746Sroberto			unit, pp->year, tp->tm_mon + 1, tp->tm_mday, pp->hour,
21056746Sroberto			pp->minute, pp->second);
21156746Sroberto#endif
21256746Sroberto
21356746Sroberto	if (!refclock_process(pp)) {
21456746Sroberto		refclock_report(peer, CEVNT_BADTIME);
21556746Sroberto		return;
21656746Sroberto	}
21756746Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
21882498Sroberto	if ((buf[1] & 1) && !(pp->sloppyclockflag & CLK_FLAG2))
21956746Sroberto		pp->leap = LEAP_NOTINSYNC;
22056746Sroberto	else
22156746Sroberto		pp->leap = LEAP_NOWARNING;
222132451Sroberto	pp->lastref = pp->lastrec;
22356746Sroberto	refclock_receive(peer);
22456746Sroberto}
22556746Sroberto#else
22656746Srobertoint refclock_pcf_bs;
22756746Sroberto#endif /* REFCLOCK */
228