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