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