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
hopfserial_start(int unit,struct peer * peer)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
hopfserial_shutdown(int unit,struct peer * peer)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
hopfserial_receive(struct recvbuf * rbufp)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
hopfserial_poll(int unit,struct peer * peer)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