xref: /illumos-gate/usr/src/uts/common/io/wscons.c (revision 092a100d)
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  * Copyright 2019 Toomas Soome <tsoome@me.com>
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_console,
44  *     vc_cons_user, vc_avl_root) with virtual console devname part
45  *    (fs/dev/sdev_vtops.c) which get 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 four global variables,
55  * It is safe to do locking this way:
56  * 1) all read access to the four global variables in THIS WC MODULE do not
57  *    need locking;
58  * 2) all write access to the four global variables in THIS WC MODULE must
59  *    hold vc_lock;
60  * 3) any access to the four 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	wcuwsrv(queue_t *);
186 static int	wcuwput(queue_t *, mblk_t *);
187 static int	wclrput(queue_t *, mblk_t *);
188 
189 static struct module_info wcm_info = {
190 	0,
191 	"wc",
192 	0,
193 	INFPSZ,
194 	2048,
195 	128
196 };
197 
198 static struct qinit wcurinit = {
199 	putq,
200 	NULL,
201 	wcopen,
202 	wcclose,
203 	NULL,
204 	&wcm_info,
205 	NULL
206 };
207 
208 static struct qinit wcuwinit = {
209 	wcuwput,
210 	wcuwsrv,
211 	wcopen,
212 	wcclose,
213 	NULL,
214 	&wcm_info,
215 	NULL
216 };
217 
218 static struct qinit wclrinit = {
219 	wclrput,
220 	NULL,
221 	NULL,
222 	NULL,
223 	NULL,
224 	&wcm_info,
225 	NULL
226 };
227 
228 /*
229  * We always putnext directly to the underlying queue.
230  */
231 static struct qinit wclwinit = {
232 	NULL,
233 	NULL,
234 	NULL,
235 	NULL,
236 	NULL,
237 	&wcm_info,
238 	NULL
239 };
240 
241 static struct streamtab wcinfo = {
242 	&wcurinit,
243 	&wcuwinit,
244 	&wclrinit,
245 	&wclwinit,
246 };
247 
248 static int wc_info(dev_info_t *, ddi_info_cmd_t, void *, void **result);
249 static int wc_attach(dev_info_t *, ddi_attach_cmd_t);
250 
251 DDI_DEFINE_STREAM_OPS(wc_ops, nulldev, nulldev, wc_attach, nodev, nodev,
252     wc_info, D_MTPERMOD | D_MP, &wcinfo, ddi_quiesce_not_supported);
253 
254 static void	wcreioctl(void *);
255 static void	wcioctl(queue_t *, mblk_t *);
256 #ifdef _HAVE_TEM_FIRMWARE
257 static void	wcopoll(void *);
258 #endif /* _HAVE_TEM_FIRMWARE */
259 static void	wcrstrt(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 static tem_vt_state_t wc_get_screen_tem(vc_state_t *);
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 static void
393 wc_init_polledio(void)
394 {
395 	static boolean_t polledio_inited = B_FALSE;
396 	_NOTE(SCHEME_PROTECTS_DATA("D_MTPERMOD protected data",
397 	    polledio_inited))
398 
399 	if (polledio_inited)
400 		return;
401 
402 	polledio_inited = B_TRUE;
403 
404 	/*
405 	 * Initialize the parts of the polled I/O struct that
406 	 * are common to both input and output modes, but which
407 	 * don't flag to the upper layers, which if any of the
408 	 * two modes are available.  We don't know at this point
409 	 * if system is configured CONS_KFB, but we will when
410 	 * consconfig_dacf asks us with CONSOPENPOLLED I/O.
411 	 */
412 	bzero(&(wscons.wc_polledio), sizeof (wscons.wc_polledio));
413 	wscons.wc_polledio.cons_polledio_version =
414 	    CONSPOLLEDIO_V0;
415 	wscons.wc_polledio.cons_polledio_argument =
416 	    (cons_polledio_arg_t)&wscons;
417 	wscons.wc_polledio.cons_polledio_enter =
418 	    wc_polled_enter;
419 	wscons.wc_polledio.cons_polledio_exit =
420 	    wc_polled_exit;
421 
422 #ifdef _HAVE_TEM_FIRMWARE
423 	/*
424 	 * If we're talking directly to a framebuffer, we assume
425 	 * that it's a "slow" device, so that rendering should
426 	 * be deferred to a timeout or softcall so that we write
427 	 * a bunch of characters at once.
428 	 */
429 	wscons.wc_defer_output = prom_stdout_is_framebuffer();
430 #endif /* _HAVE_TEM_FIRMWARE */
431 }
432 
433 /*ARGSUSED*/
434 static int
435 wcopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
436 {
437 	int minor;
438 
439 	wc_init_polledio();
440 	minor = (int)getminor(*devp);
441 	return (vt_open(minor, q, crp));
442 }
443 
444 /*ARGSUSED*/
445 static int
446 wcclose(queue_t *q, int flag, cred_t *crp)
447 {
448 	vc_state_t *pvc = (vc_state_t *)q->q_ptr;
449 
450 	qprocsoff(q);
451 
452 	mutex_enter(&vc_lock);
453 
454 	/*
455 	 * If we are closing the VT node which
456 	 * /dev/vt/console_user points to, revert
457 	 * /dev/vt/console to /dev/console
458 	 */
459 	if (vc_cons_user == pvc->vc_minor)
460 		vc_cons_user = VT_MINOR_INVALID;
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  * Service procedure for upper write queue.
494  * We need to have service procedure to make sure the keyboard events
495  * are queued up for screen output and are not dependant on the screen
496  * updates.
497  */
498 static int
499 wcuwsrv(queue_t *q)
500 {
501 	vc_state_t *pvc = (vc_state_t *)q->q_ptr;
502 	tem_vt_state_t ptem = NULL;
503 	mblk_t *mp;
504 	ssize_t cc;
505 
506 	while ((mp = getq(q)) != NULL) {
507 		/*
508 		 * If we're waiting for something to happen (delay timeout to
509 		 * expire, current transmission to finish, output to be
510 		 * restarted, output to finish draining), don't grab anything
511 		 * new.
512 		 */
513 		if (pvc->vc_flags & (WCS_DELAY|WCS_BUSY|WCS_STOPPED)) {
514 			putbq(q, mp);
515 			return (0);
516 		}
517 
518 		switch (mp->b_datap->db_type) {
519 		default:	/* drop unknown type */
520 			freemsg(mp);
521 			continue;
522 
523 		case M_IOCTL:
524 			wcioctl(q, mp);
525 			continue;
526 
527 		case M_DELAY:
528 			/*
529 			 * Arrange for "wcrstrt" to be called when the
530 			 * delay expires; it will turn WCS_DELAY off.
531 			 */
532 			if (pvc->vc_timeoutid != 0)
533 				(void) quntimeout(q, pvc->vc_timeoutid);
534 			pvc->vc_timeoutid = qtimeout(q, wcrstrt, pvc,
535 			    (clock_t)(*(unsigned char *)mp->b_rptr + 6));
536 
537 			mutex_enter(&pvc->vc_state_lock);
538 			pvc->vc_flags |= WCS_DELAY;
539 			mutex_exit(&pvc->vc_state_lock);
540 
541 			freemsg(mp);
542 			continue;
543 
544 		case M_DATA:
545 			break;
546 		}
547 
548 		if ((cc = mp->b_wptr - mp->b_rptr) == 0) {
549 			freemsg(mp);
550 			continue;
551 		}
552 
553 #ifdef _HAVE_TEM_FIRMWARE
554 		if (consmode == CONS_KFB) {
555 #endif /* _HAVE_TEM_FIRMWARE */
556 			ptem = wc_get_screen_tem(pvc);
557 
558 			if (ptem == NULL) {
559 				freemsg(mp);
560 				continue;
561 			}
562 
563 			for (mblk_t *nbp = mp; nbp != NULL; nbp = nbp->b_cont) {
564 				cc = nbp->b_wptr - nbp->b_rptr;
565 
566 				if (cc <= 0)
567 					continue;
568 
569 				tem_write(ptem, nbp->b_rptr, cc, kcred);
570 			}
571 			freemsg(mp);
572 #ifdef _HAVE_TEM_FIRMWARE
573 			continue;
574 		}
575 
576 		/* consmode = CONS_FW */
577 		if (pvc->vc_minor != 0) {
578 			freemsg(mp);
579 			continue;
580 		}
581 
582 		/*
583 		 * Direct output to the frame buffer if this device
584 		 * is not the "hardware" console.
585 		 */
586 		if (wscons.wc_defer_output) {
587 			mutex_enter(&pvc->vc_state_lock);
588 			pvc->vc_flags |= WCS_BUSY;
589 			mutex_exit(&pvc->vc_state_lock);
590 
591 			pvc->vc_pendc = -1;
592 
593 			for (mblk_t *nbp = mp; nbp != NULL; nbp = nbp->b_cont) {
594 				cc = nbp->b_wptr - nbp->b_rptr;
595 
596 				if (cc <= 0)
597 					continue;
598 
599 				console_puts((const char *)nbp->b_rptr, cc);
600 			}
601 			freemsg(mp);
602 			mutex_enter(&pvc->vc_state_lock);
603 			pvc->vc_flags &= ~WCS_BUSY;
604 			mutex_exit(&pvc->vc_state_lock);
605 			continue;
606 		}
607 		for (boolean_t done = B_FALSE; done != B_TRUE; ) {
608 			int c;
609 
610 			c = *mp->b_rptr++;
611 			cc--;
612 			if (prom_mayput((char)c) != 0) {
613 
614 				mutex_enter(&pvc->vc_state_lock);
615 				pvc->vc_flags |= WCS_BUSY;
616 				mutex_exit(&pvc->vc_state_lock);
617 
618 				pvc->vc_pendc = c;
619 				if (pvc->vc_timeoutid != 0)
620 					(void) quntimeout(q,
621 					    pvc->vc_timeoutid);
622 				pvc->vc_timeoutid = qtimeout(q, wcopoll,
623 				    pvc, 1);
624 				if (mp != NULL) {
625 					/* not done with this message yet */
626 					(void) putbq(q, mp);
627 					return (0);
628 				}
629 				break;
630 			}
631 			while (cc <= 0) {
632 				mblk_t *nbp = mp;
633 				mp = mp->b_cont;
634 				freeb(nbp);
635 				if (mp == NULL) {
636 					done = B_TRUE;
637 					break;
638 				}
639 				/* LINTED E_PTRDIFF_OVERFLOW */
640 				cc = mp->b_wptr - mp->b_rptr;
641 			}
642 		}
643 #endif /* _HAVE_TEM_FIRMWARE */
644 	}
645 	return (0);
646 }
647 
648 /*
649  * Put procedure for upper write queue.
650  * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
651  * queue up M_BREAK, M_DELAY, and M_DATA messages for processing by
652  * the service routine. Discard everything else.
653  */
654 static int
655 wcuwput(queue_t *q, mblk_t *mp)
656 {
657 	vc_state_t *pvc = (vc_state_t *)q->q_ptr;
658 
659 	switch (mp->b_datap->db_type) {
660 
661 	case M_STOP:
662 		mutex_enter(&pvc->vc_state_lock);
663 		pvc->vc_flags |= WCS_STOPPED;
664 		mutex_exit(&pvc->vc_state_lock);
665 
666 		freemsg(mp);
667 		break;
668 
669 	case M_START:
670 		mutex_enter(&pvc->vc_state_lock);
671 		pvc->vc_flags &= ~WCS_STOPPED;
672 		mutex_exit(&pvc->vc_state_lock);
673 
674 		qenable(q);
675 		freemsg(mp);
676 		break;
677 
678 	case M_IOCTL: {
679 		struct iocblk *iocp;
680 		struct linkblk *linkp;
681 
682 		iocp = (struct iocblk *)(void *)mp->b_rptr;
683 		switch (iocp->ioc_cmd) {
684 
685 		case I_LINK:	/* stupid, but permitted */
686 		case I_PLINK:
687 			if (wscons.wc_kbdqueue != NULL) {
688 				/* somebody already linked */
689 				miocnak(q, mp, 0, EINVAL);
690 				return (0);
691 			}
692 			linkp = (struct linkblk *)(void *)mp->b_cont->b_rptr;
693 			wscons.wc_kbdqueue = WR(linkp->l_qbot);
694 			mp->b_datap->db_type = M_IOCACK;
695 			iocp->ioc_count = 0;
696 			wc_open_kb_polledio(&wscons, q, mp);
697 			break;
698 
699 		case I_UNLINK:	/* stupid, but permitted */
700 		case I_PUNLINK:
701 			linkp = (struct linkblk *)(void *)mp->b_cont->b_rptr;
702 			if (wscons.wc_kbdqueue != WR(linkp->l_qbot)) {
703 				/* not us */
704 				miocnak(q, mp, 0, EINVAL);
705 				return (0);
706 			}
707 
708 			mp->b_datap->db_type = M_IOCACK;
709 			iocp->ioc_count = 0;
710 			wc_close_kb_polledio(&wscons, q, mp);
711 			break;
712 
713 		case TCSETSW:
714 		case TCSETSF:
715 		case TCSETAW:
716 		case TCSETAF:
717 		case TCSBRK:
718 			/*
719 			 * The changes do not take effect until all
720 			 * output queued before them is drained.
721 			 * Put this message on the queue, so that
722 			 * "wcuwsrv" will see it when it's done
723 			 * with the output before it.
724 			 */
725 			if (putq(q, mp) == 0)
726 				freemsg(mp);
727 			break;
728 
729 		case CONSSETABORTENABLE:
730 		case CONSGETABORTENABLE:
731 		case KIOCSDIRECT:
732 			if (wscons.wc_kbdqueue != NULL) {
733 				wscons.wc_pending_wq = q;
734 				(void) putnext(wscons.wc_kbdqueue, mp);
735 				break;
736 			}
737 			/* fall through */
738 
739 		default:
740 			/*
741 			 * Do it now.
742 			 */
743 			wcioctl(q, mp);
744 			break;
745 		}
746 		break;
747 	}
748 
749 	case M_FLUSH:
750 		if (*mp->b_rptr & FLUSHW) {
751 			/*
752 			 * Flush our write queue.
753 			 */
754 			flushq(q, FLUSHDATA);	/* XXX doesn't flush M_DELAY */
755 			*mp->b_rptr &= ~FLUSHW;	/* it has been flushed */
756 		}
757 		if (*mp->b_rptr & FLUSHR) {
758 			flushq(RD(q), FLUSHDATA);
759 			qreply(q, mp);	/* give the read queues a crack at it */
760 		} else
761 			freemsg(mp);
762 		break;
763 
764 	case M_BREAK:
765 		/*
766 		 * Ignore these, as they make no sense.
767 		 */
768 		freemsg(mp);
769 		break;
770 
771 	case M_DELAY:
772 	case M_DATA:
773 		/*
774 		 * Queue the message up to be transmitted.
775 		 */
776 		if (putq(q, mp) == 0)
777 			freemsg(mp);
778 		break;
779 
780 	case M_IOCDATA:
781 		vt_miocdata(q, mp);
782 		break;
783 
784 	default:
785 		/*
786 		 * "No, I don't want a subscription to Chain Store Age,
787 		 * thank you anyway."
788 		 */
789 		freemsg(mp);
790 		break;
791 	}
792 
793 	return (0);
794 }
795 
796 /*
797  * Retry an "ioctl", now that "qbufcall" claims we may be able to allocate
798  * the buffer we need.
799  */
800 /*ARGSUSED*/
801 static void
802 wcreioctl(void *arg)
803 {
804 	vc_state_t *pvc = (vc_state_t *)arg;
805 	queue_t *q;
806 	mblk_t *mp;
807 
808 	pvc->vc_bufcallid = 0;
809 	q = pvc->vc_ttycommon.t_writeq;
810 	if ((mp = pvc->vc_ttycommon.t_iocpending) != NULL) {
811 		/* not pending any more */
812 		pvc->vc_ttycommon.t_iocpending = NULL;
813 		wcioctl(q, mp);
814 	}
815 }
816 
817 static int
818 wc_getterm(mblk_t *mp)
819 {
820 	char *term;
821 	intptr_t arg;
822 	int flag = ((struct iocblk *)(void *)mp->b_rptr)->ioc_flag;
823 
824 	STRUCT_DECL(cons_getterm, wcterm);
825 	STRUCT_INIT(wcterm, flag);
826 
827 	arg = *((intptr_t *)(void *)mp->b_cont->b_rptr);
828 
829 	if (ddi_copyin((void *)arg, STRUCT_BUF(wcterm),
830 	    STRUCT_SIZE(wcterm), flag) != 0) {
831 		return (EFAULT);
832 	}
833 
834 	if (consmode == CONS_FW) {
835 		/* PROM terminal emulator */
836 		term = "sun";
837 	} else {
838 		/* Kernel terminal emulator */
839 		ASSERT(consmode == CONS_KFB);
840 		term = "sun-color";
841 	}
842 
843 	if (STRUCT_FGET(wcterm, cn_term_len) <
844 	    strlen(term) + 1) {
845 		return (EOVERFLOW);
846 	}
847 
848 	if (ddi_copyout(term,
849 	    STRUCT_FGETP(wcterm, cn_term_type),
850 	    strlen(term) + 1, flag) != 0) {
851 		return (EFAULT);
852 	}
853 
854 	return (0);
855 }
856 
857 /*
858  * Process an "ioctl" message sent down to us.
859  */
860 static void
861 wcioctl(queue_t *q, mblk_t *mp)
862 {
863 	vc_state_t *pvc = (vc_state_t *)q->q_ptr;
864 	struct iocblk *iocp;
865 	size_t datasize;
866 	int error;
867 	long len;
868 
869 	iocp = (struct iocblk *)(void *)mp->b_rptr;
870 
871 	if ((iocp->ioc_cmd & VTIOC) == VTIOC ||
872 	    (iocp->ioc_cmd & KDIOC) == KDIOC) {
873 		vt_ioctl(q, mp);
874 		return;
875 	}
876 
877 	switch (iocp->ioc_cmd) {
878 	case TIOCSWINSZ:
879 		/*
880 		 * Ignore all attempts to set the screen size; the
881 		 * value in the EEPROM is guaranteed (modulo PROM bugs)
882 		 * to be the value used by the PROM monitor code, so it
883 		 * is by definition correct.  Many programs (e.g.,
884 		 * "login" and "tset") will attempt to reset the size
885 		 * to (0, 0) or (34, 80), neither of which is
886 		 * necessarily correct.
887 		 * We just ACK the message, so as not to disturb
888 		 * programs that set the sizes.
889 		 */
890 		iocp->ioc_count = 0;	/* no data returned */
891 		mp->b_datap->db_type = M_IOCACK;
892 		qreply(q, mp);
893 		return;
894 
895 	case CONSOPENPOLLEDIO:
896 		DPRINTF(PRINT_L1, PRINT_MASK_ALL,
897 		    ("wcioctl: CONSOPENPOLLEDIO\n"));
898 
899 		error = miocpullup(mp, sizeof (struct cons_polledio *));
900 		if (error != 0) {
901 			miocnak(q, mp, 0, error);
902 			return;
903 		}
904 
905 		/*
906 		 * We are given an appropriate-sized data block,
907 		 * and return a pointer to our structure in it.
908 		 */
909 		if (consmode == CONS_KFB)
910 			wscons.wc_polledio.cons_polledio_putchar =
911 			    wc_polled_putchar;
912 		*(struct cons_polledio **)(void *)mp->b_cont->b_rptr =
913 		    &wscons.wc_polledio;
914 
915 		mp->b_datap->db_type = M_IOCACK;
916 
917 		qreply(q, mp);
918 		break;
919 
920 	case CONS_GETTERM:
921 		if ((error = wc_getterm(mp)) != 0)
922 			miocnak(q, mp, 0, error);
923 		else
924 			miocack(q, mp, 0, 0);
925 		return;
926 
927 	case WC_OPEN_FB:
928 		/*
929 		 * Start out pessimistic, so that we can just jump to
930 		 * the reply to bail out.
931 		 */
932 		mp->b_datap->db_type = M_IOCNAK;
933 
934 		/*
935 		 * First test:  really, this should be done only from
936 		 * inside the kernel.  Unfortunately, that information
937 		 * doesn't seem to be available in a streams ioctl,
938 		 * so restrict it to root only.  (Perhaps we could check
939 		 * for ioc_cr == kcred.)
940 		 */
941 		if ((iocp->ioc_error = secpolicy_console(iocp->ioc_cr)) != 0)
942 			goto open_fail;
943 
944 		/*
945 		 * Some miscellaneous checks...
946 		 */
947 		iocp->ioc_error = EINVAL;
948 
949 		/*
950 		 * If we don't have exactly one continuation block, fail.
951 		 */
952 		if (mp->b_cont == NULL ||
953 		    mp->b_cont->b_cont != NULL)
954 			goto open_fail;
955 
956 		/*
957 		 * If there's no null terminator in the string, fail.
958 		 */
959 		/* LINTED E_PTRDIFF_OVERFLOW */
960 		len = mp->b_cont->b_wptr - mp->b_cont->b_rptr;
961 		if (memchr(mp->b_cont->b_rptr, 0, len) == NULL)
962 			goto open_fail;
963 
964 		/*
965 		 * NOTE:  should eventually get default
966 		 * dimensions from a property, e.g. screen-#rows.
967 		 */
968 		iocp->ioc_error = tem_info_init((char *)mp->b_cont->b_rptr,
969 		    iocp->ioc_cr);
970 		/*
971 		 * Of course, if the terminal emulator initialization
972 		 * failed, fail.
973 		 */
974 		if (iocp->ioc_error != 0)
975 			goto open_fail;
976 
977 #ifdef	_HAVE_TEM_FIRMWARE
978 		if (prom_stdout_is_framebuffer()) {
979 			/*
980 			 * Drivers in the console stream may emit additional
981 			 * messages before we are ready. This causes text
982 			 * overwrite on the screen. So we set the redirection
983 			 * here. It is safe because the ioctl in consconfig_dacf
984 			 * will succeed and consmode will be set to CONS_KFB.
985 			 */
986 			prom_set_stdout_redirect(wc_cons_wrtvec,
987 			    (promif_redir_arg_t)NULL);
988 
989 		}
990 #endif	/* _HAVE_TEM_FIRMWARE */
991 
992 		tem_register_modechg_cb(wc_modechg_cb,
993 		    (tem_modechg_cb_arg_t)&wscons);
994 
995 		/*
996 		 * ... and succeed.
997 		 */
998 		mp->b_datap->db_type = M_IOCACK;
999 
1000 open_fail:
1001 		qreply(q, mp);
1002 		break;
1003 
1004 	case WC_CLOSE_FB:
1005 		/*
1006 		 * There's nothing that can call this, so it's not
1007 		 * really implemented.
1008 		 */
1009 		mp->b_datap->db_type = M_IOCNAK;
1010 		/*
1011 		 * However, if it were implemented, it would clearly
1012 		 * be root-only.
1013 		 */
1014 		if ((iocp->ioc_error = secpolicy_console(iocp->ioc_cr)) != 0)
1015 			goto close_fail;
1016 
1017 		iocp->ioc_error = EINVAL;
1018 
1019 close_fail:
1020 		qreply(q, mp);
1021 		break;
1022 
1023 	default:
1024 
1025 		/*
1026 		 * The only way in which "ttycommon_ioctl" can fail is
1027 		 * if the "ioctl" requires a response containing data
1028 		 * to be returned to the user, and no mblk could be
1029 		 * allocated for the data.  No such "ioctl" alters our
1030 		 * state.  Thus, we always go ahead and do any
1031 		 * state-changes the "ioctl" calls for.  If we couldn't
1032 		 * allocate the data, "ttycommon_ioctl" has stashed the
1033 		 * "ioctl" away safely, so we just call "qbufcall" to
1034 		 * request that we be called back when we stand a
1035 		 * better chance of allocating the data.
1036 		 */
1037 		datasize = ttycommon_ioctl(&pvc->vc_ttycommon, q, mp, &error);
1038 		if (datasize != 0) {
1039 			if (pvc->vc_bufcallid != 0)
1040 				qunbufcall(q, pvc->vc_bufcallid);
1041 			pvc->vc_bufcallid = qbufcall(q, datasize, BPRI_HI,
1042 			    wcreioctl, pvc);
1043 			return;
1044 		}
1045 
1046 		if (error < 0) {
1047 			if (iocp->ioc_cmd == TCSBRK)
1048 				error = 0;
1049 			else
1050 				error = EINVAL;
1051 		}
1052 		if (error != 0) {
1053 			iocp->ioc_error = error;
1054 			mp->b_datap->db_type = M_IOCNAK;
1055 		}
1056 		qreply(q, mp);
1057 		break;
1058 	}
1059 }
1060 
1061 /*
1062  * This function gets the polled I/O structures from the lower
1063  * keyboard driver.  If any initialization or resource allocation
1064  * needs to be done by the lower driver, it will be done when
1065  * the lower driver services this message.
1066  */
1067 static void
1068 wc_open_kb_polledio(struct wscons_state *wscons, queue_t *q, mblk_t *mp)
1069 {
1070 	mblk_t *mp2;
1071 	struct iocblk *iocp;
1072 
1073 	DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1074 	    ("wc_open_kb_polledio: sending CONSOPENPOLLEDIO\n"));
1075 
1076 	mp2 = mkiocb(CONSOPENPOLLEDIO);
1077 
1078 	if (mp2 == NULL) {
1079 		/*
1080 		 * If we can't get an mblk, then wait for it.
1081 		 */
1082 		goto nomem;
1083 	}
1084 
1085 	mp2->b_cont = allocb(sizeof (struct cons_polledio *), BPRI_HI);
1086 
1087 	if (mp2->b_cont == NULL) {
1088 		/*
1089 		 * If we can't get an mblk, then wait for it, and release
1090 		 * the mblk that we have already allocated.
1091 		 */
1092 		freemsg(mp2);
1093 		goto nomem;
1094 	}
1095 
1096 	iocp = (struct iocblk *)(void *)mp2->b_rptr;
1097 
1098 	iocp->ioc_count = sizeof (struct cons_polledio *);
1099 	mp2->b_cont->b_wptr = mp2->b_cont->b_rptr +
1100 	    sizeof (struct cons_polledio *);
1101 
1102 	wscons->wc_pending_wq = q;
1103 	wscons->wc_pending_link = mp;
1104 	wscons->wc_kb_getpolledio_id = iocp->ioc_id;
1105 
1106 	putnext(wscons->wc_kbdqueue, mp2);
1107 
1108 	return;
1109 
1110 nomem:
1111 	iocp = (struct iocblk *)(void *)mp->b_rptr;
1112 	iocp->ioc_error = ENOMEM;
1113 	mp->b_datap->db_type = M_IOCNAK;
1114 	qreply(q, mp);
1115 }
1116 
1117 /*
1118  * This function releases the polled I/O structures from the lower
1119  * keyboard driver.  If any de-initialization needs to be done, or
1120  * any resources need to be released, it will be done when the lower
1121  * driver services this message.
1122  */
1123 static void
1124 wc_close_kb_polledio(struct wscons_state *wscons, queue_t *q, mblk_t *mp)
1125 {
1126 	mblk_t *mp2;
1127 	struct iocblk *iocp;
1128 
1129 	DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1130 	    ("wc_close_kb_polledio: sending CONSCLOSEPOLLEDIO\n"));
1131 
1132 	mp2 = mkiocb(CONSCLOSEPOLLEDIO);
1133 
1134 	if (mp2 == NULL) {
1135 		/*
1136 		 * If we can't get an mblk, then wait for it.
1137 		 */
1138 		goto nomem;
1139 	}
1140 
1141 	mp2->b_cont = allocb(sizeof (struct cons_polledio *), BPRI_HI);
1142 
1143 	if (mp2->b_cont == NULL) {
1144 		/*
1145 		 * If we can't get an mblk, then wait for it, and release
1146 		 * the mblk that we have already allocated.
1147 		 */
1148 		freemsg(mp2);
1149 
1150 		goto nomem;
1151 	}
1152 
1153 	iocp = (struct iocblk *)(void *)mp2->b_rptr;
1154 
1155 	iocp->ioc_count = 0;
1156 
1157 	wscons->wc_pending_wq = q;
1158 	wscons->wc_pending_link = mp;
1159 	wscons->wc_kb_getpolledio_id = iocp->ioc_id;
1160 
1161 	putnext(wscons->wc_kbdqueue, mp2);
1162 
1163 	return;
1164 
1165 nomem:
1166 	iocp = (struct iocblk *)(void *)mp->b_rptr;
1167 	iocp->ioc_error = ENOMEM;
1168 	mp->b_datap->db_type = M_IOCNAK;
1169 	qreply(q, mp);
1170 }
1171 
1172 #ifdef _HAVE_TEM_FIRMWARE
1173 /* ARGSUSED */
1174 static void
1175 wcopoll(void *arg)
1176 {
1177 	vc_state_t *pvc = (vc_state_t *)arg;
1178 	queue_t *q;
1179 
1180 	q = pvc->vc_ttycommon.t_writeq;
1181 	pvc->vc_timeoutid = 0;
1182 
1183 	mutex_enter(&pvc->vc_state_lock);
1184 
1185 	/* See if we can continue output */
1186 	if ((pvc->vc_flags & WCS_BUSY) && pvc->vc_pendc != -1) {
1187 		if (prom_mayput((char)pvc->vc_pendc) == 0) {
1188 			pvc->vc_pendc = -1;
1189 			pvc->vc_flags &= ~WCS_BUSY;
1190 			if (!(pvc->vc_flags&(WCS_DELAY|WCS_STOPPED)))
1191 				qenable(q);
1192 		} else
1193 			pvc->vc_timeoutid = qtimeout(q, wcopoll, pvc, 1);
1194 	}
1195 
1196 	mutex_exit(&pvc->vc_state_lock);
1197 }
1198 #endif	/* _HAVE_TEM_FIRMWARE */
1199 
1200 /*
1201  * Restart output on the console after a timeout.
1202  */
1203 /* ARGSUSED */
1204 static void
1205 wcrstrt(void *arg)
1206 {
1207 	vc_state_t *pvc = (vc_state_t *)arg;
1208 
1209 	ASSERT(pvc->vc_ttycommon.t_writeq != NULL);
1210 
1211 	mutex_enter(&pvc->vc_state_lock);
1212 	pvc->vc_flags &= ~WCS_DELAY;
1213 	mutex_exit(&pvc->vc_state_lock);
1214 
1215 	qenable(pvc->vc_ttycommon.t_writeq);
1216 }
1217 
1218 /*
1219  * get screen terminal for current output
1220  */
1221 static tem_vt_state_t
1222 wc_get_screen_tem(vc_state_t *pvc)
1223 {
1224 	if (!tem_initialized(pvc->vc_tem) ||
1225 	    tem_get_fbmode(pvc->vc_tem) != KD_TEXT)
1226 		return (NULL);
1227 
1228 	return (pvc->vc_tem);
1229 }
1230 
1231 /*
1232  * Put procedure for lower read queue.
1233  * Pass everything up to queue above "upper half".
1234  */
1235 static int
1236 wclrput(queue_t *q, mblk_t *mp)
1237 {
1238 	vc_state_t *pvc;
1239 	queue_t *upq;
1240 	struct iocblk *iocp;
1241 
1242 	pvc = vt_minor2vc(VT_ACTIVE);
1243 
1244 	DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1245 	    ("wclrput: wclrput type = 0x%x\n", mp->b_datap->db_type));
1246 
1247 	switch (mp->b_datap->db_type) {
1248 
1249 	case M_FLUSH:
1250 		if (*mp->b_rptr == FLUSHW || *mp->b_rptr == FLUSHRW) {
1251 			/*
1252 			 * Flush our write queue.
1253 			 */
1254 			/* XXX doesn't flush M_DELAY */
1255 			flushq(WR(q), FLUSHDATA);
1256 			*mp->b_rptr = FLUSHR;	/* it has been flushed */
1257 		}
1258 		if (*mp->b_rptr == FLUSHR || *mp->b_rptr == FLUSHRW) {
1259 			flushq(q, FLUSHDATA);
1260 			*mp->b_rptr = FLUSHW;	/* it has been flushed */
1261 			qreply(q, mp);	/* give the read queues a crack at it */
1262 		} else
1263 			freemsg(mp);
1264 		break;
1265 
1266 	case M_DATA:
1267 		if (consmode == CONS_KFB && vt_check_hotkeys(mp)) {
1268 			freemsg(mp);
1269 			break;
1270 		}
1271 
1272 		if ((upq = pvc->vc_ttycommon.t_readq) != NULL) {
1273 			if (!canput(upq->q_next)) {
1274 				ttycommon_qfull(&pvc->vc_ttycommon, upq);
1275 				qenable(WR(upq));
1276 				freemsg(mp);
1277 			} else {
1278 				putnext(upq, mp);
1279 			}
1280 		} else
1281 			freemsg(mp);
1282 		break;
1283 
1284 	case M_IOCACK:
1285 	case M_IOCNAK:
1286 		iocp = (struct iocblk *)(void *)mp->b_rptr;
1287 		if (wscons.wc_pending_link != NULL &&
1288 		    iocp->ioc_id == wscons.wc_kb_getpolledio_id) {
1289 			switch (mp->b_datap->db_type) {
1290 
1291 			case M_IOCACK:
1292 				switch (iocp->ioc_cmd) {
1293 
1294 				case CONSOPENPOLLEDIO:
1295 					DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1296 					    ("wclrput: "
1297 					    "ACK CONSOPENPOLLEDIO\n"));
1298 					wscons.wc_kb_polledio =
1299 					    *(struct cons_polledio **)
1300 					    (void *)mp->b_cont->b_rptr;
1301 					wscons.wc_polledio.
1302 					    cons_polledio_getchar =
1303 					    wc_polled_getchar;
1304 					wscons.wc_polledio.
1305 					    cons_polledio_ischar =
1306 					    wc_polled_ischar;
1307 					break;
1308 
1309 				case CONSCLOSEPOLLEDIO:
1310 					DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1311 					    ("wclrput: "
1312 					    "ACK CONSCLOSEPOLLEDIO\n"));
1313 					wscons.wc_kb_polledio = NULL;
1314 					wscons.wc_kbdqueue = NULL;
1315 					wscons.wc_polledio.
1316 					    cons_polledio_getchar = NULL;
1317 					wscons.wc_polledio.
1318 					    cons_polledio_ischar = NULL;
1319 					break;
1320 				default:
1321 					DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1322 					    ("wclrput: "
1323 					    "ACK UNKNOWN\n"));
1324 				}
1325 
1326 				break;
1327 			case M_IOCNAK:
1328 				/*
1329 				 * Keyboard may or may not support polled I/O.
1330 				 * This ioctl may have been rejected because
1331 				 * we only have the wc->conskbd chain built,
1332 				 * and the keyboard driver has not been linked
1333 				 * underneath conskbd yet.
1334 				 */
1335 				DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1336 				    ("wclrput: NAK\n"));
1337 
1338 				switch (iocp->ioc_cmd) {
1339 
1340 				case CONSCLOSEPOLLEDIO:
1341 					wscons.wc_kb_polledio = NULL;
1342 					wscons.wc_kbdqueue = NULL;
1343 					wscons.wc_polledio.
1344 					    cons_polledio_getchar = NULL;
1345 					wscons.wc_polledio.
1346 					    cons_polledio_ischar = NULL;
1347 					break;
1348 				}
1349 				break;
1350 			}
1351 
1352 			/*
1353 			 * Discard the response, replace it with the
1354 			 * pending response to the I_PLINK, then let it
1355 			 * flow upward.
1356 			 */
1357 			freemsg(mp);
1358 			mp = wscons.wc_pending_link;
1359 			wscons.wc_pending_link = NULL;
1360 			wscons.wc_kb_getpolledio_id = 0;
1361 		}
1362 		/* FALLTHROUGH */
1363 
1364 	default:	/* inc M_ERROR, M_HANGUP, M_IOCACK, M_IOCNAK, ... */
1365 		if (wscons.wc_pending_wq != NULL) {
1366 			qreply(wscons.wc_pending_wq, mp);
1367 			wscons.wc_pending_wq = NULL;
1368 			break;
1369 		}
1370 
1371 		if ((upq = pvc->vc_ttycommon.t_readq) != NULL) {
1372 			putnext(upq, mp);
1373 		} else {
1374 			DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1375 			    ("wclrput: Message DISCARDED\n"));
1376 			freemsg(mp);
1377 		}
1378 		break;
1379 	}
1380 
1381 	return (0);
1382 }
1383 
1384 #ifdef _HAVE_TEM_FIRMWARE
1385 /*
1386  *  This routine exists so that prom_write() can redirect writes
1387  *  to the framebuffer through the kernel terminal emulator, if
1388  *  that configuration is selected during consconfig.
1389  *  When the kernel terminal emulator is enabled, consconfig_dacf
1390  *  sets up the PROM output redirect vector to enter this function.
1391  *  During panic the console will already be powered up as part of
1392  *  calling into the prom_*() layer.
1393  */
1394 /* ARGSUSED */
1395 ssize_t
1396 wc_cons_wrtvec(promif_redir_arg_t arg, uchar_t *s, size_t n)
1397 {
1398 	vc_state_t *pvc;
1399 
1400 	pvc = vt_minor2vc(VT_ACTIVE);
1401 
1402 	if (pvc->vc_tem == NULL)
1403 		return (0);
1404 
1405 	ASSERT(consmode == CONS_KFB);
1406 
1407 	if (panicstr)
1408 		polled_io_cons_write(s, n);
1409 	else
1410 		(void) tem_write(pvc->vc_tem, s, n, kcred);
1411 
1412 	return (n);
1413 }
1414 #endif /* _HAVE_TEM_FIRMWARE */
1415 
1416 /*
1417  * These are for systems without OBP, and for devices that cannot be
1418  * shared between Solaris and the OBP.
1419  */
1420 static void
1421 wc_polled_putchar(cons_polledio_arg_t arg, unsigned char c)
1422 {
1423 	vc_state_t *pvc;
1424 
1425 	pvc = vt_minor2vc(VT_ACTIVE);
1426 
1427 	if (c == '\n')
1428 		wc_polled_putchar(arg, '\r');
1429 
1430 	if (pvc->vc_tem == NULL) {
1431 		/*
1432 		 * We have no terminal emulator configured.  We have no
1433 		 * recourse but to drop the output on the floor.
1434 		 */
1435 		return;
1436 	}
1437 
1438 	tem_safe_polled_write(pvc->vc_tem, &c, 1);
1439 }
1440 
1441 /*
1442  * These are for systems without OBP, and for devices that cannot be
1443  * shared between Solaris and the OBP.
1444  */
1445 static int
1446 wc_polled_getchar(cons_polledio_arg_t arg)
1447 {
1448 	struct wscons_state *wscons = (struct wscons_state *)arg;
1449 
1450 	if (wscons->wc_kb_polledio == NULL) {
1451 		prom_printf("wscons:  getchar with no keyboard support");
1452 		prom_printf("Halted...");
1453 		for (;;)
1454 			/* HANG FOREVER */;
1455 	}
1456 
1457 	return (wscons->wc_kb_polledio->cons_polledio_getchar(
1458 	    wscons->wc_kb_polledio->cons_polledio_argument));
1459 }
1460 
1461 static boolean_t
1462 wc_polled_ischar(cons_polledio_arg_t arg)
1463 {
1464 	struct wscons_state *wscons = (struct wscons_state *)arg;
1465 
1466 	if (wscons->wc_kb_polledio == NULL)
1467 		return (B_FALSE);
1468 
1469 	return (wscons->wc_kb_polledio->cons_polledio_ischar(
1470 	    wscons->wc_kb_polledio->cons_polledio_argument));
1471 }
1472 
1473 static void
1474 wc_polled_enter(cons_polledio_arg_t arg)
1475 {
1476 	struct wscons_state *wscons = (struct wscons_state *)arg;
1477 
1478 	if (wscons->wc_kb_polledio == NULL)
1479 		return;
1480 
1481 	if (wscons->wc_kb_polledio->cons_polledio_enter != NULL) {
1482 		wscons->wc_kb_polledio->cons_polledio_enter(
1483 		    wscons->wc_kb_polledio->cons_polledio_argument);
1484 	}
1485 }
1486 
1487 static void
1488 wc_polled_exit(cons_polledio_arg_t arg)
1489 {
1490 	struct wscons_state *wscons = (struct wscons_state *)arg;
1491 
1492 	if (wscons->wc_kb_polledio == NULL)
1493 		return;
1494 
1495 	if (wscons->wc_kb_polledio->cons_polledio_exit != NULL) {
1496 		wscons->wc_kb_polledio->cons_polledio_exit(
1497 		    wscons->wc_kb_polledio->cons_polledio_argument);
1498 	}
1499 }
1500 
1501 
1502 #ifdef DEBUG
1503 static void
1504 wc_dprintf(const char *fmt, ...)
1505 {
1506 	char buf[256];
1507 	va_list ap;
1508 
1509 	va_start(ap, fmt);
1510 	(void) vsprintf(buf, fmt, ap);
1511 	va_end(ap);
1512 
1513 	cmn_err(CE_WARN, "wc: %s", buf);
1514 }
1515 #endif
1516 
1517 /*ARGSUSED*/
1518 static void
1519 update_property(vc_state_t *pvc, char *name, ushort_t value)
1520 {
1521 	char data[8];
1522 
1523 	(void) snprintf(data, sizeof (data), "%u", value);
1524 
1525 	(void) ddi_prop_update_string(wscons.wc_dev, wc_dip, name, data);
1526 }
1527 
1528 /*
1529  * Gets the number of text rows and columns and the
1530  * width and height (in pixels) of the console.
1531  */
1532 void
1533 wc_get_size(vc_state_t *pvc)
1534 {
1535 	struct winsize *t = &pvc->vc_ttycommon.t_size;
1536 	ushort_t r = LOSCREENLINES, c = LOSCREENCOLS, x = 0, y = 0;
1537 
1538 	if (pvc->vc_tem != NULL)
1539 		tem_get_size(&r, &c, &x, &y);
1540 #ifdef _HAVE_TEM_FIRMWARE
1541 	else
1542 		console_get_size(&r, &c, &x, &y);
1543 #endif /* _HAVE_TEM_FIRMWARE */
1544 
1545 	mutex_enter(&pvc->vc_ttycommon.t_excl);
1546 	t->ws_col = c;
1547 	t->ws_row = r;
1548 	t->ws_xpixel = x;
1549 	t->ws_ypixel = y;
1550 	mutex_exit(&pvc->vc_ttycommon.t_excl);
1551 
1552 	if (pvc->vc_minor != 0)
1553 		return;
1554 
1555 	/* only for the wscons:0 */
1556 	update_property(pvc, "screen-#cols",  c);
1557 	update_property(pvc, "screen-#rows",  r);
1558 	update_property(pvc, "screen-width",  x);
1559 	update_property(pvc, "screen-height", y);
1560 }
1561 
1562 /*ARGSUSED*/
1563 static void
1564 wc_modechg_cb(tem_modechg_cb_arg_t arg)
1565 {
1566 	minor_t index;
1567 	vc_state_t *pvc;
1568 
1569 	mutex_enter(&vc_lock);
1570 	for (index = 0; index < VC_INSTANCES_COUNT; index++) {
1571 		pvc = vt_minor2vc(index);
1572 
1573 		mutex_enter(&pvc->vc_state_lock);
1574 
1575 		if ((pvc->vc_flags & WCS_ISOPEN) &&
1576 		    (pvc->vc_flags & WCS_INIT))
1577 			wc_get_size(pvc);
1578 
1579 		mutex_exit(&pvc->vc_state_lock);
1580 	}
1581 	mutex_exit(&vc_lock);
1582 }
1583