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