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