1*eabc0478Schristos /* $NetBSD: refclock_tt560.c,v 1.6 2024/08/18 20:47:19 christos Exp $ */ 2abb0f93cSkardel 3abb0f93cSkardel /* 4abb0f93cSkardel * refclock_tt560 - clock driver for the TrueTime 560 IRIG-B decoder 5abb0f93cSkardel */ 6abb0f93cSkardel 7abb0f93cSkardel #ifdef HAVE_CONFIG_H 8abb0f93cSkardel #include <config.h> 9abb0f93cSkardel #endif 10abb0f93cSkardel 11abb0f93cSkardel #if defined(REFCLOCK) && defined(CLOCK_TT560) 12abb0f93cSkardel 13abb0f93cSkardel #include "ntpd.h" 14abb0f93cSkardel #include "ntp_io.h" 15abb0f93cSkardel #include "ntp_refclock.h" 16abb0f93cSkardel #include "ntp_unixtime.h" 17abb0f93cSkardel #include "sys/tt560_api.h" 18abb0f93cSkardel #include "ntp_stdlib.h" 19abb0f93cSkardel 20abb0f93cSkardel #include <stdio.h> 21abb0f93cSkardel #include <ctype.h> 22abb0f93cSkardel 23abb0f93cSkardel /* 24abb0f93cSkardel * This driver supports the TrueTime 560 IRIG-B decoder for the PCI bus. 25abb0f93cSkardel */ 26abb0f93cSkardel 27abb0f93cSkardel /* 28abb0f93cSkardel * TT560 interface definitions 29abb0f93cSkardel */ 30abb0f93cSkardel #define DEVICE "/dev/tt560%d" /* device name and unit */ 31abb0f93cSkardel #define PRECISION (-20) /* precision assumed (1 us) */ 32abb0f93cSkardel #define REFID "IRIG" /* reference ID */ 33abb0f93cSkardel #define DESCRIPTION "TrueTime 560 IRIG-B PCI Decoder" 34abb0f93cSkardel 35abb0f93cSkardel /* 36abb0f93cSkardel * Unit control structure 37abb0f93cSkardel */ 38abb0f93cSkardel struct tt560unit { 39abb0f93cSkardel tt_mem_space_t *tt_mem; /* mapped address of PCI board */ 40abb0f93cSkardel time_freeze_reg_t tt560rawt; /* data returned from PCI board */ 41abb0f93cSkardel }; 42abb0f93cSkardel 43abb0f93cSkardel typedef union byteswap_u 44abb0f93cSkardel { 45abb0f93cSkardel unsigned int long_word; 46abb0f93cSkardel unsigned char byte[4]; 47abb0f93cSkardel } byteswap_t; 48abb0f93cSkardel 49abb0f93cSkardel /* 50abb0f93cSkardel * Function prototypes 51abb0f93cSkardel */ 52abb0f93cSkardel static int tt560_start (int, struct peer *); 53abb0f93cSkardel static void tt560_shutdown (int, struct peer *); 54abb0f93cSkardel static void tt560_poll (int unit, struct peer *); 55abb0f93cSkardel 56abb0f93cSkardel /* 57abb0f93cSkardel * Transfer vector 58abb0f93cSkardel */ 59abb0f93cSkardel struct refclock refclock_tt560 = { 60abb0f93cSkardel tt560_start, /* clock_start */ 61abb0f93cSkardel tt560_shutdown, /* clock_shutdown */ 62abb0f93cSkardel tt560_poll, /* clock_poll */ 63abb0f93cSkardel noentry, /* clock_control (not used) */ 64abb0f93cSkardel noentry, /* clock_init (not used) */ 65abb0f93cSkardel noentry, /* clock_buginfo (not used) */ 66abb0f93cSkardel NOFLAGS /* clock_flags (not used) */ 67abb0f93cSkardel }; 68abb0f93cSkardel 69abb0f93cSkardel 70abb0f93cSkardel /* 71abb0f93cSkardel * tt560_start - open the TT560 device and initialize data for processing 72abb0f93cSkardel */ 73abb0f93cSkardel static int 74abb0f93cSkardel tt560_start( 75abb0f93cSkardel int unit, 76abb0f93cSkardel struct peer *peer 77abb0f93cSkardel ) 78abb0f93cSkardel { 79abb0f93cSkardel register struct tt560unit *up; 80abb0f93cSkardel struct refclockproc *pp; 81abb0f93cSkardel char device[20]; 82abb0f93cSkardel int fd; 83abb0f93cSkardel caddr_t membase; 84abb0f93cSkardel 85abb0f93cSkardel /* 86abb0f93cSkardel * Open TT560 device 87abb0f93cSkardel */ 888585484eSchristos snprintf(device, sizeof(device), DEVICE, unit); 89abb0f93cSkardel fd = open(device, O_RDWR); 90abb0f93cSkardel if (fd == -1) { 91abb0f93cSkardel msyslog(LOG_ERR, "tt560_start: open of %s: %m", device); 92abb0f93cSkardel return (0); 93abb0f93cSkardel } 94abb0f93cSkardel 95abb0f93cSkardel /* 96abb0f93cSkardel * Map the device registers into user space. 97abb0f93cSkardel */ 98abb0f93cSkardel membase = mmap ((caddr_t) 0, TTIME_MEMORY_SIZE, 99abb0f93cSkardel PROT_READ | PROT_WRITE, 100abb0f93cSkardel MAP_SHARED, fd, (off_t)0); 101abb0f93cSkardel 102abb0f93cSkardel if (membase == (caddr_t) -1) { 103abb0f93cSkardel msyslog(LOG_ERR, "tt560_start: mapping of %s: %m", device); 104abb0f93cSkardel (void) close(fd); 105abb0f93cSkardel return (0); 106abb0f93cSkardel } 107abb0f93cSkardel 108abb0f93cSkardel /* 109abb0f93cSkardel * Allocate and initialize unit structure 110abb0f93cSkardel */ 111abb0f93cSkardel if (!(up = (struct tt560unit *) emalloc(sizeof(struct tt560unit)))) { 112abb0f93cSkardel (void) close(fd); 113abb0f93cSkardel return (0); 114abb0f93cSkardel } 115abb0f93cSkardel memset((char *)up, 0, sizeof(struct tt560unit)); 116abb0f93cSkardel up->tt_mem = (tt_mem_space_t *)membase; 117abb0f93cSkardel pp = peer->procptr; 118abb0f93cSkardel pp->io.clock_recv = noentry; 119abb0f93cSkardel pp->io.srcclock = (caddr_t)peer; 120abb0f93cSkardel pp->io.datalen = 0; 121abb0f93cSkardel pp->io.fd = fd; 122abb0f93cSkardel pp->unitptr = (caddr_t)up; 123abb0f93cSkardel 124abb0f93cSkardel /* 125abb0f93cSkardel * Initialize miscellaneous peer variables 126abb0f93cSkardel */ 127abb0f93cSkardel peer->precision = PRECISION; 128abb0f93cSkardel pp->clockdesc = DESCRIPTION; 129abb0f93cSkardel memcpy((char *)&pp->refid, REFID, 4); 130abb0f93cSkardel return (1); 131abb0f93cSkardel } 132abb0f93cSkardel 133abb0f93cSkardel 134abb0f93cSkardel /* 135abb0f93cSkardel * tt560_shutdown - shut down the clock 136abb0f93cSkardel */ 137abb0f93cSkardel static void 138abb0f93cSkardel tt560_shutdown( 139abb0f93cSkardel int unit, 140abb0f93cSkardel struct peer *peer 141abb0f93cSkardel ) 142abb0f93cSkardel { 143abb0f93cSkardel register struct tt560unit *up; 144abb0f93cSkardel struct refclockproc *pp; 145abb0f93cSkardel 146abb0f93cSkardel pp = peer->procptr; 147abb0f93cSkardel up = (struct tt560unit *)pp->unitptr; 148abb0f93cSkardel io_closeclock(&pp->io); 149abb0f93cSkardel free(up); 150abb0f93cSkardel } 151abb0f93cSkardel 152abb0f93cSkardel 153abb0f93cSkardel /* 154abb0f93cSkardel * tt560_poll - called by the transmit procedure 155abb0f93cSkardel */ 156abb0f93cSkardel static void 157abb0f93cSkardel tt560_poll( 158abb0f93cSkardel int unit, 159abb0f93cSkardel struct peer *peer 160abb0f93cSkardel ) 161abb0f93cSkardel { 162abb0f93cSkardel register struct tt560unit *up; 163abb0f93cSkardel struct refclockproc *pp; 164abb0f93cSkardel time_freeze_reg_t *tp; 165abb0f93cSkardel tt_mem_space_t *mp; 166abb0f93cSkardel 167abb0f93cSkardel int i; 168abb0f93cSkardel unsigned int *p_time_t, *tt_mem_t; 169abb0f93cSkardel 170abb0f93cSkardel /* 171abb0f93cSkardel * This is the main routine. It snatches the time from the TT560 172abb0f93cSkardel * board and tacks on a local timestamp. 173abb0f93cSkardel */ 174abb0f93cSkardel pp = peer->procptr; 175abb0f93cSkardel up = (struct tt560unit *)pp->unitptr; 176abb0f93cSkardel mp = up->tt_mem; 177abb0f93cSkardel tp = &up->tt560rawt; 178abb0f93cSkardel 179abb0f93cSkardel p_time_t = (unsigned int *)tp; 180abb0f93cSkardel tt_mem_t = (unsigned int *)&mp->time_freeze_reg; 181abb0f93cSkardel 182abb0f93cSkardel *tt_mem_t = 0; /* update the time freeze register */ 183abb0f93cSkardel /* and copy time stamp to memory */ 184abb0f93cSkardel for (i=0; i < TIME_FREEZE_REG_LEN; i++) { 185abb0f93cSkardel *p_time_t = byte_swap(*tt_mem_t); 186abb0f93cSkardel p_time_t++; 187abb0f93cSkardel tt_mem_t++; 188abb0f93cSkardel } 189abb0f93cSkardel 190abb0f93cSkardel get_systime(&pp->lastrec); 191abb0f93cSkardel pp->polls++; 192abb0f93cSkardel 193abb0f93cSkardel /* 194abb0f93cSkardel * We get down to business, check the timecode format and decode 195abb0f93cSkardel * its contents. If the timecode has invalid length or is not in 196abb0f93cSkardel * proper format, we declare bad format and exit. Note: we 197abb0f93cSkardel * can't use the sec/usec conversion produced by the driver, 198abb0f93cSkardel * since the year may be suspect. All format error checking is 1998585484eSchristos * done by the snprintf() and sscanf() routines. 200abb0f93cSkardel */ 2018585484eSchristos snprintf(pp->a_lastcode, sizeof(pp->a_lastcode), 202abb0f93cSkardel "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x", 203abb0f93cSkardel tp->hun_day, tp->tens_day, tp->unit_day, 204abb0f93cSkardel tp->tens_hour, tp->unit_hour, 205abb0f93cSkardel tp->tens_min, tp->unit_min, 206abb0f93cSkardel tp->tens_sec, tp->unit_sec, 207abb0f93cSkardel tp->hun_ms, tp->tens_ms, tp->unit_ms, 208abb0f93cSkardel tp->hun_us, tp->tens_us, tp->unit_us, 209abb0f93cSkardel tp->status); 210abb0f93cSkardel pp->lencode = strlen(pp->a_lastcode); 211abb0f93cSkardel #ifdef DEBUG 212abb0f93cSkardel if (debug) 213abb0f93cSkardel printf("tt560: time %s timecode %d %s\n", 214abb0f93cSkardel ulfptoa(&pp->lastrec, 6), pp->lencode, 215abb0f93cSkardel pp->a_lastcode); 216abb0f93cSkardel #endif 217abb0f93cSkardel if (sscanf(pp->a_lastcode, "%3d %2d:%2d:%2d.%6ld", 218abb0f93cSkardel &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->usec) 219abb0f93cSkardel != 5) { 220abb0f93cSkardel refclock_report(peer, CEVNT_BADTIME); 221abb0f93cSkardel return; 222abb0f93cSkardel } 223abb0f93cSkardel if ((tp->status & 0x6) != 0x6) 224abb0f93cSkardel pp->leap = LEAP_NOTINSYNC; 225abb0f93cSkardel else 226abb0f93cSkardel pp->leap = LEAP_NOWARNING; 227abb0f93cSkardel if (!refclock_process(pp)) { 228abb0f93cSkardel refclock_report(peer, CEVNT_BADTIME); 229abb0f93cSkardel return; 230abb0f93cSkardel } 231abb0f93cSkardel if (pp->coderecv == pp->codeproc) { 232abb0f93cSkardel refclock_report(peer, CEVNT_TIMEOUT); 233abb0f93cSkardel return; 234abb0f93cSkardel } 235abb0f93cSkardel record_clock_stats(&peer->srcadr, pp->a_lastcode); 236abb0f93cSkardel refclock_receive(peer); 237abb0f93cSkardel } 238abb0f93cSkardel 239abb0f93cSkardel /****************************************************************** 240abb0f93cSkardel * 241abb0f93cSkardel * byte_swap 242abb0f93cSkardel * 243abb0f93cSkardel * Inputs: 32 bit integer 244abb0f93cSkardel * 245abb0f93cSkardel * Output: byte swapped 32 bit integer. 246abb0f93cSkardel * 247abb0f93cSkardel * This routine is used to compensate for the byte alignment 248abb0f93cSkardel * differences between big-endian and little-endian integers. 249abb0f93cSkardel * 250abb0f93cSkardel ******************************************************************/ 251abb0f93cSkardel static unsigned int 252abb0f93cSkardel byte_swap(unsigned int input_num) 253abb0f93cSkardel { 254abb0f93cSkardel byteswap_t byte_swap; 255abb0f93cSkardel unsigned char temp; 256abb0f93cSkardel 257abb0f93cSkardel byte_swap.long_word = input_num; 258abb0f93cSkardel 259abb0f93cSkardel temp = byte_swap.byte[3]; 260abb0f93cSkardel byte_swap.byte[3] = byte_swap.byte[0]; 261abb0f93cSkardel byte_swap.byte[0] = temp; 262abb0f93cSkardel 263abb0f93cSkardel temp = byte_swap.byte[2]; 264abb0f93cSkardel byte_swap.byte[2] = byte_swap.byte[1]; 265abb0f93cSkardel byte_swap.byte[1] = temp; 266abb0f93cSkardel 267abb0f93cSkardel return (byte_swap.long_word); 268abb0f93cSkardel } 269abb0f93cSkardel 270abb0f93cSkardel #else 271*eabc0478Schristos NONEMPTY_TRANSLATION_UNIT 272abb0f93cSkardel #endif /* REFCLOCK */ 273