1 /*	$NetBSD: parsestreams.c,v 1.1.1.1 2009/12/13 16:55:24 kardel 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 Universit�t Erlangen-N�rnberg, 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