#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sgtty.h>
#include "../include/ntp_fp.h"
#include "../include/ntp.h"
#include "../include/ntp_unixtime.h"
#ifdef CHULDISC
#ifdef STREAM
# ifdef HAVE_SYS_CHUDEFS_H
#include <sys/chudefs.h>
#endif
#include <stropts.h>
#endif
#endif
#ifdef CHULDISC
# ifdef HAVE_SYS_CHUDEFS_H
#include <sys/chudefs.h>
#endif
#endif
#ifndef CHULDISC
#ifndef STREAM
#define NCHUCHARS (10)
struct chucode {
u_char codechars[NCHUCHARS];
u_char ncodechars;
u_char chustatus;
struct timeval codetimes[NCHUCHARS];
};
#endif
#endif
#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
char *progname;
int debug;
int dofilter = 0;
int showtimes = 0;
int doprocess = 0;
#ifdef CHULDISC
int usechuldisc = 0;
#endif
#ifdef STREAM
int usechuldisc = 0;
#endif
struct timeval lasttv;
struct chucode chudata;
extern u_long ustotslo[];
extern u_long ustotsmid[];
extern u_long ustotshi[];
int
main(
int argc,
char *argv[]
)
{
int c;
int errflg = 0;
extern int ntp_optind;
extern char *ntp_optarg;
void init_chu();
progname = argv[0];
while ((c = ntp_getopt(argc, argv, "cdfpt")) != EOF)
switch (c) {
case 'c':
#ifdef STREAM
usechuldisc = 1;
break;
#endif
#ifdef CHULDISC
usechuldisc = 1;
break;
#endif
#ifndef STREAM
#ifndef CHULDISC
(void) fprintf(stderr,
"%s: CHU line discipline not available on this machine\n",
progname);
exit(2);
#endif
#endif
case 'd':
++debug;
break;
case 'f':
dofilter = 1;
break;
case 'p':
doprocess = 1;
case 't':
showtimes = 1;
break;
default:
errflg++;
break;
}
if (errflg || ntp_optind+1 != argc) {
#ifdef STREAM
(void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
progname);
#endif
#ifdef CHULDISC
(void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
progname);
#endif
#ifndef STREAM
#ifndef CHULDISC
(void) fprintf(stderr, "usage: %s [-cdft] tty_device\n",
progname);
#endif
#endif
exit(2);
}
(void) gettimeofday(&lasttv, (struct timezone *)0);
c = openterm(argv[ntp_optind]);
init_chu();
#ifdef STREAM
if (usechuldisc)
process_ldisc(c);
else
#endif
#ifdef CHULDISC
if (usechuldisc)
process_ldisc(c);
else
#endif
process_raw(c);
}
int
openterm(
char *dev
)
{
int s;
struct sgttyb ttyb;
if (debug)
(void) fprintf(stderr, "Doing open...");
if ((s = open(dev, O_RDONLY, 0777)) < 0)
error("open(%s)", dev, "");
if (debug)
(void) fprintf(stderr, "open okay\n");
if (debug)
(void) fprintf(stderr, "Setting exclusive use...");
if (ioctl(s, TIOCEXCL, (char *)0) < 0)
error("ioctl(TIOCEXCL)", "", "");
if (debug)
(void) fprintf(stderr, "done\n");
ttyb.sg_ispeed = ttyb.sg_ospeed = B300;
ttyb.sg_erase = ttyb.sg_kill = 0;
ttyb.sg_flags = EVENP|ODDP|RAW;
if (debug)
(void) fprintf(stderr, "Setting baud rate et al...");
if (ioctl(s, TIOCSETP, (char *)&ttyb) < 0)
error("ioctl(TIOCSETP, raw)", "", "");
if (debug)
(void) fprintf(stderr, "done\n");
#ifdef CHULDISC
if (usechuldisc) {
int ldisc;
if (debug)
(void) fprintf(stderr, "Switching to CHU ldisc...");
ldisc = CHULDISC;
if (ioctl(s, TIOCSETD, (char *)&ldisc) < 0)
error("ioctl(TIOCSETD, CHULDISC)", "", "");
if (debug)
(void) fprintf(stderr, "okay\n");
}
#endif
#ifdef STREAM
if (usechuldisc) {
if (debug)
(void) fprintf(stderr, "Poping off streams...");
while (ioctl(s, I_POP, 0) >=0) ;
if (debug)
(void) fprintf(stderr, "okay\n");
if (debug)
(void) fprintf(stderr, "Pushing CHU stream...");
if (ioctl(s, I_PUSH, "chu") < 0)
error("ioctl(I_PUSH, \"chu\")", "", "");
if (debug)
(void) fprintf(stderr, "okay\n");
}
#endif
return s;
}
int
process_raw(
int s
)
{
u_char c;
int n;
struct timeval tv;
struct timeval difftv;
while ((n = read(s, &c, sizeof(char))) > 0) {
(void) gettimeofday(&tv, (struct timezone *)0);
if (dofilter)
raw_filter((unsigned int)c, &tv);
else {
difftv.tv_sec = tv.tv_sec - lasttv.tv_sec;
difftv.tv_usec = tv.tv_usec - lasttv.tv_usec;
if (difftv.tv_usec < 0) {
difftv.tv_sec--;
difftv.tv_usec += 1000000;
}
(void) printf("%02x\t%lu.%06lu\t%lu.%06lu\n",
c, tv.tv_sec, tv.tv_usec, difftv.tv_sec,
difftv.tv_usec);
lasttv = tv;
}
}
if (n == 0) {
(void) fprintf(stderr, "%s: zero returned on read\n", progname);
exit(1);
} else
error("read()", "", "");
}
int
raw_filter(
unsigned int c,
struct timeval *tv
)
{
static struct timeval diffs[10] = { 0 };
struct timeval diff;
l_fp ts;
void chufilter();
if ((c & 0xf) > 9 || ((c>>4)&0xf) > 9) {
if (debug)
(void) fprintf(stderr,
"character %02x failed BCD test\n");
chudata.ncodechars = 0;
return;
}
if (chudata.ncodechars > 0) {
diff.tv_sec = tv->tv_sec
- chudata.codetimes[chudata.ncodechars].tv_sec;
diff.tv_usec = tv->tv_usec
- chudata.codetimes[chudata.ncodechars].tv_usec;
if (diff.tv_usec < 0) {
diff.tv_sec--;
diff.tv_usec += 1000000;
}
}
chudata.codechars[chudata.ncodechars] = c;
chudata.codetimes[chudata.ncodechars] = *tv;
if (chudata.ncodechars > 0)
diffs[chudata.ncodechars] = diff;
if (++chudata.ncodechars == 10) {
if (doprocess) {
TVTOTS(&chudata.codetimes[NCHUCHARS-1], &ts);
ts.l_ui += JAN_1970;
chufilter(&chudata, &chudata.codetimes[NCHUCHARS-1]);
} else {
register int i;
for (i = 0; i < chudata.ncodechars; i++) {
(void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
chudata.codechars[i] & 0xf,
(chudata.codechars[i] >>4 ) & 0xf,
chudata.codetimes[i].tv_sec,
chudata.codetimes[i].tv_usec,
diffs[i].tv_sec, diffs[i].tv_usec);
}
}
chudata.ncodechars = 0;
}
}
int
process_ldisc(
int s
)
{
struct chucode chu;
int n;
register int i;
struct timeval diff;
l_fp ts;
void chufilter();
while ((n = read(s, (char *)&chu, sizeof chu)) > 0) {
if (n != sizeof chu) {
(void) fprintf(stderr, "Expected %d, got %d\n",
sizeof chu, n);
continue;
}
if (doprocess) {
TVTOTS(&chu.codetimes[NCHUCHARS-1], &ts);
ts.l_ui += JAN_1970;
chufilter(&chu, &ts);
} else {
for (i = 0; i < NCHUCHARS; i++) {
if (i == 0)
diff.tv_sec = diff.tv_usec = 0;
else {
diff.tv_sec = chu.codetimes[i].tv_sec
- chu.codetimes[i-1].tv_sec;
diff.tv_usec = chu.codetimes[i].tv_usec
- chu.codetimes[i-1].tv_usec;
if (diff.tv_usec < 0) {
diff.tv_sec--;
diff.tv_usec += 1000000;
}
}
(void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
chu.codechars[i] & 0xf, (chu.codechars[i]>>4)&0xf,
chu.codetimes[i].tv_sec, chu.codetimes[i].tv_usec,
diff.tv_sec, diff.tv_usec);
}
}
}
if (n == 0) {
(void) fprintf(stderr, "%s: zero returned on read\n", progname);
exit(1);
} else
error("read()", "", "");
}
void
error(
char *fmt,
char *s1,
char *s2
)
{
(void) fprintf(stderr, "%s: ", progname);
(void) fprintf(stderr, fmt, s1, s2);
(void) fprintf(stderr, ": ");
perror("");
exit(1);
}
#define MAXUNITS 4
#define CHUDEV "/dev/chu%d"
#define NCHUCODES 9
#define CHUDISTANCE 0x00004a3d
#define CHUBASEDELAY 0x0000028f
#define CHUDELAYPENALTY 0x0000028f
#define CHUPRECISION (-9)
#define CHUREFID "CHU\0"
#define DEFPROPDELAY 0x00624dd3
#define DEFFILTFUDGE 0x000d1b71
#define MULBY10(x) (((x)<<3) + ((x)<<1))
#define MULBY60(x) (((x)<<6) - ((x)<<2))
#define MULBY24(x) (((x)<<4) + ((x)<<3))
#define ZEROPTONE 0x1999999a
#define NZPOBITS 29
#define CHARDELAY 0x081b4e80
static u_long chutable[NCHUCHARS] = {
0x2147ae14 + CHARDELAY,
0x2ac08312 + CHARDELAY,
0x34395810 + CHARDELAY,
0x3db22d0e + CHARDELAY,
0x472b020c + CHARDELAY,
0x50a3d70a + CHARDELAY,
0x5a1cac08 + CHARDELAY,
0x63958106 + CHARDELAY,
0x6d0e5604 + CHARDELAY,
0x76872b02 + CHARDELAY,
};
static l_fp propagation_delay;
static l_fp fudgefactor;
static l_fp offset_fudge;
static u_long yearstart;
extern u_long current_time;
extern struct event timerqueue[];
extern u_long ustotslo[];
extern u_long ustotsmid[];
extern u_long ustotshi[];
void
init_chu(void)
{
propagation_delay.l_ui = 0;
propagation_delay.l_uf = DEFPROPDELAY;
fudgefactor.l_ui = 0;
fudgefactor.l_uf = DEFFILTFUDGE;
offset_fudge = propagation_delay;
L_ADD(&offset_fudge, &fudgefactor);
yearstart = 0;
}
void
chufilter(
struct chucode *chuc,
l_fp *rtime
)
{
register int i;
register u_long date_ui;
register u_long tmp;
register u_char *code;
int isneg;
int imin;
int imax;
u_long reftime;
l_fp off[NCHUCHARS];
l_fp ts;
int day, hour, minute, second;
static u_char lastcode[NCHUCHARS];
extern u_long calyearstart();
extern char *mfptoa();
void chu_process();
extern char *prettydate();
for (i = 0; i < NCHUCHARS; i++)
printf("%c%c", (chuc->codechars[i] & 0xf) + '0',
((chuc->codechars[i]>>4) & 0xf) + '0');
printf("\n");
for (i = 0; i < NCHUCHARS/2; i++)
if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)]) {
(void) printf("Bad format, halves don't match\n");
return;
}
code = lastcode;
for (i = 0; i < NCHUCHARS/2; i++) {
*code++ = chuc->codechars[i] & 0xf;
*code++ = (chuc->codechars[i] >> 4) & 0xf;
}
code = lastcode;
if (*code++ != 6) {
(void) printf("Bad format, no 6 at start\n");
return;
}
day = *code++;
day = MULBY10(day) + *code++;
day = MULBY10(day) + *code++;
hour = *code++;
hour = MULBY10(hour) + *code++;
minute = *code++;
minute = MULBY10(minute) + *code++;
second = *code++;
second = MULBY10(second) + *code++;
if (day < 1 || day > 366
|| hour > 23 || minute > 59
|| second < 31 || second > 39) {
(void) printf("Failed date sanity check: %d %d %d %d\n",
day, hour, minute, second);
return;
}
tmp = (u_long)(MULBY24((day-1)) + hour);
tmp = MULBY60(tmp) + (u_long)minute;
tmp = MULBY60(tmp) + (u_long)second;
date_ui = tmp + yearstart;
if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
&& date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
goto codeokay;
date_ui = calyearstart(rtime->l_ui);
if (date_ui != yearstart) {
yearstart = date_ui;
date_ui += tmp;
(void) printf("time %u, code %u, difference %d\n",
date_ui, rtime->l_ui, (long)date_ui-(long)rtime->l_ui);
if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
&& date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
goto codeokay;
}
ts.l_uf = 0;
ts.l_ui = yearstart;
printf("yearstart %s\n", prettydate(&ts));
printf("received %s\n", prettydate(rtime));
ts.l_ui = date_ui;
printf("date_ui %s\n", prettydate(&ts));
if ((rtime->l_ui - yearstart) < CLOCK_WAYTOOBIG) {
date_ui = tmp + calyearstart(yearstart - CLOCK_WAYTOOBIG);
if ((rtime->l_ui - date_ui) < CLOCK_WAYTOOBIG)
goto codeokay;
}
date_ui = tmp + calyearstart(yearstart + (400*24*60*60));
if ((date_ui - rtime->l_ui) >= CLOCK_WAYTOOBIG) {
printf("Date hopelessly off\n");
return;
}
codeokay:
reftime = date_ui;
for (i = 0; i < NCHUCHARS; i++) {
register u_long tmp2;
off[i].l_ui = date_ui;
off[i].l_uf = chutable[i];
tmp = chuc->codetimes[i].tv_sec + JAN_1970;
TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2);
M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2);
}
date_ui = 0;
tmp = 0;
for (i = 0; i < NCHUCHARS; i++)
M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf);
if (M_ISNEG(date_ui, tmp))
isneg = 1;
else
isneg = 0;
if (date_ui < 9 || date_ui > 0xfffffff7) {
register u_long prod_ui;
register u_long prod_uf;
prod_ui = prod_uf = 0;
for (i = 1; i < NZPOBITS; i++) {
M_LSHIFT(date_ui, tmp);
if (ZEROPTONE & (1<<i))
M_ADD(prod_ui, prod_uf, date_ui, tmp);
}
if (prod_uf & 0x80000000)
prod_ui++;
if (isneg)
date_ui = 0xffffffff;
else
date_ui = 0;
tmp = prod_ui;
} else {
register u_long prod_ovr;
register u_long prod_ui;
register u_long prod_uf;
register u_long highbits;
prod_ovr = prod_ui = prod_uf = 0;
if (isneg)
highbits = 0xffffffff;
else
highbits = 0;
for (i = 1; i < NZPOBITS; i++) {
M_LSHIFT3(highbits, date_ui, tmp);
if (ZEROPTONE & (1<<i))
M_ADD3(prod_ovr, prod_uf, prod_ui,
highbits, date_ui, tmp);
}
if (prod_uf & 0x80000000)
M_ADDUF(prod_ovr, prod_ui, (u_long)1);
date_ui = prod_ovr;
tmp = prod_ui;
}
M_ADD(date_ui, tmp, offset_fudge.l_ui, offset_fudge.l_uf);
imin = imax = 0;
for (i = 1; i < NCHUCHARS; i++) {
if (L_ISGEQ(&off[i], &off[imax])) {
imax = i;
} else if (L_ISGEQ(&off[imin], &off[i])) {
imin = i;
}
}
L_ADD(&off[imin], &offset_fudge);
if (imin != imax)
L_ADD(&off[imax], &offset_fudge);
(void) printf("mean %s, min %s, max %s\n",
mfptoa(date_ui, tmp, 8), lfptoa(&off[imin], 8),
lfptoa(&off[imax], 8));
}