Coverage Report

Created: 2026-02-26 06:20

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