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