parsesolaris.c revision 290001
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 Universitaet Erlangen-Nuernberg, 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
40static char rcsid[] = "parsesolaris.c,v 4.11 2005/04/16 17:32:10 kardel RELEASE_20050508_A";
41#endif
42
43#include <config.h>
44#include <sys/types.h>
45#include <sys/conf.h>
46#include <sys/errno.h>
47#include <sys/time.h>
48#include <sys/termios.h>
49#include <sys/stream.h>
50#include <sys/strtty.h>
51#include <sys/stropts.h>
52#include <sys/modctl.h>
53#include <sys/ddi.h>
54#include <sys/sunddi.h>
55#ifdef __GNUC__ /* makes it compile on Solaris 2.6 - acc doesn't like it -- GREAT! */
56#include <stdarg.h>
57#endif
58
59#include "ntp_fp.h"
60#include "parse.h"
61#include <sys/parsestreams.h>
62
63/*--------------- loadable driver section -----------------------------*/
64
65static struct streamtab parseinfo;
66
67static struct fmodsw fmod_templ =
68{
69	"parse",			/* module name */
70	&parseinfo,			/* module information */
71	D_NEW|D_MP|D_MTQPAIR,		/* exclusive for q pair */
72	/* lock ptr */
73};
74
75extern struct mod_ops mod_strmodops;
76
77static struct modlstrmod modlstrmod =
78{
79	&mod_strmodops,		/* a STREAMS module */
80	"PARSE      - NTP reference",	/* name this baby - keep room for revision number */
81	&fmod_templ
82};
83
84static struct modlinkage modlinkage =
85{
86	MODREV_1,
87	{
88		&modlstrmod,
89		NULL
90	}
91};
92
93/*
94 * module management routines
95 */
96/*ARGSUSED*/
97int
98_init(
99     void
100     )
101{
102	static char revision[] = "4.6";
103	char *s, *S;
104	char *t;
105
106#ifndef lint
107	t = rcsid;
108#endif
109
110	/*
111	 * copy RCS revision into Drv_name
112	 *
113	 * are we forcing RCS here to do things it was not built for ?
114	 */
115	s = revision;
116	if (*s == '$')
117	{
118		/*
119		 * skip "$Revision: "
120		 * if present. - not necessary on a -kv co (cvs export)
121		 */
122		while (*s && (*s != ' '))
123		{
124			s++;
125		}
126		if (*s == ' ') s++;
127	}
128
129	t = modlstrmod.strmod_linkinfo;
130	while (*t && (*t != ' '))
131	{
132		t++;
133	}
134	if (*t == ' ') t++;
135
136	S = s;
137	while (*S && (((*S >= '0') && (*S <= '9')) || (*S == '.')))
138	{
139		S++;
140	}
141
142	if (*s && *t && (S > s))
143	{
144		if (strlen(t) >= (S - s))
145		{
146			strlcpy(t, s, (unsigned)(S - s));
147		}
148	}
149	return (mod_install(&modlinkage));
150}
151
152/*ARGSUSED*/
153int
154_info(
155      struct modinfo *modinfop
156      )
157{
158	return (mod_info(&modlinkage, modinfop));
159}
160
161/*ARGSUSED*/
162int
163_fini(
164      void
165      )
166{
167	if (mod_remove(&modlinkage) != DDI_SUCCESS)
168	{
169		return EBUSY;
170	}
171	else
172	    return DDI_SUCCESS;
173}
174
175/*--------------- stream module definition ----------------------------*/
176
177static int parseopen  (queue_t *, dev_t *, int, int, cred_t *);
178static int parseclose (queue_t *, int);
179static int parsewput  (queue_t *, mblk_t *);
180static int parserput  (queue_t *, mblk_t *);
181static int parsersvc  (queue_t *);
182
183static struct module_info driverinfo =
184{
185	0,				/* module ID number */
186	fmod_templ.f_name,		/* module name - why repeated here ? compat ?*/
187	0,				/* minimum accepted packet size */
188	INFPSZ,				/* maximum accepted packet size */
189	1,				/* high water mark - flow control */
190	0				/* low water mark - flow control */
191};
192
193static struct qinit rinit =	/* read queue definition */
194{
195	parserput,			/* put procedure */
196	parsersvc,			/* service procedure */
197	parseopen,			/* open procedure */
198	parseclose,			/* close procedure */
199	NULL,				/* admin procedure - NOT USED FOR NOW */
200	&driverinfo,			/* information structure */
201	NULL				/* statistics */
202};
203
204static struct qinit winit =	/* write queue definition */
205{
206	parsewput,			/* put procedure */
207	NULL,				/* service procedure */
208	NULL,				/* open procedure */
209	NULL,				/* close procedure */
210	NULL,				/* admin procedure - NOT USED FOR NOW */
211	&driverinfo,			/* information structure */
212	NULL				/* statistics */
213};
214
215static struct streamtab parseinfo =	/* stream info element for parse driver */
216{
217	&rinit,			/* read queue */
218	&winit,			/* write queue */
219	NULL,				/* read mux */
220	NULL				/* write mux */
221};
222
223/*--------------- driver data structures ----------------------------*/
224
225/*
226 * we usually have an inverted signal - but you
227 * can change this to suit your needs
228 */
229int cd_invert = 1;		/* invert status of CD line - PPS support via CD input */
230
231#ifdef PARSEDEBUG
232int parsedebug = ~0;
233#else
234int parsedebug = 0;
235#endif
236
237/*--------------- module implementation -----------------------------*/
238
239#define TIMEVAL_USADD(_X_, _US_) do {\
240	(_X_)->tv_usec += (_US_);\
241	if ((_X_)->tv_usec >= 1000000)\
242	{\
243	    (_X_)->tv_sec++;\
244	    (_X_)->tv_usec -= 1000000;\
245	}\
246     } while (0)
247
248static int init_linemon (queue_t *);
249static void close_linemon (queue_t *, queue_t *);
250
251#define M_PARSE		0x0001
252#define M_NOPARSE	0x0002
253
254void
255ntp_memset(
256	char *a,
257	int x,
258	int c
259	)
260{
261	while (c-- > 0)
262	    *a++ = x;
263}
264
265static void
266pprintf(
267	int lev,
268	char *form,
269	...
270	)
271{
272	va_list ap;
273
274	va_start(ap, form);
275
276	if (lev & parsedebug)
277		vcmn_err(CE_CONT, form, ap);
278
279	va_end(ap);
280}
281
282static int
283setup_stream(
284	     queue_t *q,
285	     int mode
286	     )
287{
288	register mblk_t *mp;
289
290	pprintf(DD_OPEN,"parse: SETUP_STREAM - setting up stream for q=%x\n", q);
291
292	mp = allocb(sizeof(struct stroptions), BPRI_MED);
293	if (mp)
294	{
295		struct stroptions *str = (void *)mp->b_wptr;
296
297		str->so_flags   = SO_READOPT|SO_HIWAT|SO_LOWAT|SO_ISNTTY;
298		str->so_readopt = (mode == M_PARSE) ? RMSGD : RNORM;
299		str->so_hiwat   = (mode == M_PARSE) ? sizeof(parsetime_t) : 256;
300		str->so_lowat   = 0;
301		mp->b_datap->db_type = M_SETOPTS;
302		mp->b_wptr     += sizeof(struct stroptions);
303		if (!q)
304		    panic("NULL q - strange");
305		putnext(q, mp);
306		return putctl1(WR(q)->q_next, M_CTL, (mode == M_PARSE) ? MC_SERVICEIMM :
307			       MC_SERVICEDEF);
308	}
309	else
310	{
311		pprintf(DD_OPEN, "parse: setup_stream - FAILED - no MEMORY for allocb\n");
312		return 0;
313	}
314}
315
316/*ARGSUSED*/
317static int
318parseopen(
319	  queue_t *q,
320	  dev_t *dev,
321	  int flag,
322	  int sflag,
323	  cred_t *credp
324	  )
325{
326	register parsestream_t *parse;
327	static int notice = 0;
328
329	pprintf(DD_OPEN, "parse: OPEN - q=%x\n", q);
330
331	if (sflag != MODOPEN)
332	{			/* open only for modules */
333		pprintf(DD_OPEN, "parse: OPEN - FAILED - not MODOPEN\n");
334		return EIO;
335	}
336
337	if (q->q_ptr != (caddr_t)NULL)
338	{
339		pprintf(DD_OPEN, "parse: OPEN - FAILED - EXCLUSIVE ONLY\n");
340		return EBUSY;
341	}
342
343	q->q_ptr = (caddr_t)kmem_alloc(sizeof(parsestream_t), KM_SLEEP);
344	if (q->q_ptr == (caddr_t)0)
345	{
346		return ENOMEM;
347	}
348
349	pprintf(DD_OPEN, "parse: OPEN - parse area q=%x, q->q_ptr=%x\n", q, q->q_ptr);
350	WR(q)->q_ptr = q->q_ptr;
351	pprintf(DD_OPEN, "parse: OPEN - WQ parse area q=%x, q->q_ptr=%x\n", WR(q), WR(q)->q_ptr);
352
353	parse = (parsestream_t *) q->q_ptr;
354	bzero((caddr_t)parse, sizeof(*parse));
355	parse->parse_queue     = q;
356	parse->parse_status    = PARSE_ENABLE;
357	parse->parse_ppsclockev.tv.tv_sec  = 0;
358	parse->parse_ppsclockev.tv.tv_usec = 0;
359	parse->parse_ppsclockev.serial     = 0;
360
361	qprocson(q);
362
363	pprintf(DD_OPEN, "parse: OPEN - initializing io subsystem q=%x\n", q);
364
365	if (!parse_ioinit(&parse->parse_io))
366	{
367		/*
368		 * ok guys - beat it
369		 */
370		qprocsoff(q);
371
372		kmem_free((caddr_t)parse, sizeof(parsestream_t));
373
374		return EIO;
375	}
376
377	pprintf(DD_OPEN, "parse: OPEN - initializing stream q=%x\n", q);
378
379	if (setup_stream(q, M_PARSE))
380	{
381		(void) init_linemon(q);	/* hook up PPS ISR routines if possible */
382		pprintf(DD_OPEN, "parse: OPEN - SUCCEEDED\n");
383
384		/*
385		 * I know that you know the delete key, but you didn't write this
386		 * code, did you ? - So, keep the message in here.
387		 */
388		if (!notice)
389		{
390		  cmn_err(CE_CONT, "?%s: Copyright (c) 1993-2005, Frank Kardel\n", modlstrmod.strmod_linkinfo);
391			notice = 1;
392		}
393
394		return 0;
395	}
396	else
397	{
398		qprocsoff(q);
399
400		kmem_free((caddr_t)parse, sizeof(parsestream_t));
401
402		return EIO;
403	}
404}
405
406/*ARGSUSED*/
407static int
408parseclose(
409	   queue_t *q,
410	   int flags
411	   )
412{
413	register parsestream_t *parse = (parsestream_t *)q->q_ptr;
414	register unsigned long s;
415
416	pprintf(DD_CLOSE, "parse: CLOSE\n");
417
418	qprocsoff(q);
419
420	s = splhigh();
421
422	if (parse->parse_dqueue)
423	    close_linemon(parse->parse_dqueue, q);
424	parse->parse_dqueue = (queue_t *)0;
425
426	(void) splx(s);
427
428	parse_ioend(&parse->parse_io);
429
430	kmem_free((caddr_t)parse, sizeof(parsestream_t));
431
432	q->q_ptr = (caddr_t)NULL;
433	WR(q)->q_ptr = (caddr_t)NULL;
434
435	return 0;
436}
437
438/*
439 * move unrecognized stuff upward
440 */
441static int
442parsersvc(
443	  queue_t *q
444	  )
445{
446	mblk_t *mp;
447
448	while ((mp = getq(q)))
449	{
450		if (canputnext(q) || (mp->b_datap->db_type > QPCTL))
451		{
452			putnext(q, mp);
453			pprintf(DD_RSVC, "parse: RSVC - putnext\n");
454		}
455		else
456		{
457			putbq(q, mp);
458			pprintf(DD_RSVC, "parse: RSVC - flow control wait\n");
459			break;
460		}
461	}
462	return 0;
463}
464
465/*
466 * do ioctls and
467 * send stuff down - dont care about
468 * flow control
469 */
470static int
471parsewput(
472	  queue_t *q,
473	  mblk_t *mp
474	  )
475{
476	register int ok = 1;
477	register mblk_t *datap;
478	register struct iocblk *iocp;
479	parsestream_t         *parse = (parsestream_t *)q->q_ptr;
480
481	pprintf(DD_WPUT, "parse: parsewput\n");
482
483	switch (mp->b_datap->db_type)
484	{
485	    default:
486		putnext(q, mp);
487		break;
488
489	    case M_IOCTL:
490		iocp = (void *)mp->b_rptr;
491		switch (iocp->ioc_cmd)
492		{
493		    default:
494			pprintf(DD_WPUT, "parse: parsewput - forward M_IOCTL\n");
495			putnext(q, mp);
496			break;
497
498		    case CIOGETEV:
499			/*
500			 * taken from Craig Leres ppsclock module (and modified)
501			 */
502			datap = allocb(sizeof(struct ppsclockev), BPRI_MED);
503			if (datap == NULL || mp->b_cont)
504			{
505				mp->b_datap->db_type = M_IOCNAK;
506				iocp->ioc_error = (datap == NULL) ? ENOMEM : EINVAL;
507				if (datap != NULL)
508				    freeb(datap);
509				qreply(q, mp);
510				break;
511			}
512
513			mp->b_cont = datap;
514			/* (void *) quiets cast alignment warning */
515			*(struct ppsclockev *)(void *)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 = (void *)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 */
589static unsigned long
590rdchar(
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 */
614static int
615parserput(
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
772static int  init_zs_linemon  (queue_t *, queue_t *);	/* handle line monitor for "zs" driver */
773static void close_zs_linemon (queue_t *, queue_t *);
774
775/*-------------------- CD isr status monitor ---------------*/
776
777static int
778init_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
823static void
824close_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
854static 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
862struct savedzsops
863{
864	struct zsops  zsops;
865	struct zsops *oldzsops;
866};
867
868static struct zsops   *emergencyzs;
869
870static int
871init_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 */
940static void
941close_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 */
986static void
987zs_xsisr(
988	 struct zscom *zs
989	 )
990{
991	register struct asyncline *za = (void *)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