1 /* $NetBSD: refclock_pcf.c,v 1.2 2010/12/04 23:08:35 christos Exp $ */ 2 3 /* 4 * refclock_pcf - clock driver for the Conrad parallel port radio clock 5 */ 6 7 #ifdef HAVE_CONFIG_H 8 # include <config.h> 9 #endif 10 11 #if defined(REFCLOCK) && defined(CLOCK_PCF) 12 13 #include "ntpd.h" 14 #include "ntp_io.h" 15 #include "ntp_refclock.h" 16 #include "ntp_calendar.h" 17 #include "ntp_stdlib.h" 18 19 /* 20 * This driver supports the parallel port radio clock sold by Conrad 21 * Electronic under order numbers 967602 and 642002. 22 * 23 * It requires that the local timezone be CET/CEST and that the pcfclock 24 * device driver be installed. A device driver for Linux is available at 25 * http://home.pages.de/~voegele/pcf.html. Information about a FreeBSD 26 * driver is available at http://schumann.cx/pcfclock/. 27 */ 28 29 /* 30 * Interface definitions 31 */ 32 #define DEVICE "/dev/pcfclocks/%d" 33 #define OLDDEVICE "/dev/pcfclock%d" 34 #define PRECISION (-1) /* precision assumed (about 0.5 s) */ 35 #define REFID "PCF" 36 #define DESCRIPTION "Conrad parallel port radio clock" 37 38 #define LENPCF 18 /* timecode length */ 39 40 /* 41 * Function prototypes 42 */ 43 static int pcf_start (int, struct peer *); 44 static void pcf_shutdown (int, struct peer *); 45 static void pcf_poll (int, struct peer *); 46 47 /* 48 * Transfer vector 49 */ 50 struct refclock refclock_pcf = { 51 pcf_start, /* start up driver */ 52 pcf_shutdown, /* shut down driver */ 53 pcf_poll, /* transmit poll message */ 54 noentry, /* not used */ 55 noentry, /* initialize driver (not used) */ 56 noentry, /* not used */ 57 NOFLAGS /* not used */ 58 }; 59 60 61 /* 62 * pcf_start - open the device and initialize data for processing 63 */ 64 static int 65 pcf_start( 66 int unit, 67 struct peer *peer 68 ) 69 { 70 struct refclockproc *pp; 71 int fd; 72 char device[128]; 73 74 /* 75 * Open device file for reading. 76 */ 77 (void)sprintf(device, DEVICE, unit); 78 fd = open(device, O_RDONLY); 79 if (fd == -1) { 80 (void)sprintf(device, OLDDEVICE, unit); 81 fd = open(device, O_RDONLY); 82 } 83 #ifdef DEBUG 84 if (debug) 85 printf ("starting PCF with device %s\n",device); 86 #endif 87 if (fd == -1) { 88 return (0); 89 } 90 91 pp = peer->procptr; 92 pp->io.clock_recv = noentry; 93 pp->io.srcclock = (caddr_t)peer; 94 pp->io.datalen = 0; 95 pp->io.fd = fd; 96 97 /* 98 * Initialize miscellaneous variables 99 */ 100 peer->precision = PRECISION; 101 pp->clockdesc = DESCRIPTION; 102 /* one transmission takes 172.5 milliseconds since the radio clock 103 transmits 69 bits with a period of 2.5 milliseconds per bit */ 104 pp->fudgetime1 = 0.1725; 105 memcpy((char *)&pp->refid, REFID, 4); 106 107 return (1); 108 } 109 110 111 /* 112 * pcf_shutdown - shut down the clock 113 */ 114 static void 115 pcf_shutdown( 116 int unit, 117 struct peer *peer 118 ) 119 { 120 struct refclockproc *pp; 121 122 pp = peer->procptr; 123 (void)close(pp->io.fd); 124 } 125 126 127 /* 128 * pcf_poll - called by the transmit procedure 129 */ 130 static void 131 pcf_poll( 132 int unit, 133 struct peer *peer 134 ) 135 { 136 struct refclockproc *pp; 137 char buf[LENPCF]; 138 struct tm tm, *tp; 139 time_t t; 140 141 pp = peer->procptr; 142 143 buf[0] = 0; 144 if (read(pp->io.fd, buf, sizeof(buf)) < (ssize_t)sizeof(buf) || buf[0] != 9) { 145 refclock_report(peer, CEVNT_FAULT); 146 return; 147 } 148 149 memset(&tm, 0, sizeof(tm)); 150 151 tm.tm_mday = buf[11] * 10 + buf[10]; 152 tm.tm_mon = buf[13] * 10 + buf[12] - 1; 153 tm.tm_year = buf[15] * 10 + buf[14]; 154 tm.tm_hour = buf[7] * 10 + buf[6]; 155 tm.tm_min = buf[5] * 10 + buf[4]; 156 tm.tm_sec = buf[3] * 10 + buf[2]; 157 tm.tm_isdst = (buf[8] & 1) ? 1 : (buf[8] & 2) ? 0 : -1; 158 159 /* 160 * Y2K convert the 2-digit year 161 */ 162 if (tm.tm_year < 99) 163 tm.tm_year += 100; 164 165 t = mktime(&tm); 166 if (t == (time_t) -1) { 167 refclock_report(peer, CEVNT_BADTIME); 168 return; 169 } 170 171 #if defined(__GLIBC__) && defined(_BSD_SOURCE) 172 if ((tm.tm_isdst > 0 && tm.tm_gmtoff != 7200) 173 || (tm.tm_isdst == 0 && tm.tm_gmtoff != 3600) 174 || tm.tm_isdst < 0) { 175 #ifdef DEBUG 176 if (debug) 177 printf ("local time zone not set to CET/CEST\n"); 178 #endif 179 refclock_report(peer, CEVNT_BADTIME); 180 return; 181 } 182 #endif 183 184 pp->lencode = strftime(pp->a_lastcode, BMAX, "%Y %m %d %H %M %S", &tm); 185 186 #if defined(_REENTRANT) || defined(_THREAD_SAFE) 187 tp = gmtime_r(&t, &tm); 188 #else 189 tp = gmtime(&t); 190 #endif 191 if (!tp) { 192 refclock_report(peer, CEVNT_FAULT); 193 return; 194 } 195 196 get_systime(&pp->lastrec); 197 pp->polls++; 198 pp->year = tp->tm_year + 1900; 199 pp->day = tp->tm_yday + 1; 200 pp->hour = tp->tm_hour; 201 pp->minute = tp->tm_min; 202 pp->second = tp->tm_sec; 203 pp->nsec = buf[16] * 31250000; 204 if (buf[17] & 1) 205 pp->nsec += 500000000; 206 207 #ifdef DEBUG 208 if (debug) 209 printf ("pcf%d: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 210 unit, pp->year, tp->tm_mon + 1, tp->tm_mday, pp->hour, 211 pp->minute, pp->second); 212 #endif 213 214 if (!refclock_process(pp)) { 215 refclock_report(peer, CEVNT_BADTIME); 216 return; 217 } 218 record_clock_stats(&peer->srcadr, pp->a_lastcode); 219 if ((buf[1] & 1) && !(pp->sloppyclockflag & CLK_FLAG2)) 220 pp->leap = LEAP_NOTINSYNC; 221 else 222 pp->leap = LEAP_NOWARNING; 223 pp->lastref = pp->lastrec; 224 refclock_receive(peer); 225 } 226 #else 227 int refclock_pcf_bs; 228 #endif /* REFCLOCK */ 229