Home | History | Annotate | Line # | Download | only in libparse
      1 /*	$NetBSD: parsestreams.c,v 1.6 2020/05/25 20:47:25 christos Exp $	*/
      2 
      3 /*
      4  * /src/NTP/ntp4-dev/libparse/parsestreams.c,v 4.11 2005/04/16 17:32:10 kardel RELEASE_20050508_A
      5  *
      6  * parsestreams.c,v 4.11 2005/04/16 17:32:10 kardel RELEASE_20050508_A
      7  *
      8  * STREAMS module for reference clocks
      9  * (SunOS4.x)
     10  *
     11  * Copyright (c) 1995-2005 by Frank Kardel <kardel <AT> ntp.org>
     12  * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany
     13  *
     14  * Redistribution and use in source and binary forms, with or without
     15  * modification, are permitted provided that the following conditions
     16  * are met:
     17  * 1. Redistributions of source code must retain the above copyright
     18  *    notice, this list of conditions and the following disclaimer.
     19  * 2. Redistributions in binary form must reproduce the above copyright
     20  *    notice, this list of conditions and the following disclaimer in the
     21  *    documentation and/or other materials provided with the distribution.
     22  * 3. Neither the name of the author nor the names of its contributors
     23  *    may be used to endorse or promote products derived from this software
     24  *    without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     36  * SUCH DAMAGE.
     37  *
     38  */
     39 
     40 #define KERNEL			/* MUST */
     41 #define VDDRV			/* SHOULD */
     42 
     43 #ifdef HAVE_CONFIG_H
     44 # include "config.h"
     45 #endif
     46 
     47 #ifndef lint
     48 static char rcsid[] = "parsestreams.c,v 4.11 2005/04/16 17:32:10 kardel RELEASE_20050508_A";
     49 #endif
     50 
     51 #ifndef KERNEL
     52 #include "Bletch: MUST COMPILE WITH KERNEL DEFINE"
     53 #endif
     54 
     55 #include <sys/types.h>
     56 #include <sys/conf.h>
     57 #include <sys/buf.h>
     58 #include <sys/param.h>
     59 #include <sys/sysmacros.h>
     60 #include <sys/time.h>
     61 #include <sundev/mbvar.h>
     62 #include <sun/autoconf.h>
     63 #include <sys/stream.h>
     64 #include <sys/stropts.h>
     65 #include <sys/dir.h>
     66 #include <sys/signal.h>
     67 #include <sys/termios.h>
     68 #include <sys/termio.h>
     69 #include <sys/ttold.h>
     70 #include <sys/user.h>
     71 #include <sys/tty.h>
     72 
     73 #ifdef VDDRV
     74 #include <sun/vddrv.h>
     75 #endif
     76 
     77 #include "ntp_stdlib.h"
     78 #include "ntp_fp.h"
     79 /*
     80  * just make checking compilers more silent
     81  */
     82 extern int printf      (const char *, ...);
     83 extern int putctl1     (queue_t *, int, int);
     84 extern int canput      (queue_t *);
     85 extern void putbq      (queue_t *, mblk_t *);
     86 extern void freeb      (mblk_t *);
     87 extern void qreply     (queue_t *, mblk_t *);
     88 extern void freemsg    (mblk_t *);
     89 extern void panic      (const char *, ...);
     90 extern void usec_delay (int);
     91 
     92 #include "parse.h"
     93 #include "sys/parsestreams.h"
     94 
     95 /*
     96  * use microtime instead of uniqtime if advised to
     97  */
     98 #ifdef MICROTIME
     99 #define uniqtime microtime
    100 #endif
    101 
    102 #ifdef VDDRV
    103 static unsigned int parsebusy = 0;
    104 
    105 /*--------------- loadable driver section -----------------------------*/
    106 
    107 extern struct streamtab parseinfo;
    108 
    109 
    110 #ifdef PPS_SYNC
    111 static char mnam[] = "PARSEPPS     ";	/* name this baby - keep room for revision number */
    112 #else
    113 static char mnam[] = "PARSE        ";	/* name this baby - keep room for revision number */
    114 #endif
    115 struct vdldrv parsesync_vd =
    116 {
    117 	VDMAGIC_PSEUDO,		/* nothing like a real driver - a STREAMS module */
    118 	mnam,
    119 };
    120 
    121 /*
    122  * strings support usually not in kernel
    123  */
    124 static int
    125 Strlen(
    126 	register const char *s
    127 	)
    128 {
    129 	register int c;
    130 
    131 	c = 0;
    132 	if (s)
    133 	{
    134 		while (*s++)
    135 		{
    136 			c++;
    137 		}
    138 	}
    139 	return c;
    140 }
    141 
    142 static void
    143 Strncpy(
    144 	register char *t,
    145 	register char *s,
    146 	register int   c
    147 	)
    148 {
    149 	if (s && t)
    150 	{
    151 		while ((c-- > 0) && (*t++ = *s++))
    152 		    ;
    153 	}
    154 }
    155 
    156 static int
    157 Strcmp(
    158 	register const char *s,
    159 	register const char *t
    160 	)
    161 {
    162 	register int c = 0;
    163 
    164 	if (!s || !t || (s == t))
    165 	{
    166 		return 0;
    167 	}
    168 
    169 	while (!(c = *s++ - *t++) && *s && *t)
    170 	    /* empty loop */;
    171 
    172 	return c;
    173 }
    174 
    175 static int
    176 Strncmp(
    177 	register char *s,
    178 	register char *t,
    179 	register int n
    180 	)
    181 {
    182 	register int c = 0;
    183 
    184 	if (!s || !t || (s == t))
    185 	{
    186 		return 0;
    187 	}
    188 
    189 	while (n-- && !(c = *s++ - *t++) && *s && *t)
    190 	    /* empty loop */;
    191 
    192 	return c;
    193 }
    194 
    195 void
    196 ntp_memset(
    197 	char *a,
    198 	int x,
    199 	int c
    200 	)
    201 {
    202 	while (c-- > 0)
    203 	    *a++ = x;
    204 }
    205 
    206 /*
    207  * driver init routine
    208  * since no mechanism gets us into and out of the fmodsw, we have to
    209  * do it ourselves
    210  */
    211 /*ARGSUSED*/
    212 int
    213 xxxinit(
    214 	unsigned int fc,
    215 	struct vddrv *vdp,
    216 	addr_t vdin,
    217 	struct vdstat *vds
    218 	)
    219 {
    220 	extern struct fmodsw fmodsw[];
    221 	extern int fmodcnt;
    222 
    223 	struct fmodsw *fm    = fmodsw;
    224 	struct fmodsw *fmend = &fmodsw[fmodcnt];
    225 	struct fmodsw *ifm   = (struct fmodsw *)0;
    226 	char *mname          = parseinfo.st_rdinit->qi_minfo->mi_idname;
    227 
    228 	switch (fc)
    229 	{
    230 	    case VDLOAD:
    231 		vdp->vdd_vdtab = (struct vdlinkage *)&parsesync_vd;
    232 		/*
    233 		 * now, jog along fmodsw scanning for an empty slot
    234 		 * and deposit our name there
    235 		 */
    236 		while (fm <= fmend)
    237 		{
    238 			if (!Strncmp(fm->f_name, mname, FMNAMESZ))
    239 			{
    240 				printf("vddrinit[%s]: STREAMS module already loaded.\n", mname);
    241 				return(EBUSY);
    242 			}
    243 			else
    244 			    if ((ifm == (struct fmodsw *)0) &&
    245 				(fm->f_name[0] == '\0') &&
    246 				(fm->f_str == (struct streamtab *)0))
    247 			    {
    248 				    /*
    249 				     * got one - so move in
    250 				     */
    251 				    ifm = fm;
    252 				    break;
    253 			    }
    254 			fm++;
    255 		}
    256 
    257 		if (ifm == (struct fmodsw *)0)
    258 		{
    259 			printf("vddrinit[%s]: no slot free for STREAMS module\n", mname);
    260 			return (ENOSPC);
    261 		}
    262 		else
    263 		{
    264 			static char revision[] = "4.7";
    265 			char *s, *S, *t;
    266 
    267 			s = rcsid;		/* NOOP - keep compilers happy */
    268 
    269 			Strncpy(ifm->f_name, mname, FMNAMESZ);
    270 			ifm->f_name[FMNAMESZ] = '\0';
    271 			ifm->f_str = &parseinfo;
    272 			/*
    273 			 * copy RCS revision into Drv_name
    274 			 *
    275 			 * are we forcing RCS here to do things it was not built for ?
    276 			 */
    277 			s = revision;
    278 			if (*s == '$')
    279 			{
    280 				/*
    281 				 * skip "$Revision: "
    282 				 * if present. - not necessary on a -kv co (cvs export)
    283 				 */
    284 				while (*s && (*s != ' '))
    285 				{
    286 					s++;
    287 				}
    288 				if (*s == ' ') s++;
    289 			}
    290 
    291 			t = parsesync_vd.Drv_name;
    292 			while (*t && (*t != ' '))
    293 			{
    294 				t++;
    295 			}
    296 			if (*t == ' ') t++;
    297 
    298 			S = s;
    299 			while (*S && (((*S >= '0') && (*S <= '9')) || (*S == '.')))
    300 			{
    301 				S++;
    302 			}
    303 
    304 			if (*s && *t && (S > s))
    305 			{
    306 				if (Strlen(t) >= (S - s))
    307 				{
    308 					(void) Strncpy(t, s, S - s);
    309 				}
    310 			}
    311 			return (0);
    312 		}
    313 		break;
    314 
    315 	    case VDUNLOAD:
    316 		if (parsebusy > 0)
    317 		{
    318 			printf("vddrinit[%s]: STREAMS module has still %d instances active.\n", mname, parsebusy);
    319 			return (EBUSY);
    320 		}
    321 		else
    322 		{
    323 			while (fm <= fmend)
    324 			{
    325 				if (!Strncmp(fm->f_name, mname, FMNAMESZ))
    326 				{
    327 					/*
    328 					 * got it - kill entry
    329 					 */
    330 					fm->f_name[0] = '\0';
    331 					fm->f_str = (struct streamtab *)0;
    332 					fm++;
    333 
    334 					break;
    335 				}
    336 				fm++;
    337 			}
    338 			if (fm > fmend)
    339 			{
    340 				printf("vddrinit[%s]: cannot find entry for STREAMS module\n", mname);
    341 				return (ENXIO);
    342 			}
    343 			else
    344 			    return (0);
    345 		}
    346 
    347 
    348 	    case VDSTAT:
    349 		return (0);
    350 
    351 	    default:
    352 		return (EIO);
    353 
    354 	}
    355 	return EIO;
    356 }
    357 
    358 #endif
    359 
    360 /*--------------- stream module definition ----------------------------*/
    361 
    362 static int parseopen  (queue_t *, dev_t, int, int);
    363 static int parseclose (queue_t *, int);
    364 static int parsewput  (queue_t *, mblk_t *);
    365 static int parserput  (queue_t *, mblk_t *);
    366 static int parsersvc  (queue_t *);
    367 
    368 static char mn[] = "parse";
    369 
    370 static struct module_info driverinfo =
    371 {
    372 	0,				/* module ID number */
    373 	mn,			/* module name */
    374 	0,				/* minimum accepted packet size */
    375 	INFPSZ,			/* maximum accepted packet size */
    376 	1,				/* high water mark - flow control */
    377 	0				/* low water mark - flow control */
    378 };
    379 
    380 static struct qinit rinit =	/* read queue definition */
    381 {
    382 	parserput,			/* put procedure */
    383 	parsersvc,			/* service procedure */
    384 	parseopen,			/* open procedure */
    385 	parseclose,			/* close procedure */
    386 	NULL,				/* admin procedure - NOT USED FOR NOW */
    387 	&driverinfo,			/* information structure */
    388 	NULL				/* statistics */
    389 };
    390 
    391 static struct qinit winit =	/* write queue definition */
    392 {
    393 	parsewput,			/* put procedure */
    394 	NULL,				/* service procedure */
    395 	NULL,				/* open procedure */
    396 	NULL,				/* close procedure */
    397 	NULL,				/* admin procedure - NOT USED FOR NOW */
    398 	&driverinfo,			/* information structure */
    399 	NULL				/* statistics */
    400 };
    401 
    402 struct streamtab parseinfo =	/* stream info element for dpr driver */
    403 {
    404 	&rinit,			/* read queue */
    405 	&winit,			/* write queue */
    406 	NULL,				/* read mux */
    407 	NULL,				/* write mux */
    408 	NULL				/* module auto push */
    409 };
    410 
    411 /*--------------- driver data structures ----------------------------*/
    412 
    413 /*
    414  * we usually have an inverted signal - but you
    415  * can change this to suit your needs
    416  */
    417 int cd_invert = 1;		/* invert status of CD line - PPS support via CD input */
    418 
    419 int parsedebug = ~0;
    420 
    421 extern void uniqtime (struct timeval *);
    422 
    423 /*--------------- module implementation -----------------------------*/
    424 
    425 #define TIMEVAL_USADD(_X_, _US_) {\
    426                                    (_X_)->tv_usec += (_US_);\
    427 			           if ((_X_)->tv_usec >= 1000000)\
    428                                      {\
    429                                        (_X_)->tv_sec++;\
    430 			               (_X_)->tv_usec -= 1000000;\
    431                                      }\
    432 				 } while (0)
    433 
    434 static int init_linemon (queue_t *);
    435 static void close_linemon (queue_t *, queue_t *);
    436 
    437 #define M_PARSE		0x0001
    438 #define M_NOPARSE	0x0002
    439 
    440 static int
    441 setup_stream(
    442 	     queue_t *q,
    443 	     int mode
    444 	     )
    445 {
    446 	mblk_t *mp;
    447 
    448 	mp = allocb(sizeof(struct stroptions), BPRI_MED);
    449 	if (mp)
    450 	{
    451 		struct stroptions *str = (struct stroptions *)(void *)mp->b_rptr;
    452 
    453 		str->so_flags   = SO_READOPT|SO_HIWAT|SO_LOWAT;
    454 		str->so_readopt = (mode == M_PARSE) ? RMSGD : RNORM;
    455 		str->so_hiwat   = (mode == M_PARSE) ? sizeof(parsetime_t) : 256;
    456 		str->so_lowat   = 0;
    457 		mp->b_datap->db_type = M_SETOPTS;
    458 		mp->b_wptr += sizeof(struct stroptions);
    459 		putnext(q, mp);
    460 		return putctl1(WR(q)->q_next, M_CTL, (mode == M_PARSE) ? MC_SERVICEIMM :
    461 			       MC_SERVICEDEF);
    462 	}
    463 	else
    464 	{
    465 		parseprintf(DD_OPEN,("parse: setup_stream - FAILED - no MEMORY for allocb\n"));
    466 		return 0;
    467 	}
    468 }
    469 
    470 /*ARGSUSED*/
    471 static int
    472 parseopen(
    473 	queue_t *q,
    474 	dev_t dev,
    475 	int flag,
    476 	int sflag
    477 	)
    478 {
    479 	register parsestream_t *parse;
    480 	static int notice = 0;
    481 
    482 	parseprintf(DD_OPEN,("parse: OPEN\n"));
    483 
    484 	if (sflag != MODOPEN)
    485 	{			/* open only for modules */
    486 		parseprintf(DD_OPEN,("parse: OPEN - FAILED - not MODOPEN\n"));
    487 		return OPENFAIL;
    488 	}
    489 
    490 	if (q->q_ptr != (caddr_t)NULL)
    491 	{
    492 		u.u_error = EBUSY;
    493 		parseprintf(DD_OPEN,("parse: OPEN - FAILED - EXCLUSIVE ONLY\n"));
    494 		return OPENFAIL;
    495 	}
    496 
    497 #ifdef VDDRV
    498 	parsebusy++;
    499 #endif
    500 
    501 	q->q_ptr = (caddr_t)kmem_alloc(sizeof(parsestream_t));
    502 	if (q->q_ptr == (caddr_t)0)
    503 	{
    504 		parseprintf(DD_OPEN,("parse: OPEN - FAILED - no memory\n"));
    505 #ifdef VDDRV
    506 		parsebusy--;
    507 #endif
    508 		return OPENFAIL;
    509 	}
    510 	WR(q)->q_ptr = q->q_ptr;
    511 
    512 	parse = (parsestream_t *)(void *)q->q_ptr;
    513 	bzero((caddr_t)parse, sizeof(*parse));
    514 	parse->parse_queue     = q;
    515 	parse->parse_status    = PARSE_ENABLE;
    516 	parse->parse_ppsclockev.tv.tv_sec  = 0;
    517 	parse->parse_ppsclockev.tv.tv_usec = 0;
    518 	parse->parse_ppsclockev.serial     = 0;
    519 
    520 	if (!parse_ioinit(&parse->parse_io))
    521 	{
    522 		/*
    523 		 * ok guys - beat it
    524 		 */
    525 		kmem_free((caddr_t)parse, sizeof(parsestream_t));
    526 #ifdef VDDRV
    527 		parsebusy--;
    528 #endif
    529 		return OPENFAIL;
    530 	}
    531 
    532 	if (setup_stream(q, M_PARSE))
    533 	{
    534 		(void) init_linemon(q);	/* hook up PPS ISR routines if possible */
    535 
    536 		parseprintf(DD_OPEN,("parse: OPEN - SUCCEEDED\n"));
    537 
    538 		/*
    539 		 * I know that you know the delete key, but you didn't write this
    540 		 * code, did you ? - So, keep the message in here.
    541 		 */
    542 		if (!notice)
    543 		{
    544 #ifdef VDDRV
    545 			printf("%s: Copyright (C) 1991-2005, Frank Kardel\n", parsesync_vd.Drv_name);
    546 #else
    547 			printf("%s: Copyright (C) 1991-2005, Frank Kardel\n", "parsestreams.c,v 4.11 2005/04/16 17:32:10 kardel RELEASE_20050508_A");
    548 #endif
    549 			notice = 1;
    550 		}
    551 
    552 		return MODOPEN;
    553 	}
    554 	else
    555 	{
    556 		kmem_free((caddr_t)parse, sizeof(parsestream_t));
    557 
    558 #ifdef VDDRV
    559 		parsebusy--;
    560 #endif
    561 		return OPENFAIL;
    562 	}
    563 }
    564 
    565 /*ARGSUSED*/
    566 static int
    567 parseclose(
    568 	queue_t *q,
    569 	int flags
    570 	)
    571 {
    572 	register parsestream_t *parse = (parsestream_t *)(void *)q->q_ptr;
    573 	register unsigned long s;
    574 
    575 	parseprintf(DD_CLOSE,("parse: CLOSE\n"));
    576 
    577 	s = splhigh();
    578 
    579 	if (parse->parse_dqueue)
    580 	    close_linemon(parse->parse_dqueue, q);
    581 	parse->parse_dqueue = (queue_t *)0;
    582 
    583 	(void) splx(s);
    584 
    585 	parse_ioend(&parse->parse_io);
    586 
    587 	kmem_free((caddr_t)parse, sizeof(parsestream_t));
    588 
    589 	q->q_ptr = (caddr_t)NULL;
    590 	WR(q)->q_ptr = (caddr_t)NULL;
    591 
    592 #ifdef VDDRV
    593 	parsebusy--;
    594 #endif
    595 	return 0;
    596 }
    597 
    598 /*
    599  * move unrecognized stuff upward
    600  */
    601 static int
    602 parsersvc(
    603 	queue_t *q
    604 	)
    605 {
    606 	mblk_t *mp;
    607 
    608 	while ((mp = getq(q)))
    609 	{
    610 		if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
    611 		{
    612 			putnext(q, mp);
    613 			parseprintf(DD_RSVC,("parse: RSVC - putnext\n"));
    614 		}
    615 		else
    616 		{
    617 			putbq(q, mp);
    618 			parseprintf(DD_RSVC,("parse: RSVC - flow control wait\n"));
    619 			break;
    620 		}
    621 	}
    622 	return 0;
    623 }
    624 
    625 /*
    626  * do ioctls and
    627  * send stuff down - dont care about
    628  * flow control
    629  */
    630 static int
    631 parsewput(
    632 	queue_t *q,
    633 	register mblk_t *mp
    634 	)
    635 {
    636 	register int ok = 1;
    637 	register mblk_t *datap;
    638 	register struct iocblk *iocp;
    639 	parsestream_t         *parse = (parsestream_t *)(void *)q->q_ptr;
    640 
    641 	parseprintf(DD_WPUT,("parse: parsewput\n"));
    642 
    643 	switch (mp->b_datap->db_type)
    644 	{
    645 	    default:
    646 		putnext(q, mp);
    647 		break;
    648 
    649 	    case M_IOCTL:
    650 		    iocp = (struct iocblk *)(void *)mp->b_rptr;
    651 		switch (iocp->ioc_cmd)
    652 		{
    653 		    default:
    654 			parseprintf(DD_WPUT,("parse: parsewput - forward M_IOCTL\n"));
    655 			putnext(q, mp);
    656 			break;
    657 
    658 		    case CIOGETEV:
    659 			/*
    660 			 * taken from Craig Leres ppsclock module (and modified)
    661 			 */
    662 			datap = allocb(sizeof(struct ppsclockev), BPRI_MED);
    663 			if (datap == NULL || mp->b_cont)
    664 			{
    665 				mp->b_datap->db_type = M_IOCNAK;
    666 				iocp->ioc_error = (datap == NULL) ? ENOMEM : EINVAL;
    667 				if (datap != NULL)
    668 				    freeb(datap);
    669 				qreply(q, mp);
    670 				break;
    671 			}
    672 
    673 			mp->b_cont = datap;
    674 			*(struct ppsclockev *)(void *)datap->b_wptr = parse->parse_ppsclockev;
    675 			datap->b_wptr +=
    676 				sizeof(struct ppsclockev) / sizeof(*datap->b_wptr);
    677 			mp->b_datap->db_type = M_IOCACK;
    678 			iocp->ioc_count = sizeof(struct ppsclockev);
    679 			qreply(q, mp);
    680 			break;
    681 
    682 		    case PARSEIOC_ENABLE:
    683 		    case PARSEIOC_DISABLE:
    684 			    {
    685 				    parse->parse_status = (parse->parse_status & (unsigned)~PARSE_ENABLE) |
    686 					    (iocp->ioc_cmd == PARSEIOC_ENABLE) ?
    687 					    PARSE_ENABLE : 0;
    688 				    if (!setup_stream(RD(q), (parse->parse_status & PARSE_ENABLE) ?
    689 						      M_PARSE : M_NOPARSE))
    690 				    {
    691 					    mp->b_datap->db_type = M_IOCNAK;
    692 				    }
    693 				    else
    694 				    {
    695 					    mp->b_datap->db_type = M_IOCACK;
    696 				    }
    697 				    qreply(q, mp);
    698 				    break;
    699 			    }
    700 
    701 		    case PARSEIOC_TIMECODE:
    702 		    case PARSEIOC_SETFMT:
    703 		    case PARSEIOC_GETFMT:
    704 		    case PARSEIOC_SETCS:
    705 			if (iocp->ioc_count == sizeof(parsectl_t))
    706 			{
    707 				parsectl_t *dct = (parsectl_t *)(void *)mp->b_cont->b_rptr;
    708 
    709 				switch (iocp->ioc_cmd)
    710 				{
    711 				    case PARSEIOC_TIMECODE:
    712 					parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_TIMECODE\n"));
    713 					ok = parse_timecode(dct, &parse->parse_io);
    714 					break;
    715 
    716 				    case PARSEIOC_SETFMT:
    717 					parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETFMT\n"));
    718 					ok = parse_setfmt(dct, &parse->parse_io);
    719 					break;
    720 
    721 				    case PARSEIOC_GETFMT:
    722 					parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETFMT\n"));
    723 					ok = parse_getfmt(dct, &parse->parse_io);
    724 					break;
    725 
    726 				    case PARSEIOC_SETCS:
    727 					parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETCS\n"));
    728 					ok = parse_setcs(dct, &parse->parse_io);
    729 					break;
    730 				}
    731 				mp->b_datap->db_type = ok ? M_IOCACK : M_IOCNAK;
    732 			}
    733 			else
    734 			{
    735 				mp->b_datap->db_type = M_IOCNAK;
    736 			}
    737 			parseprintf(DD_WPUT,("parse: parsewput qreply - %s\n", (mp->b_datap->db_type == M_IOCNAK) ? "M_IOCNAK" : "M_IOCACK"));
    738 			qreply(q, mp);
    739 			break;
    740 		}
    741 	}
    742 	return 0;
    743 }
    744 
    745 /*
    746  * read characters from streams buffers
    747  */
    748 static unsigned long
    749 rdchar(
    750        register mblk_t **mp
    751        )
    752 {
    753 	while (*mp != (mblk_t *)NULL)
    754 	{
    755 		if ((*mp)->b_wptr - (*mp)->b_rptr)
    756 		{
    757 			return (unsigned long)(*(unsigned char *)((*mp)->b_rptr++));
    758 		}
    759 		else
    760 		{
    761 			register mblk_t *mmp = *mp;
    762 
    763 			*mp = (*mp)->b_cont;
    764 			freeb(mmp);
    765 		}
    766 	}
    767 	return (unsigned)~0;
    768 }
    769 
    770 /*
    771  * convert incoming data
    772  */
    773 static int
    774 parserput(
    775 	queue_t *q,
    776 	mblk_t *mp
    777 	)
    778 {
    779 	unsigned char type;
    780 
    781 	switch (type = mp->b_datap->db_type)
    782 	{
    783 	    default:
    784 		/*
    785 		 * anything we don't know will be put on queue
    786 		 * the service routine will move it to the next one
    787 		 */
    788 		parseprintf(DD_RPUT,("parse: parserput - forward type 0x%x\n", type));
    789 		if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
    790 		{
    791 			putnext(q, mp);
    792 		}
    793 		else
    794 		    putq(q, mp);
    795 		break;
    796 
    797 	    case M_BREAK:
    798 	    case M_DATA:
    799 		    {
    800 			    register parsestream_t * parse = (parsestream_t *)(void *)q->q_ptr;
    801 			    register mblk_t *nmp;
    802 			    register unsigned long ch;
    803 			    timestamp_t ctime;
    804 
    805 			    /*
    806 			     * get time on packet delivery
    807 			     */
    808 			    uniqtime(&ctime.tv);
    809 
    810 			    if (!(parse->parse_status & PARSE_ENABLE))
    811 			    {
    812 				    parseprintf(DD_RPUT,("parse: parserput - parser disabled - forward type 0x%x\n", type));
    813 				    if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
    814 				    {
    815 					    putnext(q, mp);
    816 				    }
    817 				    else
    818 					putq(q, mp);
    819 			    }
    820 			    else
    821 			    {
    822 				    parseprintf(DD_RPUT,("parse: parserput - M_%s\n", (type == M_DATA) ? "DATA" : "BREAK"));
    823 
    824 				    if (type == M_DATA)
    825 				    {
    826 					    /*
    827 					     * parse packet looking for start an end characters
    828 					     */
    829 					    while (mp != (mblk_t *)NULL)
    830 					    {
    831 						    ch = rdchar(&mp);
    832 						    if (ch != ~0 && parse_ioread(&parse->parse_io, (unsigned int)ch, &ctime))
    833 						    {
    834 							    /*
    835 							     * up up and away (hopefully ...)
    836 							     * don't press it if resources are tight or nobody wants it
    837 							     */
    838 							    nmp = (mblk_t *)NULL;
    839 							    if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
    840 							    {
    841 								    bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
    842 								    nmp->b_wptr += sizeof(parsetime_t);
    843 								    putnext(parse->parse_queue, nmp);
    844 							    }
    845 							    else
    846 								if (nmp) freemsg(nmp);
    847 							    parse_iodone(&parse->parse_io);
    848 						    }
    849 					    }
    850 				    }
    851 				    else
    852 				    {
    853 					    if (parse_ioread(&parse->parse_io, (unsigned int)0, &ctime))
    854 					    {
    855 						    /*
    856 						     * up up and away (hopefully ...)
    857 						     * don't press it if resources are tight or nobody wants it
    858 						     */
    859 						    nmp = (mblk_t *)NULL;
    860 						    if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
    861 						    {
    862 							    bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
    863 							    nmp->b_wptr += sizeof(parsetime_t);
    864 							    putnext(parse->parse_queue, nmp);
    865 						    }
    866 						    else
    867 							if (nmp) freemsg(nmp);
    868 						    parse_iodone(&parse->parse_io);
    869 					    }
    870 					    freemsg(mp);
    871 				    }
    872 				    break;
    873 			    }
    874 		    }
    875 
    876 		    /*
    877 		     * CD PPS support for non direct ISR hack
    878 		     */
    879 	    case M_HANGUP:
    880 	    case M_UNHANGUP:
    881 		    {
    882 			    register parsestream_t * parse = (parsestream_t *)(void *)q->q_ptr;
    883 			    timestamp_t ctime;
    884 			    register mblk_t *nmp;
    885 			    register int status = cd_invert ^ (type == M_UNHANGUP);
    886 
    887 			    uniqtime(&ctime.tv);
    888 
    889 			    parseprintf(DD_RPUT,("parse: parserput - M_%sHANGUP\n", (type == M_HANGUP) ? "" : "UN"));
    890 
    891 			    if ((parse->parse_status & PARSE_ENABLE) &&
    892 				parse_iopps(&parse->parse_io, (int)(status ? SYNC_ONE : SYNC_ZERO), &ctime))
    893 			    {
    894 				    nmp = (mblk_t *)NULL;
    895 				    if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
    896 				    {
    897 					    bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
    898 					    nmp->b_wptr += sizeof(parsetime_t);
    899 					    putnext(parse->parse_queue, nmp);
    900 				    }
    901 				    else
    902 					if (nmp) freemsg(nmp);
    903 				    parse_iodone(&parse->parse_io);
    904 				    freemsg(mp);
    905 			    }
    906 			    else
    907 				if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
    908 				{
    909 					putnext(q, mp);
    910 				}
    911 				else
    912 				    putq(q, mp);
    913 
    914 			    if (status)
    915 			    {
    916 				    parse->parse_ppsclockev.tv = ctime.tv;
    917 				    ++(parse->parse_ppsclockev.serial);
    918 			    }
    919 		    }
    920 	}
    921 	return 0;
    922 }
    923 
    924 static int  init_zs_linemon  (queue_t *, queue_t *);	/* handle line monitor for "zs" driver */
    925 static void close_zs_linemon (queue_t *, queue_t *);
    926 
    927 /*-------------------- CD isr status monitor ---------------*/
    928 
    929 static int
    930 init_linemon(
    931 	register queue_t *q
    932 	)
    933 {
    934 	register queue_t *dq;
    935 
    936 	dq = WR(q);
    937 	/*
    938 	 * we ARE doing very bad things down here (basically stealing ISR
    939 	 * hooks)
    940 	 *
    941 	 * so we chase down the STREAMS stack searching for the driver
    942 	 * and if this is a known driver we insert our ISR routine for
    943 	 * status changes in to the ExternalStatus handling hook
    944 	 */
    945 	while (dq->q_next)
    946 	{
    947 		dq = dq->q_next;		/* skip down to driver */
    948 	}
    949 
    950 	/*
    951 	 * find appropriate driver dependent routine
    952 	 */
    953 	if (dq->q_qinfo && dq->q_qinfo->qi_minfo)
    954 	{
    955 		register char *dname = dq->q_qinfo->qi_minfo->mi_idname;
    956 
    957 		parseprintf(DD_INSTALL, ("init_linemon: driver is \"%s\"\n", dname));
    958 
    959 #ifdef sun
    960 		if (dname && !Strcmp(dname, "zs"))
    961 		{
    962 			return init_zs_linemon(dq, q);
    963 		}
    964 		else
    965 #endif
    966 		{
    967 			parseprintf(DD_INSTALL, ("init_linemon: driver \"%s\" not suitable for CD monitoring\n", dname));
    968 			return 0;
    969 		}
    970 	}
    971 	parseprintf(DD_INSTALL, ("init_linemon: cannot find driver\n"));
    972 	return 0;
    973 }
    974 
    975 static void
    976 close_linemon(
    977 	register queue_t *q,
    978 	register queue_t *my_q
    979 	)
    980 {
    981 	/*
    982 	 * find appropriate driver dependent routine
    983 	 */
    984 	if (q->q_qinfo && q->q_qinfo->qi_minfo)
    985 	{
    986 		register char *dname = q->q_qinfo->qi_minfo->mi_idname;
    987 
    988 #ifdef sun
    989 		if (dname && !Strcmp(dname, "zs"))
    990 		{
    991 			close_zs_linemon(q, my_q);
    992 			return;
    993 		}
    994 		parseprintf(DD_INSTALL, ("close_linemon: cannot find driver close routine for \"%s\"\n", dname));
    995 #endif
    996 	}
    997 	parseprintf(DD_INSTALL, ("close_linemon: cannot find driver name\n"));
    998 }
    999 
   1000 #ifdef sun
   1001 
   1002 #include <sundev/zsreg.h>
   1003 #include <sundev/zscom.h>
   1004 #include <sundev/zsvar.h>
   1005 
   1006 static unsigned long cdmask  = ZSRR0_CD;
   1007 
   1008 struct savedzsops
   1009 {
   1010 	struct zsops  zsops;
   1011 	struct zsops *oldzsops;
   1012 };
   1013 
   1014 struct zsops   *emergencyzs;
   1015 extern void zsopinit   (struct zscom *, struct zsops *);
   1016 static int  zs_xsisr   (struct zscom *);	/* zs external status interupt handler */
   1017 
   1018 static int
   1019 init_zs_linemon(
   1020 	register queue_t *q,
   1021 	register queue_t *my_q
   1022 	)
   1023 {
   1024 	register struct zscom *zs;
   1025 	register struct savedzsops *szs;
   1026 	register parsestream_t  *parsestream = (parsestream_t *)(void *)my_q->q_ptr;
   1027 	/*
   1028 	 * we expect the zsaline pointer in the q_data pointer
   1029 	 * from there on we insert our on EXTERNAL/STATUS ISR routine
   1030 	 * into the interrupt path, before the standard handler
   1031 	 */
   1032 	zs = ((struct zsaline *)(void *)q->q_ptr)->za_common;
   1033 	if (!zs)
   1034 	{
   1035 		/*
   1036 		 * well - not found on startup - just say no (shouldn't happen though)
   1037 		 */
   1038 		return 0;
   1039 	}
   1040 	else
   1041 	{
   1042 		unsigned long s;
   1043 
   1044 		/*
   1045 		 * we do a direct replacement, in case others fiddle also
   1046 		 * if somebody else grabs our hook and we disconnect
   1047 		 * we are in DEEP trouble - panic is likely to be next, sorry
   1048 		 */
   1049 		szs = (struct savedzsops *)(void *)kmem_alloc(sizeof(struct savedzsops));
   1050 
   1051 		if (szs == (struct savedzsops *)0)
   1052 		{
   1053 			parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor NOT installed - no memory\n"));
   1054 
   1055 			return 0;
   1056 		}
   1057 		else
   1058 		{
   1059 			parsestream->parse_data   = (void *)szs;
   1060 
   1061 			s = splhigh();
   1062 
   1063 			parsestream->parse_dqueue = q; /* remember driver */
   1064 
   1065 			szs->zsops            = *zs->zs_ops;
   1066 			szs->zsops.zsop_xsint = zs_xsisr; /* place our bastard */
   1067 			szs->oldzsops         = zs->zs_ops;
   1068 			emergencyzs           = zs->zs_ops;
   1069 
   1070 			zsopinit(zs, &szs->zsops); /* hook it up */
   1071 
   1072 			(void) splx(s);
   1073 
   1074 			parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor installed\n"));
   1075 
   1076 			return 1;
   1077 		}
   1078 	}
   1079 }
   1080 
   1081 /*
   1082  * unregister our ISR routine - must call under splhigh()
   1083  */
   1084 static void
   1085 close_zs_linemon(
   1086 	register queue_t *q,
   1087 	register queue_t *my_q
   1088 	)
   1089 {
   1090 	register struct zscom *zs;
   1091 	register parsestream_t  *parsestream = (parsestream_t *)(void *)my_q->q_ptr;
   1092 
   1093 	zs = ((struct zsaline *)(void *)q->q_ptr)->za_common;
   1094 	if (!zs)
   1095 	{
   1096 		/*
   1097 		 * well - not found on startup - just say no (shouldn't happen though)
   1098 		 */
   1099 		return;
   1100 	}
   1101 	else
   1102 	{
   1103 		register struct savedzsops *szs = (struct savedzsops *)parsestream->parse_data;
   1104 
   1105 		zsopinit(zs, szs->oldzsops); /* reset to previous handler functions */
   1106 
   1107 		kmem_free((caddr_t)szs, sizeof (struct savedzsops));
   1108 
   1109 		parseprintf(DD_INSTALL, ("close_zs_linemon: CD monitor deleted\n"));
   1110 		return;
   1111 	}
   1112 }
   1113 
   1114 #define MAXDEPTH 50		/* maximum allowed stream crawl */
   1115 
   1116 #ifdef PPS_SYNC
   1117 extern void hardpps (struct timeval *, long);
   1118 #ifdef PPS_NEW
   1119 extern struct timeval timestamp;
   1120 #else
   1121 extern struct timeval pps_time;
   1122 #endif
   1123 #endif
   1124 
   1125 /*
   1126  * take external status interrupt (only CD interests us)
   1127  */
   1128 static int
   1129 zs_xsisr(
   1130 	 struct zscom *zs
   1131 	)
   1132 {
   1133 	register struct zsaline *za = (struct zsaline *)(void *)zs->zs_priv;
   1134 	register struct zscc_device *zsaddr = zs->zs_addr;
   1135 	register queue_t *q;
   1136 	register unsigned char zsstatus;
   1137 	register int loopcheck;
   1138 	register char *dname;
   1139 #ifdef PPS_SYNC
   1140 	register unsigned int s;
   1141 	register long usec;
   1142 #endif
   1143 
   1144 	/*
   1145 	 * pick up current state
   1146 	 */
   1147 	zsstatus = zsaddr->zscc_control;
   1148 
   1149 	if ((za->za_rr0 ^ zsstatus) & (cdmask))
   1150 	{
   1151 		timestamp_t cdevent;
   1152 		register int status;
   1153 
   1154 		za->za_rr0 = (za->za_rr0 & ~(cdmask)) | (zsstatus & (cdmask));
   1155 
   1156 #ifdef PPS_SYNC
   1157 		s = splclock();
   1158 #ifdef PPS_NEW
   1159 		usec = timestamp.tv_usec;
   1160 #else
   1161 		usec = pps_time.tv_usec;
   1162 #endif
   1163 #endif
   1164 		/*
   1165 		 * time stamp
   1166 		 */
   1167 		uniqtime(&cdevent.tv);
   1168 
   1169 #ifdef PPS_SYNC
   1170 		(void)splx(s);
   1171 #endif
   1172 
   1173 		/*
   1174 		 * logical state
   1175 		 */
   1176 		status = cd_invert ? (zsstatus & cdmask) == 0 : (zsstatus & cdmask) != 0;
   1177 
   1178 #ifdef PPS_SYNC
   1179 		if (status)
   1180 		{
   1181 			usec = cdevent.tv.tv_usec - usec;
   1182 			if (usec < 0)
   1183 			    usec += 1000000;
   1184 
   1185 			hardpps(&cdevent.tv, usec);
   1186 		}
   1187 #endif
   1188 
   1189 		q = za->za_ttycommon.t_readq;
   1190 
   1191 		/*
   1192 		 * ok - now the hard part - find ourself
   1193 		 */
   1194 		loopcheck = MAXDEPTH;
   1195 
   1196 		while (q)
   1197 		{
   1198 			if (q->q_qinfo && q->q_qinfo->qi_minfo)
   1199 			{
   1200 				dname = q->q_qinfo->qi_minfo->mi_idname;
   1201 
   1202 				if (!Strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname))
   1203 				{
   1204 					/*
   1205 					 * back home - phew (hopping along stream queues might
   1206 					 * prove dangerous to your health)
   1207 					 */
   1208 
   1209 					if ((((parsestream_t *)(void *)q->q_ptr)->parse_status & PARSE_ENABLE) &&
   1210 					    parse_iopps(&((parsestream_t *)(void *)q->q_ptr)->parse_io, (int)(status ? SYNC_ONE : SYNC_ZERO), &cdevent))
   1211 					{
   1212 						/*
   1213 						 * XXX - currently we do not pass up the message, as
   1214 						 * we should.
   1215 						 * for a correct behaviour wee need to block out
   1216 						 * processing until parse_iodone has been posted via
   1217 						 * a softcall-ed routine which does the message pass-up
   1218 						 * right now PPS information relies on input being
   1219 						 * received
   1220 						 */
   1221 						parse_iodone(&((parsestream_t *)(void *)q->q_ptr)->parse_io);
   1222 					}
   1223 
   1224 					if (status)
   1225 					{
   1226 						((parsestream_t *)(void *)q->q_ptr)->parse_ppsclockev.tv = cdevent.tv;
   1227 						++(((parsestream_t *)(void *)q->q_ptr)->parse_ppsclockev.serial);
   1228 					}
   1229 
   1230 					parseprintf(DD_ISR, ("zs_xsisr: CD event %s has been posted for \"%s\"\n", status ? "ONE" : "ZERO", dname));
   1231 					break;
   1232 				}
   1233 			}
   1234 
   1235 			q = q->q_next;
   1236 
   1237 			if (!loopcheck--)
   1238 			{
   1239 				panic("zs_xsisr: STREAMS Queue corrupted - CD event");
   1240 			}
   1241 		}
   1242 
   1243 		/*
   1244 		 * only pretend that CD has been handled
   1245 		 */
   1246 		ZSDELAY(2);
   1247 
   1248 		if (!((za->za_rr0 ^ zsstatus) & ~(cdmask)))
   1249 		{
   1250 			/*
   1251 			 * all done - kill status indication and return
   1252 			 */
   1253 			zsaddr->zscc_control = ZSWR0_RESET_STATUS; /* might kill other conditions here */
   1254 			return 0;
   1255 		}
   1256 	}
   1257 
   1258 	if (zsstatus & cdmask)	/* fake CARRIER status */
   1259 		za->za_flags |= ZAS_CARR_ON;
   1260 	else
   1261 		za->za_flags &= ~ZAS_CARR_ON;
   1262 
   1263 	/*
   1264 	 * we are now gathered here to process some unusual external status
   1265 	 * interrupts.
   1266 	 * any CD events have also been handled and shouldn't be processed
   1267 	 * by the original routine (unless we have a VERY busy port pin)
   1268 	 * some initializations are done here, which could have been done before for
   1269 	 * both code paths but have been avoided for minimum path length to
   1270 	 * the uniq_time routine
   1271 	 */
   1272 	dname = (char *) 0;
   1273 	q = za->za_ttycommon.t_readq;
   1274 
   1275 	loopcheck = MAXDEPTH;
   1276 
   1277 	/*
   1278 	 * the real thing for everything else ...
   1279 	 */
   1280 	while (q)
   1281 	{
   1282 		if (q->q_qinfo && q->q_qinfo->qi_minfo)
   1283 		{
   1284 			dname = q->q_qinfo->qi_minfo->mi_idname;
   1285 			if (!Strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname))
   1286 			{
   1287 				register int (*zsisr) (struct zscom *);
   1288 
   1289 				/*
   1290 				 * back home - phew (hopping along stream queues might
   1291 				 * prove dangerous to your health)
   1292 				 */
   1293 				if ((zsisr = ((struct savedzsops *)((parsestream_t *)(void *)q->q_ptr)->parse_data)->oldzsops->zsop_xsint))
   1294 					return zsisr(zs);
   1295 				else
   1296 				    panic("zs_xsisr: unable to locate original ISR");
   1297 
   1298 				parseprintf(DD_ISR, ("zs_xsisr: non CD event was processed for \"%s\"\n", dname));
   1299 				/*
   1300 				 * now back to our program ...
   1301 				 */
   1302 				return 0;
   1303 			}
   1304 		}
   1305 
   1306 		q = q->q_next;
   1307 
   1308 		if (!loopcheck--)
   1309 		{
   1310 			panic("zs_xsisr: STREAMS Queue corrupted - non CD event");
   1311 		}
   1312 	}
   1313 
   1314 	/*
   1315 	 * last resort - shouldn't even come here as it indicates
   1316 	 * corrupted TTY structures
   1317 	 */
   1318 	printf("zs_zsisr: looking for \"%s\" - found \"%s\" - taking EMERGENCY path\n", parseinfo.st_rdinit->qi_minfo->mi_idname, dname ? dname : "-NIL-");
   1319 
   1320 	if (emergencyzs && emergencyzs->zsop_xsint)
   1321 	    emergencyzs->zsop_xsint(zs);
   1322 	else
   1323 	    panic("zs_xsisr: no emergency ISR handler");
   1324 	return 0;
   1325 }
   1326 #endif				/* sun */
   1327 
   1328 /*
   1329  * History:
   1330  *
   1331  * parsestreams.c,v
   1332  * Revision 4.11  2005/04/16 17:32:10  kardel
   1333  * update copyright
   1334  *
   1335  * Revision 4.10  2004/11/14 16:06:08  kardel
   1336  * update Id tags
   1337  *
   1338  * Revision 4.9  2004/11/14 15:29:41  kardel
   1339  * support PPSAPI, upgrade Copyright to Berkeley style
   1340  *
   1341  * Revision 4.7  1999/11/28 09:13:53  kardel
   1342  * RECON_4_0_98F
   1343  *
   1344  * Revision 4.6  1998/12/20 23:45:31  kardel
   1345  * fix types and warnings
   1346  *
   1347  * Revision 4.5  1998/11/15 21:23:38  kardel
   1348  * ntp_memset() replicated in Sun kernel files
   1349  *
   1350  * Revision 4.4  1998/06/13 12:15:59  kardel
   1351  * superfluous variable removed
   1352  *
   1353  * Revision 4.3  1998/06/12 15:23:08  kardel
   1354  * fix prototypes
   1355  * adjust for ansi2knr
   1356  *
   1357  * Revision 4.2  1998/05/24 18:16:22  kardel
   1358  * moved copy of shadow status to the beginning
   1359  *
   1360  * Revision 4.1  1998/05/24 09:38:47  kardel
   1361  * streams initiated iopps calls (M_xHANGUP) are now consistent with the
   1362  * respective calls from zs_xsisr()
   1363  * simulation of CARRIER status to avoid unecessary M_xHANGUP messages
   1364  *
   1365  * Revision 4.0  1998/04/10 19:45:38  kardel
   1366  * Start 4.0 release version numbering
   1367  *
   1368  * from V3 3.37 log info deleted 1998/04/11 kardel
   1369  */
   1370