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