Annotation of embedaddon/ntp/ntpd/refclock_neoclock4x.c, revision 1.1.1.1
1.1 misho 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: #define NEOCLOCK4X_TIMECODELEN 37
87:
88: #define NEOCLOCK4X_OFFSET_SERIAL 3
89: #define NEOCLOCK4X_OFFSET_RADIOSIGNAL 9
90: #define NEOCLOCK4X_OFFSET_DAY 12
91: #define NEOCLOCK4X_OFFSET_MONTH 14
92: #define NEOCLOCK4X_OFFSET_YEAR 16
93: #define NEOCLOCK4X_OFFSET_HOUR 18
94: #define NEOCLOCK4X_OFFSET_MINUTE 20
95: #define NEOCLOCK4X_OFFSET_SECOND 22
96: #define NEOCLOCK4X_OFFSET_HSEC 24
97: #define NEOCLOCK4X_OFFSET_DOW 26
98: #define NEOCLOCK4X_OFFSET_TIMESOURCE 28
99: #define NEOCLOCK4X_OFFSET_DSTSTATUS 29
100: #define NEOCLOCK4X_OFFSET_QUARZSTATUS 30
101: #define NEOCLOCK4X_OFFSET_ANTENNA1 31
102: #define NEOCLOCK4X_OFFSET_ANTENNA2 33
103: #define NEOCLOCK4X_OFFSET_CRC 35
104:
105: #define NEOCLOCK4X_DRIVER_VERSION "1.16 (2009-12-04)"
106:
107: #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, 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 *, int);
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: {
164: struct neoclock4x_unit *up;
165: struct refclockproc *pp;
166: int fd;
167: char dev[20];
168: int sl232;
169: #if defined(HAVE_TERMIOS)
170: struct termios termsettings;
171: #endif
172: #if !defined(NEOCLOCK4X_FIRMWARE)
173: int tries;
174: #endif
175:
176: (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: fd = refclock_open(dev, B2400, LDISC_STD);
182: if(fd <= 0)
183: {
184: return (0);
185: }
186:
187: #if defined(HAVE_TERMIOS)
188:
189: #if 1
190: if(tcgetattr(fd, &termsettings) < 0)
191: {
192: msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit);
193: (void) close(fd);
194: return (0);
195: }
196:
197: /* 2400 Baud 8N2 */
198: termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL;
199: termsettings.c_oflag = 0;
200: termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD;
201: (void)cfsetispeed(&termsettings, (u_int)B2400);
202: (void)cfsetospeed(&termsettings, (u_int)B2400);
203:
204: if(tcsetattr(fd, TCSANOW, &termsettings) < 0)
205: {
206: msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit);
207: (void) close(fd);
208: return (0);
209: }
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: #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: if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1)
263: {
264: msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit);
265: (void) close(fd);
266: return (0);
267: }
268: #ifdef TIOCM_RTS
269: 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: if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1)
274: {
275: msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit);
276: (void) close(fd);
277: return (0);
278: }
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: up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit));
287: if(!(up))
288: {
289: msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit);
290: (void) close(fd);
291: return (0);
292: }
293:
294: memset((char *)up, 0, sizeof(struct neoclock4x_unit));
295: pp = peer->procptr;
296: pp->clockdesc = "NeoClock4X";
297: pp->unitptr = (caddr_t)up;
298: pp->io.clock_recv = neoclock4x_receive;
299: pp->io.srcclock = (caddr_t)peer;
300: pp->io.datalen = 0;
301: 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: pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0;
310:
311: /*
312: * Initialize miscellaneous variables
313: */
314: peer->precision = -10;
315: peer->burst = NSTAGE;
316: memcpy((char *)&pp->refid, "neol", 4);
317:
318: up->leap_status = 0;
319: up->unit = unit;
320: strcpy(up->firmware, "?");
321: up->firmwaretag = '?';
322: strcpy(up->serial, "?");
323: strcpy(up->radiosignal, "?");
324: up->timesource = '?';
325: up->dststatus = '?';
326: up->quarzstatus = '?';
327: up->antenna1 = -1;
328: up->antenna2 = -1;
329: up->utc_year = 0;
330: up->utc_month = 0;
331: up->utc_day = 0;
332: up->utc_hour = 0;
333: up->utc_minute = 0;
334: up->utc_second = 0;
335: up->utc_msec = 0;
336:
337: #if defined(NEOCLOCK4X_FIRMWARE)
338: #if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A
339: strcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)");
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: for(tries=0; tries < 5; tries++)
352: {
353: NLOG(NLOG_CLOCKINFO)
354: msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries);
355: /* wait 3 seconds for receiver to power up */
356: sleep(3);
357: if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware)))
358: {
359: break;
360: }
361: }
362:
363: /* can I handle this firmware version? */
364: if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag))
365: {
366: (void) close(fd);
367: pp->io.fd = -1;
368: free(pp->unitptr);
369: pp->unitptr = NULL;
370: return (0);
371: }
372: #endif
373:
374: if(!io_addclock(&pp->io))
375: {
376: msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit);
377: (void) close(fd);
378: pp->io.fd = -1;
379: free(pp->unitptr);
380: pp->unitptr = NULL;
381: return (0);
382: }
383:
384: NLOG(NLOG_CLOCKINFO)
385: msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit);
386:
387: return (1);
388: }
389:
390: static void
391: neoclock4x_shutdown(int unit,
392: struct peer *peer)
393: {
394: struct neoclock4x_unit *up;
395: struct refclockproc *pp;
396: int sl232;
397:
398: if(NULL != peer)
399: {
400: pp = peer->procptr;
401: if(pp != NULL)
402: {
403: up = (struct neoclock4x_unit *)pp->unitptr;
404: if(up != NULL)
405: {
406: if(-1 != pp->io.fd)
407: {
408: #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: if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1)
412: {
413: msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m",
414: unit);
415: }
416: #ifdef TIOCM_RTS
417: /* turn on RTS, and DTR for power supply */
418: 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: if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1)
424: {
425: msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m",
426: unit);
427: }
428: #endif
429: io_closeclock(&pp->io);
430: }
431: free(up);
432: pp->unitptr = NULL;
433: }
434: }
435: }
436:
437: msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit);
438:
439: NLOG(NLOG_CLOCKINFO)
440: msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);
441: }
442:
443: static void
444: neoclock4x_receive(struct recvbuf *rbufp)
445: {
446: struct neoclock4x_unit *up;
447: struct refclockproc *pp;
448: struct peer *peer;
449: unsigned long calc_utc;
450: int day;
451: int month; /* ddd conversion */
452: int c;
453: int dsec;
454: unsigned char calc_chksum;
455: int recv_chksum;
456:
457: peer = (struct peer *)rbufp->recv_srcclock;
458: pp = peer->procptr;
459: up = (struct neoclock4x_unit *)pp->unitptr;
460:
461: /* wait till poll interval is reached */
462: if(0 == up->recvnow)
463: return;
464:
465: /* reset poll interval flag */
466: up->recvnow = 0;
467:
468: /* read last received timecode */
469: pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
470: pp->leap = LEAP_NOWARNING;
471:
472: if(NEOCLOCK4X_TIMECODELEN != pp->lencode)
473: {
474: NLOG(NLOG_CLOCKEVENT)
475: msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s",
476: up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode);
477: refclock_report(peer, CEVNT_BADREPLY);
478: return;
479: }
480:
481: neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2);
482:
483: /* calculate checksum */
484: calc_chksum = 0;
485: for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++)
486: {
487: calc_chksum += pp->a_lastcode[c];
488: }
489: if(recv_chksum != calc_chksum)
490: {
491: NLOG(NLOG_CLOCKEVENT)
492: msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s",
493: up->unit, pp->a_lastcode);
494: refclock_report(peer, CEVNT_BADREPLY);
495: return;
496: }
497:
498: /* Allow synchronization even is quartz clock is
499: * never initialized.
500: * WARNING: This is dangerous!
501: */
502: up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS];
503: if(0==(pp->sloppyclockflag & CLK_FLAG2))
504: {
505: if('I' != up->quarzstatus)
506: {
507: NLOG(NLOG_CLOCKEVENT)
508: msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s",
509: up->unit, pp->a_lastcode);
510: pp->leap = LEAP_NOTINSYNC;
511: refclock_report(peer, CEVNT_BADDATE);
512: return;
513: }
514: }
515: if('I' != up->quarzstatus)
516: {
517: NLOG(NLOG_CLOCKEVENT)
518: msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s",
519: up->unit, pp->a_lastcode);
520: }
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: up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE];
528: if(0==(pp->sloppyclockflag & CLK_FLAG2))
529: {
530: if('A' != up->timesource)
531: {
532: /* not allowed to sync with quartz clock */
533: if(0==(pp->sloppyclockflag & CLK_FLAG1))
534: {
535: refclock_report(peer, CEVNT_BADTIME);
536: pp->leap = LEAP_NOTINSYNC;
537: return;
538: }
539: }
540: }
541:
542: /* this should only used when first install is done */
543: if(pp->sloppyclockflag & CLK_FLAG4)
544: {
545: msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s",
546: up->unit, pp->a_lastcode);
547: }
548:
549: /* 123456789012345678901234567890123456789012345 */
550: /* S/N123456DCF1004021010001202ASX1213CR\r\n */
551:
552: neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2);
553: neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2);
554: neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2);
555: neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2);
556: neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2);
557: neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2);
558: 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: pp->nsec = dsec * 10 * NSEC_TO_MILLI; /* convert 1/100s from neoclock to nanoseconds */
563: #endif
564:
565: memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3);
566: up->radiosignal[3] = 0;
567: memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6);
568: up->serial[6] = 0;
569: up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS];
570: neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2);
571: 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: if((pp->hour < 0) || (pp->hour > 23) ||
578: (pp->minute < 0) || (pp->minute > 59) ||
579: (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
580: (day < 1) || (day > 31) ||
581: (month < 1) || (month > 12) ||
582: (pp->year < 0) || (pp->year > 99)) {
583: /* Data out of range. */
584: NLOG(NLOG_CLOCKEVENT)
585: msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s",
586: up->unit, pp->a_lastcode);
587: refclock_report(peer, CEVNT_BADDATE);
588: return;
589: }
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: pp->year += 2000;
602:
603: /* adjust NeoClock4X local time to UTC */
604: calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second);
605: calc_utc -= 3600;
606: /* adjust NeoClock4X daylight saving time if needed */
607: if('S' == up->dststatus)
608: calc_utc -= 3600;
609: neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second);
610:
611: /*
612: some preparations
613: */
614: pp->day = ymd2yd(pp->year, month, day);
615: pp->leap = 0;
616:
617: if(pp->sloppyclockflag & CLK_FLAG4)
618: {
619: msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03ld",
620: up->unit,
621: pp->year, month, day,
622: pp->hour, pp->minute, pp->second,
623: #if defined(NTP_PRE_420)
624: pp->msec
625: #else
626: pp->nsec/NSEC_TO_MILLI
627: #endif
628: );
629: }
630:
631: up->utc_year = pp->year;
632: up->utc_month = month;
633: up->utc_day = day;
634: up->utc_hour = pp->hour;
635: up->utc_minute = pp->minute;
636: up->utc_second = pp->second;
637: #if defined(NTP_PRE_420)
638: up->utc_msec = pp->msec;
639: #else
640: up->utc_msec = pp->nsec/NSEC_TO_MILLI;
641: #endif
642:
643: if(!refclock_process(pp))
644: {
645: NLOG(NLOG_CLOCKEVENT)
646: msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit);
647: refclock_report(peer, CEVNT_FAULT);
648: return;
649: }
650: refclock_receive(peer);
651:
652: /* report good status */
653: refclock_report(peer, CEVNT_NOMINAL);
654:
655: record_clock_stats(&peer->srcadr, pp->a_lastcode);
656: }
657:
658: static void
659: neoclock4x_poll(int unit,
660: struct peer *peer)
661: {
662: struct neoclock4x_unit *up;
663: struct refclockproc *pp;
664:
665: pp = peer->procptr;
666: up = (struct neoclock4x_unit *)pp->unitptr;
667:
668: pp->polls++;
669: up->recvnow = 1;
670: }
671:
672: static void
673: neoclock4x_control(int unit,
674: struct refclockstat *in,
675: struct refclockstat *out,
676: struct peer *peer)
677: {
678: struct neoclock4x_unit *up;
679: struct refclockproc *pp;
680:
681: if(NULL == peer)
682: {
683: msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
684: return;
685: }
686:
687: pp = peer->procptr;
688: if(NULL == pp)
689: {
690: msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
691: return;
692: }
693:
694: up = (struct neoclock4x_unit *)pp->unitptr;
695: if(NULL == up)
696: {
697: msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit);
698: return;
699: }
700:
701: if(NULL != in)
702: {
703: /* check to see if a user supplied time offset is given */
704: if(in->haveflags & CLK_HAVETIME1)
705: {
706: pp->fudgetime1 = in->fudgetime1;
707: NLOG(NLOG_CLOCKINFO)
708: msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.",
709: unit, pp->fudgetime1);
710: }
711:
712: /* notify */
713: if(pp->sloppyclockflag & CLK_FLAG1)
714: {
715: NLOG(NLOG_CLOCKINFO)
716: msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit);
717: }
718: else
719: {
720: NLOG(NLOG_CLOCKINFO)
721: msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit);
722: }
723: }
724:
725: if(NULL != out)
726: {
727: char *tt;
728: char tmpbuf[80];
729:
730: out->kv_list = (struct ctl_var *)0;
731: out->type = REFCLK_NEOCLOCK4X;
732:
733: snprintf(tmpbuf, sizeof(tmpbuf)-1,
734: "%04d-%02d-%02d %02d:%02d:%02d.%03d",
735: up->utc_year, up->utc_month, up->utc_day,
736: up->utc_hour, up->utc_minute, up->utc_second,
737: up->utc_msec);
738: tt = add_var(&out->kv_list, sizeof(tmpbuf)-1, RO|DEF);
739: snprintf(tt, sizeof(tmpbuf)-1, "calc_utc=\"%s\"", tmpbuf);
740:
741: tt = add_var(&out->kv_list, 40, RO|DEF);
742: snprintf(tt, 39, "radiosignal=\"%s\"", up->radiosignal);
743: tt = add_var(&out->kv_list, 40, RO|DEF);
744: snprintf(tt, 39, "antenna1=\"%d\"", up->antenna1);
745: tt = add_var(&out->kv_list, 40, RO|DEF);
746: snprintf(tt, 39, "antenna2=\"%d\"", up->antenna2);
747: tt = add_var(&out->kv_list, 40, RO|DEF);
748: if('A' == up->timesource)
749: snprintf(tt, 39, "timesource=\"radio\"");
750: else if('C' == up->timesource)
751: snprintf(tt, 39, "timesource=\"quartz\"");
752: else
753: snprintf(tt, 39, "timesource=\"unknown\"");
754: tt = add_var(&out->kv_list, 40, RO|DEF);
755: if('I' == up->quarzstatus)
756: snprintf(tt, 39, "quartzstatus=\"synchronized\"");
757: else if('X' == up->quarzstatus)
758: snprintf(tt, 39, "quartzstatus=\"not synchronized\"");
759: else
760: snprintf(tt, 39, "quartzstatus=\"unknown\"");
761: tt = add_var(&out->kv_list, 40, RO|DEF);
762: if('S' == up->dststatus)
763: snprintf(tt, 39, "dststatus=\"summer\"");
764: else if('W' == up->dststatus)
765: snprintf(tt, 39, "dststatus=\"winter\"");
766: else
767: snprintf(tt, 39, "dststatus=\"unknown\"");
768: tt = add_var(&out->kv_list, 80, RO|DEF);
769: snprintf(tt, 79, "firmware=\"%s\"", up->firmware);
770: tt = add_var(&out->kv_list, 40, RO|DEF);
771: snprintf(tt, 39, "firmwaretag=\"%c\"", up->firmwaretag);
772: tt = add_var(&out->kv_list, 80, RO|DEF);
773: snprintf(tt, 79, "driver version=\"%s\"", NEOCLOCK4X_DRIVER_VERSION);
774: tt = add_var(&out->kv_list, 80, RO|DEF);
775: snprintf(tt, 79, "serialnumber=\"%s\"", up->serial);
776: }
777: }
778:
779: static int
780: neol_hexatoi_len(const char str[],
781: int *result,
782: int maxlen)
783: {
784: int hexdigit;
785: int i;
786: int n = 0;
787:
788: for(i=0; isxdigit((int)str[i]) && i < maxlen; i++)
789: {
790: hexdigit = isdigit((int)str[i]) ? toupper(str[i]) - '0' : toupper(str[i]) - 'A' + 10;
791: n = 16 * n + hexdigit;
792: }
793: *result = n;
794: return (n);
795: }
796:
797: static int
798: neol_atoi_len(const char str[],
799: int *result,
800: int maxlen)
801: {
802: int digit;
803: int i;
804: int n = 0;
805:
806: for(i=0; isdigit((int)str[i]) && i < maxlen; i++)
807: {
808: digit = str[i] - '0';
809: n = 10 * n + digit;
810: }
811: *result = n;
812: return (n);
813: }
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: {
838: if (0 >= (int) (mon -= 2)) { /* 1..12 . 11,12,1..10 */
839: mon += 12; /* Puts Feb last since it has leap day */
840: year -= 1;
841: }
842: return (((
843: (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
844: year*365 - 719499
845: )*24 + hour /* now have hours */
846: )*60 + min /* now have minutes */
847: )*60 + sec; /* finally seconds */
848: }
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: {
859: *sec = utc % 60;
860: utc /= 60;
861: *min = utc % 60;
862: utc /= 60;
863: *hour = utc % 24;
864: utc /= 24;
865:
866: /* JDN Date 1/1/1970 */
867: neol_jdn_to_ymd(utc + 2440588L, year, month, day);
868: }
869:
870: static void
871: neol_jdn_to_ymd(unsigned long jdn,
872: int *yy,
873: int *mm,
874: int *dd)
875: {
876: unsigned long x, z, m, d, y;
877: unsigned long daysPer400Years = 146097UL;
878: unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL;
879:
880: x = jdn + 68569UL;
881: z = 4UL * x / daysPer400Years;
882: x = x - (daysPer400Years * z + 3UL) / 4UL;
883: y = 4000UL * (x + 1) / fudgedDaysPer4000Years;
884: x = x - 1461UL * y / 4UL + 31UL;
885: m = 80UL * x / 2447UL;
886: d = x - 2447UL * m / 80UL;
887: x = m / 11UL;
888: m = m + 2UL - 12UL * x;
889: y = 100UL * (z - 49UL) + y + x;
890:
891: *yy = (int)y;
892: *mm = (int)m;
893: *dd = (int)d;
894: }
895:
896: #if !defined(NEOCLOCK4X_FIRMWARE)
897: static int
898: neol_query_firmware(int fd,
899: int unit,
900: char *firmware,
901: int maxlen)
902: {
903: char tmpbuf[256];
904: int len;
905: int lastsearch;
906: unsigned char c;
907: int last_c_was_crlf;
908: int last_crlf_conv_len;
909: int init;
910: int read_errors;
911: int flag = 0;
912: int chars_read;
913:
914: /* wait a little bit */
915: sleep(1);
916: if(-1 != write(fd, "V", 1))
917: {
918: /* wait a little bit */
919: sleep(1);
920: memset(tmpbuf, 0x00, sizeof(tmpbuf));
921:
922: len = 0;
923: lastsearch = 0;
924: last_c_was_crlf = 0;
925: last_crlf_conv_len = 0;
926: init = 1;
927: read_errors = 0;
928: chars_read = 0;
929: for(;;)
930: {
931: if(read_errors > 5)
932: {
933: msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit);
934: strcpy(tmpbuf, "unknown due to timeout");
935: break;
936: }
937: if(chars_read > 500)
938: {
939: msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (garbage)", unit);
940: strcpy(tmpbuf, "unknown due to garbage input");
941: break;
942: }
943: if(-1 == read(fd, &c, 1))
944: {
945: if(EAGAIN != errno)
946: {
947: msyslog(LOG_DEBUG, "NeoClock4x(%d): read: %s", unit ,strerror(errno));
948: read_errors++;
949: }
950: else
951: {
952: sleep(1);
953: }
954: continue;
955: }
956: else
957: {
958: chars_read++;
959: }
960:
961: if(init)
962: {
963: if(0xA9 != c) /* wait for (c) char in input stream */
964: continue;
965:
966: strcpy(tmpbuf, "(c)");
967: len = 3;
968: init = 0;
969: continue;
970: }
971:
972: #if 0
973: msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c);
974: #endif
975:
976: if(0x0A == c || 0x0D == c)
977: {
978: if(last_c_was_crlf)
979: {
980: char *ptr;
981: ptr = strstr(&tmpbuf[lastsearch], "S/N");
982: if(NULL != ptr)
983: {
984: tmpbuf[last_crlf_conv_len] = 0;
985: flag = 1;
986: break;
987: }
988: /* convert \n to / */
989: last_crlf_conv_len = len;
990: tmpbuf[len++] = ' ';
991: tmpbuf[len++] = '/';
992: tmpbuf[len++] = ' ';
993: lastsearch = len;
994: }
995: last_c_was_crlf = 1;
996: }
997: else
998: {
999: last_c_was_crlf = 0;
1000: if(0x00 != c)
1001: tmpbuf[len++] = (char) c;
1002: }
1003: tmpbuf[len] = '\0';
1004: if(len > sizeof(tmpbuf)-5)
1005: break;
1006: }
1007: }
1008: else
1009: {
1010: msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit);
1011: strcpy(tmpbuf, "unknown error");
1012: }
1013: strncpy(firmware, tmpbuf, maxlen);
1014: firmware[maxlen] = '\0';
1015:
1016: if(flag)
1017: {
1018: NLOG(NLOG_CLOCKINFO)
1019: msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware);
1020:
1021: if(strstr(firmware, "/R2"))
1022: {
1023: msyslog(LOG_INFO, "NeoClock4X(%d): Your NeoClock4X uses the new R2 firmware release. Please note the changed LED behaviour.", unit);
1024: }
1025:
1026: }
1027:
1028: return (flag);
1029: }
1030:
1031: static int
1032: neol_check_firmware(int unit,
1033: const char *firmware,
1034: char *firmwaretag)
1035: {
1036: char *ptr;
1037:
1038: *firmwaretag = '?';
1039: ptr = strstr(firmware, "NDF:");
1040: if(NULL != ptr)
1041: {
1042: if((strlen(firmware) - strlen(ptr)) >= 7)
1043: {
1044: if(':' == *(ptr+5) && '*' == *(ptr+6))
1045: *firmwaretag = *(ptr+4);
1046: }
1047: }
1048:
1049: if('A' != *firmwaretag)
1050: {
1051: msyslog(LOG_CRIT, "NeoClock4X(%d): firmware version \"%c\" not supported with this driver version!", unit, *firmwaretag);
1052: return (0);
1053: }
1054:
1055: return (1);
1056: }
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:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>