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