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