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