xref: /illumos-gate/usr/src/uts/common/io/wscons.c (revision 7c478bd9)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * "Workstation console" multiplexor driver for Sun.
31  *
32  * Sends output to the primary frame buffer using the PROM monitor;
33  * gets input from a stream linked below us that is the "keyboard
34  * driver", below which is linked the primary keyboard.
35  */
36 
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/signal.h>
40 #include <sys/cred.h>
41 #include <sys/vnode.h>
42 #include <sys/termios.h>
43 #include <sys/termio.h>
44 #include <sys/ttold.h>
45 #include <sys/stropts.h>
46 #include <sys/stream.h>
47 #include <sys/strsun.h>
48 #include <sys/tty.h>
49 #include <sys/buf.h>
50 #include <sys/uio.h>
51 #include <sys/stat.h>
52 #include <sys/kmem.h>
53 #include <sys/cpuvar.h>
54 #include <sys/kbio.h>
55 #include <sys/strredir.h>
56 #include <sys/fs/snode.h>
57 #include <sys/consdev.h>
58 #include <sys/conf.h>
59 #include <sys/ddi.h>
60 #include <sys/sunddi.h>
61 #include <sys/debug.h>
62 #include <sys/console.h>
63 #include <sys/ddi_impldefs.h>
64 #include <sys/promif.h>
65 #include <sys/policy.h>
66 #if	defined(_CONSOLE_OUTPUT_VIA_SOFTWARE)
67 #include <sys/terminal-emulator.h>
68 #endif
69 
70 #define	MINLINES	10
71 #define	MAXLINES	48
72 #define	LOSCREENLINES	34
73 #define	HISCREENLINES	48
74 
75 #define	MINCOLS		10
76 #define	MAXCOLS		120
77 #define	LOSCREENCOLS	80
78 #define	HISCREENCOLS	120
79 
80 static struct wscons {
81 	int	wc_flags;		/* random flags (protected by */
82 					/* write-side exclusion lock  */
83 	dev_t	wc_dev;			/* major/minor for this device */
84 	tty_common_t wc_ttycommon;	/* data common to all tty drivers */
85 #if	defined(_CONSOLE_OUTPUT_VIA_FIRMWARE)
86 	int	wc_pendc;		/* pending output character */
87 	int	wc_defer_output;	/* set if output device is "slow" */
88 #endif
89 #if	defined(_CONSOLE_OUTPUT_VIA_SOFTWARE)
90 	struct terminal_emulator *wc_tem;	/* Terminal emulator state */
91 #endif
92 	queue_t	*wc_kbdqueue;		/* "console keyboard" device queue */
93 					/* below us */
94 	bufcall_id_t wc_bufcallid;	/* id returned by qbufcall */
95 	timeout_id_t wc_timeoutid;	/* id returned by qtimeout */
96 	cons_polledio_t		wc_polledio; /* polled I/O function pointers */
97 	cons_polledio_t		*wc_kb_polledio; /* keyboard's polledio */
98 	unsigned int	wc_kb_getpolledio_id; /* id for kb CONSOPENPOLLEDIO */
99 	mblk_t	*wc_pending_link;	/* I_PLINK pending for kb polledio */
100 } wscons;
101 
102 #define	WCS_ISOPEN	0x00000001	/* open is complete */
103 #define	WCS_STOPPED	0x00000002	/* output is stopped */
104 #define	WCS_DELAY	0x00000004	/* waiting for delay to finish */
105 #define	WCS_BUSY	0x00000008	/* waiting for transmission to finish */
106 
107 static int	wcopen(queue_t *, dev_t *, int, int, cred_t *);
108 static int	wcclose(queue_t *, int, cred_t *);
109 static int	wcuwput(queue_t *, mblk_t *);
110 static int	wclrput(queue_t *, mblk_t *);
111 
112 static struct module_info wcm_info = {
113 	0,
114 	"wc",
115 	0,
116 	INFPSZ,
117 	2048,
118 	128
119 };
120 
121 static struct qinit wcurinit = {
122 	putq,
123 	NULL,
124 	wcopen,
125 	wcclose,
126 	NULL,
127 	&wcm_info,
128 	NULL
129 };
130 
131 static struct qinit wcuwinit = {
132 	wcuwput,
133 	NULL,
134 	wcopen,
135 	wcclose,
136 	NULL,
137 	&wcm_info,
138 	NULL
139 };
140 
141 static struct qinit wclrinit = {
142 	wclrput,
143 	NULL,
144 	NULL,
145 	NULL,
146 	NULL,
147 	&wcm_info,
148 	NULL
149 };
150 
151 /*
152  * We always putnext directly to the underlying queue.
153  */
154 static struct qinit wclwinit = {
155 	NULL,
156 	NULL,
157 	NULL,
158 	NULL,
159 	NULL,
160 	&wcm_info,
161 	NULL
162 };
163 
164 static struct streamtab wcinfo = {
165 	&wcurinit,
166 	&wcuwinit,
167 	&wclrinit,
168 	&wclwinit,
169 };
170 
171 static int wc_info(dev_info_t *, ddi_info_cmd_t, void *, void **result);
172 static int wc_attach(dev_info_t *, ddi_attach_cmd_t);
173 static dev_info_t *wc_dip;
174 
175 DDI_DEFINE_STREAM_OPS(wc_ops, nulldev, nulldev, wc_attach, nodev, nodev,
176     wc_info, D_MTPERMOD | D_MP, &wcinfo);
177 
178 static void	wcreioctl(void *);
179 static void 	wcioctl(queue_t *, mblk_t *);
180 #if	defined(_CONSOLE_OUTPUT_VIA_FIRMWARE)
181 static void	wcopoll(void *);
182 static void	wconsout(void *);
183 #endif
184 static void	wcrstrt(void *);
185 static void	wcstart(void);
186 static void	wc_open_kb_polledio(struct wscons *wc, queue_t *q, mblk_t *mp);
187 static void	wc_close_kb_polledio(struct wscons *wc, queue_t *q, mblk_t *mp);
188 #if	defined(_CONSOLE_OUTPUT_VIA_SOFTWARE)
189 static void	wc_putchar(struct cons_polledio_arg *arg, unsigned char c);
190 #endif
191 static boolean_t wc_ischar(struct cons_polledio_arg *arg);
192 static int	wc_getchar(struct cons_polledio_arg *arg);
193 static void	wc_polled_enter(struct cons_polledio_arg *arg);
194 static void	wc_polled_exit(struct cons_polledio_arg *arg);
195 static void	wc_get_size(struct wscons *wscons);
196 
197 #include <sys/types.h>
198 #include <sys/conf.h>
199 #include <sys/param.h>
200 #include <sys/systm.h>
201 #include <sys/errno.h>
202 #include <sys/modctl.h>
203 
204 static struct dev_ops wc_ops;
205 
206 /*
207  * Debug printing
208  */
209 #ifndef DPRINTF
210 #ifdef DEBUG
211 /*PRINTFLIKE1*/
212 static void	wc_dprintf(const char *fmt, ...) __KPRINTFLIKE(1);
213 #define	DPRINTF(l, m, args) \
214 	(((l) >= wc_errlevel) && ((m) & wc_errmask) ?	\
215 		wc_dprintf args :				\
216 		(void) 0)
217 
218 /*
219  * Severity levels for printing
220  */
221 #define	PRINT_L0	0	/* print every message */
222 #define	PRINT_L1	1	/* debug */
223 #define	PRINT_L2	2	/* quiet */
224 
225 /*
226  * Masks
227  */
228 #define	PRINT_MASK_ALL		0xFFFFFFFFU
229 uint_t	wc_errmask = PRINT_MASK_ALL;
230 uint_t	wc_errlevel = PRINT_L2;
231 
232 #else
233 #define	DPRINTF(l, m, args)	/* NOTHING */
234 #endif
235 #endif
236 
237 /*
238  * Module linkage information for the kernel.
239  */
240 
241 static struct modldrv modldrv = {
242 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
243 	"Workstation multiplexer Driver 'wc' %I%",
244 	&wc_ops,	/* driver ops */
245 };
246 
247 static struct modlinkage modlinkage = {
248 	MODREV_1,
249 	&modldrv,
250 	NULL
251 };
252 
253 int
254 _init(void)
255 {
256 	return (mod_install(&modlinkage));
257 }
258 
259 int
260 _fini(void)
261 {
262 	return (mod_remove(&modlinkage));
263 }
264 
265 int
266 _info(struct modinfo *modinfop)
267 {
268 	return (mod_info(&modlinkage, modinfop));
269 }
270 
271 /*ARGSUSED*/
272 static int
273 wc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
274 {
275 	if (ddi_create_minor_node(devi, "wscons", S_IFCHR,
276 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
277 		ddi_remove_minor_node(devi, NULL);
278 		return (-1);
279 	}
280 	wc_dip = devi;
281 	return (DDI_SUCCESS);
282 }
283 
284 /* ARGSUSED */
285 static int
286 wc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
287 	void **result)
288 {
289 	int error;
290 
291 	switch (infocmd) {
292 	case DDI_INFO_DEVT2DEVINFO:
293 		if (wc_dip == NULL) {
294 			error = DDI_FAILURE;
295 		} else {
296 			*result = (void *) wc_dip;
297 			error = DDI_SUCCESS;
298 		}
299 		break;
300 	case DDI_INFO_DEVT2INSTANCE:
301 		*result = (void *)0;
302 		error = DDI_SUCCESS;
303 		break;
304 	default:
305 		error = DDI_FAILURE;
306 	}
307 	return (error);
308 }
309 
310 #if	defined(_CONSOLE_OUTPUT_VIA_FIRMWARE)
311 /*
312  * Output buffer. Protected by the per-module inner perimeter.
313  */
314 #define	MAXHIWAT	2000
315 static char obuf[MAXHIWAT];
316 #endif
317 
318 /*ARGSUSED*/
319 static int
320 wcopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
321 {
322 	struct termios *termiosp;
323 	int len;
324 
325 	if (getminor(*devp) != 0)
326 		return (ENXIO);		/* sorry, only one per customer */
327 
328 	if (!(wscons.wc_flags & WCS_ISOPEN)) {
329 		mutex_init(&wscons.wc_ttycommon.t_excl, NULL, MUTEX_DEFAULT,
330 		    NULL);
331 		wscons.wc_ttycommon.t_iflag = 0;
332 		/*
333 		 * Get the default termios settings (cflag).
334 		 * These are stored as a property in the
335 		 * "options" node.
336 		 */
337 		if (ddi_getlongprop(DDI_DEV_T_ANY,
338 		    ddi_root_node(), 0, "ttymodes",
339 		    (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS &&
340 		    len == sizeof (struct termios)) {
341 
342 			wscons.wc_ttycommon.t_cflag = termiosp->c_cflag;
343 			kmem_free(termiosp, len);
344 		} else {
345 			/*
346 			 * Gack!  Whine about it.
347 			 */
348 			cmn_err(CE_WARN,
349 			    "wc: Couldn't get ttymodes property!\n");
350 		}
351 		wscons.wc_ttycommon.t_iocpending = NULL;
352 		wscons.wc_flags = WCS_ISOPEN;
353 
354 		wc_get_size(&wscons);
355 
356 		bzero(&(wscons.wc_polledio), sizeof (wscons.wc_polledio));
357 		wscons.wc_polledio.cons_polledio_version = CONSPOLLEDIO_V0;
358 		wscons.wc_polledio.cons_polledio_argument =
359 			(struct cons_polledio_arg *)&wscons;
360 #if	defined(_CONSOLE_OUTPUT_VIA_SOFTWARE)
361 		wscons.wc_polledio.cons_polledio_putchar = wc_putchar;
362 #else
363 		wscons.wc_polledio.cons_polledio_putchar = NULL;
364 #endif
365 		wscons.wc_polledio.cons_polledio_getchar = wc_getchar;
366 		wscons.wc_polledio.cons_polledio_ischar = wc_ischar;
367 		wscons.wc_polledio.cons_polledio_enter = wc_polled_enter;
368 		wscons.wc_polledio.cons_polledio_exit = wc_polled_exit;
369 
370 #if	defined(_CONSOLE_OUTPUT_VIA_FIRMWARE)
371 		/*
372 		 * If we're talking directly to a framebuffer, we assume
373 		 * that it's a "slow" device, so that rendering should be
374 		 * deferred to a timeout or softcall so that we write
375 		 * a bunch of characters at once.
376 		 */
377 		wscons.wc_defer_output = prom_stdout_is_framebuffer();
378 #endif
379 	}
380 
381 	if (wscons.wc_ttycommon.t_flags & TS_XCLUDE) {
382 		if (secpolicy_excl_open(crp) != 0) {
383 			return (EBUSY);
384 		}
385 	}
386 	wscons.wc_ttycommon.t_readq = q;
387 	wscons.wc_ttycommon.t_writeq = WR(q);
388 	qprocson(q);
389 	return (0);
390 }
391 
392 /*ARGSUSED*/
393 static int
394 wcclose(queue_t *q, int flag, cred_t *crp)
395 {
396 	qprocsoff(q);
397 	if (wscons.wc_bufcallid != 0) {
398 		qunbufcall(q, wscons.wc_bufcallid);
399 		wscons.wc_bufcallid = 0;
400 	}
401 	if (wscons.wc_timeoutid != 0) {
402 		(void) quntimeout(q, wscons.wc_timeoutid);
403 		wscons.wc_timeoutid = 0;
404 	}
405 	ttycommon_close(&wscons.wc_ttycommon);
406 	wscons.wc_flags = 0;
407 	return (0);
408 }
409 
410 /*
411  * Put procedure for upper write queue.
412  * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
413  * queue up M_BREAK, M_DELAY, and M_DATA messages for processing by
414  * the start routine, and then call the start routine; discard
415  * everything else.
416  */
417 static int
418 wcuwput(queue_t *q, mblk_t *mp)
419 {
420 	switch (mp->b_datap->db_type) {
421 
422 	case M_STOP:
423 		wscons.wc_flags |= WCS_STOPPED;
424 		freemsg(mp);
425 		break;
426 
427 	case M_START:
428 		wscons.wc_flags &= ~WCS_STOPPED;
429 		wcstart();
430 		freemsg(mp);
431 		break;
432 
433 	case M_IOCTL: {
434 		struct iocblk *iocp;
435 		struct linkblk *linkp;
436 
437 		iocp = (struct iocblk *)mp->b_rptr;
438 		switch (iocp->ioc_cmd) {
439 
440 		case I_LINK:	/* stupid, but permitted */
441 		case I_PLINK:
442 			if (wscons.wc_kbdqueue != NULL) {
443 				/* somebody already linked */
444 				miocnak(q, mp, 0, EINVAL);
445 				return (0);
446 			}
447 			linkp = (struct linkblk *)mp->b_cont->b_rptr;
448 			wscons.wc_kbdqueue = WR(linkp->l_qbot);
449 			mp->b_datap->db_type = M_IOCACK;
450 			iocp->ioc_count = 0;
451 			wc_open_kb_polledio(&wscons, q, mp);
452 			break;
453 
454 		case I_UNLINK:	/* stupid, but permitted */
455 		case I_PUNLINK:
456 			linkp = (struct linkblk *)mp->b_cont->b_rptr;
457 			if (wscons.wc_kbdqueue != WR(linkp->l_qbot)) {
458 				/* not us */
459 				miocnak(q, mp, 0, EINVAL);
460 				return (0);
461 			}
462 
463 			mp->b_datap->db_type = M_IOCACK;
464 			iocp->ioc_count = 0;
465 			wc_close_kb_polledio(&wscons, q, mp);
466 			break;
467 
468 		case TCSETSW:
469 		case TCSETSF:
470 		case TCSETAW:
471 		case TCSETAF:
472 		case TCSBRK:
473 			/*
474 			 * The changes do not take effect until all
475 			 * output queued before them is drained.
476 			 * Put this message on the queue, so that
477 			 * "wcstart" will see it when it's done
478 			 * with the output before it.  Poke the
479 			 * start routine, just in case.
480 			 */
481 			(void) putq(q, mp);
482 			wcstart();
483 			break;
484 
485 		case CONSSETABORTENABLE:
486 		case CONSGETABORTENABLE:
487 		case KIOCSDIRECT:
488 			if (wscons.wc_kbdqueue != NULL) {
489 				(void) putnext(wscons.wc_kbdqueue, mp);
490 				break;
491 			}
492 			/* fall through */
493 
494 		default:
495 			/*
496 			 * Do it now.
497 			 */
498 			wcioctl(q, mp);
499 			break;
500 		}
501 		break;
502 	}
503 
504 	case M_FLUSH:
505 		if (*mp->b_rptr & FLUSHW) {
506 			/*
507 			 * Flush our write queue.
508 			 */
509 			flushq(q, FLUSHDATA);	/* XXX doesn't flush M_DELAY */
510 			*mp->b_rptr &= ~FLUSHW;	/* it has been flushed */
511 		}
512 		if (*mp->b_rptr & FLUSHR) {
513 			flushq(RD(q), FLUSHDATA);
514 			qreply(q, mp);	/* give the read queues a crack at it */
515 		} else
516 			freemsg(mp);
517 		break;
518 
519 	case M_BREAK:
520 		/*
521 		 * Ignore these, as they make no sense.
522 		 */
523 		freemsg(mp);
524 		break;
525 
526 	case M_DELAY:
527 	case M_DATA:
528 		/*
529 		 * Queue the message up to be transmitted,
530 		 * and poke the start routine.
531 		 */
532 		(void) putq(q, mp);
533 		wcstart();
534 		break;
535 
536 	default:
537 		/*
538 		 * "No, I don't want a subscription to Chain Store Age,
539 		 * thank you anyway."
540 		 */
541 		freemsg(mp);
542 		break;
543 	}
544 
545 	return (0);
546 }
547 
548 /*
549  * Retry an "ioctl", now that "qbufcall" claims we may be able to allocate
550  * the buffer we need.
551  */
552 /*ARGSUSED*/
553 static void
554 wcreioctl(void *arg)
555 {
556 	queue_t *q;
557 	mblk_t *mp;
558 
559 	wscons.wc_bufcallid = 0;
560 	q = wscons.wc_ttycommon.t_writeq;
561 	if ((mp = wscons.wc_ttycommon.t_iocpending) != NULL) {
562 		/* not pending any more */
563 		wscons.wc_ttycommon.t_iocpending = NULL;
564 		wcioctl(q, mp);
565 	}
566 }
567 
568 /*
569  * Process an "ioctl" message sent down to us.
570  */
571 static void
572 wcioctl(queue_t *q, mblk_t *mp)
573 {
574 	struct iocblk *iocp;
575 	size_t datasize;
576 	int error;
577 #if	defined(_CONSOLE_OUTPUT_VIA_SOFTWARE)
578 	int len;
579 #endif
580 
581 	iocp = (struct iocblk *)mp->b_rptr;
582 
583 	switch (iocp->ioc_cmd) {
584 	case TIOCSWINSZ:
585 		/*
586 		 * Ignore all attempts to set the screen size; the
587 		 * value in the EEPROM is guaranteed (modulo PROM bugs)
588 		 * to be the value used by the PROM monitor code, so it
589 		 * is by definition correct.  Many programs (e.g.,
590 		 * "login" and "tset") will attempt to reset the size
591 		 * to (0, 0) or (34, 80), neither of which is
592 		 * necessarily correct.
593 		 * We just ACK the message, so as not to disturb
594 		 * programs that set the sizes.
595 		 */
596 		iocp->ioc_count = 0;	/* no data returned */
597 		mp->b_datap->db_type = M_IOCACK;
598 		qreply(q, mp);
599 		return;
600 
601 	case CONSOPENPOLLEDIO:
602 		DPRINTF(PRINT_L1, PRINT_MASK_ALL,
603 			("wcioctl: CONSOPENPOLLEDIO\n"));
604 
605 		error = miocpullup(mp, sizeof (struct cons_polledio *));
606 		if (error != 0) {
607 			miocnak(q, mp, 0, error);
608 			return;
609 		}
610 
611 		/*
612 		 * If the keyboard driver does not support polled I/O
613 		 * then NAK this request.
614 		 */
615 		if (wscons.wc_kb_polledio != NULL) {
616 			/*
617 			 * We are given an appropriate-sized data block,
618 			 * and return a pointer to our structure in it.
619 			 */
620 			*(struct cons_polledio **)mp->b_cont->b_rptr =
621 				&wscons.wc_polledio;
622 
623 			mp->b_datap->db_type = M_IOCACK;
624 		} else {
625 			/*
626 			 * The driver does not support polled mode, so NAK
627 			 * the request.
628 			 */
629 			miocnak(q, mp, 0, ENXIO);
630 			return;
631 		}
632 
633 		qreply(q, mp);
634 		break;
635 
636 #if	defined(_CONSOLE_OUTPUT_VIA_SOFTWARE)
637 	case WC_OPEN_FB:
638 		/*
639 		 * Start out pessimistic, so that we can just jump to
640 		 * the reply to bail out.
641 		 */
642 		mp->b_datap->db_type = M_IOCNAK;
643 
644 		/*
645 		 * First test:  really, this should be done only from
646 		 * inside the kernel.  Unfortunately, that information
647 		 * doesn't seem to be available in a streams ioctl,
648 		 * so restrict it to root only.  (Perhaps we could check
649 		 * for ioc_cr == kcred.)
650 		 */
651 		if ((iocp->ioc_error = secpolicy_console(iocp->ioc_cr)) != 0)
652 			goto open_fail;
653 
654 		/*
655 		 * Some miscellaneous checks...
656 		 */
657 		iocp->ioc_error = EINVAL;
658 
659 		/*
660 		 * If we're already open, fail.
661 		 */
662 		if (wscons.wc_tem != NULL)
663 			goto open_fail;
664 
665 		/*
666 		 * If we don't have exactly one continuation block, fail.
667 		 */
668 		if (mp->b_cont == NULL ||
669 		    mp->b_cont->b_cont != NULL)
670 			goto open_fail;
671 
672 		/*
673 		 * If there's no null terminator in the string, fail.
674 		 */
675 		len = mp->b_cont->b_wptr - mp->b_cont->b_rptr;
676 		if (memchr(mp->b_cont->b_rptr, 0, len) == NULL)
677 			goto open_fail;
678 
679 		/*
680 		 * NOTE:  should eventually get default
681 		 * dimensions from a property, e.g. screen-#rows.
682 		 */
683 		iocp->ioc_error = tem_init(&wscons.wc_tem,
684 			(char *)mp->b_cont->b_rptr, iocp->ioc_cr,
685 			0, 0);
686 		/*
687 		 * Of course, if the terminal emulator initialization
688 		 * failed, fail.
689 		 */
690 		if (iocp->ioc_error != 0)
691 			goto open_fail;
692 
693 		/*
694 		 * Refresh terminal size with info from terminal emulator.
695 		 */
696 		wc_get_size(&wscons);
697 
698 		/*
699 		 * ... and succeed.
700 		 */
701 		mp->b_datap->db_type = M_IOCACK;
702 
703 open_fail:
704 		qreply(q, mp);
705 		break;
706 
707 	case WC_CLOSE_FB:
708 		/*
709 		 * There's nothing that can call this, so it's not
710 		 * really implemented.
711 		 */
712 		mp->b_datap->db_type = M_IOCNAK;
713 		/*
714 		 * However, if it were implemented, it would clearly
715 		 * be root-only.
716 		 */
717 		if ((iocp->ioc_error = secpolicy_console(iocp->ioc_cr)) != 0)
718 			goto close_fail;
719 
720 		iocp->ioc_error = EINVAL;
721 
722 close_fail:
723 		qreply(q, mp);
724 		break;
725 #endif
726 
727 	default:
728 
729 		/*
730 		 * The only way in which "ttycommon_ioctl" can fail is
731 		 * if the "ioctl" requires a response containing data
732 		 * to be returned to the user, and no mblk could be
733 		 * allocated for the data.  No such "ioctl" alters our
734 		 * state.  Thus, we always go ahead and do any
735 		 * state-changes the "ioctl" calls for.  If we couldn't
736 		 * allocate the data, "ttycommon_ioctl" has stashed the
737 		 * "ioctl" away safely, so we just call "qbufcall" to
738 		 * request that we be called back when we stand a
739 		 * better chance of allocating the data.
740 		 */
741 		datasize = ttycommon_ioctl(&wscons.wc_ttycommon, q, mp, &error);
742 		if (datasize != 0) {
743 			if (wscons.wc_bufcallid != 0)
744 				qunbufcall(q, wscons.wc_bufcallid);
745 			wscons.wc_bufcallid = qbufcall(q, datasize, BPRI_HI,
746 			    wcreioctl, NULL);
747 			return;
748 		}
749 
750 		if (error < 0) {
751 			if (iocp->ioc_cmd == TCSBRK)
752 				error = 0;
753 			else
754 				error = EINVAL;
755 		}
756 		if (error != 0) {
757 			iocp->ioc_error = error;
758 			mp->b_datap->db_type = M_IOCNAK;
759 		}
760 		qreply(q, mp);
761 		break;
762 	}
763 }
764 
765 /*
766  * This function gets the polled I/O structures from the lower
767  * keyboard driver.  If any initialization or resource allocation
768  * needs to be done by the lower driver, it will be done when
769  * the lower driver services this message.
770  */
771 static void
772 wc_open_kb_polledio(struct wscons *wscons, queue_t *q, mblk_t *mp)
773 {
774 	mblk_t *mp2;
775 	struct iocblk *iocp;
776 
777 	DPRINTF(PRINT_L1, PRINT_MASK_ALL,
778 		("wc_open_kb_polledio: sending CONSOPENPOLLEDIO\n"));
779 
780 	mp2 = mkiocb(CONSOPENPOLLEDIO);
781 
782 	if (mp2 == NULL) {
783 		/*
784 		 * If we can't get an mblk, then wait for it.
785 		 */
786 		goto nomem;
787 	}
788 
789 	mp2->b_cont = allocb(sizeof (struct cons_polledio *), BPRI_HI);
790 
791 	if (mp2->b_cont == NULL) {
792 		/*
793 		 * If we can't get an mblk, then wait for it, and release
794 		 * the mblk that we have already allocated.
795 		 */
796 		freemsg(mp2);
797 		goto nomem;
798 	}
799 
800 	iocp = (struct iocblk *)mp2->b_rptr;
801 
802 	iocp->ioc_count = sizeof (struct cons_polledio *);
803 	mp2->b_cont->b_wptr = mp2->b_cont->b_rptr +
804 		sizeof (struct cons_polledio *);
805 
806 	wscons->wc_pending_link = mp;
807 	wscons->wc_kb_getpolledio_id = iocp->ioc_id;
808 
809 	putnext(wscons->wc_kbdqueue, mp2);
810 
811 	return;
812 
813 nomem:
814 	iocp = (struct iocblk *)mp->b_rptr;
815 	iocp->ioc_error = ENOMEM;
816 	mp->b_datap->db_type = M_IOCNAK;
817 	qreply(q, mp);
818 }
819 
820 /*
821  * This function releases the polled I/O structures from the lower
822  * keyboard driver.  If any de-initialization needs to be done, or
823  * any resources need to be released, it will be done when the lower
824  * driver services this message.
825  */
826 static void
827 wc_close_kb_polledio(struct wscons *wscons, queue_t *q, mblk_t *mp)
828 {
829 	mblk_t *mp2;
830 	struct iocblk *iocp;
831 
832 	DPRINTF(PRINT_L1, PRINT_MASK_ALL,
833 		("wc_close_kb_polledio: sending CONSCLOSEPOLLEDIO\n"));
834 
835 	mp2 = mkiocb(CONSCLOSEPOLLEDIO);
836 
837 	if (mp2 == NULL) {
838 		/*
839 		 * If we can't get an mblk, then wait for it.
840 		 */
841 		goto nomem;
842 	}
843 
844 	mp2->b_cont = allocb(sizeof (struct cons_polledio *), BPRI_HI);
845 
846 	if (mp2->b_cont == NULL) {
847 		/*
848 		 * If we can't get an mblk, then wait for it, and release
849 		 * the mblk that we have already allocated.
850 		 */
851 		freemsg(mp2);
852 
853 		goto nomem;
854 	}
855 
856 	iocp = (struct iocblk *)mp2->b_rptr;
857 
858 	iocp->ioc_count = 0;
859 
860 	wscons->wc_pending_link = mp;
861 	wscons->wc_kb_getpolledio_id = iocp->ioc_id;
862 
863 	putnext(wscons->wc_kbdqueue, mp2);
864 
865 	return;
866 
867 nomem:
868 	iocp = (struct iocblk *)mp->b_rptr;
869 	iocp->ioc_error = ENOMEM;
870 	mp->b_datap->db_type = M_IOCNAK;
871 	qreply(q, mp);
872 }
873 
874 #if	defined(_CONSOLE_OUTPUT_VIA_FIRMWARE)
875 /* ARGSUSED */
876 static void
877 wcopoll(void *arg)
878 {
879 	queue_t *q;
880 
881 	q = wscons.wc_ttycommon.t_writeq;
882 	wscons.wc_timeoutid = 0;
883 	/* See if we can continue output */
884 	if ((wscons.wc_flags & WCS_BUSY) && wscons.wc_pendc != -1) {
885 		if (prom_mayput((char)wscons.wc_pendc) == 0) {
886 			wscons.wc_pendc = -1;
887 			wscons.wc_flags &= ~WCS_BUSY;
888 			if (!(wscons.wc_flags&(WCS_DELAY|WCS_STOPPED)))
889 				wcstart();
890 		} else
891 			wscons.wc_timeoutid = qtimeout(q, wcopoll, NULL, 1);
892 	}
893 }
894 #endif	/* _CONSOLE_OUTPUT_VIA_FIRMWARE */
895 
896 /*
897  * Restart output on the console after a timeout.
898  */
899 /* ARGSUSED */
900 static void
901 wcrstrt(void *arg)
902 {
903 	ASSERT(wscons.wc_ttycommon.t_writeq != NULL);
904 	wscons.wc_flags &= ~WCS_DELAY;
905 	wcstart();
906 }
907 
908 /*
909  * Start console output
910  */
911 static void
912 wcstart(void)
913 {
914 #if	defined(_CONSOLE_OUTPUT_VIA_FIRMWARE)
915 	int c;
916 	ssize_t cc;
917 #endif
918 	queue_t *q;
919 	mblk_t *bp;
920 	mblk_t *nbp;
921 
922 	/*
923 	 * If we're waiting for something to happen (delay timeout to
924 	 * expire, current transmission to finish, output to be
925 	 * restarted, output to finish draining), don't grab anything
926 	 * new.
927 	 */
928 	if (wscons.wc_flags & (WCS_DELAY|WCS_BUSY|WCS_STOPPED))
929 		goto out;
930 
931 	q = wscons.wc_ttycommon.t_writeq;
932 	/*
933 	 * assumes that we have been called by whoever holds the
934 	 * exclusionary lock on the write-side queue (protects
935 	 * wc_flags and wc_pendc).
936 	 */
937 	for (;;) {
938 		if ((bp = getq(q)) == NULL)
939 			goto out;	/* nothing to transmit */
940 
941 		/*
942 		 * We have a new message to work on.
943 		 * Check whether it's a delay or an ioctl (the latter
944 		 * occurs if the ioctl in question was waiting for the output
945 		 * to drain).  If it's one of those, process it immediately.
946 		 */
947 		switch (bp->b_datap->db_type) {
948 
949 		case M_DELAY:
950 			/*
951 			 * Arrange for "wcrstrt" to be called when the
952 			 * delay expires; it will turn WCS_DELAY off,
953 			 * and call "wcstart" to grab the next message.
954 			 */
955 			if (wscons.wc_timeoutid != 0)
956 				(void) quntimeout(q, wscons.wc_timeoutid);
957 			wscons.wc_timeoutid = qtimeout(q, wcrstrt, NULL,
958 			    (clock_t)(*(unsigned char *)bp->b_rptr + 6));
959 			wscons.wc_flags |= WCS_DELAY;
960 			freemsg(bp);
961 			goto out;	/* wait for this to finish */
962 
963 		case M_IOCTL:
964 			/*
965 			 * This ioctl was waiting for the output ahead of
966 			 * it to drain; obviously, it has.  Do it, and
967 			 * then grab the next message after it.
968 			 */
969 			wcioctl(q, bp);
970 			continue;
971 		}
972 
973 #if	defined(_CONSOLE_OUTPUT_VIA_SOFTWARE)
974 		if (wscons.wc_tem != NULL) {
975 		    for (nbp = bp; nbp != NULL; nbp = nbp->b_cont) {
976 			    if (nbp->b_wptr > nbp->b_rptr) {
977 				    (void) tem_write(wscons.wc_tem,
978 					nbp->b_rptr, nbp->b_wptr - nbp->b_rptr,
979 					kcred);
980 			    }
981 		    }
982 		}
983 		freemsg(bp);
984 #else	/* _CONSOLE_OUTPUT_VIA_FIRMWARE */
985 		if ((cc = bp->b_wptr - bp->b_rptr) == 0) {
986 			freemsg(bp);
987 			continue;
988 		}
989 
990 		/*
991 		 * Direct output to the frame buffer if this device
992 		 * is not the "hardware" console.
993 		 */
994 		if (wscons.wc_defer_output) {
995 			/*
996 			 * Never do output here;
997 			 * it takes forever.
998 			 */
999 			wscons.wc_flags |= WCS_BUSY;
1000 			wscons.wc_pendc = -1;
1001 			(void) putbq(q, bp);
1002 			if (q->q_count > 128) {	/* do it soon */
1003 				softcall(wconsout, NULL);
1004 			} else {		/* wait a bit */
1005 				if (wscons.wc_timeoutid != 0)
1006 					(void) quntimeout(q,
1007 					    wscons.wc_timeoutid);
1008 				wscons.wc_timeoutid = qtimeout(q, wconsout,
1009 				    NULL, hz / 30);
1010 			}
1011 			goto out;
1012 		}
1013 
1014 		for (;;) {
1015 			c = *bp->b_rptr++;
1016 			cc--;
1017 			if (prom_mayput((char)c) != 0) {
1018 				wscons.wc_flags |= WCS_BUSY;
1019 				wscons.wc_pendc = c;
1020 				if (wscons.wc_timeoutid != 0)
1021 					(void) quntimeout(q,
1022 					    wscons.wc_timeoutid);
1023 				wscons.wc_timeoutid = qtimeout(q, wcopoll,
1024 				    NULL, 1);
1025 				if (bp != NULL)
1026 				/* not done with this message yet */
1027 					(void) putbq(q, bp);
1028 				goto out;
1029 			}
1030 			while (cc <= 0) {
1031 				nbp = bp;
1032 				bp = bp->b_cont;
1033 				freeb(nbp);
1034 				if (bp == NULL)
1035 					goto out;
1036 				cc = bp->b_wptr - bp->b_rptr;
1037 			}
1038 		}
1039 #endif
1040 	}
1041 out:
1042 	;
1043 }
1044 
1045 #if	defined(_CONSOLE_OUTPUT_VIA_FIRMWARE)
1046 /*
1047  * Output to frame buffer console.
1048  * It takes a long time to scroll.
1049  */
1050 /* ARGSUSED */
1051 static void
1052 wconsout(void *dummy)
1053 {
1054 	uchar_t *cp;
1055 	ssize_t cc;
1056 	queue_t *q;
1057 	mblk_t *bp;
1058 	mblk_t *nbp;
1059 	char *current_position;
1060 	ssize_t bytes_left;
1061 
1062 	if ((q = wscons.wc_ttycommon.t_writeq) == NULL) {
1063 		return;	/* not attached to a stream */
1064 	}
1065 
1066 	/*
1067 	 * Set up to copy up to MAXHIWAT bytes.
1068 	 */
1069 	current_position = &obuf[0];
1070 	bytes_left = MAXHIWAT;
1071 	while ((bp = getq(q)) != NULL) {
1072 		if (bp->b_datap->db_type == M_IOCTL) {
1073 			/*
1074 			 * This ioctl was waiting for the output ahead of
1075 			 * it to drain; obviously, it has.  Put it back
1076 			 * so that "wcstart" can handle it, and transmit
1077 			 * what we've got.
1078 			 */
1079 			(void) putbq(q, bp);
1080 			goto transmit;
1081 		}
1082 
1083 		do {
1084 			cp = bp->b_rptr;
1085 			cc = bp->b_wptr - cp;
1086 			while (cc != 0) {
1087 				if (bytes_left == 0) {
1088 					/*
1089 					 * Out of buffer space; put this
1090 					 * buffer back on the queue, and
1091 					 * transmit what we have.
1092 					 */
1093 					bp->b_rptr = cp;
1094 					(void) putbq(q, bp);
1095 					goto transmit;
1096 				}
1097 				*current_position++ = *cp++;
1098 				cc--;
1099 				bytes_left--;
1100 			}
1101 			nbp = bp;
1102 			bp = bp->b_cont;
1103 			freeb(nbp);
1104 		} while (bp != NULL);
1105 	}
1106 
1107 transmit:
1108 	if ((cc = MAXHIWAT - bytes_left) != 0)
1109 		console_puts(obuf, cc);
1110 
1111 	wscons.wc_flags &= ~WCS_BUSY;
1112 	wcstart();
1113 }
1114 #endif
1115 
1116 /*
1117  * Put procedure for lower read queue.
1118  * Pass everything up to queue above "upper half".
1119  */
1120 static int
1121 wclrput(queue_t *q, mblk_t *mp)
1122 {
1123 	queue_t *upq;
1124 	struct iocblk *iocp;
1125 
1126 	DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1127 		("wclrput: wclrput type = 0x%x\n", mp->b_datap->db_type));
1128 
1129 	switch (mp->b_datap->db_type) {
1130 
1131 	case M_FLUSH:
1132 		if (*mp->b_rptr == FLUSHW || *mp->b_rptr == FLUSHRW) {
1133 			/*
1134 			 * Flush our write queue.
1135 			 */
1136 			/* XXX doesn't flush M_DELAY */
1137 			flushq(WR(q), FLUSHDATA);
1138 			*mp->b_rptr = FLUSHR;	/* it has been flushed */
1139 		}
1140 		if (*mp->b_rptr == FLUSHR || *mp->b_rptr == FLUSHRW) {
1141 			flushq(q, FLUSHDATA);
1142 			*mp->b_rptr = FLUSHW;	/* it has been flushed */
1143 			qreply(q, mp);	/* give the read queues a crack at it */
1144 		} else
1145 			freemsg(mp);
1146 		break;
1147 
1148 	case M_DATA:
1149 		if ((upq = wscons.wc_ttycommon.t_readq) != NULL) {
1150 			if (!canput(upq->q_next)) {
1151 				ttycommon_qfull(&wscons.wc_ttycommon, upq);
1152 				wcstart();
1153 				freemsg(mp);
1154 			} else
1155 				putnext(upq, mp);
1156 		} else
1157 			freemsg(mp);
1158 		break;
1159 
1160 	case M_IOCACK:
1161 	case M_IOCNAK:
1162 		iocp = (struct iocblk *)mp->b_rptr;
1163 		if (wscons.wc_pending_link != NULL &&
1164 		    iocp->ioc_id == wscons.wc_kb_getpolledio_id) {
1165 			switch (mp->b_datap->db_type) {
1166 
1167 			case M_IOCACK:
1168 				switch (iocp->ioc_cmd) {
1169 
1170 
1171 				case CONSOPENPOLLEDIO:
1172 					DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1173 						("wclrput: "
1174 						"ACK CONSOPENPOLLEDIO\n"));
1175 					wscons.wc_kb_polledio =
1176 					    *(struct cons_polledio **)
1177 						mp->b_cont->b_rptr;
1178 					break;
1179 
1180 				case CONSCLOSEPOLLEDIO:
1181 					DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1182 						("wclrput: "
1183 						"ACK CONSCLOSEPOLLEDIO\n"));
1184 					wscons.wc_kb_polledio = NULL;
1185 					wscons.wc_kbdqueue = NULL;
1186 					break;
1187 				default:
1188 					DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1189 						("wclrput: "
1190 						"ACK UNKNOWN\n"));
1191 				}
1192 
1193 				break;
1194 			case M_IOCNAK:
1195 				/*
1196 				 * Keyboard may or may not support polled I/O.
1197 				 * This ioctl may have been rejected because
1198 				 * we only have the wc->conskbd chain built,
1199 				 * and the keyboard driver has not been linked
1200 				 * underneath conskbd yet.
1201 				 */
1202 				DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1203 					("wclrput: NAK\n"));
1204 
1205 				switch (iocp->ioc_cmd) {
1206 
1207 				case CONSCLOSEPOLLEDIO:
1208 					wscons.wc_kb_polledio = NULL;
1209 					wscons.wc_kbdqueue = NULL;
1210 					break;
1211 				}
1212 				break;
1213 			}
1214 
1215 			/*
1216 			 * Discard the response, replace it with the
1217 			 * pending response to the I_PLINK, then let it
1218 			 * flow upward.
1219 			 */
1220 			freemsg(mp);
1221 			mp = wscons.wc_pending_link;
1222 			wscons.wc_pending_link = NULL;
1223 			wscons.wc_kb_getpolledio_id = 0;
1224 		}
1225 		/* FALLTHROUGH */
1226 
1227 	default:	/* inc M_ERROR, M_HANGUP, M_IOCACK, M_IOCNAK, ... */
1228 		DPRINTF(PRINT_L1, PRINT_MASK_ALL,
1229 			("wclrput: Message DISCARDED\n"));
1230 		if ((upq = wscons.wc_ttycommon.t_readq) != NULL) {
1231 			putnext(upq, mp);
1232 		} else {
1233 			freemsg(mp);
1234 		}
1235 		break;
1236 	}
1237 
1238 	return (0);
1239 }
1240 
1241 /*
1242  * Auxiliary routines, for allowing the workstation console to be redirected.
1243  */
1244 
1245 /*
1246  * Given a minor device number for a wscons instance, return a held vnode for
1247  * it.
1248  *
1249  * We currently support only one instance, for the "workstation console".
1250  */
1251 int
1252 wcvnget(minor_t unit, vnode_t **vpp)
1253 {
1254 	if (unit != 0 || rwsconsvp == NULL)
1255 		return (ENXIO);
1256 
1257 	/*
1258 	 * rwsconsvp is already held, so we don't have to do it here.
1259 	 */
1260 	*vpp = rwsconsvp;
1261 	return (0);
1262 }
1263 
1264 /*
1265  * Release the vnode that wcvnget returned.
1266  */
1267 /* ARGSUSED */
1268 void
1269 wcvnrele(minor_t unit, vnode_t *vp)
1270 {
1271 	/*
1272 	 * Nothing to do, since we only support the workstation console
1273 	 * instance that's held throughout the system's lifetime.
1274 	 */
1275 }
1276 
1277 /*
1278  * The declaration and initialization of the wscons_srvnops has been
1279  * moved to space.c to allow "wc" to become a loadable module.
1280  */
1281 
1282 #if	defined(_CONSOLE_OUTPUT_VIA_SOFTWARE)
1283 /*
1284  * This is for systems without OBP.
1285  */
1286 
1287 static void
1288 wc_putchar(struct cons_polledio_arg *arg, unsigned char c)
1289 {
1290 	if (c == '\n')
1291 		wc_putchar(arg, '\r');
1292 
1293 	if (wscons.wc_tem == NULL) {
1294 		/*
1295 		 * We have no terminal emulator configured.  We have no
1296 		 * recourse but to drop the output on the floor.
1297 		 */
1298 		return;
1299 	}
1300 
1301 	/*
1302 	 * We ignore the result code.  After all, what could we do?
1303 	 * Write something to the console??
1304 	 */
1305 	(void) tem_polled_write(wscons.wc_tem, &c, 1);
1306 }
1307 #endif	/* _CONSOLE_OUTPUT_VIA_SOFTWARE */
1308 
1309 /*
1310  * These are for systems without OBP, and for devices that cannot be
1311  * shared between Solaris and the OBP.
1312  */
1313 static int
1314 wc_getchar(struct cons_polledio_arg *arg)
1315 {
1316 	struct wscons *wscons = (struct wscons *)arg;
1317 
1318 	if (wscons->wc_kb_polledio == NULL) {
1319 		prom_printf("wscons:  getchar with no keyboard support");
1320 		prom_printf("Halted...");
1321 		for (;;)
1322 			/* HANG FOREVER */;
1323 	}
1324 
1325 	return (wscons->wc_kb_polledio->cons_polledio_getchar(
1326 			wscons->wc_kb_polledio->cons_polledio_argument));
1327 }
1328 
1329 static boolean_t
1330 wc_ischar(struct cons_polledio_arg *arg)
1331 {
1332 	struct wscons *wscons = (struct wscons *)arg;
1333 
1334 	if (wscons->wc_kb_polledio == NULL)
1335 		return (B_FALSE);
1336 
1337 	return (wscons->wc_kb_polledio->cons_polledio_ischar(
1338 			wscons->wc_kb_polledio->cons_polledio_argument));
1339 }
1340 
1341 static void
1342 wc_polled_enter(struct cons_polledio_arg *arg)
1343 {
1344 	struct wscons *wscons = (struct wscons *)arg;
1345 
1346 	if (wscons->wc_kb_polledio == NULL)
1347 		return;
1348 
1349 	if (wscons->wc_kb_polledio->cons_polledio_enter != NULL) {
1350 		wscons->wc_kb_polledio->cons_polledio_enter(
1351 			wscons->wc_kb_polledio->cons_polledio_argument);
1352 	}
1353 }
1354 
1355 static void
1356 wc_polled_exit(struct cons_polledio_arg *arg)
1357 {
1358 	struct wscons *wscons = (struct wscons *)arg;
1359 
1360 	if (wscons->wc_kb_polledio == NULL)
1361 		return;
1362 
1363 	if (wscons->wc_kb_polledio->cons_polledio_exit != NULL) {
1364 		wscons->wc_kb_polledio->cons_polledio_exit(
1365 			wscons->wc_kb_polledio->cons_polledio_argument);
1366 	}
1367 }
1368 
1369 
1370 #ifdef DEBUG
1371 static void
1372 wc_dprintf(const char *fmt, ...)
1373 {
1374 	char buf[256];
1375 	va_list ap;
1376 
1377 	va_start(ap, fmt);
1378 	(void) vsprintf(buf, fmt, ap);
1379 	va_end(ap);
1380 
1381 	cmn_err(CE_CONT, "wc: %s", buf);
1382 }
1383 #endif
1384 
1385 #if	defined(_CONSOLE_OUTPUT_VIA_FIRMWARE)
1386 static int
1387 atou_n(char *s, int n)
1388 {
1389 	int res;
1390 
1391 	res = 0;
1392 	while (n > 0 && *s != '\0') {
1393 		if (*s < '0' || *s > '9')
1394 			return (0);
1395 		res *= 10;
1396 		res += *s - '0';
1397 		s++;
1398 		n--;
1399 	}
1400 	return (res);
1401 }
1402 
1403 static int
1404 get_option_string_int(char *name)
1405 {
1406 	char *data;
1407 	uint_t len;
1408 	int res;
1409 
1410 	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(), 0,
1411 	    name, (uchar_t **)&data, &len) != DDI_PROP_SUCCESS)
1412 		return (0);
1413 
1414 	res = atou_n(data, len);
1415 	ddi_prop_free(data);
1416 	return (res);
1417 }
1418 #endif
1419 
1420 /*
1421  * Gets the number of text rows and columns and the
1422  * width and height (in pixels) of the console.
1423  */
1424 static void
1425 wc_get_size(struct wscons *wscons)
1426 {
1427 #if	defined _CONSOLE_OUTPUT_VIA_FIRMWARE
1428 	static char *cols = "screen-#columns";
1429 	static char *rows = "screen-#rows";
1430 	static char *width = "screen-width";
1431 	static char *height = "screen-height";
1432 	struct winsize *t;
1433 
1434 	t = &wscons->wc_ttycommon.t_size;
1435 
1436 	/*
1437 	 * Get the number of columns
1438 	 */
1439 	t->ws_col = (unsigned short)get_option_string_int(cols);
1440 
1441 	if (t->ws_col < MINCOLS)
1442 		t->ws_col = LOSCREENCOLS;
1443 	else if (t->ws_col > MAXCOLS)
1444 		t->ws_col = HISCREENCOLS;
1445 
1446 	/*
1447 	 * Get the number of rows
1448 	 */
1449 	t->ws_row = (unsigned short)get_option_string_int(rows);
1450 
1451 	if (t->ws_row < MINLINES)
1452 		t->ws_row = LOSCREENLINES;
1453 	else if (t->ws_row > MAXLINES)
1454 		t->ws_row = HISCREENLINES;
1455 
1456 	/*
1457 	 * Get the size in pixels.
1458 	 */
1459 	t->ws_xpixel = (unsigned short)get_option_string_int(width);
1460 	t->ws_ypixel = (unsigned short)get_option_string_int(height);
1461 
1462 #else	/* _CONSOLE_OUTPUT_VIA_SOFTWARE */
1463 	struct winsize *t;
1464 	int r, c, x, y;
1465 
1466 	t = &wscons->wc_ttycommon.t_size;
1467 
1468 	if (wscons->wc_tem != NULL) {
1469 		tem_get_size(wscons->wc_tem, &r, &c, &x, &y);
1470 		t->ws_row = (unsigned short)r;
1471 		t->ws_col = (unsigned short)c;
1472 		t->ws_xpixel = (unsigned short)x;
1473 		t->ws_ypixel = (unsigned short)y;
1474 	} else {
1475 		t->ws_row = LOSCREENLINES;
1476 		t->ws_col = LOSCREENCOLS;
1477 		t->ws_xpixel = 0;
1478 		t->ws_ypixel = 0;
1479 	}
1480 #endif
1481 }
1482