Commit f38348c3 authored by Alessandro Rubini's avatar Alessandro Rubini

userspace/tools: handle TAI in wr_date

moreover, adjust fractional part if integer part is already ok
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent 3dce6ddf
......@@ -8,4 +8,5 @@ com
mapper
wmapper
shw_ver
wr_date
\ No newline at end of file
wr_date
fix_tai_offset
/* This is just a subset of wr_date, user to test on the host */
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/timex.h>
#ifndef MOD_TAI
#define MOD_TAI 0x80
#endif
#define WRDATE_CFG_FILE "/wr/etc/wrdate.conf"
#define WRDATE_LEAP_FILE "/etc/leap-seconds.list"
int opt_verbose = 1;
char *prgname;
int main(int argc, char **argv)
{
struct timex t;
char s[128];
unsigned long long now, leapt, expire = 0;
int i, tai_offset = 0;
prgname = argv[0];
/* first: get the current offset */
memset(&t, 0, sizeof(t));
if (adjtimex(&t) < 0) {
fprintf(stderr, "%s: adjtimex(): %s\n", prgname,
strerror(errno));
return 0;
}
/* then, find the current time, using such offset */
now = time(NULL) + 2208988800LL; /* (for TAI: + utc_offset */
FILE *f = fopen(WRDATE_LEAP_FILE, "r");
if (!f) {
fprintf(stderr, "%s: %s: %s\n", prgname, WRDATE_LEAP_FILE,
strerror(errno));
return 0;
}
while (fgets(s, sizeof(s), f)) {
if (sscanf(s, "#@ %lli", &expire) == 1)
continue;
if (sscanf(s, "%lli %i", &leapt, &i) != 2)
continue;
/* check this line, and apply if if it's in the past */
if (leapt < now)
tai_offset = i;
}
fclose(f);
if (tai_offset != t.tai) {
if (opt_verbose)
printf("Previous TAI offset: %i\n", t.tai);
t.constant = tai_offset;
t.modes = MOD_TAI;
if (adjtimex(&t) < 0) {
fprintf(stderr, "%s: adjtimex(): %s\n", prgname,
strerror(errno));
return tai_offset;
}
}
if (opt_verbose)
printf("Current TAI offset: %i\n", t.tai);
return tai_offset;
}
......@@ -16,9 +16,14 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/timex.h>
#include "../../kernel/wbgen-regs/ppsg-regs.h"
#ifndef MOD_TAI
#define MOD_TAI 0x80
#endif
#define WRDATE_CFG_FILE "/wr/etc/wrdate.conf"
#define WRDATE_LEAP_FILE "/etc/leap-seconds.list"
/* Address for hardware, from nic-hardware.h */
#define FPGA_BASE_PPSG 0x10010500
......@@ -35,10 +40,10 @@ void help(char *prgname)
" -n do not act in practice\n"
" get print WR time to stdout\n"
" set <value> set WR time to scalar seconds\n"
" set host set from current host time\n"
" set ntp set from ntp (sets host time too)\n"
" set ntp:<ip> set from specified ntp server\n",
WRDATE_CFG_FILE);
" set host set TAI from current host time\n"
/* " set ntp set TAI from ntp and leap seconds" */
/* " set ntp:<ip> set from specified ntp server\n" */
, WRDATE_CFG_FILE);
exit(1);
}
......@@ -139,23 +144,92 @@ void gettimeof_wr(struct timeval *tv, struct PPSG_WB *pps)
tv->tv_usec = nsec / 1000;
}
/* Fix the TAI representation looking at the leap file */
int fix_host_tai(void)
{
struct timex t;
char s[128];
unsigned long long now, leapt, expire = 0;
int i, *p, tai_offset = 0;
/* first: get the current offset */
memset(&t, 0, sizeof(t));
if (adjtimex(&t) < 0) {
fprintf(stderr, "%s: adjtimex(): %s\n", prgname,
strerror(errno));
return 0;
}
/* then, find the current time, using such offset */
now = time(NULL) + 2208988800LL; /* (for TAI: + utc_offset) */
FILE *f = fopen(WRDATE_LEAP_FILE, "r");
if (!f) {
fprintf(stderr, "%s: %s: %s\n", prgname, WRDATE_LEAP_FILE,
strerror(errno));
return 0;
}
while (fgets(s, sizeof(s), f)) {
if (sscanf(s, "#@ %lli", &expire) == 1)
continue;
if (sscanf(s, "%lli %i", &leapt, &i) != 2)
continue;
/* check this line, and apply if if it's in the past */
if (leapt < now)
tai_offset = i;
}
fclose(f);
/*
* Our WRS kernel has tai support, but our compiler does not.
* We are 32-bit only, and we know for sure that tai is
* exactly after stbcnt. It's a bad hack, but it works
*/
p = (int *)(&t.stbcnt) + 1;
if (tai_offset != *p) {
if (opt_verbose)
printf("Previous TAI offset: %i\n", *p);
t.constant = tai_offset;
t.modes = MOD_TAI;
if (adjtimex(&t) < 0) {
fprintf(stderr, "%s: adjtimex(): %s\n", prgname,
strerror(errno));
return tai_offset;
}
}
if (opt_verbose)
printf("Current TAI offset: %i\n", *p);
return tai_offset;
}
/* This sets WR time from host time */
int wrdate_internal_set(struct PPSG_WB *pps)
{
struct timeval tvh, tvr; /* host, rabbit */
signed long long diff64;
signed long diff;
unsigned long prev;
int tai_offset;
tai_offset = fix_host_tai();
gettimeofday(&tvh, NULL);
gettimeof_wr(&tvr, pps);
/* Warn if more than 200ms away */
/* diff is the expected step to be added, so host - WR */
diff = tvh.tv_usec - tvr.tv_usec;
if (diff > 500 * 1000)
diff64 = tvh.tv_sec + tai_offset - tvr.tv_sec;
if (diff > 500 * 1000) {
diff64++;
diff -= 1000 * 1000;
if (diff < -500 * 1000)
}
if (diff < -500 * 1000) {
diff64--;
diff += 1000 * 1000;
}
/* Warn if more than 200ms away */
if (diff > 200 * 1000 || diff < -200 * 1000)
fprintf(stderr, "%s: Warning: fractional second differs by"
"more than 0.2 (%li ms)\n", prgname, diff / 1000);
......@@ -168,49 +242,24 @@ int wrdate_internal_set(struct PPSG_WB *pps)
printf("Fractional difference: %li usec\n", diff);
}
if (0) { /* Older implementation, wrong and needless */
/* Wait for the wr time to pass the second. */
prev = tvr.tv_sec;
do
gettimeof_wr(&tvr, pps);
while (tvr.tv_sec == prev);
/* If we are near a second change in the host, wait for it */
gettimeofday(&tvh, NULL);
if (tvh.tv_usec > 500 * 1000) {
prev = tvh.tv_sec;
do
gettimeofday(&tvh, NULL);
while (tvh.tv_sec == prev);
}
/* Finally, write the host time to White Rabbit */
/* FIXME: not 2038-clean */
pps->CNTR_UTCLO = tvh.tv_sec; // no effect: procedure is wrong
} else { /* good implementation */
signed long long diff64;
/* diff is the expected step to be added, so host - WR */
diff = tvh.tv_usec - tvr.tv_usec;
diff64 = tvh.tv_sec - tvr.tv_sec;
if (diff > 500 * 1000)
diff64++;
if (diff < -500 * 1000)
diff64--;
if (diff64) {
if (opt_verbose)
printf("adjusting by %lli seconds\n", diff64);
/* We must write a signed "adjustment" value to registers */
/* We must write a signed "adjustment" value */
pps->ADJ_UTCLO = diff64 & 0xffffffff;
pps->ADJ_UTCHI = (diff64 >> 32) & 0xff;
pps->ADJ_NSEC = 0;
asm("" : : : "memory"); /* barrier... */
pps->CR = pps->CR | PPSG_CR_CNT_ADJ;
} else {
if (opt_verbose)
printf("adjusting by %li usecs\n", diff);
pps->ADJ_UTCLO = 0;
pps->ADJ_UTCHI = 0;
pps->ADJ_NSEC = diff * 64 + diff / 2;
asm("" : : : "memory"); /* barrier... */
pps->CR = pps->CR | PPSG_CR_CNT_ADJ;
}
return 0;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment