#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_unixtime.h"
#include "ntp_refclock.h"
#include "ntp_stdlib.h"
#if defined(REFCLOCK) && defined(CLOCK_ATOM)
#ifdef HAVE_PPSAPI
# ifdef HAVE_TIMEPPS_H
# include <timepps.h>
# else
# ifdef HAVE_SYS_TIMEPPS_H
# include <sys/timepps.h>
# endif
# endif
#endif
#ifdef HAVE_PPSAPI
extern int pps_assert;
extern int pps_hardpps;
#define DEVICE "/dev/pps%d"
#endif
#define PRECISION (-20)
#define REFID "PPS\0"
#define DESCRIPTION "PPS Clock Discipline"
#define NANOSECOND 1000000000
#define RANGEGATE 500000
#define ASTAGE 8
static struct peer *pps_peer;
#ifdef HAVE_PPSAPI
struct ppsunit {
struct timespec ts;
int fddev;
pps_params_t pps_params;
pps_info_t pps_info;
pps_handle_t handle;
};
#endif
static int atom_start P((int, struct peer *));
static void atom_poll P((int, struct peer *));
#ifdef HAVE_PPSAPI
static void atom_shutdown P((int, struct peer *));
static void atom_control P((int, struct refclockstat *, struct
refclockstat *, struct peer *));
static int atom_pps P((struct peer *));
static int atom_ppsapi P((struct peer *, int, int));
#endif
struct refclock refclock_atom = {
atom_start,
#ifdef HAVE_PPSAPI
atom_shutdown,
#else
noentry,
#endif
atom_poll,
#ifdef HAVE_PPSAPI
atom_control,
#else
noentry,
#endif
noentry,
noentry,
NOFLAGS
};
static int
atom_start(
int unit,
struct peer *peer
)
{
struct refclockproc *pp;
#ifdef HAVE_PPSAPI
register struct ppsunit *up;
char device[80];
#endif
pps_peer = peer;
pp = peer->procptr;
peer->precision = PRECISION;
pp->clockdesc = DESCRIPTION;
memcpy((char *)&pp->refid, REFID, 4);
peer->burst = ASTAGE;
peer->stratum = STRATUM_UNSPEC;
#ifdef HAVE_PPSAPI
up = emalloc(sizeof(struct ppsunit));
memset(up, 0, sizeof(struct ppsunit));
pp->unitptr = (caddr_t)up;
sprintf(device, DEVICE, unit);
up->fddev = open(device, O_RDWR, 0777);
if (up->fddev <= 0 && fdpps > 0) {
strcpy(device, pps_device);
up->fddev = fdpps;
}
if (up->fddev <= 0) {
msyslog(LOG_ERR,
"refclock_atom: %s: %m", device);
return (0);
}
if (time_pps_create(up->fddev, &up->handle) < 0) {
msyslog(LOG_ERR,
"refclock_atom: time_pps_create failed: %m");
return (0);
}
return (atom_ppsapi(peer, pps_assert, pps_hardpps));
#else
return (1);
#endif
}
#ifdef HAVE_PPSAPI
static void
atom_control(
int unit,
struct refclockstat *in,
struct refclockstat *out,
struct peer *peer
)
{
struct refclockproc *pp;
pp = peer->procptr;
atom_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2,
pp->sloppyclockflag & CLK_FLAG3);
}
int
atom_ppsapi(
struct peer *peer,
int enb_clear,
int enb_hardpps
)
{
struct refclockproc *pp;
register struct ppsunit *up;
int capability;
pp = peer->procptr;
up = (struct ppsunit *)pp->unitptr;
if (time_pps_getcap(up->handle, &capability) < 0) {
msyslog(LOG_ERR,
"refclock_atom: time_pps_getcap failed: %m");
return (0);
}
memset(&up->pps_params, 0, sizeof(pps_params_t));
if (enb_clear)
up->pps_params.mode = capability & PPS_CAPTURECLEAR;
else
up->pps_params.mode = capability & PPS_CAPTUREASSERT;
if (!up->pps_params.mode) {
msyslog(LOG_ERR,
"refclock_atom: invalid capture edge %d",
pps_assert);
return (0);
}
up->pps_params.mode |= PPS_TSFMT_TSPEC;
if (time_pps_setparams(up->handle, &up->pps_params) < 0) {
msyslog(LOG_ERR,
"refclock_atom: time_pps_setparams failed: %m");
return (0);
}
if (enb_hardpps) {
if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS,
up->pps_params.mode & ~PPS_TSFMT_TSPEC,
PPS_TSFMT_TSPEC) < 0) {
msyslog(LOG_ERR,
"refclock_atom: time_pps_kcbind failed: %m");
return (0);
}
pps_enable = 1;
}
#if DEBUG
if (debug) {
time_pps_getparams(up->handle, &up->pps_params);
printf(
"refclock_ppsapi: fd %d capability 0x%x version %d mode 0x%x kern %d\n",
up->fddev, capability, up->pps_params.api_version,
up->pps_params.mode, enb_hardpps);
}
#endif
return (1);
}
static void
atom_shutdown(
int unit,
struct peer *peer
)
{
struct refclockproc *pp;
register struct ppsunit *up;
pp = peer->procptr;
up = (struct ppsunit *)pp->unitptr;
if (up->fddev > 0)
close(up->fddev);
if (up->handle != 0)
time_pps_destroy(up->handle);
if (pps_peer == peer)
pps_peer = 0;
free(up);
}
static int
atom_pps(
struct peer *peer
)
{
register struct ppsunit *up;
struct refclockproc *pp;
pps_info_t pps_info;
struct timespec timeout, ts;
double dtemp;
pp = peer->procptr;
up = (struct ppsunit *)pp->unitptr;
if (up->handle == 0)
return (-1);
timeout.tv_sec = 0;
timeout.tv_nsec = 0;
memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t));
if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info,
&timeout) < 0)
return (-1);
if (up->pps_params.mode & PPS_CAPTUREASSERT) {
if (pps_info.assert_sequence ==
up->pps_info.assert_sequence)
return (1);
ts = up->pps_info.assert_timestamp;
} else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
if (pps_info.clear_sequence ==
up->pps_info.clear_sequence)
return (1);
ts = up->pps_info.clear_timestamp;
} else {
return (-1);
}
if (!((ts.tv_sec == up->ts.tv_sec && ts.tv_nsec -
up->ts.tv_nsec > NANOSECOND - RANGEGATE) ||
(ts.tv_sec - up->ts.tv_sec == 1 && ts.tv_nsec -
up->ts.tv_nsec < RANGEGATE))) {
up->ts = ts;
return (1);
}
up->ts = ts;
pp->lastrec.l_ui = ts.tv_sec + JAN_1970;
dtemp = ts.tv_nsec * FRAC / 1e9;
if (dtemp >= FRAC)
pp->lastrec.l_ui++;
pp->lastrec.l_uf = (u_int32)dtemp;
if (ts.tv_nsec > NANOSECOND / 2)
ts.tv_nsec -= NANOSECOND;
dtemp = -(double)ts.tv_nsec / NANOSECOND;
SAMPLE(dtemp + pp->fudgetime1);
#ifdef DEBUG
if (debug > 1)
printf("atom_pps %f %f\n", dtemp, pp->fudgetime1);
#endif
return (0);
}
#endif
int
pps_sample(
l_fp *offset
)
{
register struct peer *peer;
struct refclockproc *pp;
l_fp lftmp;
double doffset;
peer = pps_peer;
if (peer == 0)
return (1);
pp = peer->procptr;
pp->lastrec = *offset;
L_CLR(&lftmp);
L_ADDF(&lftmp, pp->lastrec.l_f);
LFPTOD(&lftmp, doffset);
SAMPLE(-doffset + pp->fudgetime1);
return (0);
}
static void
atom_poll(
int unit,
struct peer *peer
)
{
struct refclockproc *pp;
#ifdef HAVE_PPSAPI
int err;
#endif
pp = peer->procptr;
pp->polls++;
#ifdef HAVE_PPSAPI
err = atom_pps(peer);
if (err < 0) {
refclock_report(peer, CEVNT_FAULT);
return;
}
#endif
if (peer->burst > 0)
return;
peer->stratum = STRATUM_UNSPEC;
if (pp->codeproc == pp->coderecv) {
refclock_report(peer, CEVNT_TIMEOUT);
peer->burst = ASTAGE;
return;
} else if (!sys_prefer) {
pp->codeproc = pp->coderecv;
peer->burst = ASTAGE;
return;
} else if (fabs(sys_prefer->offset) > clock_max) {
pp->codeproc = pp->coderecv;
peer->burst = ASTAGE;
return;
}
peer->stratum = sys_prefer->stratum;
if (peer->stratum <= 1)
peer->refid = pp->refid;
else
peer->refid = peer->srcadr.sin_addr.s_addr;
pp->leap = LEAP_NOWARNING;
refclock_receive(peer);
peer->burst = ASTAGE;
}
#else
int refclock_atom_bs;
int
pps_sample(
l_fp *offset
)
{
return 1;
}
#endif