Coverage Report

Created: 2023-05-19 06:16

/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