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