182498Sroberto/* 282498Sroberto * 382498Sroberto * refclock_hopfser.c 482498Sroberto * - clock driver for hopf serial boards (GPS or DCF77) 582498Sroberto * 682498Sroberto * Date: 30.03.2000 Revision: 01.10 782498Sroberto * 882498Sroberto * latest source and further information can be found at: 982498Sroberto * http://www.ATLSoft.de/ntp 1082498Sroberto * 1182498Sroberto */ 1282498Sroberto 1382498Sroberto#ifdef HAVE_CONFIG_H 1482498Sroberto# include "config.h" 1582498Sroberto#endif 1682498Sroberto 1782498Sroberto#if defined(REFCLOCK) && (defined(CLOCK_HOPF_SERIAL)) 1882498Sroberto 1982498Sroberto#include "ntpd.h" 2082498Sroberto#include "ntp_io.h" 2182498Sroberto#include "ntp_control.h" 2282498Sroberto#include "ntp_refclock.h" 2382498Sroberto#include "ntp_unixtime.h" 2482498Sroberto#include "ntp_stdlib.h" 2582498Sroberto 2682498Sroberto#if defined HAVE_SYS_MODEM_H 2782498Sroberto# include <sys/modem.h> 28182007Sroberto# ifndef __QNXNTO__ 29182007Sroberto# define TIOCMSET MCSETA 30182007Sroberto# define TIOCMGET MCGETA 31182007Sroberto# define TIOCM_RTS MRTS 32182007Sroberto# endif 3382498Sroberto#endif 3482498Sroberto 3582498Sroberto#ifdef HAVE_TERMIOS_H 3682498Sroberto# ifdef TERMIOS_NEEDS__SVID3 3782498Sroberto# define _SVID3 3882498Sroberto# endif 3982498Sroberto# include <termios.h> 4082498Sroberto# ifdef TERMIOS_NEEDS__SVID3 4182498Sroberto# undef _SVID3 4282498Sroberto# endif 4382498Sroberto#endif 4482498Sroberto 4582498Sroberto#ifdef HAVE_SYS_IOCTL_H 4682498Sroberto# include <sys/ioctl.h> 4782498Sroberto#endif 4882498Sroberto 49200576Sroberto#ifdef SYS_WINNT 50200576Srobertoextern int async_write(int, const void *, unsigned int); 51200576Sroberto#undef write 52200576Sroberto#define write(fd, data, octets) async_write(fd, data, octets) 53200576Sroberto#endif 54200576Sroberto 5582498Sroberto/* 5682498Sroberto * clock definitions 5782498Sroberto */ 5882498Sroberto#define DESCRIPTION "hopf Elektronik serial clock" /* Long name */ 5982498Sroberto#define PRECISION (-10) /* precision assumed (about 1 ms) */ 6082498Sroberto#define REFID "hopf\0" /* reference ID */ 6182498Sroberto/* 6282498Sroberto * I/O definitions 6382498Sroberto */ 6482498Sroberto#define DEVICE "/dev/hopfclock%d" /* device name and unit */ 6582498Sroberto#define SPEED232 B9600 /* uart speed (9600 baud) */ 6682498Sroberto 6782498Sroberto 6882498Sroberto#define STX 0x02 6982498Sroberto#define ETX 0x03 7082498Sroberto#define CR 0x0c 7182498Sroberto#define LF 0x0a 7282498Sroberto 7382498Sroberto/* parse states */ 7482498Sroberto#define REC_QUEUE_EMPTY 0 7582498Sroberto#define REC_QUEUE_FULL 1 7682498Sroberto 7782498Sroberto#define HOPF_OPMODE 0x0C /* operation mode mask */ 7882498Sroberto#define HOPF_INVALID 0x00 /* no time code available */ 7982498Sroberto#define HOPF_INTERNAL 0x04 /* internal clock */ 8082498Sroberto#define HOPF_RADIO 0x08 /* radio clock */ 8182498Sroberto#define HOPF_RADIOHP 0x0C /* high precision radio clock */ 8282498Sroberto 8382498Sroberto/* 8482498Sroberto * hopfclock unit control structure. 8582498Sroberto */ 8682498Srobertostruct hopfclock_unit { 8782498Sroberto l_fp laststamp; /* last receive timestamp */ 8882498Sroberto short unit; /* NTP refclock unit number */ 8982498Sroberto u_long polled; /* flag to detect noreplies */ 9082498Sroberto char leap_status; /* leap second flag */ 9182498Sroberto int rpt_next; 9282498Sroberto}; 9382498Sroberto 9482498Sroberto/* 9582498Sroberto * Function prototypes 9682498Sroberto */ 9782498Sroberto 98290001Sglebiusstatic int hopfserial_start (int, struct peer *); 99290001Sglebiusstatic void hopfserial_shutdown (int, struct peer *); 100290001Sglebiusstatic void hopfserial_receive (struct recvbuf *); 101290001Sglebiusstatic void hopfserial_poll (int, struct peer *); 102290001Sglebius/* static void hopfserial_io (struct recvbuf *); */ 10382498Sroberto/* 10482498Sroberto * Transfer vector 10582498Sroberto */ 10682498Srobertostruct refclock refclock_hopfser = { 10782498Sroberto hopfserial_start, /* start up driver */ 10882498Sroberto hopfserial_shutdown, /* shut down driver */ 10982498Sroberto hopfserial_poll, /* transmit poll message */ 11082498Sroberto noentry, /* not used */ 11182498Sroberto noentry, /* initialize driver (not used) */ 11282498Sroberto noentry, /* not used */ 11382498Sroberto NOFLAGS /* not used */ 11482498Sroberto}; 11582498Sroberto 11682498Sroberto/* 11782498Sroberto * hopfserial_start - open the devices and initialize data for processing 11882498Sroberto */ 11982498Srobertostatic int 12082498Srobertohopfserial_start ( 12182498Sroberto int unit, 12282498Sroberto struct peer *peer 12382498Sroberto ) 12482498Sroberto{ 12582498Sroberto register struct hopfclock_unit *up; 12682498Sroberto struct refclockproc *pp; 12782498Sroberto int fd; 12882498Sroberto char gpsdev[20]; 12982498Sroberto 130290001Sglebius snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit); 131290001Sglebius 13282498Sroberto /* LDISC_STD, LDISC_RAW 13382498Sroberto * Open serial port. Use CLK line discipline, if available. 13482498Sroberto */ 13582498Sroberto fd = refclock_open(gpsdev, SPEED232, LDISC_CLK); 13682498Sroberto if (fd <= 0) { 13782498Sroberto#ifdef DEBUG 13882498Sroberto printf("hopfSerialClock(%d) start: open %s failed\n", unit, gpsdev); 13982498Sroberto#endif 14082498Sroberto return 0; 14182498Sroberto } 14282498Sroberto 14382498Sroberto msyslog(LOG_NOTICE, "hopfSerialClock(%d) fd: %d dev: %s", unit, fd, 14482498Sroberto gpsdev); 14582498Sroberto 14682498Sroberto /* 14782498Sroberto * Allocate and initialize unit structure 14882498Sroberto */ 149290001Sglebius up = emalloc_zero(sizeof(*up)); 15082498Sroberto pp = peer->procptr; 151290001Sglebius pp->unitptr = up; 15282498Sroberto pp->io.clock_recv = hopfserial_receive; 153290001Sglebius pp->io.srcclock = peer; 15482498Sroberto pp->io.datalen = 0; 15582498Sroberto pp->io.fd = fd; 15682498Sroberto if (!io_addclock(&pp->io)) { 15782498Sroberto#ifdef DEBUG 158290001Sglebius printf("hopfSerialClock(%d) io_addclock\n", unit); 15982498Sroberto#endif 160290001Sglebius close(fd); 161290001Sglebius pp->io.fd = -1; 16282498Sroberto free(up); 163290001Sglebius pp->unitptr = NULL; 16482498Sroberto return (0); 16582498Sroberto } 16682498Sroberto 16782498Sroberto /* 16882498Sroberto * Initialize miscellaneous variables 16982498Sroberto */ 17082498Sroberto pp->clockdesc = DESCRIPTION; 17182498Sroberto peer->precision = PRECISION; 17282498Sroberto memcpy((char *)&pp->refid, REFID, 4); 17382498Sroberto 17482498Sroberto up->leap_status = 0; 17582498Sroberto up->unit = (short) unit; 17682498Sroberto 17782498Sroberto return (1); 17882498Sroberto} 17982498Sroberto 18082498Sroberto 18182498Sroberto/* 18282498Sroberto * hopfserial_shutdown - shut down the clock 18382498Sroberto */ 18482498Srobertostatic void 18582498Srobertohopfserial_shutdown ( 18682498Sroberto int unit, 18782498Sroberto struct peer *peer 18882498Sroberto ) 18982498Sroberto{ 19082498Sroberto register struct hopfclock_unit *up; 19182498Sroberto struct refclockproc *pp; 19282498Sroberto 19382498Sroberto pp = peer->procptr; 194290001Sglebius up = pp->unitptr; 195290001Sglebius 196290001Sglebius if (-1 != pp->io.fd) 197290001Sglebius io_closeclock(&pp->io); 198290001Sglebius if (NULL != up) 199290001Sglebius free(up); 20082498Sroberto} 20182498Sroberto 20282498Sroberto 20382498Sroberto 20482498Sroberto/* 20582498Sroberto * hopfserial_receive - receive data from the serial interface 20682498Sroberto */ 20782498Sroberto 20882498Srobertostatic void 20982498Srobertohopfserial_receive ( 21082498Sroberto struct recvbuf *rbufp 21182498Sroberto ) 21282498Sroberto{ 21382498Sroberto struct hopfclock_unit *up; 21482498Sroberto struct refclockproc *pp; 21582498Sroberto struct peer *peer; 21682498Sroberto 217290001Sglebius int synch; /* synchhronization indicator */ 218290001Sglebius int DoW; /* Day of Week */ 21982498Sroberto 22082498Sroberto int day, month; /* ddd conversion */ 221290001Sglebius int converted; 22282498Sroberto 22382498Sroberto /* 22482498Sroberto * Initialize pointers and read the timecode and timestamp. 22582498Sroberto */ 226290001Sglebius peer = rbufp->recv_peer; 22782498Sroberto pp = peer->procptr; 228290001Sglebius up = pp->unitptr; 22982498Sroberto 23082498Sroberto if (up->rpt_next == 0 ) 23182498Sroberto return; 23282498Sroberto 23382498Sroberto up->rpt_next = 0; /* wait until next poll interval occur */ 23482498Sroberto 235290001Sglebius pp->lencode = (u_short)refclock_gtlin(rbufp, pp->a_lastcode, 236290001Sglebius sizeof(pp->a_lastcode), 237290001Sglebius &pp->lastrec); 238290001Sglebius if (pp->lencode == 0) 23982498Sroberto return; 24082498Sroberto 241290001Sglebius converted = sscanf(pp->a_lastcode, 24282498Sroberto#if 1 24382498Sroberto "%1x%1x%2d%2d%2d%2d%2d%2d", /* ...cr,lf */ 24482498Sroberto#else 24582498Sroberto "%*c%1x%1x%2d%2d%2d%2d%2d%2d", /* stx...cr,lf,etx */ 24682498Sroberto#endif 247132451Sroberto &synch, 24882498Sroberto &DoW, 24982498Sroberto &pp->hour, 25082498Sroberto &pp->minute, 25182498Sroberto &pp->second, 25282498Sroberto &day, 25382498Sroberto &month, 25482498Sroberto &pp->year); 25582498Sroberto 25682498Sroberto 25782498Sroberto /* 25882498Sroberto Validate received values at least enough to prevent internal 25982498Sroberto array-bounds problems, etc. 26082498Sroberto */ 261290001Sglebius if ((8 != converted) || (pp->hour < 0) || (pp->hour > 23) || 262290001Sglebius (pp->minute < 0) || (pp->minute > 59) || (pp->second < 0) || 263290001Sglebius (pp->second > 60) /*Allow for leap seconds.*/ || 26482498Sroberto (day < 1) || (day > 31) || 26582498Sroberto (month < 1) || (month > 12) || 26682498Sroberto (pp->year < 0) || (pp->year > 99)) { 26782498Sroberto /* Data out of range. */ 26882498Sroberto refclock_report(peer, CEVNT_BADREPLY); 26982498Sroberto return; 27082498Sroberto } 27182498Sroberto /* 27282498Sroberto some preparations 27382498Sroberto */ 27482498Sroberto pp->day = ymd2yd(pp->year,month,day); 27582498Sroberto pp->leap=0; 27682498Sroberto 27782498Sroberto /* Year-2000 check! */ 27882498Sroberto /* wrap 2-digit date into 4-digit */ 27982498Sroberto 28082498Sroberto if(pp->year < YEAR_PIVOT) { pp->year += 100; } /* < 98 */ 28182498Sroberto pp->year += 1900; 28282498Sroberto 28382498Sroberto /* preparation for timecode ntpq rl command ! */ 28482498Sroberto 28582498Sroberto#if 0 286290001Sglebius snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), 28782498Sroberto "STATUS: %1X%1X, DATE: %02d.%02d.%04d TIME: %02d:%02d:%02d", 288132451Sroberto synch, 28982498Sroberto DoW, 29082498Sroberto day, 29182498Sroberto month, 29282498Sroberto pp->year, 29382498Sroberto pp->hour, 29482498Sroberto pp->minute, 29582498Sroberto pp->second); 29682498Sroberto 29782498Sroberto pp->lencode = strlen(pp->a_lastcode); 298132451Sroberto if ((synch && 0xc) == 0 ){ /* time ok? */ 29982498Sroberto refclock_report(peer, CEVNT_BADTIME); 30082498Sroberto pp->leap = LEAP_NOTINSYNC; 30182498Sroberto return; 30282498Sroberto } 30382498Sroberto#endif 30482498Sroberto /* 30582498Sroberto * If clock has no valid status then report error and exit 30682498Sroberto */ 307132451Sroberto if ((synch & HOPF_OPMODE) == HOPF_INVALID ){ /* time ok? */ 30882498Sroberto refclock_report(peer, CEVNT_BADTIME); 30982498Sroberto pp->leap = LEAP_NOTINSYNC; 31082498Sroberto return; 31182498Sroberto } 31282498Sroberto 31382498Sroberto /* 31482498Sroberto * Test if time is running on internal quarz 31582498Sroberto * if CLK_FLAG1 is set, sychronize even if no radio operation 31682498Sroberto */ 31782498Sroberto 318132451Sroberto if ((synch & HOPF_OPMODE) == HOPF_INTERNAL){ 31982498Sroberto if ((pp->sloppyclockflag & CLK_FLAG1) == 0) { 32082498Sroberto refclock_report(peer, CEVNT_BADTIME); 32182498Sroberto pp->leap = LEAP_NOTINSYNC; 32282498Sroberto return; 32382498Sroberto } 32482498Sroberto } 32582498Sroberto 32682498Sroberto 32782498Sroberto if (!refclock_process(pp)) { 32882498Sroberto refclock_report(peer, CEVNT_BADTIME); 32982498Sroberto return; 33082498Sroberto } 331132451Sroberto pp->lastref = pp->lastrec; 33282498Sroberto refclock_receive(peer); 33382498Sroberto 33482498Sroberto#if 0 335132451Sroberto msyslog(LOG_ERR, " D:%x D:%d D:%d",synch,pp->minute,pp->second); 33682498Sroberto#endif 33782498Sroberto 33882498Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 33982498Sroberto 34082498Sroberto return; 34182498Sroberto} 34282498Sroberto 34382498Sroberto 34482498Sroberto/* 34582498Sroberto * hopfserial_poll - called by the transmit procedure 34682498Sroberto * 34782498Sroberto */ 34882498Srobertostatic void 34982498Srobertohopfserial_poll ( 35082498Sroberto int unit, 35182498Sroberto struct peer *peer 35282498Sroberto ) 35382498Sroberto{ 35482498Sroberto register struct hopfclock_unit *up; 35582498Sroberto struct refclockproc *pp; 35682498Sroberto pp = peer->procptr; 35782498Sroberto 358290001Sglebius up = pp->unitptr; 35982498Sroberto 36082498Sroberto pp->polls++; 36182498Sroberto up->rpt_next = 1; 36282498Sroberto 36382498Sroberto#if 0 36482498Sroberto record_clock_stats(&peer->srcadr, pp->a_lastcode); 36582498Sroberto#endif 36682498Sroberto 36782498Sroberto return; 36882498Sroberto} 36982498Sroberto 37082498Sroberto#else 37182498Srobertoint refclock_hopfser_bs; 37282498Sroberto#endif /* REFCLOCK */ 373