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