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 */
4156746Srobertostatic	int 	pcf_start 		P((int, struct peer *));
4256746Srobertostatic	void	pcf_shutdown		P((int, struct peer *));
4356746Srobertostatic	void	pcf_poll		P((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	 */
7556746Sroberto	(void)sprintf(device, DEVICE, unit);
7682498Sroberto	fd = open(device, O_RDONLY);
7782498Sroberto	if (fd == -1) {
7882498Sroberto		(void)sprintf(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;
9156746Sroberto	pp->io.srcclock = (caddr_t)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;
12156746Sroberto	(void)close(pp->io.fd);
12256746Sroberto}
12356746Sroberto
12456746Sroberto
12556746Sroberto/*
12656746Sroberto * pcf_poll - called by the transmit procedure
12756746Sroberto */
12856746Srobertostatic void
12956746Srobertopcf_poll(
13056746Sroberto	int unit,
13156746Sroberto	struct peer *peer
13256746Sroberto	)
13356746Sroberto{
13456746Sroberto	struct refclockproc *pp;
13556746Sroberto	char buf[LENPCF];
13656746Sroberto	struct tm tm, *tp;
13756746Sroberto	time_t t;
13856746Sroberto
13956746Sroberto	pp = peer->procptr;
14056746Sroberto
14156746Sroberto	buf[0] = 0;
14256746Sroberto	if (read(pp->io.fd, buf, sizeof(buf)) < sizeof(buf) || buf[0] != 9) {
14356746Sroberto		refclock_report(peer, CEVNT_FAULT);
14456746Sroberto		return;
14556746Sroberto	}
14656746Sroberto
14756746Sroberto	tm.tm_mday = buf[11] * 10 + buf[10];
14856746Sroberto	tm.tm_mon = buf[13] * 10 + buf[12] - 1;
14956746Sroberto	tm.tm_year = buf[15] * 10 + buf[14];
15056746Sroberto	tm.tm_hour = buf[7] * 10 + buf[6];
15156746Sroberto	tm.tm_min = buf[5] * 10 + buf[4];
15256746Sroberto	tm.tm_sec = buf[3] * 10 + buf[2];
15382498Sroberto	tm.tm_isdst = (buf[8] & 1) ? 1 : (buf[8] & 2) ? 0 : -1;
15456746Sroberto
15556746Sroberto	/*
15656746Sroberto	 * Y2K convert the 2-digit year
15756746Sroberto	 */
15856746Sroberto	if (tm.tm_year < 99)
15956746Sroberto		tm.tm_year += 100;
16056746Sroberto
16156746Sroberto	t = mktime(&tm);
16256746Sroberto	if (t == (time_t) -1) {
16356746Sroberto		refclock_report(peer, CEVNT_BADTIME);
16456746Sroberto		return;
16556746Sroberto	}
16656746Sroberto
16756746Sroberto#if defined(__GLIBC__) && defined(_BSD_SOURCE)
16856746Sroberto	if ((tm.tm_isdst > 0 && tm.tm_gmtoff != 7200)
16956746Sroberto	    || (tm.tm_isdst == 0 && tm.tm_gmtoff != 3600)
17056746Sroberto	    || tm.tm_isdst < 0) {
17156746Sroberto#ifdef DEBUG
17256746Sroberto		if (debug)
17356746Sroberto			printf ("local time zone not set to CET/CEST\n");
17456746Sroberto#endif
17556746Sroberto		refclock_report(peer, CEVNT_BADTIME);
17656746Sroberto		return;
17756746Sroberto	}
17856746Sroberto#endif
17956746Sroberto
18056746Sroberto	pp->lencode = strftime(pp->a_lastcode, BMAX, "%Y %m %d %H %M %S", &tm);
18156746Sroberto
18256746Sroberto#if defined(_REENTRANT) || defined(_THREAD_SAFE)
18356746Sroberto	tp = gmtime_r(&t, &tm);
18456746Sroberto#else
18556746Sroberto	tp = gmtime(&t);
18656746Sroberto#endif
18756746Sroberto	if (!tp) {
18856746Sroberto		refclock_report(peer, CEVNT_FAULT);
18956746Sroberto		return;
19056746Sroberto	}
19156746Sroberto
19256746Sroberto	get_systime(&pp->lastrec);
19356746Sroberto	pp->polls++;
19456746Sroberto	pp->year = tp->tm_year + 1900;
19556746Sroberto	pp->day = tp->tm_yday + 1;
19656746Sroberto	pp->hour = tp->tm_hour;
19756746Sroberto	pp->minute = tp->tm_min;
19856746Sroberto	pp->second = tp->tm_sec;
199132451Sroberto	pp->nsec = buf[16] * 31250000;
20056746Sroberto	if (buf[17] & 1)
201132451Sroberto		pp->nsec += 500000000;
20256746Sroberto
20356746Sroberto#ifdef DEBUG
20456746Sroberto	if (debug)
20556746Sroberto		printf ("pcf%d: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
20656746Sroberto			unit, pp->year, tp->tm_mon + 1, tp->tm_mday, pp->hour,
20756746Sroberto			pp->minute, pp->second);
20856746Sroberto#endif
20956746Sroberto
21056746Sroberto	if (!refclock_process(pp)) {
21156746Sroberto		refclock_report(peer, CEVNT_BADTIME);
21256746Sroberto		return;
21356746Sroberto	}
21456746Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
21582498Sroberto	if ((buf[1] & 1) && !(pp->sloppyclockflag & CLK_FLAG2))
21656746Sroberto		pp->leap = LEAP_NOTINSYNC;
21756746Sroberto	else
21856746Sroberto		pp->leap = LEAP_NOWARNING;
219132451Sroberto	pp->lastref = pp->lastrec;
22056746Sroberto	refclock_receive(peer);
22156746Sroberto}
22256746Sroberto#else
22356746Srobertoint refclock_pcf_bs;
22456746Sroberto#endif /* REFCLOCK */
225