freebsd-dev/usr.sbin/xntpd/xntpd/refclock_local.c
1994-02-03 22:09:07 +00:00

308 lines
7.4 KiB
C

/*
* refclock_local - local pseudo-clock driver
*/
#if defined(REFCLOCK) && defined(LOCAL_CLOCK)
#include <stdio.h>
#include <ctype.h>
#include <sys/time.h>
#include "ntpd.h"
#include "ntp_refclock.h"
#include "ntp_stdlib.h"
static void local_init P((void));
static int local_start P((u_int, struct peer *));
static void local_shutdown P((int));
static void local_poll P((int, struct peer *));
static void local_control P((u_int, struct refclockstat *, struct refclockstat *));
#define local_buginfo noentry
struct refclock refclock_local = {
local_start, local_shutdown, local_poll,
local_control, local_init, local_buginfo, NOFLAGS
};
/*
* This is a hack to allow a machine to use its own system clock as
* a "reference clock", i.e. to free run against its own clock at
* a non-infinity stratum. This is certainly useful if you want to
* use NTP in an isolated environment with no radio clock (not that
* this is a good idea) to synchronize the machines together. Pick
* a machine that you figure has a good clock and configure it with
* a local reference clock running at stratum 0 (i.e. 127.127.1.0).
* Then point all the other machines at the one you're using as the
* reference.
*
* The other thing this is good for is if you want to use a particular
* server's clock as the last resort, when all radio time has gone
* away. This is especially good if that server has an ovenized
* oscillator or something which will keep the time stable for extended
* periods, since then all the other machines can benefit from this.
* For this you would configure a local clock at a higher stratum (say
* 3 or 4) to prevent the server's stratum from falling below here.
*/
/*
* Definitions
*/
#define NUMUNITS 16 /* 127.127.1.[0-15] */
/*
* Some constant values we stick in the peer structure
*/
#define LCLDISPERSION (FP_SECOND/5) /* 200 ms dispersion */
#define LCLROOTDISPERSION (FP_SECOND/5) /* 200 ms root dispersion */
#define LCLPRECISION (-5) /* what the heck */
#define LCLREFID "LCL\0"
#define LCLREFOFFSET 20 /* reftime is 20s behind */
#define LCLHSREFID 0x7f7f0101 /* 127.127.1.1 refid for hi stratum */
/*
* Description of clock
*/
#define LCLDESCRIPTION "Free running against local system clock"
/*
* Local clock unit control structure.
*/
struct lclunit {
struct peer *peer; /* associated peer structure */
u_char status; /* clock status */
u_char lastevent; /* last clock event */
u_char unit; /* unit number */
u_char unused;
U_LONG lastupdate; /* last time data received */
U_LONG polls; /* number of polls */
U_LONG timestarted; /* time we started this */
};
/*
* Data space for the unit structures. Note that we allocate these on
* the fly, but never give them back.
*/
static struct lclunit *lclunits[NUMUNITS];
static u_char unitinuse[NUMUNITS];
/*
* Imported from the timer module
*/
extern U_LONG current_time;
extern l_fp sys_clock_offset;
/*
* local_init - initialize internal local clock driver data
*/
static void
local_init()
{
/*
* Just zero the data arrays
*/
memset((char *)lclunits, 0, sizeof lclunits);
memset((char *)unitinuse, 0, sizeof unitinuse);
}
/*
* local_start - start up a local reference clock
*/
static int
local_start(unit, peer)
u_int unit;
struct peer *peer;
{
register int i;
register struct lclunit *lcl;
if (unit >= NUMUNITS) {
syslog(LOG_ERR, "local clock: unit number %d invalid (max 15)",
unit);
return 0;
}
if (unitinuse[unit]) {
syslog(LOG_ERR, "local clock: unit number %d in use", unit);
return 0;
}
/*
* Looks like this might succeed. Find memory for the structure.
* Look to see if there are any unused ones, if not we malloc()
* one.
*/
if (lclunits[unit] != 0) {
lcl = lclunits[unit]; /* The one we want is okay */
} else {
for (i = 0; i < NUMUNITS; i++) {
if (!unitinuse[i] && lclunits[i] != 0)
break;
}
if (i < NUMUNITS) {
/*
* Reclaim this one
*/
lcl = lclunits[i];
lclunits[i] = 0;
} else {
lcl = (struct lclunit *)emalloc(sizeof(struct lclunit));
}
}
memset((char *)lcl, 0, sizeof(struct lclunit));
lclunits[unit] = lcl;
/*
* Set up the structure
*/
lcl->peer = peer;
lcl->unit = (u_char)unit;
lcl->timestarted = lcl->lastupdate = current_time;
/*
* That was easy. Diddle the peer variables and return success.
*/
peer->precision = LCLPRECISION;
peer->rootdelay = 0;
peer->rootdispersion = LCLROOTDISPERSION;
peer->stratum = (u_char)unit;
if (unit <= 1)
memmove((char *)&peer->refid, LCLREFID, 4);
else
peer->refid = htonl(LCLHSREFID);
unitinuse[unit] = 1;
return 1;
}
/*
* local_shutdown - shut down a local clock
*/
static void
local_shutdown(unit)
int unit;
{
if (unit >= NUMUNITS) {
syslog(LOG_ERR,
"local clock: INTERNAL ERROR, unit number %d invalid (max 15)",
unit);
return;
}
if (!unitinuse[unit]) {
syslog(LOG_ERR,
"local clock: INTERNAL ERROR, unit number %d not in use", unit);
return;
}
unitinuse[unit] = 0;
}
/*
* local_poll - called by the transmit procedure
*/
static void
local_poll(unit, peer)
int unit;
struct peer *peer;
{
l_fp off;
l_fp ts;
if (unit >= NUMUNITS) {
syslog(LOG_ERR, "local clock poll: INTERNAL: unit %d invalid",
unit);
return;
}
if (!unitinuse[unit]) {
syslog(LOG_ERR, "local clock poll: INTERNAL: unit %d unused",
unit);
return;
}
if (peer != lclunits[unit]->peer) {
syslog(LOG_ERR,
"local clock poll: INTERNAL: peer incorrect for unit %d",
unit);
return;
}
/*
* Update clock stat counters
*/
lclunits[unit]->polls++;
lclunits[unit]->lastupdate = current_time;
/*
* This is pretty easy. Give the reference clock support
* a zero offset and our fixed dispersion. Use peer->xmt for
* our receive time. Use peer->xmt - 20 seconds for our
* reference time.
*/
off.l_ui = off.l_uf = 0;
ts = peer->xmt;
ts.l_ui -= LCLREFOFFSET;
refclock_receive(peer, &off, 0, LCLDISPERSION,
&ts, &peer->xmt, 0);
}
/*
* local_control - set fudge factors, return statistics
*/
static void
local_control(unit, in, out)
u_int unit;
struct refclockstat *in;
struct refclockstat *out;
{
extern s_fp drift_comp;
if (unit >= NUMUNITS) {
syslog(LOG_ERR, "local clock: unit %d invalid (max %d)",
unit, NUMUNITS-1);
return;
}
/*
* The time1 fudge factor is the drift compensation register.
* The time2 fudge factor is the offset of the system clock from
* what the protocol has set it to be. Most useful when SLEWALWAYS
* is defined.
*/
if (in != 0) {
if (in->haveflags & CLK_HAVETIME1)
drift_comp = LFPTOFP(&in->fudgetime1);
if (in->haveflags & CLK_HAVETIME2) {
sys_clock_offset.l_ui = in->fudgetime2.l_ui;
sys_clock_offset.l_uf = in->fudgetime2.l_uf;
}
}
if (out != 0) {
out->type = REFCLK_LOCALCLOCK;
out->flags = 0;
out->haveflags = CLK_HAVETIME1;
out->clockdesc = LCLDESCRIPTION;
FPTOLFP(drift_comp, &out->fudgetime1);
out->fudgetime2.l_ui = sys_clock_offset.l_ui;
out->fudgetime2.l_uf = sys_clock_offset.l_uf;
out->fudgeval1 = out->fudgeval2 = 0;
out->lencode = 0;
out->lastcode = "";
out->badformat = 0;
out->baddata = 0;
out->noresponse = 0;
if (unitinuse[unit]) {
out->polls = lclunits[unit]->polls;
out->timereset =
current_time - lclunits[unit]->timestarted;
out->lastevent = lclunits[unit]->lastevent;
out->currentstatus = lclunits[unit]->status;
} else {
out->polls = 0;
out->timereset = 0;
out->currentstatus = out->lastevent = CEVNT_NOMINAL;
}
}
}
#endif /* REFCLOCK */