refclock_tsyncpci.c [plain text]
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#if defined(REFCLOCK) && defined(CLOCK_TSYNCPCI)
#include <asm/ioctl.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include <netinet/in.h>
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_refclock.h"
#include "ntp_unixtime.h"
#include "ntp_stdlib.h"
#include "ntp_calendar.h"
#define TSYNC_PCI_REVISION "1.11"
#define DEVICE "/dev/tsyncpci"
#define PRECISION (-20)
#define DESCRIPTION "Spectracom TSYNC-PCI"
#define SECONDS_1900_TO_1970 (2208988800U)
#define TSYNC_REF_IID (0x2500) // SS CAI, REF IID
#define TSYNC_REF_DEST_ID (0x0001) // KTS Firmware
#define TSYNC_REF_IN_PYLD_OFF (0)
#define TSYNC_REF_IN_LEN (0)
#define TSYNC_REF_OUT_PYLD_OFF (0)
#define TSYNC_REF_OUT_LEN (8)
#define TSYNC_REF_MAX_OUT_LEN (16)
#define TSYNC_REF_PYLD_LEN (TSYNC_REF_IN_LEN + \
TSYNC_REF_MAX_OUT_LEN)
#define TSYNC_REF_LEN (4)
#define TSYNC_REF_LOCAL ("LOCL")
#define TSYNC_TMSCL_IID (0x2301) // CS CAI, TIMESCALE IID
#define TSYNC_TMSCL_DEST_ID (0x0001) // KTS Firmware
#define TSYNC_TMSCL_IN_PYLD_OFF (0)
#define TSYNC_TMSCL_IN_LEN (0)
#define TSYNC_TMSCL_OUT_PYLD_OFF (0)
#define TSYNC_TMSCL_OUT_LEN (4)
#define TSYNC_TMSCL_MAX_OUT_LEN (12)
#define TSYNC_TMSCL_PYLD_LEN (TSYNC_TMSCL_IN_LEN + \
TSYNC_TMSCL_MAX_OUT_LEN)
#define TSYNC_LEAP_IID (0x2307) // CS CAI, LEAP SEC IID
#define TSYNC_LEAP_DEST_ID (0x0001) // KTS Firmware
#define TSYNC_LEAP_IN_PYLD_OFF (0)
#define TSYNC_LEAP_IN_LEN (0)
#define TSYNC_LEAP_OUT_PYLD_OFF (0)
#define TSYNC_LEAP_OUT_LEN (28)
#define TSYNC_LEAP_MAX_OUT_LEN (36)
#define TSYNC_LEAP_PYLD_LEN (TSYNC_LEAP_IN_LEN + \
TSYNC_LEAP_MAX_OUT_LEN)
#define TSYNC_TIME_BASE_YEAR (1970) // earliest acceptable year
#define TSYNC_LCL_STRATUM (0)
typedef enum
{
TIME_SCALE_UTC = 0, TIME_SCALE_TAI = 1, TIME_SCALE_GPS = 2, TIME_SCALE_LOCAL = 3, NUM_TIME_SCALES = 4,
TIME_SCALE_MAX = 15
} TIME_SCALE;
typedef struct BoardObj {
int file_descriptor;
unsigned short devid;
unsigned short options;
unsigned char firmware[5];
unsigned char FPGA[5];
unsigned char driver[7];
} BoardObj;
typedef struct TimeObj {
unsigned char syncOption;
unsigned int secsDouble;
unsigned char seconds;
unsigned char minutes;
unsigned char hours;
unsigned short days;
unsigned short year;
unsigned short flags;
} TimeObj;
typedef struct NtpTimeObj {
TimeObj timeObj;
struct timeval tv;
unsigned int refId;
} NtpTimeObj;
typedef struct ReferenceObj {
char time[TSYNC_REF_LEN];
char pps[TSYNC_REF_LEN];
} ReferenceObj;
typedef struct SecTimeObj
{
unsigned int seconds;
unsigned int ns;
}
SecTimeObj;
typedef struct DoyTimeObj
{
unsigned int year;
unsigned int doy;
unsigned int hour;
unsigned int minute;
unsigned int second;
unsigned int ns;
}
DoyTimeObj;
typedef struct LeapSecondObj
{
int offset;
DoyTimeObj utcDate;
}
LeapSecondObj;
#define DI_PAYLOADS_STARTER_LENGTH 4
typedef struct ioctl_trans_di {
uint16_t dest;
uint16_t iid;
uint32_t inPayloadOffset;
uint32_t inLength;
uint32_t outPayloadOffset;
uint32_t maxOutLength;
uint32_t actualOutLength;
int32_t status;
uint8_t payloads[DI_PAYLOADS_STARTER_LENGTH];
}ioctl_trans_di;
typedef struct
{
const char* pRef; const char* pRefId;
} RefIdLookup;
typedef struct {
uint32_t refPrefer; uint32_t refId; uint8_t refStratum;
} TsyncUnit;
static void tsync_poll (int unit, struct peer *);
static void tsync_shutdown (int, struct peer *);
static int tsync_start (int, struct peer *);
static void ApplyTimeOffset (DoyTimeObj* pDt, int off);
static void SecTimeFromDoyTime (SecTimeObj* pSt, DoyTimeObj* pDt);
static void DoyTimeFromSecTime (DoyTimeObj* pDt, SecTimeObj* pSt);
struct refclock refclock_tsyncpci = {
tsync_start,
tsync_shutdown,
tsync_poll,
noentry,
noentry,
noentry,
NOFLAGS
};
static RefIdLookup RefIdLookupTbl[] =
{
{"gps", "GPS"},
{"ir", "IRIG"},
{"hvq", "HVQ"},
{"frq", "FREQ"},
{"mdm", "ACTS"},
{"epp", "PPS"},
{"ptp", "PTP"},
{"asc", "ATC"},
{"hst0", "USER"},
{"hst", TSYNC_REF_LOCAL},
{"self", TSYNC_REF_LOCAL},
{NULL, NULL}
};
#define IOCTL_TPRO_ID 't'
#define IOCTL_TPRO_OPEN _IOWR(IOCTL_TPRO_ID, 0, BoardObj)
#define IOCTL_TPRO_GET_NTP_TIME _IOWR(IOCTL_TPRO_ID, 25, NtpTimeObj)
#define IOCTL_TSYNC_GET _IOWR(IOCTL_TPRO_ID, 26, ioctl_trans_di)
static int tsync_start(int unit, struct peer *peer)
{
struct refclockproc *pp;
TsyncUnit *up;
pp = peer->procptr;
pp->clockdesc = DESCRIPTION;
pp->io.clock_recv = noentry;
pp->io.srcclock = peer;
pp->io.datalen = 0;
peer->precision = PRECISION;
if (!(up = (TsyncUnit*)emalloc(sizeof(TsyncUnit))))
{
return (0);
}
up->refPrefer = peer->flags & FLAG_PREFER;
up->refStratum = STRATUM_UNSPEC;
strncpy((char *)&up->refId, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
pp->unitptr = (caddr_t)up;
strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
return (1);
}
static void tsync_shutdown(int unit, struct peer *peer)
{
}
static void tsync_poll(int unit, struct peer *peer)
{
char device[32];
struct refclockproc *pp;
struct calendar jt;
TsyncUnit *up;
unsigned char synch;
double seconds;
int err;
int err1;
int err2;
int err3;
int i;
int j;
unsigned int itAllocationLength;
unsigned int itAllocationLength1;
unsigned int itAllocationLength2;
NtpTimeObj TimeContext;
BoardObj hBoard;
char timeRef[TSYNC_REF_LEN + 1];
char ppsRef [TSYNC_REF_LEN + 1];
TIME_SCALE tmscl = TIME_SCALE_UTC;
LeapSecondObj leapSec;
ioctl_trans_di *it;
ioctl_trans_di *it1;
ioctl_trans_di *it2;
l_fp offset;
l_fp ltemp;
ReferenceObj * pRefObj;
sprintf(device, "%s%d", DEVICE, (int)peer->refclkunit);
printf("Polling device number %d...\n", (int)peer->refclkunit);
hBoard.file_descriptor = open(device, O_RDONLY | O_NDELAY, 0777);
if (hBoard.file_descriptor < 0)
{
msyslog(LOG_ERR, "Couldn't open device");
return;
}
if (ioctl(hBoard.file_descriptor, IOCTL_TPRO_OPEN, &hBoard) < 0)
{
msyslog(LOG_ERR, "Couldn't initialize device");
close(hBoard.file_descriptor);
return;
}
itAllocationLength =
(sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
TSYNC_REF_IN_LEN + TSYNC_REF_MAX_OUT_LEN;
it = (ioctl_trans_di*)alloca(itAllocationLength);
if (it == NULL) {
msyslog(LOG_ERR, "Couldn't allocate transaction memory - Reference");
return;
}
it->dest = TSYNC_REF_DEST_ID;
it->iid = TSYNC_REF_IID;
it->inPayloadOffset = TSYNC_REF_IN_PYLD_OFF;
it->inLength = TSYNC_REF_IN_LEN;
it->outPayloadOffset = TSYNC_REF_OUT_PYLD_OFF;
it->maxOutLength = TSYNC_REF_MAX_OUT_LEN;
it->actualOutLength = 0;
it->status = 0;
memset(it->payloads, 0, TSYNC_REF_MAX_OUT_LEN);
err = ioctl(hBoard.file_descriptor,
IOCTL_TSYNC_GET,
(char *)it);
itAllocationLength1 =
(sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
TSYNC_TMSCL_IN_LEN + TSYNC_TMSCL_MAX_OUT_LEN;
it1 = (ioctl_trans_di*)alloca(itAllocationLength1);
if (it1 == NULL) {
msyslog(LOG_ERR, "Couldn't allocate transaction memory - Time Scale");
return;
}
it1->dest = TSYNC_TMSCL_DEST_ID;
it1->iid = TSYNC_TMSCL_IID;
it1->inPayloadOffset = TSYNC_TMSCL_IN_PYLD_OFF;
it1->inLength = TSYNC_TMSCL_IN_LEN;
it1->outPayloadOffset = TSYNC_TMSCL_OUT_PYLD_OFF;
it1->maxOutLength = TSYNC_TMSCL_MAX_OUT_LEN;
it1->actualOutLength = 0;
it1->status = 0;
memset(it1->payloads, 0, TSYNC_TMSCL_MAX_OUT_LEN);
err1 = ioctl(hBoard.file_descriptor,
IOCTL_TSYNC_GET,
(char *)it1);
itAllocationLength2 =
(sizeof(ioctl_trans_di) - DI_PAYLOADS_STARTER_LENGTH) +
TSYNC_LEAP_IN_LEN + TSYNC_LEAP_MAX_OUT_LEN;
it2 = (ioctl_trans_di*)alloca(itAllocationLength2);
if (it2 == NULL) {
msyslog(LOG_ERR, "Couldn't allocate transaction memory - Leap Second");
return;
}
it2->dest = TSYNC_LEAP_DEST_ID;
it2->iid = TSYNC_LEAP_IID;
it2->inPayloadOffset = TSYNC_LEAP_IN_PYLD_OFF;
it2->inLength = TSYNC_LEAP_IN_LEN;
it2->outPayloadOffset = TSYNC_LEAP_OUT_PYLD_OFF;
it2->maxOutLength = TSYNC_LEAP_MAX_OUT_LEN;
it2->actualOutLength = 0;
it2->status = 0;
memset(it2->payloads, 0, TSYNC_LEAP_MAX_OUT_LEN);
err2 = ioctl(hBoard.file_descriptor,
IOCTL_TSYNC_GET,
(char *)it2);
pp = peer->procptr;
up = (TsyncUnit*)pp->unitptr;
err3 = ioctl(hBoard.file_descriptor,
IOCTL_TPRO_GET_NTP_TIME,
(char *)&TimeContext);
close(hBoard.file_descriptor);
if ((err < 0) ||(err1 < 0) || (err2 < 0) || (err3 < 0) ||
(it->status != 0) || (it1->status != 0) || (it2->status != 0) ||
(it->actualOutLength != TSYNC_REF_OUT_LEN) ||
(it1->actualOutLength != TSYNC_TMSCL_OUT_LEN) ||
(it2->actualOutLength != TSYNC_LEAP_OUT_LEN)) {
refclock_report(peer, CEVNT_FAULT);
return;
}
memset(timeRef, '\0', sizeof(timeRef));
memset(ppsRef, '\0', sizeof(ppsRef));
pRefObj = (void *)it->payloads;
memcpy(timeRef, pRefObj->time, TSYNC_REF_LEN);
memcpy(ppsRef, pRefObj->pps, TSYNC_REF_LEN);
memcpy(&tmscl, it1->payloads, sizeof(tmscl));
tmscl = ntohl(tmscl);
for (i = 0; i < (sizeof(leapSec) / 4); i++)
{
for (j = 0; j < 4; j++)
{
((unsigned char*)&leapSec)[(i * 4) + j] =
((unsigned char*)(it2->payloads))[(i * 4) + (3 - j)];
}
}
for (i = 0; RefIdLookupTbl[i].pRef != NULL; i++)
{
if (strstr(timeRef, RefIdLookupTbl[i].pRef) != NULL)
{
break;
}
}
for (j = 0; RefIdLookupTbl[j].pRef != NULL; j++)
{
if (strstr(ppsRef, RefIdLookupTbl[j].pRef) != NULL)
{
break;
}
}
synch = (TimeContext.timeObj.flags == 0x4) ? 1 : 0;
seconds = (double) (TimeContext.timeObj.secsDouble);
seconds /= (double) 1000000.0;
DTOLFP(((double)TimeContext.tv.tv_usec / 1000000.0), &pp->lastrec);
pp->lastrec.Ul_i.Xl_ui += (unsigned int)TimeContext.tv.tv_sec +
SECONDS_1900_TO_1970;
pp->polls++;
sprintf(pp->a_lastcode, "%03d %02d:%02d:%02.6f",
TimeContext.timeObj.days, TimeContext.timeObj.hours,
TimeContext.timeObj.minutes, seconds);
pp->lencode = strlen (pp->a_lastcode);
pp->day = TimeContext.timeObj.days;
pp->hour = TimeContext.timeObj.hours;
pp->minute = TimeContext.timeObj.minutes;
pp->second = (int) seconds;
seconds = (seconds - (double) (pp->second / 1.0)) * 1000000000;
pp->nsec = (long) seconds;
jt.year = TimeContext.timeObj.year;
jt.yearday = 1;
jt.monthday = 1;
jt.month = 1;
jt.hour = 0;
jt.minute = 0;
jt.second = 0;
pp->yearstart = caltontp(&jt);
offset.l_ui = (long)(((pp->day - 1) * 24) + pp->hour + GMT);
offset.l_ui = (offset.l_ui * 60) + (long)pp->minute;
offset.l_ui = (offset.l_ui * 60) + (long)pp->second;
offset.l_ui = offset.l_ui + (long)pp->yearstart;
offset.l_uf = 0;
DTOLFP(pp->nsec / 1e9, <emp);
L_ADD(&offset, <emp);
refclock_process_offset(pp, offset, pp->lastrec,
pp->fudgetime1);
if (synch) {
ApplyTimeOffset(&(leapSec.utcDate), -1);
if ((tmscl != TIME_SCALE_GPS) && (tmscl != TIME_SCALE_TAI) &&
(leapSec.utcDate.year == (unsigned int)TimeContext.timeObj.year) &&
(leapSec.utcDate.doy == (unsigned int)TimeContext.timeObj.days))
{
if (leapSec.offset == 1)
{
pp->leap = LEAP_ADDSECOND;
}
else if (leapSec.offset == -1)
{
pp->leap = LEAP_DELSECOND;
}
else
{
pp->leap = LEAP_NOWARNING;
}
}
else
{
pp->leap = LEAP_NOWARNING;
}
peer->leap = pp->leap;
refclock_report(peer, CEVNT_NOMINAL);
if ((RefIdLookupTbl[i].pRef != NULL) &&
(RefIdLookupTbl[j].pRef != NULL))
{
if ((strcmp(RefIdLookupTbl[i].pRefId, TSYNC_REF_LOCAL) == 0) ||
(strcmp(RefIdLookupTbl[j].pRefId, TSYNC_REF_LOCAL) == 0))
{
peer->flags &= ~FLAG_PREFER;
pp->stratum = STRATUM_UNSPEC;
peer->stratum = pp->stratum;
if ((sys_peer != NULL) && (sys_peer != peer))
{
up->refStratum = sys_peer->stratum;
up->refId = addr2refid(&sys_peer->srcadr);
}
}
else
{
peer->flags |= up->refPrefer;
up->refStratum = TSYNC_LCL_STRATUM;
strncpy((char *)&up->refId, RefIdLookupTbl[j].pRefId,
TSYNC_REF_LEN);
pp->stratum = TSYNC_LCL_STRATUM;
peer->stratum = pp->stratum;
}
strncpy((char *)&pp->refid, RefIdLookupTbl[j].pRefId,
TSYNC_REF_LEN);
peer->refid = pp->refid;
}
else
{
peer->flags |= up->refPrefer;
pp->refid = up->refId;
peer->refid = pp->refid;
pp->stratum = up->refStratum;
peer->stratum = pp->stratum;
}
}
else {
strncpy((char *)&pp->refid, TSYNC_REF_LOCAL, TSYNC_REF_LEN);
peer->refid = pp->refid;
pp->leap = LEAP_NOTINSYNC;
peer->leap = pp->leap;
}
if (pp->coderecv == pp->codeproc) {
refclock_report(peer, CEVNT_TIMEOUT);
return;
}
record_clock_stats(&peer->srcadr, pp->a_lastcode);
refclock_receive(peer);
pp->polls++;
}
void ApplyTimeOffset(DoyTimeObj* pDt, int off)
{
SecTimeObj st;
SecTimeFromDoyTime(&st, pDt);
st.seconds = (int)((signed long long)st.seconds + (signed long long)off);
DoyTimeFromSecTime(pDt, &st);
}
void SecTimeFromDoyTime(SecTimeObj* pSt, DoyTimeObj* pDt)
{
unsigned int yrs; unsigned int lyrs;
pSt->seconds = 0;
yrs = pDt->year - TSYNC_TIME_BASE_YEAR;
lyrs = (yrs + 1) / 4;
pSt->seconds += lyrs * SECSPERLEAPYEAR;
pSt->seconds += (yrs - lyrs) * SECSPERYEAR;
pSt->seconds += (pDt->doy - 1) * SECSPERDAY;
pSt->seconds += pDt->hour * SECSPERHR;
pSt->seconds += pDt->minute * SECSPERMIN;
pSt->seconds += pDt->second;
pSt->ns = pDt->ns;
}
void DoyTimeFromSecTime(DoyTimeObj* pDt, SecTimeObj* pSt)
{
signed long long secs; unsigned int yrs; unsigned int doys; unsigned int hrs; unsigned int mins;
secs = (signed long long)(pSt->seconds);
yrs = (unsigned int)((secs /
((SECSPERYEAR * 3) + SECSPERLEAPYEAR)) * 4);
secs %= ((SECSPERYEAR * 3) + SECSPERLEAPYEAR);
if (secs >= SECSPERYEAR)
{
yrs++;
secs -= SECSPERYEAR;
}
if (secs >= SECSPERYEAR)
{
yrs++;
secs -= SECSPERYEAR;
}
if (secs >= SECSPERLEAPYEAR)
{
yrs++;
secs -= SECSPERLEAPYEAR;
}
doys = (unsigned int)((secs / SECSPERDAY) + 1);
secs %= SECSPERDAY;
hrs = (unsigned int)(secs / SECSPERHR);
secs %= SECSPERHR;
mins = (unsigned int)(secs / SECSPERMIN);
secs %= SECSPERMIN;
pDt->year = yrs + TSYNC_TIME_BASE_YEAR;
pDt->doy = doys;
pDt->hour = hrs;
pDt->minute = mins;
pDt->second = (unsigned int)secs;
pDt->ns = pSt->ns;
}
#else
int refclock_tsyncpci_bs;
#endif