xref: /illumos-gate/usr/src/uts/common/io/wscons.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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 (c) 1987, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * "Workstation console" multiplexor driver for Sun.
28  *
29  * Sends output to the primary frame buffer using the PROM monitor;
30  * gets input from a stream linked below us that is the "keyboard
31  * driver", below which is linked the primary keyboard.
32  */
33 
34 /*
35  * Locking Policy:
36  * This module has a D_MTPERMOD inner perimeter which means STREAMS
37  * only allows one thread to enter this module through STREAMS entry
38  * points each time -- open() close() put() srv() qtimeout().
39  * So for the most time we do not need locking in this module, but with
40  * the following exceptions:
41  *
42  *   - wc shares three global variables (wc_dip, vc_active_console,
43  *     vc_cons_user, vc_avl_root) with virtual console devname part
44  *    (fs/dev/sdev_vtops.c) which get compiled into genunix.
45  *
46  *   - wc_modechg_cb() is a callback function which will triggered when
47  *     framebuffer display mode is changed.
48  *
49  *   - vt_send_hotkeys() is triggered by timeout() which is not STREAMS MT
50  *     safe.
51  *
52  * Based on the fact that virtual console devname part and wc_modechg_cb()
53  * only do read access to the above mentioned shared four global variables,
54  * It is safe to do locking this way:
55  * 1) all read access to the four global variables in THIS WC MODULE do not
56  *    need locking;
57  * 2) all write access to the four global variables in THIS WC MODULE must
58  *    hold vc_lock;
59  * 3) any access to the four global variables in either DEVNAME PART or the
60  *    CALLBACK must hold vc_lock;
61  * 4) other global variables which are only shared in this wc module and only
62  *    accessible through STREAMS entry points such as "vc_last_console",
63  *    "vc_inuse_max_minor", "vc_target_console" and "vc_waitactive_list"
64  *    do not need explict locking.
65  *
66  * wc_modechg_cb() does read access to vc_state_t::vc_flags,
67  * vc_state_t::vc_state_lock is used to protect concurrently accesses to
68  * vc_state_t::vc_flags which may happen from both through STREAMS entry
69  * points and wc_modechg_cb().
70  * Since wc_modechg_cb() only does read access to vc_state_t::vc_flags,
71  * The other parts of wc module (except wc_modechg_cb()) only has to hold
72  * vc_state_t::vc_flags when writing to vc_state_t::vc_flags.
73  *
74  * vt_send_hotkeys() could access vt_pending_vtno at the same time with
75  * the rest of wc module, vt_pending_vtno_lock is used to protect
76  * vt_pending_vtno.
77  *
78  * Lock order: vc_lock -> vc_state_t::vc_state_lock.
79  * No overlap between vc_lock and vt_pending_vtno_lock.
80  */
81 
82 #include <sys/types.h>
83 #include <sys/param.h>
84 #include <sys/signal.h>
85 #include <sys/cred.h>
86 #include <sys/vnode.h>
87 #include <sys/termios.h>
88 #include <sys/termio.h>
89 #include <sys/ttold.h>
90 #include <sys/stropts.h>
91 #include <sys/stream.h>
92 #include <sys/strsun.h>
93 #include <sys/tty.h>
94 #include <sys/buf.h>
95 #include <sys/uio.h>
96 #include <sys/stat.h>
97 #include <sys/sysmacros.h>
98 #include <sys/errno.h>
99 #include <sys/proc.h>
100 #include <sys/procset.h>
101 #include <sys/fault.h>
102 #include <sys/siginfo.h>
103 #include <sys/debug.h>
104 #include <sys/session.h>
105 #include <sys/kmem.h>
106 #include <sys/cpuvar.h>
107 #include <sys/kbio.h>
108 #include <sys/strredir.h>
109 #include <sys/fs/snode.h>
110 #include <sys/consdev.h>
111 #include <sys/conf.h>
112 #include <sys/cmn_err.h>
113 #include <sys/console.h>
114 #include <sys/promif.h>
115 #include <sys/note.h>
116 #include <sys/polled_io.h>
117 #include <sys/systm.h>
118 #include <sys/ddi.h>
119 #include <sys/sunddi.h>
120 #include <sys/sunndi.h>
121 #include <sys/esunddi.h>
122 #include <sys/sunldi.h>
123 #include <sys/debug.h>
124 #include <sys/console.h>
125 #include <sys/ddi_impldefs.h>
126 #include <sys/policy.h>
127 #include <sys/modctl.h>
128 #include <sys/tem.h>
129 #include <sys/wscons.h>
130 #include <sys/vt_impl.h>
131 
132 /* streams stuff */
133 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", copyreq))
134 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", copyresp))
135 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", datab))
136 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", iocblk))
137 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", msgb))
138 _NOTE(SCHEME_PROTECTS_DATA("Unshared data", queue))
139 
140 #define	MINLINES	10
141 #define	MAXLINES	48
142 #define	LOSCREENLINES	34
143 #define	HISCREENLINES	48
144 
145 #define	MINCOLS		10
146 #define	MAXCOLS		120
147 #define	LOSCREENCOLS	80
148 #define	HISCREENCOLS	120
149 
150 struct wscons_state {
151 	dev_t	wc_dev;			/* major/minor for this device */
152 #ifdef _HAVE_TEM_FIRMWARE
153 	int	wc_defer_output;	/* set if output device is "slow" */
154 #endif /* _HAVE_TEM_FIRMWARE */
155 	queue_t	*wc_kbdqueue;		/* "console keyboard" device queue */
156 					/* below us */
157 	cons_polledio_t		wc_polledio; /* polled I/O function pointers */
158 	cons_polledio_t		*wc_kb_polledio; /* keyboard's polledio */
159 	unsigned int	wc_kb_getpolledio_id; /* id for kb CONSOPENPOLLEDIO */
160 	queue_t *wc_pending_wq;
161 	mblk_t	*wc_pending_link;	/* I_PLINK pending for kb polledio */
162 } wscons;
163 
164 /*
165  * This module has a D_MTPERMOD inner perimeter, so we don't need to protect
166  * the variables only shared within this module
167  */
168 _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", wscons))
169 _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", wscons_state))
170 _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vt_stat))
171 _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_waitactive_msg))
172 _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", tty_common))
173 _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vt_mode))
174 _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vt_dispinfo))
175 _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", winsize))
176 _NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data", vc_last_console))
177 
178 #ifdef _HAVE_TEM_FIRMWARE
179 ssize_t wc_cons_wrtvec(promif_redir_arg_t arg, uchar_t *s, size_t n);
180 #endif /* _HAVE_TEM_FIRMWARE */
181 
182 static int	wcopen(queue_t *, dev_t *, int, int, cred_t *);
183 static int	wcclose(queue_t *, int, cred_t *);
184 static int	wcuwput(queue_t *, mblk_t *);
185 static int	wclrput(queue_t *, mblk_t *);
186 
187 static struct module_info wcm_info = {
188 	0,
189 	"wc",
190 	0,
191 	INFPSZ,
192 	2048,
193 	128
194 };
195 
196 static struct qinit wcurinit = {
197 	putq,
198 	NULL,
199 	wcopen,
200 	wcclose,
201 	NULL,
202 	&wcm_info,
203 	NULL
204 };
205 
206 static struct qinit wcuwinit = {
207 	wcuwput,
208 	NULL,
209 	wcopen,
210 	wcclose,
211 	NULL,
212 	&wcm_info,
213 	NULL
214 };
215 
216 static struct qinit wclrinit = {
217 	wclrput,
218 	NULL,
219 	NULL,
220 	NULL,
221 	NULL,
222 	&wcm_info,
223 	NULL
224 };
225 
226 /*
227  * We always putnext directly to the underlying queue.
228  */
229 static struct qinit wclwinit = {
230 	NULL,
231 	NULL,
232 	NULL,
233 	NULL,
234 	NULL,
235 	&wcm_info,
236 	NULL
237 };
238 
239 static struct streamtab wcinfo = {
240 	&wcurinit,
241 	&wcuwinit,
242 	&wclrinit,
243 	&wclwinit,
244 };
245 
246 static int wc_info(dev_info_t *, ddi_info_cmd_t, void *, void **result);
247 static int wc_attach(dev_info_t *, ddi_attach_cmd_t);
248 
249 DDI_DEFINE_STREAM_OPS(wc_ops, nulldev, nulldev, wc_attach, nodev, nodev,
250     wc_info, D_MTPERMOD | D_MP, &wcinfo, ddi_quiesce_not_supported);
251 
252 static void	wcreioctl(void *);
253 static void 	wcioctl(queue_t *, mblk_t *);
254 #ifdef _HAVE_TEM_FIRMWARE
255 static void	wcopoll(void *);
256 static void	wconsout(void *);
257 #endif /* _HAVE_TEM_FIRMWARE */
258 static void	wcrstrt(void *);
259 static void	wcstart(void *);
260 static void	wc_open_kb_polledio(struct wscons_state *wc, queue_t *q,
261 		    mblk_t *mp);
262 static void	wc_close_kb_polledio(struct wscons_state *wc, queue_t *q,
263 		    mblk_t *mp);
264 static void	wc_polled_putchar(cons_polledio_arg_t arg,
265 			unsigned char c);
266 static boolean_t wc_polled_ischar(cons_polledio_arg_t arg);
267 static int	wc_polled_getchar(cons_polledio_arg_t arg);
268 static void	wc_polled_enter(cons_polledio_arg_t arg);
269 static void	wc_polled_exit(cons_polledio_arg_t arg);
270 void	wc_get_size(vc_state_t *pvc);
271 static void	wc_modechg_cb(tem_modechg_cb_arg_t arg);
272 
273 static struct dev_ops wc_ops;
274 
275 /*
276  * Debug printing
277  */
278 #ifndef DPRINTF
279 #ifdef DEBUG
280 /*PRINTFLIKE1*/
281 static void	wc_dprintf(const char *fmt, ...) __KPRINTFLIKE(1);
282 #define	DPRINTF(l, m, args) \
283 	(((l) >= wc_errlevel) && ((m) & wc_errmask) ?	\
284 		wc_dprintf args :			\
285 		(void) 0)
286 /*
287  * Severity levels for printing
288  */
289 #define	PRINT_L0	0	/* print every message */
290 #define	PRINT_L1	1	/* debug */
291 #define	PRINT_L2	2	/* quiet */
292 
293 /*
294  * Masks
295  */
296 #define	PRINT_MASK_ALL		0xFFFFFFFFU
297 uint_t	wc_errmask = PRINT_MASK_ALL;
298 uint_t	wc_errlevel = PRINT_L2;
299 
300 #else
301 #define	DPRINTF(l, m, args)	/* NOTHING */
302 #endif
303 #endif
304 
305 /*
306  * Module linkage information for the kernel.
307  */
308 static struct modldrv modldrv = {
309 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
310 	"Workstation multiplexer Driver 'wc'",
311 	&wc_ops,	/* driver ops */
312 };
313 
314 static struct modlinkage modlinkage = {
315 	MODREV_1,
316 	&modldrv,
317 	NULL
318 };
319 
320 int
321 _init(void)
322 {
323 	int rc;
324 	if ((rc = mod_install(&modlinkage)) == 0)
325 		vt_init();
326 	return (rc);
327 }
328 
329 int
330 _fini(void)
331 {
332 	return (mod_remove(&modlinkage));
333 }
334 
335 int
336 _info(struct modinfo *modinfop)
337 {
338 	return (mod_info(&modlinkage, modinfop));
339 }
340 
341 /*ARGSUSED*/
342 static int
343 wc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
344 {
345 	/* create minor node for workstation hard console */
346 	if (ddi_create_minor_node(devi, "wscons", S_IFCHR,
347 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
348 		ddi_remove_minor_node(devi, NULL);
349 		return (DDI_FAILURE);
350 	}
351 
352 	mutex_enter(&vc_lock);
353 
354 	wc_dip = devi;
355 
356 	bzero(&(wscons.wc_polledio), sizeof (wscons.wc_polledio));
357 
358 	vt_resize(VC_DEFAULT_COUNT);
359 
360 	mutex_exit(&vc_lock);
361 
362 	return (DDI_SUCCESS);
363 }
364 
365 /* ARGSUSED */
366 static int
367 wc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
368 	void **result)
369 {
370 	int error;
371 
372 	switch (infocmd) {
373 	case DDI_INFO_DEVT2DEVINFO:
374 		if (wc_dip == NULL) {
375 			error = DDI_FAILURE;
376 		} else {
377 			*result = (void *) wc_dip;
378 			error = DDI_SUCCESS;
379 		}
380 		break;
381 	case DDI_INFO_DEVT2INSTANCE:
382 		*result = (void *)0;
383 		error = DDI_SUCCESS;
384 		break;
385 	default:
386 		error = DDI_FAILURE;
387 	}
388 	return (error);
389 }
390 
391 #ifdef _HAVE_TEM_FIRMWARE
392 /*
393  * Output buffer. Protected by the per-module inner perimeter.
394  */
395 #define	MAXHIWAT	2000
396 static char obuf[MAXHIWAT];
397 #endif /* _HAVE_TEM_FIRMWARE */
398 
399 static void
400 wc_init_polledio(void)
401 {
402 	static boolean_t polledio_inited = B_FALSE;
403 	_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data",
404 	    polledio_inited))
405 
406 	if (polledio_inited)
407 		return;
408 
409 	polledio_inited = B_TRUE;
410 
411 	/*
412 	 * Initialize the parts of the polled I/O struct that
413 	 * are common to both input and output modes, but which
414 	 * don't flag to the upper layers, which if any of the
415 	 * two modes are available.  We don't know at this point
416 	 * if system is configured CONS_KFB, but we will when
417 	 * consconfig_dacf asks us with CONSOPENPOLLED I/O.
418 	 */
419 	bzero(&(wscons.wc_polledio), sizeof (wscons.wc_polledio));
420 	wscons.wc_polledio.cons_polledio_version =
421 	    CONSPOLLEDIO_V0;
422 	wscons.wc_polledio.cons_polledio_argument =
423 	    (cons_polledio_arg_t)&wscons;
424 	wscons.wc_polledio.cons_polledio_enter =
425 	    wc_polled_enter;
426 	wscons.wc_polledio.cons_polledio_exit =
427 	    wc_polled_exit;
428 
429 #ifdef _HAVE_TEM_FIRMWARE
430 	/*
431 	 * If we're talking directly to a framebuffer, we assume
432 	 * that it's a "slow" device, so that rendering should
433 	 * be deferred to a timeout or softcall so that we write
434 	 * a bunch of characters at once.
435 	 */
436 	wscons.wc_defer_output = prom_stdout_is_framebuffer();
437 #endif /* _HAVE_TEM_FIRMWARE */
438 }
439 
440 /*ARGSUSED*/
441 static int
442 wcopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
443 {
444 	int minor;
445 
446 	wc_init_polledio();
447 	minor = (int)getminor(*devp);
448 	return (vt_open(minor, q, crp));
449 }
450 
451 /*ARGSUSED*/
452 static int
453 wcclose(queue_t *q, int flag, cred_t *crp)
454 {
455 	vc_state_t *pvc = (vc_state_t *)q->q_ptr;
456 
457 	qprocsoff(q);
458 
459 	mutex_enter(&vc_lock);
460 
461 	/*
462 	 * If we are closing the VT node which
463 	 * /dev/vt/console_user points to, revert
464 	 * /dev/vt/console to /dev/console
465 	 */
466 	if (vc_cons_user == pvc->vc_minor)
467 		vc_cons_user = VT_MINOR_INVALID;
468 
469 	if (pvc->vc_minor == 0 || pvc->vc_minor == vc_active_console) {
470 
471 		/*
472 		 * If we lose the system console,
473 		 * no any other active consoles.
474 		 */
475 		if (pvc->vc_minor == 0 && pvc->vc_minor == vc_active_console) {
476 			vc_active_console = VT_MINOR_INVALID;
477 			vc_last_console = VT_MINOR_INVALID;
478 		}
479 
480 		/*
481 		 * just clean for our primary console
482 		 * and active console
483 		 */
484 		mutex_enter(&pvc->vc_state_lock);
485 		vt_clean(q, pvc);
486 		mutex_exit(&pvc->vc_state_lock);
487 
488 		mutex_exit(&vc_lock);
489 
490 		return (0);
491 	}
492 	vt_close(q, pvc, crp);
493 
494 	mutex_exit(&vc_lock);
495 
496 	return (0);
497 }
498 
499 /*
500  * Put procedure for upper write queue.
501  * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
502  * queue up M_BREAK, M_DELAY, and M_DATA messages for processing by
503  * the start routine, and then call the start routine; discard
504  * everything else.
505  */
506 static int
507 wcuwput(queue_t *q, mblk_t *mp)
508 {
509 	vc_state_t *pvc = (vc_state_t *)q->q_ptr;
510 
511 	switch (mp->b_datap->db_type) {
512 
513 	case M_STOP:
514 		mutex_enter(&pvc->vc_state_lock);
515 		pvc->vc_flags |= WCS_STOPPED;
516 		mutex_exit(&pvc->vc_state_lock);
517 
518 		freemsg(mp);
519 		break;
520 
521 	case M_START:
522 		mutex_enter(&pvc->vc_state_lock);
523 		pvc->vc_flags &= ~WCS_STOPPED;
524 		mutex_exit(&pvc->vc_state_lock);
525 
526 		wcstart(pvc);
527 		freemsg(mp);
528 		break;
529 
530 	case M_IOCTL: {
531 		struct iocblk *iocp;
532 		struct linkblk *linkp;
533 
534 		iocp = (struct iocblk *)(void *)mp->b_rptr;
535 		switch (iocp->ioc_cmd) {
536 
537 		case I_LINK:	/* stupid, but permitted */
538 		case I_PLINK:
539 			if (wscons.wc_kbdqueue != NULL) {
540 				/* somebody already linked */
541 				miocnak(q, mp, 0, EINVAL);
542 				return (0);
543 			}
544 			linkp = (struct linkblk *)(void *)mp->b_cont->b_rptr;
545 			wscons.wc_kbdqueue = WR(linkp->l_qbot);
546 			mp->b_datap->db_type = M_IOCACK;
547 			iocp->ioc_count = 0;
548 			wc_open_kb_polledio(&wscons, q, mp);
549 			break;
550 
551 		case I_UNLINK:	/* stupid, but permitted */
552 		case I_PUNLINK:
553 			linkp = (struct linkblk *)(void *)mp->b_cont->b_rptr;
554 			if (wscons.wc_kbdqueue != WR(linkp->l_qbot)) {
555 				/* not us */
556 				miocnak(q, mp, 0, EINVAL);
557 				return (0);
558 			}
559 
560 			mp->b_datap->db_type = M_IOCACK;
561 			iocp->ioc_count = 0;
562 			wc_close_kb_polledio(&wscons, q, mp);
563 			break;
564 
565 		case TCSETSW:
566 		case TCSETSF:
567 		case TCSETAW:
568 		case TCSETAF:
569 		case TCSBRK:
570 			/*
571 			 * The changes do not take effect until all
572 			 * output queued before them is drained.
573 			 * Put this message on the queue, so that
574 			 * "wcstart" will see it when it's done
575 			 * with the output before it.  Poke the
576 			 * start routine, just in case.
577 			 */
578 			(void) putq(q, mp);
579 			wcstart(pvc);
580 			break;
581 
582 		case CONSSETABORTENABLE:
583 		case CONSGETABORTENABLE:
584 		case KIOCSDIRECT:
585 			if (wscons.wc_kbdqueue != NULL) {
586 				wscons.wc_pending_wq = q;
587 				(void) putnext(wscons.wc_kbdqueue, mp);
588 				break;
589 			}
590 			/* fall through */
591 
592 		default:
593 			/*
594 			 * Do it now.
595 			 */
596 			wcioctl(q, mp);
597 			break;
598 		}
599 		break;
600 	}
601 
602 	case M_FLUSH:
603 		if (*mp->b_rptr & FLUSHW) {
604 			/*
605 			 * Flush our write queue.
606 			 */
607 			flushq(q, FLUSHDATA);	/* XXX doesn't flush M_DELAY */
608 			*mp->b_rptr &= ~FLUSHW;	/* it has been flushed */
609 		}
610 		if (*mp->b_rptr & FLUSHR) {
611 			flushq(RD(q), FLUSHDATA);
612 			qreply(q, mp);	/* give the read queues a crack at it */
613 		} else
614 			freemsg(mp);
615 		break;
616 
617 	case M_BREAK:
618 		/*
619 		 * Ignore these, as they make no sense.
620 		 */
621 		freemsg(mp);
622 		break;
623 
624 	case M_DELAY:
625 	case M_DATA:
626 		/*
627 		 * Queue the message up to be transmitted,
628 		 * and poke the start routine.
629 		 */
630 		(void) putq(q, mp);
631 		wcstart(pvc);
632 		break;
633 
634 	case M_IOCDATA:
635 		vt_miocdata(q, mp);
636 		break;
637 
638 	default:
639 		/*
640 		 * "No, I don't want a subscription to Chain Store Age,
641 		 * thank you anyway."
642 		 */
643 		freemsg(mp);
644 		break;
645 	}
646 
647 	return (0);
648 }
649 
650 /*
651  * Retry an "ioctl", now that "qbufcall" claims we may be able to allocate
652  * the buffer we need.
653  */
654 /*ARGSUSED*/
655 static void
656 wcreioctl(void *arg)
657 {
658 	vc_state_t *pvc = (vc_state_t *)arg;
659 	queue_t *q;
660 	mblk_t *mp;
661 
662 	pvc->vc_bufcallid = 0;
663 	q = pvc->vc_ttycommon.t_writeq;
664 	if ((mp = pvc->vc_ttycommon.t_iocpending) != NULL) {
665 		/* not pending any more */
666 		pvc->vc_ttycommon.t_iocpending = NULL;
667 		wcioctl(q, mp);
668 	}
669 }
670 
671 static int
672 wc_getterm(mblk_t *mp)
673 {
674 	char *term;
675 	intptr_t arg;
676 	int flag = ((struct iocblk *)(void *)mp->b_rptr)->ioc_flag;
677 
678 	STRUCT_DECL(cons_getterm, wcterm);
679 	STRUCT_INIT(wcterm, flag);
680 
681 	arg = *((intptr_t *)(void *)mp->b_cont->b_rptr);
682 
683 	if (ddi_copyin((void *)arg, STRUCT_BUF(wcterm),
684 	    STRUCT_SIZE(wcterm), flag) != 0) {
685 		return (EFAULT);
686 	}
687 
688 	if (consmode == CONS_FW) {
689 		/* PROM terminal emulator */
690 		term = "sun";
691 	} else {
692 		/* Kernel terminal emulator */
693 		ASSERT(consmode == CONS_KFB);
694 		term = "sun-color";
695 	}
696 
697 	if (STRUCT_FGET(wcterm, cn_term_len) <
698 	    strlen(term) + 1) {
699 		return (EOVERFLOW);
700 	}
701 
702 	if (ddi_copyout(term,
703 	    STRUCT_FGETP(wcterm, cn_term_type),
704 	    strlen(term) + 1, flag) != 0) {
705 		return (EFAULT);
706 	}
707 
708 	return (0);
709 }
710 
711 /*
712  * Process an "ioctl" message sent down to us.
713  */
714 static void
715 wcioctl(queue_t *q, mblk_t *mp)
716 {
717 	vc_state_t *pvc = (vc_state_t *)q->q_ptr;
718 	struct iocblk *iocp;
719 	size_t datasize;
720 	int error;
721 	long len;
722 
723 	iocp = (struct iocblk *)(void *)mp->b_rptr;
724 
725 	if ((iocp->ioc_cmd & VTIOC) == VTIOC ||
726 	    (iocp->ioc_cmd & KDIOC) == KDIOC) {
727 		vt_ioctl(q, mp);
728 		return;
729 	}
730 
731 	switch (iocp->ioc_cmd) {
732 	case TIOCSWINSZ:
733 		/*
734 		 * Ignore all attempts to set the screen size; the
735 		 * value in the EEPROM is guaranteed (modulo PROM bugs)
736 		 * to be the value used by the PROM monitor code, so it
737 		 * is by definition correct.  Many programs (e.g.,
738 		 * "login" and "tset") will attempt to reset the size
739 		 * to (0, 0) or (34, 80), neither of which is
740 		 * necessarily correct.
741 		 * We just ACK the message, so as not to disturb
742 		 * programs that set the sizes.
743 		 */
744 		iocp->ioc_count = 0;	/* no data returned */
745 		mp->b_datap->db_type = M_IOCACK;
746 		qreply(q, mp);
747 		return;
748 
749 	case CONSOPENPOLLEDIO:
750 		DPRINTF(PRINT_L1, PRINT_MASK_ALL,
751 		    ("wcioctl: CONSOPENPOLLEDIO\n"));
752 
753 		error = miocpullup(mp, sizeof (struct cons_polledio *));
754 		if (error != 0) {
755 			miocnak(q, mp, 0, error);
756 			return;
757 		}
758 
759 		/*
760 		 * We are given an appropriate-sized data block,
761 		 * and return a pointer to our structure in it.
762 		 */
763 		if (consmode == CONS_KFB)
764 			wscons.wc_polledio.cons_polledio_putchar =
765 			    wc_polled_putchar;
766 		*(struct cons_polledio **)(void *)mp->b_cont->b_rptr =
767 		    &wscons.wc_polledio;
768 
769 		mp->b_datap->db_type = M_IOCACK;
770 
771 		qreply(q, mp);
772 		break;
773 
774 	case CONS_GETTERM:
775 		if ((error = wc_getterm(mp)) != 0)
776 			miocnak(q, mp, 0, error);
777 		else
778 			miocack(q, mp, 0, 0);
779 		return;
780 
781 	case WC_OPEN_FB:
782 		/*
783 		 * Start out pessimistic, so that we can just jump to
784 		 * the reply to bail out.
785 		 */
786 		mp->b_datap->db_type = M_IOCNAK;
787 
788 		/*
789 		 * First test:  really, this should be done only from
790 		 * inside the kernel.  Unfortunately, that information
791 		 * doesn't seem to be available in a streams ioctl,
792 		 * so restrict it to root only.  (Perhaps we could check
793 		 * for ioc_cr == kcred.)
794 		 */
795 		if ((iocp->ioc_error = secpolicy_console(iocp->ioc_cr)) != 0)
796 			goto open_fail;
797 
798 		/*
799 		 * Some miscellaneous checks...
800 		 */
801 		iocp->ioc_error = EINVAL;
802 
803 		/*
804 		 * If we don't have exactly one continuation block, fail.
805 		 */
806 		if (mp->b_cont == NULL ||
807 		    mp->b_cont->b_cont != NULL)
808 			goto open_fail;
809 
810 		/*
811 		 * If there's no null terminator in the string, fail.
812 		 */
813 		/* LINTED E_PTRDIFF_OVERFLOW */
814 		len = mp->b_cont->b_wptr - mp->b_cont->b_rptr;
815 		if (memchr(mp->b_cont->b_rptr, 0, len) == NULL)
816 			goto open_fail;
817 
818 		/*
819 		 * NOTE:  should eventually get default
820 		 * dimensions from a property, e.g. screen-#rows.
821 		 */
822 		iocp->ioc_error = tem_info_init((char *)mp->b_cont->b_rptr,
823 		    iocp->ioc_cr);
824 		/*
825 		 * Of course, if the terminal emulator initialization
826 		 * failed, fail.
827 		 */
828 		if (iocp->ioc_error != 0)
829 			goto open_fail;
830 
831 #ifdef	_HAVE_TEM_FIRMWARE
832 		if (prom_stdout_is_framebuffer()) {
833 			/*
834 			 * Drivers in the console stream may emit additional
835 			 * messages before we are ready. This causes text
836 			 * overwrite on the screen. So we set the redirection
837 			 * here. It is safe because the ioctl in consconfig_dacf
838 			 * will succeed and consmode will be set to CONS_KFB.
839 			 */
840 			prom_set_stdout_redirect(wc_cons_wrtvec,
841 			    (promif_redir_arg_t)NULL);
842 
843 		}
844 #endif	/* _HAVE_TEM_FIRMWARE */
845 
846 		tem_register_modechg_cb(wc_modechg_cb,
847 		    (tem_modechg_cb_arg_t)&wscons);
848 
849 		/*
850 		 * ... and succeed.
851 		 */
852 		mp->b_datap->db_type = M_IOCACK;
853 
854 open_fail:
855 		qreply(q, mp);
856 		break;
857 
858 	case WC_CLOSE_FB:
859 		/*
860 		 * There's nothing that can call this, so it's not
861 		 * really implemented.
862 		 */
863 		mp->b_datap->db_type = M_IOCNAK;
864 		/*
865 		 * However, if it were implemented, it would clearly
866 		 * be root-only.
867 		 */
868 		if ((iocp->ioc_error = secpolicy_console(iocp->ioc_cr)) != 0)
869 			goto close_fail;
870 
871 		iocp->ioc_error = EINVAL;
872 
873 close_fail:
874 		qreply(q, mp);
875 		break;
876 
877 	default:
878 
879 		/*
880 		 * The only way in which "ttycommon_ioctl" can fail is
881 		 * if the "ioctl" requires a response containing data
882 		 * to be returned to the user, and no mblk could be
883 		 * allocated for the data.  No such "ioctl" alters our
884 		 * state.  Thus, we always go ahead and do any
885 		 * state-changes the "ioctl" calls for.  If we couldn't
886 		 * allocate the data, "ttycommon_ioctl" has stashed the
887 		 * "ioctl" away safely, so we just call "qbufcall" to
888 		 * request that we be called back when we stand a
889 		 * better chance of allocating the data.
890 		 */
891 		datasize = ttycommon_ioctl(&pvc->vc_ttycommon, q, mp, &error);
892 		if (datasize != 0) {
893 			if (pvc->vc_bufcallid != 0)
894 				qunbufcall(q, pvc->vc_bufcallid);
895 			pvc->vc_bufcallid = qbufcall(q, datasize, BPRI_HI,
896 			    wcreioctl, pvc);
897 			return;
898 		}
899 
900 		if (error < 0) {
901 			if (iocp->ioc_cmd == TCSBRK)
902 				error = 0;
903 			else
904 				error = EINVAL;
905 		}
906 		if (error != 0) {
907 			iocp->ioc_error = error;
908 			mp->b_datap->db_type = M_IOCNAK;
909 		}
910 		qreply(q, mp);
911 		break;
912 	}
913 }
914 
915 /*
916  * This function gets the polled I/O structures from the lower
917  * keyboard driver.  If any initialization or resource allocation
918  * needs to be done by the lower driver, it will be done when
919  * the lower driver services this message.
920  */
921 static void
922 wc_open_kb_polledio(struct wscons_state *wscons, queue_t *q, mblk_t *mp)
923 {
924 	mblk_t *mp2;
925 	struct iocblk *iocp;
926 
927 	DPRINTF(PRINT_L1, PRINT_MASK_ALL,
928 	    ("wc_open_kb_polledio: sending CONSOPENPOLLEDIO\n"));
929 
930 	mp2 = mkiocb(CONSOPENPOLLEDIO);
931 
932 	if (mp2 == NULL) {
933 		/*
934 		 * If we can't get an mblk, then wait for it.
935 		 */
936 		goto nomem;
937 	}
938 
939 	mp2->b_cont = allocb(sizeof (struct cons_polledio *), BPRI_HI);
940 
941 	if (mp2->b_cont == NULL) {
942 		/*
943 		 * If we can't get an mblk, then wait for it, and release
944 		 * the mblk that we have already allocated.
945 		 */
946 		freemsg(mp2);
947 		goto nomem;
948 	}
949 
950 	iocp = (struct iocblk *)(void *)mp2->b_rptr;
951 
952 	iocp->ioc_count = sizeof (struct cons_polledio *);
953 	mp2->b_cont->b_wptr = mp2->b_cont->b_rptr +
954 	    sizeof (struct cons_polledio *);
955 
956 	wscons->wc_pending_wq = q;
957 	wscons->wc_pending_link = mp;
958 	wscons->wc_kb_getpolledio_id = iocp->ioc_id;
959 
960 	putnext(wscons->wc_kbdqueue, mp2);
961 
962 	return;
963 
964 nomem:
965 	iocp = (struct iocblk *)(void *)mp->b_rptr;
966 	iocp->ioc_error = ENOMEM;
967 	mp->b_datap->db_type = M_IOCNAK;
968 	qreply(q, mp);
969 }
970 
971 /*
972  * This function releases the polled I/O structures from the lower
973  * keyboard driver.  If any de-initialization needs to be done, or
974  * any resources need to be released, it will be done when the lower
975  * driver services this message.
976  */
977 static void
978 wc_close_kb_polledio(struct wscons_state *wscons, queue_t *q, mblk_t *mp)
979 {
980 	mblk_t *mp2;
981 	struct iocblk *iocp;
982 
983 	DPRINTF(PRINT_L1, PRINT_MASK_ALL,
984 	    ("wc_close_kb_polledio: sending CONSCLOSEPOLLEDIO\n"));
985 
986 	mp2 = mkiocb(CONSCLOSEPOLLEDIO);
987 
988 	if (mp2 == NULL) {
989 		/*
990 		 * If we can't get an mblk, then wait for it.
991 		 */
992 		goto nomem;
993 	}
994 
995 	mp2->b_cont = allocb(sizeof (struct cons_polledio *), BPRI_HI);
996 
997 	if (mp2->b_cont == NULL) {
998 		/*
999 		 * If we can't get an mblk, then wait for it, and release
1000 		 * the mblk that we have already allocated.
1001 		 */
1002 		freemsg(mp2);
1003 
1004 		goto nomem;
1005 	}
1006 
1007 	iocp = (struct iocblk *)(void *)mp2->b_rptr;
1008 
1009 	iocp->ioc_count = 0;
1010 
1011 	wscons->wc_pending_wq = q;
1012 	wscons->wc_pending_link = mp;
1013 	wscons->wc_kb_getpolledio_id = iocp->ioc_id;
1014 
1015 	putnext(wscons->wc_kbdqueue, mp2);
1016 
1017 	return;
1018 
1019 nomem:
1020 	iocp = (struct iocblk *)(void *)mp->b_rptr;
1021 	iocp->ioc_error = ENOMEM;
1022 	mp->b_datap->db_type = M_IOCNAK;
1023 	qreply(q, mp);
1024 }
1025 
1026 #ifdef _HAVE_TEM_FIRMWARE
1027 /* ARGSUSED */
1028 static void
1029 wcopoll(void *arg)
1030 {
1031 	vc_state_t *pvc = (vc_state_t *)arg;
1032 	queue_t *q;
1033 
1034 	q = pvc->vc_ttycommon.t_writeq;
1035 	pvc->vc_timeoutid = 0;
1036 
1037 	mutex_enter(&pvc->vc_state_lock);
1038 
1039 	/* See if we can continue output */
1040 	if ((pvc->vc_flags & WCS_BUSY) && pvc->vc_pendc != -1) {
1041 		if (prom_mayput((char)pvc->vc_pendc) == 0) {
1042 			pvc->vc_pendc = -1;
1043 			pvc->vc_flags &= ~WCS_BUSY;
1044 			if (!(pvc->vc_flags&(WCS_DELAY|WCS_STOPPED)))
1045 				wcstart(pvc);
1046 		} else
1047 			pvc->vc_timeoutid = qtimeout(q, wcopoll, pvc, 1);
1048 	}
1049 
1050 	mutex_exit(&pvc->vc_state_lock);
1051 }
1052 #endif	/* _HAVE_TEM_FIRMWARE */
1053 
1054 /*
1055  * Restart output on the console after a timeout.
1056  */
1057 /* ARGSUSED */
1058 static void
1059 wcrstrt(void *arg)
1060 {
1061 	vc_state_t *pvc = (vc_state_t *)arg;
1062 
1063 	ASSERT(pvc->vc_ttycommon.t_writeq != NULL);
1064 
1065 	mutex_enter(&pvc->vc_state_lock);
1066 	pvc->vc_flags &= ~WCS_DELAY;
1067 	mutex_exit(&pvc->vc_state_lock);
1068 
1069 	wcstart(pvc);
1070 }
1071 
1072 /*
1073  * get screen terminal for current output
1074  */
1075 static tem_vt_state_t
1076 wc_get_screen_tem(vc_state_t *pvc)
1077 {
1078 	if (!tem_initialized(pvc->vc_tem) ||
1079 	    tem_get_fbmode(pvc->vc_tem) != KD_TEXT)
1080 		return (NULL);
1081 
1082 	return (pvc->vc_tem);
1083 }
1084 
1085 /*
1086  * Start console output
1087  */
1088 static void
1089 wcstart(void *arg)
1090 {
1091 	vc_state_t *pvc = (vc_state_t *)arg;
1092 	tem_vt_state_t ptem = NULL;
1093 #ifdef _HAVE_TEM_FIRMWARE
1094 	int c;
1095 	ssize_t cc;
1096 #endif /* _HAVE_TEM_FIRMWARE */
1097 	queue_t *q;
1098 	mblk_t *bp;
1099 	mblk_t *nbp;
1100 
1101 	/*
1102 	 * If we're waiting for something to happen (delay timeout to
1103 	 * expire, current transmission to finish, output to be
1104 	 * restarted, output to finish draining), don't grab anything
1105 	 * new.
1106 	 */
1107 	if (pvc->vc_flags & (WCS_DELAY|WCS_BUSY|WCS_STOPPED))
1108 		return;
1109 
1110 	q = pvc->vc_ttycommon.t_writeq;
1111 	/*
1112 	 * assumes that we have been called by whoever holds the
1113 	 * exclusionary lock on the write-side queue (protects
1114 	 * vc_flags and vc_pendc).
1115 	 */
1116 	for (;;) {
1117 		if ((bp = getq(q)) == NULL)
1118 			return;	/* nothing to transmit */
1119 
1120 		/*
1121 		 * We have a new message to work on.
1122 		 * Check whether it's a delay or an ioctl (the latter
1123 		 * occurs if the ioctl in question was waiting for the output
1124 		 * to drain).  If it's one of those, process it immediately.
1125 		 */
1126 		switch (bp->b_datap->db_type) {
1127 
1128 		case M_DELAY:
1129 			/*
1130 			 * Arrange for "wcrstrt" to be called when the
1131 			 * delay expires; it will turn WCS_DELAY off,
1132 			 * and call "wcstart" to grab the next message.
1133 			 */
1134 			if (pvc->vc_timeoutid != 0)
1135 				(void) quntimeout(q, pvc->vc_timeoutid);
1136 			pvc->vc_timeoutid = qtimeout(q, wcrstrt, pvc,
1137 			    (clock_t)(*(unsigned char *)bp->b_rptr + 6));
1138 
1139 			mutex_enter(&pvc->vc_state_lock);
1140 			pvc->vc_flags |= WCS_DELAY;
1141 			mutex_exit(&pvc->vc_state_lock);
1142 
1143 			freemsg(bp);
1144 			return;	/* wait for this to finish */
1145 
1146 		case M_IOCTL:
1147 			/*
1148 			 * This ioctl was waiting for the output ahead of
1149 			 * it to drain; obviously, it has.  Do it, and
1150 			 * then grab the next message after it.
1151 			 */
1152 			wcioctl(q, bp);
1153 			continue;
1154 		}
1155 
1156 #ifdef _HAVE_TEM_FIRMWARE
1157 		if (consmode == CONS_KFB) {
1158 #endif /* _HAVE_TEM_FIRMWARE */
1159 			if ((ptem = wc_get_screen_tem(pvc)) != NULL) {
1160 
1161 				for (nbp = bp; nbp != NULL; nbp = nbp->b_cont) {
1162 					if (nbp->b_wptr > nbp->b_rptr) {
1163 						(void) tem_write(ptem,
1164 						    nbp->b_rptr,
1165 						    /* LINTED */
1166 						    nbp->b_wptr - nbp->b_rptr,
1167 						    kcred);
1168 					}
1169 				}
1170 
1171 			}
1172 
1173 			freemsg(bp);
1174 
1175 #ifdef _HAVE_TEM_FIRMWARE
1176 			continue;
1177 		}
1178 
1179 		/* consmode = CONS_FW */
1180 		if (pvc->vc_minor != 0) {
1181 			freemsg(bp);
1182 			continue;
1183 		}
1184 
1185 		/* LINTED E_PTRDIFF_OVERFLOW */
1186 		if ((cc = bp->b_wptr - bp->b_rptr) == 0) {
1187 			freemsg(bp);
1188 			continue;
1189 		}
1190 		/*
1191 		 * Direct output to the frame buffer if this device
1192 		 * is not the "hardware" console.
1193 		 */
1194 		if (wscons.wc_defer_output) {
1195 			/*
1196 			 * Never do output here;
1197 			 * it takes forever.
1198 			 */
1199 			mutex_enter(&pvc->vc_state_lock);
1200 			pvc->vc_flags |= WCS_BUSY;
1201 			mutex_exit(&pvc->vc_state_lock);
1202 
1203 			pvc->vc_pendc = -1;
1204 			(void) putbq(q, bp);
1205 			if (q->q_count > 128) { /* do it soon */
1206 				softcall(wconsout, pvc);
1207 			} else {	/* wait a bit */
1208 				if (pvc->vc_timeoutid != 0)
1209 					(void) quntimeout(q,
1210 					    pvc->vc_timeoutid);
1211 				pvc->vc_timeoutid = qtimeout(q, wconsout,
1212 				    pvc, hz / 30);
1213 			}
1214 			return;
1215 		}
1216 		for (;;) {
1217 			c = *bp->b_rptr++;
1218 			cc--;
1219 			if (prom_mayput((char)c) != 0) {
1220 
1221 				mutex_enter(&pvc->vc_state_lock);
1222 				pvc->vc_flags |= WCS_BUSY;
1223 				mutex_exit(&pvc->vc_state_lock);
1224 
1225 				pvc->vc_pendc = c;
1226 				if (pvc->vc_timeoutid != 0)
1227 					(void) quntimeout(q,
1228 					    pvc->vc_timeoutid);
1229 				pvc->vc_timeoutid = qtimeout(q, wcopoll,
1230 				    pvc, 1);
1231 				if (bp != NULL)
1232 					/* not done with this message yet */
1233 					(void) putbq(q, bp);
1234 				return;
1235 			}
1236 			while (cc <= 0) {
1237 				nbp = bp;
1238 				bp = bp->b_cont;
1239 				freeb(nbp);
1240 				if (bp == NULL)
1241 					return;
1242 				/* LINTED E_PTRDIFF_OVERFLOW */
1243 				cc = bp->b_wptr - bp->b_rptr;
1244 			}
1245 		}
1246 #endif /* _HAVE_TEM_FIRMWARE */
1247 	}
1248 }
1249 
1250 #ifdef _HAVE_TEM_FIRMWARE
1251 /*
1252  * Output to frame buffer console.
1253  * It takes a long time to scroll.
1254  */
1255 /* ARGSUSED */
1256 static void
1257 wconsout(void *arg)
1258 {
1259 	vc_state_t *pvc = (vc_state_t *)arg;
1260 	uchar_t *cp;
1261 	ssize_t cc;
1262 	queue_t *q;
1263 	mblk_t *bp;
1264 	mblk_t *nbp;
1265 	char *current_position;
1266 	ssize_t bytes_left;
1267 
1268 	if ((q = pvc->vc_ttycommon.t_writeq) == NULL) {
1269 		return;	/* not attached to a stream */
1270 	}
1271 
1272 	/*
1273 	 * Set up to copy up to MAXHIWAT bytes.
1274 	 */
1275 	current_position = &obuf[0];
1276 	bytes_left = MAXHIWAT;
1277 	while ((bp = getq(q)) != NULL) {
1278 		if (bp->b_datap->db_type == M_IOCTL) {
1279 			/*
1280 			 * This ioctl was waiting for the output ahead of
1281 			 * it to drain; obviously, it has.  Put it back
1282 			 * so that "wcstart" can handle it, and transmit
1283 			 * what we've got.
1284 			 */
1285 			(void) putbq(q, bp);
1286 			goto transmit;
1287 		}
1288 
1289 		do {
1290 			cp = bp->b_rptr;
1291 			/* LINTED E_PTRDIFF_OVERFLOW */
1292 			cc = bp->b_wptr - cp;
1293 			while (cc != 0) {
1294 				if (bytes_left == 0) {
1295 					/*
1296 					 * Out of buffer space; put this
1297 					 * buffer back on the queue, and
1298 					 * transmit what we have.
1299 					 */
1300 					bp->b_rptr = cp;
1301 					(void) putbq(q, bp);
1302 					goto transmit;
1303 				}
1304 				*current_position++ = *cp++;
1305 				cc--;
1306 				bytes_left--;
1307 			}
1308 			nbp = bp;
1309 			bp = bp->b_cont;
1310 			freeb(nbp);
1311 		} while (bp != NULL);
1312 	}
1313 
1314 transmit:
1315 	if ((cc = MAXHIWAT - bytes_left) != 0)
1316 		console_puts(obuf, cc);
1317 
1318 	mutex_enter(&pvc->vc_state_lock);
1319 	pvc->vc_flags &= ~WCS_BUSY;
1320 	mutex_exit(&pvc->vc_state_lock);
1321 
1322 	wcstart(pvc);
1323 }
1324 #endif /* _HAVE_TEM_FIRMWARE */
1325 
1326 /*
1327  * Put procedure for lower read queue.
1328  * Pass everything up to queue above "upper half".
1329  */
1330 static int
1331 wclrput(queue_t *q, mblk_t *mp)
1332 {
1333 	vc_state_t *pvc;
1334 	queue_t *upq;
1335 	struct iocblk *iocp;
1336 
1337 	pvc = vt_minor2vc(VT_ACTIVE);
1338 
1339 	DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1340 	    ("wclrput: wclrput type = 0x%x\n", mp->b_datap->db_type));
1341 
1342 	switch (mp->b_datap->db_type) {
1343 
1344 	case M_FLUSH:
1345 		if (*mp->b_rptr == FLUSHW || *mp->b_rptr == FLUSHRW) {
1346 			/*
1347 			 * Flush our write queue.
1348 			 */
1349 			/* XXX doesn't flush M_DELAY */
1350 			flushq(WR(q), FLUSHDATA);
1351 			*mp->b_rptr = FLUSHR;	/* it has been flushed */
1352 		}
1353 		if (*mp->b_rptr == FLUSHR || *mp->b_rptr == FLUSHRW) {
1354 			flushq(q, FLUSHDATA);
1355 			*mp->b_rptr = FLUSHW;	/* it has been flushed */
1356 			qreply(q, mp);	/* give the read queues a crack at it */
1357 		} else
1358 			freemsg(mp);
1359 		break;
1360 
1361 	case M_DATA:
1362 		if (consmode == CONS_KFB && vt_check_hotkeys(mp)) {
1363 			freemsg(mp);
1364 			break;
1365 		}
1366 
1367 		if ((upq = pvc->vc_ttycommon.t_readq) != NULL) {
1368 			if (!canput(upq->q_next)) {
1369 				ttycommon_qfull(&pvc->vc_ttycommon, upq);
1370 				wcstart(pvc);
1371 				freemsg(mp);
1372 			} else {
1373 				putnext(upq, mp);
1374 			}
1375 		} else
1376 			freemsg(mp);
1377 		break;
1378 
1379 	case M_IOCACK:
1380 	case M_IOCNAK:
1381 		iocp = (struct iocblk *)(void *)mp->b_rptr;
1382 		if (wscons.wc_pending_link != NULL &&
1383 		    iocp->ioc_id == wscons.wc_kb_getpolledio_id) {
1384 			switch (mp->b_datap->db_type) {
1385 
1386 			case M_IOCACK:
1387 				switch (iocp->ioc_cmd) {
1388 
1389 				case CONSOPENPOLLEDIO:
1390 					DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1391 					    ("wclrput: "
1392 					    "ACK CONSOPENPOLLEDIO\n"));
1393 					wscons.wc_kb_polledio =
1394 					    *(struct cons_polledio **)
1395 					    (void *)mp->b_cont->b_rptr;
1396 					wscons.wc_polledio.
1397 					    cons_polledio_getchar =
1398 					    wc_polled_getchar;
1399 					wscons.wc_polledio.
1400 					    cons_polledio_ischar =
1401 					    wc_polled_ischar;
1402 					break;
1403 
1404 				case CONSCLOSEPOLLEDIO:
1405 					DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1406 					    ("wclrput: "
1407 					    "ACK CONSCLOSEPOLLEDIO\n"));
1408 					wscons.wc_kb_polledio = NULL;
1409 					wscons.wc_kbdqueue = NULL;
1410 					wscons.wc_polledio.
1411 					    cons_polledio_getchar = NULL;
1412 					wscons.wc_polledio.
1413 					    cons_polledio_ischar = NULL;
1414 					break;
1415 				default:
1416 					DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1417 					    ("wclrput: "
1418 					    "ACK UNKNOWN\n"));
1419 				}
1420 
1421 				break;
1422 			case M_IOCNAK:
1423 				/*
1424 				 * Keyboard may or may not support polled I/O.
1425 				 * This ioctl may have been rejected because
1426 				 * we only have the wc->conskbd chain built,
1427 				 * and the keyboard driver has not been linked
1428 				 * underneath conskbd yet.
1429 				 */
1430 				DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1431 				    ("wclrput: NAK\n"));
1432 
1433 				switch (iocp->ioc_cmd) {
1434 
1435 				case CONSCLOSEPOLLEDIO:
1436 					wscons.wc_kb_polledio = NULL;
1437 					wscons.wc_kbdqueue = NULL;
1438 					wscons.wc_polledio.
1439 					    cons_polledio_getchar = NULL;
1440 					wscons.wc_polledio.
1441 					    cons_polledio_ischar = NULL;
1442 					break;
1443 				}
1444 				break;
1445 			}
1446 
1447 			/*
1448 			 * Discard the response, replace it with the
1449 			 * pending response to the I_PLINK, then let it
1450 			 * flow upward.
1451 			 */
1452 			freemsg(mp);
1453 			mp = wscons.wc_pending_link;
1454 			wscons.wc_pending_link = NULL;
1455 			wscons.wc_kb_getpolledio_id = 0;
1456 		}
1457 		/* FALLTHROUGH */
1458 
1459 	default:	/* inc M_ERROR, M_HANGUP, M_IOCACK, M_IOCNAK, ... */
1460 		if (wscons.wc_pending_wq != NULL) {
1461 			qreply(wscons.wc_pending_wq, mp);
1462 			wscons.wc_pending_wq = NULL;
1463 			break;
1464 		}
1465 
1466 		if ((upq = pvc->vc_ttycommon.t_readq) != NULL) {
1467 			putnext(upq, mp);
1468 		} else {
1469 			DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1470 			    ("wclrput: Message DISCARDED\n"));
1471 			freemsg(mp);
1472 		}
1473 		break;
1474 	}
1475 
1476 	return (0);
1477 }
1478 
1479 #ifdef _HAVE_TEM_FIRMWARE
1480 /*
1481  *  This routine exists so that prom_write() can redirect writes
1482  *  to the framebuffer through the kernel terminal emulator, if
1483  *  that configuration is selected during consconfig.
1484  *  When the kernel terminal emulator is enabled, consconfig_dacf
1485  *  sets up the PROM output redirect vector to enter this function.
1486  *  During panic the console will already be powered up as part of
1487  *  calling into the prom_*() layer.
1488  */
1489 /* ARGSUSED */
1490 ssize_t
1491 wc_cons_wrtvec(promif_redir_arg_t arg, uchar_t *s, size_t n)
1492 {
1493 	vc_state_t *pvc;
1494 
1495 	pvc = vt_minor2vc(VT_ACTIVE);
1496 
1497 	if (pvc->vc_tem == NULL)
1498 		return (0);
1499 
1500 	ASSERT(consmode == CONS_KFB);
1501 
1502 	if (panicstr)
1503 		polled_io_cons_write(s, n);
1504 	else
1505 		(void) tem_write(pvc->vc_tem, s, n, kcred);
1506 
1507 	return (n);
1508 }
1509 #endif /* _HAVE_TEM_FIRMWARE */
1510 
1511 /*
1512  * These are for systems without OBP, and for devices that cannot be
1513  * shared between Solaris and the OBP.
1514  */
1515 static void
1516 wc_polled_putchar(cons_polledio_arg_t arg, unsigned char c)
1517 {
1518 	vc_state_t *pvc;
1519 
1520 	pvc = vt_minor2vc(VT_ACTIVE);
1521 
1522 	if (c == '\n')
1523 		wc_polled_putchar(arg, '\r');
1524 
1525 	if (pvc->vc_tem == NULL) {
1526 		/*
1527 		 * We have no terminal emulator configured.  We have no
1528 		 * recourse but to drop the output on the floor.
1529 		 */
1530 		return;
1531 	}
1532 
1533 	tem_safe_polled_write(pvc->vc_tem, &c, 1);
1534 }
1535 
1536 /*
1537  * These are for systems without OBP, and for devices that cannot be
1538  * shared between Solaris and the OBP.
1539  */
1540 static int
1541 wc_polled_getchar(cons_polledio_arg_t arg)
1542 {
1543 	struct wscons_state *wscons = (struct wscons_state *)arg;
1544 
1545 	if (wscons->wc_kb_polledio == NULL) {
1546 		prom_printf("wscons:  getchar with no keyboard support");
1547 		prom_printf("Halted...");
1548 		for (;;)
1549 			/* HANG FOREVER */;
1550 	}
1551 
1552 	return (wscons->wc_kb_polledio->cons_polledio_getchar(
1553 	    wscons->wc_kb_polledio->cons_polledio_argument));
1554 }
1555 
1556 static boolean_t
1557 wc_polled_ischar(cons_polledio_arg_t arg)
1558 {
1559 	struct wscons_state *wscons = (struct wscons_state *)arg;
1560 
1561 	if (wscons->wc_kb_polledio == NULL)
1562 		return (B_FALSE);
1563 
1564 	return (wscons->wc_kb_polledio->cons_polledio_ischar(
1565 	    wscons->wc_kb_polledio->cons_polledio_argument));
1566 }
1567 
1568 static void
1569 wc_polled_enter(cons_polledio_arg_t arg)
1570 {
1571 	struct wscons_state *wscons = (struct wscons_state *)arg;
1572 
1573 	if (wscons->wc_kb_polledio == NULL)
1574 		return;
1575 
1576 	if (wscons->wc_kb_polledio->cons_polledio_enter != NULL) {
1577 		wscons->wc_kb_polledio->cons_polledio_enter(
1578 		    wscons->wc_kb_polledio->cons_polledio_argument);
1579 	}
1580 }
1581 
1582 static void
1583 wc_polled_exit(cons_polledio_arg_t arg)
1584 {
1585 	struct wscons_state *wscons = (struct wscons_state *)arg;
1586 
1587 	if (wscons->wc_kb_polledio == NULL)
1588 		return;
1589 
1590 	if (wscons->wc_kb_polledio->cons_polledio_exit != NULL) {
1591 		wscons->wc_kb_polledio->cons_polledio_exit(
1592 		    wscons->wc_kb_polledio->cons_polledio_argument);
1593 	}
1594 }
1595 
1596 
1597 #ifdef DEBUG
1598 static void
1599 wc_dprintf(const char *fmt, ...)
1600 {
1601 	char buf[256];
1602 	va_list ap;
1603 
1604 	va_start(ap, fmt);
1605 	(void) vsprintf(buf, fmt, ap);
1606 	va_end(ap);
1607 
1608 	cmn_err(CE_WARN, "wc: %s", buf);
1609 }
1610 #endif
1611 
1612 /*ARGSUSED*/
1613 static void
1614 update_property(vc_state_t *pvc, char *name, ushort_t value)
1615 {
1616 	char data[8];
1617 
1618 	(void) snprintf(data, sizeof (data), "%u", value);
1619 
1620 	(void) ddi_prop_update_string(wscons.wc_dev, wc_dip, name, data);
1621 }
1622 
1623 /*
1624  * Gets the number of text rows and columns and the
1625  * width and height (in pixels) of the console.
1626  */
1627 void
1628 wc_get_size(vc_state_t *pvc)
1629 {
1630 	struct winsize *t = &pvc->vc_ttycommon.t_size;
1631 	ushort_t r = LOSCREENLINES, c = LOSCREENCOLS, x = 0, y = 0;
1632 
1633 	if (pvc->vc_tem != NULL)
1634 		tem_get_size(&r, &c, &x, &y);
1635 #ifdef _HAVE_TEM_FIRMWARE
1636 	else
1637 		console_get_size(&r, &c, &x, &y);
1638 #endif /* _HAVE_TEM_FIRMWARE */
1639 
1640 	mutex_enter(&pvc->vc_ttycommon.t_excl);
1641 	t->ws_col = c;
1642 	t->ws_row = r;
1643 	t->ws_xpixel = x;
1644 	t->ws_ypixel = y;
1645 	mutex_exit(&pvc->vc_ttycommon.t_excl);
1646 
1647 	if (pvc->vc_minor != 0)
1648 		return;
1649 
1650 	/* only for the wscons:0 */
1651 	update_property(pvc, "screen-#cols",  c);
1652 	update_property(pvc, "screen-#rows",  r);
1653 	update_property(pvc, "screen-width",  x);
1654 	update_property(pvc, "screen-height", y);
1655 }
1656 
1657 /*ARGSUSED*/
1658 static void
1659 wc_modechg_cb(tem_modechg_cb_arg_t arg)
1660 {
1661 	minor_t index;
1662 	vc_state_t *pvc;
1663 
1664 	mutex_enter(&vc_lock);
1665 	for (index = 0; index < VC_INSTANCES_COUNT; index++) {
1666 		pvc = vt_minor2vc(index);
1667 
1668 		mutex_enter(&pvc->vc_state_lock);
1669 
1670 		if ((pvc->vc_flags & WCS_ISOPEN) &&
1671 		    (pvc->vc_flags & WCS_INIT))
1672 			wc_get_size(pvc);
1673 
1674 		mutex_exit(&pvc->vc_state_lock);
1675 	}
1676 	mutex_exit(&vc_lock);
1677 }
1678