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