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