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 P((int, struct peer *)); 42 static void pcf_shutdown P((int, struct peer *)); 43 static void pcf_poll P((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 (void)sprintf(device, DEVICE, unit); 76 fd = open(device, O_RDONLY); 77 if (fd == -1) { 78 (void)sprintf(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 = (caddr_t)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 (void)close(pp->io.fd); 122 } 123 124 125 /* 126 * pcf_poll - called by the transmit procedure 127 */ 128 static void 129 pcf_poll( 130 int unit, 131 struct peer *peer 132 ) 133 { 134 struct refclockproc *pp; 135 char buf[LENPCF]; 136 struct tm tm, *tp; 137 time_t t; 138 139 pp = peer->procptr; 140 141 buf[0] = 0; 142 if (read(pp->io.fd, buf, sizeof(buf)) < sizeof(buf) || buf[0] != 9) { 143 refclock_report(peer, CEVNT_FAULT); 144 return; 145 } 146 147 tm.tm_mday = buf[11] * 10 + buf[10]; 148 tm.tm_mon = buf[13] * 10 + buf[12] - 1; 149 tm.tm_year = buf[15] * 10 + buf[14]; 150 tm.tm_hour = buf[7] * 10 + buf[6]; 151 tm.tm_min = buf[5] * 10 + buf[4]; 152 tm.tm_sec = buf[3] * 10 + buf[2]; 153 tm.tm_isdst = (buf[8] & 1) ? 1 : (buf[8] & 2) ? 0 : -1; 154 155 /* 156 * Y2K convert the 2-digit year 157 */ 158 if (tm.tm_year < 99) 159 tm.tm_year += 100; 160 161 t = mktime(&tm); 162 if (t == (time_t) -1) { 163 refclock_report(peer, CEVNT_BADTIME); 164 return; 165 } 166 167 #if defined(__GLIBC__) && defined(_BSD_SOURCE) 168 if ((tm.tm_isdst > 0 && tm.tm_gmtoff != 7200) 169 || (tm.tm_isdst == 0 && tm.tm_gmtoff != 3600) 170 || tm.tm_isdst < 0) { 171 #ifdef DEBUG 172 if (debug) 173 printf ("local time zone not set to CET/CEST\n"); 174 #endif 175 refclock_report(peer, CEVNT_BADTIME); 176 return; 177 } 178 #endif 179 180 pp->lencode = strftime(pp->a_lastcode, BMAX, "%Y %m %d %H %M %S", &tm); 181 182 #if defined(_REENTRANT) || defined(_THREAD_SAFE) 183 tp = gmtime_r(&t, &tm); 184 #else 185 tp = gmtime(&t); 186 #endif 187 if (!tp) { 188 refclock_report(peer, CEVNT_FAULT); 189 return; 190 } 191 192 get_systime(&pp->lastrec); 193 pp->polls++; 194 pp->year = tp->tm_year + 1900; 195 pp->day = tp->tm_yday + 1; 196 pp->hour = tp->tm_hour; 197 pp->minute = tp->tm_min; 198 pp->second = tp->tm_sec; 199 pp->nsec = buf[16] * 31250000; 200 if (buf[17] & 1) 201 pp->nsec += 500000000; 202 203 #ifdef DEBUG 204 if (debug) 205 printf ("pcf%d: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 206 unit, pp->year, tp->tm_mon + 1, tp->tm_mday, pp->hour, 207 pp->minute, pp->second); 208 #endif 209 210 if (!refclock_process(pp)) { 211 refclock_report(peer, CEVNT_BADTIME); 212 return; 213 } 214 record_clock_stats(&peer->srcadr, pp->a_lastcode); 215 if ((buf[1] & 1) && !(pp->sloppyclockflag & CLK_FLAG2)) 216 pp->leap = LEAP_NOTINSYNC; 217 else 218 pp->leap = LEAP_NOWARNING; 219 pp->lastref = pp->lastrec; 220 refclock_receive(peer); 221 } 222 #else 223 int refclock_pcf_bs; 224 #endif /* REFCLOCK */ 225