1 /* $NetBSD: refclock_hopfser.c,v 1.6 2024/08/18 20:47:18 christos Exp $ */ 2 3 /* 4 * 5 * refclock_hopfser.c 6 * - clock driver for hopf serial boards (GPS or DCF77) 7 * 8 * Date: 30.03.2000 Revision: 01.10 9 * 10 * latest source and further information can be found at: 11 * http://www.ATLSoft.de/ntp 12 * 13 */ 14 15 #ifdef HAVE_CONFIG_H 16 # include "config.h" 17 #endif 18 19 #if defined(REFCLOCK) && (defined(CLOCK_HOPF_SERIAL)) 20 21 #include "ntpd.h" 22 #include "ntp_io.h" 23 #include "ntp_control.h" 24 #include "ntp_refclock.h" 25 #include "ntp_unixtime.h" 26 #include "ntp_stdlib.h" 27 28 #if defined HAVE_SYS_MODEM_H 29 # include <sys/modem.h> 30 # ifndef __QNXNTO__ 31 # define TIOCMSET MCSETA 32 # define TIOCMGET MCGETA 33 # define TIOCM_RTS MRTS 34 # endif 35 #endif 36 37 #ifdef HAVE_TERMIOS_H 38 # ifdef TERMIOS_NEEDS__SVID3 39 # define _SVID3 40 # endif 41 # include <termios.h> 42 # ifdef TERMIOS_NEEDS__SVID3 43 # undef _SVID3 44 # endif 45 #endif 46 47 #ifdef HAVE_SYS_IOCTL_H 48 # include <sys/ioctl.h> 49 #endif 50 51 /* 52 * clock definitions 53 */ 54 #define DESCRIPTION "hopf Elektronik serial clock" /* Long name */ 55 #define PRECISION (-10) /* precision assumed (about 1 ms) */ 56 #define REFID "hopf\0" /* reference ID */ 57 /* 58 * I/O definitions 59 */ 60 #define DEVICE "/dev/hopfclock%d" /* device name and unit */ 61 #define SPEED232 B9600 /* uart speed (9600 baud) */ 62 63 64 #define STX 0x02 65 #define ETX 0x03 66 #define CR 0x0c 67 #define LF 0x0a 68 69 /* parse states */ 70 #define REC_QUEUE_EMPTY 0 71 #define REC_QUEUE_FULL 1 72 73 #define HOPF_OPMODE 0x0C /* operation mode mask */ 74 #define HOPF_INVALID 0x00 /* no time code available */ 75 #define HOPF_INTERNAL 0x04 /* internal clock */ 76 #define HOPF_RADIO 0x08 /* radio clock */ 77 #define HOPF_RADIOHP 0x0C /* high precision radio clock */ 78 79 /* 80 * hopfclock unit control structure. 81 */ 82 struct hopfclock_unit { 83 l_fp laststamp; /* last receive timestamp */ 84 short unit; /* NTP refclock unit number */ 85 u_long polled; /* flag to detect noreplies */ 86 char leap_status; /* leap second flag */ 87 int rpt_next; 88 }; 89 90 /* 91 * Function prototypes 92 */ 93 94 static int hopfserial_start (int, struct peer *); 95 static void hopfserial_shutdown (int, struct peer *); 96 static void hopfserial_receive (struct recvbuf *); 97 static void hopfserial_poll (int, struct peer *); 98 /* static void hopfserial_io (struct recvbuf *); */ 99 /* 100 * Transfer vector 101 */ 102 struct refclock refclock_hopfser = { 103 hopfserial_start, /* start up driver */ 104 hopfserial_shutdown, /* shut down driver */ 105 hopfserial_poll, /* transmit poll message */ 106 noentry, /* not used */ 107 noentry, /* initialize driver (not used) */ 108 noentry, /* not used */ 109 NOFLAGS /* not used */ 110 }; 111 112 /* 113 * hopfserial_start - open the devices and initialize data for processing 114 */ 115 static int 116 hopfserial_start ( 117 int unit, 118 struct peer *peer 119 ) 120 { 121 register struct hopfclock_unit *up; 122 struct refclockproc *pp; 123 int fd; 124 char gpsdev[20]; 125 126 snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit); 127 128 /* LDISC_STD, LDISC_RAW 129 * Open serial port. Use CLK line discipline, if available. 130 */ 131 fd = refclock_open(&peer->srcadr, gpsdev, SPEED232, LDISC_CLK); 132 if (fd <= 0) { 133 #ifdef DEBUG 134 printf("hopfSerialClock(%d) start: open %s failed\n", unit, gpsdev); 135 #endif 136 return 0; 137 } 138 139 msyslog(LOG_NOTICE, "hopfSerialClock(%d) fd: %d dev: %s", unit, fd, 140 gpsdev); 141 142 /* 143 * Allocate and initialize unit structure 144 */ 145 up = emalloc_zero(sizeof(*up)); 146 pp = peer->procptr; 147 pp->unitptr = up; 148 pp->io.clock_recv = hopfserial_receive; 149 pp->io.srcclock = peer; 150 pp->io.datalen = 0; 151 pp->io.fd = fd; 152 if (!io_addclock(&pp->io)) { 153 #ifdef DEBUG 154 printf("hopfSerialClock(%d) io_addclock\n", unit); 155 #endif 156 close(fd); 157 pp->io.fd = -1; 158 free(up); 159 pp->unitptr = NULL; 160 return (0); 161 } 162 163 /* 164 * Initialize miscellaneous variables 165 */ 166 pp->clockdesc = DESCRIPTION; 167 peer->precision = PRECISION; 168 memcpy((char *)&pp->refid, REFID, 4); 169 170 up->leap_status = 0; 171 up->unit = (short) unit; 172 173 return (1); 174 } 175 176 177 /* 178 * hopfserial_shutdown - shut down the clock 179 */ 180 static void 181 hopfserial_shutdown ( 182 int unit, 183 struct peer *peer 184 ) 185 { 186 register struct hopfclock_unit *up; 187 struct refclockproc *pp; 188 189 pp = peer->procptr; 190 up = pp->unitptr; 191 192 if (-1 != pp->io.fd) 193 io_closeclock(&pp->io); 194 if (NULL != up) 195 free(up); 196 } 197 198 199 200 /* 201 * hopfserial_receive - receive data from the serial interface 202 */ 203 204 static void 205 hopfserial_receive ( 206 struct recvbuf *rbufp 207 ) 208 { 209 struct hopfclock_unit *up; 210 struct refclockproc *pp; 211 struct peer *peer; 212 213 int synch; /* synchhronization indicator */ 214 int DoW; /* Day of Week */ 215 216 int day, month; /* ddd conversion */ 217 int converted; 218 219 /* 220 * Initialize pointers and read the timecode and timestamp. 221 */ 222 peer = rbufp->recv_peer; 223 pp = peer->procptr; 224 up = pp->unitptr; 225 226 if (up->rpt_next == 0 ) 227 return; 228 229 up->rpt_next = 0; /* wait until next poll interval occur */ 230 231 pp->lencode = (u_short)refclock_gtlin(rbufp, pp->a_lastcode, 232 sizeof(pp->a_lastcode), 233 &pp->lastrec); 234 if (pp->lencode == 0) 235 return; 236 237 converted = sscanf(pp->a_lastcode, 238 #if 1 239 "%1x%1x%2d%2d%2d%2d%2d%2d", /* ...cr,lf */ 240 #else 241 "%*c%1x%1x%2d%2d%2d%2d%2d%2d", /* stx...cr,lf,etx */ 242 #endif 243 &synch, 244 &DoW, 245 &pp->hour, 246 &pp->minute, 247 &pp->second, 248 &day, 249 &month, 250 &pp->year); 251 252 253 /* 254 Validate received values at least enough to prevent internal 255 array-bounds problems, etc. 256 */ 257 if ((8 != converted) || (pp->hour < 0) || (pp->hour > 23) || 258 (pp->minute < 0) || (pp->minute > 59) || (pp->second < 0) || 259 (pp->second > 60) /*Allow for leap seconds.*/ || 260 (day < 1) || (day > 31) || 261 (month < 1) || (month > 12) || 262 (pp->year < 0) || (pp->year > 99)) { 263 /* Data out of range. */ 264 refclock_report(peer, CEVNT_BADREPLY); 265 return; 266 } 267 /* 268 some preparations 269 */ 270 pp->day = ymd2yd(pp->year,month,day); 271 pp->leap=0; 272 273 /* Year-2000 check! */ 274 /* wrap 2-digit date into 4-digit */ 275 276 if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* < 98 */ 277 pp->year += 1900; 278 279 /* preparation for timecode ntpq rl command ! */ 280 281 #if 0 282 snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), 283 "STATUS: %1X%1X, DATE: %02d.%02d.%04d TIME: %02d:%02d:%02d", 284 synch, 285 DoW, 286 day, 287 month, 288 pp->year, 289 pp->hour, 290 pp->minute, 291 pp->second); 292 293 pp->lencode = strlen(pp->a_lastcode); 294 if ((synch && 0xc) == 0 ){ /* time ok? */ 295 refclock_report(peer, CEVNT_BADTIME); 296 pp->leap = LEAP_NOTINSYNC; 297 return; 298 } 299 #endif 300 /* 301 * If clock has no valid status then report error and exit 302 */ 303 if ((synch & HOPF_OPMODE) == HOPF_INVALID ){ /* time ok? */ 304 refclock_report(peer, CEVNT_BADTIME); 305 pp->leap = LEAP_NOTINSYNC; 306 return; 307 } 308 309 /* 310 * Test if time is running on internal quarz 311 * if CLK_FLAG1 is set, sychronize even if no radio operation 312 */ 313 314 if ((synch & HOPF_OPMODE) == HOPF_INTERNAL){ 315 if ((pp->sloppyclockflag & CLK_FLAG1) == 0) { 316 refclock_report(peer, CEVNT_BADTIME); 317 pp->leap = LEAP_NOTINSYNC; 318 return; 319 } 320 } 321 322 323 if (!refclock_process(pp)) { 324 refclock_report(peer, CEVNT_BADTIME); 325 return; 326 } 327 pp->lastref = pp->lastrec; 328 refclock_receive(peer); 329 330 #if 0 331 msyslog(LOG_ERR, " D:%x D:%d D:%d",synch,pp->minute,pp->second); 332 #endif 333 334 record_clock_stats(&peer->srcadr, pp->a_lastcode); 335 336 return; 337 } 338 339 340 /* 341 * hopfserial_poll - called by the transmit procedure 342 * 343 */ 344 static void 345 hopfserial_poll ( 346 int unit, 347 struct peer *peer 348 ) 349 { 350 register struct hopfclock_unit *up; 351 struct refclockproc *pp; 352 pp = peer->procptr; 353 354 up = pp->unitptr; 355 356 pp->polls++; 357 up->rpt_next = 1; 358 359 #if 0 360 record_clock_stats(&peer->srcadr, pp->a_lastcode); 361 #endif 362 363 return; 364 } 365 366 #else 367 NONEMPTY_TRANSLATION_UNIT 368 #endif /* REFCLOCK */ 369