/src/ntp-dev/ntpd/refclock_neoclock4x.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * |
3 | | * Refclock_neoclock4x.c |
4 | | * - NeoClock4X driver for DCF77 or FIA Timecode |
5 | | * |
6 | | * Date: 2009-12-04 v1.16 |
7 | | * |
8 | | * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir |
9 | | * for details about the NeoClock4X device |
10 | | * |
11 | | */ |
12 | | |
13 | | #ifdef HAVE_CONFIG_H |
14 | | # include "config.h" |
15 | | #endif |
16 | | |
17 | | #if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X)) |
18 | | |
19 | | #include <unistd.h> |
20 | | #include <sys/time.h> |
21 | | #include <sys/types.h> |
22 | | #include <termios.h> |
23 | | #include <sys/ioctl.h> |
24 | | #include <ctype.h> |
25 | | |
26 | | #include "ntpd.h" |
27 | | #include "ntp_io.h" |
28 | | #include "ntp_control.h" |
29 | | #include "ntp_refclock.h" |
30 | | #include "ntp_unixtime.h" |
31 | | #include "ntp_stdlib.h" |
32 | | |
33 | | #if defined HAVE_SYS_MODEM_H |
34 | | # include <sys/modem.h> |
35 | | # ifndef __QNXNTO__ |
36 | | # define TIOCMSET MCSETA |
37 | | # define TIOCMGET MCGETA |
38 | | # define TIOCM_RTS MRTS |
39 | | # endif |
40 | | #endif |
41 | | |
42 | | #ifdef HAVE_TERMIOS_H |
43 | | # ifdef TERMIOS_NEEDS__SVID3 |
44 | | # define _SVID3 |
45 | | # endif |
46 | | # include <termios.h> |
47 | | # ifdef TERMIOS_NEEDS__SVID3 |
48 | | # undef _SVID3 |
49 | | # endif |
50 | | #endif |
51 | | |
52 | | #ifdef HAVE_SYS_IOCTL_H |
53 | | # include <sys/ioctl.h> |
54 | | #endif |
55 | | |
56 | | /* |
57 | | * NTP version 4.20 change the pp->msec field to pp->nsec. |
58 | | * To allow to support older ntp versions with this sourcefile |
59 | | * you can define NTP_PRE_420 to allow this driver to compile |
60 | | * with ntp version back to 4.1.2. |
61 | | * |
62 | | */ |
63 | | #if 0 |
64 | | #define NTP_PRE_420 |
65 | | #endif |
66 | | |
67 | | /* |
68 | | * If you want the driver for whatever reason to not use |
69 | | * the TX line to send anything to your NeoClock4X |
70 | | * device you must tell the NTP refclock driver which |
71 | | * firmware you NeoClock4X device uses. |
72 | | * |
73 | | * If you want to enable this feature change the "#if 0" |
74 | | * line to "#if 1" and make sure that the defined firmware |
75 | | * matches the firmware off your NeoClock4X receiver! |
76 | | * |
77 | | */ |
78 | | |
79 | | #if 0 |
80 | | #define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A |
81 | | #endif |
82 | | |
83 | | /* at this time only firmware version A is known */ |
84 | | #define NEOCLOCK4X_FIRMWARE_VERSION_A 'A' |
85 | | |
86 | 0 | #define NEOCLOCK4X_TIMECODELEN 37 |
87 | | |
88 | 0 | #define NEOCLOCK4X_OFFSET_SERIAL 3 |
89 | 0 | #define NEOCLOCK4X_OFFSET_RADIOSIGNAL 9 |
90 | 0 | #define NEOCLOCK4X_OFFSET_DAY 12 |
91 | 0 | #define NEOCLOCK4X_OFFSET_MONTH 14 |
92 | 0 | #define NEOCLOCK4X_OFFSET_YEAR 16 |
93 | 0 | #define NEOCLOCK4X_OFFSET_HOUR 18 |
94 | 0 | #define NEOCLOCK4X_OFFSET_MINUTE 20 |
95 | 0 | #define NEOCLOCK4X_OFFSET_SECOND 22 |
96 | 0 | #define NEOCLOCK4X_OFFSET_HSEC 24 |
97 | | #define NEOCLOCK4X_OFFSET_DOW 26 |
98 | 0 | #define NEOCLOCK4X_OFFSET_TIMESOURCE 28 |
99 | 0 | #define NEOCLOCK4X_OFFSET_DSTSTATUS 29 |
100 | 0 | #define NEOCLOCK4X_OFFSET_QUARZSTATUS 30 |
101 | 0 | #define NEOCLOCK4X_OFFSET_ANTENNA1 31 |
102 | 0 | #define NEOCLOCK4X_OFFSET_ANTENNA2 33 |
103 | 0 | #define NEOCLOCK4X_OFFSET_CRC 35 |
104 | | |
105 | | #define NEOCLOCK4X_DRIVER_VERSION "1.16 (2009-12-04)" |
106 | | |
107 | 0 | #define NSEC_TO_MILLI 1000000 |
108 | | |
109 | | struct neoclock4x_unit { |
110 | | l_fp laststamp; /* last receive timestamp */ |
111 | | short unit; /* NTP refclock unit number */ |
112 | | u_long polled; /* flag to detect noreplies */ |
113 | | char leap_status; /* leap second flag */ |
114 | | int recvnow; |
115 | | |
116 | | char firmware[80]; |
117 | | char firmwaretag; |
118 | | char serial[7]; |
119 | | char radiosignal[4]; |
120 | | char timesource; |
121 | | char dststatus; |
122 | | char quarzstatus; |
123 | | int antenna1; |
124 | | int antenna2; |
125 | | int utc_year; |
126 | | int utc_month; |
127 | | int utc_day; |
128 | | int utc_hour; |
129 | | int utc_minute; |
130 | | int utc_second; |
131 | | int utc_msec; |
132 | | }; |
133 | | |
134 | | static int neoclock4x_start (int, struct peer *); |
135 | | static void neoclock4x_shutdown (int, struct peer *); |
136 | | static void neoclock4x_receive (struct recvbuf *); |
137 | | static void neoclock4x_poll (int, struct peer *); |
138 | | static void neoclock4x_control (int, const struct refclockstat *, struct refclockstat *, struct peer *); |
139 | | |
140 | | static int neol_atoi_len (const char str[], int *, int); |
141 | | static int neol_hexatoi_len (const char str[], int *, int); |
142 | | static void neol_jdn_to_ymd (unsigned long, int *, int *, int *); |
143 | | static void neol_localtime (unsigned long, int* , int*, int*, int*, int*, int*); |
144 | | static unsigned long neol_mktime (int, int, int, int, int, int); |
145 | | #if !defined(NEOCLOCK4X_FIRMWARE) |
146 | | static int neol_query_firmware (int, int, char *, size_t); |
147 | | static int neol_check_firmware (int, const char*, char *); |
148 | | #endif |
149 | | |
150 | | struct refclock refclock_neoclock4x = { |
151 | | neoclock4x_start, /* start up driver */ |
152 | | neoclock4x_shutdown, /* shut down driver */ |
153 | | neoclock4x_poll, /* transmit poll message */ |
154 | | neoclock4x_control, |
155 | | noentry, /* initialize driver (not used) */ |
156 | | noentry, /* not used */ |
157 | | NOFLAGS /* not used */ |
158 | | }; |
159 | | |
160 | | static int |
161 | | neoclock4x_start(int unit, |
162 | | struct peer *peer) |
163 | 0 | { |
164 | 0 | struct neoclock4x_unit *up; |
165 | 0 | struct refclockproc *pp; |
166 | 0 | int fd; |
167 | 0 | char dev[20]; |
168 | 0 | int sl232; |
169 | 0 | #if defined(HAVE_TERMIOS) |
170 | 0 | struct termios termsettings; |
171 | 0 | #endif |
172 | 0 | #if !defined(NEOCLOCK4X_FIRMWARE) |
173 | 0 | int tries; |
174 | 0 | #endif |
175 | |
|
176 | 0 | (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit); |
177 | | |
178 | | /* LDISC_STD, LDISC_RAW |
179 | | * Open serial port. Use CLK line discipline, if available. |
180 | | */ |
181 | 0 | fd = refclock_open(dev, B2400, LDISC_STD); |
182 | 0 | if(fd <= 0) |
183 | 0 | { |
184 | 0 | return (0); |
185 | 0 | } |
186 | | |
187 | 0 | #if defined(HAVE_TERMIOS) |
188 | | |
189 | 0 | #if 1 |
190 | 0 | if(tcgetattr(fd, &termsettings) < 0) |
191 | 0 | { |
192 | 0 | msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit); |
193 | 0 | (void) close(fd); |
194 | 0 | return (0); |
195 | 0 | } |
196 | | |
197 | | /* 2400 Baud 8N2 */ |
198 | 0 | termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL; |
199 | 0 | termsettings.c_oflag = 0; |
200 | 0 | termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD; |
201 | 0 | (void)cfsetispeed(&termsettings, (u_int)B2400); |
202 | 0 | (void)cfsetospeed(&termsettings, (u_int)B2400); |
203 | |
|
204 | 0 | if(tcsetattr(fd, TCSANOW, &termsettings) < 0) |
205 | 0 | { |
206 | 0 | msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit); |
207 | 0 | (void) close(fd); |
208 | 0 | return (0); |
209 | 0 | } |
210 | | |
211 | | #else |
212 | | if(tcgetattr(fd, &termsettings) < 0) |
213 | | { |
214 | | msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit); |
215 | | (void) close(fd); |
216 | | return (0); |
217 | | } |
218 | | |
219 | | /* 2400 Baud 8N2 */ |
220 | | termsettings.c_cflag &= ~PARENB; |
221 | | termsettings.c_cflag |= CSTOPB; |
222 | | termsettings.c_cflag &= ~CSIZE; |
223 | | termsettings.c_cflag |= CS8; |
224 | | |
225 | | if(tcsetattr(fd, TCSANOW, &termsettings) < 0) |
226 | | { |
227 | | msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit); |
228 | | (void) close(fd); |
229 | | return (0); |
230 | | } |
231 | | #endif |
232 | | |
233 | | #elif defined(HAVE_SYSV_TTYS) |
234 | | if(ioctl(fd, TCGETA, &termsettings) < 0) |
235 | | { |
236 | | msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit); |
237 | | (void) close(fd); |
238 | | return (0); |
239 | | } |
240 | | |
241 | | /* 2400 Baud 8N2 */ |
242 | | termsettings.c_cflag &= ~PARENB; |
243 | | termsettings.c_cflag |= CSTOPB; |
244 | | termsettings.c_cflag &= ~CSIZE; |
245 | | termsettings.c_cflag |= CS8; |
246 | | |
247 | | if(ioctl(fd, TCSETA, &termsettings) < 0) |
248 | | { |
249 | | msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit); |
250 | | (void) close(fd); |
251 | | return (0); |
252 | | } |
253 | | #else |
254 | | msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit); |
255 | | (void) close(fd); |
256 | | return (0); |
257 | | #endif |
258 | | |
259 | 0 | #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) |
260 | | /* turn on RTS, and DTR for power supply */ |
261 | | /* NeoClock4x is powered from serial line */ |
262 | 0 | if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1) |
263 | 0 | { |
264 | 0 | msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit); |
265 | 0 | (void) close(fd); |
266 | 0 | return (0); |
267 | 0 | } |
268 | 0 | #ifdef TIOCM_RTS |
269 | 0 | sl232 = sl232 | TIOCM_DTR | TIOCM_RTS; /* turn on RTS, and DTR for power supply */ |
270 | | #else |
271 | | sl232 = sl232 | CIOCM_DTR | CIOCM_RTS; /* turn on RTS, and DTR for power supply */ |
272 | | #endif |
273 | 0 | if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1) |
274 | 0 | { |
275 | 0 | msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit); |
276 | 0 | (void) close(fd); |
277 | 0 | return (0); |
278 | 0 | } |
279 | | #else |
280 | | msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!", |
281 | | unit); |
282 | | (void) close(fd); |
283 | | return (0); |
284 | | #endif |
285 | | |
286 | 0 | up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit)); |
287 | 0 | if(!(up)) |
288 | 0 | { |
289 | 0 | msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit); |
290 | 0 | (void) close(fd); |
291 | 0 | return (0); |
292 | 0 | } |
293 | | |
294 | 0 | memset((char *)up, 0, sizeof(struct neoclock4x_unit)); |
295 | 0 | pp = peer->procptr; |
296 | 0 | pp->clockdesc = "NeoClock4X"; |
297 | 0 | pp->unitptr = up; |
298 | 0 | pp->io.clock_recv = neoclock4x_receive; |
299 | 0 | pp->io.srcclock = peer; |
300 | 0 | pp->io.datalen = 0; |
301 | 0 | pp->io.fd = fd; |
302 | | /* |
303 | | * no fudge time is given by user! |
304 | | * use 169.583333 ms to compensate the serial line delay |
305 | | * formula is: |
306 | | * 2400 Baud / 11 bit = 218.18 charaters per second |
307 | | * (NeoClock4X timecode len) |
308 | | */ |
309 | 0 | pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0; |
310 | | |
311 | | /* |
312 | | * Initialize miscellaneous variables |
313 | | */ |
314 | 0 | peer->precision = -10; |
315 | 0 | memcpy((char *)&pp->refid, "neol", 4); |
316 | |
|
317 | 0 | up->leap_status = 0; |
318 | 0 | up->unit = unit; |
319 | 0 | strlcpy(up->firmware, "?", sizeof(up->firmware)); |
320 | 0 | up->firmwaretag = '?'; |
321 | 0 | strlcpy(up->serial, "?", sizeof(up->serial)); |
322 | 0 | strlcpy(up->radiosignal, "?", sizeof(up->radiosignal)); |
323 | 0 | up->timesource = '?'; |
324 | 0 | up->dststatus = '?'; |
325 | 0 | up->quarzstatus = '?'; |
326 | 0 | up->antenna1 = -1; |
327 | 0 | up->antenna2 = -1; |
328 | 0 | up->utc_year = 0; |
329 | 0 | up->utc_month = 0; |
330 | 0 | up->utc_day = 0; |
331 | 0 | up->utc_hour = 0; |
332 | 0 | up->utc_minute = 0; |
333 | 0 | up->utc_second = 0; |
334 | 0 | up->utc_msec = 0; |
335 | |
|
336 | | #if defined(NEOCLOCK4X_FIRMWARE) |
337 | | #if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A |
338 | | strlcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)", |
339 | | sizeof(up->firmware)); |
340 | | up->firmwaretag = 'A'; |
341 | | #else |
342 | | msyslog(LOG_EMERG, "NeoClock4X(%d): unknown firmware defined at compile time for NeoClock4X", |
343 | | unit); |
344 | | (void) close(fd); |
345 | | pp->io.fd = -1; |
346 | | free(pp->unitptr); |
347 | | pp->unitptr = NULL; |
348 | | return (0); |
349 | | #endif |
350 | | #else |
351 | 0 | for(tries=0; tries < 5; tries++) |
352 | 0 | { |
353 | 0 | NLOG(NLOG_CLOCKINFO) |
354 | 0 | msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries); |
355 | | /* wait 3 seconds for receiver to power up */ |
356 | 0 | sleep(3); |
357 | 0 | if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware))) |
358 | 0 | { |
359 | 0 | break; |
360 | 0 | } |
361 | 0 | } |
362 | | |
363 | | /* can I handle this firmware version? */ |
364 | 0 | if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag)) |
365 | 0 | { |
366 | 0 | (void) close(fd); |
367 | 0 | pp->io.fd = -1; |
368 | 0 | free(pp->unitptr); |
369 | 0 | pp->unitptr = NULL; |
370 | 0 | return (0); |
371 | 0 | } |
372 | 0 | #endif |
373 | | |
374 | 0 | if(!io_addclock(&pp->io)) |
375 | 0 | { |
376 | 0 | msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit); |
377 | 0 | (void) close(fd); |
378 | 0 | pp->io.fd = -1; |
379 | 0 | free(pp->unitptr); |
380 | 0 | pp->unitptr = NULL; |
381 | 0 | return (0); |
382 | 0 | } |
383 | | |
384 | 0 | NLOG(NLOG_CLOCKINFO) |
385 | 0 | msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit); |
386 | |
|
387 | 0 | return (1); |
388 | 0 | } |
389 | | |
390 | | static void |
391 | | neoclock4x_shutdown(int unit, |
392 | | struct peer *peer) |
393 | 0 | { |
394 | 0 | struct neoclock4x_unit *up; |
395 | 0 | struct refclockproc *pp; |
396 | 0 | int sl232; |
397 | |
|
398 | 0 | if(NULL != peer) |
399 | 0 | { |
400 | 0 | pp = peer->procptr; |
401 | 0 | if(pp != NULL) |
402 | 0 | { |
403 | 0 | up = pp->unitptr; |
404 | 0 | if(up != NULL) |
405 | 0 | { |
406 | 0 | if(-1 != pp->io.fd) |
407 | 0 | { |
408 | 0 | #if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) |
409 | | /* turn on RTS, and DTR for power supply */ |
410 | | /* NeoClock4x is powered from serial line */ |
411 | 0 | if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1) |
412 | 0 | { |
413 | 0 | msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", |
414 | 0 | unit); |
415 | 0 | } |
416 | 0 | #ifdef TIOCM_RTS |
417 | | /* turn on RTS, and DTR for power supply */ |
418 | 0 | sl232 &= ~(TIOCM_DTR | TIOCM_RTS); |
419 | | #else |
420 | | /* turn on RTS, and DTR for power supply */ |
421 | | sl232 &= ~(CIOCM_DTR | CIOCM_RTS); |
422 | | #endif |
423 | 0 | if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1) |
424 | 0 | { |
425 | 0 | msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", |
426 | 0 | unit); |
427 | 0 | } |
428 | 0 | #endif |
429 | 0 | io_closeclock(&pp->io); |
430 | 0 | } |
431 | 0 | free(up); |
432 | 0 | pp->unitptr = NULL; |
433 | 0 | } |
434 | 0 | } |
435 | 0 | } |
436 | |
|
437 | 0 | msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit); |
438 | |
|
439 | 0 | NLOG(NLOG_CLOCKINFO) |
440 | 0 | msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit); |
441 | 0 | } |
442 | | |
443 | | static void |
444 | | neoclock4x_receive(struct recvbuf *rbufp) |
445 | 0 | { |
446 | 0 | struct neoclock4x_unit *up; |
447 | 0 | struct refclockproc *pp; |
448 | 0 | struct peer *peer; |
449 | 0 | unsigned long calc_utc; |
450 | 0 | int day; |
451 | 0 | int month; /* ddd conversion */ |
452 | 0 | int c; |
453 | 0 | int dsec; |
454 | 0 | unsigned char calc_chksum; |
455 | 0 | int recv_chksum; |
456 | |
|
457 | 0 | peer = rbufp->recv_peer; |
458 | 0 | pp = peer->procptr; |
459 | 0 | up = pp->unitptr; |
460 | | |
461 | | /* wait till poll interval is reached */ |
462 | 0 | if(0 == up->recvnow) |
463 | 0 | return; |
464 | | |
465 | | /* reset poll interval flag */ |
466 | 0 | up->recvnow = 0; |
467 | | |
468 | | /* read last received timecode */ |
469 | 0 | pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); |
470 | 0 | pp->leap = LEAP_NOWARNING; |
471 | |
|
472 | 0 | if(NEOCLOCK4X_TIMECODELEN != pp->lencode) |
473 | 0 | { |
474 | 0 | NLOG(NLOG_CLOCKEVENT) |
475 | 0 | msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s", |
476 | 0 | up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode); |
477 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
478 | 0 | return; |
479 | 0 | } |
480 | | |
481 | 0 | neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2); |
482 | | |
483 | | /* calculate checksum */ |
484 | 0 | calc_chksum = 0; |
485 | 0 | for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++) |
486 | 0 | { |
487 | 0 | calc_chksum += pp->a_lastcode[c]; |
488 | 0 | } |
489 | 0 | if(recv_chksum != calc_chksum) |
490 | 0 | { |
491 | 0 | NLOG(NLOG_CLOCKEVENT) |
492 | 0 | msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s", |
493 | 0 | up->unit, pp->a_lastcode); |
494 | 0 | refclock_report(peer, CEVNT_BADREPLY); |
495 | 0 | return; |
496 | 0 | } |
497 | | |
498 | | /* Allow synchronization even is quartz clock is |
499 | | * never initialized. |
500 | | * WARNING: This is dangerous! |
501 | | */ |
502 | 0 | up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS]; |
503 | 0 | if(0==(pp->sloppyclockflag & CLK_FLAG2)) |
504 | 0 | { |
505 | 0 | if('I' != up->quarzstatus) |
506 | 0 | { |
507 | 0 | NLOG(NLOG_CLOCKEVENT) |
508 | 0 | msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s", |
509 | 0 | up->unit, pp->a_lastcode); |
510 | 0 | pp->leap = LEAP_NOTINSYNC; |
511 | 0 | refclock_report(peer, CEVNT_BADDATE); |
512 | 0 | return; |
513 | 0 | } |
514 | 0 | } |
515 | 0 | if('I' != up->quarzstatus) |
516 | 0 | { |
517 | 0 | NLOG(NLOG_CLOCKEVENT) |
518 | 0 | msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s", |
519 | 0 | up->unit, pp->a_lastcode); |
520 | 0 | } |
521 | | |
522 | | /* |
523 | | * If NeoClock4X is not synchronized to a radio clock |
524 | | * check if we're allowed to synchronize with the quartz |
525 | | * clock. |
526 | | */ |
527 | 0 | up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE]; |
528 | 0 | if(0==(pp->sloppyclockflag & CLK_FLAG2)) |
529 | 0 | { |
530 | 0 | if('A' != up->timesource) |
531 | 0 | { |
532 | | /* not allowed to sync with quartz clock */ |
533 | 0 | if(0==(pp->sloppyclockflag & CLK_FLAG1)) |
534 | 0 | { |
535 | 0 | refclock_report(peer, CEVNT_BADTIME); |
536 | 0 | pp->leap = LEAP_NOTINSYNC; |
537 | 0 | return; |
538 | 0 | } |
539 | 0 | } |
540 | 0 | } |
541 | | |
542 | | /* this should only used when first install is done */ |
543 | 0 | if(pp->sloppyclockflag & CLK_FLAG4) |
544 | 0 | { |
545 | 0 | msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s", |
546 | 0 | up->unit, pp->a_lastcode); |
547 | 0 | } |
548 | | |
549 | | /* 123456789012345678901234567890123456789012345 */ |
550 | | /* S/N123456DCF1004021010001202ASX1213CR\r\n */ |
551 | |
|
552 | 0 | neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2); |
553 | 0 | neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2); |
554 | 0 | neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2); |
555 | 0 | neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2); |
556 | 0 | neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2); |
557 | 0 | neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2); |
558 | 0 | neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &dsec, 2); |
559 | | #if defined(NTP_PRE_420) |
560 | | pp->msec = dsec * 10; /* convert 1/100s from neoclock to real miliseconds */ |
561 | | #else |
562 | 0 | pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */ |
563 | 0 | #endif |
564 | |
|
565 | 0 | memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3); |
566 | 0 | up->radiosignal[3] = 0; |
567 | 0 | memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6); |
568 | 0 | up->serial[6] = 0; |
569 | 0 | up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS]; |
570 | 0 | neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2); |
571 | 0 | neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2); |
572 | | |
573 | | /* |
574 | | Validate received values at least enough to prevent internal |
575 | | array-bounds problems, etc. |
576 | | */ |
577 | 0 | if((pp->hour < 0) || (pp->hour > 23) || |
578 | 0 | (pp->minute < 0) || (pp->minute > 59) || |
579 | 0 | (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ || |
580 | 0 | (day < 1) || (day > 31) || |
581 | 0 | (month < 1) || (month > 12) || |
582 | 0 | (pp->year < 0) || (pp->year > 99)) { |
583 | | /* Data out of range. */ |
584 | 0 | NLOG(NLOG_CLOCKEVENT) |
585 | 0 | msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s", |
586 | 0 | up->unit, pp->a_lastcode); |
587 | 0 | refclock_report(peer, CEVNT_BADDATE); |
588 | 0 | return; |
589 | 0 | } |
590 | | |
591 | | /* Year-2000 check not needed anymore. Same problem |
592 | | * will arise at 2099 but what should we do...? |
593 | | * |
594 | | * wrap 2-digit date into 4-digit |
595 | | * |
596 | | * if(pp->year < YEAR_PIVOT) |
597 | | * { |
598 | | * pp->year += 100; |
599 | | * } |
600 | | */ |
601 | 0 | pp->year += 2000; |
602 | | |
603 | | /* adjust NeoClock4X local time to UTC */ |
604 | 0 | calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second); |
605 | 0 | calc_utc -= 3600; |
606 | | /* adjust NeoClock4X daylight saving time if needed */ |
607 | 0 | if('S' == up->dststatus) |
608 | 0 | calc_utc -= 3600; |
609 | 0 | neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second); |
610 | | |
611 | | /* |
612 | | some preparations |
613 | | */ |
614 | 0 | pp->day = ymd2yd(pp->year, month, day); |
615 | 0 | pp->leap = 0; |
616 | |
|
617 | 0 | if(pp->sloppyclockflag & CLK_FLAG4) |
618 | 0 | { |
619 | 0 | msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld", |
620 | 0 | up->unit, |
621 | 0 | pp->year, month, day, |
622 | 0 | pp->hour, pp->minute, pp->second, |
623 | | #if defined(NTP_PRE_420) |
624 | | pp->msec |
625 | | #else |
626 | 0 | pp->nsec/NSEC_TO_MILLI |
627 | 0 | #endif |
628 | 0 | ); |
629 | 0 | } |
630 | |
|
631 | 0 | up->utc_year = pp->year; |
632 | 0 | up->utc_month = month; |
633 | 0 | up->utc_day = day; |
634 | 0 | up->utc_hour = pp->hour; |
635 | 0 | up->utc_minute = pp->minute; |
636 | 0 | up->utc_second = pp->second; |
637 | | #if defined(NTP_PRE_420) |
638 | | up->utc_msec = pp->msec; |
639 | | #else |
640 | 0 | up->utc_msec = pp->nsec/NSEC_TO_MILLI; |
641 | 0 | #endif |
642 | |
|
643 | 0 | if(!refclock_process(pp)) |
644 | 0 | { |
645 | 0 | NLOG(NLOG_CLOCKEVENT) |
646 | 0 | msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit); |
647 | 0 | refclock_report(peer, CEVNT_FAULT); |
648 | 0 | return; |
649 | 0 | } |
650 | 0 | refclock_receive(peer); |
651 | | |
652 | | /* report good status */ |
653 | 0 | refclock_report(peer, CEVNT_NOMINAL); |
654 | |
|
655 | 0 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
656 | 0 | } |
657 | | |
658 | | static void |
659 | | neoclock4x_poll(int unit, |
660 | | struct peer *peer) |
661 | 0 | { |
662 | 0 | struct neoclock4x_unit *up; |
663 | 0 | struct refclockproc *pp; |
664 | |
|
665 | 0 | pp = peer->procptr; |
666 | 0 | up = pp->unitptr; |
667 | |
|
668 | 0 | pp->polls++; |
669 | 0 | up->recvnow = 1; |
670 | 0 | } |
671 | | |
672 | | static void |
673 | | neoclock4x_control(int unit, |
674 | | const struct refclockstat *in, |
675 | | struct refclockstat *out, |
676 | | struct peer *peer) |
677 | 0 | { |
678 | 0 | struct neoclock4x_unit *up; |
679 | 0 | struct refclockproc *pp; |
680 | |
|
681 | 0 | if(NULL == peer) |
682 | 0 | { |
683 | 0 | msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); |
684 | 0 | return; |
685 | 0 | } |
686 | | |
687 | 0 | pp = peer->procptr; |
688 | 0 | if(NULL == pp) |
689 | 0 | { |
690 | 0 | msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); |
691 | 0 | return; |
692 | 0 | } |
693 | | |
694 | 0 | up = pp->unitptr; |
695 | 0 | if(NULL == up) |
696 | 0 | { |
697 | 0 | msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); |
698 | 0 | return; |
699 | 0 | } |
700 | | |
701 | 0 | if(NULL != in) |
702 | 0 | { |
703 | | /* check to see if a user supplied time offset is given */ |
704 | 0 | if(in->haveflags & CLK_HAVETIME1) |
705 | 0 | { |
706 | 0 | pp->fudgetime1 = in->fudgetime1; |
707 | 0 | NLOG(NLOG_CLOCKINFO) |
708 | 0 | msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.", |
709 | 0 | unit, pp->fudgetime1); |
710 | 0 | } |
711 | | |
712 | | /* notify */ |
713 | 0 | if(pp->sloppyclockflag & CLK_FLAG1) |
714 | 0 | { |
715 | 0 | NLOG(NLOG_CLOCKINFO) |
716 | 0 | msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit); |
717 | 0 | } |
718 | 0 | else |
719 | 0 | { |
720 | 0 | NLOG(NLOG_CLOCKINFO) |
721 | 0 | msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit); |
722 | 0 | } |
723 | 0 | } |
724 | |
|
725 | 0 | if(NULL != out) |
726 | 0 | { |
727 | 0 | char *tt; |
728 | 0 | char tmpbuf[80]; |
729 | |
|
730 | 0 | out->kv_list = (struct ctl_var *)0; |
731 | 0 | out->type = REFCLK_NEOCLOCK4X; |
732 | |
|
733 | 0 | snprintf(tmpbuf, sizeof(tmpbuf)-1, |
734 | 0 | "%04d-%02d-%02d %02d:%02d:%02d.%03d", |
735 | 0 | up->utc_year, up->utc_month, up->utc_day, |
736 | 0 | up->utc_hour, up->utc_minute, up->utc_second, |
737 | 0 | up->utc_msec); |
738 | 0 | tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF); |
739 | 0 | snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf); |
740 | |
|
741 | 0 | tt = add_var(&out->kv_list, 40, RO|DEF); |
742 | 0 | snprintf(tt, 39, "radiosignal=\"%s\"", up->radiosignal); |
743 | 0 | tt = add_var(&out->kv_list, 40, RO|DEF); |
744 | 0 | snprintf(tt, 39, "antenna1=\"%d\"", up->antenna1); |
745 | 0 | tt = add_var(&out->kv_list, 40, RO|DEF); |
746 | 0 | snprintf(tt, 39, "antenna2=\"%d\"", up->antenna2); |
747 | 0 | tt = add_var(&out->kv_list, 40, RO|DEF); |
748 | 0 | if('A' == up->timesource) |
749 | 0 | snprintf(tt, 39, "timesource=\"radio\""); |
750 | 0 | else if('C' == up->timesource) |
751 | 0 | snprintf(tt, 39, "timesource=\"quartz\""); |
752 | 0 | else |
753 | 0 | snprintf(tt, 39, "timesource=\"unknown\""); |
754 | 0 | tt = add_var(&out->kv_list, 40, RO|DEF); |
755 | 0 | if('I' == up->quarzstatus) |
756 | 0 | snprintf(tt, 39, "quartzstatus=\"synchronized\""); |
757 | 0 | else if('X' == up->quarzstatus) |
758 | 0 | snprintf(tt, 39, "quartzstatus=\"not synchronized\""); |
759 | 0 | else |
760 | 0 | snprintf(tt, 39, "quartzstatus=\"unknown\""); |
761 | 0 | tt = add_var(&out->kv_list, 40, RO|DEF); |
762 | 0 | if('S' == up->dststatus) |
763 | 0 | snprintf(tt, 39, "dststatus=\"summer\""); |
764 | 0 | else if('W' == up->dststatus) |
765 | 0 | snprintf(tt, 39, "dststatus=\"winter\""); |
766 | 0 | else |
767 | 0 | snprintf(tt, 39, "dststatus=\"unknown\""); |
768 | 0 | tt = add_var(&out->kv_list, 80, RO|DEF); |
769 | 0 | snprintf(tt, 79, "firmware=\"%s\"", up->firmware); |
770 | 0 | tt = add_var(&out->kv_list, 40, RO|DEF); |
771 | 0 | snprintf(tt, 39, "firmwaretag=\"%c\"", up->firmwaretag); |
772 | 0 | tt = add_var(&out->kv_list, 80, RO|DEF); |
773 | 0 | snprintf(tt, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION); |
774 | 0 | tt = add_var(&out->kv_list, 80, RO|DEF); |
775 | 0 | snprintf(tt, 79, "serialnumber=\"%s\"", up->serial); |
776 | 0 | } |
777 | 0 | } |
778 | | |
779 | | static int |
780 | | neol_hexatoi_len(const char str[], |
781 | | int *result, |
782 | | int maxlen) |
783 | 0 | { |
784 | 0 | int hexdigit; |
785 | 0 | int i; |
786 | 0 | int n = 0; |
787 | |
|
788 | 0 | for(i=0; isxdigit((unsigned char)str[i]) && i < maxlen; i++) |
789 | 0 | { |
790 | 0 | hexdigit = isdigit((unsigned char)str[i]) ? toupper((unsigned char)str[i]) - '0' : toupper((unsigned char)str[i]) - 'A' + 10; |
791 | 0 | n = 16 * n + hexdigit; |
792 | 0 | } |
793 | 0 | *result = n; |
794 | 0 | return (n); |
795 | 0 | } |
796 | | |
797 | | static int |
798 | | neol_atoi_len(const char str[], |
799 | | int *result, |
800 | | int maxlen) |
801 | 0 | { |
802 | 0 | int digit; |
803 | 0 | int i; |
804 | 0 | int n = 0; |
805 | |
|
806 | 0 | for(i=0; isdigit((unsigned char)str[i]) && i < maxlen; i++) |
807 | 0 | { |
808 | 0 | digit = str[i] - '0'; |
809 | 0 | n = 10 * n + digit; |
810 | 0 | } |
811 | 0 | *result = n; |
812 | 0 | return (n); |
813 | 0 | } |
814 | | |
815 | | /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. |
816 | | * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 |
817 | | * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. |
818 | | * |
819 | | * [For the Julian calendar (which was used in Russia before 1917, |
820 | | * Britain & colonies before 1752, anywhere else before 1582, |
821 | | * and is still in use by some communities) leave out the |
822 | | * -year/100+year/400 terms, and add 10.] |
823 | | * |
824 | | * This algorithm was first published by Gauss (I think). |
825 | | * |
826 | | * WARNING: this function will overflow on 2106-02-07 06:28:16 on |
827 | | * machines were long is 32-bit! (However, as time_t is signed, we |
828 | | * will already get problems at other places on 2038-01-19 03:14:08) |
829 | | */ |
830 | | static unsigned long |
831 | | neol_mktime(int year, |
832 | | int mon, |
833 | | int day, |
834 | | int hour, |
835 | | int min, |
836 | | int sec) |
837 | 0 | { |
838 | 0 | if (0 >= (int) (mon -= 2)) { /* 1..12 . 11,12,1..10 */ |
839 | 0 | mon += 12; /* Puts Feb last since it has leap day */ |
840 | 0 | year -= 1; |
841 | 0 | } |
842 | 0 | return ((( |
843 | 0 | (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + |
844 | 0 | year*365 - 719499 |
845 | 0 | )*24 + hour /* now have hours */ |
846 | 0 | )*60 + min /* now have minutes */ |
847 | 0 | )*60 + sec; /* finally seconds */ |
848 | 0 | } |
849 | | |
850 | | static void |
851 | | neol_localtime(unsigned long utc, |
852 | | int* year, |
853 | | int* month, |
854 | | int* day, |
855 | | int* hour, |
856 | | int* min, |
857 | | int* sec) |
858 | 0 | { |
859 | 0 | *sec = utc % 60; |
860 | 0 | utc /= 60; |
861 | 0 | *min = utc % 60; |
862 | 0 | utc /= 60; |
863 | 0 | *hour = utc % 24; |
864 | 0 | utc /= 24; |
865 | | |
866 | | /* JDN Date 1/1/1970 */ |
867 | 0 | neol_jdn_to_ymd(utc + 2440588L, year, month, day); |
868 | 0 | } |
869 | | |
870 | | static void |
871 | | neol_jdn_to_ymd(unsigned long jdn, |
872 | | int *yy, |
873 | | int *mm, |
874 | | int *dd) |
875 | 0 | { |
876 | 0 | unsigned long x, z, m, d, y; |
877 | 0 | unsigned long daysPer400Years = 146097UL; |
878 | 0 | unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL; |
879 | |
|
880 | 0 | x = jdn + 68569UL; |
881 | 0 | z = 4UL * x / daysPer400Years; |
882 | 0 | x = x - (daysPer400Years * z + 3UL) / 4UL; |
883 | 0 | y = 4000UL * (x + 1) / fudgedDaysPer4000Years; |
884 | 0 | x = x - 1461UL * y / 4UL + 31UL; |
885 | 0 | m = 80UL * x / 2447UL; |
886 | 0 | d = x - 2447UL * m / 80UL; |
887 | 0 | x = m / 11UL; |
888 | 0 | m = m + 2UL - 12UL * x; |
889 | 0 | y = 100UL * (z - 49UL) + y + x; |
890 | |
|
891 | 0 | *yy = (int)y; |
892 | 0 | *mm = (int)m; |
893 | 0 | *dd = (int)d; |
894 | 0 | } |
895 | | |
896 | | #if !defined(NEOCLOCK4X_FIRMWARE) |
897 | | static int |
898 | | neol_query_firmware(int fd, |
899 | | int unit, |
900 | | char *firmware, |
901 | | size_t maxlen) |
902 | 0 | { |
903 | 0 | char tmpbuf[256]; |
904 | 0 | size_t len; |
905 | 0 | int lastsearch; |
906 | 0 | unsigned char c; |
907 | 0 | int last_c_was_crlf; |
908 | 0 | int last_crlf_conv_len; |
909 | 0 | int init; |
910 | 0 | int read_errors; |
911 | 0 | int flag = 0; |
912 | 0 | int chars_read; |
913 | | |
914 | | /* wait a little bit */ |
915 | 0 | sleep(1); |
916 | 0 | if(-1 != write(fd, "V", 1)) |
917 | 0 | { |
918 | | /* wait a little bit */ |
919 | 0 | sleep(1); |
920 | 0 | memset(tmpbuf, 0x00, sizeof(tmpbuf)); |
921 | |
|
922 | 0 | len = 0; |
923 | 0 | lastsearch = 0; |
924 | 0 | last_c_was_crlf = 0; |
925 | 0 | last_crlf_conv_len = 0; |
926 | 0 | init = 1; |
927 | 0 | read_errors = 0; |
928 | 0 | chars_read = 0; |
929 | 0 | for(;;) |
930 | 0 | { |
931 | 0 | if(read_errors > 5) |
932 | 0 | { |
933 | 0 | msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit); |
934 | 0 | strlcpy(tmpbuf, "unknown due to timeout", sizeof(tmpbuf)); |
935 | 0 | break; |
936 | 0 | } |
937 | 0 | if(chars_read > 500) |
938 | 0 | { |
939 | 0 | msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit); |
940 | 0 | strlcpy(tmpbuf, "unknown due to garbage input", sizeof(tmpbuf)); |
941 | 0 | break; |
942 | 0 | } |
943 | 0 | if(-1 == read(fd, &c, 1)) |
944 | 0 | { |
945 | 0 | if(EAGAIN != errno) |
946 | 0 | { |
947 | 0 | msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %m", unit); |
948 | 0 | read_errors++; |
949 | 0 | } |
950 | 0 | else |
951 | 0 | { |
952 | 0 | sleep(1); |
953 | 0 | } |
954 | 0 | continue; |
955 | 0 | } |
956 | 0 | else |
957 | 0 | { |
958 | 0 | chars_read++; |
959 | 0 | } |
960 | | |
961 | 0 | if(init) |
962 | 0 | { |
963 | 0 | if(0xA9 != c) /* wait for (c) char in input stream */ |
964 | 0 | continue; |
965 | | |
966 | 0 | strlcpy(tmpbuf, "(c)", sizeof(tmpbuf)); |
967 | 0 | len = 3; |
968 | 0 | init = 0; |
969 | 0 | continue; |
970 | 0 | } |
971 | | |
972 | | #if 0 |
973 | | msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c); |
974 | | #endif |
975 | | |
976 | 0 | if(0x0A == c || 0x0D == c) |
977 | 0 | { |
978 | 0 | if(last_c_was_crlf) |
979 | 0 | { |
980 | 0 | char *ptr; |
981 | 0 | ptr = strstr(&tmpbuf[lastsearch], "S/N"); |
982 | 0 | if(NULL != ptr) |
983 | 0 | { |
984 | 0 | tmpbuf[last_crlf_conv_len] = 0; |
985 | 0 | flag = 1; |
986 | 0 | break; |
987 | 0 | } |
988 | | /* convert \n to / */ |
989 | 0 | last_crlf_conv_len = len; |
990 | 0 | tmpbuf[len++] = ' '; |
991 | 0 | tmpbuf[len++] = '/'; |
992 | 0 | tmpbuf[len++] = ' '; |
993 | 0 | lastsearch = len; |
994 | 0 | } |
995 | 0 | last_c_was_crlf = 1; |
996 | 0 | } |
997 | 0 | else |
998 | 0 | { |
999 | 0 | last_c_was_crlf = 0; |
1000 | 0 | if(0x00 != c) |
1001 | 0 | tmpbuf[len++] = (char) c; |
1002 | 0 | } |
1003 | 0 | tmpbuf[len] = '\0'; |
1004 | 0 | if (len > sizeof(tmpbuf)-5) |
1005 | 0 | break; |
1006 | 0 | } |
1007 | 0 | } |
1008 | 0 | else |
1009 | 0 | { |
1010 | 0 | msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit); |
1011 | 0 | strlcpy(tmpbuf, "unknown error", sizeof(tmpbuf)); |
1012 | 0 | } |
1013 | 0 | if (strlcpy(firmware, tmpbuf, maxlen) >= maxlen) |
1014 | 0 | strlcpy(firmware, "buffer too small", maxlen); |
1015 | |
|
1016 | 0 | if(flag) |
1017 | 0 | { |
1018 | 0 | NLOG(NLOG_CLOCKINFO) |
1019 | 0 | msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware); |
1020 | |
|
1021 | 0 | if(strstr(firmware, "/R2")) |
1022 | 0 | { |
1023 | 0 | msyslog(LOG_INFO, "NeoClock4X(%d): Your NeoClock4X uses the new R2 firmware release. Please note the changed LED behaviour.", unit); |
1024 | 0 | } |
1025 | |
|
1026 | 0 | } |
1027 | |
|
1028 | 0 | return (flag); |
1029 | 0 | } |
1030 | | |
1031 | | static int |
1032 | | neol_check_firmware(int unit, |
1033 | | const char *firmware, |
1034 | | char *firmwaretag) |
1035 | 0 | { |
1036 | 0 | char *ptr; |
1037 | |
|
1038 | 0 | *firmwaretag = '?'; |
1039 | 0 | ptr = strstr(firmware, "NDF:"); |
1040 | 0 | if(NULL != ptr) |
1041 | 0 | { |
1042 | 0 | if((strlen(firmware) - strlen(ptr)) >= 7) |
1043 | 0 | { |
1044 | 0 | if(':' == *(ptr+5) && '*' == *(ptr+6)) |
1045 | 0 | *firmwaretag = *(ptr+4); |
1046 | 0 | } |
1047 | 0 | } |
1048 | |
|
1049 | 0 | if('A' != *firmwaretag) |
1050 | 0 | { |
1051 | 0 | msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag); |
1052 | 0 | return (0); |
1053 | 0 | } |
1054 | | |
1055 | 0 | return (1); |
1056 | 0 | } |
1057 | | #endif |
1058 | | |
1059 | | #else |
1060 | | int refclock_neoclock4x_bs; |
1061 | | #endif /* REFCLOCK */ |
1062 | | |
1063 | | /* |
1064 | | * History: |
1065 | | * refclock_neoclock4x.c |
1066 | | * |
1067 | | * 2002/04/27 cjh |
1068 | | * Revision 1.0 first release |
1069 | | * |
1070 | | * 2002/07/15 cjh |
1071 | | * preparing for bitkeeper reposity |
1072 | | * |
1073 | | * 2002/09/09 cjh |
1074 | | * Revision 1.1 |
1075 | | * - don't assume sprintf returns an int anymore |
1076 | | * - change the way the firmware version is read |
1077 | | * - some customers would like to put a device called |
1078 | | * data diode to the NeoClock4X device to disable |
1079 | | * the write line. We need to now the firmware |
1080 | | * version even in this case. We made a compile time |
1081 | | * definition in this case. The code was previously |
1082 | | * only available on request. |
1083 | | * |
1084 | | * 2003/01/08 cjh |
1085 | | * Revision 1.11 |
1086 | | * - changing xprinf to xnprinf to avoid buffer overflows |
1087 | | * - change some logic |
1088 | | * - fixed memory leaks if drivers can't initialize |
1089 | | * |
1090 | | * 2003/01/10 cjh |
1091 | | * Revision 1.12 |
1092 | | * - replaced ldiv |
1093 | | * - add code to support FreeBSD |
1094 | | * |
1095 | | * 2003/07/07 cjh |
1096 | | * Revision 1.13 |
1097 | | * - fix reporting of clock status |
1098 | | * changes. previously a bad clock |
1099 | | * status was never reset. |
1100 | | * |
1101 | | * 2004/04/07 cjh |
1102 | | * Revision 1.14 |
1103 | | * - open serial port in a way |
1104 | | * AIX and some other OS can |
1105 | | * handle much better |
1106 | | * |
1107 | | * 2006/01/11 cjh |
1108 | | * Revision 1.15 |
1109 | | * - remove some unsued #ifdefs |
1110 | | * - fix nsec calculation, closes #499 |
1111 | | * |
1112 | | * 2009/12/04 cjh |
1113 | | * Revision 1.16 |
1114 | | * - change license to ntp COPYRIGHT notice. This should allow Debian |
1115 | | * to add this refclock driver in further releases. |
1116 | | * - detect R2 hardware |
1117 | | * |
1118 | | */ |
1119 | | |
1120 | | |
1121 | | |
1122 | | |
1123 | | |
1124 | | |