xref: /freebsd/contrib/ntp/ntpd/refclock_neoclock4x.c (revision ba371819a70ed076ebef04af93922a043272bfbe)
1ba371819SOllivier Robert /*
2ba371819SOllivier Robert  *
3ba371819SOllivier Robert  * refclock_neoclock4x.c
4ba371819SOllivier Robert  * - NeoClock4X driver for DCF77 or FIA Timecode
5ba371819SOllivier Robert  *
6ba371819SOllivier Robert  * Date: 2002-04-27 1.0
7ba371819SOllivier Robert  *
8ba371819SOllivier Robert  * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir
9ba371819SOllivier Robert  * for details about the NeoClock4X device
10ba371819SOllivier Robert  *
11ba371819SOllivier Robert  * Copyright (C) 2002 by Linum Software GmbH <support@linum.com>
12ba371819SOllivier Robert  *
13ba371819SOllivier Robert  * This program is distributed in the hope that it will be useful,
14ba371819SOllivier Robert  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15ba371819SOllivier Robert  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16ba371819SOllivier Robert  *
17ba371819SOllivier Robert  *
18ba371819SOllivier Robert  */
19ba371819SOllivier Robert 
20ba371819SOllivier Robert #ifdef HAVE_CONFIG_H
21ba371819SOllivier Robert # include "config.h"
22ba371819SOllivier Robert #endif
23ba371819SOllivier Robert 
24ba371819SOllivier Robert #if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))
25ba371819SOllivier Robert 
26ba371819SOllivier Robert #include <unistd.h>
27ba371819SOllivier Robert #include <sys/time.h>
28ba371819SOllivier Robert #include <sys/types.h>
29ba371819SOllivier Robert #include <termios.h>
30ba371819SOllivier Robert #include <sys/ioctl.h>
31ba371819SOllivier Robert #include <ctype.h>
32ba371819SOllivier Robert 
33ba371819SOllivier Robert #include "ntpd.h"
34ba371819SOllivier Robert #include "ntp_io.h"
35ba371819SOllivier Robert #include "ntp_control.h"
36ba371819SOllivier Robert #include "ntp_refclock.h"
37ba371819SOllivier Robert #include "ntp_unixtime.h"
38ba371819SOllivier Robert #include "ntp_stdlib.h"
39ba371819SOllivier Robert 
40ba371819SOllivier Robert #if defined HAVE_SYS_MODEM_H
41ba371819SOllivier Robert # include <sys/modem.h>
42ba371819SOllivier Robert # define TIOCMSET MCSETA
43ba371819SOllivier Robert # define TIOCMGET MCGETA
44ba371819SOllivier Robert # define TIOCM_RTS MRTS
45ba371819SOllivier Robert #endif
46ba371819SOllivier Robert 
47ba371819SOllivier Robert #ifdef HAVE_TERMIOS_H
48ba371819SOllivier Robert # ifdef TERMIOS_NEEDS__SVID3
49ba371819SOllivier Robert #  define _SVID3
50ba371819SOllivier Robert # endif
51ba371819SOllivier Robert # include <termios.h>
52ba371819SOllivier Robert # ifdef TERMIOS_NEEDS__SVID3
53ba371819SOllivier Robert #  undef _SVID3
54ba371819SOllivier Robert # endif
55ba371819SOllivier Robert #endif
56ba371819SOllivier Robert 
57ba371819SOllivier Robert #ifdef HAVE_SYS_IOCTL_H
58ba371819SOllivier Robert # include <sys/ioctl.h>
59ba371819SOllivier Robert #endif
60ba371819SOllivier Robert 
61ba371819SOllivier Robert #define NEOCLOCK4X_TIMECODELEN 37
62ba371819SOllivier Robert 
63ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_SERIAL            3
64ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_RADIOSIGNAL       9
65ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_DAY              12
66ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_MONTH            14
67ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_YEAR             16
68ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_HOUR             18
69ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_MINUTE           20
70ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_SECOND           22
71ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_HSEC             24
72ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_DOW              26
73ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_TIMESOURCE       28
74ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_DSTSTATUS        29
75ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_QUARZSTATUS      30
76ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_ANTENNA1         31
77ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_ANTENNA2         33
78ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_CRC              35
79ba371819SOllivier Robert 
80ba371819SOllivier Robert struct neoclock4x_unit {
81ba371819SOllivier Robert   l_fp	laststamp;	/* last receive timestamp */
82ba371819SOllivier Robert   short	unit;		/* NTP refclock unit number */
83ba371819SOllivier Robert   u_long	polled;		/* flag to detect noreplies */
84ba371819SOllivier Robert   char	leap_status;	/* leap second flag */
85ba371819SOllivier Robert   int	recvnow;
86ba371819SOllivier Robert 
87ba371819SOllivier Robert   char  firmware[80];
88ba371819SOllivier Robert   char  serial[7];
89ba371819SOllivier Robert   char  radiosignal[4];
90ba371819SOllivier Robert   char  timesource;
91ba371819SOllivier Robert   char  dststatus;
92ba371819SOllivier Robert   char  quarzstatus;
93ba371819SOllivier Robert   int   antenna1;
94ba371819SOllivier Robert   int   antenna2;
95ba371819SOllivier Robert   int   utc_year;
96ba371819SOllivier Robert   int   utc_month;
97ba371819SOllivier Robert   int   utc_day;
98ba371819SOllivier Robert   int   utc_hour;
99ba371819SOllivier Robert   int   utc_minute;
100ba371819SOllivier Robert   int   utc_second;
101ba371819SOllivier Robert   int   utc_msec;
102ba371819SOllivier Robert };
103ba371819SOllivier Robert 
104ba371819SOllivier Robert static	int	neoclock4x_start        P((int, struct peer *));
105ba371819SOllivier Robert static	void	neoclock4x_shutdown	P((int, struct peer *));
106ba371819SOllivier Robert static	void	neoclock4x_receive	P((struct recvbuf *));
107ba371819SOllivier Robert static	void	neoclock4x_poll		P((int, struct peer *));
108ba371819SOllivier Robert static	void	neoclock4x_control      P((int, struct refclockstat *, struct refclockstat *, struct peer *));
109ba371819SOllivier Robert 
110ba371819SOllivier Robert static int      neol_atoi_len           P((const char str[], int *, int));
111ba371819SOllivier Robert static int      neol_hexatoi_len        P((const char str[], int *, int));
112ba371819SOllivier Robert static void     neol_jdn_to_ymd         P((unsigned long, int *, int *, int *));
113ba371819SOllivier Robert static void     neol_localtime          P((unsigned long, int* , int*, int*, int*, int*, int*));
114ba371819SOllivier Robert static unsigned long neol_mktime        P((int, int, int, int, int, int));
115ba371819SOllivier Robert static void     neol_mdelay             P((int));
116ba371819SOllivier Robert static int      neol_query_firmware     P((int, int, char *, int));
117ba371819SOllivier Robert 
118ba371819SOllivier Robert struct refclock refclock_neoclock4x = {
119ba371819SOllivier Robert   neoclock4x_start,	/* start up driver */
120ba371819SOllivier Robert   neoclock4x_shutdown,	/* shut down driver */
121ba371819SOllivier Robert   neoclock4x_poll,	/* transmit poll message */
122ba371819SOllivier Robert   neoclock4x_control,
123ba371819SOllivier Robert   noentry,		/* initialize driver (not used) */
124ba371819SOllivier Robert   noentry,		/* not used */
125ba371819SOllivier Robert   NOFLAGS			/* not used */
126ba371819SOllivier Robert };
127ba371819SOllivier Robert 
128ba371819SOllivier Robert static int
129ba371819SOllivier Robert neoclock4x_start(int unit,
130ba371819SOllivier Robert 		 struct peer *peer)
131ba371819SOllivier Robert {
132ba371819SOllivier Robert   struct neoclock4x_unit *up;
133ba371819SOllivier Robert   struct refclockproc *pp;
134ba371819SOllivier Robert   int fd;
135ba371819SOllivier Robert   char dev[20];
136ba371819SOllivier Robert   int sl232;
137ba371819SOllivier Robert   struct termios termsettings;
138ba371819SOllivier Robert   int tries;
139ba371819SOllivier Robert 
140ba371819SOllivier Robert   (void) sprintf(dev, "/dev/neoclock4x-%d", unit);
141ba371819SOllivier Robert 
142ba371819SOllivier Robert   /* LDISC_STD, LDISC_RAW
143ba371819SOllivier Robert    * Open serial port. Use CLK line discipline, if available.
144ba371819SOllivier Robert    */
145ba371819SOllivier Robert   fd = refclock_open(dev, B2400, LDISC_CLK);
146ba371819SOllivier Robert   if(fd <= 0)
147ba371819SOllivier Robert     {
148ba371819SOllivier Robert       return (0);
149ba371819SOllivier Robert     }
150ba371819SOllivier Robert 
151ba371819SOllivier Robert #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
152ba371819SOllivier Robert   /* turn on RTS, and DTR for power supply */
153ba371819SOllivier Robert   /* NeoClock4x is powered from serial line */
154ba371819SOllivier Robert   if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1)
155ba371819SOllivier Robert     {
156ba371819SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
157ba371819SOllivier Robert     }
158ba371819SOllivier Robert #ifdef TIOCM_RTS
159ba371819SOllivier Robert   sl232 = sl232 | TIOCM_DTR | TIOCM_RTS;	/* turn on RTS, and DTR for power supply */
160ba371819SOllivier Robert #else
161ba371819SOllivier Robert   sl232 = sl232 | CIOCM_DTR | CIOCM_RTS;	/* turn on RTS, and DTR for power supply */
162ba371819SOllivier Robert #endif
163ba371819SOllivier Robert   if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1)
164ba371819SOllivier Robert     {
165ba371819SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
166ba371819SOllivier Robert     }
167ba371819SOllivier Robert 
168ba371819SOllivier Robert   if(ioctl(fd, TCGETS, (caddr_t)&termsettings) == -1)
169ba371819SOllivier Robert     {
170ba371819SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): can't query serial port settings: %m", unit);
171ba371819SOllivier Robert     }
172ba371819SOllivier Robert 
173ba371819SOllivier Robert   /* 2400 Baud mit 8N2 */
174ba371819SOllivier Robert   termsettings.c_cflag &= ~PARENB;
175ba371819SOllivier Robert   termsettings.c_cflag |= CSTOPB;
176ba371819SOllivier Robert   termsettings.c_cflag &= ~CSIZE;
177ba371819SOllivier Robert   termsettings.c_cflag |= CS8;
178ba371819SOllivier Robert 
179ba371819SOllivier Robert   if(ioctl(fd, TCSETS, &termsettings) == -1)
180ba371819SOllivier Robert     {
181ba371819SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): can't set serial port to 2400 8N2: %m", unit);
182ba371819SOllivier Robert     }
183ba371819SOllivier Robert #else
184ba371819SOllivier Robert   msyslog(LOG_EMERG, "NeoClock4X(%d): OS interface is incapable of setting DTR/RTS to power NeoClock4X",
185ba371819SOllivier Robert 	  unit);
186ba371819SOllivier Robert #endif
187ba371819SOllivier Robert 
188ba371819SOllivier Robert   up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
189ba371819SOllivier Robert   if(!(up))
190ba371819SOllivier Robert     {
191ba371819SOllivier Robert       msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
192ba371819SOllivier Robert       (void) close(fd);
193ba371819SOllivier Robert       return (0);
194ba371819SOllivier Robert     }
195ba371819SOllivier Robert 
196ba371819SOllivier Robert   memset((char *)up, 0, sizeof(struct neoclock4x_unit));
197ba371819SOllivier Robert   pp = peer->procptr;
198ba371819SOllivier Robert   pp->clockdesc = "NeoClock4X";
199ba371819SOllivier Robert   pp->unitptr = (caddr_t)up;
200ba371819SOllivier Robert   pp->io.clock_recv = neoclock4x_receive;
201ba371819SOllivier Robert   pp->io.srcclock = (caddr_t)peer;
202ba371819SOllivier Robert   pp->io.datalen = 0;
203ba371819SOllivier Robert   pp->io.fd = fd;
204ba371819SOllivier Robert   /* no time is given by user! use 169.583333 ms to compensate the serial line delay
205ba371819SOllivier Robert    * formula is:
206ba371819SOllivier Robert    * 2400 Baud / 11 bit = 218.18 charaters per second
207ba371819SOllivier Robert    *  (NeoClock4X timecode len)
208ba371819SOllivier Robert    */
209ba371819SOllivier Robert   pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0;
210ba371819SOllivier Robert 
211ba371819SOllivier Robert   if (!io_addclock(&pp->io))
212ba371819SOllivier Robert     {
213ba371819SOllivier Robert       msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m",unit);
214ba371819SOllivier Robert       (void) close(fd);
215ba371819SOllivier Robert       free(up);
216ba371819SOllivier Robert       return (0);
217ba371819SOllivier Robert     }
218ba371819SOllivier Robert 
219ba371819SOllivier Robert   /*
220ba371819SOllivier Robert    * Initialize miscellaneous variables
221ba371819SOllivier Robert    */
222ba371819SOllivier Robert   peer->precision = -10;
223ba371819SOllivier Robert   peer->burst = NSTAGE;
224ba371819SOllivier Robert   memcpy((char *)&pp->refid, "neol", 4);
225ba371819SOllivier Robert 
226ba371819SOllivier Robert   up->leap_status = 0;
227ba371819SOllivier Robert   up->unit = unit;
228ba371819SOllivier Robert   strcpy(up->firmware, "?");
229ba371819SOllivier Robert   strcpy(up->serial, "?");
230ba371819SOllivier Robert   strcpy(up->radiosignal, "?");
231ba371819SOllivier Robert   up->timesource  = '?';
232ba371819SOllivier Robert   up->dststatus   = '?';
233ba371819SOllivier Robert   up->quarzstatus = '?';
234ba371819SOllivier Robert   up->antenna1    = -1;
235ba371819SOllivier Robert   up->antenna2    = -1;
236ba371819SOllivier Robert   up->utc_year    = 0;
237ba371819SOllivier Robert   up->utc_month   = 0;
238ba371819SOllivier Robert   up->utc_day     = 0;
239ba371819SOllivier Robert   up->utc_hour    = 0;
240ba371819SOllivier Robert   up->utc_minute  = 0;
241ba371819SOllivier Robert   up->utc_second  = 0;
242ba371819SOllivier Robert   up->utc_msec    = 0;
243ba371819SOllivier Robert 
244ba371819SOllivier Robert   for(tries=0; tries < 5; tries++)
245ba371819SOllivier Robert     {
246ba371819SOllivier Robert       /*
247ba371819SOllivier Robert        * Wait 3 second for receiver to power up
248ba371819SOllivier Robert        */
249ba371819SOllivier Robert       NLOG(NLOG_CLOCKINFO)
250ba371819SOllivier Robert 	msyslog(LOG_INFO, "NeoClock4X(%d): try query NeoClock4X firmware version (%d/5)", unit, tries);
251ba371819SOllivier Robert       sleep(3);
252ba371819SOllivier Robert       if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
253ba371819SOllivier Robert 	{
254ba371819SOllivier Robert 	  break;
255ba371819SOllivier Robert 	}
256ba371819SOllivier Robert     }
257ba371819SOllivier Robert 
258ba371819SOllivier Robert   NLOG(NLOG_CLOCKINFO)
259ba371819SOllivier Robert     msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
260ba371819SOllivier Robert 
261ba371819SOllivier Robert   return (1);
262ba371819SOllivier Robert }
263ba371819SOllivier Robert 
264ba371819SOllivier Robert static void
265ba371819SOllivier Robert neoclock4x_shutdown(int unit,
266ba371819SOllivier Robert 		   struct peer *peer)
267ba371819SOllivier Robert {
268ba371819SOllivier Robert   struct neoclock4x_unit *up;
269ba371819SOllivier Robert   struct refclockproc *pp;
270ba371819SOllivier Robert   int sl232;
271ba371819SOllivier Robert 
272ba371819SOllivier Robert   pp = peer->procptr;
273ba371819SOllivier Robert   up = (struct neoclock4x_unit *)pp->unitptr;
274ba371819SOllivier Robert 
275ba371819SOllivier Robert #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
276ba371819SOllivier Robert   /* turn on RTS, and DTR for power supply */
277ba371819SOllivier Robert   /* NeoClock4x is powered from serial line */
278ba371819SOllivier Robert   if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
279ba371819SOllivier Robert     {
280ba371819SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
281ba371819SOllivier Robert     }
282ba371819SOllivier Robert #ifdef TIOCM_RTS
283ba371819SOllivier Robert   sl232 &= ~(TIOCM_DTR | TIOCM_RTS);	/* turn on RTS, and DTR for power supply */
284ba371819SOllivier Robert #else
285ba371819SOllivier Robert   sl232 &= ~(CIOCM_DTR | CIOCM_RTS);	/* turn on RTS, and DTR for power supply */
286ba371819SOllivier Robert #endif
287ba371819SOllivier Robert   if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
288ba371819SOllivier Robert     {
289ba371819SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
290ba371819SOllivier Robert     }
291ba371819SOllivier Robert #endif
292ba371819SOllivier Robert   msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
293ba371819SOllivier Robert 
294ba371819SOllivier Robert   io_closeclock(&pp->io);
295ba371819SOllivier Robert   free(up);
296ba371819SOllivier Robert   NLOG(NLOG_CLOCKINFO)
297ba371819SOllivier Robert     msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
298ba371819SOllivier Robert }
299ba371819SOllivier Robert 
300ba371819SOllivier Robert static void
301ba371819SOllivier Robert neoclock4x_receive(struct recvbuf *rbufp)
302ba371819SOllivier Robert {
303ba371819SOllivier Robert   struct neoclock4x_unit *up;
304ba371819SOllivier Robert   struct refclockproc *pp;
305ba371819SOllivier Robert   struct peer *peer;
306ba371819SOllivier Robert   unsigned long calc_utc;
307ba371819SOllivier Robert   int day;
308ba371819SOllivier Robert   int month;	/* ddd conversion */
309ba371819SOllivier Robert   int c;
310ba371819SOllivier Robert   unsigned char calc_chksum;
311ba371819SOllivier Robert   int recv_chksum;
312ba371819SOllivier Robert 
313ba371819SOllivier Robert   peer = (struct peer *)rbufp->recv_srcclock;
314ba371819SOllivier Robert   pp = peer->procptr;
315ba371819SOllivier Robert   up = (struct neoclock4x_unit *)pp->unitptr;
316ba371819SOllivier Robert 
317ba371819SOllivier Robert   /* wait till poll interval is reached */
318ba371819SOllivier Robert   if(0 == up->recvnow)
319ba371819SOllivier Robert     return;
320ba371819SOllivier Robert 
321ba371819SOllivier Robert   /* reset poll interval flag */
322ba371819SOllivier Robert   up->recvnow = 0;
323ba371819SOllivier Robert 
324ba371819SOllivier Robert   /* read last received timecode */
325ba371819SOllivier Robert   pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
326ba371819SOllivier Robert 
327ba371819SOllivier Robert   if(NEOCLOCK4X_TIMECODELEN != pp->lencode)
328ba371819SOllivier Robert     {
329ba371819SOllivier Robert       NLOG(NLOG_CLOCKEVENT)
330ba371819SOllivier Robert 	msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s",
331ba371819SOllivier Robert 		up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode);
332ba371819SOllivier Robert       refclock_report(peer, CEVNT_BADREPLY);
333ba371819SOllivier Robert       return;
334ba371819SOllivier Robert     }
335ba371819SOllivier Robert 
336ba371819SOllivier Robert   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
337ba371819SOllivier Robert 
338ba371819SOllivier Robert   /* calculate checksum */
339ba371819SOllivier Robert   calc_chksum = 0;
340ba371819SOllivier Robert   for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++)
341ba371819SOllivier Robert     {
342ba371819SOllivier Robert       calc_chksum += pp->a_lastcode[c];
343ba371819SOllivier Robert     }
344ba371819SOllivier Robert   if(recv_chksum != calc_chksum)
345ba371819SOllivier Robert     {
346ba371819SOllivier Robert       NLOG(NLOG_CLOCKEVENT)
347ba371819SOllivier Robert 	msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s",
348ba371819SOllivier Robert 		up->unit, pp->a_lastcode);
349ba371819SOllivier Robert       refclock_report(peer, CEVNT_BADREPLY);
350ba371819SOllivier Robert       return;
351ba371819SOllivier Robert     }
352ba371819SOllivier Robert 
353ba371819SOllivier Robert   /* Allow synchronization even is quartz clock is
354ba371819SOllivier Robert    * never initialized.
355ba371819SOllivier Robert    * WARNING: This is dangerous!
356ba371819SOllivier Robert    */
357ba371819SOllivier Robert   up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS];
358ba371819SOllivier Robert   if(0==(pp->sloppyclockflag & CLK_FLAG2))
359ba371819SOllivier Robert     {
360ba371819SOllivier Robert       if('I' != up->quarzstatus)
361ba371819SOllivier Robert 	{
362ba371819SOllivier Robert 	  NLOG(NLOG_CLOCKEVENT)
363ba371819SOllivier Robert 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s",
364ba371819SOllivier Robert 		    up->unit, pp->a_lastcode);
365ba371819SOllivier Robert 	  pp->leap = LEAP_NOTINSYNC;
366ba371819SOllivier Robert 	  refclock_report(peer, CEVNT_BADDATE);
367ba371819SOllivier Robert 	  return;
368ba371819SOllivier Robert 	}
369ba371819SOllivier Robert     }
370ba371819SOllivier Robert   if('I' != up->quarzstatus)
371ba371819SOllivier Robert     {
372ba371819SOllivier Robert       NLOG(NLOG_CLOCKEVENT)
373ba371819SOllivier Robert 	msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
374ba371819SOllivier Robert 		up->unit, pp->a_lastcode);
375ba371819SOllivier Robert     }
376ba371819SOllivier Robert 
377ba371819SOllivier Robert   /*
378ba371819SOllivier Robert    * If NeoClock4X is not synchronized to a radio clock
379ba371819SOllivier Robert    * check if we're allowed to synchronize with the quartz
380ba371819SOllivier Robert    * clock.
381ba371819SOllivier Robert    */
382ba371819SOllivier Robert   up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE];
383ba371819SOllivier Robert   if(0==(pp->sloppyclockflag & CLK_FLAG2))
384ba371819SOllivier Robert     {
385ba371819SOllivier Robert       if('A' != up->timesource)
386ba371819SOllivier Robert 	{
387ba371819SOllivier Robert 	  /* not allowed to sync with quartz clock */
388ba371819SOllivier Robert 	  if(0==(pp->sloppyclockflag & CLK_FLAG1))
389ba371819SOllivier Robert 	    {
390ba371819SOllivier Robert 	      refclock_report(peer, CEVNT_BADTIME);
391ba371819SOllivier Robert 	      pp->leap = LEAP_NOTINSYNC;
392ba371819SOllivier Robert 	      return;
393ba371819SOllivier Robert 	    }
394ba371819SOllivier Robert 	}
395ba371819SOllivier Robert     }
396ba371819SOllivier Robert 
397ba371819SOllivier Robert   /* this should only used when first install is done */
398ba371819SOllivier Robert   if(pp->sloppyclockflag & CLK_FLAG4)
399ba371819SOllivier Robert     {
400ba371819SOllivier Robert       msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s",
401ba371819SOllivier Robert 	      up->unit, pp->a_lastcode);
402ba371819SOllivier Robert     }
403ba371819SOllivier Robert 
404ba371819SOllivier Robert   /* 123456789012345678901234567890123456789012345 */
405ba371819SOllivier Robert   /* S/N123456DCF1004021010001202ASX1213CR\r\n */
406ba371819SOllivier Robert 
407ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2);
408ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2);
409ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2);
410ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2);
411ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2);
412ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2);
413ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &pp->msec, 2);
414ba371819SOllivier Robert   pp->msec *= 10; /* convert 1/100s from neoclock to real miliseconds */
415ba371819SOllivier Robert 
416ba371819SOllivier Robert   memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3);
417ba371819SOllivier Robert   up->radiosignal[3] = 0;
418ba371819SOllivier Robert   memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6);
419ba371819SOllivier Robert   up->serial[6] = 0;
420ba371819SOllivier Robert   up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS];
421ba371819SOllivier Robert   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2);
422ba371819SOllivier Robert   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2);
423ba371819SOllivier Robert 
424ba371819SOllivier Robert   /*
425ba371819SOllivier Robert     Validate received values at least enough to prevent internal
426ba371819SOllivier Robert     array-bounds problems, etc.
427ba371819SOllivier Robert   */
428ba371819SOllivier Robert   if((pp->hour < 0) || (pp->hour > 23) ||
429ba371819SOllivier Robert      (pp->minute < 0) || (pp->minute > 59) ||
430ba371819SOllivier Robert      (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
431ba371819SOllivier Robert      (day < 1) || (day > 31) ||
432ba371819SOllivier Robert      (month < 1) || (month > 12) ||
433ba371819SOllivier Robert      (pp->year < 0) || (pp->year > 99)) {
434ba371819SOllivier Robert     /* Data out of range. */
435ba371819SOllivier Robert     NLOG(NLOG_CLOCKEVENT)
436ba371819SOllivier Robert       msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s",
437ba371819SOllivier Robert 	      up->unit, pp->a_lastcode);
438ba371819SOllivier Robert     refclock_report(peer, CEVNT_BADDATE);
439ba371819SOllivier Robert     return;
440ba371819SOllivier Robert   }
441ba371819SOllivier Robert 
442ba371819SOllivier Robert   /* Year-2000 check! */
443ba371819SOllivier Robert   /* wrap 2-digit date into 4-digit */
444ba371819SOllivier Robert 
445ba371819SOllivier Robert   if(pp->year < YEAR_PIVOT) /* < 98 */
446ba371819SOllivier Robert     {
447ba371819SOllivier Robert       pp->year += 100;
448ba371819SOllivier Robert     }
449ba371819SOllivier Robert   pp->year += 1900;
450ba371819SOllivier Robert 
451ba371819SOllivier Robert   calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
452ba371819SOllivier Robert   calc_utc -= 3600;
453ba371819SOllivier Robert   if('S' == up->dststatus)
454ba371819SOllivier Robert     calc_utc -= 3600;
455ba371819SOllivier Robert   neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
456ba371819SOllivier Robert 
457ba371819SOllivier Robert   /*
458ba371819SOllivier Robert     some preparations
459ba371819SOllivier Robert   */
460ba371819SOllivier Robert   pp->day = ymd2yd(pp->year,month,day);
461ba371819SOllivier Robert   pp->leap = 0;
462ba371819SOllivier Robert 
463ba371819SOllivier Robert 
464ba371819SOllivier Robert   if(pp->sloppyclockflag & CLK_FLAG4)
465ba371819SOllivier Robert     {
466ba371819SOllivier Robert       msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03d",
467ba371819SOllivier Robert 	      up->unit,
468ba371819SOllivier Robert 	      pp->year, month, day,
469ba371819SOllivier Robert 	      pp->hour, pp->minute, pp->second, pp->msec);
470ba371819SOllivier Robert     }
471ba371819SOllivier Robert 
472ba371819SOllivier Robert   up->utc_year   = pp->year;
473ba371819SOllivier Robert   up->utc_month  = month;
474ba371819SOllivier Robert   up->utc_day    = day;
475ba371819SOllivier Robert   up->utc_hour   = pp->hour;
476ba371819SOllivier Robert   up->utc_minute = pp->minute;
477ba371819SOllivier Robert   up->utc_second = pp->second;
478ba371819SOllivier Robert   up->utc_msec   = pp->msec;
479ba371819SOllivier Robert 
480ba371819SOllivier Robert   if(!refclock_process(pp))
481ba371819SOllivier Robert     {
482ba371819SOllivier Robert       NLOG(NLOG_CLOCKEVENT)
483ba371819SOllivier Robert 	msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit);
484ba371819SOllivier Robert       refclock_report(peer, CEVNT_FAULT);
485ba371819SOllivier Robert       return;
486ba371819SOllivier Robert     }
487ba371819SOllivier Robert   refclock_receive(peer);
488ba371819SOllivier Robert 
489ba371819SOllivier Robert   record_clock_stats(&peer->srcadr, pp->a_lastcode);
490ba371819SOllivier Robert }
491ba371819SOllivier Robert 
492ba371819SOllivier Robert static void
493ba371819SOllivier Robert neoclock4x_poll(int unit,
494ba371819SOllivier Robert 		struct peer *peer)
495ba371819SOllivier Robert {
496ba371819SOllivier Robert   struct neoclock4x_unit *up;
497ba371819SOllivier Robert   struct refclockproc *pp;
498ba371819SOllivier Robert 
499ba371819SOllivier Robert   pp = peer->procptr;
500ba371819SOllivier Robert   up = (struct neoclock4x_unit *)pp->unitptr;
501ba371819SOllivier Robert 
502ba371819SOllivier Robert   pp->polls++;
503ba371819SOllivier Robert   up->recvnow = 1;
504ba371819SOllivier Robert }
505ba371819SOllivier Robert 
506ba371819SOllivier Robert static void
507ba371819SOllivier Robert neoclock4x_control(int unit,
508ba371819SOllivier Robert 		   struct refclockstat *in,
509ba371819SOllivier Robert 		   struct refclockstat *out,
510ba371819SOllivier Robert 		   struct peer *peer)
511ba371819SOllivier Robert {
512ba371819SOllivier Robert   struct neoclock4x_unit *up;
513ba371819SOllivier Robert   struct refclockproc *pp;
514ba371819SOllivier Robert 
515ba371819SOllivier Robert   if(NULL == peer)
516ba371819SOllivier Robert     {
517ba371819SOllivier Robert       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
518ba371819SOllivier Robert       return;
519ba371819SOllivier Robert     }
520ba371819SOllivier Robert 
521ba371819SOllivier Robert   pp = peer->procptr;
522ba371819SOllivier Robert   if(NULL == pp)
523ba371819SOllivier Robert     {
524ba371819SOllivier Robert       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
525ba371819SOllivier Robert       return;
526ba371819SOllivier Robert     }
527ba371819SOllivier Robert 
528ba371819SOllivier Robert   up = (struct neoclock4x_unit *)pp->unitptr;
529ba371819SOllivier Robert   if(NULL == up)
530ba371819SOllivier Robert     {
531ba371819SOllivier Robert       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
532ba371819SOllivier Robert       return;
533ba371819SOllivier Robert     }
534ba371819SOllivier Robert 
535ba371819SOllivier Robert   if(NULL != in)
536ba371819SOllivier Robert     {
537ba371819SOllivier Robert       /* check to see if a user supplied time offset is given */
538ba371819SOllivier Robert       if(in->haveflags & CLK_HAVETIME1)
539ba371819SOllivier Robert 	{
540ba371819SOllivier Robert 	  pp->fudgetime1 = in->fudgetime1;
541ba371819SOllivier Robert 	  NLOG(NLOG_CLOCKINFO)
542ba371819SOllivier Robert 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
543ba371819SOllivier Robert 		    unit, pp->fudgetime1);
544ba371819SOllivier Robert 	}
545ba371819SOllivier Robert 
546ba371819SOllivier Robert       /* notify */
547ba371819SOllivier Robert       if(pp->sloppyclockflag & CLK_FLAG1)
548ba371819SOllivier Robert 	{
549ba371819SOllivier Robert 	  NLOG(NLOG_CLOCKINFO)
550ba371819SOllivier Robert 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
551ba371819SOllivier Robert 	}
552ba371819SOllivier Robert       else
553ba371819SOllivier Robert 	{
554ba371819SOllivier Robert 	  NLOG(NLOG_CLOCKINFO)
555ba371819SOllivier Robert 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
556ba371819SOllivier Robert 	}
557ba371819SOllivier Robert     }
558ba371819SOllivier Robert 
559ba371819SOllivier Robert   if(NULL != out)
560ba371819SOllivier Robert     {
561ba371819SOllivier Robert       static char outstatus[800];	/* status output buffer */
562ba371819SOllivier Robert       char *tt;
563ba371819SOllivier Robert       char tmpbuf[80];
564ba371819SOllivier Robert 
565ba371819SOllivier Robert       outstatus[0] = '\0';
566ba371819SOllivier Robert       out->kv_list = (struct ctl_var *)0;
567ba371819SOllivier Robert       out->type    = REFCLK_NEOCLOCK4X;
568ba371819SOllivier Robert 
569ba371819SOllivier Robert       sprintf(tmpbuf, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
570ba371819SOllivier Robert 	      up->utc_year, up->utc_month, up->utc_day,
571ba371819SOllivier Robert 	      up->utc_hour, up->utc_minute, up->utc_second,
572ba371819SOllivier Robert 	      up->utc_msec);
573ba371819SOllivier Robert 
574ba371819SOllivier Robert       tt = add_var(&out->kv_list, 512, RO|DEF);
575ba371819SOllivier Robert       tt += sprintf(tt, "calc_utc=\"%s\"", tmpbuf);
576ba371819SOllivier Robert       tt = add_var(&out->kv_list, 512, RO|DEF);
577ba371819SOllivier Robert       tt += sprintf(tt, "radiosignal=\"%s\"", up->radiosignal);
578ba371819SOllivier Robert       tt = add_var(&out->kv_list, 512, RO|DEF);
579ba371819SOllivier Robert       tt += sprintf(tt, "antenna1=\"%d\"", up->antenna1);
580ba371819SOllivier Robert       tt = add_var(&out->kv_list, 512, RO|DEF);
581ba371819SOllivier Robert       tt += sprintf(tt, "antenna2=\"%d\"", up->antenna2);
582ba371819SOllivier Robert       tt = add_var(&out->kv_list, 512, RO|DEF);
583ba371819SOllivier Robert       if('A' == up->timesource)
584ba371819SOllivier Robert 	tt += sprintf(tt, "timesource=\"radio\"");
585ba371819SOllivier Robert       else if('C' == up->timesource)
586ba371819SOllivier Robert 	tt += sprintf(tt, "timesource=\"quartz\"");
587ba371819SOllivier Robert       else
588ba371819SOllivier Robert 	tt += sprintf(tt, "timesource=\"unknown\"");
589ba371819SOllivier Robert       tt = add_var(&out->kv_list, 512, RO|DEF);
590ba371819SOllivier Robert       if('I' == up->quarzstatus)
591ba371819SOllivier Robert 	tt += sprintf(tt, "quartzstatus=\"synchronized\"");
592ba371819SOllivier Robert       else if('X' == up->quarzstatus)
593ba371819SOllivier Robert         tt += sprintf(tt, "quartzstatus=\"not synchronized\"");
594ba371819SOllivier Robert       else
595ba371819SOllivier Robert 	tt += sprintf(tt, "quartzstatus=\"unknown\"");
596ba371819SOllivier Robert       tt = add_var(&out->kv_list, 512, RO|DEF);
597ba371819SOllivier Robert       if('S' == up->dststatus)
598ba371819SOllivier Robert         tt += sprintf(tt, "dststatus=\"summer\"");
599ba371819SOllivier Robert       else if('W' == up->dststatus)
600ba371819SOllivier Robert         tt += sprintf(tt, "dststatus=\"winter\"");
601ba371819SOllivier Robert       else
602ba371819SOllivier Robert         tt += sprintf(tt, "dststatus=\"unknown\"");
603ba371819SOllivier Robert       tt = add_var(&out->kv_list, 512, RO|DEF);
604ba371819SOllivier Robert       tt += sprintf(tt, "firmware=\"%s\"", up->firmware);
605ba371819SOllivier Robert       tt = add_var(&out->kv_list, 512, RO|DEF);
606ba371819SOllivier Robert       tt += sprintf(tt, "serialnumber=\"%s\"", up->serial);
607ba371819SOllivier Robert       tt = add_var(&out->kv_list, 512, RO|DEF);
608ba371819SOllivier Robert     }
609ba371819SOllivier Robert }
610ba371819SOllivier Robert 
611ba371819SOllivier Robert static int neol_hexatoi_len(const char str[],
612ba371819SOllivier Robert 			    int *result,
613ba371819SOllivier Robert 			    int maxlen)
614ba371819SOllivier Robert {
615ba371819SOllivier Robert   int hexdigit;
616ba371819SOllivier Robert   int i;
617ba371819SOllivier Robert   int n = 0;
618ba371819SOllivier Robert 
619ba371819SOllivier Robert   for(i=0; isxdigit(str[i]) && i < maxlen; i++)
620ba371819SOllivier Robert     {
621ba371819SOllivier Robert       hexdigit = isdigit(str[i]) ? toupper(str[i]) - '0' : toupper(str[i]) - 'A' + 10;
622ba371819SOllivier Robert       n = 16 * n + hexdigit;
623ba371819SOllivier Robert     }
624ba371819SOllivier Robert   *result = n;
625ba371819SOllivier Robert   return (n);
626ba371819SOllivier Robert }
627ba371819SOllivier Robert 
628ba371819SOllivier Robert int neol_atoi_len(const char str[],
629ba371819SOllivier Robert 		  int *result,
630ba371819SOllivier Robert 		  int maxlen)
631ba371819SOllivier Robert {
632ba371819SOllivier Robert   int digit;
633ba371819SOllivier Robert   int i;
634ba371819SOllivier Robert   int n = 0;
635ba371819SOllivier Robert 
636ba371819SOllivier Robert   for(i=0; isdigit(str[i]) && i < maxlen; i++)
637ba371819SOllivier Robert     {
638ba371819SOllivier Robert       digit = str[i] - '0';
639ba371819SOllivier Robert       n = 10 * n + digit;
640ba371819SOllivier Robert     }
641ba371819SOllivier Robert   *result = n;
642ba371819SOllivier Robert   return (n);
643ba371819SOllivier Robert }
644ba371819SOllivier Robert 
645ba371819SOllivier Robert /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
646ba371819SOllivier Robert  * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
647ba371819SOllivier Robert  * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
648ba371819SOllivier Robert  *
649ba371819SOllivier Robert  * [For the Julian calendar (which was used in Russia before 1917,
650ba371819SOllivier Robert  * Britain & colonies before 1752, anywhere else before 1582,
651ba371819SOllivier Robert  * and is still in use by some communities) leave out the
652ba371819SOllivier Robert  * -year/100+year/400 terms, and add 10.]
653ba371819SOllivier Robert  *
654ba371819SOllivier Robert  * This algorithm was first published by Gauss (I think).
655ba371819SOllivier Robert  *
656ba371819SOllivier Robert  * WARNING: this function will overflow on 2106-02-07 06:28:16 on
657ba371819SOllivier Robert  * machines were long is 32-bit! (However, as time_t is signed, we
658ba371819SOllivier Robert  * will already get problems at other places on 2038-01-19 03:14:08)
659ba371819SOllivier Robert  */
660ba371819SOllivier Robert static unsigned long neol_mktime(int year,
661ba371819SOllivier Robert 				 int mon,
662ba371819SOllivier Robert 				 int day,
663ba371819SOllivier Robert 				 int hour,
664ba371819SOllivier Robert 				 int min,
665ba371819SOllivier Robert 				 int sec)
666ba371819SOllivier Robert {
667ba371819SOllivier Robert   if (0 >= (int) (mon -= 2)) {    /* 1..12 . 11,12,1..10 */
668ba371819SOllivier Robert     mon += 12;      /* Puts Feb last since it has leap day */
669ba371819SOllivier Robert     year -= 1;
670ba371819SOllivier Robert   }
671ba371819SOllivier Robert   return (((
672ba371819SOllivier Robert             (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
673ba371819SOllivier Robert             year*365 - 719499
674ba371819SOllivier Robert             )*24 + hour /* now have hours */
675ba371819SOllivier Robert            )*60 + min /* now have minutes */
676ba371819SOllivier Robert           )*60 + sec; /* finally seconds */
677ba371819SOllivier Robert }
678ba371819SOllivier Robert 
679ba371819SOllivier Robert static void neol_localtime(unsigned long utc,
680ba371819SOllivier Robert 			   int* year,
681ba371819SOllivier Robert 			   int* month,
682ba371819SOllivier Robert 			   int* day,
683ba371819SOllivier Robert 			   int* hour,
684ba371819SOllivier Robert 			   int* minute,
685ba371819SOllivier Robert 			   int* second)
686ba371819SOllivier Robert {
687ba371819SOllivier Robert   ldiv_t d;
688ba371819SOllivier Robert 
689ba371819SOllivier Robert   /* Sekunden */
690ba371819SOllivier Robert   d  = ldiv(utc, 60);
691ba371819SOllivier Robert   *second = d.rem;
692ba371819SOllivier Robert 
693ba371819SOllivier Robert   /* Minute */
694ba371819SOllivier Robert   d  = ldiv(d.quot, 60);
695ba371819SOllivier Robert   *minute = d.rem;
696ba371819SOllivier Robert 
697ba371819SOllivier Robert   /* Stunden */
698ba371819SOllivier Robert   d  = ldiv(d.quot, 24);
699ba371819SOllivier Robert   *hour = d.rem;
700ba371819SOllivier Robert 
701ba371819SOllivier Robert   /*             JDN Date 1/1/1970 */
702ba371819SOllivier Robert   neol_jdn_to_ymd(d.quot + 2440588L, year, month, day);
703ba371819SOllivier Robert }
704ba371819SOllivier Robert 
705ba371819SOllivier Robert static void neol_jdn_to_ymd(unsigned long jdn,
706ba371819SOllivier Robert 			    int *yy,
707ba371819SOllivier Robert 			    int *mm,
708ba371819SOllivier Robert 			    int *dd)
709ba371819SOllivier Robert {
710ba371819SOllivier Robert   unsigned long x, z, m, d, y;
711ba371819SOllivier Robert   unsigned long daysPer400Years = 146097UL;
712ba371819SOllivier Robert   unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
713ba371819SOllivier Robert 
714ba371819SOllivier Robert   x = jdn + 68569UL;
715ba371819SOllivier Robert   z = 4UL * x / daysPer400Years;
716ba371819SOllivier Robert   x = x - (daysPer400Years * z + 3UL) / 4UL;
717ba371819SOllivier Robert   y = 4000UL * (x + 1) / fudgedDaysPer4000Years;
718ba371819SOllivier Robert   x = x - 1461UL * y / 4UL + 31UL;
719ba371819SOllivier Robert   m = 80UL * x / 2447UL;
720ba371819SOllivier Robert   d = x - 2447UL * m / 80UL;
721ba371819SOllivier Robert   x = m / 11UL;
722ba371819SOllivier Robert   m = m + 2UL - 12UL * x;
723ba371819SOllivier Robert   y = 100UL * (z - 49UL) + y + x;
724ba371819SOllivier Robert 
725ba371819SOllivier Robert   *yy = (int)y;
726ba371819SOllivier Robert   *mm = (int)m;
727ba371819SOllivier Robert   *dd = (int)d;
728ba371819SOllivier Robert }
729ba371819SOllivier Robert 
730ba371819SOllivier Robert /*
731ba371819SOllivier Robert  *  delay in milliseconds
732ba371819SOllivier Robert  */
733ba371819SOllivier Robert static void
734ba371819SOllivier Robert neol_mdelay(int milliseconds)
735ba371819SOllivier Robert {
736ba371819SOllivier Robert   struct timeval        tv;
737ba371819SOllivier Robert 
738ba371819SOllivier Robert   if (milliseconds)
739ba371819SOllivier Robert     {
740ba371819SOllivier Robert       tv.tv_sec  = 0;
741ba371819SOllivier Robert       tv.tv_usec = milliseconds * 1000;
742ba371819SOllivier Robert       select(1, NULL, NULL, NULL, &tv);
743ba371819SOllivier Robert     }
744ba371819SOllivier Robert }
745ba371819SOllivier Robert 
746ba371819SOllivier Robert static int
747ba371819SOllivier Robert neol_query_firmware(int fd,
748ba371819SOllivier Robert 		    int unit,
749ba371819SOllivier Robert 		    char *firmware,
750ba371819SOllivier Robert 		    int maxlen)
751ba371819SOllivier Robert {
752ba371819SOllivier Robert   unsigned char tmpbuf[256];
753ba371819SOllivier Robert   int len;
754ba371819SOllivier Robert   int lastsearch;
755ba371819SOllivier Robert   unsigned char c;
756ba371819SOllivier Robert   int last_c_was_crlf;
757ba371819SOllivier Robert   int last_crlf_conv_len;
758ba371819SOllivier Robert   int init;
759ba371819SOllivier Robert   int read_tries;
760ba371819SOllivier Robert   int flag = 0;
761ba371819SOllivier Robert 
762ba371819SOllivier Robert   /* wait a little bit */
763ba371819SOllivier Robert   neol_mdelay(250);
764ba371819SOllivier Robert   if(-1 != write(fd, "V", 1))
765ba371819SOllivier Robert     {
766ba371819SOllivier Robert       /* wait a little bit */
767ba371819SOllivier Robert       neol_mdelay(250);
768ba371819SOllivier Robert       memset(tmpbuf, 0x00, sizeof(tmpbuf));
769ba371819SOllivier Robert 
770ba371819SOllivier Robert       len = 0;
771ba371819SOllivier Robert       lastsearch = 0;
772ba371819SOllivier Robert       last_c_was_crlf = 0;
773ba371819SOllivier Robert       last_crlf_conv_len = 0;
774ba371819SOllivier Robert       init = 1;
775ba371819SOllivier Robert       read_tries = 0;
776ba371819SOllivier Robert       for(;;)
777ba371819SOllivier Robert 	{
778ba371819SOllivier Robert 	  if(read_tries++ > 500)
779ba371819SOllivier Robert 	    {
780ba371819SOllivier Robert 	      msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
781ba371819SOllivier Robert 	      strcpy(tmpbuf, "unknown due to timeout");
782ba371819SOllivier Robert 	      break;
783ba371819SOllivier Robert 	    }
784ba371819SOllivier Robert 	  if(-1 == read(fd, &c, 1))
785ba371819SOllivier Robert 	    {
786ba371819SOllivier Robert 	      neol_mdelay(25);
787ba371819SOllivier Robert 	      continue;
788ba371819SOllivier Robert 	    }
789ba371819SOllivier Robert 	  if(init)
790ba371819SOllivier Robert 	    {
791ba371819SOllivier Robert 	      if(0xA9 != c) /* wait for (c) char in input stream */
792ba371819SOllivier Robert 		continue;
793ba371819SOllivier Robert 
794ba371819SOllivier Robert 	      strcpy(tmpbuf, "(c)");
795ba371819SOllivier Robert 	      len = 3;
796ba371819SOllivier Robert 	      init = 0;
797ba371819SOllivier Robert 	      continue;
798ba371819SOllivier Robert 	    }
799ba371819SOllivier Robert 
800ba371819SOllivier Robert 	  //msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
801ba371819SOllivier Robert 	  if(0x0A == c || 0x0D == c)
802ba371819SOllivier Robert 	    {
803ba371819SOllivier Robert 	      if(last_c_was_crlf)
804ba371819SOllivier Robert 		{
805ba371819SOllivier Robert 		  char *ptr;
806ba371819SOllivier Robert 		  ptr = strstr(&tmpbuf[lastsearch], "S/N");
807ba371819SOllivier Robert 		  if(NULL != ptr)
808ba371819SOllivier Robert 		    {
809ba371819SOllivier Robert 		      tmpbuf[last_crlf_conv_len] = 0;
810ba371819SOllivier Robert 		      flag = 1;
811ba371819SOllivier Robert 		      break;
812ba371819SOllivier Robert 		    }
813ba371819SOllivier Robert 		  /* convert \n to / */
814ba371819SOllivier Robert 		  last_crlf_conv_len = len;
815ba371819SOllivier Robert 		  tmpbuf[len++] = ' ';
816ba371819SOllivier Robert 		  tmpbuf[len++] = '/';
817ba371819SOllivier Robert 		  tmpbuf[len++] = ' ';
818ba371819SOllivier Robert 		  lastsearch = len;
819ba371819SOllivier Robert 		}
820ba371819SOllivier Robert 	      last_c_was_crlf = 1;
821ba371819SOllivier Robert 	    }
822ba371819SOllivier Robert 	  else
823ba371819SOllivier Robert 	    {
824ba371819SOllivier Robert 	      last_c_was_crlf = 0;
825ba371819SOllivier Robert 	      if(0x00 != c)
826ba371819SOllivier Robert 		tmpbuf[len++] = c;
827ba371819SOllivier Robert 	    }
828ba371819SOllivier Robert 	  tmpbuf[len] = '\0';
829ba371819SOllivier Robert 	  if(len > sizeof(tmpbuf)-5)
830ba371819SOllivier Robert 	    break;
831ba371819SOllivier Robert 	}
832ba371819SOllivier Robert     }
833ba371819SOllivier Robert   else
834ba371819SOllivier Robert     {
835ba371819SOllivier Robert       msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit);
836ba371819SOllivier Robert       strcpy(tmpbuf, "unknown error");
837ba371819SOllivier Robert     }
838ba371819SOllivier Robert   strncpy(firmware, tmpbuf, maxlen);
839ba371819SOllivier Robert   firmware[maxlen] = '\0';
840ba371819SOllivier Robert 
841ba371819SOllivier Robert   if(flag)
842ba371819SOllivier Robert     {
843ba371819SOllivier Robert       NLOG(NLOG_CLOCKINFO)
844ba371819SOllivier Robert 	msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
845ba371819SOllivier Robert     }
846ba371819SOllivier Robert 
847ba371819SOllivier Robert   return (flag);
848ba371819SOllivier Robert }
849ba371819SOllivier Robert 
850ba371819SOllivier Robert #else
851ba371819SOllivier Robert int refclock_neoclock4x_bs;
852ba371819SOllivier Robert #endif /* REFCLOCK */
853ba371819SOllivier Robert 
854ba371819SOllivier Robert /*
855ba371819SOllivier Robert  * History:
856ba371819SOllivier Robert  * refclock_neoclock4x.c
857ba371819SOllivier Robert  *
858ba371819SOllivier Robert  * 2002/04/27 cjh
859ba371819SOllivier Robert  * Revision 1.0  first release
860ba371819SOllivier Robert  *
861ba371819SOllivier Robert  * 2002/0715 cjh
862ba371819SOllivier Robert  * preparing for bitkeeper reposity
863ba371819SOllivier Robert  *
864ba371819SOllivier Robert  */
865