Coverage Report

Created: 2023-05-19 06:16

/src/ntp-dev/ntpd/refclock_dumbclock.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * refclock_dumbclock - clock driver for a unknown time distribution system
3
 * that only provides hh:mm:ss (in local time, yet!).
4
 */
5
6
/*
7
 * Must interpolate back to local time.  Very annoying.
8
 */
9
#define GET_LOCALTIME
10
11
#ifdef HAVE_CONFIG_H
12
#include <config.h>
13
#endif
14
15
#if defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK)
16
17
#include "ntpd.h"
18
#include "ntp_io.h"
19
#include "ntp_refclock.h"
20
#include "ntp_calendar.h"
21
#include "ntp_stdlib.h"
22
23
#include <stdio.h>
24
#include <ctype.h>
25
26
#ifdef SYS_WINNT
27
extern int async_write(int, const void *, unsigned int);
28
#undef write
29
#define write(fd, data, octets) async_write(fd, data, octets)
30
#endif
31
32
/*
33
 * This driver supports a generic dumb clock that only outputs hh:mm:ss,
34
 * in local time, no less.
35
 *
36
 * Input format:
37
 *
38
 *  hh:mm:ss   <cr>
39
 *
40
 * hh:mm:ss -- what you'd expect, with a 24 hour clock.  (Heck, that's the only
41
 * way it could get stupider.)  We take time on the <cr>.
42
 *
43
 * The original source of this module was the WWVB module.
44
 */
45
46
/*
47
 * Interface definitions
48
 */
49
#define DEVICE    "/dev/dumbclock%d" /* device name and unit */
50
0
#define SPEED232  B9600  /* uart speed (9600 baud) */
51
0
#define PRECISION (-13)  /* precision assumed (about 100 us) */
52
0
#define REFID   "dumbclock"  /* reference ID */
53
0
#define DESCRIPTION "Dumb clock" /* WRU */
54
55
56
/*
57
 * Insanity check.  Since the time is local, we need to make sure that during midnight
58
 * transitions, we can convert back to Unix time.  If the conversion results in some number
59
 * worse than this number of seconds away, assume the next day and retry.
60
 */
61
0
#define INSANE_SECONDS 3600
62
63
/*
64
 * Dumb clock control structure
65
 */
66
struct dumbclock_unit {
67
  u_char    tcswitch; /* timecode switch */
68
  l_fp    laststamp;  /* last receive timestamp */
69
  u_char    lasthour; /* last hour (for monitor) */
70
  u_char    linect; /* count ignored lines (for monitor */
71
  struct tm ymd;    /* struct tm for y/m/d only */
72
};
73
74
/*
75
 * Function prototypes
76
 */
77
static  int dumbclock_start   (int, struct peer *);
78
static  void  dumbclock_shutdown  (int, struct peer *);
79
static  void  dumbclock_receive (struct recvbuf *);
80
#if 0
81
static  void  dumbclock_poll    (int, struct peer *);
82
#endif
83
84
/*
85
 * Transfer vector
86
 */
87
struct  refclock refclock_dumbclock = {
88
  dumbclock_start,         /* start up driver */
89
  dumbclock_shutdown,        /* shut down driver */
90
  noentry,           /* poll the driver -- a nice fabrication */
91
  noentry,           /* not used */
92
  noentry,           /* not used */
93
  noentry,           /* not used */
94
  NOFLAGS            /* not used */
95
};
96
97
98
/*
99
 * dumbclock_start - open the devices and initialize data for processing
100
 */
101
static int
102
dumbclock_start(
103
  int unit,
104
  struct peer *peer
105
  )
106
0
{
107
0
  register struct dumbclock_unit *up;
108
0
  struct refclockproc *pp;
109
0
  int fd;
110
0
  char device[20];
111
0
  struct tm *tm_time_p;
112
0
  time_t     now;
113
114
  /*
115
   * Open serial port. Don't bother with CLK line discipline, since
116
   * it's not available.
117
   */
118
0
  snprintf(device, sizeof(device), DEVICE, unit);
119
0
#ifdef DEBUG
120
0
  if (debug)
121
0
    printf ("starting Dumbclock with device %s\n",device);
122
0
#endif
123
0
  fd = refclock_open(device, SPEED232, 0);
124
0
  if (fd <= 0)
125
0
    return (0);
126
127
  /*
128
   * Allocate and initialize unit structure
129
   */
130
0
  up = emalloc_zero(sizeof(*up));
131
0
  pp = peer->procptr;
132
0
  pp->unitptr = up;
133
0
  pp->io.clock_recv = dumbclock_receive;
134
0
  pp->io.srcclock = peer;
135
0
  pp->io.datalen = 0;
136
0
  pp->io.fd = fd;
137
0
  if (!io_addclock(&pp->io)) {
138
0
    close(fd);
139
0
    pp->io.fd = -1;
140
0
    free(up);
141
0
    pp->unitptr = NULL;
142
0
    return (0);
143
0
  }
144
145
146
0
  time(&now);
147
0
#ifdef GET_LOCALTIME
148
0
  tm_time_p = localtime(&now);
149
#else
150
  tm_time_p = gmtime(&now);
151
#endif
152
0
  if (tm_time_p)
153
0
    up->ymd = *tm_time_p;
154
0
  else
155
0
    return 0;
156
157
  /*
158
   * Initialize miscellaneous variables
159
   */
160
0
  peer->precision = PRECISION;
161
0
  pp->clockdesc = DESCRIPTION;
162
0
  memcpy((char *)&pp->refid, REFID, 4);
163
0
  return (1);
164
0
}
165
166
167
/*
168
 * dumbclock_shutdown - shut down the clock
169
 */
170
static void
171
dumbclock_shutdown(
172
  int unit,
173
  struct peer *peer
174
  )
175
0
{
176
0
  register struct dumbclock_unit *up;
177
0
  struct refclockproc *pp;
178
179
0
  pp = peer->procptr;
180
0
  up = pp->unitptr;
181
0
  if (-1 != pp->io.fd)
182
0
    io_closeclock(&pp->io);
183
0
  if (NULL != up)
184
0
    free(up);
185
0
}
186
187
188
/*
189
 * dumbclock_receive - receive data from the serial interface
190
 */
191
static void
192
dumbclock_receive(
193
  struct recvbuf *rbufp
194
  )
195
0
{
196
0
  struct dumbclock_unit *up;
197
0
  struct refclockproc *pp;
198
0
  struct peer *peer;
199
200
0
  l_fp  trtmp;    /* arrival timestamp */
201
0
  int hours;    /* hour-of-day */
202
0
  int minutes;  /* minutes-past-the-hour */
203
0
  int seconds;  /* seconds */
204
0
  int temp;   /* int temp */
205
0
  int got_good; /* got a good time flag */
206
207
  /*
208
   * Initialize pointers and read the timecode and timestamp
209
   */
210
0
  peer = rbufp->recv_peer;
211
0
  pp = peer->procptr;
212
0
  up = pp->unitptr;
213
0
  temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
214
215
0
  if (temp == 0) {
216
0
    if (up->tcswitch == 0) {
217
0
      up->tcswitch = 1;
218
0
      up->laststamp = trtmp;
219
0
    } else
220
0
      up->tcswitch = 0;
221
0
    return;
222
0
  }
223
0
  pp->lencode = (u_short)temp;
224
0
  pp->lastrec = up->laststamp;
225
0
  up->laststamp = trtmp;
226
0
  up->tcswitch = 1;
227
228
0
#ifdef DEBUG
229
0
  if (debug)
230
0
    printf("dumbclock: timecode %d %s\n",
231
0
           pp->lencode, pp->a_lastcode);
232
0
#endif
233
234
  /*
235
   * We get down to business. Check the timecode format...
236
   */
237
0
  got_good=0;
238
0
  if (sscanf(pp->a_lastcode,"%02d:%02d:%02d",
239
0
       &hours,&minutes,&seconds) == 3)
240
0
  {
241
0
      struct tm *gmtp;
242
0
      struct tm *lt_p;
243
0
      time_t     asserted_time;      /* the SPM time based on the composite time+date */
244
0
      struct tm  asserted_tm;      /* the struct tm of the same */
245
0
      int        adjyear;
246
0
      int        adjmon;
247
0
      time_t     reality_delta;
248
0
      time_t     now;
249
250
251
      /*
252
       * Convert to GMT for sites that distribute localtime.  This
253
       * means we have to figure out what day it is.  Easier said
254
       * than done...
255
       */
256
257
0
      memset(&asserted_tm, 0, sizeof(asserted_tm));
258
259
0
      asserted_tm.tm_year  = up->ymd.tm_year;
260
0
      asserted_tm.tm_mon   = up->ymd.tm_mon;
261
0
      asserted_tm.tm_mday  = up->ymd.tm_mday;
262
0
      asserted_tm.tm_hour  = hours;
263
0
      asserted_tm.tm_min   = minutes;
264
0
      asserted_tm.tm_sec   = seconds;
265
0
      asserted_tm.tm_isdst = -1;
266
267
0
#ifdef GET_LOCALTIME
268
0
      asserted_time = mktime (&asserted_tm);
269
0
      time(&now);
270
#else
271
#include "GMT unsupported for dumbclock!"
272
#endif
273
0
      reality_delta = asserted_time - now;
274
275
      /*
276
       * We assume that if the time is grossly wrong, it's because we got the
277
       * year/month/day wrong.
278
       */
279
0
      if (reality_delta > INSANE_SECONDS)
280
0
      {
281
0
    asserted_time -= SECSPERDAY; /* local clock behind real time */
282
0
      }
283
0
      else if (-reality_delta > INSANE_SECONDS)
284
0
      {
285
0
    asserted_time += SECSPERDAY; /* local clock ahead of real time */
286
0
      }
287
0
      lt_p = localtime(&asserted_time);
288
0
      if (lt_p)
289
0
      {
290
0
    up->ymd = *lt_p;
291
0
      }
292
0
      else
293
0
      {
294
0
    refclock_report (peer, CEVNT_FAULT);
295
0
    return;
296
0
      }
297
298
0
      if ((gmtp = gmtime (&asserted_time)) == NULL)
299
0
      {
300
0
    refclock_report (peer, CEVNT_FAULT);
301
0
    return;
302
0
      }
303
0
      adjyear = gmtp->tm_year+1900;
304
0
      adjmon  = gmtp->tm_mon+1;
305
0
      pp->day = ymd2yd (adjyear, adjmon, gmtp->tm_mday);
306
0
      pp->hour   = gmtp->tm_hour;
307
0
      pp->minute = gmtp->tm_min;
308
0
      pp->second = gmtp->tm_sec;
309
0
#ifdef DEBUG
310
0
      if (debug)
311
0
    printf ("time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
312
0
      adjyear,adjmon,gmtp->tm_mday,pp->hour,pp->minute,
313
0
      pp->second);
314
0
#endif
315
316
0
      got_good=1;
317
0
  }
318
319
0
  if (!got_good)
320
0
  {
321
0
      if (up->linect > 0)
322
0
        up->linect--;
323
0
      else
324
0
        refclock_report(peer, CEVNT_BADREPLY);
325
0
      return;
326
0
  }
327
328
  /*
329
   * Process the new sample in the median filter and determine the
330
   * timecode timestamp.
331
   */
332
0
  if (!refclock_process(pp)) {
333
0
    refclock_report(peer, CEVNT_BADTIME);
334
0
    return;
335
0
  }
336
0
  pp->lastref = pp->lastrec;
337
0
  refclock_receive(peer);
338
0
  record_clock_stats(&peer->srcadr, pp->a_lastcode);
339
0
  up->lasthour = (u_char)pp->hour;
340
0
}
341
342
#if 0
343
/*
344
 * dumbclock_poll - called by the transmit procedure
345
 */
346
static void
347
dumbclock_poll(
348
  int unit,
349
  struct peer *peer
350
  )
351
{
352
  register struct dumbclock_unit *up;
353
  struct refclockproc *pp;
354
  char pollchar;
355
356
  /*
357
   * Time to poll the clock. The Chrono-log clock is supposed to
358
   * respond to a 'T' by returning a timecode in the format(s)
359
   * specified above.  Ours does (can?) not, but this seems to be
360
   * an installation-specific problem.  This code is dyked out,
361
   * but may be re-enabled if anyone ever finds a Chrono-log that
362
   * actually listens to this command.
363
   */
364
#if 0
365
  pp = peer->procptr;
366
  up = pp->unitptr;
367
  if (peer->reach == 0)
368
    refclock_report(peer, CEVNT_TIMEOUT);
369
  if (up->linect > 0)
370
    pollchar = 'R';
371
  else
372
    pollchar = 'T';
373
  if (write(pp->io.fd, &pollchar, 1) != 1)
374
    refclock_report(peer, CEVNT_FAULT);
375
  else
376
    pp->polls++;
377
#endif
378
}
379
#endif
380
381
#else
382
int refclock_dumbclock_bs;
383
#endif  /* defined(REFCLOCK) && defined(CLOCK_DUMBCLOCK) */