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