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