154359Sroberto/*
2280849Scy * /src/NTP/REPOSITORY/ntp4-dev/libparse/clk_trimtsip.c,v 4.19 2009/11/01 10:47:49 kardel RELEASE_20091101_A
354359Sroberto *
4280849Scy * clk_trimtsip.c,v 4.19 2009/11/01 10:47:49 kardel RELEASE_20091101_A
554359Sroberto *
6182007Sroberto * Trimble TSIP support
7182007Sroberto * Thanks to Sven Dietrich for providing test hardware
8182007Sroberto *
9280849Scy * Copyright (c) 1995-2009 by Frank Kardel <kardel <AT> ntp.org>
10282408Scy * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany
11182007Sroberto *
12182007Sroberto * Redistribution and use in source and binary forms, with or without
13182007Sroberto * modification, are permitted provided that the following conditions
14182007Sroberto * are met:
15182007Sroberto * 1. Redistributions of source code must retain the above copyright
16182007Sroberto *    notice, this list of conditions and the following disclaimer.
17182007Sroberto * 2. Redistributions in binary form must reproduce the above copyright
18182007Sroberto *    notice, this list of conditions and the following disclaimer in the
19182007Sroberto *    documentation and/or other materials provided with the distribution.
20182007Sroberto * 3. Neither the name of the author nor the names of its contributors
21182007Sroberto *    may be used to endorse or promote products derived from this software
22182007Sroberto *    without specific prior written permission.
23182007Sroberto *
24182007Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25182007Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26182007Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27182007Sroberto * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28182007Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29182007Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30182007Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31182007Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32182007Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33182007Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34182007Sroberto * SUCH DAMAGE.
35182007Sroberto *
3654359Sroberto */
3754359Sroberto
3854359Sroberto#ifdef HAVE_CONFIG_H
3954359Sroberto# include <config.h>
4054359Sroberto#endif
4154359Sroberto
4254359Sroberto#if defined(REFCLOCK) && defined(CLOCK_PARSE) && defined(CLOCK_TRIMTSIP)
4354359Sroberto
4454359Sroberto#include "ntp_syslog.h"
4554359Sroberto#include "ntp_types.h"
4654359Sroberto#include "ntp_fp.h"
47280849Scy#include "timevalops.h"
4854359Sroberto#include "ntp_calendar.h"
4954359Sroberto#include "ntp_machine.h"
5054359Sroberto#include "ntp_stdlib.h"
5154359Sroberto
5254359Sroberto#include "parse.h"
5354359Sroberto
5454359Sroberto#ifndef PARSESTREAM
5582498Sroberto# include <stdio.h>
5654359Sroberto#else
5782498Sroberto# include "sys/parsestreams.h"
5882498Sroberto#endif
5954359Sroberto
6054359Sroberto#include "ascii.h"
6154359Sroberto#include "binio.h"
6254359Sroberto#include "ieee754io.h"
6354359Sroberto#include "trimble.h"
6454359Sroberto
6554359Sroberto/*
6654359Sroberto * Trimble low level TSIP parser / time converter
6754359Sroberto *
6854359Sroberto * The receiver uses a serial message protocol called Trimble Standard
6954359Sroberto * Interface Protocol (it can support others but this driver only supports
7054359Sroberto * TSIP). Messages in this protocol have the following form:
7154359Sroberto *
7254359Sroberto * <DLE><id> ... <data> ... <DLE><ETX>
7354359Sroberto *
7454359Sroberto * Any bytes within the <data> portion of value 10 hex (<DLE>) are doubled
7554359Sroberto * on transmission and compressed back to one on reception. Otherwise
7654359Sroberto * the values of data bytes can be anything. The serial interface is RS-422
7754359Sroberto * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits
7854359Sroberto * in total!), and 1 stop bit. The protocol supports byte, integer, single,
7954359Sroberto * and double datatypes. Integers are two bytes, sent most significant first.
8054359Sroberto * Singles are IEEE754 single precision floating point numbers (4 byte) sent
8154359Sroberto * sign & exponent first. Doubles are IEEE754 double precision floating point
8254359Sroberto * numbers (8 byte) sent sign & exponent first.
8354359Sroberto * The receiver supports a large set of messages, only a very small subset of
8454359Sroberto * which is used here.
8554359Sroberto *
8654359Sroberto * From this module the following are recognised:
8754359Sroberto *
8854359Sroberto *  ID    Description
8954359Sroberto *
9054359Sroberto *  41    GPS Time
9154359Sroberto *  46    Receiver health
9254359Sroberto *  4F    UTC correction data (used to get leap second warnings)
9354359Sroberto *
9454359Sroberto * All others are accepted but ignored for time conversion - they are passed up to higher layers.
9554359Sroberto *
9654359Sroberto */
9754359Sroberto
9854359Srobertostatic offsets_t trim_offsets = { 0, 1, 2, 3, 4, 5, 6, 7 };
9954359Sroberto
10054359Srobertostruct trimble
10154359Sroberto{
10254359Sroberto	u_char  t_in_pkt;	/* first DLE received */
10354359Sroberto	u_char  t_dle;		/* subsequent DLE received */
10454359Sroberto	u_short t_week;		/* GPS week */
10554359Sroberto	u_short t_weekleap;	/* GPS week of next/last week */
10654359Sroberto	u_short t_dayleap;	/* day in week */
10754359Sroberto	u_short t_gpsutc;	/* GPS - UTC offset */
10854359Sroberto	u_short t_gpsutcleap;	/* offset at next/last leap */
10954359Sroberto	u_char  t_operable;	/* receiver feels OK */
11054359Sroberto	u_char  t_mode;		/* actual operating mode */
11154359Sroberto	u_char  t_leap;		/* possible leap warning */
11254359Sroberto        u_char  t_utcknown;	/* utc offset known */
11354359Sroberto};
11454359Sroberto
11554359Sroberto#define STATUS_BAD    0		/* BAD or UNINITIALIZED receiver status */
11654359Sroberto#define STATUS_UNSAFE 1		/* not enough receivers for full precision */
11754359Sroberto#define STATUS_SYNC   2		/* enough information for good operation */
11854359Sroberto
119282408Scystatic unsigned long inp_tsip (parse_t *, char, timestamp_t *);
120280849Scystatic unsigned long cvt_trimtsip (unsigned char *, int, struct format *, clocktime_t *, void *);
12154359Sroberto
12254359Srobertostruct clockformat clock_trimtsip =
12354359Sroberto{
12454359Sroberto	inp_tsip,		/* Trimble TSIP input handler */
12554359Sroberto	cvt_trimtsip,		/* Trimble TSIP conversion */
12654359Sroberto	pps_one,		/* easy PPS monitoring */
12754359Sroberto	0,			/* no configuration data */
12854359Sroberto	"Trimble TSIP",
12954359Sroberto	400,			/* input buffer */
13054359Sroberto	sizeof(struct trimble)	/* private data */
13154359Sroberto};
13254359Sroberto
13354359Sroberto#define ADDSECOND	0x01
13454359Sroberto#define DELSECOND	0x02
13554359Sroberto
13654359Srobertostatic unsigned long
13754359Srobertoinp_tsip(
13854359Sroberto	 parse_t      *parseio,
139282408Scy	 char         ch,
14054359Sroberto	 timestamp_t  *tstamp
14154359Sroberto	)
14254359Sroberto{
14354359Sroberto	struct trimble *t = (struct trimble *)parseio->parse_pdata;
14454359Sroberto
14554359Sroberto	if (!t)
14654359Sroberto	    return PARSE_INP_SKIP;		/* local data not allocated - sigh! */
14754359Sroberto
14854359Sroberto	if (!t->t_in_pkt && ch != DLE) {
14954359Sroberto		/* wait for start of packet */
15054359Sroberto		return PARSE_INP_SKIP;
15154359Sroberto	}
15254359Sroberto
15354359Sroberto	if ((parseio->parse_index >= (parseio->parse_dsize - 2)) ||
15454359Sroberto	    (parseio->parse_dtime.parse_msglen >= (sizeof(parseio->parse_dtime.parse_msg) - 2)))
15554359Sroberto		{		/* OVERFLOW - DROP! */
15654359Sroberto			t->t_in_pkt = t->t_dle = 0;
15754359Sroberto			parseio->parse_index = 0;
15854359Sroberto			parseio->parse_dtime.parse_msglen = 0;
15954359Sroberto		return PARSE_INP_SKIP;
16054359Sroberto	}
16154359Sroberto
16254359Sroberto	switch (ch) {
16354359Sroberto	    case DLE:
16454359Sroberto		if (!t->t_in_pkt) {
16554359Sroberto			t->t_dle = 0;
16654359Sroberto			t->t_in_pkt = 1;
16754359Sroberto			parseio->parse_index = 0;
16854359Sroberto			parseio->parse_data[parseio->parse_index++] = ch;
16954359Sroberto			parseio->parse_dtime.parse_msglen = 0;
17054359Sroberto			parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = ch;
17154359Sroberto			parseio->parse_dtime.parse_stime = *tstamp; /* pick up time stamp at packet start */
17254359Sroberto		} else if (t->t_dle) {
17354359Sroberto			/* Double DLE -> insert a DLE */
17454359Sroberto			t->t_dle = 0;
17554359Sroberto			parseio->parse_data[parseio->parse_index++] = DLE;
17654359Sroberto			parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = DLE;
17754359Sroberto		} else
17854359Sroberto		    t->t_dle = 1;
17954359Sroberto		break;
18054359Sroberto
18154359Sroberto	    case ETX:
18254359Sroberto		if (t->t_dle) {
18354359Sroberto			/* DLE,ETX -> end of packet */
18454359Sroberto			parseio->parse_data[parseio->parse_index++] = DLE;
18554359Sroberto			parseio->parse_data[parseio->parse_index] = ch;
186282408Scy			parseio->parse_ldsize = (u_short) (parseio->parse_index + 1);
18754359Sroberto			memcpy(parseio->parse_ldata, parseio->parse_data, parseio->parse_ldsize);
18854359Sroberto			parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = DLE;
18954359Sroberto			parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = ch;
19054359Sroberto			t->t_in_pkt = t->t_dle = 0;
19154359Sroberto			return PARSE_INP_TIME|PARSE_INP_DATA;
19254359Sroberto		}
193280849Scy		/*FALLTHROUGH*/
19454359Sroberto
19554359Sroberto	    default:		/* collect data */
19654359Sroberto		t->t_dle = 0;
19754359Sroberto		parseio->parse_data[parseio->parse_index++] = ch;
19854359Sroberto		parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = ch;
19954359Sroberto	}
20054359Sroberto
20154359Sroberto  return PARSE_INP_SKIP;
20254359Sroberto}
203282408Scy
204282408Scystatic short
20554359Srobertogetshort(
20654359Sroberto	 unsigned char *p
20754359Sroberto	 )
20854359Sroberto{
209282408Scy	return (short) get_msb_short(&p);
21054359Sroberto}
21154359Sroberto
21254359Sroberto/*
21354359Sroberto * cvt_trimtsip
21454359Sroberto *
21554359Sroberto * convert TSIP type format
21654359Sroberto */
21754359Srobertostatic unsigned long
21854359Srobertocvt_trimtsip(
21954359Sroberto	     unsigned char *buffer,
22054359Sroberto	     int            size,
22154359Sroberto	     struct format *format,
22254359Sroberto	     clocktime_t   *clock_time,
22354359Sroberto	     void          *local
22454359Sroberto	     )
22554359Sroberto{
22654359Sroberto        register struct trimble *t = (struct trimble *)local; /* get local data space */
22754359Sroberto#define mb(_X_) (buffer[2+(_X_)]) /* shortcut for buffer access */
22854359Sroberto	register u_char cmd;
22954359Sroberto
23054359Sroberto	clock_time->flags = 0;
23154359Sroberto
23254359Sroberto	if (!t) {
23354359Sroberto		return CVT_NONE;		/* local data not allocated - sigh! */
23454359Sroberto	}
23554359Sroberto
23654359Sroberto	if ((size < 4) ||
23754359Sroberto	    (buffer[0]      != DLE) ||
23854359Sroberto	    (buffer[size-1] != ETX) ||
23954359Sroberto	    (buffer[size-2] != DLE))
24054359Sroberto	{
24154359Sroberto		printf("TRIMBLE BAD packet, size %d:\n", size);
24254359Sroberto		return CVT_NONE;
24354359Sroberto	}
24454359Sroberto	else
24554359Sroberto	{
24654359Sroberto		unsigned char *bp;
24754359Sroberto		cmd = buffer[1];
248282408Scy
24954359Sroberto		    switch(cmd)
25054359Sroberto		    {
25154359Sroberto		    case CMD_RCURTIME:
25254359Sroberto			    {			/* GPS time */
253316068Sdelphij				    l_fp  secs;
254316068Sdelphij				    u_int week = getshort((unsigned char *)&mb(4));
255316068Sdelphij				    l_fp  utcoffset;
256316068Sdelphij				    l_fp  gpstime;
25754359Sroberto
25854359Sroberto				    bp = &mb(0);
25954359Sroberto				    if (fetch_ieee754(&bp, IEEE_SINGLE, &secs, trim_offsets) != IEEE_OK)
26054359Sroberto					    return CVT_FAIL|CVT_BADFMT;
261282408Scy
26254359Sroberto				    if ((secs.l_i <= 0) ||
26354359Sroberto					(t->t_utcknown == 0))
26454359Sroberto				    {
26554359Sroberto					    clock_time->flags = PARSEB_POWERUP;
26654359Sroberto					    return CVT_OK;
26754359Sroberto				    }
268344884Scy				    week = basedate_expand_gpsweek(week);
26954359Sroberto
27054359Sroberto				    /* time OK */
27154359Sroberto
27254359Sroberto				    /* fetch UTC offset */
27354359Sroberto				    bp = &mb(6);
27454359Sroberto				    if (fetch_ieee754(&bp, IEEE_SINGLE, &utcoffset, trim_offsets) != IEEE_OK)
27554359Sroberto					    return CVT_FAIL|CVT_BADFMT;
276282408Scy
27754359Sroberto				    L_SUB(&secs, &utcoffset); /* adjust GPS time to UTC time */
27854359Sroberto
27954359Sroberto				    gpstolfp((unsigned short)week, (unsigned short)0,
28054359Sroberto					     secs.l_ui, &gpstime);
28154359Sroberto
28254359Sroberto				    gpstime.l_uf = secs.l_uf;
28354359Sroberto
28454359Sroberto				    clock_time->utctime = gpstime.l_ui - JAN_1970;
28554359Sroberto
28654359Sroberto				    TSFTOTVU(gpstime.l_uf, clock_time->usecond);
28754359Sroberto
28854359Sroberto				    if (t->t_leap == ADDSECOND)
28954359Sroberto					clock_time->flags |= PARSEB_LEAPADD;
290282408Scy
29154359Sroberto				    if (t->t_leap == DELSECOND)
29254359Sroberto					clock_time->flags |= PARSEB_LEAPDEL;
293282408Scy
29454359Sroberto				    switch (t->t_operable)
29554359Sroberto				      {
29654359Sroberto				      case STATUS_SYNC:
29754359Sroberto					clock_time->flags &= ~(PARSEB_POWERUP|PARSEB_NOSYNC);
29854359Sroberto					break;
29954359Sroberto
30054359Sroberto				      case STATUS_UNSAFE:
30154359Sroberto					clock_time->flags |= PARSEB_NOSYNC;
30254359Sroberto					break;
30354359Sroberto
30454359Sroberto				      case STATUS_BAD:
30554359Sroberto					clock_time->flags |= PARSEB_NOSYNC|PARSEB_POWERUP;
30654359Sroberto					break;
30754359Sroberto				      }
308282408Scy
30954359Sroberto				    if (t->t_mode == 0)
31054359Sroberto					    clock_time->flags |= PARSEB_POSITION;
311282408Scy
31254359Sroberto				    clock_time->flags |= PARSEB_S_LEAP|PARSEB_S_POSITION;
313282408Scy
31454359Sroberto				    return CVT_OK;
31554359Sroberto
31654359Sroberto			    } /* case 0x41 */
31754359Sroberto
31854359Sroberto		    case CMD_RRECVHEALTH:
31954359Sroberto			    {
32054359Sroberto				    /* TRIMBLE health */
32154359Sroberto				    u_char status = mb(0);
32254359Sroberto
32354359Sroberto				    switch (status)
32454359Sroberto				    {
32554359Sroberto				      case 0x00: /* position fixes */
32654359Sroberto					t->t_operable = STATUS_SYNC;
32754359Sroberto					break;
32854359Sroberto
32954359Sroberto				      case 0x09: /* 1 satellite */
33054359Sroberto				      case 0x0A: /* 2 satellites */
33154359Sroberto				      case 0x0B: /* 3 satellites */
33254359Sroberto					t->t_operable = STATUS_UNSAFE;
33354359Sroberto					break;
33454359Sroberto
33554359Sroberto				      default:
33654359Sroberto					t->t_operable = STATUS_BAD;
33754359Sroberto					break;
33854359Sroberto				    }
33954359Sroberto				    t->t_mode = status;
34054359Sroberto			    }
34154359Sroberto			    break;
34254359Sroberto
34354359Sroberto		    case CMD_RUTCPARAM:
34454359Sroberto			    {
34554359Sroberto			            l_fp t0t;
34654359Sroberto				    unsigned char *lbp;
347282408Scy
34854359Sroberto				    /* UTC correction data - derive a leap warning */
349282408Scy				    int tls   = t->t_gpsutc     = (u_short) getshort((unsigned char *)&mb(12)); /* current leap correction (GPS-UTC) */
350282408Scy				    int tlsf  = t->t_gpsutcleap = (u_short) getshort((unsigned char *)&mb(24)); /* new leap correction */
35154359Sroberto
352344884Scy				    t->t_weekleap   = basedate_expand_gpsweek(
353344884Scy					(u_short) getshort((unsigned char *)&mb(20))); /* week no of leap correction */
35454359Sroberto
355282408Scy				    t->t_dayleap    = (u_short) getshort((unsigned char *)&mb(22)); /* day in week of leap correction */
356344884Scy				    t->t_week = basedate_expand_gpsweek(
357344884Scy					(u_short) getshort((unsigned char *)&mb(18))); /* current week no */
358282408Scy
35954359Sroberto				    lbp = (unsigned char *)&mb(14); /* last update time */
36054359Sroberto				    if (fetch_ieee754(&lbp, IEEE_SINGLE, &t0t, trim_offsets) != IEEE_OK)
36154359Sroberto					    return CVT_FAIL|CVT_BADFMT;
36254359Sroberto
36354359Sroberto				    t->t_utcknown = t0t.l_ui != 0;
364282408Scy
36554359Sroberto				    if ((t->t_utcknown) && /* got UTC information */
36654359Sroberto					(tlsf != tls)   && /* something will change */
36754359Sroberto					((t->t_weekleap - t->t_week) < 5)) /* and close in the future */
36854359Sroberto				    {
36954359Sroberto					    /* generate a leap warning */
37054359Sroberto					    if (tlsf > tls)
37154359Sroberto						t->t_leap = ADDSECOND;
37254359Sroberto					    else
37354359Sroberto						t->t_leap = DELSECOND;
37454359Sroberto				    }
37554359Sroberto				    else
37654359Sroberto				    {
37754359Sroberto					    t->t_leap = 0;
37854359Sroberto				    }
37954359Sroberto			    }
38054359Sroberto			    break;
38154359Sroberto
38254359Sroberto		    default:
38354359Sroberto			    /* it's validly formed, but we don't care about it! */
38454359Sroberto			    break;
38554359Sroberto		}
38654359Sroberto	}
38754359Sroberto	return CVT_SKIP;
38854359Sroberto}
38954359Sroberto
39054359Sroberto#else /* not (REFCLOCK && CLOCK_PARSE && CLOCK_TRIMTSIP && !PARSESTREAM) */
39154359Srobertoint clk_trimtsip_bs;
39254359Sroberto#endif /* not (REFCLOCK && CLOCK_PARSE && CLOCK_TRIMTSIP && !PARSESTREAM) */
39354359Sroberto
39454359Sroberto/*
39554359Sroberto * History:
39654359Sroberto *
39754359Sroberto * clk_trimtsip.c,v
398280849Scy * Revision 4.19  2009/11/01 10:47:49  kardel
399280849Scy * de-P()
400280849Scy *
401280849Scy * Revision 4.18  2009/11/01 08:46:46  kardel
402280849Scy * clarify case FALLTHROUGH
403280849Scy *
404182007Sroberto * Revision 4.17  2005/04/16 17:32:10  kardel
405182007Sroberto * update copyright
406182007Sroberto *
407182007Sroberto * Revision 4.16  2004/11/14 15:29:41  kardel
408182007Sroberto * support PPSAPI, upgrade Copyright to Berkeley style
409182007Sroberto *
41056746Sroberto * Revision 4.13  1999/11/28 09:13:51  kardel
41156746Sroberto * RECON_4_0_98F
41256746Sroberto *
41354359Sroberto * Revision 4.12  1999/02/28 13:00:08  kardel
41454359Sroberto * *** empty log message ***
41554359Sroberto *
41654359Sroberto * Revision 4.11  1999/02/28 11:47:54  kardel
41754359Sroberto * (struct trimble): new member t_utcknown
41854359Sroberto * (cvt_trimtsip): fixed status monitoring, bad receiver states are
41954359Sroberto * now recognized
42054359Sroberto *
42154359Sroberto * Revision 4.10  1999/02/27 15:57:15  kardel
42254359Sroberto * use mmemcpy instead of bcopy
42354359Sroberto *
42454359Sroberto * Revision 4.9  1999/02/21 12:17:42  kardel
42554359Sroberto * 4.91f reconcilation
42654359Sroberto *
42754359Sroberto * Revision 4.8  1998/11/15 20:27:58  kardel
42854359Sroberto * Release 4.0.73e13 reconcilation
42954359Sroberto *
43054359Sroberto * Revision 4.7  1998/08/16 18:49:20  kardel
43154359Sroberto * (cvt_trimtsip): initial kernel capable version (no more floats)
43254359Sroberto * (clock_trimtsip =): new format name
43354359Sroberto *
43454359Sroberto * Revision 4.6  1998/08/09 22:26:05  kardel
43554359Sroberto * Trimble TSIP support
43654359Sroberto *
43754359Sroberto * Revision 4.5  1998/08/02 10:37:05  kardel
43854359Sroberto * working TSIP parser
43954359Sroberto *
44054359Sroberto * Revision 4.4  1998/06/28 16:50:40  kardel
44154359Sroberto * (getflt): fixed ENDIAN issue
44254359Sroberto * (getdbl): fixed ENDIAN issue
44354359Sroberto * (getint): use get_msb_short()
44454359Sroberto * (cvt_trimtsip): use gpstolfp() for conversion
44554359Sroberto *
44654359Sroberto * Revision 4.3  1998/06/13 12:07:31  kardel
44754359Sroberto * fix SYSV clock name clash
44854359Sroberto *
44954359Sroberto * Revision 4.2  1998/06/12 15:22:30  kardel
45054359Sroberto * fix prototypes
45154359Sroberto *
45254359Sroberto * Revision 4.1  1998/05/24 09:39:54  kardel
45354359Sroberto * implementation of the new IO handling model
45454359Sroberto *
45554359Sroberto * Revision 4.0  1998/04/10 19:45:32  kardel
45654359Sroberto * Start 4.0 release version numbering
45754359Sroberto *
45854359Sroberto * from V3 1.8 loginfo deleted 1998/04/11 kardel
45954359Sroberto */
460