xref: /freebsd/contrib/ntp/ntpd/refclock_neoclock4x.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1ba371819SOllivier Robert /*
2ba371819SOllivier Robert  *
39c2daa00SOllivier Robert  * Refclock_neoclock4x.c
4ba371819SOllivier Robert  * - NeoClock4X driver for DCF77 or FIA Timecode
5ba371819SOllivier Robert  *
62b15cb3dSCy Schubert  * Date: 2009-12-04 v1.16
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  */
12ba371819SOllivier Robert 
13ba371819SOllivier Robert #ifdef HAVE_CONFIG_H
14ba371819SOllivier Robert # include "config.h"
15ba371819SOllivier Robert #endif
16ba371819SOllivier Robert 
17ba371819SOllivier Robert #if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))
18ba371819SOllivier Robert 
19ba371819SOllivier Robert #include <unistd.h>
20ba371819SOllivier Robert #include <sys/time.h>
21ba371819SOllivier Robert #include <sys/types.h>
22ba371819SOllivier Robert #include <termios.h>
23ba371819SOllivier Robert #include <sys/ioctl.h>
24ba371819SOllivier Robert #include <ctype.h>
25ba371819SOllivier Robert 
26ba371819SOllivier Robert #include "ntpd.h"
27ba371819SOllivier Robert #include "ntp_io.h"
28ba371819SOllivier Robert #include "ntp_control.h"
29ba371819SOllivier Robert #include "ntp_refclock.h"
30ba371819SOllivier Robert #include "ntp_unixtime.h"
31ba371819SOllivier Robert #include "ntp_stdlib.h"
32ba371819SOllivier Robert 
33ba371819SOllivier Robert #if defined HAVE_SYS_MODEM_H
34ba371819SOllivier Robert # include <sys/modem.h>
35ea906c41SOllivier Robert # ifndef __QNXNTO__
36ba371819SOllivier Robert #  define TIOCMSET MCSETA
37ba371819SOllivier Robert #  define TIOCMGET MCGETA
38ba371819SOllivier Robert #  define TIOCM_RTS MRTS
39ba371819SOllivier Robert # endif
40ea906c41SOllivier Robert #endif
41ba371819SOllivier Robert 
42ba371819SOllivier Robert #ifdef HAVE_TERMIOS_H
43ba371819SOllivier Robert # ifdef TERMIOS_NEEDS__SVID3
44ba371819SOllivier Robert #  define _SVID3
45ba371819SOllivier Robert # endif
46ba371819SOllivier Robert # include <termios.h>
47ba371819SOllivier Robert # ifdef TERMIOS_NEEDS__SVID3
48ba371819SOllivier Robert #  undef _SVID3
49ba371819SOllivier Robert # endif
50ba371819SOllivier Robert #endif
51ba371819SOllivier Robert 
52ba371819SOllivier Robert #ifdef HAVE_SYS_IOCTL_H
53ba371819SOllivier Robert # include <sys/ioctl.h>
54ba371819SOllivier Robert #endif
55ba371819SOllivier Robert 
569c2daa00SOllivier Robert /*
57ea906c41SOllivier Robert  * NTP version 4.20 change the pp->msec field to pp->nsec.
58ea906c41SOllivier Robert  * To allow to support older ntp versions with this sourcefile
59ea906c41SOllivier Robert  * you can define NTP_PRE_420 to allow this driver to compile
60ea906c41SOllivier Robert  * with ntp version back to 4.1.2.
61ea906c41SOllivier Robert  *
62ea906c41SOllivier Robert  */
63ea906c41SOllivier Robert #if 0
64ea906c41SOllivier Robert #define NTP_PRE_420
65ea906c41SOllivier Robert #endif
66ea906c41SOllivier Robert 
67ea906c41SOllivier Robert /*
689c2daa00SOllivier Robert  * If you want the driver for whatever reason to not use
699c2daa00SOllivier Robert  * the TX line to send anything to your NeoClock4X
709c2daa00SOllivier Robert  * device you must tell the NTP refclock driver which
719c2daa00SOllivier Robert  * firmware you NeoClock4X device uses.
729c2daa00SOllivier Robert  *
739c2daa00SOllivier Robert  * If you want to enable this feature change the "#if 0"
749c2daa00SOllivier Robert  * line to "#if 1" and make sure that the defined firmware
759c2daa00SOllivier Robert  * matches the firmware off your NeoClock4X receiver!
769c2daa00SOllivier Robert  *
779c2daa00SOllivier Robert  */
789c2daa00SOllivier Robert 
799c2daa00SOllivier Robert #if 0
809c2daa00SOllivier Robert #define NEOCLOCK4X_FIRMWARE                NEOCLOCK4X_FIRMWARE_VERSION_A
819c2daa00SOllivier Robert #endif
829c2daa00SOllivier Robert 
83ea906c41SOllivier Robert /* at this time only firmware version A is known */
849c2daa00SOllivier Robert #define NEOCLOCK4X_FIRMWARE_VERSION_A      'A'
859c2daa00SOllivier Robert 
86ba371819SOllivier Robert #define NEOCLOCK4X_TIMECODELEN 37
87ba371819SOllivier Robert 
88ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_SERIAL            3
89ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_RADIOSIGNAL       9
90ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_DAY              12
91ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_MONTH            14
92ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_YEAR             16
93ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_HOUR             18
94ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_MINUTE           20
95ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_SECOND           22
96ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_HSEC             24
97ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_DOW              26
98ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_TIMESOURCE       28
99ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_DSTSTATUS        29
100ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_QUARZSTATUS      30
101ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_ANTENNA1         31
102ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_ANTENNA2         33
103ba371819SOllivier Robert #define NEOCLOCK4X_OFFSET_CRC              35
104ba371819SOllivier Robert 
1052b15cb3dSCy Schubert #define NEOCLOCK4X_DRIVER_VERSION          "1.16 (2009-12-04)"
106ea906c41SOllivier Robert 
107ea906c41SOllivier Robert #define NSEC_TO_MILLI                      1000000
1089c2daa00SOllivier Robert 
109ba371819SOllivier Robert struct neoclock4x_unit {
110ba371819SOllivier Robert   l_fp	laststamp;	/* last receive timestamp */
111ba371819SOllivier Robert   short	unit;		/* NTP refclock unit number */
112ba371819SOllivier Robert   u_long polled;	/* flag to detect noreplies */
113ba371819SOllivier Robert   char	leap_status;	/* leap second flag */
114ba371819SOllivier Robert   int	recvnow;
115ba371819SOllivier Robert 
116ba371819SOllivier Robert   char  firmware[80];
1179c2daa00SOllivier Robert   char  firmwaretag;
118ba371819SOllivier Robert   char  serial[7];
119ba371819SOllivier Robert   char  radiosignal[4];
120ba371819SOllivier Robert   char  timesource;
121ba371819SOllivier Robert   char  dststatus;
122ba371819SOllivier Robert   char  quarzstatus;
123ba371819SOllivier Robert   int   antenna1;
124ba371819SOllivier Robert   int   antenna2;
125ba371819SOllivier Robert   int   utc_year;
126ba371819SOllivier Robert   int   utc_month;
127ba371819SOllivier Robert   int   utc_day;
128ba371819SOllivier Robert   int   utc_hour;
129ba371819SOllivier Robert   int   utc_minute;
130ba371819SOllivier Robert   int   utc_second;
131ba371819SOllivier Robert   int   utc_msec;
132ba371819SOllivier Robert };
133ba371819SOllivier Robert 
1342b15cb3dSCy Schubert static	int	neoclock4x_start	(int, struct peer *);
1352b15cb3dSCy Schubert static	void	neoclock4x_shutdown	(int, struct peer *);
1362b15cb3dSCy Schubert static	void	neoclock4x_receive	(struct recvbuf *);
1372b15cb3dSCy Schubert static	void	neoclock4x_poll		(int, struct peer *);
1382b15cb3dSCy Schubert static	void	neoclock4x_control	(int, const struct refclockstat *, struct refclockstat *, struct peer *);
139ba371819SOllivier Robert 
1402b15cb3dSCy Schubert static int	neol_atoi_len		(const char str[], int *, int);
1412b15cb3dSCy Schubert static int	neol_hexatoi_len	(const char str[], int *, int);
1422b15cb3dSCy Schubert static void	neol_jdn_to_ymd		(unsigned long, int *, int *, int *);
1432b15cb3dSCy Schubert static void	neol_localtime		(unsigned long, int* , int*, int*, int*, int*, int*);
1442b15cb3dSCy Schubert static unsigned long neol_mktime	(int, int, int, int, int, int);
1459c2daa00SOllivier Robert #if !defined(NEOCLOCK4X_FIRMWARE)
1462b15cb3dSCy Schubert static int	neol_query_firmware	(int, int, char *, size_t);
1472b15cb3dSCy Schubert static int	neol_check_firmware	(int, const char*, char *);
1489c2daa00SOllivier Robert #endif
149ba371819SOllivier Robert 
150ba371819SOllivier Robert struct refclock refclock_neoclock4x = {
151ba371819SOllivier Robert   neoclock4x_start,	/* start up driver */
152ba371819SOllivier Robert   neoclock4x_shutdown,	/* shut down driver */
153ba371819SOllivier Robert   neoclock4x_poll,	/* transmit poll message */
154ba371819SOllivier Robert   neoclock4x_control,
155ba371819SOllivier Robert   noentry,		/* initialize driver (not used) */
156ba371819SOllivier Robert   noentry,		/* not used */
157ba371819SOllivier Robert   NOFLAGS			/* not used */
158ba371819SOllivier Robert };
159ba371819SOllivier Robert 
160ba371819SOllivier Robert static int
161ba371819SOllivier Robert neoclock4x_start(int unit,
162ba371819SOllivier Robert 		 struct peer *peer)
163ba371819SOllivier Robert {
164ba371819SOllivier Robert   struct neoclock4x_unit *up;
165ba371819SOllivier Robert   struct refclockproc *pp;
166ba371819SOllivier Robert   int fd;
167ba371819SOllivier Robert   char dev[20];
168ba371819SOllivier Robert   int sl232;
1699c2daa00SOllivier Robert #if defined(HAVE_TERMIOS)
170ba371819SOllivier Robert   struct termios termsettings;
1719c2daa00SOllivier Robert #endif
1729c2daa00SOllivier Robert #if !defined(NEOCLOCK4X_FIRMWARE)
173ba371819SOllivier Robert   int tries;
1749c2daa00SOllivier Robert #endif
175ba371819SOllivier Robert 
1769c2daa00SOllivier Robert   (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit);
177ba371819SOllivier Robert 
178ba371819SOllivier Robert   /* LDISC_STD, LDISC_RAW
179ba371819SOllivier Robert    * Open serial port. Use CLK line discipline, if available.
180ba371819SOllivier Robert    */
181a466cc55SCy Schubert   fd = refclock_open(&peer->srcadr, dev, B2400, LDISC_STD);
182ba371819SOllivier Robert   if(fd <= 0)
183ba371819SOllivier Robert     {
184ba371819SOllivier Robert       return (0);
185ba371819SOllivier Robert     }
186ba371819SOllivier Robert 
1879c2daa00SOllivier Robert #if defined(HAVE_TERMIOS)
188ea906c41SOllivier Robert 
189ea906c41SOllivier Robert #if 1
190ea906c41SOllivier Robert   if(tcgetattr(fd, &termsettings) < 0)
191ea906c41SOllivier Robert     {
192ea906c41SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
193ea906c41SOllivier Robert       (void) close(fd);
194ea906c41SOllivier Robert       return (0);
195ea906c41SOllivier Robert     }
196ea906c41SOllivier Robert 
197ea906c41SOllivier Robert   /* 2400 Baud 8N2 */
198ea906c41SOllivier Robert   termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL;
199ea906c41SOllivier Robert   termsettings.c_oflag = 0;
200ea906c41SOllivier Robert   termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD;
201ea906c41SOllivier Robert   (void)cfsetispeed(&termsettings, (u_int)B2400);
202ea906c41SOllivier Robert   (void)cfsetospeed(&termsettings, (u_int)B2400);
203ea906c41SOllivier Robert 
204ea906c41SOllivier Robert   if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
205ea906c41SOllivier Robert     {
206ea906c41SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
207ea906c41SOllivier Robert       (void) close(fd);
208ea906c41SOllivier Robert       return (0);
209ea906c41SOllivier Robert     }
210ea906c41SOllivier Robert 
211ea906c41SOllivier Robert #else
2129c2daa00SOllivier Robert   if(tcgetattr(fd, &termsettings) < 0)
2139c2daa00SOllivier Robert     {
2149c2daa00SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
2159c2daa00SOllivier Robert       (void) close(fd);
2169c2daa00SOllivier Robert       return (0);
2179c2daa00SOllivier Robert     }
2189c2daa00SOllivier Robert 
2199c2daa00SOllivier Robert   /* 2400 Baud 8N2 */
2209c2daa00SOllivier Robert   termsettings.c_cflag &= ~PARENB;
2219c2daa00SOllivier Robert   termsettings.c_cflag |= CSTOPB;
2229c2daa00SOllivier Robert   termsettings.c_cflag &= ~CSIZE;
2239c2daa00SOllivier Robert   termsettings.c_cflag |= CS8;
2249c2daa00SOllivier Robert 
2259c2daa00SOllivier Robert   if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
2269c2daa00SOllivier Robert     {
2279c2daa00SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
2289c2daa00SOllivier Robert       (void) close(fd);
2299c2daa00SOllivier Robert       return (0);
2309c2daa00SOllivier Robert     }
231ea906c41SOllivier Robert #endif
232ea906c41SOllivier Robert 
2339c2daa00SOllivier Robert #elif defined(HAVE_SYSV_TTYS)
2349c2daa00SOllivier Robert   if(ioctl(fd, TCGETA, &termsettings) < 0)
2359c2daa00SOllivier Robert     {
2369c2daa00SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit);
2379c2daa00SOllivier Robert       (void) close(fd);
2389c2daa00SOllivier Robert       return (0);
2399c2daa00SOllivier Robert     }
2409c2daa00SOllivier Robert 
2419c2daa00SOllivier Robert   /* 2400 Baud 8N2 */
2429c2daa00SOllivier Robert   termsettings.c_cflag &= ~PARENB;
2439c2daa00SOllivier Robert   termsettings.c_cflag |= CSTOPB;
2449c2daa00SOllivier Robert   termsettings.c_cflag &= ~CSIZE;
2459c2daa00SOllivier Robert   termsettings.c_cflag |= CS8;
2469c2daa00SOllivier Robert 
2479c2daa00SOllivier Robert   if(ioctl(fd, TCSETA, &termsettings) < 0)
2489c2daa00SOllivier Robert     {
2499c2daa00SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit);
2509c2daa00SOllivier Robert       (void) close(fd);
2519c2daa00SOllivier Robert       return (0);
2529c2daa00SOllivier Robert     }
2539c2daa00SOllivier Robert #else
2549c2daa00SOllivier Robert   msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit);
2559c2daa00SOllivier Robert   (void) close(fd);
2569c2daa00SOllivier Robert   return (0);
2579c2daa00SOllivier Robert #endif
2589c2daa00SOllivier Robert 
259ba371819SOllivier Robert #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
260ba371819SOllivier Robert   /* turn on RTS, and DTR for power supply */
261ba371819SOllivier Robert   /* NeoClock4x is powered from serial line */
262ba371819SOllivier Robert   if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1)
263ba371819SOllivier Robert     {
264ba371819SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
2659c2daa00SOllivier Robert       (void) close(fd);
2669c2daa00SOllivier Robert       return (0);
267ba371819SOllivier Robert     }
268ba371819SOllivier Robert #ifdef TIOCM_RTS
269ba371819SOllivier Robert   sl232 = sl232 | TIOCM_DTR | TIOCM_RTS;	/* turn on RTS, and DTR for power supply */
270ba371819SOllivier Robert #else
271ba371819SOllivier Robert   sl232 = sl232 | CIOCM_DTR | CIOCM_RTS;	/* turn on RTS, and DTR for power supply */
272ba371819SOllivier Robert #endif
273ba371819SOllivier Robert   if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1)
274ba371819SOllivier Robert     {
275ba371819SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
2769c2daa00SOllivier Robert       (void) close(fd);
2779c2daa00SOllivier Robert       return (0);
278ba371819SOllivier Robert     }
279ba371819SOllivier Robert #else
2809c2daa00SOllivier Robert   msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!",
281ba371819SOllivier Robert 	  unit);
2829c2daa00SOllivier Robert   (void) close(fd);
2839c2daa00SOllivier Robert   return (0);
284ba371819SOllivier Robert #endif
285ba371819SOllivier Robert 
286ba371819SOllivier Robert   up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
287ba371819SOllivier Robert   if(!(up))
288ba371819SOllivier Robert     {
289ba371819SOllivier Robert       msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
290ba371819SOllivier Robert       (void) close(fd);
291ba371819SOllivier Robert       return (0);
292ba371819SOllivier Robert     }
293ba371819SOllivier Robert 
294ba371819SOllivier Robert   memset((char *)up, 0, sizeof(struct neoclock4x_unit));
295ba371819SOllivier Robert   pp = peer->procptr;
296ba371819SOllivier Robert   pp->clockdesc = "NeoClock4X";
2972b15cb3dSCy Schubert   pp->unitptr = up;
298ba371819SOllivier Robert   pp->io.clock_recv = neoclock4x_receive;
2992b15cb3dSCy Schubert   pp->io.srcclock = peer;
300ba371819SOllivier Robert   pp->io.datalen = 0;
301ba371819SOllivier Robert   pp->io.fd = fd;
3029c2daa00SOllivier Robert   /*
3039c2daa00SOllivier Robert    * no fudge time is given by user!
3049c2daa00SOllivier Robert    * use 169.583333 ms to compensate the serial line delay
305ba371819SOllivier Robert    * formula is:
306ba371819SOllivier Robert    * 2400 Baud / 11 bit = 218.18 charaters per second
307ba371819SOllivier Robert    *  (NeoClock4X timecode len)
308ba371819SOllivier Robert    */
309ba371819SOllivier Robert   pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0;
310ba371819SOllivier Robert 
311ba371819SOllivier Robert   /*
312ba371819SOllivier Robert    * Initialize miscellaneous variables
313ba371819SOllivier Robert    */
314ba371819SOllivier Robert   peer->precision = -10;
315ba371819SOllivier Robert   memcpy((char *)&pp->refid, "neol", 4);
316ba371819SOllivier Robert 
317ba371819SOllivier Robert   up->leap_status = 0;
318ba371819SOllivier Robert   up->unit = unit;
3192b15cb3dSCy Schubert   strlcpy(up->firmware, "?", sizeof(up->firmware));
3209c2daa00SOllivier Robert   up->firmwaretag = '?';
3212b15cb3dSCy Schubert   strlcpy(up->serial, "?", sizeof(up->serial));
3222b15cb3dSCy Schubert   strlcpy(up->radiosignal, "?", sizeof(up->radiosignal));
323ba371819SOllivier Robert   up->timesource  = '?';
324ba371819SOllivier Robert   up->dststatus   = '?';
325ba371819SOllivier Robert   up->quarzstatus = '?';
326ba371819SOllivier Robert   up->antenna1    = -1;
327ba371819SOllivier Robert   up->antenna2    = -1;
328ba371819SOllivier Robert   up->utc_year    = 0;
329ba371819SOllivier Robert   up->utc_month   = 0;
330ba371819SOllivier Robert   up->utc_day     = 0;
331ba371819SOllivier Robert   up->utc_hour    = 0;
332ba371819SOllivier Robert   up->utc_minute  = 0;
333ba371819SOllivier Robert   up->utc_second  = 0;
334ba371819SOllivier Robert   up->utc_msec    = 0;
335ba371819SOllivier Robert 
3369c2daa00SOllivier Robert #if defined(NEOCLOCK4X_FIRMWARE)
3379c2daa00SOllivier Robert #if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A
3382b15cb3dSCy Schubert   strlcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)",
3392b15cb3dSCy Schubert 	  sizeof(up->firmware));
3409c2daa00SOllivier Robert   up->firmwaretag = 'A';
3419c2daa00SOllivier Robert #else
342ea906c41SOllivier Robert   msyslog(LOG_EMERG, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X",
3439c2daa00SOllivier Robert 	  unit);
3449c2daa00SOllivier Robert   (void) close(fd);
3459c2daa00SOllivier Robert   pp->io.fd = -1;
3469c2daa00SOllivier Robert   free(pp->unitptr);
3479c2daa00SOllivier Robert   pp->unitptr = NULL;
3489c2daa00SOllivier Robert   return (0);
3499c2daa00SOllivier Robert #endif
3509c2daa00SOllivier Robert #else
351ba371819SOllivier Robert   for(tries=0; tries < 5; tries++)
352ba371819SOllivier Robert     {
353ba371819SOllivier Robert       NLOG(NLOG_CLOCKINFO)
3549c2daa00SOllivier Robert 	msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries);
3559c2daa00SOllivier Robert       /* wait 3 seconds for receiver to power up */
356ba371819SOllivier Robert       sleep(3);
357ba371819SOllivier Robert       if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
358ba371819SOllivier Robert 	{
359ba371819SOllivier Robert 	  break;
360ba371819SOllivier Robert 	}
361ba371819SOllivier Robert     }
362ba371819SOllivier Robert 
3639c2daa00SOllivier Robert   /* can I handle this firmware version? */
3649c2daa00SOllivier Robert   if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag))
3659c2daa00SOllivier Robert     {
3669c2daa00SOllivier Robert       (void) close(fd);
3679c2daa00SOllivier Robert       pp->io.fd = -1;
3689c2daa00SOllivier Robert       free(pp->unitptr);
3699c2daa00SOllivier Robert       pp->unitptr = NULL;
3709c2daa00SOllivier Robert       return (0);
3719c2daa00SOllivier Robert     }
3729c2daa00SOllivier Robert #endif
3739c2daa00SOllivier Robert 
3749c2daa00SOllivier Robert   if(!io_addclock(&pp->io))
3759c2daa00SOllivier Robert     {
3769c2daa00SOllivier Robert       msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit);
3779c2daa00SOllivier Robert       (void) close(fd);
3789c2daa00SOllivier Robert       pp->io.fd = -1;
3799c2daa00SOllivier Robert       free(pp->unitptr);
3809c2daa00SOllivier Robert       pp->unitptr = NULL;
3819c2daa00SOllivier Robert       return (0);
3829c2daa00SOllivier Robert     }
3839c2daa00SOllivier Robert 
384ba371819SOllivier Robert   NLOG(NLOG_CLOCKINFO)
385ba371819SOllivier Robert     msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
386ba371819SOllivier Robert 
387ba371819SOllivier Robert   return (1);
388ba371819SOllivier Robert }
389ba371819SOllivier Robert 
390ba371819SOllivier Robert static void
391ba371819SOllivier Robert neoclock4x_shutdown(int unit,
392ba371819SOllivier Robert 		   struct peer *peer)
393ba371819SOllivier Robert {
394ba371819SOllivier Robert   struct neoclock4x_unit *up;
395ba371819SOllivier Robert   struct refclockproc *pp;
396ba371819SOllivier Robert   int sl232;
397ba371819SOllivier Robert 
3989c2daa00SOllivier Robert   if(NULL != peer)
3999c2daa00SOllivier Robert     {
400ba371819SOllivier Robert       pp = peer->procptr;
4019c2daa00SOllivier Robert       if(pp != NULL)
4029c2daa00SOllivier Robert         {
4032b15cb3dSCy Schubert           up = pp->unitptr;
4049c2daa00SOllivier Robert           if(up != NULL)
4059c2daa00SOllivier Robert             {
4069c2daa00SOllivier Robert               if(-1 !=  pp->io.fd)
4079c2daa00SOllivier Robert                 {
408ba371819SOllivier Robert #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS))
409ba371819SOllivier Robert                   /* turn on RTS, and DTR for power supply */
410ba371819SOllivier Robert                   /* NeoClock4x is powered from serial line */
411ba371819SOllivier Robert                   if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
412ba371819SOllivier Robert                     {
4139c2daa00SOllivier Robert                       msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m",
4149c2daa00SOllivier Robert                               unit);
415ba371819SOllivier Robert                     }
416ba371819SOllivier Robert #ifdef TIOCM_RTS
4179c2daa00SOllivier Robert                   /* turn on RTS, and DTR for power supply */
4189c2daa00SOllivier Robert                   sl232 &= ~(TIOCM_DTR | TIOCM_RTS);
419ba371819SOllivier Robert #else
4209c2daa00SOllivier Robert                   /* turn on RTS, and DTR for power supply */
4219c2daa00SOllivier Robert                   sl232 &= ~(CIOCM_DTR | CIOCM_RTS);
422ba371819SOllivier Robert #endif
423ba371819SOllivier Robert                   if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
424ba371819SOllivier Robert                     {
4259c2daa00SOllivier Robert                       msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m",
4269c2daa00SOllivier Robert                               unit);
427ba371819SOllivier Robert                     }
428ba371819SOllivier Robert #endif
4299c2daa00SOllivier Robert                   io_closeclock(&pp->io);
4309c2daa00SOllivier Robert                 }
4319c2daa00SOllivier Robert               free(up);
4329c2daa00SOllivier Robert               pp->unitptr = NULL;
4339c2daa00SOllivier Robert             }
4349c2daa00SOllivier Robert         }
4359c2daa00SOllivier Robert     }
4369c2daa00SOllivier Robert 
437ba371819SOllivier Robert   msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
438ba371819SOllivier Robert 
439ba371819SOllivier Robert   NLOG(NLOG_CLOCKINFO)
440ba371819SOllivier Robert     msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
441ba371819SOllivier Robert }
442ba371819SOllivier Robert 
443ba371819SOllivier Robert static void
444ba371819SOllivier Robert neoclock4x_receive(struct recvbuf *rbufp)
445ba371819SOllivier Robert {
446ba371819SOllivier Robert   struct neoclock4x_unit *up;
447ba371819SOllivier Robert   struct refclockproc *pp;
448ba371819SOllivier Robert   struct peer *peer;
449ba371819SOllivier Robert   unsigned long calc_utc;
450ba371819SOllivier Robert   int day;
451ba371819SOllivier Robert   int month;	/* ddd conversion */
452ba371819SOllivier Robert   int c;
4539c2daa00SOllivier Robert   int dsec;
454ba371819SOllivier Robert   unsigned char calc_chksum;
455ba371819SOllivier Robert   int recv_chksum;
456ba371819SOllivier Robert 
4572b15cb3dSCy Schubert   peer = rbufp->recv_peer;
458ba371819SOllivier Robert   pp = peer->procptr;
4592b15cb3dSCy Schubert   up = pp->unitptr;
460ba371819SOllivier Robert 
461ba371819SOllivier Robert   /* wait till poll interval is reached */
462ba371819SOllivier Robert   if(0 == up->recvnow)
463ba371819SOllivier Robert     return;
464ba371819SOllivier Robert 
465ba371819SOllivier Robert   /* reset poll interval flag */
466ba371819SOllivier Robert   up->recvnow = 0;
467ba371819SOllivier Robert 
468ba371819SOllivier Robert   /* read last received timecode */
469ba371819SOllivier Robert   pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
4709c2daa00SOllivier Robert   pp->leap = LEAP_NOWARNING;
471ba371819SOllivier Robert 
472ba371819SOllivier Robert   if(NEOCLOCK4X_TIMECODELEN != pp->lencode)
473ba371819SOllivier Robert     {
474ba371819SOllivier Robert       NLOG(NLOG_CLOCKEVENT)
475ba371819SOllivier Robert 	msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s",
476ba371819SOllivier Robert 		up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode);
477ba371819SOllivier Robert       refclock_report(peer, CEVNT_BADREPLY);
478ba371819SOllivier Robert       return;
479ba371819SOllivier Robert     }
480ba371819SOllivier Robert 
481ba371819SOllivier Robert   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
482ba371819SOllivier Robert 
483ba371819SOllivier Robert   /* calculate checksum */
484ba371819SOllivier Robert   calc_chksum = 0;
485ba371819SOllivier Robert   for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++)
486ba371819SOllivier Robert     {
487ba371819SOllivier Robert       calc_chksum += pp->a_lastcode[c];
488ba371819SOllivier Robert     }
489ba371819SOllivier Robert   if(recv_chksum != calc_chksum)
490ba371819SOllivier Robert     {
491ba371819SOllivier Robert       NLOG(NLOG_CLOCKEVENT)
492ba371819SOllivier Robert 	msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s",
493ba371819SOllivier Robert 		up->unit, pp->a_lastcode);
494ba371819SOllivier Robert       refclock_report(peer, CEVNT_BADREPLY);
495ba371819SOllivier Robert       return;
496ba371819SOllivier Robert     }
497ba371819SOllivier Robert 
498ba371819SOllivier Robert   /* Allow synchronization even is quartz clock is
499ba371819SOllivier Robert    * never initialized.
500ba371819SOllivier Robert    * WARNING: This is dangerous!
501ba371819SOllivier Robert    */
502ba371819SOllivier Robert   up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS];
503ba371819SOllivier Robert   if(0==(pp->sloppyclockflag & CLK_FLAG2))
504ba371819SOllivier Robert     {
505ba371819SOllivier Robert       if('I' != up->quarzstatus)
506ba371819SOllivier Robert 	{
507ba371819SOllivier Robert 	  NLOG(NLOG_CLOCKEVENT)
508ba371819SOllivier Robert 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s",
509ba371819SOllivier Robert 		    up->unit, pp->a_lastcode);
510ba371819SOllivier Robert 	  pp->leap = LEAP_NOTINSYNC;
511ba371819SOllivier Robert 	  refclock_report(peer, CEVNT_BADDATE);
512ba371819SOllivier Robert 	  return;
513ba371819SOllivier Robert 	}
514ba371819SOllivier Robert     }
515ba371819SOllivier Robert   if('I' != up->quarzstatus)
516ba371819SOllivier Robert     {
517ba371819SOllivier Robert       NLOG(NLOG_CLOCKEVENT)
518ba371819SOllivier Robert 	msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
519ba371819SOllivier Robert 		up->unit, pp->a_lastcode);
520ba371819SOllivier Robert     }
521ba371819SOllivier Robert 
522ba371819SOllivier Robert   /*
523ba371819SOllivier Robert    * If NeoClock4X is not synchronized to a radio clock
524ba371819SOllivier Robert    * check if we're allowed to synchronize with the quartz
525ba371819SOllivier Robert    * clock.
526ba371819SOllivier Robert    */
527ba371819SOllivier Robert   up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE];
528ba371819SOllivier Robert   if(0==(pp->sloppyclockflag & CLK_FLAG2))
529ba371819SOllivier Robert     {
530ba371819SOllivier Robert       if('A' != up->timesource)
531ba371819SOllivier Robert 	{
532ba371819SOllivier Robert 	  /* not allowed to sync with quartz clock */
533ba371819SOllivier Robert 	  if(0==(pp->sloppyclockflag & CLK_FLAG1))
534ba371819SOllivier Robert 	    {
535ba371819SOllivier Robert 	      refclock_report(peer, CEVNT_BADTIME);
536ba371819SOllivier Robert 	      pp->leap = LEAP_NOTINSYNC;
537ba371819SOllivier Robert 	      return;
538ba371819SOllivier Robert 	    }
539ba371819SOllivier Robert 	}
540ba371819SOllivier Robert     }
541ba371819SOllivier Robert 
542ba371819SOllivier Robert   /* this should only used when first install is done */
543ba371819SOllivier Robert   if(pp->sloppyclockflag & CLK_FLAG4)
544ba371819SOllivier Robert     {
545ba371819SOllivier Robert       msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s",
546ba371819SOllivier Robert 	      up->unit, pp->a_lastcode);
547ba371819SOllivier Robert     }
548ba371819SOllivier Robert 
549ba371819SOllivier Robert   /* 123456789012345678901234567890123456789012345 */
550ba371819SOllivier Robert   /* S/N123456DCF1004021010001202ASX1213CR\r\n */
551ba371819SOllivier Robert 
552ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2);
553ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2);
554ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2);
555ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2);
556ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2);
557ba371819SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2);
5589c2daa00SOllivier Robert   neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2);
559ea906c41SOllivier Robert #if defined(NTP_PRE_420)
560ea906c41SOllivier Robert   pp->msec = dsec * 10; /* convert 1/100s from neoclock to real miliseconds */
561ea906c41SOllivier Robert #else
562ea906c41SOllivier Robert   pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */
563ea906c41SOllivier Robert #endif
564ba371819SOllivier Robert 
565ba371819SOllivier Robert   memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3);
566ba371819SOllivier Robert   up->radiosignal[3] = 0;
567ba371819SOllivier Robert   memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6);
568ba371819SOllivier Robert   up->serial[6] = 0;
569ba371819SOllivier Robert   up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS];
570ba371819SOllivier Robert   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2);
571ba371819SOllivier Robert   neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2);
572ba371819SOllivier Robert 
573ba371819SOllivier Robert   /*
574ba371819SOllivier Robert     Validate received values at least enough to prevent internal
575ba371819SOllivier Robert     array-bounds problems, etc.
576ba371819SOllivier Robert   */
577ba371819SOllivier Robert   if((pp->hour < 0) || (pp->hour > 23) ||
578ba371819SOllivier Robert      (pp->minute < 0) || (pp->minute > 59) ||
579ba371819SOllivier Robert      (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
580ba371819SOllivier Robert      (day < 1) || (day > 31) ||
581ba371819SOllivier Robert      (month < 1) || (month > 12) ||
582ba371819SOllivier Robert      (pp->year < 0) || (pp->year > 99)) {
583ba371819SOllivier Robert     /* Data out of range. */
584ba371819SOllivier Robert     NLOG(NLOG_CLOCKEVENT)
585ba371819SOllivier Robert       msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s",
586ba371819SOllivier Robert 	      up->unit, pp->a_lastcode);
587ba371819SOllivier Robert     refclock_report(peer, CEVNT_BADDATE);
588ba371819SOllivier Robert     return;
589ba371819SOllivier Robert   }
590ba371819SOllivier Robert 
5919c2daa00SOllivier Robert   /* Year-2000 check not needed anymore. Same problem
5929c2daa00SOllivier Robert    * will arise at 2099 but what should we do...?
5939c2daa00SOllivier Robert    *
5949c2daa00SOllivier Robert    * wrap 2-digit date into 4-digit
5959c2daa00SOllivier Robert    *
5969c2daa00SOllivier Robert    * if(pp->year < YEAR_PIVOT)
5979c2daa00SOllivier Robert    * {
5989c2daa00SOllivier Robert    *   pp->year += 100;
5999c2daa00SOllivier Robert    * }
6009c2daa00SOllivier Robert   */
6019c2daa00SOllivier Robert   pp->year += 2000;
602ba371819SOllivier Robert 
6039c2daa00SOllivier Robert   /* adjust NeoClock4X local time to UTC */
604ba371819SOllivier Robert   calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
605ba371819SOllivier Robert   calc_utc -= 3600;
6069c2daa00SOllivier Robert   /* adjust NeoClock4X daylight saving time if needed */
607ba371819SOllivier Robert   if('S' == up->dststatus)
608ba371819SOllivier Robert     calc_utc -= 3600;
609ba371819SOllivier Robert   neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
610ba371819SOllivier Robert 
611ba371819SOllivier Robert   /*
612ba371819SOllivier Robert     some preparations
613ba371819SOllivier Robert   */
614ba371819SOllivier Robert   pp->day = ymd2yd(pp->year, month, day);
615ba371819SOllivier Robert   pp->leap = 0;
616ba371819SOllivier Robert 
617ba371819SOllivier Robert   if(pp->sloppyclockflag & CLK_FLAG4)
618ba371819SOllivier Robert     {
6199c2daa00SOllivier Robert       msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld",
620ba371819SOllivier Robert 	      up->unit,
621ba371819SOllivier Robert 	      pp->year, month, day,
622ea906c41SOllivier Robert 	      pp->hour, pp->minute, pp->second,
623ea906c41SOllivier Robert #if defined(NTP_PRE_420)
624ea906c41SOllivier Robert               pp->msec
625ea906c41SOllivier Robert #else
626ea906c41SOllivier Robert               pp->nsec/NSEC_TO_MILLI
627ea906c41SOllivier Robert #endif
628ea906c41SOllivier Robert               );
629ba371819SOllivier Robert     }
630ba371819SOllivier Robert 
631ba371819SOllivier Robert   up->utc_year   = pp->year;
632ba371819SOllivier Robert   up->utc_month  = month;
633ba371819SOllivier Robert   up->utc_day    = day;
634ba371819SOllivier Robert   up->utc_hour   = pp->hour;
635ba371819SOllivier Robert   up->utc_minute = pp->minute;
636ba371819SOllivier Robert   up->utc_second = pp->second;
637ea906c41SOllivier Robert #if defined(NTP_PRE_420)
638ea906c41SOllivier Robert   up->utc_msec   = pp->msec;
639ea906c41SOllivier Robert #else
640ea906c41SOllivier Robert   up->utc_msec   = pp->nsec/NSEC_TO_MILLI;
641ea906c41SOllivier Robert #endif
642ba371819SOllivier Robert 
643ba371819SOllivier Robert   if(!refclock_process(pp))
644ba371819SOllivier Robert     {
645ba371819SOllivier Robert       NLOG(NLOG_CLOCKEVENT)
646ba371819SOllivier Robert 	msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit);
647ba371819SOllivier Robert       refclock_report(peer, CEVNT_FAULT);
648ba371819SOllivier Robert       return;
649ba371819SOllivier Robert     }
650ba371819SOllivier Robert   refclock_receive(peer);
651ba371819SOllivier Robert 
6529c2daa00SOllivier Robert   /* report good status */
6539c2daa00SOllivier Robert   refclock_report(peer, CEVNT_NOMINAL);
6549c2daa00SOllivier Robert 
655ba371819SOllivier Robert   record_clock_stats(&peer->srcadr, pp->a_lastcode);
656ba371819SOllivier Robert }
657ba371819SOllivier Robert 
658ba371819SOllivier Robert static void
659ba371819SOllivier Robert neoclock4x_poll(int unit,
660ba371819SOllivier Robert 		struct peer *peer)
661ba371819SOllivier Robert {
662ba371819SOllivier Robert   struct neoclock4x_unit *up;
663ba371819SOllivier Robert   struct refclockproc *pp;
664ba371819SOllivier Robert 
665ba371819SOllivier Robert   pp = peer->procptr;
6662b15cb3dSCy Schubert   up = pp->unitptr;
667ba371819SOllivier Robert 
668ba371819SOllivier Robert   pp->polls++;
669ba371819SOllivier Robert   up->recvnow = 1;
670ba371819SOllivier Robert }
671ba371819SOllivier Robert 
672ba371819SOllivier Robert static void
673ba371819SOllivier Robert neoclock4x_control(int unit,
6742b15cb3dSCy Schubert 		   const struct refclockstat *in,
675ba371819SOllivier Robert 		   struct refclockstat *out,
676ba371819SOllivier Robert 		   struct peer *peer)
677ba371819SOllivier Robert {
678ba371819SOllivier Robert   struct neoclock4x_unit *up;
679ba371819SOllivier Robert   struct refclockproc *pp;
680ba371819SOllivier Robert 
681ba371819SOllivier Robert   if(NULL == peer)
682ba371819SOllivier Robert     {
683ba371819SOllivier Robert       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
684ba371819SOllivier Robert       return;
685ba371819SOllivier Robert     }
686ba371819SOllivier Robert 
687ba371819SOllivier Robert   pp = peer->procptr;
688ba371819SOllivier Robert   if(NULL == pp)
689ba371819SOllivier Robert     {
690ba371819SOllivier Robert       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
691ba371819SOllivier Robert       return;
692ba371819SOllivier Robert     }
693ba371819SOllivier Robert 
6942b15cb3dSCy Schubert   up = pp->unitptr;
695ba371819SOllivier Robert   if(NULL == up)
696ba371819SOllivier Robert     {
697ba371819SOllivier Robert       msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
698ba371819SOllivier Robert       return;
699ba371819SOllivier Robert     }
700ba371819SOllivier Robert 
701ba371819SOllivier Robert   if(NULL != in)
702ba371819SOllivier Robert     {
703ba371819SOllivier Robert       /* check to see if a user supplied time offset is given */
704ba371819SOllivier Robert       if(in->haveflags & CLK_HAVETIME1)
705ba371819SOllivier Robert 	{
706ba371819SOllivier Robert 	  pp->fudgetime1 = in->fudgetime1;
707ba371819SOllivier Robert 	  NLOG(NLOG_CLOCKINFO)
708ba371819SOllivier Robert 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
709ba371819SOllivier Robert 		    unit, pp->fudgetime1);
710ba371819SOllivier Robert 	}
711ba371819SOllivier Robert 
712ba371819SOllivier Robert       /* notify */
713ba371819SOllivier Robert       if(pp->sloppyclockflag & CLK_FLAG1)
714ba371819SOllivier Robert 	{
715ba371819SOllivier Robert 	  NLOG(NLOG_CLOCKINFO)
716ba371819SOllivier Robert 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
717ba371819SOllivier Robert 	}
718ba371819SOllivier Robert       else
719ba371819SOllivier Robert 	{
720ba371819SOllivier Robert 	  NLOG(NLOG_CLOCKINFO)
721ba371819SOllivier Robert 	    msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
722ba371819SOllivier Robert 	}
723ba371819SOllivier Robert     }
724ba371819SOllivier Robert 
725ba371819SOllivier Robert   if(NULL != out)
726ba371819SOllivier Robert     {
727ba371819SOllivier Robert       char *tt;
728ba371819SOllivier Robert       char tmpbuf[80];
729ba371819SOllivier Robert 
730ba371819SOllivier Robert       out->kv_list = (struct ctl_var *)0;
731ba371819SOllivier Robert       out->type    = REFCLK_NEOCLOCK4X;
732ba371819SOllivier Robert 
7339c2daa00SOllivier Robert       snprintf(tmpbuf, sizeof(tmpbuf)-1,
7349c2daa00SOllivier Robert 	       "%04d-%02d-%02d %02d:%02d:%02d.%03d",
735ba371819SOllivier Robert 	       up->utc_year, up->utc_month, up->utc_day,
736ba371819SOllivier Robert 	       up->utc_hour, up->utc_minute, up->utc_second,
737ba371819SOllivier Robert 	       up->utc_msec);
7389c2daa00SOllivier Robert       tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF);
7399c2daa00SOllivier Robert       snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf);
740ba371819SOllivier Robert 
7419c2daa00SOllivier Robert       tt = add_var(&out->kv_list, 40, RO|DEF);
7429c2daa00SOllivier Robert       snprintf(tt, 39, "radiosignal=\"%s\"", up->radiosignal);
7439c2daa00SOllivier Robert       tt = add_var(&out->kv_list, 40, RO|DEF);
7449c2daa00SOllivier Robert       snprintf(tt, 39, "antenna1=\"%d\"", up->antenna1);
7459c2daa00SOllivier Robert       tt = add_var(&out->kv_list, 40, RO|DEF);
7469c2daa00SOllivier Robert       snprintf(tt, 39, "antenna2=\"%d\"", up->antenna2);
7479c2daa00SOllivier Robert       tt = add_var(&out->kv_list, 40, RO|DEF);
748ba371819SOllivier Robert       if('A' == up->timesource)
7499c2daa00SOllivier Robert 	snprintf(tt, 39, "timesource=\"radio\"");
750ba371819SOllivier Robert       else if('C' == up->timesource)
7519c2daa00SOllivier Robert 	snprintf(tt, 39, "timesource=\"quartz\"");
752ba371819SOllivier Robert       else
7539c2daa00SOllivier Robert 	snprintf(tt, 39, "timesource=\"unknown\"");
7549c2daa00SOllivier Robert       tt = add_var(&out->kv_list, 40, RO|DEF);
755ba371819SOllivier Robert       if('I' == up->quarzstatus)
7569c2daa00SOllivier Robert 	snprintf(tt, 39, "quartzstatus=\"synchronized\"");
757ba371819SOllivier Robert       else if('X' == up->quarzstatus)
7589c2daa00SOllivier Robert         snprintf(tt, 39, "quartzstatus=\"not synchronized\"");
759ba371819SOllivier Robert       else
7609c2daa00SOllivier Robert 	snprintf(tt, 39, "quartzstatus=\"unknown\"");
7619c2daa00SOllivier Robert       tt = add_var(&out->kv_list, 40, RO|DEF);
762ba371819SOllivier Robert       if('S' == up->dststatus)
7639c2daa00SOllivier Robert         snprintf(tt, 39, "dststatus=\"summer\"");
764ba371819SOllivier Robert       else if('W' == up->dststatus)
7659c2daa00SOllivier Robert         snprintf(tt, 39, "dststatus=\"winter\"");
766ba371819SOllivier Robert       else
7679c2daa00SOllivier Robert         snprintf(tt, 39, "dststatus=\"unknown\"");
7689c2daa00SOllivier Robert       tt = add_var(&out->kv_list, 80, RO|DEF);
7699c2daa00SOllivier Robert       snprintf(tt, 79, "firmware=\"%s\"", up->firmware);
7709c2daa00SOllivier Robert       tt = add_var(&out->kv_list, 40, RO|DEF);
7719c2daa00SOllivier Robert       snprintf(tt, 39, "firmwaretag=\"%c\"", up->firmwaretag);
7729c2daa00SOllivier Robert       tt = add_var(&out->kv_list, 80, RO|DEF);
7739c2daa00SOllivier Robert       snprintf(tt, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION);
7749c2daa00SOllivier Robert       tt = add_var(&out->kv_list, 80, RO|DEF);
7759c2daa00SOllivier Robert       snprintf(tt, 79, "serialnumber=\"%s\"", up->serial);
776ba371819SOllivier Robert     }
777ba371819SOllivier Robert }
778ba371819SOllivier Robert 
7799c2daa00SOllivier Robert static int
7809c2daa00SOllivier Robert neol_hexatoi_len(const char str[],
781ba371819SOllivier Robert 		 int *result,
782ba371819SOllivier Robert 		 int maxlen)
783ba371819SOllivier Robert {
784ba371819SOllivier Robert   int hexdigit;
785ba371819SOllivier Robert   int i;
786ba371819SOllivier Robert   int n = 0;
787ba371819SOllivier Robert 
7882b15cb3dSCy Schubert   for(i=0; isxdigit((unsigned char)str[i]) && i < maxlen; i++)
789ba371819SOllivier Robert     {
7902b15cb3dSCy Schubert       hexdigit = isdigit((unsigned char)str[i]) ? toupper((unsigned char)str[i]) - '0' : toupper((unsigned char)str[i]) - 'A' + 10;
791ba371819SOllivier Robert       n = 16 * n + hexdigit;
792ba371819SOllivier Robert     }
793ba371819SOllivier Robert   *result = n;
794ba371819SOllivier Robert   return (n);
795ba371819SOllivier Robert }
796ba371819SOllivier Robert 
7979c2daa00SOllivier Robert static int
7989c2daa00SOllivier Robert neol_atoi_len(const char str[],
799ba371819SOllivier Robert 		  int *result,
800ba371819SOllivier Robert 		  int maxlen)
801ba371819SOllivier Robert {
802ba371819SOllivier Robert   int digit;
803ba371819SOllivier Robert   int i;
804ba371819SOllivier Robert   int n = 0;
805ba371819SOllivier Robert 
8062b15cb3dSCy Schubert   for(i=0; isdigit((unsigned char)str[i]) && i < maxlen; i++)
807ba371819SOllivier Robert     {
808ba371819SOllivier Robert       digit = str[i] - '0';
809ba371819SOllivier Robert       n = 10 * n + digit;
810ba371819SOllivier Robert     }
811ba371819SOllivier Robert   *result = n;
812ba371819SOllivier Robert   return (n);
813ba371819SOllivier Robert }
814ba371819SOllivier Robert 
815ba371819SOllivier Robert /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
816ba371819SOllivier Robert  * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
817ba371819SOllivier Robert  * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
818ba371819SOllivier Robert  *
819ba371819SOllivier Robert  * [For the Julian calendar (which was used in Russia before 1917,
820ba371819SOllivier Robert  * Britain & colonies before 1752, anywhere else before 1582,
821ba371819SOllivier Robert  * and is still in use by some communities) leave out the
822ba371819SOllivier Robert  * -year/100+year/400 terms, and add 10.]
823ba371819SOllivier Robert  *
824ba371819SOllivier Robert  * This algorithm was first published by Gauss (I think).
825ba371819SOllivier Robert  *
826ba371819SOllivier Robert  * WARNING: this function will overflow on 2106-02-07 06:28:16 on
827ba371819SOllivier Robert  * machines were long is 32-bit! (However, as time_t is signed, we
828ba371819SOllivier Robert  * will already get problems at other places on 2038-01-19 03:14:08)
829ba371819SOllivier Robert  */
8309c2daa00SOllivier Robert static unsigned long
8319c2daa00SOllivier Robert neol_mktime(int year,
832ba371819SOllivier Robert 	    int mon,
833ba371819SOllivier Robert 	    int day,
834ba371819SOllivier Robert 	    int hour,
835ba371819SOllivier Robert 	    int min,
836ba371819SOllivier Robert 	    int sec)
837ba371819SOllivier Robert {
838ba371819SOllivier Robert   if (0 >= (int) (mon -= 2)) {    /* 1..12 . 11,12,1..10 */
839ba371819SOllivier Robert     mon += 12;      /* Puts Feb last since it has leap day */
840ba371819SOllivier Robert     year -= 1;
841ba371819SOllivier Robert   }
842ba371819SOllivier Robert   return (((
843ba371819SOllivier Robert             (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
844ba371819SOllivier Robert             year*365 - 719499
845ba371819SOllivier Robert             )*24 + hour /* now have hours */
846ba371819SOllivier Robert            )*60 + min /* now have minutes */
847ba371819SOllivier Robert           )*60 + sec; /* finally seconds */
848ba371819SOllivier Robert }
849ba371819SOllivier Robert 
8509c2daa00SOllivier Robert static void
8519c2daa00SOllivier Robert neol_localtime(unsigned long utc,
852ba371819SOllivier Robert 	       int* year,
853ba371819SOllivier Robert 	       int* month,
854ba371819SOllivier Robert 	       int* day,
855ba371819SOllivier Robert 	       int* hour,
8569c2daa00SOllivier Robert 	       int* min,
8579c2daa00SOllivier Robert 	       int* sec)
858ba371819SOllivier Robert {
8599c2daa00SOllivier Robert   *sec = utc % 60;
8609c2daa00SOllivier Robert   utc /= 60;
8619c2daa00SOllivier Robert   *min = utc % 60;
8629c2daa00SOllivier Robert   utc /= 60;
8639c2daa00SOllivier Robert   *hour = utc % 24;
8649c2daa00SOllivier Robert   utc /= 24;
865ba371819SOllivier Robert 
866ba371819SOllivier Robert   /*             JDN Date 1/1/1970 */
8679c2daa00SOllivier Robert   neol_jdn_to_ymd(utc + 2440588L, year, month, day);
868ba371819SOllivier Robert }
869ba371819SOllivier Robert 
8709c2daa00SOllivier Robert static void
8719c2daa00SOllivier Robert neol_jdn_to_ymd(unsigned long jdn,
872ba371819SOllivier Robert 		int *yy,
873ba371819SOllivier Robert 		int *mm,
874ba371819SOllivier Robert 		int *dd)
875ba371819SOllivier Robert {
876ba371819SOllivier Robert   unsigned long x, z, m, d, y;
877ba371819SOllivier Robert   unsigned long daysPer400Years = 146097UL;
878ba371819SOllivier Robert   unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
879ba371819SOllivier Robert 
880ba371819SOllivier Robert   x = jdn + 68569UL;
881ba371819SOllivier Robert   z = 4UL * x / daysPer400Years;
882ba371819SOllivier Robert   x = x - (daysPer400Years * z + 3UL) / 4UL;
883ba371819SOllivier Robert   y = 4000UL * (x + 1) / fudgedDaysPer4000Years;
884ba371819SOllivier Robert   x = x - 1461UL * y / 4UL + 31UL;
885ba371819SOllivier Robert   m = 80UL * x / 2447UL;
886ba371819SOllivier Robert   d = x - 2447UL * m / 80UL;
887ba371819SOllivier Robert   x = m / 11UL;
888ba371819SOllivier Robert   m = m + 2UL - 12UL * x;
889ba371819SOllivier Robert   y = 100UL * (z - 49UL) + y + x;
890ba371819SOllivier Robert 
891ba371819SOllivier Robert   *yy = (int)y;
892ba371819SOllivier Robert   *mm = (int)m;
893ba371819SOllivier Robert   *dd = (int)d;
894ba371819SOllivier Robert }
895ba371819SOllivier Robert 
8969c2daa00SOllivier Robert #if !defined(NEOCLOCK4X_FIRMWARE)
897ba371819SOllivier Robert static int
898ba371819SOllivier Robert neol_query_firmware(int fd,
899ba371819SOllivier Robert 		    int unit,
900ba371819SOllivier Robert 		    char *firmware,
9012b15cb3dSCy Schubert 		    size_t maxlen)
902ba371819SOllivier Robert {
9039c2daa00SOllivier Robert   char tmpbuf[256];
9042b15cb3dSCy Schubert   size_t len;
905ba371819SOllivier Robert   int lastsearch;
906ba371819SOllivier Robert   unsigned char c;
907ba371819SOllivier Robert   int last_c_was_crlf;
908ba371819SOllivier Robert   int last_crlf_conv_len;
909ba371819SOllivier Robert   int init;
9109c2daa00SOllivier Robert   int read_errors;
911ba371819SOllivier Robert   int flag = 0;
9129c2daa00SOllivier Robert   int chars_read;
913ba371819SOllivier Robert 
914ba371819SOllivier Robert   /* wait a little bit */
9159c2daa00SOllivier Robert   sleep(1);
916ba371819SOllivier Robert   if(-1 != write(fd, "V", 1))
917ba371819SOllivier Robert     {
918ba371819SOllivier Robert       /* wait a little bit */
9199c2daa00SOllivier Robert       sleep(1);
920ba371819SOllivier Robert       memset(tmpbuf, 0x00, sizeof(tmpbuf));
921ba371819SOllivier Robert 
922ba371819SOllivier Robert       len = 0;
923ba371819SOllivier Robert       lastsearch = 0;
924ba371819SOllivier Robert       last_c_was_crlf = 0;
925ba371819SOllivier Robert       last_crlf_conv_len = 0;
926ba371819SOllivier Robert       init = 1;
9279c2daa00SOllivier Robert       read_errors = 0;
9289c2daa00SOllivier Robert       chars_read = 0;
929ba371819SOllivier Robert       for(;;)
930ba371819SOllivier Robert 	{
9319c2daa00SOllivier Robert 	  if(read_errors > 5)
932ba371819SOllivier Robert 	    {
933ba371819SOllivier Robert 	      msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
9342b15cb3dSCy Schubert 	      strlcpy(tmpbuf, "unknown due to timeout", sizeof(tmpbuf));
935ba371819SOllivier Robert 	      break;
936ba371819SOllivier Robert 	    }
9379c2daa00SOllivier Robert           if(chars_read > 500)
9389c2daa00SOllivier Robert             {
9399c2daa00SOllivier Robert 	      msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit);
9402b15cb3dSCy Schubert 	      strlcpy(tmpbuf, "unknown due to garbage input", sizeof(tmpbuf));
9419c2daa00SOllivier Robert 	      break;
9429c2daa00SOllivier Robert             }
943ba371819SOllivier Robert 	  if(-1 == read(fd, &c, 1))
944ba371819SOllivier Robert 	    {
9459c2daa00SOllivier Robert               if(EAGAIN != errno)
9469c2daa00SOllivier Robert                 {
9472b15cb3dSCy Schubert                   msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %m", unit);
9489c2daa00SOllivier Robert                   read_errors++;
9499c2daa00SOllivier Robert                 }
9509c2daa00SOllivier Robert               else
9519c2daa00SOllivier Robert                 {
9529c2daa00SOllivier Robert                   sleep(1);
9539c2daa00SOllivier Robert                 }
954ba371819SOllivier Robert 	      continue;
955ba371819SOllivier Robert 	    }
9569c2daa00SOllivier Robert           else
9579c2daa00SOllivier Robert             {
9589c2daa00SOllivier Robert               chars_read++;
9599c2daa00SOllivier Robert             }
9609c2daa00SOllivier Robert 
961ba371819SOllivier Robert 	  if(init)
962ba371819SOllivier Robert 	    {
963ba371819SOllivier Robert 	      if(0xA9 != c) /* wait for (c) char in input stream */
964ba371819SOllivier Robert 		continue;
965ba371819SOllivier Robert 
9662b15cb3dSCy Schubert 	      strlcpy(tmpbuf, "(c)", sizeof(tmpbuf));
967ba371819SOllivier Robert 	      len = 3;
968ba371819SOllivier Robert 	      init = 0;
969ba371819SOllivier Robert 	      continue;
970ba371819SOllivier Robert 	    }
971ba371819SOllivier Robert 
9729c2daa00SOllivier Robert #if 0
9739c2daa00SOllivier Robert 	  msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
9749c2daa00SOllivier Robert #endif
9759c2daa00SOllivier Robert 
976ba371819SOllivier Robert 	  if(0x0A == c || 0x0D == c)
977ba371819SOllivier Robert 	    {
978ba371819SOllivier Robert 	      if(last_c_was_crlf)
979ba371819SOllivier Robert 		{
980ba371819SOllivier Robert 		  char *ptr;
981ba371819SOllivier Robert 		  ptr = strstr(&tmpbuf[lastsearch], "S/N");
982ba371819SOllivier Robert 		  if(NULL != ptr)
983ba371819SOllivier Robert 		    {
984ba371819SOllivier Robert 		      tmpbuf[last_crlf_conv_len] = 0;
985ba371819SOllivier Robert 		      flag = 1;
986ba371819SOllivier Robert 		      break;
987ba371819SOllivier Robert 		    }
988ba371819SOllivier Robert 		  /* convert \n to / */
989ba371819SOllivier Robert 		  last_crlf_conv_len = len;
990ba371819SOllivier Robert 		  tmpbuf[len++] = ' ';
991ba371819SOllivier Robert 		  tmpbuf[len++] = '/';
992ba371819SOllivier Robert 		  tmpbuf[len++] = ' ';
993ba371819SOllivier Robert 		  lastsearch = len;
994ba371819SOllivier Robert 		}
995ba371819SOllivier Robert 	      last_c_was_crlf = 1;
996ba371819SOllivier Robert 	    }
997ba371819SOllivier Robert 	  else
998ba371819SOllivier Robert 	    {
999ba371819SOllivier Robert 	      last_c_was_crlf = 0;
1000ba371819SOllivier Robert 	      if(0x00 != c)
10019c2daa00SOllivier Robert 		tmpbuf[len++] = (char) c;
1002ba371819SOllivier Robert 	    }
1003ba371819SOllivier Robert 	  tmpbuf[len] = '\0';
1004ba371819SOllivier Robert 	  if (len > sizeof(tmpbuf)-5)
1005ba371819SOllivier Robert 	    break;
1006ba371819SOllivier Robert 	}
1007ba371819SOllivier Robert     }
1008ba371819SOllivier Robert   else
1009ba371819SOllivier Robert     {
1010ba371819SOllivier Robert       msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit);
10112b15cb3dSCy Schubert       strlcpy(tmpbuf, "unknown error", sizeof(tmpbuf));
1012ba371819SOllivier Robert     }
10132b15cb3dSCy Schubert   if (strlcpy(firmware, tmpbuf, maxlen) >= maxlen)
10142b15cb3dSCy Schubert     strlcpy(firmware, "buffer too small", maxlen);
1015ba371819SOllivier Robert 
1016ba371819SOllivier Robert   if(flag)
1017ba371819SOllivier Robert     {
1018ba371819SOllivier Robert       NLOG(NLOG_CLOCKINFO)
1019ba371819SOllivier Robert 	msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
10202b15cb3dSCy Schubert 
10212b15cb3dSCy Schubert       if(strstr(firmware, "/R2"))
10222b15cb3dSCy Schubert 	{
10232b15cb3dSCy Schubert 	  msyslog(LOG_INFO, "NeoClock4X(%d): Your NeoClock4X uses the new R2 firmware release. Please note the changed LED behaviour.", unit);
10242b15cb3dSCy Schubert 	}
10252b15cb3dSCy Schubert 
1026ba371819SOllivier Robert     }
1027ba371819SOllivier Robert 
1028ba371819SOllivier Robert   return (flag);
1029ba371819SOllivier Robert }
1030ba371819SOllivier Robert 
10319c2daa00SOllivier Robert static int
10329c2daa00SOllivier Robert neol_check_firmware(int unit,
10339c2daa00SOllivier Robert                     const char *firmware,
10349c2daa00SOllivier Robert                     char *firmwaretag)
10359c2daa00SOllivier Robert {
10369c2daa00SOllivier Robert   char *ptr;
10379c2daa00SOllivier Robert 
10389c2daa00SOllivier Robert   *firmwaretag = '?';
10399c2daa00SOllivier Robert   ptr = strstr(firmware, "NDF:");
10409c2daa00SOllivier Robert   if(NULL != ptr)
10419c2daa00SOllivier Robert     {
10429c2daa00SOllivier Robert       if((strlen(firmware) - strlen(ptr)) >= 7)
10439c2daa00SOllivier Robert         {
10449c2daa00SOllivier Robert           if(':' == *(ptr+5) && '*' == *(ptr+6))
10459c2daa00SOllivier Robert             *firmwaretag = *(ptr+4);
10469c2daa00SOllivier Robert         }
10479c2daa00SOllivier Robert     }
10489c2daa00SOllivier Robert 
10499c2daa00SOllivier Robert   if('A' != *firmwaretag)
10509c2daa00SOllivier Robert     {
10519c2daa00SOllivier Robert       msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag);
10529c2daa00SOllivier Robert       return (0);
10539c2daa00SOllivier Robert     }
10549c2daa00SOllivier Robert 
10559c2daa00SOllivier Robert   return (1);
10569c2daa00SOllivier Robert }
10579c2daa00SOllivier Robert #endif
10589c2daa00SOllivier Robert 
1059ba371819SOllivier Robert #else
1060*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT
1061ba371819SOllivier Robert #endif /* REFCLOCK */
1062ba371819SOllivier Robert 
1063ba371819SOllivier Robert /*
1064ba371819SOllivier Robert  * History:
1065ba371819SOllivier Robert  * refclock_neoclock4x.c
1066ba371819SOllivier Robert  *
1067ba371819SOllivier Robert  * 2002/04/27 cjh
1068ba371819SOllivier Robert  * Revision 1.0  first release
1069ba371819SOllivier Robert  *
10709c2daa00SOllivier Robert  * 2002/07/15 cjh
1071ba371819SOllivier Robert  * preparing for bitkeeper reposity
1072ba371819SOllivier Robert  *
10739c2daa00SOllivier Robert  * 2002/09/09 cjh
10749c2daa00SOllivier Robert  * Revision 1.1
10759c2daa00SOllivier Robert  * - don't assume sprintf returns an int anymore
10769c2daa00SOllivier Robert  * - change the way the firmware version is read
10779c2daa00SOllivier Robert  * - some customers would like to put a device called
10789c2daa00SOllivier Robert  *   data diode to the NeoClock4X device to disable
10799c2daa00SOllivier Robert  *   the write line. We need to now the firmware
10809c2daa00SOllivier Robert  *   version even in this case. We made a compile time
10819c2daa00SOllivier Robert  *   definition in this case. The code was previously
10829c2daa00SOllivier Robert  *   only available on request.
10839c2daa00SOllivier Robert  *
10849c2daa00SOllivier Robert  * 2003/01/08 cjh
10859c2daa00SOllivier Robert  * Revision 1.11
10869c2daa00SOllivier Robert  * - changing xprinf to xnprinf to avoid buffer overflows
10879c2daa00SOllivier Robert  * - change some logic
10889c2daa00SOllivier Robert  * - fixed memory leaks if drivers can't initialize
10899c2daa00SOllivier Robert  *
10909c2daa00SOllivier Robert  * 2003/01/10 cjh
10919c2daa00SOllivier Robert  * Revision 1.12
10929c2daa00SOllivier Robert  * - replaced ldiv
10939c2daa00SOllivier Robert  * - add code to support FreeBSD
10949c2daa00SOllivier Robert  *
10959c2daa00SOllivier Robert  * 2003/07/07 cjh
10969c2daa00SOllivier Robert  * Revision 1.13
10979c2daa00SOllivier Robert  * - fix reporting of clock status
10989c2daa00SOllivier Robert  *   changes. previously a bad clock
10999c2daa00SOllivier Robert  *   status was never reset.
1100ea906c41SOllivier Robert  *
1101ea906c41SOllivier Robert  * 2004/04/07 cjh
1102ea906c41SOllivier Robert  * Revision 1.14
1103ea906c41SOllivier Robert  * - open serial port in a way
1104ea906c41SOllivier Robert  *   AIX and some other OS can
1105ea906c41SOllivier Robert  *   handle much better
1106ea906c41SOllivier Robert  *
1107ea906c41SOllivier Robert  * 2006/01/11 cjh
1108ea906c41SOllivier Robert  * Revision 1.15
1109ea906c41SOllivier Robert  * - remove some unsued #ifdefs
1110ea906c41SOllivier Robert  * - fix nsec calculation, closes #499
1111ea906c41SOllivier Robert  *
11122b15cb3dSCy Schubert  * 2009/12/04 cjh
11132b15cb3dSCy Schubert  * Revision 1.16
11142b15cb3dSCy Schubert  * - change license to ntp COPYRIGHT notice. This should allow Debian
11152b15cb3dSCy Schubert  *   to add this refclock driver in further releases.
11162b15cb3dSCy Schubert  * - detect R2 hardware
11172b15cb3dSCy Schubert  *
1118ba371819SOllivier Robert  */
11192b15cb3dSCy Schubert 
11202b15cb3dSCy Schubert 
11212b15cb3dSCy Schubert 
11222b15cb3dSCy Schubert 
11232b15cb3dSCy Schubert 
11242b15cb3dSCy Schubert 
1125