diff options
Diffstat (limited to 'usr.sbin/xntpd/parse')
-rw-r--r-- | usr.sbin/xntpd/parse/Makefile.kernel | 71 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/Makefile.tmpl | 111 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/README | 100 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/README.parse | 142 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/clk_dcf7000.c | 142 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/clk_meinberg.c | 444 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/clk_rawdcf.c | 553 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/clk_schmid.c | 195 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/clk_trimble.c | 131 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/empty.c | 7 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/parse.c | 1193 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/parse_conf.c | 117 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/parsesolaris.c | 1170 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/parsestreams.c | 1277 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/util/Makefile | 49 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/util/Makefile.tmpl | 49 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/util/README | 12 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/util/dcfd.c | 1206 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/util/parsetest.c | 268 | ||||
-rw-r--r-- | usr.sbin/xntpd/parse/util/testdcf.c | 485 |
20 files changed, 7722 insertions, 0 deletions
diff --git a/usr.sbin/xntpd/parse/Makefile.kernel b/usr.sbin/xntpd/parse/Makefile.kernel new file mode 100644 index 000000000000..36dcf86e7239 --- /dev/null +++ b/usr.sbin/xntpd/parse/Makefile.kernel @@ -0,0 +1,71 @@ +# +# very simple makefile (SunOS!) +# +# Possible defines: +# DEBUG_DCF: include debug code (STREAMS mechanism and parsing) +# DEBUG_CD: include signal propagation to sun4c LED (sun4c only) +# +# Possible defines (parsestreams variants only): +# KERNEL: must define +# VDDRV: loadable driver support - recommended +# KARCH: must define at make call for correct kernel module +# (currently only needed for parsestreams variants) +# +KARCH= +DEFS=-DSTREAM -DKERNEL -DVDDRV -D$(KARCH) +MICROTIME=../ppsclock/sys/$(KARCH)/microtime.s + +all: + @if [ -f /kernel/unix ]; then \ + $(MAKE) $(MFLAGS) MFLAGS="$(MFLAGS)" KARCH="`(arch -k) 2>/dev/null || uname -a | awk '{ print $5 }'`" -f Makefile.kernel parse; \ + else \ + $(MAKE) $(MFLAGS) MFLAGS="$(MFLAGS)" KARCH="`(arch -k) 2>/dev/null || uname -a | awk '{ print $5 }'`" -f Makefile.kernel parsestreams.o; \ + fi + +parse: parsesolaris.c libparse_kernel.a ../lib/libntp.a + @echo "--- WARNING: SunOS5 support is fresh and hardly tested" + @echo "--- This code could lead to kernel panics more" + @echo "--- easily than other streams modules" + $(CC) -c -I../include -D_KERNEL parsesolaris.c + ld -r -o parse parsesolaris.o libparse_kernel.a ../lib/libntp.a + @echo "--- Install 'parse' in /kernel/strmod for automatic loading" + +mparsestreams.o: parsestreams.c microtime.o ../lib/libntp.a libparse_kernel.a ../include/parse.h ../include/sys/parsestreams.h + cc -c -DMICROTIME $(DEFS) -I../include parsestreams.c + ld -r -o $@ parsestreams.o ../lib/libntp.a libparse_kernel.a \ + microtime.o + rm -f parsestreams.o + @echo "--- You may load mparsestreams.o via 'modload mparsestreams.o' into the kernel" + +parsestreams.o: parsestreams.c ../lib/libntp.a libparse_kernel.a ../include/parse.h ../include/sys/parsestreams.h + cc -c $(DEFS) -I../include parsestreams.c + ld -r -o $@ $@ ../lib/libntp.a libparse_kernel.a + @echo "--- You may load parsestreams.o via 'modload parsestreams.o' into the kernel" + +microtime.o: $(MICROTIME) assym.s + cc -E -I. $(MICROTIME) | sed -e '/\.global _uniqtime/d' > $@.i + as -o $@ $@.i + rm -f $@.i assym.s + +assym.s: genassym + ./genassym > $@ + +genassym: ../ppsclock/sys/genassym/genassym.c + cc -o $@ $? + +libparse_kernel.a: + $(MAKE) $(MFLAGS) MFLAGS="$(MFLAGS)" libparse_kernel.a + +../lib/libntp.a: + cd ../lib && $(MAKE) $(MFLAGS) MFLAGS="$(MFLAGS)" + +lint: parsestreams.c ../include/parse.h ../include/sys/parsestreams.h ../lib/llib-llibntp.ln + lint -u -I../include $(DEFS) parsestreams.c ../lib/llib-llibntp.ln + +../lib/llib-llibntp.ln: + cd ../lib && make lintlib + +clean: + rm -f *.o genassym assym.s parsestreams + +distclean: clean diff --git a/usr.sbin/xntpd/parse/Makefile.tmpl b/usr.sbin/xntpd/parse/Makefile.tmpl new file mode 100644 index 000000000000..173c91b3d89b --- /dev/null +++ b/usr.sbin/xntpd/parse/Makefile.tmpl @@ -0,0 +1,111 @@ +# +# /src/NTP/REPOSITORY/v3/parse/Makefile.tmpl,v 3.6 1993/10/10 22:44:36 kardel Exp +# +LIBNAME= libparse +KLIBNAME= libparse_kernel +# +# parse routine that could be used in two places +# +COMPILER= cc +COPTS= -O +AUTHDEFS=-DDES +LIBDEFS= -DBIG_ENDIAN +RANLIB= ranlib +INSTALL= install +CLOCKDEFS= +DEFS= +DEFS_OPT= +DEFS_LOCAL= +# +INCL=-I../include +CFLAGS= $(COPTS) $(DEFS) $(DEFS_LOCAL) $(INCL) +CC= $(COMPILER) +# +SOURCE= parse.c parse_conf.c clk_meinberg.c clk_schmid.c clk_rawdcf.c \ + clk_dcf7000.c clk_trimble.c + +OBJS= parse.o parse_conf.o clk_meinberg.o clk_schmid.o clk_rawdcf.o \ + clk_dcf7000.o clk_trimble.o + +all: + @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \ + awk '/-DREFCLOCK/ && /-DPARSE/ && /-DCLOCK_/ { makeit=1; }\ + END { if (makeit) \ + { print "echo ; echo --- creating parse libraries ; $(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" MAKE=\"$(MAKE)\" libs"; } \ + else \ + { print "echo ; echo --- creating parse placebo libraries ; $(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" MAKE=\"$(MAKE)\" emptyplacebolibs";} }' |\ + sh + @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \ + awk '/-DREFCLOCK/ && /-DPARSE/ && /-DCLOCK_/ {makeit=1; }\ + END { if (makeit) \ + { print "echo ; echo --- creating utility programs ; cd util && $(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" MAKE=\"$(MAKE)\" -k"; } \ + else \ + { print ":";} }' |\ + sh + @if (sun) > /dev/null 2>&1; then \ + echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS); \ + else :; fi | \ + awk '/-DSTREAM/ && /-DREFCLOCK/ && /-DPARSE/ && /-DCLOCK_/ { makeit=1; }\ + END { if (makeit) \ + { print "echo ; echo --- creating kernel files ; $(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" MAKE=\"$(MAKE)\" -f Makefile.kernel"; } \ + else \ + { print ":";} }' |\ + sh + +emptyplacebolibs: empty.c + @if [ ! -f "$(LIBNAME).a" -o ! -f "$(KLIBNAME).a" ]; then \ + $(CC) -c empty.c; \ + rm -f $(LIBNAME).a $(KLIBNAME).a; \ + ar r $(LIBNAME).a empty.o; \ + $(RANLIB) $(LIBNAME).a; \ + ar r $(KLIBNAME).a empty.o; \ + $(RANLIB) $(KLIBNAME).a; \ + rm -f empty.o; \ + else \ + : sorry guys - but i always get bitten by the broken ultrix sh; \ + fi + +libs: $(LIBNAME).a $(KLIBNAME).a + +$(LIBNAME).a: $(SOURCE) + $(CC) -c $(CFLAGS) $(CLOCKDEFS) -UPARSESTREAM $(SOURCE) + ar rv $@ $(OBJS) + rm -f $(OBJS) + $(RANLIB) $@ + +$(KLIBNAME).a: $(SOURCE) $(LIBNAME).a + $(CC) -c $(CFLAGS) $(CLOCKDEFS) -DPARSESTREAM $(SOURCE) + ar rv $@ $(OBJS) + rm -f $(OBJS) + $(RANLIB) $@ + +lintlib: llib-l$(LIBNAME).ln + +llib-l$(LIBNAME).ln: $(SOURCE) + lint -C$(LIBNAME) $(INCL) $(CLOCKDEFS) $(AUTHDEFS) $(LIBDEFS) $(SOURCE) >lintlib.errs + +lint: + lint -u $(DEFS) $(INCL) $(CLOCKDEFS) $(AUTHDEFS) $(LIBDEFS) $(SOURCE) >lint.errs + +depend: + mkdep $(CFLAGS) $(SOURCE) + +clean: + -@rm -f $(LIBNAME).a $(KLIBNAME).a *.o *.out *.ln make.log Makefile.bak \ + lintlib.errs lint.errs genassym assym.s parsestreams parse + -@cd util && $(MAKE) $(MFLAGS) MFLAGS="$(MFLAGS)" -f Makefile.tmpl clean + +distclean: clean + -@rm -f *.orig *.rej .version Makefile + -@cd util && $(MAKE) $(MFLAGS) MFLAGS="$(MFLAGS)" -f Makefile.tmpl distclean + +install: all + @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \ + awk '/-DREFCLOCK/ && /-DPARSE/ && /-DCLOCK_/ { makeit=1; }\ + END { if (makeit) \ + { print "echo --- installing utility programs ; cd util && $(MAKE) MAKE=\"$(MAKE)\" $@"; } \ + else \ + { print ":";} }' |\ + sh + @echo "--- Kernel modules like "parse" or "parsestreams.o" must be installed manually" + @echo "--- if applicable." diff --git a/usr.sbin/xntpd/parse/README b/usr.sbin/xntpd/parse/README new file mode 100644 index 000000000000..aa83aa7ae009 --- /dev/null +++ b/usr.sbin/xntpd/parse/README @@ -0,0 +1,100 @@ +PARSE reference clock driver: + +This directory contains the files making up the parser for +the parse refclock driver. For reasonably sane clocks this refclock +drivers allows a refclock implementation by just providing a +conversion routine and the appropriate NTP parameters. Refclock +support can run as low a 3k code with the parse refclock driver. + +The modules in here are designed to live in two worlds. In userlevel +as part of the xntp daemon and in kernel land as part of a STREAMS module +or, if someone gets to it, as part of a line discipline. Currently only +SunOS4.x/SunOS5.x STREAMS are supported (volunteers for other vendors like HP?). +This structure means, that refclock_parse can work with or without kernel +support. Kernelsupport increases accuracy tremendingly. The current restriction +of the parse driver is that it only supports SYSV type ttys and that kernel +support is only available for Suns right now. + +Three kernel modules are part of this directory. These work only on +SunOS (SunOS4 and SunOS5 (not fully tested!)). + + SunOS4 (aka Solaris 1.x): + parsestreams.o - standard parse module for SunOS 4 + mparsestreams.o - parse module for SunOS 4 with better + clock reading code (assembler) + + Both modules can be loaded via modload <modulename>. + + SunOS5 (aka Solaris 2.x): + parse - auto loadable streams module + (not fully tested - don't kill me if + it kills you machine) + + To install just drop "parse" into /kernel/strmod and + start the daemon (SunOS5 will do the rest). + +The structure of the parse reference clock driver is as follows: + + xntpd - contains NTP implementation and calls a reference clock + 127.127.8.x which is implemented by + refclock_parse.c + - which contains several refclock decriptions. These are + selected by the x part of the refclock address. + The lower two bits specify the device to use. Thus the + value (x % 4) determines the device to open + (/dev/refclock-0 - /dev/refclock-3). + + The kind of clock is selected by bits 2-6. This parameter + selects the clock type which deterimines how I/O is done, + the tty parameters and the NTP parameters. + + refclock_parse operates on an abstract reference clock + that delivers time stamps and stati. Offsets and sychron- + isation information is derived from this data and passed + on to refclock_receive of xntp which uses that data for + syncronisation. + + The abstract reference clock is generated by the parse* + routines. They parse the incoming data stream from the + clock and convert it to the appropriate time stamps. + The data is also mapped int the abstract clock states + POWERUP - clock has no valid phase and time code + information + + NOSYNC - Time code is not confirmed, phase is probably + ok. + SYNC - Time code and phase are correct. + + A clock is trusted for a certain time (type parameter) when + it leaves the SYNC state. This is derived from the + observation that quite a few clocks can still generate good + time code information when losing contact to their + synchronisation source. When the clock does not reagain + synchronisation in that trust period it will be deemed + unsynchronised until it regains synchronisation. The same + will happen if xntp sees the clock unsynchronised at + startup. + + The upper bit of x specifies that all samples delivered + from the clock should be used to discipline the NTP + loppfilter. For clock with accurate once a second time + information this means big improvements for time keeping. + A prerequisite for passing on the time stamps to + the loopfilter is, that the clock is in synchronised state. + + parse.c These are the general routines to parse the incoming data + stream. Usually these routines should not require + modification. + + clk_*.c These files hole the conversion code for the time stamps + and the description how the time code can be parsed and + where the time stamps are to be taken. + If you want to add a new clock type this is the file + you need to write in addition to mention it in + parse_conf.c and setting up the NTP and TTY parameters + in refclock_parse.c. + +Further information can be found in parse/README.parse and the various source +files. + +Frank Kardel diff --git a/usr.sbin/xntpd/parse/README.parse b/usr.sbin/xntpd/parse/README.parse new file mode 100644 index 000000000000..660973af319a --- /dev/null +++ b/usr.sbin/xntpd/parse/README.parse @@ -0,0 +1,142 @@ +MINI INFO: +The following info pertains mainly to SunOS4.x in respect to installation. +Installation for SunOS5.x (Solaris 2.x) is very simple - just drop the parse +module into /kernel/strmod. +All others notes about the software structure refer to both environments. + +#ifdef ENGLISH +Installation of a Streams module requires knowledge in kernel generation +and possession of "superuser" rights. + +This directory contains the STREAMS module code for the supported DCF/GPS +receivers of the "parse" driver. +The dataformat should be easy to adept for other clocks. + +A suitable kernel module can be generated in two ways: + 1) loadable driver + 2) linking into the kernel + +Solution 1 has the advantage that the kernel module is present right at system startup, +while solution 2 avoids reconfigurating the kernel (except for VDDRV). + +Loadable Driver: (Kernel must be configured with VDDRV option like e.g. GENERIC) + make -f Makefile.kernel + +# make one module for each kernel architecture you intend to use this module for + + make -f Makefile.kernel mparsestreams.o +# use the above command for a version with increased time stamp precision +# (available only for sun4c and sun4m architectures (thanks Craig Leres) + +Integration into kernel (refer to the Manual for complete instructions) + Still possible, but not recommended + +if you run into trouble: time@informatik.uni-erlangen.de + +Porting to different clock formats: +The streams module is designed to be able to parse different time code +packets. The parser is very simple and expects at least a start or end of packet +character. In order to be able to distinguish time code packets a list +of several start/end pairs and conversion routines can be defined in the +clockformats structure. Whenever a packet delimited by any start/end pair is +detected the conversion routines are called in a RR fashion for converting the +time code into a clocktime structure. A return code of CVT_OK indicates a +correct conversion. +(This routine will be called first on the next conversion attempt). CVT_FAIL +indicates the the packet format was detected, but the actual conversion failed +(e.g. illegal time codes). A CVT_NONE indicates that this conversion routine +did not recognize the packet format. +See the simpleformat conversion routines for Meinberg clocks for examples. +It might be possible to parse other periodically sent time codes with a fixed +format with these simple conversion routines. +The parser can be found in parse/*.c + +The actual STREAMS module is parsestreams.c. It contains some fudge factors. +These are needed if a PPS hardware signal is sampled via the serial CD input. +There are some emperically determined valued for sun4c type machine in there. +Measurements have shown, that for full precision these values have to be +determined in the actual environment, as line lengths and capacities DO matter. +So for absolute precision you need a good oscilloscope and the license for +hardware work. +WARNING: DO NOT ATTEMPT TO MEASURE IF YOU ARE NOT ABSOLUTELY CERTAIN WHAT YOU +ARE DOING. + +This instructions are distributed in the hope that they will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +I will not be in any case responsible for any damage cause by this +Software/Instruction. + +USE AT YOUR OWN RISK. + +#else + +Die folgenden Hinweise zur Uebersetzung und Installation besiehen sich auf +SunOS 4.x (Solaris1.x). Die Installation auf SunOS5.x (Solaris 2.x) gestaltet +sich erheblich einfacher. Man muss nur die Daten "parse" in dem Verzeichnis +/kernel/strmod ablegen. +Alle anderen Hinweise zur Softwarestruktur sind fuer beide Umgebungen gueltig. + +Installation eines STREAMS Moduls setzt Kenntnisse in der Kerngenerierung +und "Superuser"-Rechte vorraus. + +Dieses Inhaltsverzeichnis enthaelt das aktuelle Streams Modul fuer Sun. + +Man kann dieses Modul auf zwei Weisen in den Kern integrieren: + 1) direkt durch Einbinden (neuer Kern) + 2) als ladbarer Treiber + +Loesung 1 hat den Vorteil, dass das Modul gleich nach Systemstart zur +Verfuegung steht. +Loesung 2 besticht dadurch, das man das Modul nachtraeglich laden und +auch debuggen kann, ohne einen neuen Kern zu booten. + +Fuer ein ladbares Modul muss der Kern mit der VDDRV option konfiguriert sein und das +parsestreams.c muss mit -DVDDRV uebersetzt werden. + +Uebersetzung fuer ladbaren Treiber (Kern muss mit VDDRV konfiguriert sein): + make -f Makefile.kernel + bitte einmal fuer jede Kernelarchitektur, fuer die dieses Modul + benoetigt wird durchfuehren. + + make -f Makefile.kernel mparsestreams.o + Das obige make erstellt eine Version, die die Rechneruhr besser + als SunOS abliest. Nur fur sun4c und sun4m Architekturen verfuegbar + +Uebersetzung als .o Modul oder vorherige Einbindung in die Kernbauumgebung: + Immer noch moeglich, wird aber nicht mehr empfohlen. + +Anpassung an andere Datenformate: +Das Streamsmodul ist in der Lage verschiedene Datenformate zu erkennen und +umzusetzen. Der Parser ist einfach gehalten und kann Datenpakete anhand von +Start und Endekennzeichen unterscheiden. Jedes so erkannte Paket wird einer +Liste von Konvertierroutinen vorgelegt (clockformats Struktur). Die +Konvertierroutinen koennen mit drei verschiedenen Rueckgabewerten angeben, +wie die Konvertierung verlaufen ist. CVT_OK heisst, dass die Konvertierung +in die clocktime Struktur erfolgreich verlaufen ist. Beim naechsten +Umsetzungsversuch wird diese Routine als erstes wieder befragt werden +(Optimierung). CVT_FAIL bedeutet, dass zwar das Format erkannt wurde, aber +die eigentliche Konvertierung fehlgeschlagen ist (z. B. illegale Feldwerte). +CVT_NONE heisst, dass das Format dieser Konvertierroutine nicht erkannt wurde. +Die simpleformat Routinen fuer Meinberg Uhren koennen als Vorlage fuer eigene +Anpassungen an Uhren mit periodischem Zeittelegramm und festem Format genommen werden. +Der Parser ist in parse/*.c zu finden. + +Das eigentliche STREAMSmodul ist parsestreams.c. Es enthaelt einige +Korrekturfaktoren, die beim Einsatz von Hardware-PPS Signalen benoetigt werden. +Einige empirische Werte fuer sun4c Maschinen sind schon vorgegeben. Bei exterm +hohen Genauigkeitsanforderungen muessen diese Werte aber in der aktuellen +Installation NEU ermittelt werden, weil die Zeiten unter anderem von +Leitunglaengen der PPS Leitung abhaengen. Wenn Sie diese Abstimmung +durchfuehren, benoetigen Sie ein gutes Oszilloskop und die Lizenz fuer +Hardwarearbeiten. + +ACHTUNG: VERSUCHEN SIE NICHT DIESE MESSUNGEN ZU MACHEN, WENN IHNEN DIE +VORAUSSETZUNGEN DAFUER FEHLEN ! + +WIR GEBEN KEINE GARANTIEN + +Bei Schwierigkeiten email an: time@informatik.uni-erlangen.de + +#endif diff --git a/usr.sbin/xntpd/parse/clk_dcf7000.c b/usr.sbin/xntpd/parse/clk_dcf7000.c new file mode 100644 index 000000000000..c1c01d2480a7 --- /dev/null +++ b/usr.sbin/xntpd/parse/clk_dcf7000.c @@ -0,0 +1,142 @@ +#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_DCF7000) +/* + * /src/NTP/REPOSITORY/v3/parse/clk_dcf7000.c,v 3.8 1993/10/30 09:44:35 kardel Exp + * + * clk_dcf7000.c,v 3.8 1993/10/30 09:44:35 kardel Exp + * + * ELV DCF7000 module + * + * Copyright (c) 1992,1993 + * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#include "sys/types.h" +#include "sys/time.h" +#include "sys/errno.h" +#include "ntp_fp.h" +#include "ntp_unixtime.h" +#include "ntp_calendar.h" + +#include "parse.h" + +static struct format dcf7000_fmt = +{ /* ELV DCF7000 */ + { + { 6, 2}, { 3, 2}, { 0, 2}, + { 12, 2}, { 15, 2}, { 18, 2}, + { 9, 2}, { 21, 2}, + }, + " - - - - - - - \r", + 0 +}; + +static unsigned LONG cvt_dcf7000(); + +clockformat_t clock_dcf7000 = +{ + cvt_dcf7000, /* ELV DCF77 conversion */ + syn_simple, /* easy time stamps */ + (unsigned LONG (*)())0, /* no direct PPS monitoring */ + (unsigned LONG (*)())0, /* no time code synthesizer monitoring */ + (void *)&dcf7000_fmt, /* conversion configuration */ + "ELV DCF7000", /* ELV clock */ + 24, /* string buffer */ + F_END|SYNC_END, /* END packet delimiter / synchronisation */ + { 0, 0}, + '\0', + '\r', + '\0' +}; + +/* + * cvt_dcf7000 + * + * convert dcf7000 type format + */ +static unsigned LONG +cvt_dcf7000(buffer, size, format, clock) + register char *buffer; + register int size; + register struct format *format; + register clocktime_t *clock; +{ + if (!Strok(buffer, format->fixed_string)) + { + return CVT_NONE; + } + else + { + if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock->day, + format->field_offsets[O_DAY].length) || + Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock->month, + format->field_offsets[O_MONTH].length) || + Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock->year, + format->field_offsets[O_YEAR].length) || + Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock->hour, + format->field_offsets[O_HOUR].length) || + Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock->minute, + format->field_offsets[O_MIN].length) || + Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock->second, + format->field_offsets[O_SEC].length)) + { + return CVT_FAIL|CVT_BADFMT; + } + else + { + char *f = &buffer[format->field_offsets[O_FLAGS].offset]; + LONG flags; + + clock->flags = 0; + clock->usecond = 0; + + if (Stoi(f, &flags, format->field_offsets[O_FLAGS].length)) + { + return CVT_FAIL|CVT_BADFMT; + } + else + { + if (flags & 0x1) + clock->utcoffset = -2*60*60; + else + clock->utcoffset = -1*60*60; + + if (flags & 0x2) + clock->flags |= PARSEB_ANNOUNCE; + + if (flags & 0x4) + clock->flags |= PARSEB_NOSYNC; + } + return CVT_OK; + } + } +} +#endif /* defined(PARSE) && defined(CLOCK_DCF7000) */ + +/* + * History: + * + * clk_dcf7000.c,v + * Revision 3.6 1993/10/09 15:01:27 kardel + * file structure unified + * + * Revision 3.5 1993/10/03 19:10:41 kardel + * restructured I/O handling + * + * Revision 3.4 1993/09/27 21:08:02 kardel + * utcoffset now in seconds + * + * Revision 3.3 1993/09/26 23:40:20 kardel + * new parse driver logic + * + * Revision 3.2 1993/07/09 11:37:15 kardel + * Initial restructured version + GPS support + * + * Revision 3.1 1993/07/06 10:00:14 kardel + * DCF77 driver goes generic... + * + */ diff --git a/usr.sbin/xntpd/parse/clk_meinberg.c b/usr.sbin/xntpd/parse/clk_meinberg.c new file mode 100644 index 000000000000..363120b1eb1e --- /dev/null +++ b/usr.sbin/xntpd/parse/clk_meinberg.c @@ -0,0 +1,444 @@ +#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_MEINBERG) +/* + * /src/NTP/REPOSITORY/v3/parse/clk_meinberg.c,v 3.9 1993/10/30 09:44:38 kardel Exp + * + * clk_meinberg.c,v 3.9 1993/10/30 09:44:38 kardel Exp + * + * Meinberg clock support + * + * Copyright (c) 1992,1993 + * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#include "sys/types.h" +#include "sys/time.h" +#include "sys/errno.h" +#include "ntp_fp.h" +#include "ntp_unixtime.h" +#include "ntp_calendar.h" + +#include "parse.h" + +/* + * The Meinberg receiver every second sends a datagram of the following form + * (Standard Format) + * + * <STX>D:<dd>.<mm>.<yy>;T:<w>;U:<hh>:<mm>:<ss>;<S><F><D><A><ETX> + * pos: 0 00 00 0 00 0 11 111 1 111 12 2 22 2 22 2 2 2 3 3 3 + * 1 23 45 6 78 9 01 234 5 678 90 1 23 4 56 7 8 9 0 1 2 + * <STX> = '\002' ASCII start of text + * <ETX> = '\003' ASCII end of text + * <dd>,<mm>,<yy> = day, month, year(2 digits!!) + * <w> = day of week (sunday= 0) + * <hh>,<mm>,<ss> = hour, minute, second + * <S> = '#' if never synced since powerup else ' ' for DCF U/A 31 + * '#' if not PZF sychronisation available else ' ' for PZF 535 + * <F> = '*' if time comes from internal quartz else ' ' + * <D> = 'S' if daylight saving time is active else ' ' + * <A> = '!' during the hour preceeding an daylight saving time + * start/end change + * + * For the university of Erlangen a special format was implemented to support + * LEAP announcement and anouncement of alternate antenna. + * + * Version for UNI-ERLANGEN Software is: PZFUERL V4.6 (Meinberg) + * + * The use of this software release (or higher) is *ABSOLUTELY* + * recommended (ask for PZFUERL version as some minor HW fixes have + * been introduced) due to the LEAP second support and UTC indication. + * The standard timecode does not indicate when the timecode is in + * UTC (by front panel configuration) thus we have no chance to find + * the correct utc offset. For the standard format do not ever use + * UTC display as this is not detectable in the time code !!! + * + * <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <U><S><F><D><A><L><R><ETX> + * pos: 0 00 0 00 0 00 11 1 11 11 1 11 2 22 22 2 2 2 2 2 3 3 3 + * 1 23 4 56 7 89 01 2 34 56 7 89 0 12 34 5 6 7 8 9 0 1 2 + * <STX> = '\002' ASCII start of text + * <ETX> = '\003' ASCII end of text + * <dd>,<mm>,<yy> = day, month, year(2 digits!!) + * <w> = day of week (sunday= 0) + * <hh>,<mm>,<ss> = hour, minute, second + * <U> = 'U' UTC time display + * <S> = '#' if never synced since powerup else ' ' for DCF U/A 31 + * '#' if not PZF sychronisation available else ' ' for PZF 535 + * <F> = '*' if time comes from internal quartz else ' ' + * <D> = 'S' if daylight saving time is active else ' ' + * <A> = '!' during the hour preceeding an daylight saving time + * start/end change + * <L> = 'A' LEAP second announcement + * <R> = 'R' alternate antenna + * + * Meinberg GPS166 receiver + * + * You must get the Uni-Erlangen firmware for the GPS receiver support + * to work to full satisfaction ! + * + * <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <+/-><00:00>; <U><S><F><D><A><L><R><L>; <position...><ETX> + * + * 000000000111111111122222222223333333333444444444455555555556666666 + * 123456789012345678901234567890123456789012345678901234567890123456 + * \x0209.07.93; 5; 08:48:26; +00:00; ; 49.5736N 11.0280E 373m\x03 + * + * + * <STX> = '\002' ASCII start of text + * <ETX> = '\003' ASCII end of text + * <dd>,<mm>,<yy> = day, month, year(2 digits!!) + * <w> = day of week (sunday= 0) + * <hh>,<mm>,<ss> = hour, minute, second + * <+/->,<00:00> = offset to UTC + * <S> = '#' if never synced since powerup else ' ' for DCF U/A 31 + * '#' if not PZF sychronisation available else ' ' for PZF 535 + * <U> = 'U' UTC time display + * <F> = '*' if time comes from internal quartz else ' ' + * <D> = 'S' if daylight saving time is active else ' ' + * <A> = '!' during the hour preceeding an daylight saving time + * start/end change + * <L> = 'A' LEAP second announcement + * <R> = 'R' alternate antenna (reminiscent of PZF535) usually ' ' + * <L> = 'L' on 23:59:60 + */ + +static struct format meinberg_fmt[] = +{ + { + { + { 3, 2}, { 6, 2}, { 9, 2}, + { 18, 2}, { 21, 2}, { 24, 2}, + { 14, 1}, { 27, 4}, { 29, 1}, + }, + "\2D: . . ;T: ;U: . . ; \3", + 0 + }, + { /* special extended FAU Erlangen extended format */ + { + { 1, 2}, { 4, 2}, { 7, 2}, + { 14, 2}, { 17, 2}, { 20, 2}, + { 11, 1}, { 25, 4}, { 27, 1}, + }, + "\2 . . ; ; : : ; \3", + MBG_EXTENDED + }, + { /* special extended FAU Erlangen GPS format */ + { + { 1, 2}, { 4, 2}, { 7, 2}, + { 14, 2}, { 17, 2}, { 20, 2}, + { 11, 1}, { 32, 8}, { 35, 1}, + { 25, 2}, { 28, 2}, { 24, 1} + }, + "\2 . . ; ; : : ; : ; ; . . ", + 0 + } +}; + +static unsigned LONG cvt_meinberg(); +static unsigned LONG cvt_mgps(); + +clockformat_t clock_meinberg[] = +{ + { + cvt_meinberg, /* Meinberg conversion */ + syn_simple, /* easy time stamps for RS232 (fallback) */ + pps_simple, /* easy PPS monitoring */ + (unsigned LONG (*)())0, /* no time code synthesizer monitoring */ + (void *)&meinberg_fmt[0], /* conversion configuration */ + "Meinberg Standard", /* Meinberg simple format - beware */ + 32, /* string buffer */ + F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */ + { 0, 0}, + '\2', + '\3', + '\0' + }, + { + cvt_meinberg, /* Meinberg conversion */ + syn_simple, /* easy time stamps for RS232 (fallback) */ + pps_simple, /* easy PPS monitoring */ + (unsigned LONG (*)())0, /* no time code synthesizer monitoring */ + (void *)&meinberg_fmt[1], /* conversion configuration */ + "Meinberg Extended", /* Meinberg enhanced format */ + 32, /* string buffer */ + F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */ + { 0, 0}, + '\2', + '\3', + '\0' + }, + { + cvt_mgps, /* Meinberg GPS166 conversion */ + syn_simple, /* easy time stamps for RS232 (fallback) */ + pps_simple, /* easy PPS monitoring */ + (unsigned LONG (*)())0, /* no time code synthesizer monitoring */ + (void *)&meinberg_fmt[2], /* conversion configuration */ + "Meinberg GPS Extended", /* Meinberg FAU GPS format */ + 70, /* string buffer */ + F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */ + { 0, 0}, + '\2', + '\3', + '\0' + } +}; + +/* + * cvt_meinberg + * + * convert simple type format + */ +static unsigned LONG +cvt_meinberg(buffer, size, format, clock) + register char *buffer; + register int size; + register struct format *format; + register clocktime_t *clock; +{ + if (!Strok(buffer, format->fixed_string)) + { + return CVT_NONE; + } + else + { + if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock->day, + format->field_offsets[O_DAY].length) || + Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock->month, + format->field_offsets[O_MONTH].length) || + Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock->year, + format->field_offsets[O_YEAR].length) || + Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock->hour, + format->field_offsets[O_HOUR].length) || + Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock->minute, + format->field_offsets[O_MIN].length) || + Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock->second, + format->field_offsets[O_SEC].length)) + { + return CVT_FAIL|CVT_BADFMT; + } + else + { + char *f = &buffer[format->field_offsets[O_FLAGS].offset]; + + clock->flags = 0; + clock->usecond = 0; + + /* + * in the extended timecode format we have also the + * indication that the timecode is in UTC + * for compatibilty reasons we start at the USUAL + * offset (POWERUP flag) and know that the UTC indication + * is the character before the powerup flag + */ + if ((format->flags & MBG_EXTENDED) && (f[-1] == 'U')) + { + /* + * timecode is in UTC + */ + clock->utcoffset = 0; /* UTC */ + clock->flags |= PARSEB_UTC; + } + else + { + /* + * only calculate UTC offset if MET/MED is in time code + * or we have the old time code format, where we do not + * know whether it is UTC time or MET/MED + * pray that nobody switches to UTC in the standard time code + * ROMS !!!! + */ + switch (buffer[format->field_offsets[O_ZONE].offset]) + { + case ' ': + clock->utcoffset = -1*60*60; /* MET */ + break; + + case 'S': + clock->utcoffset = -2*60*60; /* MED */ + break; + + default: + return CVT_FAIL|CVT_BADFMT; + } + } + + /* + * gather status flags + */ + if (buffer[format->field_offsets[O_ZONE].offset] == 'S') + clock->flags |= PARSEB_DST; + + if (f[0] == '#') + clock->flags |= PARSEB_POWERUP; + + if (f[1] == '*') + clock->flags |= PARSEB_NOSYNC; + + if (f[3] == '!') + clock->flags |= PARSEB_ANNOUNCE; + + if (format->flags & MBG_EXTENDED) + { + clock->flags |= PARSEB_S_LEAP; + clock->flags |= PARSEB_S_ANTENNA; + + if (f[4] == 'A') + clock->flags |= PARSEB_LEAP; + + if (f[5] == 'R') + clock->flags |= PARSEB_ALTERNATE; + } + return CVT_OK; + } + } +} + +/* + * cvt_mgps + * + * convert Meinberg GPS format + */ +static unsigned LONG +cvt_mgps(buffer, size, format, clock) + register char *buffer; + register int size; + register struct format *format; + register clocktime_t *clock; +{ + if (!Strok(buffer, format->fixed_string)) + { + return CVT_NONE; + } + else + { + if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock->day, + format->field_offsets[O_DAY].length) || + Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock->month, + format->field_offsets[O_MONTH].length) || + Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock->year, + format->field_offsets[O_YEAR].length) || + Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock->hour, + format->field_offsets[O_HOUR].length) || + Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock->minute, + format->field_offsets[O_MIN].length) || + Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock->second, + format->field_offsets[O_SEC].length)) + { + return CVT_FAIL|CVT_BADFMT; + } + else + { + LONG h; + char *f = &buffer[format->field_offsets[O_FLAGS].offset]; + + clock->flags = PARSEB_S_LEAP|PARSEB_S_POSITION; + + clock->usecond = 0; + + /* + * calculate UTC offset + */ + if (Stoi(&buffer[format->field_offsets[O_UTCHOFFSET].offset], &h, + format->field_offsets[O_UTCHOFFSET].length)) + { + return CVT_FAIL|CVT_BADFMT; + } + else + { + if (Stoi(&buffer[format->field_offsets[O_UTCMOFFSET].offset], &clock->utcoffset, + format->field_offsets[O_UTCMOFFSET].length)) + { + return CVT_FAIL|CVT_BADFMT; + } + + clock->utcoffset += TIMES60(h); + + if (buffer[format->field_offsets[O_UTCSOFFSET].offset] != '-') + { + clock->utcoffset = -clock->utcoffset; + } + } + + /* + * gather status flags + */ + if (buffer[format->field_offsets[O_ZONE].offset] == 'S') + clock->flags |= PARSEB_DST; + + if ((f[0] == 'U') || + (clock->utcoffset == 0)) + clock->flags |= PARSEB_UTC; + + /* + * no sv's seen - no time & position + */ + if (f[1] == '#') + clock->flags |= PARSEB_POWERUP; + + /* + * at least one sv seen - time (for last position) + */ + if (f[2] == '*') + clock->flags |= PARSEB_NOSYNC; + else + if (!(clock->flags & PARSEB_POWERUP)) + clock->flags |= PARSEB_POSITION; + + /* + * oncoming zone switch + */ + if (f[4] == '!') + clock->flags |= PARSEB_ANNOUNCE; + + /* + * oncoming leap second + */ + if (f[5] == 'A') + clock->flags |= PARSEB_LEAP; + + /* + * this is the leap second + */ + if (f[7] == 'L') + clock->flags |= PARSEB_LEAPSECOND; + + return CVT_OK; + } + } +} +#endif /* defined(PARSE) && defined(CLOCK_MEINBERG) */ + +/* + * History: + * + * clk_meinberg.c,v + * Revision 3.9 1993/10/30 09:44:38 kardel + * conditional compilation flag cleanup + * + * Revision 3.8 1993/10/22 14:27:48 kardel + * Oct. 22nd 1993 reconcilation + * + * Revision 3.7 1993/10/09 15:01:30 kardel + * file structure unified + * + * Revision 3.6 1993/10/03 19:10:43 kardel + * restructured I/O handling + * + * Revision 3.5 1993/09/27 21:08:04 kardel + * utcoffset now in seconds + * + * Revision 3.4 1993/09/26 23:40:22 kardel + * new parse driver logic + * + * Revision 3.3 1993/08/18 09:29:32 kardel + * GPS format is somewhat variable length - variable length part holds position + * + * Revision 3.2 1993/07/09 11:37:16 kardel + * Initial restructured version + GPS support + * + * Revision 3.1 1993/07/06 10:00:17 kardel + * DCF77 driver goes generic... + * + */ diff --git a/usr.sbin/xntpd/parse/clk_rawdcf.c b/usr.sbin/xntpd/parse/clk_rawdcf.c new file mode 100644 index 000000000000..4a5ad5608a4e --- /dev/null +++ b/usr.sbin/xntpd/parse/clk_rawdcf.c @@ -0,0 +1,553 @@ +#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_RAWDCF) +/* + * /src/NTP/REPOSITORY/v3/parse/clk_rawdcf.c,v 3.7 1993/10/30 09:44:41 kardel Exp + * + * clk_rawdcf.c,v 3.7 1993/10/30 09:44:41 kardel Exp + * + * Raw DCF77 pulse clock support + * + * Copyright (c) 1992,1993 + * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#include "sys/types.h" +#include "sys/time.h" +#include "sys/errno.h" +#include "ntp_fp.h" +#include "ntp_unixtime.h" +#include "ntp_calendar.h" + +#include "parse.h" +#ifdef PARSESTREAM +#include "sys/parsestreams.h" +#endif + +#ifndef PARSEKERNEL +#include "ntp_stdlib.h" +#endif + +/* + * DCF77 raw time code + * + * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig + * und Berlin, Maerz 1989 + * + * Timecode transmission: + * AM: + * time marks are send every second except for the second before the + * next minute mark + * time marks consist of a reduction of transmitter power to 25% + * of the nominal level + * the falling edge is the time indication (on time) + * time marks of a 100ms duration constitute a logical 0 + * time marks of a 200ms duration constitute a logical 1 + * FM: + * see the spec. (basically a (non-)inverted psuedo random phase shift) + * + * Encoding: + * Second Contents + * 0 - 10 AM: free, FM: 0 + * 11 - 14 free + * 15 R - alternate antenna + * 16 A1 - expect zone change (1 hour before) + * 17 - 18 Z1,Z2 - time zone + * 0 0 illegal + * 0 1 MEZ (MET) + * 1 0 MESZ (MED, MET DST) + * 1 1 illegal + * 19 A2 - expect leap insertion/deletion (1 hour before) + * 20 S - start of time code (1) + * 21 - 24 M1 - BCD (lsb first) Minutes + * 25 - 27 M10 - BCD (lsb first) 10 Minutes + * 28 P1 - Minute Parity (even) + * 29 - 32 H1 - BCD (lsb first) Hours + * 33 - 34 H10 - BCD (lsb first) 10 Hours + * 35 P2 - Hour Parity (even) + * 36 - 39 D1 - BCD (lsb first) Days + * 40 - 41 D10 - BCD (lsb first) 10 Days + * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday) + * 45 - 49 MO - BCD (lsb first) Month + * 50 MO0 - 10 Months + * 51 - 53 Y1 - BCD (lsb first) Years + * 54 - 57 Y10 - BCD (lsb first) 10 Years + * 58 P3 - Date Parity (even) + * 59 - usually missing (minute indication), except for leap insertion + */ + +static unsigned LONG cvt_rawdcf(); +static unsigned LONG pps_rawdcf(); +static unsigned LONG snt_rawdcf(); + +clockformat_t clock_rawdcf = +{ + cvt_rawdcf, /* raw dcf input conversion */ + (void (*)())0, /* no character bound synchronisation */ + pps_rawdcf, /* examining PPS information */ + snt_rawdcf, /* synthesize time code from input */ + (void *)0, /* buffer bit representation */ + "RAW DCF77 Timecode", /* direct decoding / time synthesis */ + 61, /* bit buffer */ + SYNC_ONE|SYNC_ZERO|SYNC_TIMEOUT|SYNC_SYNTHESIZE|CVT_FIXEDONLY, + /* catch all transitions, buffer restart on timeout, fixed configuration only */ + { 1, 500000}, /* restart after 1.5 seconds */ + '\0', + '\0', + '\0' +}; + +static struct dcfparam +{ + unsigned char onebits[60]; + unsigned char zerobits[60]; +} dcfparam = +{ + "###############RADMLS1248124P124812P1248121241248112481248P", /* 'ONE' representation */ + "--------------------s-------p------p----------------------p" /* 'ZERO' representation */ +}; + +static struct rawdcfcode +{ + char offset; /* start bit */ +} rawdcfcode[] = +{ + { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 }, + { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 } +}; + +#define DCF_M 0 +#define DCF_R 1 +#define DCF_A1 2 +#define DCF_Z 3 +#define DCF_A2 4 +#define DCF_S 5 +#define DCF_M1 6 +#define DCF_M10 7 +#define DCF_P1 8 +#define DCF_H1 9 +#define DCF_H10 10 +#define DCF_P2 11 +#define DCF_D1 12 +#define DCF_D10 13 +#define DCF_DW 14 +#define DCF_MO 15 +#define DCF_MO0 16 +#define DCF_Y1 17 +#define DCF_Y10 18 +#define DCF_P3 19 + +static struct partab +{ + char offset; /* start bit of parity field */ +} partab[] = +{ + { 21 }, { 29 }, { 36 }, { 59 } +}; + +#define DCF_P_P1 0 +#define DCF_P_P2 1 +#define DCF_P_P3 2 + +#define DCF_Z_MET 0x2 +#define DCF_Z_MED 0x1 + +static unsigned LONG ext_bf(buf, idx, zero) + register char *buf; + register int idx; + register char *zero; +{ + register unsigned LONG sum = 0; + register int i, first; + + first = rawdcfcode[idx].offset; + + for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--) + { + sum <<= 1; + sum |= (buf[i] != zero[i]); + } + return sum; +} + +static unsigned pcheck(buf, idx, zero) + register char *buf; + register int idx; + register char *zero; +{ + register int i,last; + register unsigned psum = 1; + + last = partab[idx+1].offset; + + for (i = partab[idx].offset; i < last; i++) + psum ^= (buf[i] != zero[i]); + + return psum; +} + +static unsigned LONG convert_rawdcf(buffer, size, dcfparam, clock) + register unsigned char *buffer; + register int size; + register struct dcfparam *dcfparam; + register clocktime_t *clock; +{ + register unsigned char *s = buffer; + register unsigned char *b = dcfparam->onebits; + register unsigned char *c = dcfparam->zerobits; + register int i; + + parseprintf(DD_RAWDCF,("parse: convert_rawdcf: \"%s\"\n", buffer)); + + if (size < 57) + { +#ifdef PARSEKERNEL + printf("parse: convert_rawdcf: INCOMPLETE DATA - time code only has %d bits\n", size); +#else + syslog(LOG_ERR, "parse: convert_rawdcf: INCOMPLETE DATA - time code only has %d bits\n", size); +#endif + return CVT_NONE; + } + + for (i = 0; i < 58; i++) + { + if ((*s != *b) && (*s != *c)) + { + /* + * we only have two types of bytes (ones and zeros) + */ +#ifdef PARSEKERNEL + printf("parse: convert_rawdcf: BAD DATA - no conversion for \"%s\"\n", buffer); +#else + syslog(LOG_ERR, "parse: convert_rawdcf: BAD DATA - no conversion for \"%s\"\n", buffer); +#endif + return CVT_NONE; + } + b++; + c++; + s++; + } + + /* + * check Start and Parity bits + */ + if ((ext_bf(buffer, DCF_S, dcfparam->zerobits) == 1) && + pcheck(buffer, DCF_P_P1, dcfparam->zerobits) && + pcheck(buffer, DCF_P_P2, dcfparam->zerobits) && + pcheck(buffer, DCF_P_P3, dcfparam->zerobits)) + { + /* + * buffer OK + */ + parseprintf(DD_RAWDCF,("parse: convert_rawdcf: parity check passed\n")); + + clock->flags = PARSEB_S_ANTENNA|PARSEB_S_LEAP; + clock->usecond= 0; + clock->second = 0; + clock->minute = ext_bf(buffer, DCF_M10, dcfparam->zerobits); + clock->minute = TIMES10(clock->minute) + ext_bf(buffer, DCF_M1, dcfparam->zerobits); + clock->hour = ext_bf(buffer, DCF_H10, dcfparam->zerobits); + clock->hour = TIMES10(clock->hour) + ext_bf(buffer, DCF_H1, dcfparam->zerobits); + clock->day = ext_bf(buffer, DCF_D10, dcfparam->zerobits); + clock->day = TIMES10(clock->day) + ext_bf(buffer, DCF_D1, dcfparam->zerobits); + clock->month = ext_bf(buffer, DCF_MO0, dcfparam->zerobits); + clock->month = TIMES10(clock->month) + ext_bf(buffer, DCF_MO, dcfparam->zerobits); + clock->year = ext_bf(buffer, DCF_Y10, dcfparam->zerobits); + clock->year = TIMES10(clock->year) + ext_bf(buffer, DCF_Y1, dcfparam->zerobits); + + switch (ext_bf(buffer, DCF_Z, dcfparam->zerobits)) + { + case DCF_Z_MET: + clock->utcoffset = -1*60*60; + break; + + case DCF_Z_MED: + clock->flags |= PARSEB_DST; + clock->utcoffset = -2*60*60; + break; + + default: + parseprintf(DD_RAWDCF,("parse: convert_rawdcf: BAD TIME ZONE\n")); + return CVT_FAIL|CVT_BADFMT; + } + + if (ext_bf(buffer, DCF_A1, dcfparam->zerobits)) + clock->flags |= PARSEB_ANNOUNCE; + + if (ext_bf(buffer, DCF_A2, dcfparam->zerobits)) + clock->flags |= PARSEB_LEAP; + + if (ext_bf(buffer, DCF_R, dcfparam->zerobits)) + clock->flags |= PARSEB_ALTERNATE; + + parseprintf(DD_RAWDCF,("parse: convert_rawdcf: TIME CODE OK: %d:%d, %d.%d.%d, flags 0x%x\n", + clock->hour, clock->minute, clock->day, clock->month, clock->year, + clock->flags)); + return CVT_OK; + } + else + { + /* + * bad format - not for us + */ +#ifdef PARSEKERNEL + printf("parse: convert_rawdcf: parity check FAILED for \"%s\"\n", buffer); +#else + syslog(LOG_ERR, "parse: convert_rawdcf: parity check FAILED for \"%s\"\n", buffer); +#endif + return CVT_FAIL|CVT_BADFMT; + } +} + +/* + * raw dcf input routine - needs to fix up 50 baud + * characters for 1/0 decision + */ +static unsigned LONG cvt_rawdcf(buffer, size, param, clock) + register unsigned char *buffer; + register int size; + register void *param; + register clocktime_t *clock; +{ + register unsigned char *s = buffer; + register unsigned char *e = buffer + size; + register unsigned char *b = dcfparam.onebits; + register unsigned char *c = dcfparam.zerobits; + register unsigned rtc = CVT_NONE; + register unsigned int i, lowmax, highmax, cutoff, span; +#define BITS 9 + unsigned char histbuf[BITS]; + /* + * the input buffer contains characters with runs of consecutive + * bits set. These set bits are an indication of the DCF77 pulse + * length. We assume that we receive the pulse at 50 Baud. Thus + * a 100ms pulse would generate a 4 bit train (20ms per bit and + * start bit) + * a 200ms pulse would create all zeroes (and probably a frame error) + */ + + for (i = 0; i < BITS; i++) + { + histbuf[i] = 0; + } + + cutoff = 0; + lowmax = 0; + + while (s < e) + { + register unsigned int ch = *s ^ 0xFF; + /* + * these lines are left as an excercise to the reader 8-) + */ + if (!((ch+1) & ch) || !*s) + { + + for (i = 0; ch; i++) + { + ch >>= 1; + } + + *s = i; + histbuf[i]++; + cutoff += i; + lowmax++; + } + else + { + parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: character check for 0x%x@%d FAILED\n", *s, s - buffer)); + *s = ~0; + rtc = CVT_FAIL|CVT_BADFMT; + } + s++; + } + + if (lowmax) + { + cutoff /= lowmax; + } + else + { + cutoff = 4; /* doesn't really matter - it'll fail anyway, but gives error output */ + } + + parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: average bit count: %d\n", cutoff)); + + lowmax = 0; + highmax = 0; + + parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: histogram:")); + for (i = 0; i <= cutoff; i++) + { + lowmax+=histbuf[i] * i; + highmax += histbuf[i]; + parseprintf(DD_RAWDCF,(" %d", histbuf[i])); + } + parseprintf(DD_RAWDCF, (" <M>")); + + lowmax += highmax / 2; + + if (highmax) + { + lowmax /= highmax; + } + else + { + lowmax = 0; + } + + highmax = 0; + cutoff = 0; + + for (; i < BITS; i++) + { + highmax+=histbuf[i] * i; + cutoff +=histbuf[i]; + parseprintf(DD_RAWDCF,(" %d", histbuf[i])); + } + parseprintf(DD_RAWDCF,("\n")); + + if (cutoff) + { + highmax /= cutoff; + } + else + { + highmax = BITS-1; + } + + span = cutoff = lowmax; + for (i = lowmax; i <= highmax; i++) + { + if (histbuf[cutoff] > histbuf[i]) + { + cutoff = i; + span = i; + } + else + if (histbuf[cutoff] == histbuf[i]) + { + span = i; + } + } + + cutoff = (cutoff + span) / 2; + + parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: lower maximum %d, higher maximum %d, cutoff %d\n", lowmax, highmax, cutoff)); + + s = buffer; + while ((s < e) && *c && *b) + { + if (*s == (unsigned char)~0) + { + *s = '?'; + } + else + { + *s = (*s >= cutoff) ? *b : *c; + } + s++; + b++; + c++; + } + + return (rtc == CVT_NONE) ? convert_rawdcf(buffer, size, &dcfparam, clock) : rtc; +} + +/* + * pps_rawdcf + * + * currently a very stupid version - should be extended to decode + * also ones and zeros (which is easy) + */ +/*ARGSUSED*/ +static unsigned LONG pps_rawdcf(parseio, status, ptime) + register parse_t *parseio; + register int status; + register timestamp_t *ptime; +{ + if (status == SYNC_ONE) + { + parseio->parse_dtime.parse_ptime = *ptime; + parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS; + } + + return CVT_NONE; +} + +/*ARGSUSED*/ +static unsigned LONG snt_rawdcf(parseio, ptime) + register parse_t *parseio; + register timestamp_t *ptime; +{ + clocktime_t clock; + unsigned LONG cvtrtc; + time_t t; + + /* + * start at last sample and add second index - gross, may have to be much more careful + */ + if (convert_rawdcf(parseio->parse_ldata, parseio->parse_ldsize - 1, &dcfparam, &clock) == CVT_OK) + { + if ((t = parse_to_unixtime(&clock, &cvtrtc)) == -1) + { + parseprintf(DD_RAWDCF,("parse: snt_rawdcf: time conversion FAILED\n")); + return CVT_FAIL|cvtrtc; + } + } + else + { + parseprintf(DD_RAWDCF,("parse: snt_rawdcf: data conversion FAILED\n")); + return CVT_NONE; + } + + parseio->parse_dtime.parse_stime = *ptime; + + t += parseio->parse_index - 1; + + /* + * time stamp + */ +#ifdef PARSEKERNEL + parseio->parse_dtime.parse_time.tv.tv_sec = t; + parseio->parse_dtime.parse_time.tv.tv_usec = clock.usecond; +#else + parseio->parse_dtime.parse_time.fp.l_ui = t + JAN_1970; + TVUTOTSF(clock.usecond, parseio->parse_dtime.parse_time.fp.l_uf); +#endif + + parseprintf(DD_RAWDCF,("parse: snt_rawdcf: time stamp synthesized offset %d seconds\n", parseio->parse_index - 1)); + + return updatetimeinfo(parseio, t, clock.usecond, clock.flags); +} +#endif /* defined(PARSE) && defined(CLOCK_RAWDCF) */ + +/* + * History: + * + * clk_rawdcf.c,v + * Revision 3.7 1993/10/30 09:44:41 kardel + * conditional compilation flag cleanup + * + * Revision 3.6 1993/10/03 19:10:45 kardel + * restructured I/O handling + * + * Revision 3.5 1993/09/27 21:08:07 kardel + * utcoffset now in seconds + * + * Revision 3.4 1993/09/26 23:40:25 kardel + * new parse driver logic + * + * Revision 3.3 1993/09/01 21:44:54 kardel + * conditional cleanup + * + * Revision 3.2 1993/07/09 11:37:18 kardel + * Initial restructured version + GPS support + * + * Revision 3.1 1993/07/06 10:00:19 kardel + * DCF77 driver goes generic... + * + */ diff --git a/usr.sbin/xntpd/parse/clk_schmid.c b/usr.sbin/xntpd/parse/clk_schmid.c new file mode 100644 index 000000000000..12b06fdcc304 --- /dev/null +++ b/usr.sbin/xntpd/parse/clk_schmid.c @@ -0,0 +1,195 @@ +#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_SCHMID) +/* + * /src/NTP/REPOSITORY/v3/parse/clk_schmid.c,v 3.8 1993/11/01 20:00:18 kardel Exp + * + * clk_schmid.c,v 3.8 1993/11/01 20:00:18 kardel Exp + * + * Schmid clock support + * + * Copyright (c) 1992,1993 + * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#include "sys/types.h" +#include "sys/time.h" +#include "sys/errno.h" +#include "ntp_fp.h" +#include "ntp_unixtime.h" +#include "ntp_calendar.h" + +#include "parse.h" + +/* + * Description courtesy of Adam W. Feigin et. al (Swisstime iis.ethz.ch) + * + * The command to Schmid's DCF77 clock is a single byte; each bit + * allows the user to select some part of the time string, as follows (the + * output for the lsb is sent first). + * + * Bit 0: time in MEZ, 4 bytes *binary, not BCD*; hh.mm.ss.tenths + * Bit 1: date 3 bytes *binary, not BCD: dd.mm.yy + * Bit 2: week day, 1 byte (unused here) + * Bit 3: time zone, 1 byte, 0=MET, 1=MEST. (unused here) + * Bit 4: clock status, 1 byte, 0=time invalid, + * 1=time from crystal backup, + * 3=time from DCF77 + * Bit 5: transmitter status, 1 byte, + * bit 0: backup antenna + * bit 1: time zone change within 1h + * bit 3,2: TZ 01=MEST, 10=MET + * bit 4: leap second will be + * added within one hour + * bits 5-7: Zero + * Bit 6: time in backup mode, units of 5 minutes (unused here) + * + */ +#define WS_TIME 0x01 +#define WS_SIGNAL 0x02 + +#define WS_ALTERNATE 0x01 +#define WS_ANNOUNCE 0x02 +#define WS_TZ 0x0c +#define WS_MET 0x08 +#define WS_MEST 0x04 +#define WS_LEAP 0x10 + +static unsigned LONG cvt_schmid(); + +clockformat_t clock_schmid = +{ + cvt_schmid, /* Schmid conversion */ + syn_simple, /* easy time stamps */ + (unsigned LONG (*)())0, /* not direct PPS monitoring */ + (unsigned LONG (*)())0, /* no time code synthesizer monitoring */ + (void *)0, /* conversion configuration */ + "Schmid", /* Schmid receiver */ + 12, /* binary data buffer */ + F_END|SYNC_START, /* END packet delimiter / synchronisation */ + { 0, 0}, + '\0', + (unsigned char)'\375', + '\0' +}; + + +static unsigned LONG +cvt_schmid(buffer, size, format, clock) + register char *buffer; + register int size; + register struct format *format; + register clocktime_t *clock; +{ + if ((size != 11) || (buffer[10] != '\375')) + { + return CVT_NONE; + } + else + { + if (buffer[0] > 23 || buffer[1] > 59 || buffer[2] > 59 || buffer[3] > 9 /* Time */ + || buffer[0] < 0 || buffer[1] < 0 || buffer[2] < 0 || buffer[3] < 0) + { + return CVT_FAIL|CVT_BADTIME; + } + else + if (buffer[4] < 1 || buffer[4] > 31 || buffer[5] < 1 || buffer[5] > 12 + || buffer[6] > 99) + { + return CVT_FAIL|CVT_BADDATE; + } + else + { + clock->hour = buffer[0]; + clock->minute = buffer[1]; + clock->second = buffer[2]; + clock->usecond = buffer[3] * 100000; + clock->day = buffer[4]; + clock->month = buffer[5]; + clock->year = buffer[6]; + + clock->flags = 0; + + switch (buffer[8] & WS_TZ) + { + case WS_MET: + clock->utcoffset = -1*60*60; + break; + + case WS_MEST: + clock->utcoffset = -2*60*60; + clock->flags |= PARSEB_DST; + break; + + default: + return CVT_FAIL|CVT_BADFMT; + } + + if (!(buffer[7] & WS_TIME)) + { + clock->flags |= PARSEB_POWERUP; + } + + if (!(buffer[7] & WS_SIGNAL)) + { + clock->flags |= PARSEB_NOSYNC; + } + + if (buffer[7] & WS_SIGNAL) + { + if (buffer[8] & WS_ALTERNATE) + { + clock->flags |= PARSEB_ALTERNATE; + } + + if (buffer[8] & WS_ANNOUNCE) + { + clock->flags |= PARSEB_ANNOUNCE; + } + + if (buffer[8] & WS_LEAP) + { + clock->flags |= PARSEB_LEAP; + } + } + + clock->flags |= PARSEB_S_LEAP|PARSEB_S_ANTENNA; + + return CVT_OK; + } + } +} +#endif /* defined(PARSE) && defined(CLOCK_SCHMID) */ + +/* + * History: + * + * clk_schmid.c,v + * Revision 3.8 1993/11/01 20:00:18 kardel + * parse Solaris support (initial version) + * + * Revision 3.7 1993/10/30 09:44:43 kardel + * conditional compilation flag cleanup + * + * Revision 3.6 1993/10/09 15:01:32 kardel + * file structure unified + * + * Revision 3.5 1993/10/03 19:10:47 kardel + * restructured I/O handling + * + * Revision 3.4 1993/09/27 21:08:09 kardel + * utcoffset now in seconds + * + * Revision 3.3 1993/09/26 23:40:27 kardel + * new parse driver logic + * + * Revision 3.2 1993/07/09 11:37:19 kardel + * Initial restructured version + GPS support + * + * Revision 3.1 1993/07/06 10:00:22 kardel + * DCF77 driver goes generic... + * + */ diff --git a/usr.sbin/xntpd/parse/clk_trimble.c b/usr.sbin/xntpd/parse/clk_trimble.c new file mode 100644 index 000000000000..c9c124ea4dee --- /dev/null +++ b/usr.sbin/xntpd/parse/clk_trimble.c @@ -0,0 +1,131 @@ +#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_TRIMSV6) +/* + * /src/NTP/REPOSITORY/v3/parse/clk_trimble.c,v 3.6 1993/10/30 09:44:45 kardel Exp + * + * Trimble SV6 clock support + */ + +#include "sys/types.h" +#include "sys/time.h" +#include "sys/errno.h" +#include "ntp_fp.h" +#include "ntp_unixtime.h" +#include "ntp_calendar.h" + +#include "parse.h" + +/* 0000000000111111111122222222223333333 / char + * 0123456789012345678901234567890123456 \ posn + * >RTMhhmmssdddDDMMYYYYoodnnvrrrrr;*xx< Actual + * ----33445566600112222BB7__-_____--99- Parse + * >RTM 1 ;* <", Check + */ + +#define hexval(x) (('0' <= (x) && (x) <= '9') ? (x) - '0' : \ + ('a' <= (x) && (x) <= 'f') ? (x) - 'a' + 10 : \ + ('A' <= (x) && (x) <= 'F') ? (x) - 'A' + 10 : \ + -1) +#define O_USEC O_WDAY +#define O_GPSFIX O_FLAGS +#define O_CHKSUM O_UTCHOFFSET +static struct format trimsv6_fmt = +{ { { 13, 2 }, {15, 2}, { 17, 4}, /* Day, Month, Year */ + { 4, 2 }, { 6, 2}, { 8, 2}, /* Hour, Minute, Second */ + { 10, 3 }, {23, 1}, { 0, 0}, /* uSec, FIXes (WeekDAY, FLAGS, ZONE) */ + { 34, 2 }, { 0, 0}, { 21, 2}, /* cksum, -, utcS (UTC[HMS]OFFSET) */ + }, + ">RTM 1 ;* <", + 0 +}; + +static unsigned LONG cvt_trimsv6(); + +clockformat_t clock_trimsv6 = +{ cvt_trimsv6, /* Trimble conversion */ + syn_simple, /* easy time stamps for RS232 (fallback) */ + pps_simple, /* easy PPS monitoring */ + (unsigned LONG (*)())0, /* no time code synthesizer monitoring */ + (void *)&trimsv6_fmt, /* conversion configuration */ + "Trimble SV6", + 37, /* string buffer */ + F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */ + { 0, 0}, + '>', + '<', + '\0' +}; + +static unsigned LONG +cvt_trimsv6(buffer, size, format, clock) + register char *buffer; + register int size; + register struct format *format; + register clocktime_t *clock; +{ + LONG gpsfix; + u_char calc_csum = 0; + long recv_csum; + int i; + + if (!Strok(buffer, format->fixed_string)) return CVT_NONE; +#define OFFS(x) format->field_offsets[(x)].offset +#define STOI(x, y) \ + Stoi(&buffer[OFFS(x)], y, \ + format->field_offsets[(x)].length) + if ( STOI(O_DAY, &clock->day) || + STOI(O_MONTH, &clock->month) || + STOI(O_YEAR, &clock->year) || + STOI(O_HOUR, &clock->hour) || + STOI(O_MIN, &clock->minute) || + STOI(O_SEC, &clock->second) || + STOI(O_USEC, &clock->usecond)|| + STOI(O_GPSFIX, &gpsfix) + ) return CVT_FAIL|CVT_BADFMT; + + clock->usecond *= 1000; + /* Check that the checksum is right */ + for (i=OFFS(O_CHKSUM)-1; i >= 0; i--) calc_csum ^= buffer[i]; + recv_csum = (hexval(buffer[OFFS(O_CHKSUM)]) << 4) | + hexval(buffer[OFFS(O_CHKSUM)+1]); + if (recv_csum < 0) return CVT_FAIL|CVT_BADTIME; + if (((u_char) recv_csum) != calc_csum) return CVT_FAIL|CVT_BADTIME; + + clock->utcoffset = 0; + + /* What should flags be set to ? */ + clock->flags = PARSEB_UTC; + + /* if the current GPS fix is 9 (unknown), reject */ + if (0 > gpsfix || gpsfix > 9) clock->flags |= PARSEB_POWERUP; + + return CVT_OK; +} +#endif /* defined(PARSE) && defined(CLOCK_TRIMSV6) */ + +/* + * History: + * + * clk_trimble.c,v + * Revision 3.6 1993/10/30 09:44:45 kardel + * conditional compilation flag cleanup + * + * Revision 3.5 1993/10/09 15:01:35 kardel + * file structure unified + * + * revision 3.4 + * date: 1993/10/08 14:44:51; author: kardel; + * trimble - initial working version + * + * revision 3.3 + * date: 1993/10/03 19:10:50; author: kardel; + * restructured I/O handling + * + * revision 3.2 + * date: 1993/09/27 21:07:17; author: kardel; + * Trimble alpha integration + * + * revision 3.1 + * date: 1993/09/26 23:40:29; author: kardel; + * new parse driver logic + * + */ diff --git a/usr.sbin/xntpd/parse/empty.c b/usr.sbin/xntpd/parse/empty.c new file mode 100644 index 000000000000..91b777afe709 --- /dev/null +++ b/usr.sbin/xntpd/parse/empty.c @@ -0,0 +1,7 @@ +/* + * Well, some ranlibs, ar's or compilers react funny + * if asked to do nothing but build empty valid files + * I would have preferred to a no or at least a static + * symbol here... + */ +char * _____empty__ = "empty .o file"; diff --git a/usr.sbin/xntpd/parse/parse.c b/usr.sbin/xntpd/parse/parse.c new file mode 100644 index 000000000000..7a360021e167 --- /dev/null +++ b/usr.sbin/xntpd/parse/parse.c @@ -0,0 +1,1193 @@ +#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) +/* + * /src/NTP/REPOSITORY/v3/parse/parse.c,v 3.17 1993/11/11 11:20:29 kardel Exp + * + * parse.c,v 3.17 1993/11/11 11:20:29 kardel Exp + * + * Parser module for reference clock + * + * PARSEKERNEL define switches between two personalities of the module + * if PARSEKERNEL is defined this module can be used with dcf77sync.c as + * a PARSEKERNEL kernel module. In this case the time stamps will be + * a struct timeval. + * when PARSEKERNEL is not defined NTP time stamps will be used. + * + * Copyright (c) 1992,1993 + * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#if !(defined(lint) || defined(__GNUC__)) +static char rcsid[] = "parse.c,v 3.17 1993/11/11 11:20:29 kardel Exp"; +#endif + +#include "sys/types.h" +#include "sys/time.h" +#include "sys/errno.h" + +#include "ntp_fp.h" +#include "ntp_unixtime.h" +#include "ntp_calendar.h" + +#include "parse.h" + +#if defined(PARSESTREAM) && (defined(SYS_SUNOS4) || defined(SYS_SOLARIS)) && defined(STREAM) +/* + * Sorry, but in SunOS 4.x kernels there are no + * mem* operations. I don't want them - bcopy, bzero + * are fine in the kernel + */ +#define _ntp_string_h +extern void bcopy(); +extern void bzero(); +#endif + +#include "ntp_stdlib.h" + +#ifdef PARSESTREAM +#include "sys/parsestreams.h" +#endif + +extern clockformat_t *clockformats[]; +extern unsigned short nformats; + +static unsigned LONG timepacket(); + +/* + * strings support usually not in kernel - duplicated, but what the heck + */ +static int +Strlen(s) + register char *s; +{ + register int c; + + c = 0; + if (s) + { + while (*s++) + { + c++; + } + } + return c; +} + +static int +Strcmp(s, t) + register char *s; + register char *t; +{ + register int c = 0; + + if (!s || !t || (s == t)) + { + return 0; + } + + while (!(c = *s++ - *t++) && *s && *t) + /* empty loop */; + + return c; +} + +static int +timedout(parseio, ctime) + register parse_t *parseio; + register timestamp_t *ctime; +{ + struct timeval delta; + +#ifdef PARSEKERNEL + delta.tv_sec = ctime->tv.tv_sec - parseio->parse_lastchar.tv.tv_sec; + delta.tv_usec = ctime->tv.tv_usec - parseio->parse_lastchar.tv.tv_usec; + if (delta.tv_usec < 0) + { + delta.tv_sec -= 1; + delta.tv_usec += 1000000; + } +#else + extern LONG tstouslo[]; + extern LONG tstousmid[]; + extern LONG tstoushi[]; + + l_fp delt; + + delt = ctime->fp; + L_SUB(&delt, &parseio->parse_lastchar.fp); + TSTOTV(&delt, &delta); +#endif + + if (timercmp(&delta, &parseio->parse_timeout, >)) + { + parseprintf(DD_PARSE, ("parse: timedout: TRUE\n")); + return 1; + } + else + { + parseprintf(DD_PARSE, ("parse: timedout: FALSE\n")); + return 0; + } +} + +/* + * setup_bitmaps + */ +static int +setup_bitmaps(parseio, low, high) + register parse_t *parseio; + register unsigned short low; + register unsigned short high; +{ + register unsigned short i; + register int f = 0; + register clockformat_t *fmt; + register unsigned index, mask; + + if ((low >= high) || + (high > nformats)) + { + parseprintf(DD_PARSE, ("setup_bitmaps: failed: bounds error (low=%d, high=%d, nformats=%d)\n", low, high, nformats)); + return 0; + } + + bzero(parseio->parse_startsym, sizeof (parseio->parse_startsym)); + bzero(parseio->parse_endsym, sizeof (parseio->parse_endsym)); + bzero(parseio->parse_syncsym, sizeof (parseio->parse_syncsym)); + parseio->parse_syncflags = 0; + parseio->parse_timeout.tv_sec = 0; + parseio->parse_timeout.tv_usec = 0; + + /* + * gather bitmaps of possible start and end values + */ + for (i=low; i < high; i++) + { + fmt = clockformats[i]; + + if (fmt->flags & F_START) + { + index = fmt->startsym / 8; + mask = 1 << (fmt->startsym % 8); + + if (parseio->parse_endsym[index] & mask) + { +#ifdef PARSEKERNEL + printf("parse: setup_bitmaps: failed: START symbol collides with END symbol (format %d)\n", i); +#else + syslog(LOG_ERR, "parse: setup_bitmaps: failed: START symbol collides with END symbol (format %d)\n", i); +#endif + return 0; + } + else + { + parseio->parse_startsym[index] |= mask; + f = 1; + } + } + + if (fmt->flags & F_END) + { + index = fmt->endsym / 8; + mask = 1 << (fmt->endsym % 8); + + if (parseio->parse_startsym[index] & mask) + { +#ifdef PARSEKERNEL + printf("parse: setup_bitmaps: failed: END symbol collides with START symbol (format %d)\n", i); +#else + syslog(LOG_ERR, "parse: setup_bitmaps: failed: END symbol collides with START symbol (format %d)\n", i); +#endif + return 0; + } + else + { + parseio->parse_endsym[index] |= mask; + f = 1; + } + } + + if (fmt->flags & SYNC_CHAR) + { + parseio->parse_syncsym[fmt->syncsym / 8] |= (1 << (fmt->syncsym % 8)); + } + + parseio->parse_syncflags |= fmt->flags & (SYNC_START|SYNC_END|SYNC_CHAR|SYNC_ONE|SYNC_ZERO|SYNC_TIMEOUT|SYNC_SYNTHESIZE); + + if ((fmt->flags & SYNC_TIMEOUT) && + ((parseio->parse_timeout.tv_sec || parseio->parse_timeout.tv_usec) ? timercmp(&parseio->parse_timeout, &fmt->timeout, >) : 1)) + { + parseio->parse_timeout = fmt->timeout; + } + + if (parseio->parse_dsize < fmt->length) + parseio->parse_dsize = fmt->length; + } + + if (!f && ((int)(high - low) > 1)) + { + /* + * need at least one start or end symbol + */ +#ifdef PARSEKERNEL + printf("parse: setup_bitmaps: failed: neither START nor END symbol defined\n"); +#else + syslog(LOG_ERR, "parse: setup_bitmaps: failed: neither START nor END symbol defined\n"); +#endif + return 0; + } + + return 1; +} + +/*ARGSUSED*/ +int +parse_ioinit(parseio) + register parse_t *parseio; +{ + parseprintf(DD_PARSE, ("parse_iostart\n")); + + if (!setup_bitmaps(parseio, 0, nformats)) + return 0; + + parseio->parse_data = MALLOC(parseio->parse_dsize * 2 + 2); + if (!parseio->parse_data) + { + parseprintf(DD_PARSE, ("init failed: malloc for data area failed\n")); + return 0; + } + + /* + * leave room for '\0' + */ + parseio->parse_ldata = parseio->parse_data + parseio->parse_dsize + 1; + parseio->parse_lformat = 0; + parseio->parse_badformat = 0; + parseio->parse_ioflags = PARSE_IO_CS7; /* usual unix default */ + parseio->parse_flags = 0; /* true samples */ + parseio->parse_index = 0; + parseio->parse_ldsize = 0; + + return 1; +} + +/*ARGSUSED*/ +void +parse_ioend(parseio) + register parse_t *parseio; +{ + parseprintf(DD_PARSE, ("parse_ioend\n")); + if (parseio->parse_data) + FREE(parseio->parse_data, parseio->parse_dsize * 2 + 2); +} + +/*ARGSUSED*/ +int +parse_ioread(parseio, ch, ctime) + register parse_t *parseio; + register unsigned char ch; + register timestamp_t *ctime; +{ + register unsigned updated = CVT_NONE; + register unsigned short low, high; + register unsigned index, mask; + + parseprintf(DD_PARSE, ("parse_ioread(0x%x, char=0x%x, ..., ...)\n", (unsigned int)parseio, ch & 0xFF)); + + if (parseio->parse_flags & PARSE_FIXED_FMT) + { + if (!clockformats[parseio->parse_lformat]->convert) + { + parseprintf(DD_PARSE, ("parse_ioread: input dropped.\n")); + return CVT_NONE; + } + low = parseio->parse_lformat; + high = low + 1; + } + else + { + low = 0; + high = nformats; + } + + /* + * within STREAMS CSx (x < 8) chars still have the upper bits set + * so we normalize the characters by masking unecessary bits off. + */ + switch (parseio->parse_ioflags & PARSE_IO_CSIZE) + { + case PARSE_IO_CS5: + ch &= 0x1F; + break; + + case PARSE_IO_CS6: + ch &= 0x3F; + break; + + case PARSE_IO_CS7: + ch &= 0x7F; + break; + + case PARSE_IO_CS8: + break; + } + + index = ch / 8; + mask = 1 << (ch % 8); + + if ((parseio->parse_syncflags & SYNC_CHAR) && + (parseio->parse_syncsym[index] & mask)) + { + register clockformat_t *fmt; + register unsigned short i; + /* + * got a sync event - call sync routine + */ + + for (i = low; i < high; i++) + { + fmt = clockformats[i]; + + if ((fmt->flags & SYNC_CHAR) && + (fmt->syncsym == ch)) + { + parseprintf(DD_PARSE, ("parse_ioread: SYNC_CHAR event\n")); + if (fmt->syncevt) + fmt->syncevt(parseio, ctime, fmt->data, SYNC_CHAR); + } + } + } + + if ((((parseio->parse_syncflags & SYNC_START) && + (parseio->parse_startsym[index] & mask)) || + (parseio->parse_index == 0)) || + ((parseio->parse_syncflags & SYNC_TIMEOUT) && + timedout(parseio, ctime))) + { + register unsigned short i; + /* + * packet start - re-fill buffer + */ + if (parseio->parse_index) + { + /* + * filled buffer - thus not end character found + * do processing now + */ + parseio->parse_data[parseio->parse_index] = '\0'; + + updated = timepacket(parseio); + bcopy(parseio->parse_data, parseio->parse_ldata, parseio->parse_index+1); + parseio->parse_ldsize = parseio->parse_index+1; + if (parseio->parse_syncflags & SYNC_TIMEOUT) + parseio->parse_dtime.parse_stime = *ctime; + } + + /* + * could be a sync event - call sync routine if needed + */ + if (parseio->parse_syncflags & SYNC_START) + for (i = low; i < high; i++) + { + register clockformat_t *fmt = clockformats[i]; + + if ((parseio->parse_index == 0) || + ((fmt->flags & SYNC_START) && (fmt->startsym == ch))) + { + parseprintf(DD_PARSE, ("parse_ioread: SYNC_START event\n")); + if (fmt->syncevt) + fmt->syncevt(parseio, ctime, fmt->data, SYNC_START); + } + } + parseio->parse_index = 1; + parseio->parse_data[0] = ch; + parseprintf(DD_PARSE, ("parse: parse_ioread: buffer start\n")); + } + else + { + register unsigned short i; + + if (parseio->parse_index < parseio->parse_dsize) + { + /* + * collect into buffer + */ + parseprintf(DD_PARSE, ("parse: parse_ioread: buffer[%d] = 0x%x\n", parseio->parse_index, ch)); + parseio->parse_data[parseio->parse_index++] = ch; + } + + if ((parseio->parse_endsym[index] & mask) || + (parseio->parse_index >= parseio->parse_dsize)) + { + /* + * packet end - process buffer + */ + if (parseio->parse_syncflags & SYNC_END) + for (i = low; i < high; i++) + { + register clockformat_t *fmt = clockformats[i]; + + if ((fmt->flags & SYNC_END) && (fmt->endsym == ch)) + { + parseprintf(DD_PARSE, ("parse_ioread: SYNC_END event\n")); + if (fmt->syncevt) + fmt->syncevt(parseio, ctime, fmt->data, SYNC_END); + } + } + parseio->parse_data[parseio->parse_index] = '\0'; + updated = timepacket(parseio); + bcopy(parseio->parse_data, parseio->parse_ldata, parseio->parse_index+1); + parseio->parse_ldsize = parseio->parse_index+1; + parseio->parse_index = 0; + parseprintf(DD_PARSE, ("parse: parse_ioread: buffer end\n")); + } + } + + if ((updated == CVT_NONE) && + (parseio->parse_flags & PARSE_FIXED_FMT) && + (parseio->parse_syncflags & SYNC_SYNTHESIZE) && + ((parseio->parse_dtime.parse_status & CVT_MASK) == CVT_OK) && + clockformats[parseio->parse_lformat]->synth) + { + updated = clockformats[parseio->parse_lformat]->synth(parseio, ctime); + } + + /* + * remember last character time + */ + parseio->parse_lastchar = *ctime; + +#ifdef DEBUG + if ((updated & CVT_MASK) != CVT_NONE) + parseprintf(DD_PARSE, ("parse_ioread: time sample accumulated (status=0x%x)\n", updated)); +#endif + + parseio->parse_dtime.parse_status = updated; + + return (updated & CVT_MASK) != CVT_NONE; +} + +/* + * parse_iopps + * + * take status line indication and derive synchronisation information + * from it. + * It can also be used to decode a serial serial data format (such as the + * ONE, ZERO, MINUTE sync data stream from DCF77) + */ +/*ARGSUSED*/ +int +parse_iopps(parseio, status, ptime) + register parse_t *parseio; + register int status; + register timestamp_t *ptime; +{ + register unsigned updated = CVT_NONE; + + /* + * PPS pulse information will only be delivered to ONE clock format + * this is either the last successful conversion module with a ppssync + * routine, or a fixed format with a ppssync routine + */ + parseprintf(DD_PARSE, ("parse_iopps: STATUS %s\n", (status == SYNC_ONE) ? "ONE" : "ZERO")); + + if (((parseio->parse_flags & PARSE_FIXED_FMT) || + ((parseio->parse_dtime.parse_status & CVT_MASK) == CVT_OK)) && + clockformats[parseio->parse_lformat]->syncpps && + (status & clockformats[parseio->parse_lformat]->flags)) + { + updated = clockformats[parseio->parse_lformat]->syncpps(parseio, status == SYNC_ONE, ptime); + parseprintf(DD_PARSE, ("parse_iopps: updated = 0x%x\n", updated)); + } + else + { + parseprintf(DD_PARSE, ("parse_iopps: STATUS dropped\n")); + } + + return (updated & CVT_MASK) != CVT_NONE; +} + +/* + * parse_iodone + * + * clean up internal status for new round + */ +/*ARGSUSED*/ +void +parse_iodone(parseio) + register parse_t *parseio; +{ + /* + * we need to clean up certain flags for the next round + */ + parseio->parse_dtime.parse_state = 0; /* no problems with ISRs */ +} + +/*---------- conversion implementation --------------------*/ + +/* + * convert a struct clock to UTC since Jan, 1st 1970 0:00 (the UNIX EPOCH) + */ +#define dysize(x) ((x) % 4 ? 365 : ((x % 400) ? 365 :366)) + +time_t +parse_to_unixtime(clock, cvtrtc) + register clocktime_t *clock; + register unsigned LONG *cvtrtc; +{ +#define SETRTC(_X_) { if (cvtrtc) *cvtrtc = (_X_); } + static int days_of_month[] = + { + 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + register int i; + time_t t; + + if (clock->year < 100) + clock->year += 1900; + + if (clock->year < 1970) + clock->year += 100; /* XXX this will do it till <2070 */ + + if (clock->year < 0) + { + SETRTC(CVT_FAIL|CVT_BADDATE); + return -1; + } + + /* + * sorry, slow section here - but it's not time critical anyway + */ + t = (clock->year - 1970) * 365; + t += (clock->year >> 2) - (1970 >> 2); + t -= clock->year / 400 - 1970 / 400; + + /* month */ + if (clock->month <= 0 || clock->month > 12) + { + SETRTC(CVT_FAIL|CVT_BADDATE); + return -1; /* bad month */ + } + /* adjust leap year */ + if (clock->month >= 3 && dysize(clock->year) == 366) + t++; + + for (i = 1; i < clock->month; i++) + { + t += days_of_month[i]; + } + /* day */ + if (clock->day < 1 || ((clock->month == 2 && dysize(clock->year) == 366) ? + clock->day > 29 : clock->day > days_of_month[clock->month])) + { + SETRTC(CVT_FAIL|CVT_BADDATE); + return -1; /* bad day */ + } + + t += clock->day - 1; + /* hour */ + if (clock->hour < 0 || clock->hour >= 24) + { + SETRTC(CVT_FAIL|CVT_BADTIME); + return -1; /* bad hour */ + } + + t = TIMES24(t) + clock->hour; + + /* min */ + if (clock->minute < 0 || clock->minute > 59) + { + SETRTC(CVT_FAIL|CVT_BADTIME); + return -1; /* bad min */ + } + + t = TIMES60(t) + clock->minute; + /* sec */ + + if (clock->second < 0 || clock->second > 60) /* allow for LEAPs */ + { + SETRTC(CVT_FAIL|CVT_BADTIME); + return -1; /* bad sec */ + } + + t = TIMES60(t) + clock->second; + + t += clock->utcoffset; /* warp to UTC */ + + /* done */ + return t; +} + +/*--------------- format conversion -----------------------------------*/ + +int +Stoi(s, zp, cnt) + char *s; + LONG *zp; + int cnt; +{ + char *b = s; + int f,z,v; + char c; + + f=z=v=0; + + while(*s == ' ') + s++; + + if (*s == '-') + { + s++; + v = 1; + } + else + if (*s == '+') + s++; + + for(;;) + { + c = *s++; + if (c == '\0' || c < '0' || c > '9' || (cnt && ((s-b) > cnt))) + { + if (f == 0) + { + return(-1); + } + if (v) + z = -z; + *zp = z; + return(0); + } + z = (z << 3) + (z << 1) + ( c - '0' ); + f=1; + } +} + + +int +Strok(s, m) + char *s; + char *m; +{ + if (!s || !m) + return 0; + + while(*s && *m) + { + if ((*m == ' ') ? 1 : (*s == *m)) + { + s++; + m++; + } + else + { + return 0; + } + } + return !*m; +} + +unsigned LONG +updatetimeinfo(parseio, t, usec, flags) + register parse_t *parseio; + register time_t t; + register unsigned LONG usec; + register unsigned LONG flags; +{ + register LONG usecoff; + register LONG mean; + LONG delta[PARSE_DELTA]; + +#ifdef PARSEKERNEL + usecoff = (t - parseio->parse_dtime.parse_stime.tv.tv_sec) * 1000000 + - parseio->parse_dtime.parse_stime.tv.tv_usec + usec; +#else + extern LONG tstouslo[]; + extern LONG tstousmid[]; + extern LONG tstoushi[]; + + TSFTOTVU(parseio->parse_dtime.parse_stime.fp.l_uf, usecoff); + usecoff = -usecoff; + usecoff += (t - parseio->parse_dtime.parse_stime.fp.l_ui + JAN_1970) * 1000000 + + usec; +#endif + + /* + * filtering (median) if requested + */ + if (parseio->parse_flags & PARSE_STAT_FILTER) + { + register int n, i, s, k; + + parseio->parse_delta[parseio->parse_dindex] = usecoff; + + parseio->parse_dindex = (parseio->parse_dindex + 1) % PARSE_DELTA; + + /* + * sort always - thus every sample gets its data + */ + bcopy((caddr_t)parseio->parse_delta, (caddr_t)delta, sizeof(delta)); + + for (s = 0; s < PARSE_DELTA; s++) + for (k = s+1; k < PARSE_DELTA; k++) + { /* Yes - it's slow sort */ + if (delta[s] > delta[k]) + { + register LONG tmp; + + tmp = delta[k]; + delta[k] = delta[s]; + delta[s] = tmp; + } + } + + i = 0; + n = PARSE_DELTA; + + /* + * you know this median loop if you have read the other code + */ + while ((n - i) > 8) + { + register LONG top = delta[n-1]; + register LONG mid = delta[(n+i)>>1]; + register LONG low = delta[i]; + + if ((top - mid) > (mid - low)) + { + /* + * cut off high end + */ + n--; + } + else + { + /* + * cut off low end + */ + i++; + } + } + + parseio->parse_dtime.parse_usecdisp = delta[n-1] - delta[i]; + + if (parseio->parse_flags & PARSE_STAT_AVG) + { + /* + * take the average of the median samples as this clock + * is a little bumpy + */ + mean = 0; + + while (i < n) + { + mean += delta[i++]; + } + + mean >>= 3; + } + else + { + mean = delta[(n+i)>>1]; + } + + parseio->parse_dtime.parse_usecerror = mean; + } + else + { + parseio->parse_dtime.parse_usecerror = usecoff; + parseio->parse_dtime.parse_usecdisp = 0; + } + + parseprintf(DD_PARSE,("parse: updatetimeinfo: T=%x+%d usec, useccoff=%d, usecerror=%d, usecdisp=%d\n", + t, usec, usecoff, parseio->parse_dtime.parse_usecerror, parseio->parse_dtime.parse_usecdisp)); + + +#ifdef PARSEKERNEL + { + int s = splhigh(); +#endif + + parseio->parse_lstate = parseio->parse_dtime.parse_state | flags | PARSEB_TIMECODE; + + parseio->parse_dtime.parse_state = parseio->parse_lstate; + +#ifdef PARSEKERNEL + (void)splx(s); + } +#endif + + return CVT_OK; /* everything fine and dandy... */ +} + + +/* + * syn_simple + * + * handle a sync time stamp + */ +/*ARGSUSED*/ +void +syn_simple(parseio, ts, format, why) + register parse_t *parseio; + register timestamp_t *ts; + register struct format *format; + register unsigned LONG why; +{ + parseio->parse_dtime.parse_stime = *ts; +} + +/* + * pps_simple + * + * handle a pps time stamp + */ +/*ARGSUSED*/ +unsigned LONG +pps_simple(parseio, status, ptime) + register parse_t *parseio; + register int status; + register timestamp_t *ptime; +{ + parseio->parse_dtime.parse_ptime = *ptime; + parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS; + + return CVT_NONE; +} + +/* + * timepacket + * + * process a data packet + */ +static unsigned LONG +timepacket(parseio) + register parse_t *parseio; +{ + register int k; + register unsigned short format; + register time_t t; + register unsigned LONG cvtsum = 0;/* accumulated CVT_FAIL errors */ + unsigned LONG cvtrtc; /* current conversion result */ + clocktime_t clock; + + format = parseio->parse_lformat; + + k = 0; + + if (parseio->parse_flags & PARSE_FIXED_FMT) + { + switch ((cvtrtc = clockformats[format]->convert ? clockformats[format]->convert(parseio->parse_data, parseio->parse_index, clockformats[format]->data, &clock) : CVT_NONE) & CVT_MASK) + { + case CVT_FAIL: + parseio->parse_badformat++; + cvtsum = cvtrtc & ~CVT_MASK; + + /* + * may be too often ... but is nice to know when it happens + */ +#ifdef PARSEKERNEL + printf("parse: \"%s\" failed to convert\n", clockformats[format]->name); +#else + syslog(LOG_WARNING, "parse: \"%s\" failed to convert\n", clockformats[format]->name); +#endif + break; + + case CVT_NONE: + /* + * too bad - pretend bad format + */ + parseio->parse_badformat++; + cvtsum = CVT_BADFMT; + + break; + + case CVT_OK: + k = 1; + break; + + default: + /* shouldn't happen */ +#ifdef PARSEKERNEL + printf("parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name); +#else + syslog(LOG_WARNING, "parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name); +#endif + return CVT_FAIL|cvtrtc; + } + } + else + { + /* + * find correct conversion routine + * and convert time packet + * RR search starting at last successful conversion routine + */ + + if (nformats) /* very careful ... */ + { + do + { + switch ((cvtrtc = (clockformats[format]->convert && !(clockformats[format]->flags & CVT_FIXEDONLY)) ? + clockformats[format]->convert(parseio->parse_data, parseio->parse_index, clockformats[format]->data, &clock) : + CVT_NONE) & CVT_MASK) + { + case CVT_FAIL: + parseio->parse_badformat++; + cvtsum |= cvtrtc & ~CVT_MASK; + + /* + * may be too often ... but is nice to know when it happens + */ +#ifdef PARSEKERNEL + printf("parse: \"%s\" failed to convert\n", clockformats[format]->name); +#else + syslog(LOG_WARNING, "parse: \"%s\" failed to convert\n", clockformats[format]->name); +#endif + /*FALLTHROUGH*/ + case CVT_NONE: + format++; + break; + + case CVT_OK: + k = 1; + break; + + default: + /* shouldn't happen */ +#ifdef PARSEKERNEL + printf("parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name); +#else + syslog(LOG_WARNING, "parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name); +#endif + return CVT_BADFMT; + } + if (format >= nformats) + format = 0; + } + while (!k && (format != parseio->parse_lformat)); + } + } + + if (!k) + { +#ifdef PARSEKERNEL + printf("parse: time format \"%s\" not convertable\n", parseio->parse_data); +#else + syslog(LOG_WARNING, "parse: time format \"%s\" not convertable\n", parseio->parse_data); +#endif + return CVT_FAIL|cvtsum; + } + + if ((t = parse_to_unixtime(&clock, &cvtrtc)) == -1) + { +#ifdef PARSEKERNEL + printf("parse: bad time format \"%s\"\n", parseio->parse_data); +#else + syslog(LOG_WARNING,"parse: bad time format \"%s\"\n", parseio->parse_data); +#endif + return CVT_FAIL|cvtrtc; + } + + parseio->parse_lformat = format; + + /* + * time stamp + */ +#ifdef PARSEKERNEL + parseio->parse_dtime.parse_time.tv.tv_sec = t; + parseio->parse_dtime.parse_time.tv.tv_usec = clock.usecond; +#else + parseio->parse_dtime.parse_time.fp.l_ui = t + JAN_1970; + TVUTOTSF(clock.usecond, parseio->parse_dtime.parse_time.fp.l_uf); +#endif + + parseio->parse_dtime.parse_format = format; + + return updatetimeinfo(parseio, t, clock.usecond, clock.flags); +} + + +/* + * control operations + */ +/*ARGSUSED*/ +int +parse_getstat(dct, parse) + parsectl_t *dct; + parse_t *parse; +{ + dct->parsestatus.flags = parse->parse_flags & PARSE_STAT_FLAGS; + return 1; +} + + +/*ARGSUSED*/ +int +parse_setstat(dct, parse) + parsectl_t *dct; + parse_t *parse; +{ + parse->parse_flags = (parse->parse_flags & ~PARSE_STAT_FLAGS) | dct->parsestatus.flags; + return 1; +} + + +/*ARGSUSED*/ +int +parse_timecode(dct, parse) + parsectl_t *dct; + parse_t *parse; +{ + dct->parsegettc.parse_state = parse->parse_lstate; + dct->parsegettc.parse_format = parse->parse_lformat; + /* + * move out current bad packet count + * user program is expected to sum these up + * this is not a problem, as "parse" module are + * exclusive open only + */ + dct->parsegettc.parse_badformat = parse->parse_badformat; + parse->parse_badformat = 0; + + if (parse->parse_ldsize <= PARSE_TCMAX) + { + dct->parsegettc.parse_count = parse->parse_ldsize; + bcopy(parse->parse_ldata, dct->parsegettc.parse_buffer, dct->parsegettc.parse_count); + return 1; + } + else + { + return 0; + } +} + + +/*ARGSUSED*/ +int +parse_setfmt(dct, parse) + parsectl_t *dct; + parse_t *parse; +{ + if (dct->parseformat.parse_count <= PARSE_TCMAX) + { + if (dct->parseformat.parse_count) + { + register unsigned short i; + + for (i = 0; i < nformats; i++) + { + if (!Strcmp(dct->parseformat.parse_buffer, clockformats[i]->name)) + { + parse->parse_lformat = i; + parse->parse_flags |= PARSE_FIXED_FMT; /* set fixed format indication */ + return setup_bitmaps(parse, i, i+1); + } + } + + return 0; + } + else + { + parse->parse_flags &= ~PARSE_FIXED_FMT; /* clear fixed format indication */ + return setup_bitmaps(parse, 0, nformats); + } + } + else + { + return 0; + } +} + +/*ARGSUSED*/ +int +parse_getfmt(dct, parse) + parsectl_t *dct; + parse_t *parse; +{ + if (dct->parseformat.parse_format < nformats && + Strlen(clockformats[dct->parseformat.parse_format]->name) <= PARSE_TCMAX) + { + dct->parseformat.parse_count = Strlen(clockformats[dct->parseformat.parse_format]->name)+1; + bcopy(clockformats[dct->parseformat.parse_format]->name, dct->parseformat.parse_buffer, dct->parseformat.parse_count); + return 1; + } + else + { + return 0; + } +} + +/*ARGSUSED*/ +int +parse_setcs(dct, parse) + parsectl_t *dct; + parse_t *parse; +{ + parse->parse_ioflags &= ~PARSE_IO_CSIZE; + parse->parse_ioflags |= dct->parsesetcs.parse_cs & PARSE_IO_CSIZE; + return 1; +} + +#endif /* defined(REFCLOCK) && defined(PARSE) */ + +/* + * History: + * + * parse.c,v + * Revision 3.17 1993/11/11 11:20:29 kardel + * declaration fixes + * + * Revision 3.16 1993/11/06 22:26:07 duwe + * Linux cleanup after config change + * + * Revision 3.15 1993/11/04 11:14:18 kardel + * ansi/K&R traps + * + * Revision 3.14 1993/11/04 10:03:28 kardel + * disarmed ansiism + * + * Revision 3.13 1993/11/01 20:14:13 kardel + * useless comparision removed + * + * Revision 3.12 1993/11/01 20:00:22 kardel + * parse Solaris support (initial version) + * + * Revision 3.11 1993/10/30 09:41:25 kardel + * minor optimizations + * + * Revision 3.10 1993/10/22 14:27:51 kardel + * Oct. 22nd 1993 reconcilation + * + * Revision 3.9 1993/10/05 23:15:09 kardel + * more STREAM protection + * + * Revision 3.8 1993/09/27 21:08:00 kardel + * utcoffset now in seconds + * + * Revision 3.7 1993/09/26 23:40:16 kardel + * new parse driver logic + * + * Revision 3.6 1993/09/07 10:12:46 kardel + * September 7th reconcilation - 3.2 (alpha) + * + * Revision 3.5 1993/09/01 21:44:48 kardel + * conditional cleanup + * + * Revision 3.4 1993/08/27 00:29:39 kardel + * compilation cleanup + * + * Revision 3.3 1993/08/24 22:27:13 kardel + * cleaned up AUTOCONF DCF77 mess 8-) - wasn't too bad + * + * Revision 3.2 1993/07/09 11:37:11 kardel + * Initial restructured version + GPS support + * + * Revision 3.1 1993/07/06 10:00:08 kardel + * DCF77 driver goes generic... + * + */ diff --git a/usr.sbin/xntpd/parse/parse_conf.c b/usr.sbin/xntpd/parse/parse_conf.c new file mode 100644 index 000000000000..78ddb3a1c9dd --- /dev/null +++ b/usr.sbin/xntpd/parse/parse_conf.c @@ -0,0 +1,117 @@ +#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) +/* + * /src/NTP/REPOSITORY/v3/parse/parse_conf.c,v 3.11 1993/11/01 20:00:24 kardel Exp + * + * parse_conf.c,v 3.11 1993/11/01 20:00:24 kardel Exp + * + * Parser configuration module for reference clocks + * + * STREAM define switches between two personalities of the module + * if STREAM is defined this module can be used with dcf77sync.c as + * a STREAMS kernel module. In this case the time stamps will be + * a struct timeval. + * when STREAM is not defined NTP time stamps will be used. + * + * Copyright (c) 1992,1993 + * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#include "sys/types.h" +#include "sys/time.h" +#include "sys/errno.h" +#include "ntp_fp.h" +#include "ntp_unixtime.h" +#include "ntp_calendar.h" + +#include "parse.h" + +#ifdef CLOCK_SCHMID +extern clockformat_t clock_schmid; +#endif + +#ifdef CLOCK_DCF7000 +extern clockformat_t clock_dcf7000; +#endif + +#ifdef CLOCK_MEINBERG +extern clockformat_t clock_meinberg[]; +#endif + +#ifdef CLOCK_RAWDCF +extern clockformat_t clock_rawdcf; +#endif + +#ifdef CLOCK_TRIMSV6 +extern clockformat_t clock_trimsv6; +#endif + +/* + * format definitions + */ +clockformat_t *clockformats[] = +{ +#ifdef CLOCK_MEINBERG + &clock_meinberg[0], + &clock_meinberg[1], + &clock_meinberg[2], +#endif +#ifdef CLOCK_DCF7000 + &clock_dcf7000, +#endif +#ifdef CLOCK_SCHMID + &clock_schmid, +#endif +#ifdef CLOCK_RAWDCF + &clock_rawdcf, +#endif +#ifdef CLOCK_TRIMSV6 + &clock_trimsv6, +#endif +0}; + +unsigned short nformats = sizeof(clockformats) / sizeof(clockformats[0]) - 1; +#endif /* REFCLOCK PARSE */ + +/* + * History: + * + * parse_conf.c,v + * Revision 3.11 1993/11/01 20:00:24 kardel + * parse Solaris support (initial version) + * + * Revision 3.10 1993/10/09 15:01:37 kardel + * file structure unified + * + * Revision 3.9 1993/09/26 23:40:19 kardel + * new parse driver logic + * + * Revision 3.8 1993/09/02 23:20:57 kardel + * dragon extiction + * + * Revision 3.7 1993/09/01 21:44:52 kardel + * conditional cleanup + * + * Revision 3.6 1993/09/01 11:25:09 kardel + * patch accident 8-( + * + * Revision 3.5 1993/08/31 22:31:14 kardel + * SINIX-M SysVR4 integration + * + * Revision 3.4 1993/08/27 00:29:42 kardel + * compilation cleanup + * + * Revision 3.3 1993/07/14 09:04:45 kardel + * only when REFCLOCK && PARSE is defined + * + * Revision 3.2 1993/07/09 11:37:13 kardel + * Initial restructured version + GPS support + * + * Revision 3.1 1993/07/06 10:00:11 kardel + * DCF77 driver goes generic... + * + */ diff --git a/usr.sbin/xntpd/parse/parsesolaris.c b/usr.sbin/xntpd/parse/parsesolaris.c new file mode 100644 index 000000000000..0da27e7a087a --- /dev/null +++ b/usr.sbin/xntpd/parse/parsesolaris.c @@ -0,0 +1,1170 @@ +/* + * /src/NTP/REPOSITORY/v3/parse/parsesolaris.c,v 3.4 1993/11/13 11:13:17 kardel Exp + * + * parsesolaris.c,v 3.4 1993/11/13 11:13:17 kardel Exp + * + * STREAMS module for reference clocks + * (SunOS5.x - not fully tested - buyer beware ! - OS KILLERS may still be + * lurking in the code!) + * + * Copyright (c) 1993 + * derived work from parsestreams.c ((c) 1991-1993, Frank Kardel) and + * dcf77sync.c((c) Frank Kardel) + * Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef lint +static char rcsid[] = "parsesolaris.c,v 3.4 1993/11/13 11:13:17 kardel Exp"; +#endif + +/* + * Well, the man spec says we have to do this junk - the + * header files tell a different story (i like that one more) + */ +#define SAFE_WR(q) (((q)->q_flag & QREADR) ? WR((q)) : (q)) +#define SAFE_RD(q) (((q)->q_flag & QREADR) ? (q) : RD((q))) + +/* + * needed to cope with Solaris 2.3 header file chaos + */ +#include <sys/types.h> +/* + * the Solaris 2.2 include list + */ +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/termios.h> +#include <sys/stream.h> +#include <sys/strtty.h> +#include <sys/stropts.h> +#include <sys/modctl.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/cpu.h> + +#define STREAM /* that's what we are here for */ + +#define HAVE_NO_NICE /* for the NTP headerfiles */ +#include "ntp_fp.h" +#include "parse.h" +#include "sys/parsestreams.h" + +static unsigned int parsebusy = 0; + +/*--------------- loadable driver section -----------------------------*/ + +static struct streamtab parseinfo; + +static struct fmodsw fmod_templ = +{ + "parse", /* module name */ + &parseinfo, /* module information */ + 0, /* not clean yet */ + /* lock ptr */ +}; + +extern struct mod_ops mod_strmodops; + +static struct modlstrmod modlstrmod = +{ + &mod_strmodops, /* a STREAMS module */ + "PARSE - NTP reference", /* name this baby - keep room for revision number */ + &fmod_templ +}; + +static struct modlinkage modlinkage = +{ + MODREV_1, + &modlstrmod, + NULL +}; + +/* + * strings support usually not in kernel + */ +static int Strlen(s) + register char *s; +{ + register int c; + + c = 0; + if (s) + { + while (*s++) + { + c++; + } + } + return c; +} + +static void Strncpy(t, s, c) + register char *t; + register char *s; + register int c; +{ + if (s && t) + { + while ((c-- > 0) && (*t++ = *s++)) + ; + } +} + +int Strcmp(s, t) + register char *s; + register char *t; +{ + register int c = 0; + + if (!s || !t || (s == t)) + { + return 0; + } + + while (!(c = *s++ - *t++) && *s && *t) + /* empty loop */; + + return c; +} + +/* + * module management routines + */ +/*ARGSUSED*/ +int _init(void) +{ + static char revision[] = "3.4"; + char *s, *S, *t; + + /* + * copy RCS revision into Drv_name + * + * are we forcing RCS here to do things it was not built for ? + */ + s = revision; + if (*s == '$') + { + /* + * skip "$Revision: " + * if present. - not necessary on a -kv co (cvs export) + */ + while (*s && (*s != ' ')) + { + s++; + } + if (*s == ' ') s++; + } + + t = modlstrmod.strmod_linkinfo; + while (*t && (*t != ' ')) + { + t++; + } + if (*t == ' ') t++; + + S = s; + while (*S && (((*S >= '0') && (*S <= '9')) || (*S == '.'))) + { + S++; + } + + if (*s && *t && (S > s)) + { + if (Strlen(t) >= (S - s)) + { + (void) Strncpy(t, s, S - s); + } + } + return (mod_install(&modlinkage)); +} + +/*ARGSUSED*/ +int _info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +/*ARGSUSED*/ +int _fini(void) +{ + if (parsebusy > 0) + { + printf("_fini[%s]: STREAMS module has still %d instances active.\n", modlstrmod.strmod_linkinfo, parsebusy); + return (EBUSY); + } + else + return (mod_remove(&modlinkage)); +} + +/*--------------- stream module definition ----------------------------*/ + +static int parseopen(), parseclose(), parsewput(), parserput(), parsersvc(); + +static struct module_info driverinfo = +{ + 0, /* module ID number */ + fmod_templ.f_name, /* module name - why repeated here ? compat ?*/ + 0, /* minimum accepted packet size */ + INFPSZ, /* maximum accepted packet size */ + 1, /* high water mark - flow control */ + 0 /* low water mark - flow control */ +}; + +static struct qinit rinit = /* read queue definition */ +{ + parserput, /* put procedure */ + parsersvc, /* service procedure */ + parseopen, /* open procedure */ + parseclose, /* close procedure */ + NULL, /* admin procedure - NOT USED FOR NOW */ + &driverinfo, /* information structure */ + NULL /* statistics */ +}; + +static struct qinit winit = /* write queue definition */ +{ + parsewput, /* put procedure */ + NULL, /* service procedure */ + NULL, /* open procedure */ + NULL, /* close procedure */ + NULL, /* admin procedure - NOT USED FOR NOW */ + &driverinfo, /* information structure */ + NULL /* statistics */ +}; + +static struct streamtab parseinfo = /* stream info element for parse driver */ +{ + &rinit, /* read queue */ + &winit, /* write queue */ + NULL, /* read mux */ + NULL /* write mux */ +}; + +/*--------------- driver data structures ----------------------------*/ + +/* + * we usually have an inverted signal - but you + * can change this to suit your needs + */ +int cd_invert = 1; /* invert status of CD line - PPS support via CD input */ + +int parsedebug = ~0; + +extern void uniqtime(); + +/*--------------- module implementation -----------------------------*/ + +#define TIMEVAL_USADD(_X_, _US_) do {\ + (_X_)->tv_usec += (_US_);\ + if ((_X_)->tv_usec >= 1000000)\ + {\ + (_X_)->tv_sec++;\ + (_X_)->tv_usec -= 1000000;\ + }\ + } while (0) + +#if defined(sun4c) && defined(DEBUG_CD) +#include <sun4c/cpu.h> +#include <sun4c/auxio.h> +#define SET_LED(_X_) (((cpu & CPU_ARCH) == SUN4C_ARCH) ? *(u_char *)AUXIO_REG = AUX_MBO|AUX_EJECT|((_X_)?AUX_LED:0) : 0) +#else +#define SET_LED(_X_) +#endif + +static int init_linemon(); +static void close_linemon(); + +/* + * keep here MACHINE AND OS AND ENVIRONMENT DEPENDENT + * timing constants + * + * FOR ABSOLUTE PRECISION YOU NEED TO MEASURE THE TIMING + * SKEW BETWEEN THE HW-PPS SIGNAL AND KERNEL uniqtime() + * YOURSELF. + * + * YOU MUST BE QUALIFIED APPROPRIATELY FOR THESE TYPE + * OF HW MANIPULATION ! + * + * you need an oscilloscope and the permission for HW work + * in order to figure out these timing constants/variables + */ + +static unsigned long xsdelay = 10; /* assume an SS2 */ +static unsigned long stdelay = 350; + +struct delays +{ + unsigned char mask; /* what to check for */ + unsigned char type; /* what to match */ + unsigned long xsdelay; /* external status direct delay in us */ + unsigned long stdelay; /* STREAMS message delay (M_[UN]HANGUP) */ +} isr_delays[] = +{ + /* + * WARNING: must still be measured - currently taken from Craig Leres ppsdev + */ +#ifdef sun4c + {CPU_ARCH|CPU_MACH, CPU_SUN4C_50, 10, 350}, + {CPU_ARCH|CPU_MACH, CPU_SUN4C_65, 15, 700}, + {CPU_ARCH|CPU_MACH, CPU_SUN4C_75, 10, 350}, +#endif +#ifdef sun4m + {CPU_ARCH|CPU_MACH, CPU_SUN4M_50, 8, 250}, + {CPU_ARCH|CPU_MACH, CPU_SUN4M_690, 8, 250}, +#endif + {0,} +}; + +void setup_delays() +{ + register int i; + + if (cputype & OBP_ARCH) + { + printf("parse: WARNING: PPS kernel fudge factors no yet determinable (no dev tree walk yet) - assuming SS2 (Sun4/75)\n", cputype); + return; + } + + for (i = 0; isr_delays[i].mask; i++) + { + if ((cputype & isr_delays[i].mask) == isr_delays[i].type) + { + xsdelay = isr_delays[i].xsdelay; + stdelay = isr_delays[i].stdelay; + return; + } + } + printf("parse: WARNING: PPS kernel fudge factors unknown for this machine (Type 0x%x) - assuming SS2 (Sun4/75)\n", cputype); +} + +#define M_PARSE 0x0001 +#define M_NOPARSE 0x0002 + +static int +setup_stream(queue_t *q, int mode) +{ + register mblk_t *mp; + + parseprintf(DD_OPEN,("parse: SETUP_STREAM - setting up stream for q=%x\n", q)); + + mp = allocb(sizeof(struct stroptions), BPRI_MED); + if (mp) + { + struct stroptions *str = (struct stroptions *)mp->b_wptr; + + str->so_flags = SO_READOPT|SO_HIWAT|SO_LOWAT; + str->so_readopt = (mode == M_PARSE) ? RMSGD : RNORM; + str->so_hiwat = (mode == M_PARSE) ? sizeof(parsetime_t) : 256; + str->so_lowat = 0; + mp->b_datap->db_type = M_SETOPTS; + mp->b_wptr += sizeof(struct stroptions); + if (!q) + panic("NULL q - strange"); + putnext(q, mp); + return putctl1(SAFE_WR(q)->q_next, M_CTL, (mode == M_PARSE) ? MC_SERVICEIMM : + MC_SERVICEDEF); + } + else + { + parseprintf(DD_OPEN,("parse: setup_stream - FAILED - no MEMORY for allocb\n")); + return 0; + } +} + +/*ARGSUSED*/ +static int parseopen(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *credp) +{ + register mblk_t *mp; + register parsestream_t *parse; + static int notice = 0; + + parseprintf(DD_OPEN,("parse: OPEN - q=%x\n", q)); + + if (sflag != MODOPEN) + { /* open only for modules */ + parseprintf(DD_OPEN,("parse: OPEN - FAILED - not MODOPEN\n")); + return EIO; + } + + if (q->q_ptr != (caddr_t)NULL) + { + parseprintf(DD_OPEN,("parse: OPEN - FAILED - EXCLUSIVE ONLY\n")); + return EBUSY; + } + + parsebusy++; + + q->q_ptr = (caddr_t)kmem_alloc(sizeof(parsestream_t), KM_SLEEP); + parseprintf(DD_OPEN,("parse: OPEN - parse area q=%x, q->q_ptr=%x\n", q, q->q_ptr)); + SAFE_WR(q)->q_ptr = q->q_ptr; + parseprintf(DD_OPEN,("parse: OPEN - WQ parse area q=%x, q->q_ptr=%x\n", SAFE_WR(q), SAFE_WR(q)->q_ptr)); + + parse = (parsestream_t *) q->q_ptr; + bzero((caddr_t)parse, sizeof(*parse)); + parse->parse_queue = q; + parse->parse_status = PARSE_ENABLE; + parse->parse_ppsclockev.tv.tv_sec = 0; + parse->parse_ppsclockev.tv.tv_usec = 0; + parse->parse_ppsclockev.serial = 0; + + parseprintf(DD_OPEN,("parse: OPEN - initializing io subsystem q=%x\n", q)); + + if (!parse_ioinit(&parse->parse_io)) + { + /* + * ok guys - beat it + */ + kmem_free((caddr_t)parse, sizeof(parsestream_t)); + + parsebusy--; + + return EIO; + } + + parseprintf(DD_OPEN,("parse: OPEN - initializing stream q=%x\n", q)); + + if (setup_stream(q, M_PARSE)) + { + (void) init_linemon(q); /* hook up PPS ISR routines if possible */ + setup_delays(); + parseprintf(DD_OPEN,("parse: OPEN - SUCCEEDED\n")); + + /* + * I know that you know the delete key, but you didn't write this + * code, did you ? - So, keep the message in here. + */ + if (!notice) + { + printf("%s: Copyright (c) 1991-1993, Frank Kardel\n", modlstrmod.strmod_linkinfo); + notice = 1; + } + + return 0; + } + else + { + parsebusy--; + return EIO; + } +} + +/*ARGSUSED*/ +static int parseclose(queue_t *q, int flags) +{ + register parsestream_t *parse = (parsestream_t *)q->q_ptr; + register unsigned long s; + + parseprintf(DD_CLOSE,("parse: CLOSE\n")); + + s = splhigh(); + + if (parse->parse_dqueue) + close_linemon(parse->parse_dqueue, q); + parse->parse_dqueue = (queue_t *)0; + + (void) splx(s); + + parse_ioend(&parse->parse_io); + + kmem_free((caddr_t)parse, sizeof(parsestream_t)); + + q->q_ptr = (caddr_t)NULL; + SAFE_WR(q)->q_ptr = (caddr_t)NULL; + + parsebusy--; +} + +/* + * move unrecognized stuff upward + */ +static parsersvc(queue_t *q) +{ + mblk_t *mp; + + while (mp = getq(q)) + { + if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL)) + { + putnext(q, mp); + parseprintf(DD_RSVC,("parse: RSVC - putnext\n")); + } + else + { + putbq(q, mp); + parseprintf(DD_RSVC,("parse: RSVC - flow control wait\n")); + break; + } + } +} + +/* + * do ioctls and + * send stuff down - dont care about + * flow control + */ +static int parsewput(queue_t *q, mblk_t *mp) +{ + register int ok = 1; + register mblk_t *datap; + register struct iocblk *iocp; + parsestream_t *parse = (parsestream_t *)q->q_ptr; + + parseprintf(DD_WPUT,("parse: parsewput\n")); + + switch (mp->b_datap->db_type) + { + default: + putnext(q, mp); + break; + + case M_IOCTL: + iocp = (struct iocblk *)mp->b_rptr; + switch (iocp->ioc_cmd) + { + default: + parseprintf(DD_WPUT,("parse: parsewput - forward M_IOCTL\n")); + putnext(q, mp); + break; + + case CIOGETEV: + /* + * taken from Craig Leres ppsclock module (and modified) + */ + datap = allocb(sizeof(struct ppsclockev), BPRI_MED); + if (datap == NULL || mp->b_cont) + { + mp->b_datap->db_type = M_IOCNAK; + iocp->ioc_error = (datap == NULL) ? ENOMEM : EINVAL; + if (datap != NULL) + freeb(datap); + qreply(q, mp); + break; + } + + mp->b_cont = datap; + *(struct ppsclockev *)datap->b_wptr = parse->parse_ppsclockev; + datap->b_wptr += + sizeof(struct ppsclockev) / sizeof(*datap->b_wptr); + mp->b_datap->db_type = M_IOCACK; + iocp->ioc_count = sizeof(struct ppsclockev); + qreply(q, mp); + break; + + case PARSEIOC_ENABLE: + case PARSEIOC_DISABLE: + { + parse->parse_status = (parse->parse_status & ~PARSE_ENABLE) | + (iocp->ioc_cmd == PARSEIOC_ENABLE) ? + PARSE_ENABLE : 0; + if (!setup_stream(SAFE_RD(q), (parse->parse_status & PARSE_ENABLE) ? + M_PARSE : M_NOPARSE)) + { + mp->b_datap->db_type = M_IOCNAK; + } + else + { + mp->b_datap->db_type = M_IOCACK; + } + qreply(q, mp); + break; + } + + case PARSEIOC_SETSTAT: + case PARSEIOC_GETSTAT: + case PARSEIOC_TIMECODE: + case PARSEIOC_SETFMT: + case PARSEIOC_GETFMT: + case PARSEIOC_SETCS: + if (iocp->ioc_count == sizeof(parsectl_t)) + { + parsectl_t *dct = (parsectl_t *)mp->b_cont->b_rptr; + + switch (iocp->ioc_cmd) + { + case PARSEIOC_GETSTAT: + parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETSTAT\n")); + ok = parse_getstat(dct, &parse->parse_io); + break; + + case PARSEIOC_SETSTAT: + parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETSTAT\n")); + ok = parse_setstat(dct, &parse->parse_io); + break; + + case PARSEIOC_TIMECODE: + parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_TIMECODE\n")); + ok = parse_timecode(dct, &parse->parse_io); + break; + + case PARSEIOC_SETFMT: + parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETFMT\n")); + ok = parse_setfmt(dct, &parse->parse_io); + break; + + case PARSEIOC_GETFMT: + parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETFMT\n")); + ok = parse_getfmt(dct, &parse->parse_io); + break; + + case PARSEIOC_SETCS: + parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETCS\n")); + ok = parse_setcs(dct, &parse->parse_io); + break; + } + mp->b_datap->db_type = ok ? M_IOCACK : M_IOCNAK; + } + else + { + mp->b_datap->db_type = M_IOCNAK; + } + parseprintf(DD_WPUT,("parse: parsewput qreply - %s\n", (mp->b_datap->db_type == M_IOCNAK) ? "M_IOCNAK" : "M_IOCACK")); + qreply(q, mp); + break; + } + } +} + +/* + * read characters from streams buffers + */ +static unsigned long rdchar(mblk_t **mp) +{ + while (*mp != (mblk_t *)NULL) + { + if ((*mp)->b_wptr - (*mp)->b_rptr) + { + return (unsigned long)(*(unsigned char *)((*mp)->b_rptr++)); + } + else + { + register mblk_t *mmp = *mp; + + *mp = (*mp)->b_cont; + freeb(mmp); + } + } + return ~0; +} + +/* + * convert incoming data + */ +static int parserput(queue_t *q, mblk_t *imp) +{ + register unsigned char type; + mblk_t *mp = imp; + + switch (type = mp->b_datap->db_type) + { + default: + /* + * anything we don't know will be put on queue + * the service routine will move it to the next one + */ + parseprintf(DD_RPUT,("parse: parserput - forward type 0x%x\n", type)); + if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL)) + { + putnext(q, mp); + } + else + putq(q, mp); + break; + + case M_BREAK: + case M_DATA: + { + register parsestream_t * parse = (parsestream_t *)q->q_ptr; + register mblk_t *nmp; + register unsigned long ch; + timestamp_t ctime; + + /* + * get time on packet delivery + */ + uniqtime(&ctime.tv); + + if (!(parse->parse_status & PARSE_ENABLE)) + { + parseprintf(DD_RPUT,("parse: parserput - parser disabled - forward type 0x%x\n", type)); + if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL)) + { + putnext(q, mp); + } + else + putq(q, mp); + } + else + { + parseprintf(DD_RPUT,("parse: parserput - M_%s\n", (type == M_DATA) ? "DATA" : "BREAK")); + + if (type == M_DATA) + { + /* + * parse packet looking for start an end characters + */ + while (mp != (mblk_t *)NULL) + { + ch = rdchar(&mp); + if (ch != ~0 && parse_ioread(&parse->parse_io, (char)ch, &ctime)) + { + /* + * up up and away (hopefully ...) + * don't press it if resources are tight or nobody wants it + */ + nmp = (mblk_t *)NULL; + if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED))) + { + bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t)); + nmp->b_wptr += sizeof(parsetime_t); + putnext(parse->parse_queue, nmp); + } + else + if (nmp) freemsg(nmp); + parse_iodone(&parse->parse_io); + } + } + } + else + { + if (parse_ioread(&parse->parse_io, (char)0, &ctime)) + { + /* + * up up and away (hopefully ...) + * don't press it if resources are tight or nobody wants it + */ + nmp = (mblk_t *)NULL; + if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED))) + { + bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t)); + nmp->b_wptr += sizeof(parsetime_t); + putnext(parse->parse_queue, nmp); + } + else + if (nmp) freemsg(nmp); + parse_iodone(&parse->parse_io); + } + freemsg(mp); + } + break; + } + } + + /* + * CD PPS support for non direct ISR hack + */ + case M_HANGUP: + case M_UNHANGUP: + { + register parsestream_t * parse = (parsestream_t *)q->q_ptr; + timestamp_t ctime; + register mblk_t *nmp; + register int status = cd_invert ^ (type == M_HANGUP); + + SET_LED(status); + + uniqtime(&ctime.tv); + + TIMEVAL_USADD(&ctime.tv, stdelay); + + parseprintf(DD_RPUT,("parse: parserput - M_%sHANGUP\n", (type == M_HANGUP) ? "" : "UN")); + + if ((parse->parse_status & PARSE_ENABLE) && + parse_iopps(&parse->parse_io, status ? SYNC_ONE : SYNC_ZERO, &ctime)) + { + nmp = (mblk_t *)NULL; + if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED))) + { + bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t)); + nmp->b_wptr += sizeof(parsetime_t); + putnext(parse->parse_queue, nmp); + } + else + if (nmp) freemsg(nmp); + parse_iodone(&parse->parse_io); + } + + if (status) + { + parse->parse_ppsclockev.tv = ctime.tv; + ++(parse->parse_ppsclockev.serial); + } + } + } +} + +static int init_zs_linemon(); /* handle line monitor for "zs" driver */ +static void close_zs_linemon(); +static void zs_xsisr(); /* zs external status interupt handler */ + +/*-------------------- CD isr status monitor ---------------*/ + +static int init_linemon(queue_t *q) +{ + register queue_t *dq; + + dq = SAFE_WR(q); + /* + * we ARE doing very bad things down here (basically stealing ISR + * hooks) + * + * so we chase down the STREAMS stack searching for the driver + * and if this is a known driver we insert our ISR routine for + * status changes in to the ExternalStatus handling hook + */ + while (dq->q_next) + { + dq = dq->q_next; /* skip down to driver */ + } + + /* + * find appropriate driver dependent routine + */ + if (dq->q_qinfo && dq->q_qinfo->qi_minfo) + { + register char *dname = dq->q_qinfo->qi_minfo->mi_idname; + + parseprintf(DD_INSTALL, ("init_linemon: driver is \"%s\"\n", dname)); + +#ifdef sun + if (dname && !Strcmp(dname, "zs")) + { + return init_zs_linemon(dq, q); + } + else +#endif + { + parseprintf(DD_INSTALL, ("init_linemon: driver \"%s\" not suitable for CD monitoring\n", dname)); + return 0; + } + } + parseprintf(DD_INSTALL, ("init_linemon: cannot find driver\n")); + return 0; +} + +static void close_linemon(queue_t *q, queue_t *my_q) +{ + /* + * find appropriate driver dependent routine + */ + if (q->q_qinfo && q->q_qinfo->qi_minfo) + { + register char *dname = q->q_qinfo->qi_minfo->mi_idname; + +#ifdef sun + if (dname && !Strcmp(dname, "zs")) + { + close_zs_linemon(q, my_q); + return; + } + parseprintf(DD_INSTALL, ("close_linemon: cannot find driver close routine for \"%s\"\n", dname)); +#endif + } + parseprintf(DD_INSTALL, ("close_linemon: cannot find driver name\n")); +} + +#ifdef sun +#include <sys/tty.h> +#include <sys/zsdev.h> +#include <sys/ser_async.h> +#include <sys/ser_zscc.h> + +/* + * there should be some docs telling how to get to + * sz:zs_usec_delay and zs:initzsops() + */ +#define zs_usec_delay 5 + +struct savedzsops +{ + struct zsops zsops; + struct zsops *oldzsops; +}; + +static struct zsops *emergencyzs; + +static int init_zs_linemon(queue_t *q, queue_t *my_q) +{ + register struct zscom *zs; + register struct savedzsops *szs; + register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr; + /* + * we expect the zsaline pointer in the q_data pointer + * from there on we insert our on EXTERNAL/STATUS ISR routine + * into the interrupt path, before the standard handler + */ + zs = ((struct asyncline *)q->q_ptr)->za_common; + if (!zs) + { + /* + * well - not found on startup - just say no (shouldn't happen though) + */ + return 0; + } + else + { + unsigned long s; + + /* + * we do a direct replacement, in case others fiddle also + * if somebody else grabs our hook and we disconnect + * we are in DEEP trouble - panic is likely to be next, sorry + */ + szs = (struct savedzsops *) kmem_alloc(sizeof(struct savedzsops), KM_SLEEP); + + parsestream->parse_data = (void *)szs; + + mutex_enter(zs->zs_excl); + + parsestream->parse_dqueue = q; /* remember driver */ + + szs->zsops = *zs->zs_ops; + szs->zsops.zsop_xsint = (void (*)())zs_xsisr; /* place our bastard */ + szs->oldzsops = zs->zs_ops; + emergencyzs = zs->zs_ops; + + zs->zs_ops = &szs->zsops; /* hook it up */ + + mutex_exit(zs->zs_excl); + + parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor installed\n")); + + return 1; + } +} + +/* + * unregister our ISR routine - must call under splhigh() + */ +static void close_zs_linemon(queue_t *q, queue_t *my_q) +{ + register struct zscom *zs; + register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr; + + zs = ((struct asyncline *)q->q_ptr)->za_common; + if (!zs) + { + /* + * well - not found on startup - just say no (shouldn't happen though) + */ + return; + } + else + { + register struct savedzsops *szs = (struct savedzsops *)parsestream->parse_data; + + mutex_enter(zs->zs_excl); + + zs->zs_ops = szs->oldzsops; /* reset to previous handler functions */ + + mutex_exit(zs->zs_excl); + + kmem_free((caddr_t)szs, sizeof (struct savedzsops)); + + parseprintf(DD_INSTALL, ("close_zs_linemon: CD monitor deleted\n")); + return; + } +} + +#define MAXDEPTH 50 /* maximum allowed stream crawl */ + +/* + * take external status interrupt (only CD interests us) + */ +static void zs_xsisr(struct zscom *zs) +{ + register struct asyncline *za = (struct asyncline *)zs->zs_priv; + register queue_t *q; + register unsigned char zsstatus; + register int loopcheck; + register unsigned char cdstate; + register char *dname; + + /* + * pick up current state + */ + zsstatus = SCC_READ0(); + + if (za->za_rr0 ^ (cdstate = zsstatus & ZSRR0_CD)) + { + timestamp_t cdevent; + register int status; + + /* + * CONDITIONAL external measurement support + */ + SET_LED(cdstate); /* + * inconsistent with upper SET_LED, but this + * is for oscilloscope business anyway and we + * are just interested in edge delays in the + * lower us range + */ + + /* + * time stamp + */ + uniqtime(&cdevent.tv); + + TIMEVAL_USADD(&cdevent.tv, xsdelay); + + q = za->za_ttycommon.t_readq; + + /* + * logical state + */ + status = cd_invert ? cdstate == 0 : cdstate != 0; + + /* + * ok - now the hard part - find ourself + */ + loopcheck = MAXDEPTH; + + while (q) + { + if (q->q_qinfo && q->q_qinfo->qi_minfo) + { + dname = q->q_qinfo->qi_minfo->mi_idname; + + if (!Strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname)) + { + /* + * back home - phew (hopping along stream queues might + * prove dangerous to your health) + */ + + if ((((parsestream_t *)q->q_ptr)->parse_status & PARSE_ENABLE) && + parse_iopps(&((parsestream_t *)q->q_ptr)->parse_io, status ? SYNC_ONE : SYNC_ZERO, &cdevent)) + { + /* + * XXX - currently we do not pass up the message, as + * we should. + * for a correct behaviour wee need to block out + * processing until parse_iodone has been posted via + * a softcall-ed routine which does the message pass-up + * right now PPS information relies on input being + * received + */ + parse_iodone(&((parsestream_t *)q->q_ptr)->parse_io); + } + + if (status) + { + ((parsestream_t *)q->q_ptr)->parse_ppsclockev.tv = cdevent.tv; + ++(((parsestream_t *)q->q_ptr)->parse_ppsclockev.serial); + } + + parseprintf(DD_ISR, ("zs_xsisr: CD event %s has been posted for \"%s\"\n", status ? "ONE" : "ZERO", dname)); + break; + } + } + + q = q->q_next; + + if (!loopcheck--) + { + panic("zs_xsisr: STREAMS Queue corrupted - CD event"); + } + } + + /* + * only pretend that CD has been handled + */ + za->za_rr0 = za->za_rr0 & ~ZSRR0_CD | zsstatus & ZSRR0_CD; + + if (!((za->za_rr0 ^ zsstatus) & ~ZSRR0_CD)) + { + /* + * all done - kill status indication and return + */ + SCC_WRITE0(ZSWR0_RESET_STATUS); /* might kill other conditions here */ + return; + } + } + + /* + * we are now gathered here to process some unusual external status + * interrupts. + * any CD events have also been handled and shouldn't be processed + * by the original routine (unless we have a VERY busy port pin) + * some initializations are done here, which could have been done before for + * both code paths but have been avioded for minimum path length to + * the uniq_time routine + */ + dname = (char *) 0; + q = za->za_ttycommon.t_readq; + + loopcheck = MAXDEPTH; + + /* + * the real thing for everything else ... + */ + while (q) + { + if (q->q_qinfo && q->q_qinfo->qi_minfo) + { + dname = q->q_qinfo->qi_minfo->mi_idname; + if (!Strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname)) + { + register void (*zsisr)(); + + /* + * back home - phew (hopping along stream queues might + * prove dangerous to your health) + */ + if (zsisr = ((struct savedzsops *)((parsestream_t *)q->q_ptr)->parse_data)->oldzsops->zsop_xsint) + zsisr(zs); + else + panic("zs_xsisr: unable to locate original ISR"); + + parseprintf(DD_ISR, ("zs_xsisr: non CD event was processed for \"%s\"\n", dname)); + /* + * now back to our program ... + */ + return; + } + } + + q = q->q_next; + + if (!loopcheck--) + { + panic("zs_xsisr: STREAMS Queue corrupted - non CD event"); + } + } + + /* + * last resort - shouldn't even come here as it indicates + * corrupted TTY structures + */ + printf("zs_zsisr: looking for \"%s\" - found \"%s\" - taking EMERGENCY path\n", parseinfo.st_rdinit->qi_minfo->mi_idname, dname ? dname : "-NIL-"); + + if (emergencyzs && emergencyzs->zsop_xsint) + emergencyzs->zsop_xsint(zs); + else + panic("zs_xsisr: no emergency ISR handler"); +} +#endif /* sun */ + +/* + * History: + * + * parsesolaris.c,v + * Revision 3.4 1993/11/13 11:13:17 kardel + * Solaris 2.3 additional includes + * + * Revision 3.3 1993/11/11 11:20:33 kardel + * declaration fixes + * + * Revision 3.2 1993/11/05 15:40:25 kardel + * shut up nice feature detection + * + * Revision 3.1 1993/11/01 20:00:29 kardel + * parse Solaris support (initial version) + * + * + */ diff --git a/usr.sbin/xntpd/parse/parsestreams.c b/usr.sbin/xntpd/parse/parsestreams.c new file mode 100644 index 000000000000..56ce07c93e6a --- /dev/null +++ b/usr.sbin/xntpd/parse/parsestreams.c @@ -0,0 +1,1277 @@ +/* + * /src/NTP/REPOSITORY/v3/parse/parsestreams.c,v 3.9 1993/11/05 15:34:55 kardel Exp + * + * parsestreams.c,v 3.9 1993/11/05 15:34:55 kardel Exp + * + * STREAMS module for reference clocks + * (SunOS4.x) + * + * Copyright (c) 1989,1990,1991,1992,1993 + * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef lint +static char rcsid[] = "parsestreams.c,v 3.9 1993/11/05 15:34:55 kardel Exp"; +#endif + +#include "sys/types.h" +#include "sys/conf.h" +#include "sys/buf.h" +#include "sys/param.h" +#include "sys/sysmacros.h" +#include "sys/errno.h" +#include "sys/time.h" +#include "sundev/mbvar.h" +#include "sun/autoconf.h" +#include "sys/stream.h" +#include "sys/stropts.h" +#include "sys/dir.h" +#include "sys/signal.h" +#include "sys/termios.h" +#include "sys/termio.h" +#include "sys/ttold.h" +#include "sys/user.h" +#include "sys/errno.h" +#include "sys/tty.h" +#include "machine/cpu.h" + +#ifdef VDDRV +#include "sun/vddrv.h" +#endif + +/* + * no protypes here ! + */ +#define P(x) () + +/* + * use microtime instead of uniqtime if advised to + */ +#ifdef MICROTIME +#define uniqtime microtime +#endif + +#define HAVE_NO_NICE /* for the NTP headerfiles */ +#include "ntp_fp.h" +#include "parse.h" +#include "sys/parsestreams.h" + +#ifdef VDDRV +static unsigned int parsebusy = 0; + +/*--------------- loadable driver section -----------------------------*/ + +extern struct streamtab parseinfo; + +struct vdldrv parsesync_vd = +{ + VDMAGIC_PSEUDO, /* nothing like a real driver - a STREAMS module */ + "PARSE ", /* name this baby - keep room for revision number */ +}; + +/* + * strings support usually not in kernel + */ +static int strlen(s) + register char *s; +{ + register int c; + + c = 0; + if (s) + { + while (*s++) + { + c++; + } + } + return c; +} + +static void strncpy(t, s, c) + register char *t; + register char *s; + register int c; +{ + if (s && t) + { + while ((c-- > 0) && (*t++ = *s++)) + ; + } +} + +static int strcmp(s, t) + register char *s; + register char *t; +{ + register int c = 0; + + if (!s || !t || (s == t)) + { + return 0; + } + + while (!(c = *s++ - *t++) && *s && *t) + /* empty loop */; + + return c; +} + +static int strncmp(s, t, n) + register char *s; + register char *t; + register int n; +{ + register int c = 0; + + if (!s || !t || (s == t)) + { + return 0; + } + + while (n-- && !(c = *s++ - *t++) && *s && *t) + /* empty loop */; + + return c; +} + +/* + * driver init routine + * since no mechanism gets us into and out of the fmodsw, we have to + * do it ourselves + */ +/*ARGSUSED*/ +int xxxinit(fc, vdp, vdi, vds) + unsigned int fc; + struct vddrv *vdp; + addr_t vdi; + struct vdstat *vds; +{ + extern struct fmodsw fmodsw[]; + extern int fmodcnt; + + struct fmodsw *fm = fmodsw; + struct fmodsw *fmend = &fmodsw[fmodcnt]; + struct fmodsw *ifm = (struct fmodsw *)0; + char *mname = parseinfo.st_rdinit->qi_minfo->mi_idname; + + switch (fc) + { + case VDLOAD: + vdp->vdd_vdtab = (struct vdlinkage *)&parsesync_vd; + /* + * now, jog along fmodsw scanning for an empty slot + * and deposit our name there + */ + while (fm <= fmend) + { + if (!strncmp(fm->f_name, mname, FMNAMESZ)) + { + printf("vddrinit[%s]: STREAMS module already loaded.\n", mname); + return(EBUSY); + } + else + if ((ifm == (struct fmodsw *)0) && + (fm->f_name[0] == '\0') && (fm->f_str == (struct streamtab *)0)) + { + /* + * got one - so move in + */ + ifm = fm; + break; + } + fm++; + } + + if (ifm == (struct fmodsw *)0) + { + printf("vddrinit[%s]: no slot free for STREAMS module\n", mname); + return (ENOSPC); + } + else + { + static char revision[] = "3.9"; + char *s, *S, *t; + + strncpy(ifm->f_name, mname, FMNAMESZ); + ifm->f_name[FMNAMESZ] = '\0'; + ifm->f_str = &parseinfo; + /* + * copy RCS revision into Drv_name + * + * are we forcing RCS here to do things it was not built for ? + */ + s = revision; + if (*s == '$') + { + /* + * skip "$Revision: " + * if present. - not necessary on a -kv co (cvs export) + */ + while (*s && (*s != ' ')) + { + s++; + } + if (*s == ' ') s++; + } + + t = parsesync_vd.Drv_name; + while (*t && (*t != ' ')) + { + t++; + } + if (*t == ' ') t++; + + S = s; + while (*S && (((*S >= '0') && (*S <= '9')) || (*S == '.'))) + { + S++; + } + + if (*s && *t && (S > s)) + { + if (strlen(t) >= (S - s)) + { + (void) strncpy(t, s, S - s); + } + } + return (0); + } + break; + + case VDUNLOAD: + if (parsebusy > 0) + { + printf("vddrinit[%s]: STREAMS module has still %d instances active.\n", mname, parsebusy); + return (EBUSY); + } + else + { + while (fm <= fmend) + { + if (!strncmp(fm->f_name, mname, FMNAMESZ)) + { + /* + * got it - kill entry + */ + fm->f_name[0] = '\0'; + fm->f_str = (struct streamtab *)0; + fm++; + + break; + } + fm++; + } + if (fm > fmend) + { + printf("vddrinit[%s]: cannot find entry for STREAMS module\n", mname); + return (ENXIO); + } + else + return (0); + } + + + case VDSTAT: + return (0); + + default: + return (EIO); + + } + return EIO; +} + +#endif + +/*--------------- stream module definition ----------------------------*/ + +static int parseopen(), parseclose(), parsewput(), parserput(), parsersvc(); + +static struct module_info driverinfo = +{ + 0, /* module ID number */ + "parse", /* module name */ + 0, /* minimum accepted packet size */ + INFPSZ, /* maximum accepted packet size */ + 1, /* high water mark - flow control */ + 0 /* low water mark - flow control */ +}; + +static struct qinit rinit = /* read queue definition */ +{ + parserput, /* put procedure */ + parsersvc, /* service procedure */ + parseopen, /* open procedure */ + parseclose, /* close procedure */ + NULL, /* admin procedure - NOT USED FOR NOW */ + &driverinfo, /* information structure */ + NULL /* statistics */ +}; + +static struct qinit winit = /* write queue definition */ +{ + parsewput, /* put procedure */ + NULL, /* service procedure */ + NULL, /* open procedure */ + NULL, /* close procedure */ + NULL, /* admin procedure - NOT USED FOR NOW */ + &driverinfo, /* information structure */ + NULL /* statistics */ +}; + +struct streamtab parseinfo = /* stream info element for dpr driver */ +{ + &rinit, /* read queue */ + &winit, /* write queue */ + NULL, /* read mux */ + NULL, /* write mux */ + NULL /* module auto push */ +}; + +/*--------------- driver data structures ----------------------------*/ + +/* + * we usually have an inverted signal - but you + * can change this to suit your needs + */ +int cd_invert = 1; /* invert status of CD line - PPS support via CD input */ + +int parsedebug = ~0; + +extern void uniqtime(); + +/*--------------- module implementation -----------------------------*/ + +#define TIMEVAL_USADD(_X_, _US_) {\ + (_X_)->tv_usec += (_US_);\ + if ((_X_)->tv_usec >= 1000000)\ + {\ + (_X_)->tv_sec++;\ + (_X_)->tv_usec -= 1000000;\ + }\ + } while (0) + +#if defined(sun4c) && defined(DEBUG_CD) +#include <sun4c/cpu.h> +#include <sun4c/auxio.h> +#define SET_LED(_X_) (((cpu & CPU_ARCH) == SUN4C_ARCH) ? *(u_char *)AUXIO_REG = AUX_MBO|AUX_EJECT|((_X_)?AUX_LED:0) : 0) +#else +#define SET_LED(_X_) +#endif + +static int init_linemon(); +static void close_linemon(); + +/* + * keep here MACHINE AND OS AND ENVIRONMENT DEPENDENT + * timing constants + * + * FOR ABSOLUTE PRECISION YOU NEED TO MEASURE THE TIMING + * SKEW BETWEEN THE HW-PPS SIGNAL AND KERNEL uniqtime() + * YOURSELF. + * + * YOU MUST BE QUALIFIED APPROPRIATELY FOR THESE TYPE + * OF HW MANIPULATION ! + * + * you need an oscilloscope and the permission for HW work + * in order to figure out these timing constants/variables + */ +#ifdef sun +static unsigned long xsdelay = 10; /* assume an SS2 */ +static unsigned long stdelay = 350; + +struct delays +{ + unsigned char mask; /* what to check for */ + unsigned char type; /* what to match */ + unsigned long xsdelay; /* external status direct delay in us */ + unsigned long stdelay; /* STREAMS message delay (M_[UN]HANGUP) */ +} isr_delays[] = +{ + /* + * WARNING: must still be measured - currently taken from Craig Leres ppsdev + */ +#ifdef sun4c + {CPU_ARCH|CPU_MACH, CPU_SUN4C_50, 10, 350}, + {CPU_ARCH|CPU_MACH, CPU_SUN4C_65, 15, 700}, + {CPU_ARCH|CPU_MACH, CPU_SUN4C_75, 10, 350}, +#endif +#ifdef sun4m + {CPU_ARCH|CPU_MACH, CPU_SUN4M_50, 8, 250}, + {CPU_ARCH|CPU_MACH, CPU_SUN4M_690, 8, 250}, +#endif + {0,} +}; + +void setup_delays() +{ + register int i; + + for (i = 0; isr_delays[i].mask; i++) + { + if ((cpu & isr_delays[i].mask) == isr_delays[i].type) + { + xsdelay = isr_delays[i].xsdelay; + stdelay = isr_delays[i].stdelay; + return; + } + } + printf("parse: WARNING: PPS kernel fudge factors unknown for this machine (Type 0x%x) - assuming SS2 (Sun4/75)\n", cpu); +} +#else +#define setup_delays() /* empty - no need for clobbering kernel with this */ +static unsigned long xsdelay = 0; /* assume nothing */ +static unsigned long stdelay = 0; +#endif + +#define M_PARSE 0x0001 +#define M_NOPARSE 0x0002 + +static int +setup_stream(q, mode) + queue_t *q; + int mode; +{ + mblk_t *mp; + + mp = allocb(sizeof(struct stroptions), BPRI_MED); + if (mp) + { + struct stroptions *str = (struct stroptions *)mp->b_rptr; + + str->so_flags = SO_READOPT|SO_HIWAT|SO_LOWAT; + str->so_readopt = (mode == M_PARSE) ? RMSGD : RNORM; + str->so_hiwat = (mode == M_PARSE) ? sizeof(parsetime_t) : 256; + str->so_lowat = 0; + mp->b_datap->db_type = M_SETOPTS; + mp->b_wptr += sizeof(struct stroptions); + putnext(q, mp); + return putctl1(WR(q)->q_next, M_CTL, (mode == M_PARSE) ? MC_SERVICEIMM : + MC_SERVICEDEF); + } + else + { + parseprintf(DD_OPEN,("parse: setup_stream - FAILED - no MEMORY for allocb\n")); + return 0; + } +} + +/*ARGSUSED*/ +static int parseopen(q, dev, flag, sflag) + queue_t *q; + dev_t dev; + int flag; + int sflag; +{ + register mblk_t *mp; + register parsestream_t *parse; + static int notice = 0; + + parseprintf(DD_OPEN,("parse: OPEN\n")); + + if (sflag != MODOPEN) + { /* open only for modules */ + parseprintf(DD_OPEN,("parse: OPEN - FAILED - not MODOPEN\n")); + return OPENFAIL; + } + + if (q->q_ptr != (caddr_t)NULL) + { + u.u_error = EBUSY; + parseprintf(DD_OPEN,("parse: OPEN - FAILED - EXCLUSIVE ONLY\n")); + return OPENFAIL; + } + +#ifdef VDDRV + parsebusy++; +#endif + + q->q_ptr = (caddr_t)kmem_alloc(sizeof(parsestream_t)); + WR(q)->q_ptr = q->q_ptr; + + parse = (parsestream_t *) q->q_ptr; + bzero((caddr_t)parse, sizeof(*parse)); + parse->parse_queue = q; + parse->parse_status = PARSE_ENABLE; + parse->parse_ppsclockev.tv.tv_sec = 0; + parse->parse_ppsclockev.tv.tv_usec = 0; + parse->parse_ppsclockev.serial = 0; + + if (!parse_ioinit(&parse->parse_io)) + { + /* + * ok guys - beat it + */ + kmem_free((caddr_t)parse, sizeof(parsestream_t)); +#ifdef VDDRV + parsebusy--; +#endif + return OPENFAIL; + } + + if (setup_stream(q, M_PARSE)) + { + (void) init_linemon(q); /* hook up PPS ISR routines if possible */ + setup_delays(); + parseprintf(DD_OPEN,("parse: OPEN - SUCCEEDED\n")); + + /* + * I know that you know the delete key, but you didn't write this + * code, did you ? - So, keep the message in here. + */ + if (!notice) + { + printf("%s: Copyright (c) 1991-1993, Frank Kardel\n", parsesync_vd.Drv_name); + notice = 1; + } + + return 1; + } + else + { +#ifdef VDDRV + parsebusy--; +#endif + return OPENFAIL; + } +} + +/*ARGSUSED*/ +static int parseclose(q, flags) + queue_t *q; + int flags; +{ + register parsestream_t *parse = (parsestream_t *)q->q_ptr; + register unsigned long s; + + parseprintf(DD_CLOSE,("parse: CLOSE\n")); + + s = splhigh(); + + if (parse->parse_dqueue) + close_linemon(parse->parse_dqueue, q); + parse->parse_dqueue = (queue_t *)0; + + (void) splx(s); + + parse_ioend(&parse->parse_io); + + kmem_free((caddr_t)parse, sizeof(parsestream_t)); + + q->q_ptr = (caddr_t)NULL; + WR(q)->q_ptr = (caddr_t)NULL; + +#ifdef VDDRV + parsebusy--; +#endif +} + +/* + * move unrecognized stuff upward + */ +static parsersvc(q) + queue_t *q; +{ + mblk_t *mp; + + while (mp = getq(q)) + { + if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL)) + { + putnext(q, mp); + parseprintf(DD_RSVC,("parse: RSVC - putnext\n")); + } + else + { + putbq(q, mp); + parseprintf(DD_RSVC,("parse: RSVC - flow control wait\n")); + break; + } + } +} + +/* + * do ioctls and + * send stuff down - dont care about + * flow control + */ +static int parsewput(q, mp) + queue_t *q; + register mblk_t *mp; +{ + register int ok = 1; + register mblk_t *datap; + register struct iocblk *iocp; + parsestream_t *parse = (parsestream_t *)q->q_ptr; + + parseprintf(DD_WPUT,("parse: parsewput\n")); + + switch (mp->b_datap->db_type) + { + default: + putnext(q, mp); + break; + + case M_IOCTL: + iocp = (struct iocblk *)mp->b_rptr; + switch (iocp->ioc_cmd) + { + default: + parseprintf(DD_WPUT,("parse: parsewput - forward M_IOCTL\n")); + putnext(q, mp); + break; + + case CIOGETEV: + /* + * taken from Craig Leres ppsclock module (and modified) + */ + datap = allocb(sizeof(struct ppsclockev), BPRI_MED); + if (datap == NULL || mp->b_cont) + { + mp->b_datap->db_type = M_IOCNAK; + iocp->ioc_error = (datap == NULL) ? ENOMEM : EINVAL; + if (datap != NULL) + freeb(datap); + qreply(q, mp); + break; + } + + mp->b_cont = datap; + *(struct ppsclockev *)datap->b_wptr = parse->parse_ppsclockev; + datap->b_wptr += + sizeof(struct ppsclockev) / sizeof(*datap->b_wptr); + mp->b_datap->db_type = M_IOCACK; + iocp->ioc_count = sizeof(struct ppsclockev); + qreply(q, mp); + break; + + case PARSEIOC_ENABLE: + case PARSEIOC_DISABLE: + { + parse->parse_status = (parse->parse_status & ~PARSE_ENABLE) | + (iocp->ioc_cmd == PARSEIOC_ENABLE) ? + PARSE_ENABLE : 0; + if (!setup_stream(RD(q), (parse->parse_status & PARSE_ENABLE) ? + M_PARSE : M_NOPARSE)) + { + mp->b_datap->db_type = M_IOCNAK; + } + else + { + mp->b_datap->db_type = M_IOCACK; + } + qreply(q, mp); + break; + } + + case PARSEIOC_SETSTAT: + case PARSEIOC_GETSTAT: + case PARSEIOC_TIMECODE: + case PARSEIOC_SETFMT: + case PARSEIOC_GETFMT: + case PARSEIOC_SETCS: + if (iocp->ioc_count == sizeof(parsectl_t)) + { + parsectl_t *dct = (parsectl_t *)mp->b_cont->b_rptr; + + switch (iocp->ioc_cmd) + { + case PARSEIOC_GETSTAT: + parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETSTAT\n")); + ok = parse_getstat(dct, &parse->parse_io); + break; + + case PARSEIOC_SETSTAT: + parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETSTAT\n")); + ok = parse_setstat(dct, &parse->parse_io); + break; + + case PARSEIOC_TIMECODE: + parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_TIMECODE\n")); + ok = parse_timecode(dct, &parse->parse_io); + break; + + case PARSEIOC_SETFMT: + parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETFMT\n")); + ok = parse_setfmt(dct, &parse->parse_io); + break; + + case PARSEIOC_GETFMT: + parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETFMT\n")); + ok = parse_getfmt(dct, &parse->parse_io); + break; + + case PARSEIOC_SETCS: + parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETCS\n")); + ok = parse_setcs(dct, &parse->parse_io); + break; + } + mp->b_datap->db_type = ok ? M_IOCACK : M_IOCNAK; + } + else + { + mp->b_datap->db_type = M_IOCNAK; + } + parseprintf(DD_WPUT,("parse: parsewput qreply - %s\n", (mp->b_datap->db_type == M_IOCNAK) ? "M_IOCNAK" : "M_IOCACK")); + qreply(q, mp); + break; + } + } +} + +/* + * read characters from streams buffers + */ +static unsigned long rdchar(mp) + register mblk_t **mp; +{ + while (*mp != (mblk_t *)NULL) + { + if ((*mp)->b_wptr - (*mp)->b_rptr) + { + return (unsigned long)(*(unsigned char *)((*mp)->b_rptr++)); + } + else + { + register mblk_t *mmp = *mp; + + *mp = (*mp)->b_cont; + freeb(mmp); + } + } + return ~0; +} + +/* + * convert incoming data + */ +static int parserput(q, mp) + queue_t *q; + mblk_t *mp; +{ + unsigned char type; + + switch (type = mp->b_datap->db_type) + { + default: + /* + * anything we don't know will be put on queue + * the service routine will move it to the next one + */ + parseprintf(DD_RPUT,("parse: parserput - forward type 0x%x\n", type)); + if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL)) + { + putnext(q, mp); + } + else + putq(q, mp); + break; + + case M_BREAK: + case M_DATA: + { + register parsestream_t * parse = (parsestream_t *)q->q_ptr; + register mblk_t *nmp; + register unsigned long ch; + timestamp_t ctime; + + /* + * get time on packet delivery + */ + uniqtime(&ctime.tv); + + if (!(parse->parse_status & PARSE_ENABLE)) + { + parseprintf(DD_RPUT,("parse: parserput - parser disabled - forward type 0x%x\n", type)); + if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL)) + { + putnext(q, mp); + } + else + putq(q, mp); + } + else + { + parseprintf(DD_RPUT,("parse: parserput - M_%s\n", (type == M_DATA) ? "DATA" : "BREAK")); + + if (type == M_DATA) + { + /* + * parse packet looking for start an end characters + */ + while (mp != (mblk_t *)NULL) + { + ch = rdchar(&mp); + if (ch != ~0 && parse_ioread(&parse->parse_io, (char)ch, &ctime)) + { + /* + * up up and away (hopefully ...) + * don't press it if resources are tight or nobody wants it + */ + nmp = (mblk_t *)NULL; + if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED))) + { + bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t)); + nmp->b_wptr += sizeof(parsetime_t); + putnext(parse->parse_queue, nmp); + } + else + if (nmp) freemsg(nmp); + parse_iodone(&parse->parse_io); + } + } + } + else + { + if (parse_ioread(&parse->parse_io, (char)0, &ctime)) + { + /* + * up up and away (hopefully ...) + * don't press it if resources are tight or nobody wants it + */ + nmp = (mblk_t *)NULL; + if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED))) + { + bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t)); + nmp->b_wptr += sizeof(parsetime_t); + putnext(parse->parse_queue, nmp); + } + else + if (nmp) freemsg(nmp); + parse_iodone(&parse->parse_io); + } + freemsg(mp); + } + break; + } + } + + /* + * CD PPS support for non direct ISR hack + */ + case M_HANGUP: + case M_UNHANGUP: + { + register parsestream_t * parse = (parsestream_t *)q->q_ptr; + timestamp_t ctime; + register mblk_t *nmp; + register int status = cd_invert ^ (type == M_HANGUP); + + SET_LED(status); + + uniqtime(&ctime.tv); + + TIMEVAL_USADD(&ctime.tv, stdelay); + + parseprintf(DD_RPUT,("parse: parserput - M_%sHANGUP\n", (type == M_HANGUP) ? "" : "UN")); + + if ((parse->parse_status & PARSE_ENABLE) && + parse_iopps(&parse->parse_io, status ? SYNC_ONE : SYNC_ZERO, &ctime)) + { + nmp = (mblk_t *)NULL; + if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED))) + { + bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t)); + nmp->b_wptr += sizeof(parsetime_t); + putnext(parse->parse_queue, nmp); + } + else + if (nmp) freemsg(nmp); + parse_iodone(&parse->parse_io); + } + + if (status) + { + parse->parse_ppsclockev.tv = ctime.tv; + ++(parse->parse_ppsclockev.serial); + } + } + } +} + +static int init_zs_linemon(); /* handle line monitor for "zs" driver */ +static void close_zs_linemon(); +static void zs_xsisr(); /* zs external status interupt handler */ + +/*-------------------- CD isr status monitor ---------------*/ + +static int init_linemon(q) + register queue_t *q; +{ + register queue_t *dq; + + dq = WR(q); + /* + * we ARE doing very bad things down here (basically stealing ISR + * hooks) + * + * so we chase down the STREAMS stack searching for the driver + * and if this is a known driver we insert our ISR routine for + * status changes in to the ExternalStatus handling hook + */ + while (dq->q_next) + { + dq = dq->q_next; /* skip down to driver */ + } + + /* + * find appropriate driver dependent routine + */ + if (dq->q_qinfo && dq->q_qinfo->qi_minfo) + { + register char *dname = dq->q_qinfo->qi_minfo->mi_idname; + + parseprintf(DD_INSTALL, ("init_linemon: driver is \"%s\"\n", dname)); + +#ifdef sun + if (dname && !strcmp(dname, "zs")) + { + return init_zs_linemon(dq, q); + } + else +#endif + { + parseprintf(DD_INSTALL, ("init_linemon: driver \"%s\" not suitable for CD monitoring\n", dname)); + return 0; + } + } + parseprintf(DD_INSTALL, ("init_linemon: cannot find driver\n")); + return 0; +} + +static void close_linemon(q, my_q) + register queue_t *q; + register queue_t *my_q; +{ + /* + * find appropriate driver dependent routine + */ + if (q->q_qinfo && q->q_qinfo->qi_minfo) + { + register char *dname = q->q_qinfo->qi_minfo->mi_idname; + +#ifdef sun + if (dname && !strcmp(dname, "zs")) + { + close_zs_linemon(q, my_q); + return; + } + parseprintf(DD_INSTALL, ("close_linemon: cannot find driver close routine for \"%s\"\n", dname)); +#endif + } + parseprintf(DD_INSTALL, ("close_linemon: cannot find driver name\n")); +} + +#ifdef sun +#include <sundev/zsreg.h> +#include <sundev/zscom.h> +#include <sundev/zsvar.h> + +struct savedzsops +{ + struct zsops zsops; + struct zsops *oldzsops; +}; + +struct zsops *emergencyzs; + +static int init_zs_linemon(q, my_q) + register queue_t *q; + register queue_t *my_q; +{ + register struct zscom *zs; + register struct savedzsops *szs; + register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr; + /* + * we expect the zsaline pointer in the q_data pointer + * from there on we insert our on EXTERNAL/STATUS ISR routine + * into the interrupt path, before the standard handler + */ + zs = ((struct zsaline *)q->q_ptr)->za_common; + if (!zs) + { + /* + * well - not found on startup - just say no (shouldn't happen though) + */ + return 0; + } + else + { + unsigned long s; + + /* + * we do a direct replacement, in case others fiddle also + * if somebody else grabs our hook and we disconnect + * we are in DEEP trouble - panic is likely to be next, sorry + */ + szs = (struct savedzsops *) kmem_alloc(sizeof(struct savedzsops)); + + parsestream->parse_data = (void *)szs; + + s = splhigh(); + + parsestream->parse_dqueue = q; /* remember driver */ + + szs->zsops = *zs->zs_ops; + szs->zsops.zsop_xsint = (int (*)())zs_xsisr; /* place our bastard */ + szs->oldzsops = zs->zs_ops; + emergencyzs = zs->zs_ops; + + zsopinit(zs, &szs->zsops); /* hook it up */ + + (void) splx(s); + + parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor installed\n")); + + return 1; + } +} + +/* + * unregister our ISR routine - must call under splhigh() + */ +static void close_zs_linemon(q, my_q) + register queue_t *q; + register queue_t *my_q; +{ + register struct zscom *zs; + register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr; + + zs = ((struct zsaline *)q->q_ptr)->za_common; + if (!zs) + { + /* + * well - not found on startup - just say no (shouldn't happen though) + */ + return; + } + else + { + register struct savedzsops *szs = (struct savedzsops *)parsestream->parse_data; + + zsopinit(zs, szs->oldzsops); /* reset to previous handler functions */ + + kmem_free((caddr_t)szs, sizeof (struct savedzsops)); + + parseprintf(DD_INSTALL, ("close_zs_linemon: CD monitor deleted\n")); + return; + } +} + +#define MAXDEPTH 50 /* maximum allowed stream crawl */ + +/* + * take external status interrupt (only CD interests us) + */ +static void zs_xsisr(zs) + register struct zscom *zs; +{ + register struct zsaline *za = (struct zsaline *)zs->zs_priv; + register struct zscc_device *zsaddr = zs->zs_addr; + register queue_t *q; + register unsigned char zsstatus; + register int loopcheck; + register unsigned char cdstate; + register char *dname; + + /* + * pick up current state + */ + zsstatus = zsaddr->zscc_control; + + if (za->za_rr0 ^ (cdstate = zsstatus & ZSRR0_CD)) + { + timestamp_t cdevent; + register int status; + + /* + * CONDITIONAL external measurement support + */ + SET_LED(cdstate); /* + * inconsistent with upper SET_LED, but this + * is for oscilloscope business anyway and we + * are just interested in edge delays in the + * lower us range + */ + + /* + * time stamp + */ + uniqtime(&cdevent.tv); + + TIMEVAL_USADD(&cdevent.tv, xsdelay); + + q = za->za_ttycommon.t_readq; + + /* + * logical state + */ + status = cd_invert ? cdstate == 0 : cdstate != 0; + + /* + * ok - now the hard part - find ourself + */ + loopcheck = MAXDEPTH; + + while (q) + { + if (q->q_qinfo && q->q_qinfo->qi_minfo) + { + dname = q->q_qinfo->qi_minfo->mi_idname; + + if (!strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname)) + { + /* + * back home - phew (hopping along stream queues might + * prove dangerous to your health) + */ + + if ((((parsestream_t *)q->q_ptr)->parse_status & PARSE_ENABLE) && + parse_iopps(&((parsestream_t *)q->q_ptr)->parse_io, status ? SYNC_ONE : SYNC_ZERO, &cdevent)) + { + /* + * XXX - currently we do not pass up the message, as + * we should. + * for a correct behaviour wee need to block out + * processing until parse_iodone has been posted via + * a softcall-ed routine which does the message pass-up + * right now PPS information relies on input being + * received + */ + parse_iodone(&((parsestream_t *)q->q_ptr)->parse_io); + } + + if (status) + { + ((parsestream_t *)q->q_ptr)->parse_ppsclockev.tv = cdevent.tv; + ++(((parsestream_t *)q->q_ptr)->parse_ppsclockev.serial); + } + + parseprintf(DD_ISR, ("zs_xsisr: CD event %s has been posted for \"%s\"\n", status ? "ONE" : "ZERO", dname)); + break; + } + } + + q = q->q_next; + + if (!loopcheck--) + { + panic("zs_xsisr: STREAMS Queue corrupted - CD event"); + } + } + + /* + * only pretend that CD has been handled + */ + za->za_rr0 = za->za_rr0 & ~ZSRR0_CD | zsstatus & ZSRR0_CD; + ZSDELAY(2); + + if (!((za->za_rr0 ^ zsstatus) & ~ZSRR0_CD)) + { + /* + * all done - kill status indication and return + */ + zsaddr->zscc_control = ZSWR0_RESET_STATUS; /* might kill other conditions here */ + return; + } + } + + /* + * we are now gathered here to process some unusual external status + * interrupts. + * any CD events have also been handled and shouldn't be processed + * by the original routine (unless we have a VERY busy port pin) + * some initializations are done here, which could have been done before for + * both code paths but have been avioded for minimum path length to + * the uniq_time routine + */ + dname = (char *) 0; + q = za->za_ttycommon.t_readq; + + loopcheck = MAXDEPTH; + + /* + * the real thing for everything else ... + */ + while (q) + { + if (q->q_qinfo && q->q_qinfo->qi_minfo) + { + dname = q->q_qinfo->qi_minfo->mi_idname; + if (!strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname)) + { + register int (*zsisr)(); + + /* + * back home - phew (hopping along stream queues might + * prove dangerous to your health) + */ + if (zsisr = ((struct savedzsops *)((parsestream_t *)q->q_ptr)->parse_data)->oldzsops->zsop_xsint) + (void)zsisr(zs); + else + panic("zs_xsisr: unable to locate original ISR"); + + parseprintf(DD_ISR, ("zs_xsisr: non CD event was processed for \"%s\"\n", dname)); + /* + * now back to our program ... + */ + return; + } + } + + q = q->q_next; + + if (!loopcheck--) + { + panic("zs_xsisr: STREAMS Queue corrupted - non CD event"); + } + } + + /* + * last resort - shouldn't even come here as it indicates + * corrupted TTY structures + */ + printf("zs_zsisr: looking for \"%s\" - found \"%s\" - taking EMERGENCY path\n", parseinfo.st_rdinit->qi_minfo->mi_idname, dname ? dname : "-NIL-"); + + if (emergencyzs && emergencyzs->zsop_xsint) + emergencyzs->zsop_xsint(zs); + else + panic("zs_xsisr: no emergency ISR handler"); +} +#endif /* sun */ + +/* + * History: + * + * parsestreams.c,v + * Revision 3.9 1993/11/05 15:34:55 kardel + * shut up nice feature detection + * + * Revision 3.8 1993/10/22 14:27:56 kardel + * Oct. 22nd 1993 reconcilation + * + * Revision 3.7 1993/10/10 18:13:53 kardel + * Makefile reorganisation, file relocation + * + * Revision 3.6 1993/10/09 15:01:18 kardel + * file structure unified + * + * Revision 3.5 1993/10/04 07:59:31 kardel + * Well, at least we should know that a the tv_usec field should be in the range 0..999999 + * + * Revision 3.4 1993/09/26 23:41:33 kardel + * new parse driver logic + * + * Revision 3.3 1993/09/11 00:38:34 kardel + * LINEMON must also cover M_[UN]HANGUP handling + * + * Revision 3.2 1993/07/06 10:02:56 kardel + * DCF77 driver goes generic... + * + */ diff --git a/usr.sbin/xntpd/parse/util/Makefile b/usr.sbin/xntpd/parse/util/Makefile new file mode 100644 index 000000000000..99caacffa6df --- /dev/null +++ b/usr.sbin/xntpd/parse/util/Makefile @@ -0,0 +1,49 @@ +# +# /src/NTP/REPOSITORY/v3/parse/util/Makefile.tmpl,v 3.11 1993/11/17 13:34:12 kardel Exp +# +COMPILER= gcc +DEFS= -DSYS_FREEBSD -DSYS_386BSD +DEFS_OPT=-DDEBUG +DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DPPSPPS -DKERNEL_PLL +CLOCKDEFS= -DLOCAL_CLOCK -DAS2201PPS -DCHU -DGOES -DIRIG -DMX4200PPS -DOMEGA -DPSTCLK -DTPRO -DWWVBCLK -DMSFEESPPS -DLEITCH +INCL= +COPTS= -O2 +INSTALL= install +BINDIR= /usr/local/bin +# +CFLAGS= $(COPTS) $(DEFS) $(DEFS_LOCAL) $(INCL) -I../../include +CC= $(COMPILER) +TOP=../../ +# +EXECS=parsetest testdcf dcfd + +all: + @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \ + awk '/-DSTREAM/ && /-DPARSE/ && /-DCLOCK_RAWDCF/ && ( /-DSYS_SUNOS/ || /-DSYS_SOLARIS/ ) { makeit = 1 } \ + END { if (makeit) \ + { print "$(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" parsetest"; } \ + }' | \ + sh + @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \ + awk '/-DPARSE/ && /-DCLOCK_RAWDCF/ && ( /-DSYS_SUNOS/ || /-DSYS_SOLARIS/ ) { makeit = 1 } \ + END { if (makeit) \ + { print "$(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" testdcf"; } \ + }' | \ + sh + @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \ + awk '/-DPARSE/ && /-DCLOCK_RAWDCF/ && ( /-DSYS_SUNOS/ || /-DSYS_SOLARIS/ ) { makeit = 1 } \ + END { if (makeit) \ + { print "$(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" dcfd"; } \ + }' | \ + sh + +clean: + -@rm -f $(EXECS) *.o + +distclean: clean + -@rm -f *.orig *.rej .version Makefile + +install: + @echo "--- DCF77 utilities should be installed manually" + @#[ -f testdcf ] && $(INSTALL) -c -m 0755 testdcf $(BINDIR) || true + @#[ -f dcfd ] && $(INSTALL) -c -m 0755 dcfd $(BINDIR) || true diff --git a/usr.sbin/xntpd/parse/util/Makefile.tmpl b/usr.sbin/xntpd/parse/util/Makefile.tmpl new file mode 100644 index 000000000000..a72100f9ec75 --- /dev/null +++ b/usr.sbin/xntpd/parse/util/Makefile.tmpl @@ -0,0 +1,49 @@ +# +# /src/NTP/REPOSITORY/v3/parse/util/Makefile.tmpl,v 3.11 1993/11/17 13:34:12 kardel Exp +# +COMPILER= cc +DEFS= +DEFS_OPT= +DEFS_LOCAL= +CLOCKDEFS= +INCL= +COPTS= -O +INSTALL= install +BINDIR= +# +CFLAGS= $(COPTS) $(DEFS) $(DEFS_LOCAL) $(INCL) -I../../include +CC= $(COMPILER) +TOP=../../ +# +EXECS=parsetest testdcf dcfd + +all: + @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \ + awk '/-DSTREAM/ && /-DPARSE/ && /-DCLOCK_RAWDCF/ && ( /-DSYS_SUNOS/ || /-DSYS_SOLARIS/ ) { makeit = 1 } \ + END { if (makeit) \ + { print "$(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" parsetest"; } \ + }' | \ + sh + @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \ + awk '/-DPARSE/ && /-DCLOCK_RAWDCF/ && ( /-DSYS_SUNOS/ || /-DSYS_SOLARIS/ ) { makeit = 1 } \ + END { if (makeit) \ + { print "$(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" testdcf"; } \ + }' | \ + sh + @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \ + awk '/-DPARSE/ && /-DCLOCK_RAWDCF/ && ( /-DSYS_SUNOS/ || /-DSYS_SOLARIS/ ) { makeit = 1 } \ + END { if (makeit) \ + { print "$(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" dcfd"; } \ + }' | \ + sh + +clean: + -@rm -f $(EXECS) *.o + +distclean: clean + -@rm -f *.orig *.rej .version Makefile + +install: + @echo "--- DCF77 utilities should be installed manually" + @#[ -f testdcf ] && $(INSTALL) -c -m 0755 testdcf $(BINDIR) || true + @#[ -f dcfd ] && $(INSTALL) -c -m 0755 dcfd $(BINDIR) || true diff --git a/usr.sbin/xntpd/parse/util/README b/usr.sbin/xntpd/parse/util/README new file mode 100644 index 000000000000..4f6dab08e940 --- /dev/null +++ b/usr.sbin/xntpd/parse/util/README @@ -0,0 +1,12 @@ +This directory contains some DCF77 related programs. +They have not yet fully been ported to other architectures then Sun with +SunOS 4.x. So if you want to try them you are on your own - a little +porting may be necessary. + +parsetest: simple parse streams module test +testdcf: simple DCF77 raw impulse test program via 50Baud RS232 +dcfd: simple DCF77 raw impulse receiver with NTP loopfilter + mechanics for synchronisation (allows DCF77 synchronisation + without network code in a nutshell) + +Frank Kardel diff --git a/usr.sbin/xntpd/parse/util/dcfd.c b/usr.sbin/xntpd/parse/util/dcfd.c new file mode 100644 index 000000000000..723e2c8e8cad --- /dev/null +++ b/usr.sbin/xntpd/parse/util/dcfd.c @@ -0,0 +1,1206 @@ +/* + * /src/NTP/REPOSITORY/v3/parse/util/dcfd.c,v 3.8 1993/11/04 20:02:05 kardel Exp + * + * dcfd.c,v 3.8 1993/11/04 20:02:05 kardel Exp + * + * DCF77 100/200ms pulse synchronisation daemon program (via 50Baud serial line) + * + * Features: + * DCF77 decoding + * NTP loopfilter logic for local clock + * interactive display for debugging + * + * Lacks: + * Leap second handling (at that level you should switch to xntp3 - really!) + * + * Copyright (c) 1993 + * Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * This program may not be sold or used for profit without prior + * written consent of the author. + */ + +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/signal.h> +#include <sys/errno.h> +#include <syslog.h> + +#ifdef USE_PROTOTYPES +#include "ntp_stdlib.h" +extern int sigvec P((int, struct sigvec *, struct sigvec *)); +extern int fscanf P((FILE *, char *, ...)); +#endif + +#ifdef SYS_LINUX +#include "ntp_timex.h" +#endif + +#if defined(HAVE_TERMIOS) || defined(STREAM) +#include <termios.h> +#define TTY_GETATTR(_FD_, _ARG_) tcgetattr((_FD_), (_ARG_)) +#define TTY_SETATTR(_FD_, _ARG_) tcsetattr((_FD_), TCSANOW, (_ARG_)) +#endif + +#if defined(HAVE_TERMIO) || defined(HAVE_SYSV_TTYS) +#include <termio.h> +#define TTY_GETATTR(_FD_, _ARG_) ioctl((_FD_), TCGETA, (_ARG_)) +#define TTY_SETATTR(_FD_, _ARG_) ioctl((_FD_), TCSETAW, (_ARG_)) +#endif + +#ifndef TTY_GETATTR +MUST DEFINE ONE OF "HAVE_TERMIOS" or "HAVE_TERMIO" +#endif + +#ifndef dysize +#define dysize(_x_) (((_x_) % 4) ? 365 : (((_x_) % 400) ? 365 : 366)) +#endif + +#define timernormalize(_a_, _b_) \ + if ((_a_)->tv_usec >= 1000000) \ + { \ + (_a_)->tv_sec += (_a_)->tv_usec / 1000000; \ + (_a_)->tv_usec -= (_a_)->tv_usec % 1000000; \ + } \ + +#define timeradd(_a_, _b_) \ + (_a_)->tv_sec += (_b_)->tv_sec; \ + (_a_)->tv_usec += (_b_)->tv_usec; \ + timernormalize((_a_), (_b_)) + +#define timersub(_a_, _b_) \ + (_a_)->tv_sec -= (_b_)->tv_sec; \ + (_a_)->tv_usec -= (_b_)->tv_usec; \ + timernormalize((_a_), (_b_)) + +#define PRINTF if (interactive) printf + +#ifdef DEBUG +#define dprintf(_x_) PRINTF _x_ +#else +#define dprintf(_x_) +#endif + +extern int errno; + +static int interactive = 0; +static int loop_filter_debug = 0; + +#define NO_SYNC 0x01 +#define SYNC 0x02 + +static int sync_state = NO_SYNC; +static time_t last_sync; + +static unsigned long ticks = 0; + +static char pat[] = "-\\|/"; + +#define LINES (24-2) /* error lines after which the two headlines are repeated */ + +#define MAX_UNSYNC (5*60) /* allow synchronisation loss for 5 minutes */ +#define NOTICE_INTERVAL (20*60) /* mention missing synchronisation every 20 minutes */ + +/* + * clock adjustment PLL - see NTP protocol spec (RFC1305) for details + */ + +#define USECSCALE 10 +#define TIMECONSTANT 0 +#define ADJINTERVAL 0 +#define FREQ_WEIGHT 18 +#define PHASE_WEIGHT 7 +#define MAX_DRIFT 0x3FFFFFFF + +#define R_SHIFT(_X_, _Y_) (((_X_) < 0) ? -(-(_X_) >> (_Y_)) : ((_X_) >> (_Y_))) + +static struct timeval max_adj_offset = { 0, 128000 }; + +static long clock_adjust = 0; /* current adjustment value (usec * 2^USECSCALE) */ +static long drift_comp = 0; /* accumulated drift value (usec / ADJINTERVAL) */ +static long adjustments = 0; +static char skip_adjust = 1; /* discard first adjustment (bad samples) */ + +/* + * state flags + */ +#define DCFB_ANNOUNCE 0x0001 /* switch time zone warning (DST switch) */ +#define DCFB_DST 0x0002 /* DST in effect */ +#define DCFB_LEAP 0x0004 /* LEAP warning (1 hour prior to occurence) */ +#define DCFB_ALTERNATE 0x0008 /* alternate antenna used */ + +struct clocktime /* clock time broken up from time code */ +{ + long wday; + long day; + long month; + long year; + long hour; + long minute; + long second; + long usecond; + long utcoffset; /* in minutes */ + long flags; /* current clock status */ +}; + +typedef struct clocktime clocktime_t; + +#define TIMES10(_X_) (((_X_) << 3) + ((_X_) << 1)) /* *8 + *2 */ +#define TIMES24(_X_) (((_X_) << 4) + ((_X_) << 3)) /* *16 + *8 */ +#define TIMES60(_X_) ((((_X_) << 4) - (_X_)) << 2) /* *(16 - 1) *4 */ +#define abs(_x_) (((_x_) < 0) ? -(_x_) : (_x_)) + +/* + * parser related return/error codes + */ +#define CVT_MASK 0x0000000F /* conversion exit code */ +#define CVT_NONE 0x00000001 /* format not applicable */ +#define CVT_FAIL 0x00000002 /* conversion failed - error code returned */ +#define CVT_OK 0x00000004 /* conversion succeeded */ +#define CVT_BADFMT 0x00000010 /* general format error - (unparsable) */ +#define CVT_BADDATE 0x00000020 /* invalid date */ +#define CVT_BADTIME 0x00000040 /* invalid time */ + +/* + * DCF77 raw time code + * + * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig + * und Berlin, Maerz 1989 + * + * Timecode transmission: + * AM: + * time marks are send every second except for the second before the + * next minute mark + * time marks consist of a reduction of transmitter power to 25% + * of the nominal level + * the falling edge is the time indication (on time) + * time marks of a 100ms duration constitute a logical 0 + * time marks of a 200ms duration constitute a logical 1 + * FM: + * see the spec. (basically a (non-)inverted psuedo random phase shift) + * + * Encoding: + * Second Contents + * 0 - 10 AM: free, FM: 0 + * 11 - 14 free + * 15 R - alternate antenna + * 16 A1 - expect zone change (1 hour before) + * 17 - 18 Z1,Z2 - time zone + * 0 0 illegal + * 0 1 MEZ (MET) + * 1 0 MESZ (MED, MET DST) + * 1 1 illegal + * 19 A2 - expect leap insertion/deletion (1 hour before) + * 20 S - start of time code (1) + * 21 - 24 M1 - BCD (lsb first) Minutes + * 25 - 27 M10 - BCD (lsb first) 10 Minutes + * 28 P1 - Minute Parity (even) + * 29 - 32 H1 - BCD (lsb first) Hours + * 33 - 34 H10 - BCD (lsb first) 10 Hours + * 35 P2 - Hour Parity (even) + * 36 - 39 D1 - BCD (lsb first) Days + * 40 - 41 D10 - BCD (lsb first) 10 Days + * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday) + * 45 - 49 MO - BCD (lsb first) Month + * 50 MO0 - 10 Months + * 51 - 53 Y1 - BCD (lsb first) Years + * 54 - 57 Y10 - BCD (lsb first) 10 Years + * 58 P3 - Date Parity (even) + * 59 - usually missing (minute indication), except for leap insertion + */ + +static struct rawdcfcode +{ + char offset; /* start bit */ +} rawdcfcode[] = +{ + { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 }, + { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 } +}; + +#define DCF_M 0 +#define DCF_R 1 +#define DCF_A1 2 +#define DCF_Z 3 +#define DCF_A2 4 +#define DCF_S 5 +#define DCF_M1 6 +#define DCF_M10 7 +#define DCF_P1 8 +#define DCF_H1 9 +#define DCF_H10 10 +#define DCF_P2 11 +#define DCF_D1 12 +#define DCF_D10 13 +#define DCF_DW 14 +#define DCF_MO 15 +#define DCF_MO0 16 +#define DCF_Y1 17 +#define DCF_Y10 18 +#define DCF_P3 19 + +static struct partab +{ + char offset; /* start bit of parity field */ +} partab[] = +{ + { 21 }, { 29 }, { 36 }, { 59 } +}; + +static struct dcfparam +{ + unsigned char onebits[60]; + unsigned char zerobits[60]; +} dcfparam = +{ + "###############RADMLS1248124P124812P1248121241248112481248P", /* 'ONE' representation */ + "--------------------s-------p------p----------------------p" /* 'ZERO' representation */ +}; + +#define DCF_P_P1 0 +#define DCF_P_P2 1 +#define DCF_P_P3 2 + +#define DCF_Z_MET 0x2 +#define DCF_Z_MED 0x1 + +static unsigned long ext_bf(buf, idx) + register unsigned char *buf; + register int idx; +{ + register unsigned long sum = 0; + register int i, first; + + first = rawdcfcode[idx].offset; + + for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--) + { + sum <<= 1; + sum |= (buf[i] != dcfparam.zerobits[i]); + } + return sum; +} + +static unsigned pcheck(buf, idx) + register unsigned char *buf; + register int idx; +{ + register int i,last; + register unsigned psum = 1; + + last = partab[idx+1].offset; + + for (i = partab[idx].offset; i < last; i++) + psum ^= (buf[i] != dcfparam.zerobits[i]); + + return psum; +} + +static unsigned long convert_rawdcf(buffer, size, clock) + register unsigned char *buffer; + register int size; + register clocktime_t *clock; +{ + if (size < 57) + { + PRINTF("%-30s", "*** INCOMPLETE"); + return CVT_NONE; + } + + /* + * check Start and Parity bits + */ + if ((ext_bf(buffer, DCF_S) == 1) && + pcheck(buffer, DCF_P_P1) && + pcheck(buffer, DCF_P_P2) && + pcheck(buffer, DCF_P_P3)) + { + /* + * buffer OK + */ + + clock->flags = 0; + clock->usecond= 0; + clock->second = 0; + clock->minute = ext_bf(buffer, DCF_M10); + clock->minute = TIMES10(clock->minute) + ext_bf(buffer, DCF_M1); + clock->hour = ext_bf(buffer, DCF_H10); + clock->hour = TIMES10(clock->hour) + ext_bf(buffer, DCF_H1); + clock->day = ext_bf(buffer, DCF_D10); + clock->day = TIMES10(clock->day) + ext_bf(buffer, DCF_D1); + clock->month = ext_bf(buffer, DCF_MO0); + clock->month = TIMES10(clock->month) + ext_bf(buffer, DCF_MO); + clock->year = ext_bf(buffer, DCF_Y10); + clock->year = TIMES10(clock->year) + ext_bf(buffer, DCF_Y1); + clock->wday = ext_bf(buffer, DCF_DW); + + switch (ext_bf(buffer, DCF_Z)) + { + case DCF_Z_MET: + clock->utcoffset = -60; + break; + + case DCF_Z_MED: + clock->flags |= DCFB_DST; + clock->utcoffset = -120; + break; + + default: + PRINTF("%-30s", "*** BAD TIME ZONE"); + return CVT_FAIL|CVT_BADFMT; + } + + if (ext_bf(buffer, DCF_A1)) + clock->flags |= DCFB_ANNOUNCE; + + if (ext_bf(buffer, DCF_A2)) + clock->flags |= DCFB_LEAP; + + if (ext_bf(buffer, DCF_R)) + clock->flags |= DCFB_ALTERNATE; + + return CVT_OK; + } + else + { + /* + * bad format - not for us + */ + PRINTF("%-30s", "*** BAD FORMAT (invalid/parity)"); + return CVT_FAIL|CVT_BADFMT; + } +} + +/* + * raw dcf input routine - fix up 50 baud + * characters for 1/0 decision + */ +static unsigned long cvt_rawdcf(buffer, size, clock) + register unsigned char *buffer; + register int size; + register clocktime_t *clock; +{ + register unsigned char *s = buffer; + register unsigned char *e = buffer + size; + register unsigned char *b = dcfparam.onebits; + register unsigned char *c = dcfparam.zerobits; + register unsigned rtc = CVT_NONE; + register unsigned int i, lowmax, highmax, cutoff, span; +#define BITS 9 + unsigned char histbuf[BITS]; + /* + * the input buffer contains characters with runs of consecutive + * bits set. These set bits are an indication of the DCF77 pulse + * length. We assume that we receive the pulse at 50 Baud. Thus + * a 100ms pulse would generate a 4 bit train (20ms per bit and + * start bit) + * a 200ms pulse would create all zeroes (and probably a frame error) + */ + + for (i = 0; i < BITS; i++) + { + histbuf[i] = 0; + } + + cutoff = 0; + lowmax = 0; + + while (s < e) + { + register unsigned int ch = *s ^ 0xFF; + /* + * these lines are left as an excercise to the reader 8-) + */ + if (!((ch+1) & ch) || !*s) + { + + for (i = 0; ch; i++) + { + ch >>= 1; + } + + *s = i; + histbuf[i]++; + cutoff += i; + lowmax++; + } + else + { + dprintf(("parse: cvt_rawdcf: character check for 0x%x@%d FAILED\n", *s, s - buffer)); + *s = ~0; + rtc = CVT_FAIL|CVT_BADFMT; + } + s++; + } + + if (lowmax) + { + cutoff /= lowmax; + } + else + { + cutoff = 4; /* doesn't really matter - it'll fail anyway, but gives error output */ + } + + dprintf(("parse: cvt_rawdcf: average bit count: %d\n", cutoff)); + + lowmax = 0; + highmax = 0; + + dprintf(("parse: cvt_rawdcf: histogram:")); + for (i = 0; i <= cutoff; i++) + { + lowmax+=histbuf[i] * i; + highmax += histbuf[i]; + dprintf((" %d", histbuf[i])); + } + dprintf((" <M>")); + + lowmax += highmax / 2; + + if (highmax) + { + lowmax /= highmax; + } + else + { + lowmax = 0; + } + + highmax = 0; + cutoff = 0; + + for (; i < BITS; i++) + { + highmax+=histbuf[i] * i; + cutoff +=histbuf[i]; + dprintf((" %d", histbuf[i])); + } + dprintf(("\n")); + + if (cutoff) + { + highmax /= cutoff; + } + else + { + highmax = BITS-1; + } + + span = cutoff = lowmax; + for (i = lowmax; i <= highmax; i++) + { + if (histbuf[cutoff] > histbuf[i]) + { + cutoff = i; + span = i; + } + else + if (histbuf[cutoff] == histbuf[i]) + { + span = i; + } + } + + cutoff = (cutoff + span) / 2; + + dprintf(("parse: cvt_rawdcf: lower maximum %d, higher maximum %d, cutoff %d\n", lowmax, highmax, cutoff)); + + s = buffer; + while ((s < e) && *c && *b) + { + if (*s == (unsigned char)~0) + { + *s = '?'; + } + else + { + *s = (*s >= cutoff) ? *b : *c; + } + s++; + b++; + c++; + } + + return (rtc == CVT_NONE) ? convert_rawdcf(buffer, size, clock) : rtc; +} + +time_t +parse_to_unixtime(clock, cvtrtc) + register clocktime_t *clock; + register unsigned long *cvtrtc; +{ +#define SETRTC(_X_) { if (cvtrtc) *cvtrtc = (_X_); } + static int days_of_month[] = + { + 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + register int i; + time_t t; + + if (clock->year < 100) + clock->year += 1900; + + if (clock->year < 1970) + clock->year += 100; /* XXX this will do it till <2070 */ + + if (clock->year < 0) + { + SETRTC(CVT_FAIL|CVT_BADDATE); + return -1; + } + + /* + * sorry, slow section here - but it's not time critical anyway + */ + t = (clock->year - 1970) * 365; + t += (clock->year >> 2) - (1970 >> 2); + t -= clock->year / 400 - 1970 / 400; + + /* month */ + if (clock->month <= 0 || clock->month > 12) + { + SETRTC(CVT_FAIL|CVT_BADDATE); + return -1; /* bad month */ + } + /* adjust leap year */ + if (clock->month >= 3 && dysize(clock->year) == 366) + t++; + + for (i = 1; i < clock->month; i++) + { + t += days_of_month[i]; + } + /* day */ + if (clock->day < 1 || ((clock->month == 2 && dysize(clock->year) == 366) ? + clock->day > 29 : clock->day > days_of_month[clock->month])) + { + SETRTC(CVT_FAIL|CVT_BADDATE); + return -1; /* bad day */ + } + + t += clock->day - 1; + /* hour */ + if (clock->hour < 0 || clock->hour >= 24) + { + SETRTC(CVT_FAIL|CVT_BADTIME); + return -1; /* bad hour */ + } + + t = TIMES24(t) + clock->hour; + + /* min */ + if (clock->minute < 0 || clock->minute > 59) + { + SETRTC(CVT_FAIL|CVT_BADTIME); + return -1; /* bad min */ + } + + t = TIMES60(t) + clock->minute; + /* sec */ + + t += clock->utcoffset; /* warp to UTC */ + + if (clock->second < 0 || clock->second > 60) /* allow for LEAPs */ + { + SETRTC(CVT_FAIL|CVT_BADTIME); + return -1; /* bad sec */ + } + + t = TIMES60(t) + clock->second; + /* done */ + return t; +} + +/* + * cheap half baked 1/0 decision - for interactive operation only + */ +static char type(c) +unsigned char c; +{ + c ^= 0xFF; + return (c > 0xF); +} + +static char *wday[8] = +{ + "??", + "Mo", + "Tu", + "We", + "Th", + "Fr", + "Sa", + "Su" +}; + +static char * pr_timeval(val) + struct timeval *val; +{ + static char buf[20]; + + if (val->tv_sec == 0) + sprintf(buf, "%c0.%06d", (val->tv_usec < 0) ? '-' : '+', abs(val->tv_usec)); + else + sprintf(buf, "%d.%06d", val->tv_sec, abs(val->tv_usec)); + return buf; +} + +static void set_time(offset) + struct timeval *offset; +{ + struct timeval the_time; + /*XXX*/ if (loop_filter_debug) printf("set_time: %s ", pr_timeval(offset)); + if (gettimeofday(&the_time, 0L) == -1) + { + perror("gettimeofday()"); + } + else + { + timeradd(&the_time, offset); + if (settimeofday(&the_time, 0L) == -1) + { + perror("settimeofday()"); + } + } +} + +static void adj_time(offset) + register long offset; +{ + struct timeval time_offset; + + time_offset.tv_sec = offset / 1000000; + time_offset.tv_usec = offset % 1000000; + + /*XXX*/ if (loop_filter_debug) + printf("adj_time: %d us ", offset); + if (adjtime(&time_offset, 0L) == -1) + perror("adjtime()"); +} + +static void read_drift(drift_file) + char *drift_file; +{ + FILE *df; + + df = fopen(drift_file, "r"); + if (df != NULL) + { + int idrift, fdrift; + + fscanf(df, "%4d.%03d", &idrift, &fdrift); + fclose(df); + /*XXX*/ if (loop_filter_debug) + printf("read_drift: %d.%03d ppm ", idrift, fdrift); + + drift_comp = idrift << USECSCALE; + fdrift = (fdrift << USECSCALE) / 1000; + drift_comp += fdrift & (1<<USECSCALE); + /*XXX*/ if (loop_filter_debug) + printf("read_drift: drift_comp %d ", drift_comp); + } +} + +static void update_drift(drift_file, offset, reftime) + char *drift_file; + long offset; + time_t reftime; +{ + FILE *df; + + df = fopen(drift_file, "w"); + if (df != NULL) + { + int idrift = R_SHIFT(drift_comp, USECSCALE); + int fdrift = drift_comp & ((1<<USECSCALE)-1); + + /*XXX*/ if (loop_filter_debug) + printf("update_drift: drift_comp %d ", drift_comp); + fdrift = (fdrift * 1000) / (1<<USECSCALE); + fprintf(df, "%4d.%03d %c%d.%06d %.24s\n", idrift, fdrift, + (offset < 0) ? '-' : '+', abs(offset) / 1000000, abs(offset) % 1000000, + asctime(localtime(&reftime))); + fclose(df); + /*XXX*/ if (loop_filter_debug) + printf("update_drift: %d.%03d ppm ", idrift, fdrift); + } +} + +static void adjust_clock(offset, drift_file, reftime) + struct timeval *offset; + char *drift_file; + time_t reftime; +{ + struct timeval toffset; + register long usecoffset; + int tmp; + + if (skip_adjust) + { + skip_adjust = 0; + return; + } + + toffset = *offset; + toffset.tv_sec = abs(toffset.tv_sec); + toffset.tv_usec = abs(toffset.tv_usec); + if (timercmp(&toffset, &max_adj_offset, >)) + { + /* + * hopeless - set the clock - and clear the timing + */ + set_time(offset); + clock_adjust = 0; + skip_adjust = 1; + return; + } + + usecoffset = offset->tv_sec * 1000000 + offset->tv_usec; + + clock_adjust = R_SHIFT(usecoffset, TIMECONSTANT); /* adjustment to make for next period */ + + tmp = 0; + while (adjustments > (1 << tmp)) + tmp++; + adjustments = 0; + if (tmp > FREQ_WEIGHT) + tmp = FREQ_WEIGHT; + + drift_comp += R_SHIFT(usecoffset << USECSCALE, TIMECONSTANT+TIMECONSTANT+FREQ_WEIGHT-tmp); + + if (drift_comp > MAX_DRIFT) /* clamp into interval */ + drift_comp = MAX_DRIFT; + else + if (drift_comp < -MAX_DRIFT) + drift_comp = -MAX_DRIFT; + + update_drift(drift_file, usecoffset, reftime); + /*XXX*/ if (loop_filter_debug) + printf("clock_adjust: %s, clock_adjust %d, drift_comp %d(%d) ", + pr_timeval(offset), R_SHIFT(clock_adjust, USECSCALE) , R_SHIFT(drift_comp, USECSCALE), drift_comp); +} + +static void periodic_adjust() +{ + register long adjustment; + + adjustments++; + + adjustment = R_SHIFT(clock_adjust, PHASE_WEIGHT); + + clock_adjust -= adjustment; + + adjustment += R_SHIFT(drift_comp, USECSCALE+ADJINTERVAL); + + adj_time(adjustment); +} + +static void tick() +{ + static unsigned long last_notice; + +#ifndef SV_ONSTACK + (void)signal(SIGALRM, tick); +#endif + + periodic_adjust(); + + ticks += 1<<ADJINTERVAL; + + if ((sync_state == NO_SYNC) && ((ticks - last_sync) > MAX_UNSYNC) && + ((last_notice - ticks) > NOTICE_INTERVAL)) + { + syslog(LOG_NOTICE, "still not synchronized - check receiver/signal"); + last_notice = ticks; + } + +#ifndef ITIMER_REAL + (void) alarm(1<<ADJINTERVAL); +#endif +} + +static void detach() +{ + int s; + + if (fork()) + exit(0); + + for (s = 0; s < 3; s++) + (void) close(s); + (void) open("/", 0); + (void) dup2(0, 1); + (void) dup2(0, 2); + +#if defined(NTP_POSIX_SOURCE) || defined(_POSIX_) + (void) setsid(); +#else /* _POSIX_ */ +#ifndef BSD + (void) setpgrp(); +#else /* BSD */ + (void) setpgrp(0, getpid()); +#endif /* BSD */ +#endif /* _POSIX_ */ +#if defined(hpux) + if (fork()) + exit(0); +#endif /* hpux */ +} + +static void usage(program) + char *program; +{ + fprintf(stderr, "usage: %s [-f] [-l] [-t] [-i] [-o] [-d <drift_file>] <device>\n", program); + fprintf(stderr, "\t-i interactive\n"); + fprintf(stderr, "\t-t trace (print all datagrams)\n"); + fprintf(stderr, "\t-f print all databits (includes PTB private data)\n"); + fprintf(stderr, "\t-l print loop filter debug information\n"); + fprintf(stderr, "\t-o print offet average for current minute\n"); + fprintf(stderr, "\t-d <drift_file> specify alternate drift file\n"); +} + +int +main(argc, argv) + int argc; + char **argv; +{ + unsigned char c; + char **a = argv; + int ac = argc; + char *file = NULL; + char *drift_file = "/etc/dcfd.drift"; + int fd; + int offset = 15; + int offsets = 0; + int trace = 0; + int errs = 0; + + while (--ac) + { + char *arg = *++a; + if (*arg == '-') + while ((c = *++arg)) + switch (c) + { + case 't': + trace = 1; + interactive = 1; + break; + + case 'f': + offset = 0; + interactive = 1; + break; + + case 'l': + loop_filter_debug = 1; + offsets = 1; + interactive = 1; + break; + + case 'o': + offsets = 1; + interactive = 1; + break; + + case 'i': + interactive = 1; + break; + + case 'd': + if (ac > 1) + { + drift_file = *++a; + ac--; + } + else + { + fprintf(stderr, "%s: -d requires file name argument\n", argv[0]); + errs=1; + } + break; + + default: + fprintf(stderr, "%s: unknown option -%c\n", argv[0], c); + errs=1; + break; + } + else + if (file == NULL) + file = arg; + else + { + fprintf(stderr, "%s: device specified twice\n", argv[0]); + errs=1; + } + } + + if (errs) + { + usage(argv[0]); + exit(1); + } + else + if (file == NULL) + { + fprintf(stderr, "%s: device not specified\n", argv[0]); + usage(argv[0]); + exit(1); + } + + errs = LINES+1; + + fd = open(file, O_RDONLY); + if (fd == -1) + { + perror(file); + exit(1); + } + else + { + int i, rrc; + struct timeval t, tt, tlast; + struct timeval timeout; + struct timeval phase; + struct timeval time_offset; + char pbuf[61]; /* printable version */ + char buf[61]; /* raw data */ + clocktime_t clock; /* wall clock time */ + time_t utc_time = 0; + long usecerror = 0; + long lasterror = 0; +#if defined(HAVE_TERMIOS) || defined(STREAM) + struct termios term; +#endif +#if defined(HAVE_TERMIO) || defined(HAVE_SYSV_TTYS) + struct termio term; +#endif + int rtc = CVT_NONE; + + timeout.tv_sec = 1; + timeout.tv_usec = 500000; + + phase.tv_sec = 0; + phase.tv_usec = 230000; + + if (TTY_GETATTR(fd, &term) == -1) + { + perror("tcgetattr"); + exit(1); + } + + memset(term.c_cc, 0, sizeof(term.c_cc)); + term.c_cc[VMIN] = 1; + term.c_cflag = B50|CS8|CREAD|CLOCAL; + term.c_iflag = 0; + term.c_oflag = 0; + term.c_lflag = 0; + + if (TTY_SETATTR(fd, &term) == -1) + { + perror("tcsetattr"); + exit(1); + } + + if (!interactive) + detach(); + +#ifdef LOG_DAEMON + openlog("dcfd", LOG_PID, LOG_DAEMON); +#else + openlog("dcfd", LOG_PID); +#endif + +#ifdef SV_ONSTACK + { + struct sigvec vec; + + vec.sv_handler = tick; + vec.sv_mask = 0; + vec.sv_flags = 0; + + if (sigvec(SIGALRM, &vec, (struct sigvec *)0) == -1) + { + syslog(LOG_ERR, "sigvec(SIGALRM): %m"); + exit(1); + } + } +#else + (void) signal(SIGALRM, tick); +#endif + +#ifdef ITIMER_REAL + { + struct itimerval it; + + it.it_interval.tv_sec = 1<<ADJINTERVAL; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = 1<<ADJINTERVAL; + it.it_value.tv_usec = 0; + + if (setitimer(ITIMER_REAL, &it, (struct itimerval *)0) == -1) + { + syslog(LOG_ERR, "setitimer: %m"); + exit(1); + } + } +#else + (void) alarm(1<<ADJINTERVAL); +#endif + + PRINTF(" DCF77 monitor - Copyright 1993, Frank Kardel\n\n"); + + pbuf[60] = '\0'; + for ( i = 0; i < 60; i++) + pbuf[i] = '.'; + + read_drift(drift_file); + + gettimeofday(&tlast, 0L); + i = 0; + do + { + while ((rrc = read(fd, &c, 1)) == 1) + { + gettimeofday(&t, 0L); + tt = t; + timersub(&t, &tlast); + + if (errs > LINES) + { + PRINTF(" %s", &"PTB private....RADMLSMin....PHour..PMDay..DayMonthYear....P\n"[offset]); + PRINTF(" %s", &"---------------RADMLS1248124P124812P1248121241248112481248P\n"[offset]); + errs = 0; + } + + if (timercmp(&t, &timeout, >)) + { + PRINTF("%c %.*s ", pat[i % (sizeof(pat)-1)], 59 - offset, &pbuf[offset]); + + if ((rtc = cvt_rawdcf(buf, i, &clock)) != CVT_OK) + { + PRINTF("\n"); + if (sync_state == SYNC) + { + sync_state = NO_SYNC; + syslog(LOG_DEBUG, "DCF77 reception lost"); + } + errs++; + } + + buf[0] = c; + + if (((c^0xFF)+1) & (c^0xFF)) + pbuf[0] = '?'; + else + pbuf[0] = type(c) ? '#' : '-'; + + for ( i = 1; i < 60; i++) + pbuf[i] = '.'; + + i = 0; + } + else + { + buf[i] = c; + + /* + * initial guess (usually correct) + */ + if (((c^0xFF)+1) & (c^0xFF)) + pbuf[i] = '?'; + else + pbuf[i] = type(c) ? '#' : '-'; + + PRINTF("%c %.*s ", pat[i % (sizeof(pat)-1)], 59 - offset, &pbuf[offset]); + } + + if (i == 0 && rtc == CVT_OK) + { + if ((utc_time = parse_to_unixtime(&clock, &rtc)) == -1) + { + PRINTF("*** BAD CONVERSION\n"); + } + + usecerror = 0; + } + + if (rtc == CVT_OK) + { + if (trace && (i == 0)) + { + PRINTF("\r %.*s ", 59 - offset, &buf[offset]); + } + + if (i == 0) + { + time_offset.tv_sec = lasterror / 1000000; + time_offset.tv_usec = lasterror % 1000000; + adjust_clock(&time_offset, drift_file, utc_time+i); + last_sync = ticks; + if (sync_state == NO_SYNC) + { + syslog(LOG_INFO, "receiving DCF77"); + } + sync_state = SYNC; + } + + time_offset.tv_sec = utc_time + i; + time_offset.tv_usec = 0; + + timeradd(&time_offset, &phase); + + usecerror += (time_offset.tv_sec - tt.tv_sec) * 1000000 + time_offset.tv_usec + -tt.tv_usec; + + PRINTF(offsets ? "%s, %2d:%02d:%02d, %d.%02d.%02d, <%s%s%s%s> (%c%d.%06ds)" : + "%s, %2d:%02d:%02d, %d.%02d.%02d, <%s%s%s%s>", + wday[clock.wday], + clock.hour, clock.minute, i, clock.day, clock.month, + clock.year, + (clock.flags & DCFB_ALTERNATE) ? "R" : "_", + (clock.flags & DCFB_ANNOUNCE) ? "A" : "_", + (clock.flags & DCFB_DST) ? "D" : "_", + (clock.flags & DCFB_LEAP) ? "L" : "_", + (lasterror < 0) ? '-' : '+', abs(lasterror) / 1000000, abs(lasterror) % 1000000 + ); + + if (trace && (i == 0)) + { + PRINTF("\n"); + errs++; + } + lasterror = usecerror / (i+1); + } + + PRINTF("\r"); + + if (i < 60) + { + i++; + } + + tlast = tt; + + if (interactive) + fflush(stdout); + } + } while ((rrc == -1) && (errno == EINTR)); + + syslog(LOG_ERR, "TERMINATING - cannot read from device %s", file); + + (void)close(fd); + } + + closelog(); + + return 0; +} diff --git a/usr.sbin/xntpd/parse/util/parsetest.c b/usr.sbin/xntpd/parse/util/parsetest.c new file mode 100644 index 000000000000..4f26a0a6a98a --- /dev/null +++ b/usr.sbin/xntpd/parse/util/parsetest.c @@ -0,0 +1,268 @@ +/* + * /src/NTP/REPOSITORY/v3/kernel/parsetest.c,v 3.4 1993/03/17 17:16:57 kardel Exp + * + * parsetest.c,v 3.4 1993/03/17 17:16:57 kardel Exp + * + * Copyright (c) 1989,1990,1991,1992,1993 + * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * parsetest.c,v + * Revision 3.4 1993/03/17 17:16:57 kardel + * DEC OSF/1 ALPHA Integration - 930314 + * + * Revision 3.3 1993/01/18 09:24:33 kardel + * updated copyright conditions in conjunction with + * conditions set up in the COPYRIGHT file + * + * Revision 3.2 1993/01/17 13:43:00 kardel + * 1993 initial update + * + */ + +#ifndef STREAM +ONLY STREAM OPERATION SUPPORTED +#endif + +#define PARSESTREAM /* there is no other choice - TEST HACK */ + +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/stream.h> +#include <sys/stropts.h> +#include <sys/errno.h> +#include <fcntl.h> + +#define P(X) () + +#include "ntp_fp.h" +#ifdef USE_PROTOTYPES +#include "ntp_stdlib.h" +#endif +#include "parse.h" + +static char *strstatus(buffer, state) + char *buffer; + unsigned LONG state; +{ + static struct bits + { + unsigned LONG bit; + char *name; + } flagstrings[] = + { + { PARSEB_ANNOUNCE, "DST SWITCH WARNING" }, + { PARSEB_POWERUP, "NOT SYNCHRONIZED" }, + { PARSEB_NOSYNC, "TIME CODE NOT CONFIRMED" }, + { PARSEB_DST, "DST" }, + { PARSEB_UTC, "UTC DISPLAY" }, + { PARSEB_LEAP, "LEAP WARNING" }, + { PARSEB_LEAPSECOND, "LEAP SECOND" }, + { PARSEB_ALTERNATE,"ALTERNATE ANTENNA" }, + { PARSEB_TIMECODE, "TIME CODE" }, + { PARSEB_PPS, "PPS" }, + { PARSEB_POSITION, "POSITION" }, + { 0 } + }; + + static struct sbits + { + unsigned LONG bit; + char *name; + } sflagstrings[] = + { + { PARSEB_S_LEAP, "LEAP INDICATION" }, + { PARSEB_S_PPS, "PPS SIGNAL" }, + { PARSEB_S_ANTENNA, "ANTENNA" }, + { PARSEB_S_POSITION, "POSITION" }, + { 0 } + }; + int i; + + *buffer = '\0'; + + i = 0; + while (flagstrings[i].bit) + { + if (flagstrings[i].bit & state) + { + if (buffer[0]) + strcat(buffer, "; "); + strcat(buffer, flagstrings[i].name); + } + i++; + } + + if (state & (PARSEB_S_LEAP|PARSEB_S_ANTENNA|PARSEB_S_PPS|PARSEB_S_POSITION)) + { + register char *s, *t; + + if (buffer[0]) + strcat(buffer, "; "); + + strcat(buffer, "("); + + t = s = buffer + strlen(buffer); + + i = 0; + while (sflagstrings[i].bit) + { + if (sflagstrings[i].bit & state) + { + if (t != s) + { + strcpy(t, "; "); + t += 2; + } + + strcpy(t, sflagstrings[i].name); + t += strlen(t); + } + i++; + } + strcpy(t, ")"); + } + return buffer; +} + +/*-------------------------------------------------- + * convert a status flag field to a string + */ +static char *parsestatus(state, buffer) + unsigned LONG state; + char *buffer; +{ + static struct bits + { + unsigned LONG bit; + char *name; + } flagstrings[] = + { + { CVT_OK, "CONVERSION SUCCESSFUL" }, + { CVT_NONE, "NO CONVERSION" }, + { CVT_FAIL, "CONVERSION FAILED" }, + { CVT_BADFMT, "ILLEGAL FORMAT" }, + { CVT_BADDATE, "DATE ILLEGAL" }, + { CVT_BADTIME, "TIME ILLEGAL" }, + { 0 } + }; + int i; + + *buffer = '\0'; + + i = 0; + while (flagstrings[i].bit) + { + if (flagstrings[i].bit & state) + { + if (buffer[0]) + strcat(buffer, "; "); + strcat(buffer, flagstrings[i].name); + } + i++; + } + + return buffer; +} + +int +main(argc, argv) + int argc; + char **argv; +{ + if (argc != 2) + { + fprintf(stderr,"usage: %s <parse-device>\n", argv[0]); + exit(1); + } + else + { + int fd; + + fd = open(argv[1], O_RDWR); + if (fd == -1) + { + perror(argv[1]); + exit(1); + } + else + { + parsectl_t dct; + parsetime_t parsetime; + struct strioctl strioc; + + printf("parsetest.c,v 3.9 1993/10/10 21:18:49 kardel Exp\n"); + + while (ioctl(fd, I_POP, 0) == 0) + ; + + if (ioctl(fd, I_PUSH, "parse") == -1) + { + perror("ioctl(I_PUSH,\"parse\")"); + exit(1); + } + + strioc.ic_cmd = PARSEIOC_GETSTAT; + strioc.ic_timout = 0; + strioc.ic_dp = (char *)&dct; + strioc.ic_len = sizeof(parsectl_t); + + if (ioctl(fd, I_STR, &strioc) == -1) + { + perror("ioctl(fd, I_STR(PARSEIOC_GETSTAT))"); + exit(1); + } + printf("parse status: %04x\n", dct.parsestatus.flags); + + dct.parsestatus.flags |= PARSE_STAT_FILTER; + strioc.ic_cmd = PARSEIOC_SETSTAT; + + if (ioctl(fd, I_STR, &strioc) == -1) + { + perror("ioctl(fd, I_STR(PARSEIOC_SETSTAT))"); + exit(1); + } + printf("PARSE clock FILTERMODE\n"); + + if (ioctl(fd, I_STR, &strioc) == -1) + { + perror("ioctl(fd, I_STR(PARSEIOC_GETSTAT))"); + exit(1); + } + printf("parse status: %04x\n", dct.parsestatus.flags); + + while (read(fd, &parsetime, sizeof(parsetime)) == sizeof(parsetime)) + { + char tmp[200], tmp1[200], tmp2[60]; + + strncpy(tmp, asctime(localtime(&parsetime.parse_time.tv.tv_sec)), 30); + strncpy(tmp1,asctime(localtime(&parsetime.parse_stime.tv.tv_sec)), 30); + strncpy(tmp2,asctime(localtime(&parsetime.parse_ptime.tv.tv_sec)), 30); + tmp[24] = '\0'; + tmp1[24] = '\0'; + tmp2[24] = '\0'; + + printf("%s (+%06dus) %s PPS: %s (+%06dus), ", tmp1, parsetime.parse_stime.tv.tv_usec, tmp, tmp2, parsetime.parse_ptime.tv.tv_usec); + + strstatus(tmp, parsetime.parse_state); + printf("state: 0x%x (%s) error: %dus, dispersion: %dus, Status: 0x%x (%s)\n", + parsetime.parse_state, + tmp, + parsetime.parse_usecerror, + parsetime.parse_usecdisp, + parsetime.parse_status, + parsestatus(parsetime.parse_status, tmp1)); + } + + close(fd); + } + } + return 0; +} diff --git a/usr.sbin/xntpd/parse/util/testdcf.c b/usr.sbin/xntpd/parse/util/testdcf.c new file mode 100644 index 000000000000..e07bc0c1cc01 --- /dev/null +++ b/usr.sbin/xntpd/parse/util/testdcf.c @@ -0,0 +1,485 @@ +/* + * /src/NTP/REPOSITORY/v3/parse/util/testdcf.c,v 3.7 1993/10/10 22:44:48 kardel Exp + * + * testdcf.c,v 3.7 1993/10/10 22:44:48 kardel Exp + * + * simple DCF77 100/200ms pulse test program (via 50Baud serial line) + * + * Copyright (c) 1993 + * Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * This program may not be sold or used for profit without prior + * written consent of the author. + */ + +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <termios.h> +#include <sys/types.h> +#ifdef STREAM +#include <sys/stream.h> +#include <sys/stropts.h> +#endif +#include <sys/time.h> + +#include "ntp_stdlib.h" + +/* + * state flags + */ +#define DCFB_ANNOUNCE 0x0001 /* switch time zone warning (DST switch) */ +#define DCFB_DST 0x0002 /* DST in effect */ +#define DCFB_LEAP 0x0004 /* LEAP warning (1 hour prior to occurence) */ +#define DCFB_ALTERNATE 0x0008 /* alternate antenna used */ + +struct clocktime /* clock time broken up from time code */ +{ + long wday; + long day; + long month; + long year; + long hour; + long minute; + long second; + long usecond; + long utcoffset; /* in minutes */ + long flags; /* current clock status */ +}; + +typedef struct clocktime clocktime_t; + +#define TIMES10(_X_) (((_X_) << 3) + ((_X_) << 1)) + +/* + * parser related return/error codes + */ +#define CVT_MASK 0x0000000F /* conversion exit code */ +#define CVT_NONE 0x00000001 /* format not applicable */ +#define CVT_FAIL 0x00000002 /* conversion failed - error code returned */ +#define CVT_OK 0x00000004 /* conversion succeeded */ +#define CVT_BADFMT 0x00000010 /* general format error - (unparsable) */ + +/* + * DCF77 raw time code + * + * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig + * und Berlin, Maerz 1989 + * + * Timecode transmission: + * AM: + * time marks are send every second except for the second before the + * next minute mark + * time marks consist of a reduction of transmitter power to 25% + * of the nominal level + * the falling edge is the time indication (on time) + * time marks of a 100ms duration constitute a logical 0 + * time marks of a 200ms duration constitute a logical 1 + * FM: + * see the spec. (basically a (non-)inverted psuedo random phase shift) + * + * Encoding: + * Second Contents + * 0 - 10 AM: free, FM: 0 + * 11 - 14 free + * 15 R - alternate antenna + * 16 A1 - expect zone change (1 hour before) + * 17 - 18 Z1,Z2 - time zone + * 0 0 illegal + * 0 1 MEZ (MET) + * 1 0 MESZ (MED, MET DST) + * 1 1 illegal + * 19 A2 - expect leap insertion/deletion (1 hour before) + * 20 S - start of time code (1) + * 21 - 24 M1 - BCD (lsb first) Minutes + * 25 - 27 M10 - BCD (lsb first) 10 Minutes + * 28 P1 - Minute Parity (even) + * 29 - 32 H1 - BCD (lsb first) Hours + * 33 - 34 H10 - BCD (lsb first) 10 Hours + * 35 P2 - Hour Parity (even) + * 36 - 39 D1 - BCD (lsb first) Days + * 40 - 41 D10 - BCD (lsb first) 10 Days + * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday) + * 45 - 49 MO - BCD (lsb first) Month + * 50 MO0 - 10 Months + * 51 - 53 Y1 - BCD (lsb first) Years + * 54 - 57 Y10 - BCD (lsb first) 10 Years + * 58 P3 - Date Parity (even) + * 59 - usually missing (minute indication), except for leap insertion + */ + +static struct rawdcfcode +{ + char offset; /* start bit */ +} rawdcfcode[] = +{ + { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 }, + { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 } +}; + +#define DCF_M 0 +#define DCF_R 1 +#define DCF_A1 2 +#define DCF_Z 3 +#define DCF_A2 4 +#define DCF_S 5 +#define DCF_M1 6 +#define DCF_M10 7 +#define DCF_P1 8 +#define DCF_H1 9 +#define DCF_H10 10 +#define DCF_P2 11 +#define DCF_D1 12 +#define DCF_D10 13 +#define DCF_DW 14 +#define DCF_MO 15 +#define DCF_MO0 16 +#define DCF_Y1 17 +#define DCF_Y10 18 +#define DCF_P3 19 + +static struct partab +{ + char offset; /* start bit of parity field */ +} partab[] = +{ + { 21 }, { 29 }, { 36 }, { 59 } +}; + +#define DCF_P_P1 0 +#define DCF_P_P2 1 +#define DCF_P_P3 2 + +#define DCF_Z_MET 0x2 +#define DCF_Z_MED 0x1 + +static unsigned long ext_bf(buf, idx) + register char *buf; + register int idx; +{ + register unsigned long sum = 0; + register int i, first; + + first = rawdcfcode[idx].offset; + + for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--) + { + sum <<= 1; + sum |= (buf[i] != '-'); + } + return sum; +} + +static unsigned pcheck(buf, idx) + register char *buf; + register int idx; +{ + register int i,last; + register unsigned psum = 1; + + last = partab[idx+1].offset; + + for (i = partab[idx].offset; i < last; i++) + psum ^= (buf[i] != '-'); + + return psum; +} + +static unsigned long convert_rawdcf(buffer, size, clock) + register unsigned char *buffer; + register int size; + register clocktime_t *clock; +{ + if (size < 57) + { + printf("%-30s", "*** INCOMPLETE"); + return CVT_NONE; + } + + /* + * check Start and Parity bits + */ + if ((ext_bf(buffer, DCF_S) == 1) && + pcheck(buffer, DCF_P_P1) && + pcheck(buffer, DCF_P_P2) && + pcheck(buffer, DCF_P_P3)) + { + /* + * buffer OK + */ + + clock->flags = 0; + clock->usecond= 0; + clock->second = 0; + clock->minute = ext_bf(buffer, DCF_M10); + clock->minute = TIMES10(clock->minute) + ext_bf(buffer, DCF_M1); + clock->hour = ext_bf(buffer, DCF_H10); + clock->hour = TIMES10(clock->hour) + ext_bf(buffer, DCF_H1); + clock->day = ext_bf(buffer, DCF_D10); + clock->day = TIMES10(clock->day) + ext_bf(buffer, DCF_D1); + clock->month = ext_bf(buffer, DCF_MO0); + clock->month = TIMES10(clock->month) + ext_bf(buffer, DCF_MO); + clock->year = ext_bf(buffer, DCF_Y10); + clock->year = TIMES10(clock->year) + ext_bf(buffer, DCF_Y1); + clock->wday = ext_bf(buffer, DCF_DW); + + switch (ext_bf(buffer, DCF_Z)) + { + case DCF_Z_MET: + clock->utcoffset = -60; + break; + + case DCF_Z_MED: + clock->flags |= DCFB_DST; + clock->utcoffset = -120; + break; + + default: + printf("%-30s", "*** BAD TIME ZONE"); + return CVT_FAIL|CVT_BADFMT; + } + + if (ext_bf(buffer, DCF_A1)) + clock->flags |= DCFB_ANNOUNCE; + + if (ext_bf(buffer, DCF_A2)) + clock->flags |= DCFB_LEAP; + + if (ext_bf(buffer, DCF_R)) + clock->flags |= DCFB_ALTERNATE; + + return CVT_OK; + } + else + { + /* + * bad format - not for us + */ + printf("%-30s", "*** BAD FORMAT (invalid/parity)"); + return CVT_FAIL|CVT_BADFMT; + } +} + +char type(c) +unsigned char c; +{ + c ^= 0xFF; + return (c > 0xF); +} + +static char *wday[8] = +{ + "??", + "Mo", + "Tu", + "We", + "Th", + "Fr", + "Sa", + "Su" +}; + +static char pat[] = "-\\|/"; + +#define LINES (24-2) /* error lines after which the two headlines are repeated */ + +int +main(argc, argv) + int argc; + char **argv; +{ + if ((argc != 2) && (argc != 3)) + { + fprintf(stderr, "usage: %s [-f|-t|-ft|-tf] <device>\n", argv[0]); + exit(1); + } + else + { + unsigned char c; + char *file; + int fd; + int offset = 15; + int trace = 0; + int errs = LINES+1; + + /* + * SIMPLE(!) argument "parser" + */ + if (argc == 3) + { + if (strcmp(argv[1], "-f") == 0) + offset = 0; + if (strcmp(argv[1], "-t") == 0) + trace = 1; + if ((strcmp(argv[1], "-ft") == 0) || + (strcmp(argv[1], "-tf") == 0)) + { + offset = 0; + trace = 1; + } + file = argv[2]; + } + else + { + file = argv[1]; + } + + fd = open(file, O_RDONLY); + if (fd == -1) + { + perror(file); + exit(1); + } + else + { + int i; +#ifdef TIOCM_RTS + int on = TIOCM_RTS; +#endif + struct timeval t, tt, tlast; + char buf[61]; + clocktime_t clock; + struct termios term; + int rtc = CVT_NONE; + + if (tcgetattr(fd, &term) == -1) + { + perror("tcgetattr"); + exit(1); + } + + bzero(term.c_cc, sizeof(term.c_cc)); + term.c_cc[VMIN] = 1; + term.c_cflag = B50|CS8|CREAD|CLOCAL; + term.c_iflag = 0; + term.c_oflag = 0; + term.c_lflag = 0; + + if (tcsetattr(fd, TCSANOW, &term) == -1) + { + perror("tcsetattr"); + exit(1); + } + +#ifdef I_POP + while (ioctl(fd, I_POP, 0) == 0) + ; +#endif +#if defined(TIOCMBIC) && defined(TIOCM_RTS) + if (ioctl(fd, TIOCMBIC, (caddr_t)&on) == -1) + { + perror("TIOCM_RTS"); + } +#endif + + printf(" DCF77 monitor - Copyright 1993, Frank Kardel\n\n"); + + clock.hour = 0; + clock.minute = 0; + clock.day = 0; + clock.wday = 0; + clock.month = 0; + clock.year = 0; + clock.flags = 0; + buf[60] = '\0'; + for ( i = 0; i < 60; i++) + buf[i] = '.'; + + gettimeofday(&tlast, 0L); + i = 0; + while (read(fd, &c, 1) == 1) + { + gettimeofday(&t, 0L); + tt = t; + t.tv_sec -= tlast.tv_sec; + t.tv_usec -= tlast.tv_usec; + if (t.tv_usec < 0) + { + t.tv_usec += 1000000; + t.tv_sec -= 1; + } + + if (errs > LINES) + { + printf(" %s", &"PTB private....RADMLSMin....PHour..PMDay..DayMonthYear....P\n"[offset]); + printf(" %s", &"---------------RADMLS1248124P124812P1248121241248112481248P\n"[offset]); + errs = 0; + } + + if (t.tv_sec > 1 || + t.tv_sec == 1 && + t.tv_usec > 500000) + { + printf("%c %.*s ", pat[i % (sizeof(pat)-1)], 59 - offset, &buf[offset]); + + if ((rtc = convert_rawdcf(buf, i, &clock)) != CVT_OK) + { + printf("\n"); + clock.hour = 0; + clock.minute = 0; + clock.day = 0; + clock.wday = 0; + clock.month = 0; + clock.year = 0; + clock.flags = 0; + errs++; + } + + if (((c^0xFF)+1) & (c^0xFF)) + buf[0] = '?'; + else + buf[0] = type(c) ? '#' : '-'; + + for ( i = 1; i < 60; i++) + buf[i] = '.'; + + i = 0; + } + else + { + if (((c^0xFF)+1) & (c^0xFF)) + buf[i] = '?'; + else + buf[i] = type(c) ? '#' : '-'; + + printf("%c %.*s ", pat[i % (sizeof(pat)-1)], 59 - offset, &buf[offset]); + } + + if (rtc == CVT_OK) + { + printf("%s, %2d:%02d:%02d, %d.%02d.%02d, <%s%s%s%s>", + wday[clock.wday], + clock.hour, clock.minute, i, clock.day, clock.month, + clock.year, + (clock.flags & DCFB_ALTERNATE) ? "R" : "_", + (clock.flags & DCFB_ANNOUNCE) ? "A" : "_", + (clock.flags & DCFB_DST) ? "D" : "_", + (clock.flags & DCFB_LEAP) ? "L" : "_" + ); + if (trace && (i == 0)) + { + printf("\n"); + errs++; + } + } + + printf("\r"); + + if (i < 60) + { + i++; + } + + tlast = tt; + + fflush(stdout); + } + close(fd); + } + } + return 0; +} |