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