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