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 <time.h> 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 clocks 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 2.2 is available 25 * at http://home.pages.de/~voegele/pcf.html. 26 */ 27 28 /* 29 * Interface definitions 30 */ 31 #define DEVICE "/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[20]; 71 72 /* 73 * Open device file for reading. 74 */ 75 (void)sprintf(device, DEVICE, unit); 76 #ifdef DEBUG 77 if (debug) 78 printf ("starting PCF with device %s\n",device); 79 #endif 80 if ((fd = open(device, O_RDONLY)) == -1) { 81 return (0); 82 } 83 84 pp = peer->procptr; 85 pp->io.clock_recv = noentry; 86 pp->io.srcclock = (caddr_t)peer; 87 pp->io.datalen = 0; 88 pp->io.fd = fd; 89 90 /* 91 * Initialize miscellaneous variables 92 */ 93 peer->precision = PRECISION; 94 pp->clockdesc = DESCRIPTION; 95 memcpy((char *)&pp->refid, REFID, 4); 96 97 return (1); 98 } 99 100 101 /* 102 * pcf_shutdown - shut down the clock 103 */ 104 static void 105 pcf_shutdown( 106 int unit, 107 struct peer *peer 108 ) 109 { 110 struct refclockproc *pp; 111 112 pp = peer->procptr; 113 (void)close(pp->io.fd); 114 } 115 116 117 /* 118 * pcf_poll - called by the transmit procedure 119 */ 120 static void 121 pcf_poll( 122 int unit, 123 struct peer *peer 124 ) 125 { 126 struct refclockproc *pp; 127 char buf[LENPCF]; 128 struct tm tm, *tp; 129 time_t t; 130 131 pp = peer->procptr; 132 133 buf[0] = 0; 134 if (read(pp->io.fd, buf, sizeof(buf)) < sizeof(buf) || buf[0] != 9) { 135 refclock_report(peer, CEVNT_FAULT); 136 return; 137 } 138 139 tm.tm_mday = buf[11] * 10 + buf[10]; 140 tm.tm_mon = buf[13] * 10 + buf[12] - 1; 141 tm.tm_year = buf[15] * 10 + buf[14]; 142 tm.tm_hour = buf[7] * 10 + buf[6]; 143 tm.tm_min = buf[5] * 10 + buf[4]; 144 tm.tm_sec = buf[3] * 10 + buf[2]; 145 tm.tm_isdst = -1; 146 147 /* 148 * Y2K convert the 2-digit year 149 */ 150 if (tm.tm_year < 99) 151 tm.tm_year += 100; 152 153 t = mktime(&tm); 154 if (t == (time_t) -1) { 155 refclock_report(peer, CEVNT_BADTIME); 156 return; 157 } 158 159 #if defined(__GLIBC__) && defined(_BSD_SOURCE) 160 if ((tm.tm_isdst > 0 && tm.tm_gmtoff != 7200) 161 || (tm.tm_isdst == 0 && tm.tm_gmtoff != 3600) 162 || tm.tm_isdst < 0) { 163 #ifdef DEBUG 164 if (debug) 165 printf ("local time zone not set to CET/CEST\n"); 166 #endif 167 refclock_report(peer, CEVNT_BADTIME); 168 return; 169 } 170 #endif 171 172 pp->lencode = strftime(pp->a_lastcode, BMAX, "%Y %m %d %H %M %S", &tm); 173 174 #if defined(_REENTRANT) || defined(_THREAD_SAFE) 175 tp = gmtime_r(&t, &tm); 176 #else 177 tp = gmtime(&t); 178 #endif 179 if (!tp) { 180 refclock_report(peer, CEVNT_FAULT); 181 return; 182 } 183 184 get_systime(&pp->lastrec); 185 pp->polls++; 186 pp->year = tp->tm_year + 1900; 187 pp->day = tp->tm_yday + 1; 188 pp->hour = tp->tm_hour; 189 pp->minute = tp->tm_min; 190 pp->second = tp->tm_sec; 191 pp->usec = buf[16] * 31250; 192 if (buf[17] & 1) 193 pp->usec += 500000; 194 195 #ifdef DEBUG 196 if (debug) 197 printf ("pcf%d: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", 198 unit, pp->year, tp->tm_mon + 1, tp->tm_mday, pp->hour, 199 pp->minute, pp->second); 200 #endif 201 202 if (!refclock_process(pp)) { 203 refclock_report(peer, CEVNT_BADTIME); 204 return; 205 } 206 record_clock_stats(&peer->srcadr, pp->a_lastcode); 207 if (buf[1] & 1) 208 pp->leap = LEAP_NOTINSYNC; 209 else 210 pp->leap = LEAP_NOWARNING; 211 refclock_receive(peer); 212 } 213 #else 214 int refclock_pcf_bs; 215 #endif /* REFCLOCK */ 216