refclock_pcf.c revision 280849
1174199Srwatson/* 2224859Srwatson * refclock_pcf - clock driver for the Conrad parallel port radio clock 3174199Srwatson */ 4174199Srwatson 5174199Srwatson#ifdef HAVE_CONFIG_H 6174199Srwatson# include <config.h> 7174199Srwatson#endif 8174199Srwatson 9174199Srwatson#if defined(REFCLOCK) && defined(CLOCK_PCF) 10174199Srwatson 11174199Srwatson#include "ntpd.h" 12174199Srwatson#include "ntp_io.h" 13174199Srwatson#include "ntp_refclock.h" 14174199Srwatson#include "ntp_calendar.h" 15174199Srwatson#include "ntp_stdlib.h" 16174199Srwatson 17174199Srwatson/* 18174199Srwatson * This driver supports the parallel port radio clock sold by Conrad 19174199Srwatson * Electronic under order numbers 967602 and 642002. 20174199Srwatson * 21174199Srwatson * It requires that the local timezone be CET/CEST and that the pcfclock 22174199Srwatson * device driver be installed. A device driver for Linux is available at 23174199Srwatson * http://home.pages.de/~voegele/pcf.html. Information about a FreeBSD 24174199Srwatson * driver is available at http://schumann.cx/pcfclock/. 25174199Srwatson */ 26174199Srwatson 27174199Srwatson/* 28174199Srwatson * Interface definitions 29186567Srwatson */ 30174199Srwatson#define DEVICE "/dev/pcfclocks/%d" 31174199Srwatson#define OLDDEVICE "/dev/pcfclock%d" 32174199Srwatson#define PRECISION (-1) /* precision assumed (about 0.5 s) */ 33174199Srwatson#define REFID "PCF" 34221807Sstas#define DESCRIPTION "Conrad parallel port radio clock" 35174199Srwatson 36174199Srwatson#define LENPCF 18 /* timecode length */ 37174199Srwatson 38174199Srwatson/* 39174199Srwatson * Function prototypes 40174199Srwatson */ 41174199Srwatsonstatic int pcf_start (int, struct peer *); 42267979Sjhbstatic void pcf_shutdown (int, struct peer *); 43280344Sscottlstatic void pcf_poll (int, struct peer *); 44267979Sjhb 45174199Srwatson/* 46174199Srwatson * Transfer vector 47174199Srwatson */ 48174199Srwatsonstruct refclock refclock_pcf = { 49174199Srwatson pcf_start, /* start up driver */ 50267979Sjhb pcf_shutdown, /* shut down driver */ 51227838Strociny pcf_poll, /* transmit poll message */ 52227838Strociny noentry, /* not used */ 53280344Sscottl noentry, /* initialize driver (not used) */ 54267979Sjhb noentry, /* not used */ 55174199Srwatson NOFLAGS /* not used */ 56174199Srwatson}; 57174199Srwatson 58174199Srwatson 59221807Sstas/* 60174199Srwatson * pcf_start - open the device and initialize data for processing 61174199Srwatson */ 62174199Srwatsonstatic int 63249678Strocinypcf_start( 64174199Srwatson int unit, 65249680Strociny struct peer *peer 66227838Strociny ) 67249680Strociny{ 68174199Srwatson struct refclockproc *pp; 69221807Sstas int fd; 70204879Skib char device[128]; 71221807Sstas 72204879Skib /* 73221807Sstas * Open device file for reading. 74174199Srwatson */ 75249685Strociny snprintf(device, sizeof(device), DEVICE, unit); 76227956Strociny fd = open(device, O_RDONLY); 77249675Strociny if (fd == -1) { 78267979Sjhb snprintf(device, sizeof(device), OLDDEVICE, unit); 79267979Sjhb fd = open(device, O_RDONLY); 80174199Srwatson } 81249671Strociny#ifdef DEBUG 82174199Srwatson if (debug) 83249668Strociny printf ("starting PCF with device %s\n",device); 84174199Srwatson#endif 85249669Strociny if (fd == -1) { 86227838Strociny return (0); 87249683Strociny } 88280344Sscottl 89280344Sscottl pp = peer->procptr; 90174199Srwatson pp->io.clock_recv = noentry; 91221807Sstas pp->io.srcclock = peer; 92174199Srwatson pp->io.datalen = 0; 93174199Srwatson pp->io.fd = fd; 94174199Srwatson 95174199Srwatson /* 96174199Srwatson * Initialize miscellaneous variables 97174199Srwatson */ 98174199Srwatson peer->precision = PRECISION; 99174199Srwatson pp->clockdesc = DESCRIPTION; 100174199Srwatson /* one transmission takes 172.5 milliseconds since the radio clock 101174199Srwatson transmits 69 bits with a period of 2.5 milliseconds per bit */ 102176107Sdwmalone pp->fudgetime1 = 0.1725; 103176107Sdwmalone memcpy((char *)&pp->refid, REFID, 4); 104174199Srwatson 105174199Srwatson return (1); 106176107Sdwmalone} 107176107Sdwmalone 108174199Srwatson 109174199Srwatson/* 110174199Srwatson * pcf_shutdown - shut down the clock 111174199Srwatson */ 112174199Srwatsonstatic void 113174199Srwatsonpcf_shutdown( 114174199Srwatson int unit, 115174199Srwatson struct peer *peer 116174199Srwatson ) 117174199Srwatson{ 118174199Srwatson struct refclockproc *pp; 119174199Srwatson 120174199Srwatson pp = peer->procptr; 121221807Sstas if (NULL != pp) 122221807Sstas close(pp->io.fd); 123221807Sstas} 124249686Strociny 125174199Srwatson 126174199Srwatson/* 127174199Srwatson * pcf_poll - called by the transmit procedure 128221807Sstas */ 129221807Sstasstatic void 130174199Srwatsonpcf_poll( 131174199Srwatson int unit, 132221807Sstas struct peer *peer 133280344Sscottl ) 134174199Srwatson{ 135224859Srwatson struct refclockproc *pp; 136224859Srwatson char buf[LENPCF]; 137224859Srwatson struct tm tm, *tp; 138224859Srwatson time_t t; 139267979Sjhb 140267979Sjhb pp = peer->procptr; 141267979Sjhb 142267979Sjhb buf[0] = 0; 143221807Sstas if (read(pp->io.fd, buf, sizeof(buf)) < (ssize_t)sizeof(buf) || buf[0] != 9) { 144221807Sstas refclock_report(peer, CEVNT_FAULT); 145221807Sstas return; 146221807Sstas } 147221807Sstas 148221807Sstas ZERO(tm); 149280344Sscottl 150280344Sscottl tm.tm_mday = buf[11] * 10 + buf[10]; 151280344Sscottl tm.tm_mon = buf[13] * 10 + buf[12] - 1; 152174199Srwatson tm.tm_year = buf[15] * 10 + buf[14]; 153174199Srwatson tm.tm_hour = buf[7] * 10 + buf[6]; 154174199Srwatson tm.tm_min = buf[5] * 10 + buf[4]; 155174199Srwatson tm.tm_sec = buf[3] * 10 + buf[2]; 156174199Srwatson tm.tm_isdst = (buf[8] & 1) ? 1 : (buf[8] & 2) ? 0 : -1; 157174199Srwatson 158174199Srwatson /* 159174199Srwatson * Y2K convert the 2-digit year 160174199Srwatson */ 161174199Srwatson if (tm.tm_year < 99) 162174199Srwatson tm.tm_year += 100; 163174199Srwatson 164227838Strociny t = mktime(&tm); 165227838Strociny if (t == (time_t) -1) { 166227838Strociny refclock_report(peer, CEVNT_BADTIME); 167227838Strociny return; 168174199Srwatson } 169174199Srwatson 170174199Srwatson#if defined(__GLIBC__) && defined(_BSD_SOURCE) 171174199Srwatson if ((tm.tm_isdst > 0 && tm.tm_gmtoff != 7200) 172204879Skib || (tm.tm_isdst == 0 && tm.tm_gmtoff != 3600) 173204879Skib || tm.tm_isdst < 0) { 174204879Skib#ifdef DEBUG 175204879Skib if (debug) 176204879Skib printf ("local time zone not set to CET/CEST\n"); 177204879Skib#endif 178204879Skib refclock_report(peer, CEVNT_BADTIME); 179204879Skib return; 180174199Srwatson } 181174199Srwatson#endif 182174199Srwatson 183174199Srwatson pp->lencode = strftime(pp->a_lastcode, BMAX, "%Y %m %d %H %M %S", &tm); 184227956Strociny 185227956Strociny#if defined(_REENTRANT) || defined(_THREAD_SAFE) 186227956Strociny tp = gmtime_r(&t, &tm); 187227956Strociny#else 188204879Skib tp = gmtime(&t); 189204879Skib#endif 190204879Skib if (!tp) { 191204879Skib refclock_report(peer, CEVNT_FAULT); 192174199Srwatson return; 193174199Srwatson } 194174199Srwatson 195174199Srwatson get_systime(&pp->lastrec); 196267979Sjhb pp->polls++; 197267979Sjhb pp->year = tp->tm_year + 1900; 198267979Sjhb pp->day = tp->tm_yday + 1; 199267979Sjhb pp->hour = tp->tm_hour; 200174199Srwatson pp->minute = tp->tm_min; 201174199Srwatson pp->second = tp->tm_sec; 202174199Srwatson pp->nsec = buf[16] * 31250000; 203174199Srwatson if (buf[17] & 1) 204174199Srwatson pp->nsec += 500000000; 205174199Srwatson 206174199Srwatson#ifdef DEBUG 207174199Srwatson if (debug) 208174199Srwatson printf ("pcf%d: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 209174199Srwatson unit, pp->year, tp->tm_mon + 1, tp->tm_mday, pp->hour, 210174199Srwatson pp->minute, pp->second); 211174199Srwatson#endif 212174199Srwatson 213174199Srwatson if (!refclock_process(pp)) { 214174199Srwatson refclock_report(peer, CEVNT_BADTIME); 215174199Srwatson return; 216174199Srwatson } 217174199Srwatson record_clock_stats(&peer->srcadr, pp->a_lastcode); 218174199Srwatson if ((buf[1] & 1) && !(pp->sloppyclockflag & CLK_FLAG2)) 219174199Srwatson pp->leap = LEAP_NOTINSYNC; 220174199Srwatson else 221227838Strociny pp->leap = LEAP_NOWARNING; 222227838Strociny pp->lastref = pp->lastrec; 223227838Strociny refclock_receive(peer); 224227838Strociny} 225174199Srwatson#else 226174199Srwatsonint refclock_pcf_bs; 227174199Srwatson#endif /* REFCLOCK */ 228174199Srwatson