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