📄 ntp_refclock.c
字号:
/* * ntp_refclock - processing support for reference clocks */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <stdio.h>#include <errno.h>#include <sys/types.h>#ifdef HAVE_SYS_IOCTL_H# include <sys/ioctl.h>#endif /* HAVE_SYS_IOCTL_H */#include "ntpd.h"#include "ntp_io.h"#include "ntp_unixtime.h"#include "ntp_refclock.h"#include "ntp_stdlib.h"#ifdef REFCLOCK#ifdef TTYCLK#include <sys/clkdefs.h>#endif /* TTYCLK */#ifdef CHUCLK#include <sys/chudefs.h>#endif /* CHUCLK */#ifdef PPS#include <sys/ppsclock.h>#endif /* PPS *//* * Reference clock support is provided here by maintaining the fiction * that the clock is actually a peer. As no packets are exchanged with a * reference clock, however, we replace the transmit, receive and packet * procedures with separate code to simulate them. Routines * refclock_transmit() and refclock_receive() maintain the peer * variables in a state analogous to an actual peer and pass reference * clock data on through the filters. Routines refclock_peer() and * refclock_unpeer() are called to initialize and terminate reference * clock associations. A set of utility routines is included to open * serial devices, process sample data, edit input lines to extract * embedded timestamps and to peform various debugging functions. * * The main interface used by these routines is the refclockproc * structure, which contains for most drivers the decimal equivalants of * the year, day, month, hour, second and millisecond/microsecond * decoded from the ASCII timecode. Additional information includes the * receive timestamp, exception report, statistics tallies, etc. In * addition, there may be a driver-specific unit structure used for * local control of the device. * * The support routines are passed a pointer to the peer structure, * which is used for all peer-specific processing and contains a pointer * to the refclockproc structure, which in turn containes a pointer to * the unit structure, if used. In addition, some routines expect an * address in the dotted quad form 127.127.t.u, where t is the clock * type and u the unit. A table typeunit[type][unit] contains the peer * structure pointer for each configured clock type and unit. * * Most drivers support the 1-pps signal provided by some radios and * connected via a level converted described in the gadget directory. * The signal is captured using a separate, dedicated serial port and * the tty_clk line discipline/streams modules described in the kernel * directory. For the highest precision, the signal is captured using * the carrier-detect line of the same serial port using the ppsclock * streams module described in the ppsclock directory. */#define REFCLOCKMAXDISPERSE (FP_SECOND/4) /* max sample dispersion */#define MAXUNIT 4 /* max units */#ifndef CLKLDISC#define CLKLDISC 10 /* XXX temp tty_clk line discipline */#endif#ifndef CHULDISC#define CHULDISC 10 /* XXX temp tty_chu line discipline */#endif/* * The refclock configuration table. Imported from refclock_conf */extern struct refclock *refclock_conf[];extern u_char num_refclock_conf;/* * Imported from the I/O module */extern struct interface *any_interface;extern struct interface *loopback_interface;/* * Imported from ntp_loopfilter module */extern int fdpps; /* pps file descriptor */#ifdef PPSextern int pps_enable; /* pps enabled indicator (from ntp_loopfilter.c) */#endif /* PPS *//* * Imported from the timer module */extern u_long current_time;extern struct event timerqueue[];/* * Imported from the main and peer modules. We use the same algorithm * for spacing out timers at configuration time that the peer module * does. */extern u_long init_peer_starttime;extern int initializing;extern int debug;/* * Type/unit peer index. Used to find the peer structure for control and * debugging. When all clock drivers have been converted to new style, * this dissapears. */static struct peer *typeunit[REFCLK_MAX + 1][MAXUNIT];/* * Forward declarations */static int refclock_cmpl_fp P((const void *, const void *));/* * refclock_report - note the occurance of an event * * This routine presently just remembers the report and logs it, but * does nothing heroic for the trap handler. It tries to be a good * citizen and bothers the system log only if things change. */voidrefclock_report(peer, code) struct peer *peer; u_int code;{ struct refclockproc *pp; if (!(pp = peer->procptr)) return; if (code == CEVNT_BADREPLY) pp->badformat++; if (code == CEVNT_BADTIME) pp->baddata++; if (code == CEVNT_TIMEOUT) pp->noreply++; if (pp->currentstatus != code) { pp->currentstatus = code; pp->lastevent = code; if (code == CEVNT_FAULT) NLOG(NLOG_CLOCKEVENT) msyslog(LOG_ERR, "clock %s fault '%s' (0x%02x)", refnumtoa(peer->srcadr.sin_addr.s_addr), ceventstr(code), code); else { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_INFO, "clock %s event '%s' (0x%02x)", refnumtoa(peer->srcadr.sin_addr.s_addr), ceventstr(code), code); } }}/* * init_refclock - initialize the reference clock drivers * * This routine calls each of the drivers in turn to initialize internal * variables, if necessary. Most drivers have nothing to say at this * point. */voidinit_refclock(){ int i, j; for (i = 0; i < (int)num_refclock_conf; i++) { if (refclock_conf[i]->clock_init != noentry) (refclock_conf[i]->clock_init)(); for (j = 0; j < MAXUNIT; j++) typeunit[i][j] = 0; }}/* * refclock_newpeer - initialize and start a reference clock * * This routine allocates and initializes the interface structure which * supports a reference clock in the form of an ordinary NTP peer. A * driver-specific support routine completes the initialization, if * used. Default peer variables which identify the clock and establish * its reference ID and stratum are set here. It returns one if success * and zero if the clock address is invalid or already running, * insufficient resources are available or the driver declares a bum * rap. */intrefclock_newpeer(peer) struct peer *peer; /* peer structure pointer */{ struct refclockproc *pp; u_char clktype; int unit; /* * Check for valid clock address. If already running, shut it * down first. */ if (!ISREFCLOCKADR(&peer->srcadr)) { msyslog(LOG_ERR, "refclock_newpeer: clock address %s invalid", ntoa(&peer->srcadr)); return (0); } clktype = (u_char)REFCLOCKTYPE(&peer->srcadr); unit = REFCLOCKUNIT(&peer->srcadr); if (clktype >= num_refclock_conf || unit > MAXUNIT || refclock_conf[clktype]->clock_start == noentry) { msyslog(LOG_ERR, "refclock_newpeer: clock type %d invalid\n", clktype); return (0); } refclock_unpeer(peer); /* * Allocate and initialize interface structure */ if (!(pp = (struct refclockproc *) emalloc(sizeof(struct refclockproc)))) return (0); memset((char *)pp, 0, sizeof(struct refclockproc)); typeunit[clktype][unit] = peer; peer->procptr = pp; /* * Initialize structures */ peer->refclktype = clktype; peer->refclkunit = unit; peer->flags |= FLAG_REFCLOCK; peer->event_timer.peer = peer; peer->event_timer.event_handler = refclock_transmit; pp->type = clktype; pp->timestarted = current_time; peer->stratum = STRATUM_REFCLOCK; peer->refid = peer->srcadr.sin_addr.s_addr; peer->maxpoll = peer->minpoll; /* * Do driver dependent initialization */ if (!((refclock_conf[clktype]->clock_start)(unit, peer))) { free(pp); return (0); } peer->hpoll = peer->minpoll; peer->ppoll = peer->maxpoll; if (peer->stratum <= 1) peer->refid = pp->refid; else peer->refid = peer->srcadr.sin_addr.s_addr; /* * Set up the timeout for polling and reachability determination */ if (initializing) { init_peer_starttime += (1 << EVENT_TIMEOUT); if (init_peer_starttime >= (u_long)(1 << peer->minpoll)) init_peer_starttime = (1 << EVENT_TIMEOUT); peer->event_timer.event_time = init_peer_starttime; } else { peer->event_timer.event_time = current_time + (1 << peer->hpoll); } TIMER_ENQUEUE(timerqueue, &peer->event_timer); return (1);}/* * refclock_unpeer - shut down a clock */voidrefclock_unpeer(peer) struct peer *peer; /* peer structure pointer */{ u_char clktype; int unit; /* * Wiggle the driver to release its resources, then give back * the interface structure. */ if (!peer->procptr) return; clktype = peer->refclktype; unit = peer->refclkunit; if (refclock_conf[clktype]->clock_shutdown != noentry) (refclock_conf[clktype]->clock_shutdown)(unit, peer); free(peer->procptr); peer->procptr = 0;}/* * refclock_transmit - simulate the transmit procedure * * This routine implements the NTP transmit procedure for a reference * clock. This provides a mechanism to call the driver at the NTP poll * interval, as well as provides a reachability mechanism to detect a * broken radio or other madness. */voidrefclock_transmit(peer) struct peer *peer; /* peer structure pointer */{ struct refclockproc *pp; u_char clktype; int unit; u_char opeer_reach; pp = peer->procptr; clktype = peer->refclktype; unit = peer->refclkunit; peer->sent++; /* * The transmit procedure is supposed to freeze a timestamp. * Get one just for fun, and to tell when we last were here. */ get_systime(&peer->xmt); /* * Fiddle reachability. */ opeer_reach = peer->reach; peer->reach <<= 1; if (peer->reach == 0) { /* * Clear this one out. No need to redo selection since * this fellow will definitely be suffering from * dispersion madness. */ if (opeer_reach != 0) { peer_clear(peer); peer->timereachable = current_time; report_event(EVNT_UNREACH, peer); } /* * Update reachability and poll variables */ } else if ((opeer_reach & 3) == 0) { l_fp off; if (peer->valid > 0) peer->valid--; L_CLR(&off); clock_filter(peer, &off, 0, NTP_MAXDISPERSE); if (peer->flags & FLAG_SYSPEER) clock_select(); } else if (peer->valid < NTP_SHIFT) peer->valid++; /* * If he wants to be polled, do it. New style drivers do not use * the unit argument, since the fudge stuff is in the * refclockproc structure. */ if (refclock_conf[clktype]->clock_poll != noentry) (refclock_conf[clktype]->clock_poll)(unit, peer); /* * Finally, reset the timer */ peer->event_timer.event_time += (1 << peer->hpoll); TIMER_ENQUEUE(timerqueue, &peer->event_timer);}/* * Compare two l_fp's - used with qsort() */static intrefclock_cmpl_fp(p1, p2) register const void *p1, *p2; /* l_fp to compare */{ if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2)) return (-1); if (L_ISEQU((l_fp *)p1, (l_fp *)p2)) return (0); return (1);}/* * refclock_process - process a pile of samples from the clock * * This routine converts the timecode in the form days, hours, miinutes, * seconds, milliseconds/microseconds to internal timestamp format. * Further processing is then delegated to refclock sample */intrefclock_process(pp, nstart, nskeep) struct refclockproc *pp; /* peer structure pointer */ int nstart; /* stages of median filter */ int nskeep; /* stages after outlyer trim */{ l_fp offset; /* * Compute the timecode timestamp from the days, hours, minutes, * seconds and milliseconds/microseconds of the timecode. Use * clocktime() for the aggregate seconds and the msec/usec for * the fraction, when present. Note that this code relies on the * filesystem time for the years and does not use the years of * the timecode. */ if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT, pp->lastrec.l_ui, &pp->yearstart, &offset.l_ui)) {#ifdef DEBUG FILE* fdToWrite = fopen("/tmp/ntp.out","w"); fprintf(fdToWrite,"Error in refclock_process clocktime \n"); fprintf(fdToWrite,"%d %d %d %d %d %u %u %d\n", pp->day, pp->hour, pp->minute, pp->second, GMT, pp->lastrec.l_ui, pp->yearstart, offset.l_ui); fclose (fdToWrite);#endif return (0); } if (pp->usec) { TVUTOTSF(pp->usec, offset.l_uf); } else { MSUTOTSF(pp->msec, offset.l_uf); } L_ADD(&offset, &pp->fudgetime1); pp->lastref = offset; /* save last reference time */ /* * Include the configured fudgetime1 adjustment. */ L_SUB(&offset, &pp->lastrec); /* form true offset */ return refclock_sample(&offset, pp, nstart, nskeep);}/* * refclock_sample - process a pile of samples from the clock * * This routine converts the timecode in the form days, hours, miinutes, * seconds, milliseconds/microseconds to internal timestamp format. It * then calculates the difference from the receive timestamp and
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -