xref: /titanic_52/usr/src/uts/sun4u/starcat/io/cvc.c (revision 53dbcc5939527e6d5d52d814e51e364b5e8bb532)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*
28  * MT STREAMS Virtual Console Device Driver
29  */
30 
31 #include <sys/types.h>
32 #include <sys/open.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/signal.h>
36 #include <sys/cred.h>
37 #include <sys/user.h>
38 #include <sys/proc.h>
39 #include <sys/disp.h>
40 #include <sys/vnode.h>
41 #include <sys/uio.h>
42 #include <sys/buf.h>
43 #include <sys/file.h>
44 #include <sys/kmem.h>
45 #include <sys/stat.h>
46 #include <sys/stream.h>
47 #include <sys/stropts.h>
48 #include <sys/strsubr.h>
49 #include <sys/strsun.h>
50 #include <sys/tty.h>
51 #include <sys/ptyvar.h>
52 #include <sys/poll.h>
53 #include <sys/debug.h>
54 #include <sys/conf.h>
55 #include <sys/ddi.h>
56 #include <sys/sunddi.h>
57 #include <sys/errno.h>
58 #include <sys/modctl.h>
59 
60 #include <sys/sc_cvc.h>
61 #include <sys/sc_cvcio.h>
62 #include <sys/iosramio.h>
63 
64 static int	cvc_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
65 static int	cvc_attach(dev_info_t *, ddi_attach_cmd_t);
66 static int	cvc_detach(dev_info_t *, ddi_detach_cmd_t);
67 static int	cvc_open(register queue_t *, dev_t *, int, int, cred_t *);
68 static int	cvc_close(queue_t *, int, cred_t *);
69 static int	cvc_wput(queue_t *, mblk_t *);
70 static int	cvc_wsrv(queue_t *);
71 static void	cvc_ioctl(queue_t *, mblk_t *);
72 static void	cvc_reioctl(void *);
73 static void	cvc_input_daemon(void);
74 static void	cvc_send_to_iosram(mblk_t **chainpp);
75 static void	cvc_flush_queue(void *);
76 static void	cvc_iosram_ops(uint8_t);
77 static void	cvc_getstr(char *cp);
78 static void	cvc_win_resize(int clear_flag);
79 
80 #define	ESUCCESS 0
81 #ifndef	TRUE
82 #define	TRUE	1
83 #define	FALSE	0
84 #endif
85 
86 /*
87  * Private copy of devinfo pointer; cvc_info uses it.
88  */
89 static dev_info_t	*cvcdip;
90 
91 /*
92  * This structure reflects the layout of data in CONI and CONO.  If you are
93  * going to add fields that don't get written into those chunks, be sure to
94  * place them _after_ the buffer field.
95  */
96 typedef struct cvc_buf {
97 	ushort_t	count;
98 	uchar_t		buffer[MAX_XFER_COUTPUT];
99 } cvc_buf_t;
100 
101 typedef struct cvc_s {
102 	bufcall_id_t	cvc_wbufcid;
103 	tty_common_t	cvc_tty;
104 } cvc_t;
105 
106 cvc_t	cvc_common_tty;
107 
108 static struct module_info cvcm_info = {
109 	1313,		/* mi_idnum Bad luck number  ;-) */
110 	"cvc",		/* mi_idname */
111 	0,		/* mi_minpsz */
112 	INFPSZ,		/* mi_maxpsz */
113 	2048,		/* mi_hiwat */
114 	2048		/* mi_lowat */
115 };
116 
117 static struct qinit cvcrinit = {
118 	NULL,		/* qi_putp */
119 	NULL,		/* qi_srvp */
120 	cvc_open,	/* qi_qopen */
121 	cvc_close,	/* qi_qclose */
122 	NULL,		/* qi_qadmin */
123 	&cvcm_info,	/* qi_minfo */
124 	NULL		/* qi_mstat */
125 };
126 
127 static struct qinit cvcwinit = {
128 	cvc_wput,	/* qi_putp */
129 	cvc_wsrv,	/* qi_srvp */
130 	cvc_open,	/* qi_qopen */
131 	cvc_close,	/* qi_qclose */
132 	NULL,		/* qi_qadmin */
133 	&cvcm_info,	/* qi_minfo */
134 	NULL		/* qi_mstat */
135 };
136 
137 struct streamtab	cvcinfo = {
138 	&cvcrinit,	/* st_rdinit */
139 	&cvcwinit,	/* st_wrinit */
140 	NULL,		/* st_muxrinit */
141 	NULL		/* st_muxwrinit */
142 };
143 
144 static krwlock_t	cvclock;	/* lock protecting everything here */
145 static queue_t		*cvcinput_q;	/* queue for console input */
146 static queue_t		*cvcoutput_q;	/* queue for console output */
147 static int		cvc_instance = -1;
148 static int		cvc_stopped = 0;
149 static int		cvc_suspended = 0;
150 
151 kthread_id_t		cvc_input_daemon_thread; /* just to aid debugging */
152 static kmutex_t		cvcmutex;	/* protects input */
153 static kmutex_t		cvc_iosram_input_mutex; /* protects IOSRAM inp buff */
154 static int		input_ok = 0;	/* true when stream is valid */
155 
156 static int		via_iosram = 0; /* toggle switch */
157 static timeout_id_t	cvc_timeout_id = (timeout_id_t)-1;
158 static int		input_daemon_started = 0;
159 
160 /* debugging functions */
161 #ifdef DEBUG
162 uint32_t cvc_dbg_flags = 0x0;
163 static void cvc_dbg(uint32_t flag, char *fmt,
164 	uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5);
165 #endif
166 
167 /*
168  * Module linkage information for the kernel.
169  */
170 
171 DDI_DEFINE_STREAM_OPS(cvcops, nulldev, nulldev, cvc_attach, cvc_detach,
172 		    nodev, cvc_info, (D_NEW|D_MTPERQ|D_MP), &cvcinfo,
173 		    ddi_quiesce_not_supported);
174 
175 extern int nodev(), nulldev();
176 extern struct mod_ops mod_driverops;
177 
178 static struct modldrv modldrv = {
179 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
180 	"CVC driver 'cvc'",
181 	&cvcops,	/* driver ops */
182 };
183 
184 static struct modlinkage modlinkage = {
185 	MODREV_1,
186 	&modldrv,
187 	NULL
188 };
189 
190 int
191 _init()
192 {
193 	int	status;
194 
195 	status = mod_install(&modlinkage);
196 	if (status == 0) {
197 		mutex_init(&cvcmutex, NULL, MUTEX_DEFAULT, NULL);
198 	}
199 	return (status);
200 }
201 
202 int
203 _fini()
204 {
205 	return (EBUSY);
206 }
207 
208 int
209 _info(struct modinfo *modinfop)
210 {
211 	return (mod_info(&modlinkage, modinfop));
212 }
213 
214 /*
215  * DDI glue routines.
216  */
217 
218 /* ARGSUSED */
219 static int
220 cvc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
221 {
222 	static char	been_here = 0;
223 
224 	if (cmd == DDI_RESUME) {
225 		cvc_suspended = 0;
226 		if (cvcinput_q != NULL) {
227 			qenable(WR(cvcinput_q));
228 		}
229 		return (DDI_SUCCESS);
230 	}
231 
232 	mutex_enter(&cvcmutex);
233 	if (!been_here) {
234 		been_here = 1;
235 		mutex_init(&cvc_iosram_input_mutex, NULL, MUTEX_DEFAULT, NULL);
236 		rw_init(&cvclock, NULL, RW_DRIVER, NULL);
237 		cvc_instance = ddi_get_instance(devi);
238 	} else {
239 #if defined(DEBUG)
240 		cmn_err(CE_NOTE,
241 		    "cvc_attach: called multiple times!! (instance = %d)",
242 		    ddi_get_instance(devi));
243 #endif /* DEBUG */
244 		mutex_exit(&cvcmutex);
245 		return (DDI_SUCCESS);
246 	}
247 	mutex_exit(&cvcmutex);
248 
249 	if (ddi_create_minor_node(devi, "cvc", S_IFCHR,
250 	    0, NULL, NULL) == DDI_FAILURE) {
251 		ddi_remove_minor_node(devi, NULL);
252 		return (-1);
253 	}
254 	cvcdip = devi;
255 	cvcinput_q = NULL;
256 	cvcoutput_q = NULL;
257 
258 	CVC_DBG0(CVC_DBG_ATTACH, "Attached");
259 
260 	return (DDI_SUCCESS);
261 }
262 
263 static int
264 cvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
265 {
266 	if (cmd == DDI_SUSPEND) {
267 		cvc_suspended = 1;
268 	} else {
269 		if (cmd != DDI_DETACH) {
270 			return (DDI_FAILURE);
271 		}
272 		/*
273 		 * XXX this doesn't even begin to address the detach
274 		 * issues - it doesn't terminate the outstanding thread,
275 		 * it doesn't clean up mutexes, kill the timeout routine
276 		 * etc.
277 		 */
278 		if (cvc_instance == ddi_get_instance(dip)) {
279 			ddi_remove_minor_node(dip, NULL);
280 		}
281 	}
282 
283 	CVC_DBG0(CVC_DBG_DETACH, "Detached");
284 
285 	return (DDI_SUCCESS);
286 }
287 
288 /* ARGSUSED */
289 static int
290 cvc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
291 {
292 	register int error;
293 
294 	switch (infocmd) {
295 	case DDI_INFO_DEVT2DEVINFO:
296 		if (cvcdip == NULL) {
297 			error = DDI_FAILURE;
298 		} else {
299 			*result = (void *)cvcdip;
300 			error = DDI_SUCCESS;
301 		}
302 		break;
303 	case DDI_INFO_DEVT2INSTANCE:
304 		*result = (void *)0;
305 		error = DDI_SUCCESS;
306 		break;
307 	default:
308 		error = DDI_FAILURE;
309 	}
310 	return (error);
311 }
312 
313 /* ARGSUSED */
314 static int
315 cvc_open(register queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
316 {
317 	register int		unit = getminor(*devp);
318 	register int		err = DDI_SUCCESS;
319 	tty_common_t		*tty;
320 	cvc_t			*cp;
321 
322 	if (unit != 0)
323 		return (ENXIO);
324 
325 	if (q->q_ptr)
326 		return (0);
327 
328 	cp = (cvc_t *)&cvc_common_tty;
329 	bzero((caddr_t)cp, sizeof (cvc_t));
330 	cp->cvc_wbufcid = 0;
331 	tty = &cp->cvc_tty;
332 	tty->t_readq = q;
333 	tty->t_writeq = WR(q);
334 	WR(q)->q_ptr = q->q_ptr = (caddr_t)cp;
335 	cvcinput_q = RD(q);		/* save for cvc_redir */
336 	qprocson(q);
337 	mutex_enter(&cvcmutex);
338 	input_ok = 1;
339 
340 	/*
341 	 * Start the thread that handles input polling if it hasn't been started
342 	 * previously.
343 	 */
344 	if (!input_daemon_started) {
345 		input_daemon_started = 1;
346 		mutex_exit(&cvcmutex);
347 
348 		cvc_input_daemon_thread = thread_create(NULL, 0,
349 		    cvc_input_daemon, NULL, 0, &p0, TS_RUN, minclsyspri);
350 		CVC_DBG0(CVC_DBG_IOSRAM_RD, "Started input daemon");
351 	} else {
352 		mutex_exit(&cvcmutex);
353 	}
354 
355 	/*
356 	 * Set the console window size.
357 	 */
358 	mutex_enter(&cvc_iosram_input_mutex);
359 	cvc_win_resize(FALSE);
360 	mutex_exit(&cvc_iosram_input_mutex);
361 
362 	CVC_DBG0(CVC_DBG_OPEN, "Plumbed successfully");
363 
364 	return (err);
365 }
366 
367 /* ARGSUSED */
368 static int
369 cvc_close(queue_t *q, int flag, cred_t *crp)
370 {
371 	register int		err = DDI_SUCCESS;
372 	register cvc_t		*cp;
373 
374 	mutex_enter(&cvcmutex);
375 	input_ok = 0;
376 	mutex_exit(&cvcmutex);
377 
378 	cp = q->q_ptr;
379 	if (cp->cvc_wbufcid != 0) {
380 		unbufcall(cp->cvc_wbufcid);
381 	}
382 	ttycommon_close(&cp->cvc_tty);
383 	WR(q)->q_ptr = q->q_ptr = NULL;
384 	cvcinput_q = NULL;
385 	bzero((caddr_t)cp, sizeof (cvc_t));
386 	qprocsoff(q);
387 
388 	CVC_DBG0(CVC_DBG_CLOSE, "Un-plumbed successfully");
389 
390 	return (err);
391 }
392 
393 
394 /*
395  * cvc_wput()
396  *	cn driver does a strwrite of console output data to rconsvp which has
397  *	been set by consconfig. The data enters the cvc stream at the streamhead
398  *	and flows thru ttycompat and ldterm which have been pushed on the
399  *	stream.  Console output data gets sent out either to cvcredir, if the
400  *	network path is available and selected, or to IOSRAM otherwise.  Data is
401  *	sent to cvcredir via its read queue (cvcoutput_q, which gets set in
402  *	cvc_register()).  If the IOSRAM path is selected, or if previous mblks
403  *	are currently queued up for processing, the new mblk will be queued
404  *	and handled later on by cvc_wsrv.
405  */
406 static int
407 cvc_wput(queue_t *q, mblk_t *mp)
408 {
409 	int		error = 0;
410 
411 	rw_enter(&cvclock, RW_READER);
412 
413 	CVC_DBG2(CVC_DBG_WPUT, "mp 0x%x db_type 0x%x",
414 	    mp, mp->b_datap->db_type);
415 
416 	switch (mp->b_datap->db_type) {
417 
418 		case M_IOCTL:
419 		case M_CTL: {
420 			struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
421 
422 			switch (iocp->ioc_cmd) {
423 				/*
424 				 * These ioctls are only supposed to be
425 				 * processed after everything else that is
426 				 * already queued awaiting processing, so throw
427 				 * them on the queue and let cvc_wsrv handle
428 				 * them.
429 				 */
430 				case TCSETSW:
431 				case TCSETSF:
432 				case TCSETAW:
433 				case TCSETAF:
434 				case TCSBRK:
435 					putq(q, mp);
436 					break;
437 
438 				default:
439 					cvc_ioctl(q, mp);
440 			}
441 			break;
442 		}
443 
444 		case M_FLUSH:
445 			if (*mp->b_rptr & FLUSHW) {
446 				/*
447 				 * Flush our write queue.
448 				 */
449 				flushq(q, FLUSHDATA);
450 				*mp->b_rptr &= ~FLUSHW;
451 			}
452 			if (*mp->b_rptr & FLUSHR) {
453 				flushq(RD(q), FLUSHDATA);
454 				qreply(q, mp);
455 			} else
456 				freemsg(mp);
457 			break;
458 
459 		case M_STOP:
460 			cvc_stopped = 1;
461 			freemsg(mp);
462 			break;
463 
464 		case M_START:
465 			cvc_stopped = 0;
466 			freemsg(mp);
467 			qenable(q);  /* Start up delayed messages */
468 			break;
469 
470 		case M_READ:
471 			/*
472 			 * ldterm handles this (VMIN/VTIME processing).
473 			 */
474 			freemsg(mp);
475 			break;
476 
477 		default:
478 			cmn_err(CE_WARN, "cvc_wput: unexpected mblk type - mp ="
479 			    " 0x%p, type = 0x%x", mp, mp->b_datap->db_type);
480 			freemsg(mp);
481 			break;
482 
483 		case M_DATA:
484 			/*
485 			 * If there are other mblks queued up for transmission,
486 			 * or we're using IOSRAM either because cvcredir hasn't
487 			 * registered yet or because we were configured that
488 			 * way, or cvc has been stopped or suspended, place this
489 			 * mblk on the input queue for future processing.
490 			 * Otherwise, hand it off to cvcredir for transmission
491 			 * via the network.
492 			 */
493 			if (q->q_first != NULL || cvcoutput_q == NULL ||
494 			    via_iosram || cvc_stopped == 1 ||
495 			    cvc_suspended == 1) {
496 				(void) putq(q, mp);
497 			} else {
498 				/*
499 				 * XXX - should canputnext be called here?
500 				 * Starfire's cvc doesn't do that, and it
501 				 * appears to work anyway.
502 				 */
503 				(void) putnext(cvcoutput_q, mp);
504 			}
505 			break;
506 
507 	}
508 	rw_exit(&cvclock);
509 	return (error);
510 }
511 
512 /*
513  * cvc_wsrv()
514  *	cvc_wsrv handles mblks that have been queued by cvc_wput either because
515  *	the IOSRAM path was selected or the queue contained preceding mblks.  To
516  *	optimize processing (particularly if the IOSRAM path is selected), all
517  *	mblks are pulled off of the queue and chained together.  Then, if there
518  *	are any mblks on the chain, they are either forwarded to cvcredir or
519  *	sent for IOSRAM processing as appropriate given current circumstances.
520  *	IOSRAM processing may not be able to handle all of the data in the
521  *	chain, in which case the remaining data is placed back on the queue and
522  *	a timeout routine is registered to reschedule cvc_wsrv in the future.
523  *	Automatic scheduling of the queue is disabled (noenable(q)) while
524  *	cvc_wsrv is running to avoid superfluous calls.
525  */
526 static int
527 cvc_wsrv(queue_t *q)
528 {
529 	mblk_t *total_mp = NULL;
530 	mblk_t *mp;
531 
532 	if (cvc_stopped == 1 || cvc_suspended == 1) {
533 		return (0);
534 	}
535 
536 	rw_enter(&cvclock, RW_READER);
537 	noenable(q);
538 
539 	/*
540 	 * If there's already a timeout registered for scheduling this routine
541 	 * in the future, it's a safe bet that we don't want to run right now.
542 	 */
543 	if (cvc_timeout_id != (timeout_id_t)-1) {
544 		enableok(q);
545 		rw_exit(&cvclock);
546 		return (0);
547 	}
548 
549 	/*
550 	 * Start by linking all of the queued M_DATA mblks into a single chain
551 	 * so we can flush as much as possible to IOSRAM (if we choose that
552 	 * route).
553 	 */
554 	while ((mp = getq(q)) != NULL) {
555 		/*
556 		 * Technically, certain IOCTLs are supposed to be processed only
557 		 * after all preceding data has completely "drained".  In an
558 		 * attempt to support that, we delay processing of those IOCTLs
559 		 * until this point.  It is still possible that an IOCTL will be
560 		 * processed before all preceding data is drained, for instance
561 		 * in the case where not all of the preceding data would fit
562 		 * into IOSRAM and we have to place it back on the queue.
563 		 * However, since none of these IOCTLs really appear to have any
564 		 * relevance for cvc, and we weren't supporting delayed
565 		 * processing at _all_ previously, this partial implementation
566 		 * should suffice.  (Fully implementing the delayed IOCTL
567 		 * processing would be unjustifiably difficult given the nature
568 		 * of the underlying IOSRAM console protocol.)
569 		 */
570 		if (mp->b_datap->db_type == M_IOCTL) {
571 			cvc_ioctl(q, mp);
572 			continue;
573 		}
574 
575 		/*
576 		 * We know that only M_IOCTL and M_DATA blocks are placed on our
577 		 * queue.  Since this block isn't an M_IOCTL, it must be M_DATA.
578 		 */
579 		if (total_mp != NULL) {
580 			linkb(total_mp, mp);
581 		} else {
582 			total_mp = mp;
583 		}
584 	}
585 
586 	/*
587 	 * Do we actually have anything to do?
588 	 */
589 	if (total_mp == NULL) {
590 		enableok(q);
591 		rw_exit(&cvclock);
592 		return (0);
593 	}
594 
595 	/*
596 	 * Yes, we do, so send the data to either cvcredir or IOSRAM as
597 	 * appropriate.  In the latter case, we might not be able to transmit
598 	 * everything right now, so re-queue the remainder.
599 	 */
600 	if (cvcoutput_q != NULL && !via_iosram) {
601 		CVC_DBG0(CVC_DBG_NETWORK_WR, "Sending to cvcredir.");
602 		/*
603 		 * XXX - should canputnext be called here?  Starfire's cvc
604 		 * doesn't do that, and it appears to work anyway.
605 		 */
606 		(void) putnext(cvcoutput_q, total_mp);
607 	} else {
608 		CVC_DBG0(CVC_DBG_IOSRAM_WR, "Send to IOSRAM.");
609 		cvc_send_to_iosram(&total_mp);
610 		if (total_mp != NULL) {
611 			(void) putbq(q, total_mp);
612 		}
613 	}
614 
615 	/*
616 	 * If there is still data queued at this point, make sure the queue
617 	 * gets scheduled again after an appropriate delay (which has been
618 	 * somewhat arbitrarily selected as half of the SC's input polling
619 	 * frequency).
620 	 */
621 	enableok(q);
622 	if (q->q_first != NULL) {
623 		if (cvc_timeout_id == (timeout_id_t)-1) {
624 			cvc_timeout_id = timeout(cvc_flush_queue,
625 			    NULL, drv_usectohz(CVC_IOSRAM_POLL_USECS / 2));
626 		}
627 	}
628 	rw_exit(&cvclock);
629 	return (0);
630 }
631 
632 
633 /*
634  * cvc_ioctl()
635  *	handle normal console ioctls.
636  */
637 static void
638 cvc_ioctl(register queue_t *q, register mblk_t *mp)
639 {
640 	register cvc_t			*cp = q->q_ptr;
641 	int				datasize;
642 	int				error = 0;
643 
644 	/*
645 	 * Let ttycommon_ioctl take the first shot at processing the ioctl.  If
646 	 * it fails because it can't allocate memory, schedule processing of the
647 	 * ioctl later when a proper buffer is available.  The mblk that
648 	 * couldn't be processed will have been stored in the tty structure by
649 	 * ttycommon_ioctl.
650 	 */
651 	datasize = ttycommon_ioctl(&cp->cvc_tty, q, mp, &error);
652 	if (datasize != 0) {
653 		if (cp->cvc_wbufcid) {
654 			unbufcall(cp->cvc_wbufcid);
655 		}
656 		cp->cvc_wbufcid = bufcall(datasize, BPRI_HI, cvc_reioctl, cp);
657 		return;
658 	}
659 
660 	/*
661 	 * ttycommon_ioctl didn't do anything, but there's nothing we really
662 	 * support either with the exception of TCSBRK, which is supported
663 	 * only to appear a bit more like a serial device for software that
664 	 * expects TCSBRK to work.
665 	 */
666 	if (error != 0) {
667 		struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
668 
669 		if (iocp->ioc_cmd == TCSBRK) {
670 			miocack(q, mp, 0, 0);
671 		} else {
672 			miocnak(q, mp, 0, EINVAL);
673 		}
674 	} else {
675 		qreply(q, mp);
676 	}
677 }
678 
679 
680 /*
681  * cvc_redir()
682  *	called from cvcredir:cvcr_wput() to handle console input
683  *	data. This routine puts the cvcredir write (downstream) data
684  *	onto the cvc read (upstream) queues.
685  */
686 int
687 cvc_redir(mblk_t *mp)
688 {
689 	register struct iocblk	*iocp;
690 	int			rv = 1;
691 
692 	/*
693 	 * This function shouldn't be called if cvcredir hasn't registered yet.
694 	 */
695 	if (cvcinput_q == NULL) {
696 		/*
697 		 * Need to let caller know that it may be necessary for them to
698 		 * free the message buffer, so return 0.
699 		 */
700 		CVC_DBG0(CVC_DBG_REDIR, "redirection not enabled");
701 		cmn_err(CE_WARN, "cvc_redir: cvcinput_q NULL!");
702 		return (0);
703 	}
704 
705 	CVC_DBG1(CVC_DBG_REDIR, "type 0x%x", mp->b_datap->db_type);
706 	if (mp->b_datap->db_type == M_DATA) {
707 		/*
708 		 * XXX - should canputnext be called here?  Starfire's cvc
709 		 * doesn't do that, and it appears to work anyway.
710 		 */
711 		CVC_DBG1(CVC_DBG_NETWORK_RD, "Sending mp 0x%x", mp);
712 		(void) putnext(cvcinput_q, mp);
713 	} else if (mp->b_datap->db_type == M_IOCTL) {
714 		/*
715 		 * The cvcredir driver filters out ioctl mblks we wouldn't
716 		 * understand, so we don't have to check for every conceivable
717 		 * ioc_cmd.  However, additional ioctls may be supported (again)
718 		 * some day, so the code is structured to check the value even
719 		 * though there's only one that is currently supported.
720 		 */
721 		iocp = (struct iocblk *)mp->b_rptr;
722 		if (iocp->ioc_cmd == CVC_DISCONNECT) {
723 			(void) putnextctl(cvcinput_q, M_HANGUP);
724 		}
725 	} else {
726 		/*
727 		 * Since we don't know what this mblk is, we're not going to
728 		 * process it.
729 		 */
730 		CVC_DBG1(CVC_DBG_REDIR, "unrecognized mblk type: %d",
731 		    mp->b_datap->db_type);
732 		rv = 0;
733 	}
734 
735 	return (rv);
736 }
737 
738 
739 /*
740  * cvc_register()
741  *	called from cvcredir to register it's queues.  cvc
742  *	receives data from cn via the streamhead and sends it to cvcredir
743  *	via pointers to cvcredir's queues.
744  */
745 int
746 cvc_register(queue_t *q)
747 {
748 	int error = -1;
749 
750 	if (cvcinput_q == NULL)
751 		cmn_err(CE_WARN, "cvc_register: register w/ no console open!");
752 	rw_enter(&cvclock, RW_WRITER);
753 	if (cvcoutput_q == NULL) {
754 		cvcoutput_q = RD(q);  /* Make sure its the upstream q */
755 		qprocson(cvcoutput_q);	/* must be done within cvclock */
756 		error = 0;
757 	} else {
758 		/*
759 		 * cmn_err will call us, so release lock.
760 		 */
761 		rw_exit(&cvclock);
762 		if (cvcoutput_q == q)
763 			cmn_err(CE_WARN, "cvc_register: duplicate q!");
764 		else
765 			cmn_err(CE_WARN, "cvc_register: nondup q = 0x%p",
766 			    q);
767 		return (error);
768 	}
769 	rw_exit(&cvclock);
770 	return (error);
771 }
772 
773 
774 /*
775  * cvc_unregister()
776  *	called from cvcredir to clear pointers to its queues.
777  *	cvcredir no longer wants to send or receive data.
778  */
779 void
780 cvc_unregister(queue_t *q)
781 {
782 	rw_enter(&cvclock, RW_WRITER);
783 	if (q == cvcoutput_q) {
784 		qprocsoff(cvcoutput_q);	/* must be done within cvclock */
785 		cvcoutput_q = NULL;
786 	} else {
787 		rw_exit(&cvclock);
788 		cmn_err(CE_WARN, "cvc_unregister: q = 0x%p not registered", q);
789 		return;
790 	}
791 	rw_exit(&cvclock);
792 }
793 
794 
795 /*
796  * cvc_reioctl()
797  *	Retry an "ioctl", now that "bufcall" claims we may be able
798  *	to allocate the buffer we need.
799  */
800 static void
801 cvc_reioctl(void *unit)
802 {
803 	register queue_t	*q;
804 	register mblk_t		*mp;
805 	register cvc_t		*cp = (cvc_t *)unit;
806 
807 	/*
808 	 * The bufcall is no longer pending.
809 	 */
810 	if (!cp->cvc_wbufcid) {
811 		return;
812 	}
813 	cp->cvc_wbufcid = 0;
814 	if ((q = cp->cvc_tty.t_writeq) == NULL) {
815 		return;
816 	}
817 	if ((mp = cp->cvc_tty.t_iocpending) != NULL) {
818 		/* not pending any more */
819 		cp->cvc_tty.t_iocpending = NULL;
820 		cvc_ioctl(q, mp);
821 	}
822 }
823 
824 
825 /*
826  * cvc_iosram_ops()
827  *	Process commands sent to cvc from netcon_server via IOSRAM
828  */
829 static void
830 cvc_iosram_ops(uint8_t op)
831 {
832 	int		rval = ESUCCESS;
833 	static uint8_t	stale_op = 0;
834 
835 	ASSERT(MUTEX_HELD(&cvc_iosram_input_mutex));
836 
837 	CVC_DBG1(CVC_DBG_IOSRAM_CNTL, "cntl msg 0x%x", op);
838 
839 	/*
840 	 * If this is a repeated notice of a command that was previously
841 	 * processed but couldn't be cleared due to EAGAIN (tunnel switch in
842 	 * progress), just clear the data_valid flag and return.
843 	 */
844 	if (op == stale_op) {
845 		if (iosram_set_flag(IOSRAM_KEY_CONC, IOSRAM_DATA_INVALID,
846 		    IOSRAM_INT_NONE) == 0) {
847 			stale_op = 0;
848 		}
849 		return;
850 	}
851 	stale_op = 0;
852 
853 	switch (op) {
854 		case CVC_IOSRAM_BREAK:		/* A console break (L1-A) */
855 			abort_sequence_enter((char *)NULL);
856 			break;
857 
858 		case CVC_IOSRAM_DISCONNECT:	/* Break connection, hang up */
859 			if (cvcinput_q)
860 				(void) putnextctl(cvcinput_q, M_HANGUP);
861 			break;
862 
863 		case CVC_IOSRAM_VIA_NET:	/* console via network */
864 			via_iosram = 0;
865 			break;
866 
867 		case CVC_IOSRAM_VIA_IOSRAM:	/* console via iosram */
868 			via_iosram = 1;
869 			/*
870 			 * Tell cvcd to close any network connection it has.
871 			 */
872 			rw_enter(&cvclock, RW_READER);
873 			if (cvcoutput_q != NULL) {
874 				(void) putnextctl(cvcoutput_q, M_HANGUP);
875 			}
876 			rw_exit(&cvclock);
877 			break;
878 
879 		case CVC_IOSRAM_WIN_RESIZE:	/* console window size data */
880 			/*
881 			 * In the case of window resizing, we don't want to
882 			 * record a stale_op value because we should always use
883 			 * the most recent winsize info, which could change
884 			 * between the time that we fail to clear the flag and
885 			 * the next time we try to process the command.  So,
886 			 * we'll just let cvc_win_resize clear the data_valid
887 			 * flag itself (hence the TRUE parameter) and not worry
888 			 * about whether or not it succeeds.
889 			 */
890 			cvc_win_resize(TRUE);
891 			return;
892 			/* NOTREACHED */
893 
894 		default:
895 			cmn_err(CE_WARN, "cvc: unknown IOSRAM opcode %d", op);
896 			break;
897 	}
898 
899 	/*
900 	 * Clear CONC's data_valid flag to indicate that the chunk is available
901 	 * for further communications.  If the flag can't be cleared due to an
902 	 * error, record the op value so we'll know to ignore it when we see it
903 	 * on the next poll.
904 	 */
905 	rval = iosram_set_flag(IOSRAM_KEY_CONC, IOSRAM_DATA_INVALID,
906 	    IOSRAM_INT_NONE);
907 	if (rval != 0) {
908 		stale_op = op;
909 		if (rval != EAGAIN) {
910 			cmn_err(CE_WARN,
911 			    "cvc_iosram_ops: set flag for cntlbuf ret %d",
912 			    rval);
913 		}
914 	}
915 }
916 
917 
918 /*
919  * cvc_send_to_iosram()
920  *	Flush as much data as possible to the CONO chunk.  If successful, free
921  *	any mblks that were completely transmitted, update the b_rptr field in
922  *	the first remaining mblk if it was partially transmitted, and update the
923  *	caller's pointer to the new head of the mblk chain.  Since the software
924  *	that will be pulling this data out of IOSRAM (dxs on the SC) is just
925  *	polling at some frequency, we avoid attempts to flush data to IOSRAM any
926  *	faster than a large divisor of that polling frequency.
927  *
928  *	Note that "cvc_buf_t out" is only declared "static" to keep it from
929  *	being allocated on the stack.  Allocating 1K+ structures on the stack
930  *	seems rather antisocial.
931  */
932 static void
933 cvc_send_to_iosram(mblk_t **chainpp)
934 {
935 	int			rval;
936 	uint8_t			dvalid;
937 	uchar_t			*cp;
938 	mblk_t			*mp;
939 	mblk_t			*last_empty_mp;
940 	static clock_t		last_flush = (clock_t)-1;
941 	static cvc_buf_t	out;   /* see note above about static */
942 
943 	ASSERT(chainpp != NULL);
944 
945 	/*
946 	 * We _do_ have something to do, right?
947 	 */
948 	if (*chainpp == NULL) {
949 		return;
950 	}
951 
952 	/*
953 	 * We can actually increase throughput by throttling back on attempts to
954 	 * flush data to IOSRAM, since trying to write every little bit of data
955 	 * as it shows up will actually generate more delays waiting for the SC
956 	 * to pick up each of those bits.  Instead, we'll avoid attempting to
957 	 * write data to IOSRAM any faster than half of the polling frequency we
958 	 * expect the SC to be using.
959 	 */
960 	if (ddi_get_lbolt() - last_flush <
961 	    drv_usectohz(CVC_IOSRAM_POLL_USECS / 2)) {
962 		return;
963 	}
964 
965 	/*
966 	 * If IOSRAM is inaccessible or the CONO chunk still holds data that
967 	 * hasn't been picked up by the SC, there's nothing we can do right now.
968 	 */
969 	rval = iosram_get_flag(IOSRAM_KEY_CONO, &dvalid, NULL);
970 	if ((rval != 0) || (dvalid == IOSRAM_DATA_VALID)) {
971 		if ((rval != 0) && (rval != EAGAIN)) {
972 			cmn_err(CE_WARN, "cvc_send_to_iosram: get_flag ret %d",
973 			    rval);
974 		}
975 		return;
976 	}
977 
978 	/*
979 	 * Copy up to MAX_XFER_COUTPUT chars from the mblk chain into a buffer.
980 	 * Don't change any of the mblks just yet, since we can't be certain
981 	 * that we'll be successful in writing data to the CONO chunk.
982 	 */
983 	out.count = 0;
984 	mp = *chainpp;
985 	cp = mp->b_rptr;
986 	last_empty_mp = NULL;
987 	while ((mp != NULL) && (out.count < MAX_XFER_COUTPUT)) {
988 		/*
989 		 * Process as many of the characters in the current mblk as
990 		 * possible.
991 		 */
992 		while ((cp != mp->b_wptr) && (out.count < MAX_XFER_COUTPUT)) {
993 			out.buffer[out.count++] = *cp++;
994 		}
995 
996 		/*
997 		 * Did we process that entire mblk?  If so, move on to the next
998 		 * one.  If not, we're done filling the buffer even if there's
999 		 * space left, because apparently there wasn't room to process
1000 		 * the next character.
1001 		 */
1002 		if (cp != mp->b_wptr) {
1003 			break;
1004 		}
1005 
1006 		/*
1007 		 * When this loop terminates, last_empty_mp will point to the
1008 		 * last mblk that was completely processed, mp will point to the
1009 		 * following mblk (or NULL if no more mblks exist), and cp will
1010 		 * point to the first untransmitted character in the mblk
1011 		 * pointed to by mp.  We'll need this data to update the mblk
1012 		 * chain if all of the data is successfully transmitted.
1013 		 */
1014 		last_empty_mp = mp;
1015 		mp = mp->b_cont;
1016 		cp = (mp != NULL) ? mp->b_rptr : NULL;
1017 	}
1018 
1019 	/*
1020 	 * If we succeeded in preparing some data, try to transmit it through
1021 	 * IOSRAM.  First write the count and the data, which can be done in a
1022 	 * single operation thanks to the buffer structure we use, then set the
1023 	 * data_valid flag if the first step succeeded.
1024 	 */
1025 	if (out.count != 0) {
1026 		rval = iosram_wr(IOSRAM_KEY_CONO, COUNT_OFFSET,
1027 		    CONSBUF_COUNT_SIZE + out.count, (caddr_t)&out);
1028 		if ((rval != 0) && (rval != EAGAIN)) {
1029 			cmn_err(CE_WARN, "cvc_putc: write ret %d", rval);
1030 		}
1031 
1032 		/* if the data write succeeded, set the data_valid flag */
1033 		if (rval == 0) {
1034 			rval = iosram_set_flag(IOSRAM_KEY_CONO,
1035 			    IOSRAM_DATA_VALID, IOSRAM_INT_NONE);
1036 			if ((rval != 0) && (rval != EAGAIN)) {
1037 				cmn_err(CE_WARN,
1038 				    "cvc_putc: set flags for outbuf ret %d",
1039 				    rval);
1040 			}
1041 		}
1042 
1043 		/*
1044 		 * If we successfully transmitted any data, modify the caller's
1045 		 * mblk chain to remove the data that was transmitted, freeing
1046 		 * all mblks that were completely processed.
1047 		 */
1048 		if (rval == 0) {
1049 			last_flush = ddi_get_lbolt();
1050 
1051 			/*
1052 			 * If any data is left over, update the b_rptr field of
1053 			 * the first remaining mblk in case some of its data was
1054 			 * processed.
1055 			 */
1056 			if (mp != NULL) {
1057 				mp->b_rptr = cp;
1058 			}
1059 
1060 			/*
1061 			 * If any mblks have been emptied, unlink them from the
1062 			 * residual chain, free them, and update the caller's
1063 			 * mblk pointer.
1064 			 */
1065 			if (last_empty_mp != NULL) {
1066 				last_empty_mp->b_cont = NULL;
1067 				freemsg(*chainpp);
1068 				*chainpp = mp;
1069 			}
1070 		}
1071 	}
1072 }
1073 
1074 
1075 /*
1076  * cvc_flush_queue()
1077  *	Tell the STREAMS subsystem to schedule cvc_wsrv to process the queue we
1078  *	use to gather console output.
1079  */
1080 /* ARGSUSED */
1081 static void
1082 cvc_flush_queue(void *notused)
1083 {
1084 	rw_enter(&cvclock, RW_WRITER);
1085 	if (cvcinput_q != NULL) {
1086 		qenable(WR(cvcinput_q));
1087 	}
1088 
1089 	cvc_timeout_id = (timeout_id_t)-1;
1090 	rw_exit(&cvclock);
1091 }
1092 
1093 
1094 /*
1095  * cvc_getstr()
1096  *	Poll IOSRAM for console input while available.
1097  */
1098 static void
1099 cvc_getstr(char *cp)
1100 {
1101 	short		count;
1102 	uint8_t		command = 0;
1103 	int		rval = ESUCCESS;
1104 	uint8_t		dvalid = IOSRAM_DATA_INVALID;
1105 	uint8_t		intrpending = 0;
1106 
1107 	mutex_enter(&cvc_iosram_input_mutex);
1108 	while (dvalid == IOSRAM_DATA_INVALID) {
1109 		/*
1110 		 * Check the CONC data_valid flag to see if a control message is
1111 		 * available.
1112 		 */
1113 		rval = iosram_get_flag(IOSRAM_KEY_CONC, &dvalid, &intrpending);
1114 		if ((rval != 0) && (rval != EAGAIN)) {
1115 			cmn_err(CE_WARN,
1116 			    "cvc_getstr: get flag for cntl ret %d", rval);
1117 		}
1118 
1119 		/*
1120 		 * If a control message is available, try to read and process
1121 		 * it.
1122 		 */
1123 		if ((dvalid == IOSRAM_DATA_VALID) && (rval == 0)) {
1124 			/* read the control reg offset */
1125 			rval = iosram_rd(IOSRAM_KEY_CONC,
1126 			    CVC_CTL_OFFSET(command), CVC_CTL_SIZE(command),
1127 			    (caddr_t)&command);
1128 			if ((rval != 0) && (rval != EAGAIN)) {
1129 				cmn_err(CE_WARN,
1130 				    "cvc_getstr: read for command ret %d",
1131 				    rval);
1132 			}
1133 
1134 			/* process the cntl msg and clear the data_valid flag */
1135 			if (rval == 0) {
1136 				cvc_iosram_ops(command);
1137 			}
1138 		}
1139 
1140 		/*
1141 		 * Check the CONI data_valid flag to see if console input data
1142 		 * is available.
1143 		 */
1144 		rval = iosram_get_flag(IOSRAM_KEY_CONI, &dvalid, &intrpending);
1145 		if ((rval != 0) && (rval != EAGAIN)) {
1146 			cmn_err(CE_WARN,
1147 			    "cvc_getstr: get flag for inbuf ret %d",
1148 			    rval);
1149 		}
1150 		if ((rval != 0) || (dvalid != IOSRAM_DATA_VALID)) {
1151 			goto retry;
1152 		}
1153 
1154 		/*
1155 		 * Try to read the count.
1156 		 */
1157 		rval = iosram_rd(IOSRAM_KEY_CONI, COUNT_OFFSET,
1158 		    CONSBUF_COUNT_SIZE, (caddr_t)&count);
1159 		if (rval != 0) {
1160 			if (rval != EAGAIN) {
1161 				cmn_err(CE_WARN,
1162 				    "cvc_getstr: read for count ret %d", rval);
1163 			}
1164 			goto retry;
1165 		}
1166 
1167 		/*
1168 		 * If there is data to be read, try to read it.
1169 		 */
1170 		if (count != 0) {
1171 			rval = iosram_rd(IOSRAM_KEY_CONI, DATA_OFFSET, count,
1172 			    (caddr_t)cp);
1173 			if (rval != 0) {
1174 				if (rval != EAGAIN) {
1175 					cmn_err(CE_WARN,
1176 					    "cvc_getstr: read for count ret %d",
1177 					    rval);
1178 				}
1179 				goto retry;
1180 			}
1181 			cp[count] = '\0';
1182 		}
1183 
1184 		/*
1185 		 * Try to clear the data_valid flag to indicate that whatever
1186 		 * was in CONI was read successfully.  If successful, and some
1187 		 * data was read, break out of the loop to return to the caller.
1188 		 */
1189 		rval = iosram_set_flag(IOSRAM_KEY_CONI, IOSRAM_DATA_INVALID,
1190 		    IOSRAM_INT_NONE);
1191 		if (rval != 0) {
1192 			if (rval != EAGAIN) {
1193 				cmn_err(CE_WARN,
1194 				    "cvc_getstr: set flag for inbuf ret %d",
1195 				    rval);
1196 			}
1197 		} else if (count != 0) {
1198 			CVC_DBG1(CVC_DBG_IOSRAM_RD, "Read 0x%x", count);
1199 			break;
1200 		}
1201 
1202 		/*
1203 		 * Use a smaller delay between checks of IOSRAM for input
1204 		 * when cvcd/cvcredir are not running or "via_iosram" has
1205 		 * been set.
1206 		 * We don't go away completely when i/o is going through the
1207 		 * network via cvcd since a command may be sent via IOSRAM
1208 		 * to switch if the network is down or hung.
1209 		 */
1210 retry:
1211 		if ((cvcoutput_q == NULL) || (via_iosram))
1212 			delay(drv_usectohz(CVC_IOSRAM_POLL_USECS));
1213 		else
1214 			delay(drv_usectohz(CVC_IOSRAM_POLL_USECS * 10));
1215 
1216 	}
1217 
1218 	mutex_exit(&cvc_iosram_input_mutex);
1219 }
1220 
1221 
1222 /*
1223  * cvc_input_daemon()
1224  *	this function runs as a separate kernel thread and polls IOSRAM for
1225  *	input, and possibly put it on read stream for the console.
1226  *	There are two poll rates (implemented in cvc_getstr):
1227  *		 100 000 uS (10 Hz) - no cvcd communications || via_iosram
1228  *		1000 000 uS ( 1 Hz) - cvcd communications
1229  * 	This continues to run even if there are network console communications
1230  *	in order to handle out-of-band signaling.
1231  */
1232 /* ARGSUSED */
1233 static void
1234 cvc_input_daemon(void)
1235 {
1236 	char		linebuf[MAX_XFER_CINPUT + 1];
1237 	char		*cp;
1238 	mblk_t		*mbp;
1239 	int		c;
1240 	int		dropped_read = 0;
1241 
1242 	for (;;) {
1243 		cvc_getstr(linebuf);
1244 
1245 		mbp = allocb(strlen(linebuf), BPRI_MED);
1246 		if (mbp == NULL) {	/* drop it & go on if no buffer */
1247 			if (!dropped_read) {
1248 				cmn_err(CE_WARN, "cvc_input_daemon: "
1249 				    "dropping IOSRAM reads");
1250 			}
1251 			dropped_read++;
1252 			continue;
1253 		}
1254 
1255 		if (dropped_read) {
1256 			cmn_err(CE_WARN,
1257 			    "cvc_input_daemon: dropped %d IOSRAM reads",
1258 			    dropped_read);
1259 			dropped_read = 0;
1260 		}
1261 
1262 		for (cp = linebuf; *cp != '\0'; cp++) {
1263 			c = (int)*cp;
1264 			if (c == '\r')
1265 				c = '\n';
1266 			c &= 0177;
1267 			*mbp->b_wptr = (char)c;
1268 			mbp->b_wptr++;
1269 		}
1270 		mutex_enter(&cvcmutex);
1271 		if (input_ok) {
1272 			if (cvcinput_q == NULL) {
1273 				cmn_err(CE_WARN,
1274 				    "cvc_input_daemon: cvcinput_q is NULL!");
1275 			} else {
1276 				/*
1277 				 * XXX - should canputnext be called here?
1278 				 * Starfire's cvc doesn't do that, and it
1279 				 * appears to work anyway.
1280 				 */
1281 				(void) putnext(cvcinput_q, mbp);
1282 			}
1283 		} else {
1284 			freemsg(mbp);
1285 		}
1286 		mutex_exit(&cvcmutex);
1287 	}
1288 
1289 	/* NOTREACHED */
1290 }
1291 
1292 /*
1293  * cvc_win_resize()
1294  *	cvc_win_resize will read winsize data from the CONC IOSRAM chunk and set
1295  *	the console window size accordingly.  If indicated by the caller, CONC's
1296  *	data_valid flag will also be cleared.  The flag isn't cleared in all
1297  *	cases because we need to process winsize data at startup without waiting
1298  *	for a command.
1299  */
1300 static void
1301 cvc_win_resize(int clear_flag)
1302 {
1303 	int		rval;
1304 	uint16_t	rows;
1305 	uint16_t	cols;
1306 	uint16_t	xpixels;
1307 	uint16_t	ypixels;
1308 	tty_common_t	*tty;
1309 	cvc_t		*cp;
1310 	struct winsize	ws;
1311 
1312 	/*
1313 	 * Start by reading the new window size out of the CONC chunk and, if
1314 	 * requested, clearing CONC's data_valid flag.  If any of that fails,
1315 	 * return immediately.  (Note that the rather bulky condition in the
1316 	 * two "if" statements takes advantage of C's short-circuit logic
1317 	 * evaluation)
1318 	 */
1319 	if (((rval = iosram_rd(IOSRAM_KEY_CONC, CVC_CTL_OFFSET(winsize_rows),
1320 	    CVC_CTL_SIZE(winsize_rows), (caddr_t)&rows)) != 0) ||
1321 	    ((rval = iosram_rd(IOSRAM_KEY_CONC, CVC_CTL_OFFSET(winsize_cols),
1322 	    CVC_CTL_SIZE(winsize_cols), (caddr_t)&cols)) != 0) ||
1323 	    ((rval = iosram_rd(IOSRAM_KEY_CONC,
1324 	    CVC_CTL_OFFSET(winsize_xpixels), CVC_CTL_SIZE(winsize_xpixels),
1325 	    (caddr_t)&xpixels)) != 0) || ((rval = iosram_rd(IOSRAM_KEY_CONC,
1326 	    CVC_CTL_OFFSET(winsize_ypixels), CVC_CTL_SIZE(winsize_ypixels),
1327 	    (caddr_t)&ypixels)) != 0)) {
1328 		if (rval != EAGAIN) {
1329 			cmn_err(CE_WARN,
1330 			    "cvc_win_resize: read for ctlbuf ret %d", rval);
1331 		}
1332 		return;
1333 	}
1334 
1335 	if (clear_flag && ((rval = iosram_set_flag(IOSRAM_KEY_CONC,
1336 	    IOSRAM_DATA_INVALID, IOSRAM_INT_NONE)) != 0)) {
1337 		if (rval != EAGAIN) {
1338 			cmn_err(CE_WARN,
1339 			    "cvc_win_resize: set_flag for ctlbuf ret %d", rval);
1340 		}
1341 		return;
1342 	}
1343 
1344 	/*
1345 	 * Copy the parameters from IOSRAM to a winsize struct.
1346 	 */
1347 	ws.ws_row = rows;
1348 	ws.ws_col = cols;
1349 	ws.ws_xpixel = xpixels;
1350 	ws.ws_ypixel = ypixels;
1351 
1352 	/*
1353 	 * This code was taken from Starfire, and it appears to work correctly.
1354 	 * However, since the original developer felt it necessary to add the
1355 	 * following comment, it's probably worth preserving:
1356 	 *
1357 	 * XXX I hope this is safe...
1358 	 */
1359 	cp = cvcinput_q->q_ptr;
1360 	tty = &cp->cvc_tty;
1361 	mutex_enter(&tty->t_excl);
1362 	if (bcmp((caddr_t)&tty->t_size, (caddr_t)&ws,
1363 	    sizeof (struct winsize))) {
1364 		tty->t_size = ws;
1365 		mutex_exit(&tty->t_excl);
1366 		(void) putnextctl1(cvcinput_q, M_PCSIG,
1367 			SIGWINCH);
1368 	} else {
1369 		mutex_exit(&tty->t_excl);
1370 	}
1371 }
1372 
1373 #ifdef DEBUG
1374 
1375 void
1376 cvc_dbg(uint32_t flag, char *fmt,
1377 	uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5)
1378 {
1379 	char *s = NULL;
1380 	char buf[256];
1381 
1382 	if (cvc_dbg_flags && ((cvc_dbg_flags & flag) == flag)) {
1383 		switch (flag) {
1384 		case CVC_DBG_ATTACH:
1385 			s = "attach";
1386 			break;
1387 		case CVC_DBG_DETACH:
1388 			s = "detach";
1389 			break;
1390 		case CVC_DBG_OPEN:
1391 			s = "open";
1392 			break;
1393 		case CVC_DBG_CLOSE:
1394 			s = "close";
1395 			break;
1396 		case CVC_DBG_IOCTL:
1397 			s = "ioctl";
1398 			break;
1399 		case CVC_DBG_REDIR:
1400 			s = "redir";
1401 			break;
1402 		case CVC_DBG_WPUT:
1403 			s = "wput";
1404 			break;
1405 		case CVC_DBG_WSRV:
1406 			s = "wsrv";
1407 			break;
1408 		case CVC_DBG_IOSRAM_WR:
1409 			s = "iosram_wr";
1410 			break;
1411 		case CVC_DBG_IOSRAM_RD:
1412 			s = "iosram_rd";
1413 			break;
1414 		case CVC_DBG_NETWORK_WR:
1415 			s = "network_wr";
1416 			break;
1417 		case CVC_DBG_NETWORK_RD:
1418 			s = "network_rd";
1419 			break;
1420 		case CVC_DBG_IOSRAM_CNTL:
1421 			s = "iosram_cntlmsg";
1422 			break;
1423 		default:
1424 			s = "Unknown debug flag";
1425 			break;
1426 		}
1427 
1428 		sprintf(buf, "!%s_%s(%d): %s", ddi_driver_name(cvcdip), s,
1429 		    cvc_instance, fmt);
1430 		cmn_err(CE_NOTE, buf, a1, a2, a3, a4, a5);
1431 	}
1432 }
1433 
1434 #endif /* DEBUG */
1435