xref: /onnv-gate/usr/src/uts/common/io/wscons.c (revision 1762:b91aae9ab33e)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51253Slq150181  * Common Development and Distribution License (the "License").
61253Slq150181  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
211253Slq150181 
220Sstevel@tonic-gate /*
231253Slq150181  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * "Workstation console" multiplexor driver for Sun.
310Sstevel@tonic-gate  *
320Sstevel@tonic-gate  * Sends output to the primary frame buffer using the PROM monitor;
330Sstevel@tonic-gate  * gets input from a stream linked below us that is the "keyboard
340Sstevel@tonic-gate  * driver", below which is linked the primary keyboard.
350Sstevel@tonic-gate  */
360Sstevel@tonic-gate 
370Sstevel@tonic-gate #include <sys/types.h>
380Sstevel@tonic-gate #include <sys/param.h>
390Sstevel@tonic-gate #include <sys/signal.h>
400Sstevel@tonic-gate #include <sys/cred.h>
410Sstevel@tonic-gate #include <sys/vnode.h>
420Sstevel@tonic-gate #include <sys/termios.h>
430Sstevel@tonic-gate #include <sys/termio.h>
440Sstevel@tonic-gate #include <sys/ttold.h>
450Sstevel@tonic-gate #include <sys/stropts.h>
460Sstevel@tonic-gate #include <sys/stream.h>
470Sstevel@tonic-gate #include <sys/strsun.h>
480Sstevel@tonic-gate #include <sys/tty.h>
490Sstevel@tonic-gate #include <sys/buf.h>
500Sstevel@tonic-gate #include <sys/uio.h>
510Sstevel@tonic-gate #include <sys/stat.h>
520Sstevel@tonic-gate #include <sys/kmem.h>
530Sstevel@tonic-gate #include <sys/cpuvar.h>
540Sstevel@tonic-gate #include <sys/kbio.h>
550Sstevel@tonic-gate #include <sys/strredir.h>
560Sstevel@tonic-gate #include <sys/fs/snode.h>
570Sstevel@tonic-gate #include <sys/consdev.h>
580Sstevel@tonic-gate #include <sys/conf.h>
590Sstevel@tonic-gate #include <sys/ddi.h>
600Sstevel@tonic-gate #include <sys/sunddi.h>
610Sstevel@tonic-gate #include <sys/debug.h>
620Sstevel@tonic-gate #include <sys/console.h>
630Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
640Sstevel@tonic-gate #include <sys/promif.h>
650Sstevel@tonic-gate #include <sys/policy.h>
661253Slq150181 #include <sys/tem.h>
671253Slq150181 #include <sys/wscons.h>
680Sstevel@tonic-gate 
690Sstevel@tonic-gate #define	MINLINES	10
700Sstevel@tonic-gate #define	MAXLINES	48
710Sstevel@tonic-gate #define	LOSCREENLINES	34
720Sstevel@tonic-gate #define	HISCREENLINES	48
730Sstevel@tonic-gate 
740Sstevel@tonic-gate #define	MINCOLS		10
750Sstevel@tonic-gate #define	MAXCOLS		120
760Sstevel@tonic-gate #define	LOSCREENCOLS	80
770Sstevel@tonic-gate #define	HISCREENCOLS	120
780Sstevel@tonic-gate 
791253Slq150181 struct wscons {
801253Slq150181 	struct tem *wc_tem;		/* Terminal emulator state */
810Sstevel@tonic-gate 	int	wc_flags;		/* random flags (protected by */
820Sstevel@tonic-gate 					/* write-side exclusion lock  */
830Sstevel@tonic-gate 	dev_t	wc_dev;			/* major/minor for this device */
840Sstevel@tonic-gate 	tty_common_t wc_ttycommon;	/* data common to all tty drivers */
851253Slq150181 #ifdef _HAVE_TEM_FIRMWARE
860Sstevel@tonic-gate 	int	wc_pendc;		/* pending output character */
870Sstevel@tonic-gate 	int	wc_defer_output;	/* set if output device is "slow" */
881253Slq150181 #endif /* _HAVE_TEM_FIRMWARE */
890Sstevel@tonic-gate 	queue_t	*wc_kbdqueue;		/* "console keyboard" device queue */
900Sstevel@tonic-gate 					/* below us */
910Sstevel@tonic-gate 	bufcall_id_t wc_bufcallid;	/* id returned by qbufcall */
920Sstevel@tonic-gate 	timeout_id_t wc_timeoutid;	/* id returned by qtimeout */
930Sstevel@tonic-gate 	cons_polledio_t		wc_polledio; /* polled I/O function pointers */
940Sstevel@tonic-gate 	cons_polledio_t		*wc_kb_polledio; /* keyboard's polledio */
950Sstevel@tonic-gate 	unsigned int	wc_kb_getpolledio_id; /* id for kb CONSOPENPOLLEDIO */
960Sstevel@tonic-gate 	mblk_t	*wc_pending_link;	/* I_PLINK pending for kb polledio */
970Sstevel@tonic-gate } wscons;
980Sstevel@tonic-gate 
990Sstevel@tonic-gate #define	WCS_ISOPEN	0x00000001	/* open is complete */
1000Sstevel@tonic-gate #define	WCS_STOPPED	0x00000002	/* output is stopped */
1010Sstevel@tonic-gate #define	WCS_DELAY	0x00000004	/* waiting for delay to finish */
1020Sstevel@tonic-gate #define	WCS_BUSY	0x00000008	/* waiting for transmission to finish */
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate static int	wcopen(queue_t *, dev_t *, int, int, cred_t *);
1050Sstevel@tonic-gate static int	wcclose(queue_t *, int, cred_t *);
1060Sstevel@tonic-gate static int	wcuwput(queue_t *, mblk_t *);
1070Sstevel@tonic-gate static int	wclrput(queue_t *, mblk_t *);
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate static struct module_info wcm_info = {
1100Sstevel@tonic-gate 	0,
1110Sstevel@tonic-gate 	"wc",
1120Sstevel@tonic-gate 	0,
1130Sstevel@tonic-gate 	INFPSZ,
1140Sstevel@tonic-gate 	2048,
1150Sstevel@tonic-gate 	128
1160Sstevel@tonic-gate };
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate static struct qinit wcurinit = {
1190Sstevel@tonic-gate 	putq,
1200Sstevel@tonic-gate 	NULL,
1210Sstevel@tonic-gate 	wcopen,
1220Sstevel@tonic-gate 	wcclose,
1230Sstevel@tonic-gate 	NULL,
1240Sstevel@tonic-gate 	&wcm_info,
1250Sstevel@tonic-gate 	NULL
1260Sstevel@tonic-gate };
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate static struct qinit wcuwinit = {
1290Sstevel@tonic-gate 	wcuwput,
1300Sstevel@tonic-gate 	NULL,
1310Sstevel@tonic-gate 	wcopen,
1320Sstevel@tonic-gate 	wcclose,
1330Sstevel@tonic-gate 	NULL,
1340Sstevel@tonic-gate 	&wcm_info,
1350Sstevel@tonic-gate 	NULL
1360Sstevel@tonic-gate };
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate static struct qinit wclrinit = {
1390Sstevel@tonic-gate 	wclrput,
1400Sstevel@tonic-gate 	NULL,
1410Sstevel@tonic-gate 	NULL,
1420Sstevel@tonic-gate 	NULL,
1430Sstevel@tonic-gate 	NULL,
1440Sstevel@tonic-gate 	&wcm_info,
1450Sstevel@tonic-gate 	NULL
1460Sstevel@tonic-gate };
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate /*
1490Sstevel@tonic-gate  * We always putnext directly to the underlying queue.
1500Sstevel@tonic-gate  */
1510Sstevel@tonic-gate static struct qinit wclwinit = {
1520Sstevel@tonic-gate 	NULL,
1530Sstevel@tonic-gate 	NULL,
1540Sstevel@tonic-gate 	NULL,
1550Sstevel@tonic-gate 	NULL,
1560Sstevel@tonic-gate 	NULL,
1570Sstevel@tonic-gate 	&wcm_info,
1580Sstevel@tonic-gate 	NULL
1590Sstevel@tonic-gate };
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate static struct streamtab wcinfo = {
1620Sstevel@tonic-gate 	&wcurinit,
1630Sstevel@tonic-gate 	&wcuwinit,
1640Sstevel@tonic-gate 	&wclrinit,
1650Sstevel@tonic-gate 	&wclwinit,
1660Sstevel@tonic-gate };
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate static int wc_info(dev_info_t *, ddi_info_cmd_t, void *, void **result);
1690Sstevel@tonic-gate static int wc_attach(dev_info_t *, ddi_attach_cmd_t);
1700Sstevel@tonic-gate static dev_info_t *wc_dip;
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate DDI_DEFINE_STREAM_OPS(wc_ops, nulldev, nulldev, wc_attach, nodev, nodev,
1730Sstevel@tonic-gate     wc_info, D_MTPERMOD | D_MP, &wcinfo);
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate static void	wcreioctl(void *);
1760Sstevel@tonic-gate static void 	wcioctl(queue_t *, mblk_t *);
1771253Slq150181 #ifdef _HAVE_TEM_FIRMWARE
1780Sstevel@tonic-gate static void	wcopoll(void *);
1790Sstevel@tonic-gate static void	wconsout(void *);
1801253Slq150181 #endif /* _HAVE_TEM_FIRMWARE */
1810Sstevel@tonic-gate static void	wcrstrt(void *);
1820Sstevel@tonic-gate static void	wcstart(void);
1830Sstevel@tonic-gate static void	wc_open_kb_polledio(struct wscons *wc, queue_t *q, mblk_t *mp);
1840Sstevel@tonic-gate static void	wc_close_kb_polledio(struct wscons *wc, queue_t *q, mblk_t *mp);
185*1762Slt200341 static void	wc_polled_putchar(cons_polledio_arg_t arg, unsigned char c);
186*1762Slt200341 static boolean_t wc_polled_ischar(cons_polledio_arg_t arg);
187*1762Slt200341 static int	wc_polled_getchar(cons_polledio_arg_t arg);
188*1762Slt200341 static void	wc_polled_enter(cons_polledio_arg_t arg);
189*1762Slt200341 static void	wc_polled_exit(cons_polledio_arg_t arg);
1900Sstevel@tonic-gate static void	wc_get_size(struct wscons *wscons);
1911253Slq150181 static void	wc_modechg_cb(tem_modechg_cb_arg_t arg);
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate #include <sys/types.h>
1940Sstevel@tonic-gate #include <sys/conf.h>
1950Sstevel@tonic-gate #include <sys/param.h>
1960Sstevel@tonic-gate #include <sys/systm.h>
1970Sstevel@tonic-gate #include <sys/errno.h>
1980Sstevel@tonic-gate #include <sys/modctl.h>
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate static struct dev_ops wc_ops;
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate /*
2030Sstevel@tonic-gate  * Debug printing
2040Sstevel@tonic-gate  */
2050Sstevel@tonic-gate #ifndef DPRINTF
2060Sstevel@tonic-gate #ifdef DEBUG
2070Sstevel@tonic-gate /*PRINTFLIKE1*/
2080Sstevel@tonic-gate static void	wc_dprintf(const char *fmt, ...) __KPRINTFLIKE(1);
2090Sstevel@tonic-gate #define	DPRINTF(l, m, args) \
2100Sstevel@tonic-gate 	(((l) >= wc_errlevel) && ((m) & wc_errmask) ?	\
2111253Slq150181 		wc_dprintf args :			\
2120Sstevel@tonic-gate 		(void) 0)
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate /*
2150Sstevel@tonic-gate  * Severity levels for printing
2160Sstevel@tonic-gate  */
2170Sstevel@tonic-gate #define	PRINT_L0	0	/* print every message */
2180Sstevel@tonic-gate #define	PRINT_L1	1	/* debug */
2190Sstevel@tonic-gate #define	PRINT_L2	2	/* quiet */
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate /*
2220Sstevel@tonic-gate  * Masks
2230Sstevel@tonic-gate  */
2240Sstevel@tonic-gate #define	PRINT_MASK_ALL		0xFFFFFFFFU
2250Sstevel@tonic-gate uint_t	wc_errmask = PRINT_MASK_ALL;
2260Sstevel@tonic-gate uint_t	wc_errlevel = PRINT_L2;
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate #else
2290Sstevel@tonic-gate #define	DPRINTF(l, m, args)	/* NOTHING */
2300Sstevel@tonic-gate #endif
2310Sstevel@tonic-gate #endif
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate /*
2340Sstevel@tonic-gate  * Module linkage information for the kernel.
2350Sstevel@tonic-gate  */
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate static struct modldrv modldrv = {
2380Sstevel@tonic-gate 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
2390Sstevel@tonic-gate 	"Workstation multiplexer Driver 'wc' %I%",
2400Sstevel@tonic-gate 	&wc_ops,	/* driver ops */
2410Sstevel@tonic-gate };
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate static struct modlinkage modlinkage = {
2440Sstevel@tonic-gate 	MODREV_1,
2450Sstevel@tonic-gate 	&modldrv,
2460Sstevel@tonic-gate 	NULL
2470Sstevel@tonic-gate };
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate int
2500Sstevel@tonic-gate _init(void)
2510Sstevel@tonic-gate {
2520Sstevel@tonic-gate 	return (mod_install(&modlinkage));
2530Sstevel@tonic-gate }
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate int
2560Sstevel@tonic-gate _fini(void)
2570Sstevel@tonic-gate {
2580Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
2590Sstevel@tonic-gate }
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate int
2620Sstevel@tonic-gate _info(struct modinfo *modinfop)
2630Sstevel@tonic-gate {
2640Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2650Sstevel@tonic-gate }
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate /*ARGSUSED*/
2680Sstevel@tonic-gate static int
2690Sstevel@tonic-gate wc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2700Sstevel@tonic-gate {
2710Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "wscons", S_IFCHR,
2720Sstevel@tonic-gate 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
2730Sstevel@tonic-gate 		ddi_remove_minor_node(devi, NULL);
2740Sstevel@tonic-gate 		return (-1);
2750Sstevel@tonic-gate 	}
2760Sstevel@tonic-gate 	wc_dip = devi;
2771253Slq150181 
2781253Slq150181 	bzero(&(wscons.wc_polledio), sizeof (wscons.wc_polledio));
2791253Slq150181 
2800Sstevel@tonic-gate 	return (DDI_SUCCESS);
2810Sstevel@tonic-gate }
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate /* ARGSUSED */
2840Sstevel@tonic-gate static int
2850Sstevel@tonic-gate wc_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
2860Sstevel@tonic-gate 	void **result)
2870Sstevel@tonic-gate {
2880Sstevel@tonic-gate 	int error;
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 	switch (infocmd) {
2910Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
2920Sstevel@tonic-gate 		if (wc_dip == NULL) {
2930Sstevel@tonic-gate 			error = DDI_FAILURE;
2940Sstevel@tonic-gate 		} else {
2950Sstevel@tonic-gate 			*result = (void *) wc_dip;
2960Sstevel@tonic-gate 			error = DDI_SUCCESS;
2970Sstevel@tonic-gate 		}
2980Sstevel@tonic-gate 		break;
2990Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
3000Sstevel@tonic-gate 		*result = (void *)0;
3010Sstevel@tonic-gate 		error = DDI_SUCCESS;
3020Sstevel@tonic-gate 		break;
3030Sstevel@tonic-gate 	default:
3040Sstevel@tonic-gate 		error = DDI_FAILURE;
3050Sstevel@tonic-gate 	}
3060Sstevel@tonic-gate 	return (error);
3070Sstevel@tonic-gate }
3080Sstevel@tonic-gate 
3091253Slq150181 #ifdef _HAVE_TEM_FIRMWARE
3100Sstevel@tonic-gate /*
3110Sstevel@tonic-gate  * Output buffer. Protected by the per-module inner perimeter.
3120Sstevel@tonic-gate  */
3130Sstevel@tonic-gate #define	MAXHIWAT	2000
3140Sstevel@tonic-gate static char obuf[MAXHIWAT];
3151253Slq150181 #endif /* _HAVE_TEM_FIRMWARE */
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate /*ARGSUSED*/
3180Sstevel@tonic-gate static int
3190Sstevel@tonic-gate wcopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
3200Sstevel@tonic-gate {
3211253Slq150181 	static boolean_t polledio_inited = B_FALSE;
3220Sstevel@tonic-gate 	struct termios *termiosp;
3230Sstevel@tonic-gate 	int len;
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	if (getminor(*devp) != 0)
3260Sstevel@tonic-gate 		return (ENXIO);		/* sorry, only one per customer */
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	if (!(wscons.wc_flags & WCS_ISOPEN)) {
3290Sstevel@tonic-gate 		mutex_init(&wscons.wc_ttycommon.t_excl, NULL, MUTEX_DEFAULT,
3300Sstevel@tonic-gate 		    NULL);
3310Sstevel@tonic-gate 		wscons.wc_ttycommon.t_iflag = 0;
3320Sstevel@tonic-gate 		/*
3330Sstevel@tonic-gate 		 * Get the default termios settings (cflag).
3340Sstevel@tonic-gate 		 * These are stored as a property in the
3350Sstevel@tonic-gate 		 * "options" node.
3360Sstevel@tonic-gate 		 */
3370Sstevel@tonic-gate 		if (ddi_getlongprop(DDI_DEV_T_ANY,
3380Sstevel@tonic-gate 		    ddi_root_node(), 0, "ttymodes",
3390Sstevel@tonic-gate 		    (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS &&
3400Sstevel@tonic-gate 		    len == sizeof (struct termios)) {
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 			wscons.wc_ttycommon.t_cflag = termiosp->c_cflag;
3430Sstevel@tonic-gate 			kmem_free(termiosp, len);
3440Sstevel@tonic-gate 		} else {
3450Sstevel@tonic-gate 			/*
3460Sstevel@tonic-gate 			 * Gack!  Whine about it.
3470Sstevel@tonic-gate 			 */
3480Sstevel@tonic-gate 			cmn_err(CE_WARN,
3490Sstevel@tonic-gate 			    "wc: Couldn't get ttymodes property!\n");
3500Sstevel@tonic-gate 		}
3510Sstevel@tonic-gate 		wscons.wc_ttycommon.t_iocpending = NULL;
3520Sstevel@tonic-gate 		wscons.wc_flags = WCS_ISOPEN;
3530Sstevel@tonic-gate 
3541253Slq150181 		wscons.wc_dev = *devp;
3550Sstevel@tonic-gate 		wc_get_size(&wscons);
3560Sstevel@tonic-gate 
3571253Slq150181 		if (!polledio_inited) {
3581253Slq150181 			polledio_inited = B_TRUE;
3590Sstevel@tonic-gate 
3601253Slq150181 			/*
3611253Slq150181 			 * Initialize the parts of the polled I/O struct that
3621253Slq150181 			 * are common to both input and output modes, but which
3631253Slq150181 			 * don't flag to the upper layers, which if any of the
3641253Slq150181 			 * two modes are available.  We don't know at this point
3651253Slq150181 			 * if system is configured CONS_KFB, but we will when
3661253Slq150181 			 * consconfig_dacf asks us with CONSOPENPOLLED I/O.
3671253Slq150181 			 */
3681253Slq150181 			wscons.wc_polledio.cons_polledio_version =
3691253Slq150181 				CONSPOLLEDIO_V0;
3701253Slq150181 			wscons.wc_polledio.cons_polledio_argument =
371*1762Slt200341 				(cons_polledio_arg_t)&wscons;
3721253Slq150181 			wscons.wc_polledio.cons_polledio_enter =
3731253Slq150181 				wc_polled_enter;
3741253Slq150181 			wscons.wc_polledio.cons_polledio_exit =
3751253Slq150181 				wc_polled_exit;
3761253Slq150181 
3771253Slq150181 #ifdef _HAVE_TEM_FIRMWARE
3781253Slq150181 			/*
3791253Slq150181 			 * If we're talking directly to a framebuffer, we assume
3801253Slq150181 			 * that it's a "slow" device, so that rendering should
3811253Slq150181 			 * be deferred to a timeout or softcall so that we write
3821253Slq150181 			 * a bunch of characters at once.
3831253Slq150181 			 */
3841253Slq150181 			wscons.wc_defer_output = prom_stdout_is_framebuffer();
3851253Slq150181 #endif /* _HAVE_TEM_FIRMWARE */
3861253Slq150181 		}
3870Sstevel@tonic-gate 	}
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 	if (wscons.wc_ttycommon.t_flags & TS_XCLUDE) {
3900Sstevel@tonic-gate 		if (secpolicy_excl_open(crp) != 0) {
3910Sstevel@tonic-gate 			return (EBUSY);
3920Sstevel@tonic-gate 		}
3930Sstevel@tonic-gate 	}
3940Sstevel@tonic-gate 	wscons.wc_ttycommon.t_readq = q;
3950Sstevel@tonic-gate 	wscons.wc_ttycommon.t_writeq = WR(q);
3960Sstevel@tonic-gate 	qprocson(q);
3970Sstevel@tonic-gate 	return (0);
3980Sstevel@tonic-gate }
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate /*ARGSUSED*/
4010Sstevel@tonic-gate static int
4020Sstevel@tonic-gate wcclose(queue_t *q, int flag, cred_t *crp)
4030Sstevel@tonic-gate {
4040Sstevel@tonic-gate 	qprocsoff(q);
4050Sstevel@tonic-gate 	if (wscons.wc_bufcallid != 0) {
4060Sstevel@tonic-gate 		qunbufcall(q, wscons.wc_bufcallid);
4070Sstevel@tonic-gate 		wscons.wc_bufcallid = 0;
4080Sstevel@tonic-gate 	}
4090Sstevel@tonic-gate 	if (wscons.wc_timeoutid != 0) {
4100Sstevel@tonic-gate 		(void) quntimeout(q, wscons.wc_timeoutid);
4110Sstevel@tonic-gate 		wscons.wc_timeoutid = 0;
4120Sstevel@tonic-gate 	}
4130Sstevel@tonic-gate 	ttycommon_close(&wscons.wc_ttycommon);
4140Sstevel@tonic-gate 	wscons.wc_flags = 0;
4150Sstevel@tonic-gate 	return (0);
4160Sstevel@tonic-gate }
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate /*
4190Sstevel@tonic-gate  * Put procedure for upper write queue.
4200Sstevel@tonic-gate  * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
4210Sstevel@tonic-gate  * queue up M_BREAK, M_DELAY, and M_DATA messages for processing by
4220Sstevel@tonic-gate  * the start routine, and then call the start routine; discard
4230Sstevel@tonic-gate  * everything else.
4240Sstevel@tonic-gate  */
4250Sstevel@tonic-gate static int
4260Sstevel@tonic-gate wcuwput(queue_t *q, mblk_t *mp)
4270Sstevel@tonic-gate {
4280Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	case M_STOP:
4310Sstevel@tonic-gate 		wscons.wc_flags |= WCS_STOPPED;
4320Sstevel@tonic-gate 		freemsg(mp);
4330Sstevel@tonic-gate 		break;
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 	case M_START:
4360Sstevel@tonic-gate 		wscons.wc_flags &= ~WCS_STOPPED;
4370Sstevel@tonic-gate 		wcstart();
4380Sstevel@tonic-gate 		freemsg(mp);
4390Sstevel@tonic-gate 		break;
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate 	case M_IOCTL: {
4420Sstevel@tonic-gate 		struct iocblk *iocp;
4430Sstevel@tonic-gate 		struct linkblk *linkp;
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 		iocp = (struct iocblk *)mp->b_rptr;
4460Sstevel@tonic-gate 		switch (iocp->ioc_cmd) {
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 		case I_LINK:	/* stupid, but permitted */
4490Sstevel@tonic-gate 		case I_PLINK:
4500Sstevel@tonic-gate 			if (wscons.wc_kbdqueue != NULL) {
4510Sstevel@tonic-gate 				/* somebody already linked */
4520Sstevel@tonic-gate 				miocnak(q, mp, 0, EINVAL);
4530Sstevel@tonic-gate 				return (0);
4540Sstevel@tonic-gate 			}
4550Sstevel@tonic-gate 			linkp = (struct linkblk *)mp->b_cont->b_rptr;
4560Sstevel@tonic-gate 			wscons.wc_kbdqueue = WR(linkp->l_qbot);
4570Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCACK;
4580Sstevel@tonic-gate 			iocp->ioc_count = 0;
4590Sstevel@tonic-gate 			wc_open_kb_polledio(&wscons, q, mp);
4600Sstevel@tonic-gate 			break;
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 		case I_UNLINK:	/* stupid, but permitted */
4630Sstevel@tonic-gate 		case I_PUNLINK:
4640Sstevel@tonic-gate 			linkp = (struct linkblk *)mp->b_cont->b_rptr;
4650Sstevel@tonic-gate 			if (wscons.wc_kbdqueue != WR(linkp->l_qbot)) {
4660Sstevel@tonic-gate 				/* not us */
4670Sstevel@tonic-gate 				miocnak(q, mp, 0, EINVAL);
4680Sstevel@tonic-gate 				return (0);
4690Sstevel@tonic-gate 			}
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCACK;
4720Sstevel@tonic-gate 			iocp->ioc_count = 0;
4730Sstevel@tonic-gate 			wc_close_kb_polledio(&wscons, q, mp);
4740Sstevel@tonic-gate 			break;
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate 		case TCSETSW:
4770Sstevel@tonic-gate 		case TCSETSF:
4780Sstevel@tonic-gate 		case TCSETAW:
4790Sstevel@tonic-gate 		case TCSETAF:
4800Sstevel@tonic-gate 		case TCSBRK:
4810Sstevel@tonic-gate 			/*
4820Sstevel@tonic-gate 			 * The changes do not take effect until all
4830Sstevel@tonic-gate 			 * output queued before them is drained.
4840Sstevel@tonic-gate 			 * Put this message on the queue, so that
4850Sstevel@tonic-gate 			 * "wcstart" will see it when it's done
4860Sstevel@tonic-gate 			 * with the output before it.  Poke the
4870Sstevel@tonic-gate 			 * start routine, just in case.
4880Sstevel@tonic-gate 			 */
4890Sstevel@tonic-gate 			(void) putq(q, mp);
4900Sstevel@tonic-gate 			wcstart();
4910Sstevel@tonic-gate 			break;
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 		case CONSSETABORTENABLE:
4940Sstevel@tonic-gate 		case CONSGETABORTENABLE:
4950Sstevel@tonic-gate 		case KIOCSDIRECT:
4960Sstevel@tonic-gate 			if (wscons.wc_kbdqueue != NULL) {
4970Sstevel@tonic-gate 				(void) putnext(wscons.wc_kbdqueue, mp);
4980Sstevel@tonic-gate 				break;
4990Sstevel@tonic-gate 			}
5000Sstevel@tonic-gate 			/* fall through */
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 		default:
5030Sstevel@tonic-gate 			/*
5040Sstevel@tonic-gate 			 * Do it now.
5050Sstevel@tonic-gate 			 */
5060Sstevel@tonic-gate 			wcioctl(q, mp);
5070Sstevel@tonic-gate 			break;
5080Sstevel@tonic-gate 		}
5090Sstevel@tonic-gate 		break;
5100Sstevel@tonic-gate 	}
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 	case M_FLUSH:
5130Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW) {
5140Sstevel@tonic-gate 			/*
5150Sstevel@tonic-gate 			 * Flush our write queue.
5160Sstevel@tonic-gate 			 */
5170Sstevel@tonic-gate 			flushq(q, FLUSHDATA);	/* XXX doesn't flush M_DELAY */
5180Sstevel@tonic-gate 			*mp->b_rptr &= ~FLUSHW;	/* it has been flushed */
5190Sstevel@tonic-gate 		}
5200Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHR) {
5210Sstevel@tonic-gate 			flushq(RD(q), FLUSHDATA);
5220Sstevel@tonic-gate 			qreply(q, mp);	/* give the read queues a crack at it */
5230Sstevel@tonic-gate 		} else
5240Sstevel@tonic-gate 			freemsg(mp);
5250Sstevel@tonic-gate 		break;
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 	case M_BREAK:
5280Sstevel@tonic-gate 		/*
5290Sstevel@tonic-gate 		 * Ignore these, as they make no sense.
5300Sstevel@tonic-gate 		 */
5310Sstevel@tonic-gate 		freemsg(mp);
5320Sstevel@tonic-gate 		break;
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	case M_DELAY:
5350Sstevel@tonic-gate 	case M_DATA:
5360Sstevel@tonic-gate 		/*
5370Sstevel@tonic-gate 		 * Queue the message up to be transmitted,
5380Sstevel@tonic-gate 		 * and poke the start routine.
5390Sstevel@tonic-gate 		 */
5400Sstevel@tonic-gate 		(void) putq(q, mp);
5410Sstevel@tonic-gate 		wcstart();
5420Sstevel@tonic-gate 		break;
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 	default:
5450Sstevel@tonic-gate 		/*
5460Sstevel@tonic-gate 		 * "No, I don't want a subscription to Chain Store Age,
5470Sstevel@tonic-gate 		 * thank you anyway."
5480Sstevel@tonic-gate 		 */
5490Sstevel@tonic-gate 		freemsg(mp);
5500Sstevel@tonic-gate 		break;
5510Sstevel@tonic-gate 	}
5520Sstevel@tonic-gate 
5530Sstevel@tonic-gate 	return (0);
5540Sstevel@tonic-gate }
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate /*
5570Sstevel@tonic-gate  * Retry an "ioctl", now that "qbufcall" claims we may be able to allocate
5580Sstevel@tonic-gate  * the buffer we need.
5590Sstevel@tonic-gate  */
5600Sstevel@tonic-gate /*ARGSUSED*/
5610Sstevel@tonic-gate static void
5620Sstevel@tonic-gate wcreioctl(void *arg)
5630Sstevel@tonic-gate {
5640Sstevel@tonic-gate 	queue_t *q;
5650Sstevel@tonic-gate 	mblk_t *mp;
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 	wscons.wc_bufcallid = 0;
5680Sstevel@tonic-gate 	q = wscons.wc_ttycommon.t_writeq;
5690Sstevel@tonic-gate 	if ((mp = wscons.wc_ttycommon.t_iocpending) != NULL) {
5700Sstevel@tonic-gate 		/* not pending any more */
5710Sstevel@tonic-gate 		wscons.wc_ttycommon.t_iocpending = NULL;
5720Sstevel@tonic-gate 		wcioctl(q, mp);
5730Sstevel@tonic-gate 	}
5740Sstevel@tonic-gate }
5750Sstevel@tonic-gate 
5761253Slq150181 static int
5771253Slq150181 wc_getterm(mblk_t *mp)
5781253Slq150181 {
5791253Slq150181 	char *term;
5801253Slq150181 	intptr_t arg;
5811253Slq150181 	int flag = ((struct iocblk *)mp->b_rptr)->ioc_flag;
5821253Slq150181 
5831253Slq150181 	STRUCT_DECL(cons_getterm, wcterm);
5841253Slq150181 	STRUCT_INIT(wcterm, flag);
5851253Slq150181 
5861253Slq150181 	arg = *((intptr_t *)mp->b_cont->b_rptr);
5871253Slq150181 
5881253Slq150181 	if (ddi_copyin((void *)arg, STRUCT_BUF(wcterm),
5891253Slq150181 	    STRUCT_SIZE(wcterm), flag) != 0) {
5901253Slq150181 		return (EFAULT);
5911253Slq150181 	}
5921253Slq150181 
5931253Slq150181 	if (consmode == CONS_FW) {
5941253Slq150181 		/* PROM terminal emulator */
5951253Slq150181 		term = "sun";
5961253Slq150181 	} else {
5971253Slq150181 		/* Kernel terminal emulator */
5981253Slq150181 		ASSERT(consmode == CONS_KFB);
5991253Slq150181 		term = "sun-color";
6001253Slq150181 	}
6011253Slq150181 
6021253Slq150181 	if (STRUCT_FGET(wcterm, cn_term_len) <
6031253Slq150181 	    strlen(term) + 1) {
6041253Slq150181 		return (EOVERFLOW);
6051253Slq150181 	}
6061253Slq150181 
6071253Slq150181 	if (ddi_copyout(term,
6081253Slq150181 	    STRUCT_FGETP(wcterm, cn_term_type),
6091253Slq150181 	    strlen(term) + 1, flag) != 0) {
6101253Slq150181 		return (EFAULT);
6111253Slq150181 	}
6121253Slq150181 
6131253Slq150181 	return (0);
6141253Slq150181 }
6151253Slq150181 
6160Sstevel@tonic-gate /*
6170Sstevel@tonic-gate  * Process an "ioctl" message sent down to us.
6180Sstevel@tonic-gate  */
6190Sstevel@tonic-gate static void
6200Sstevel@tonic-gate wcioctl(queue_t *q, mblk_t *mp)
6210Sstevel@tonic-gate {
6220Sstevel@tonic-gate 	struct iocblk *iocp;
6230Sstevel@tonic-gate 	size_t datasize;
6240Sstevel@tonic-gate 	int error;
6251253Slq150181 	long len;
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 	switch (iocp->ioc_cmd) {
6300Sstevel@tonic-gate 	case TIOCSWINSZ:
6310Sstevel@tonic-gate 		/*
6320Sstevel@tonic-gate 		 * Ignore all attempts to set the screen size; the
6330Sstevel@tonic-gate 		 * value in the EEPROM is guaranteed (modulo PROM bugs)
6340Sstevel@tonic-gate 		 * to be the value used by the PROM monitor code, so it
6350Sstevel@tonic-gate 		 * is by definition correct.  Many programs (e.g.,
6360Sstevel@tonic-gate 		 * "login" and "tset") will attempt to reset the size
6370Sstevel@tonic-gate 		 * to (0, 0) or (34, 80), neither of which is
6380Sstevel@tonic-gate 		 * necessarily correct.
6390Sstevel@tonic-gate 		 * We just ACK the message, so as not to disturb
6400Sstevel@tonic-gate 		 * programs that set the sizes.
6410Sstevel@tonic-gate 		 */
6420Sstevel@tonic-gate 		iocp->ioc_count = 0;	/* no data returned */
6430Sstevel@tonic-gate 		mp->b_datap->db_type = M_IOCACK;
6440Sstevel@tonic-gate 		qreply(q, mp);
6450Sstevel@tonic-gate 		return;
6460Sstevel@tonic-gate 
6470Sstevel@tonic-gate 	case CONSOPENPOLLEDIO:
6480Sstevel@tonic-gate 		DPRINTF(PRINT_L1, PRINT_MASK_ALL,
6490Sstevel@tonic-gate 			("wcioctl: CONSOPENPOLLEDIO\n"));
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 		error = miocpullup(mp, sizeof (struct cons_polledio *));
6520Sstevel@tonic-gate 		if (error != 0) {
6530Sstevel@tonic-gate 			miocnak(q, mp, 0, error);
6540Sstevel@tonic-gate 			return;
6550Sstevel@tonic-gate 		}
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 		/*
6581253Slq150181 		 * We are given an appropriate-sized data block,
6591253Slq150181 		 * and return a pointer to our structure in it.
6600Sstevel@tonic-gate 		 */
6611253Slq150181 		if (consmode == CONS_KFB)
6621253Slq150181 			wscons.wc_polledio.cons_polledio_putchar =
6631253Slq150181 			    wc_polled_putchar;
6641253Slq150181 		*(struct cons_polledio **)mp->b_cont->b_rptr =
6651253Slq150181 			&wscons.wc_polledio;
6660Sstevel@tonic-gate 
6671253Slq150181 		mp->b_datap->db_type = M_IOCACK;
6680Sstevel@tonic-gate 
6690Sstevel@tonic-gate 		qreply(q, mp);
6700Sstevel@tonic-gate 		break;
6710Sstevel@tonic-gate 
6721253Slq150181 	case CONS_GETTERM:
6731253Slq150181 		if ((error = wc_getterm(mp)) != 0)
6741253Slq150181 			miocnak(q, mp, 0, error);
6751253Slq150181 		else
6761253Slq150181 			miocack(q, mp, 0, 0);
6771253Slq150181 		return;
6781253Slq150181 
6790Sstevel@tonic-gate 	case WC_OPEN_FB:
6800Sstevel@tonic-gate 		/*
6810Sstevel@tonic-gate 		 * Start out pessimistic, so that we can just jump to
6820Sstevel@tonic-gate 		 * the reply to bail out.
6830Sstevel@tonic-gate 		 */
6840Sstevel@tonic-gate 		mp->b_datap->db_type = M_IOCNAK;
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 		/*
6870Sstevel@tonic-gate 		 * First test:  really, this should be done only from
6880Sstevel@tonic-gate 		 * inside the kernel.  Unfortunately, that information
6890Sstevel@tonic-gate 		 * doesn't seem to be available in a streams ioctl,
6900Sstevel@tonic-gate 		 * so restrict it to root only.  (Perhaps we could check
6910Sstevel@tonic-gate 		 * for ioc_cr == kcred.)
6920Sstevel@tonic-gate 		 */
6930Sstevel@tonic-gate 		if ((iocp->ioc_error = secpolicy_console(iocp->ioc_cr)) != 0)
6940Sstevel@tonic-gate 			goto open_fail;
6950Sstevel@tonic-gate 
6960Sstevel@tonic-gate 		/*
6970Sstevel@tonic-gate 		 * Some miscellaneous checks...
6980Sstevel@tonic-gate 		 */
6990Sstevel@tonic-gate 		iocp->ioc_error = EINVAL;
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 		/*
7020Sstevel@tonic-gate 		 * If we're already open, fail.
7030Sstevel@tonic-gate 		 */
7040Sstevel@tonic-gate 		if (wscons.wc_tem != NULL)
7050Sstevel@tonic-gate 			goto open_fail;
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 		/*
7080Sstevel@tonic-gate 		 * If we don't have exactly one continuation block, fail.
7090Sstevel@tonic-gate 		 */
7100Sstevel@tonic-gate 		if (mp->b_cont == NULL ||
7110Sstevel@tonic-gate 		    mp->b_cont->b_cont != NULL)
7120Sstevel@tonic-gate 			goto open_fail;
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate 		/*
7150Sstevel@tonic-gate 		 * If there's no null terminator in the string, fail.
7160Sstevel@tonic-gate 		 */
7170Sstevel@tonic-gate 		len = mp->b_cont->b_wptr - mp->b_cont->b_rptr;
7180Sstevel@tonic-gate 		if (memchr(mp->b_cont->b_rptr, 0, len) == NULL)
7190Sstevel@tonic-gate 			goto open_fail;
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate 		/*
7220Sstevel@tonic-gate 		 * NOTE:  should eventually get default
7230Sstevel@tonic-gate 		 * dimensions from a property, e.g. screen-#rows.
7240Sstevel@tonic-gate 		 */
7250Sstevel@tonic-gate 		iocp->ioc_error = tem_init(&wscons.wc_tem,
7261253Slq150181 			(char *)mp->b_cont->b_rptr, iocp->ioc_cr);
7270Sstevel@tonic-gate 		/*
7280Sstevel@tonic-gate 		 * Of course, if the terminal emulator initialization
7290Sstevel@tonic-gate 		 * failed, fail.
7300Sstevel@tonic-gate 		 */
7310Sstevel@tonic-gate 		if (iocp->ioc_error != 0)
7320Sstevel@tonic-gate 			goto open_fail;
7330Sstevel@tonic-gate 
7341253Slq150181 		tem_register_modechg_cb(wscons.wc_tem, wc_modechg_cb,
7351253Slq150181 			(tem_modechg_cb_arg_t)&wscons);
7361253Slq150181 
7370Sstevel@tonic-gate 		/*
7380Sstevel@tonic-gate 		 * Refresh terminal size with info from terminal emulator.
7390Sstevel@tonic-gate 		 */
7400Sstevel@tonic-gate 		wc_get_size(&wscons);
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate 		/*
7430Sstevel@tonic-gate 		 * ... and succeed.
7440Sstevel@tonic-gate 		 */
7450Sstevel@tonic-gate 		mp->b_datap->db_type = M_IOCACK;
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate open_fail:
7480Sstevel@tonic-gate 		qreply(q, mp);
7490Sstevel@tonic-gate 		break;
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 	case WC_CLOSE_FB:
7520Sstevel@tonic-gate 		/*
7530Sstevel@tonic-gate 		 * There's nothing that can call this, so it's not
7540Sstevel@tonic-gate 		 * really implemented.
7550Sstevel@tonic-gate 		 */
7560Sstevel@tonic-gate 		mp->b_datap->db_type = M_IOCNAK;
7570Sstevel@tonic-gate 		/*
7580Sstevel@tonic-gate 		 * However, if it were implemented, it would clearly
7590Sstevel@tonic-gate 		 * be root-only.
7600Sstevel@tonic-gate 		 */
7610Sstevel@tonic-gate 		if ((iocp->ioc_error = secpolicy_console(iocp->ioc_cr)) != 0)
7620Sstevel@tonic-gate 			goto close_fail;
7630Sstevel@tonic-gate 
7640Sstevel@tonic-gate 		iocp->ioc_error = EINVAL;
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate close_fail:
7670Sstevel@tonic-gate 		qreply(q, mp);
7680Sstevel@tonic-gate 		break;
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 	default:
7710Sstevel@tonic-gate 
7720Sstevel@tonic-gate 		/*
7730Sstevel@tonic-gate 		 * The only way in which "ttycommon_ioctl" can fail is
7740Sstevel@tonic-gate 		 * if the "ioctl" requires a response containing data
7750Sstevel@tonic-gate 		 * to be returned to the user, and no mblk could be
7760Sstevel@tonic-gate 		 * allocated for the data.  No such "ioctl" alters our
7770Sstevel@tonic-gate 		 * state.  Thus, we always go ahead and do any
7780Sstevel@tonic-gate 		 * state-changes the "ioctl" calls for.  If we couldn't
7790Sstevel@tonic-gate 		 * allocate the data, "ttycommon_ioctl" has stashed the
7800Sstevel@tonic-gate 		 * "ioctl" away safely, so we just call "qbufcall" to
7810Sstevel@tonic-gate 		 * request that we be called back when we stand a
7820Sstevel@tonic-gate 		 * better chance of allocating the data.
7830Sstevel@tonic-gate 		 */
7840Sstevel@tonic-gate 		datasize = ttycommon_ioctl(&wscons.wc_ttycommon, q, mp, &error);
7850Sstevel@tonic-gate 		if (datasize != 0) {
7860Sstevel@tonic-gate 			if (wscons.wc_bufcallid != 0)
7870Sstevel@tonic-gate 				qunbufcall(q, wscons.wc_bufcallid);
7880Sstevel@tonic-gate 			wscons.wc_bufcallid = qbufcall(q, datasize, BPRI_HI,
7890Sstevel@tonic-gate 			    wcreioctl, NULL);
7900Sstevel@tonic-gate 			return;
7910Sstevel@tonic-gate 		}
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 		if (error < 0) {
7940Sstevel@tonic-gate 			if (iocp->ioc_cmd == TCSBRK)
7950Sstevel@tonic-gate 				error = 0;
7960Sstevel@tonic-gate 			else
7970Sstevel@tonic-gate 				error = EINVAL;
7980Sstevel@tonic-gate 		}
7990Sstevel@tonic-gate 		if (error != 0) {
8000Sstevel@tonic-gate 			iocp->ioc_error = error;
8010Sstevel@tonic-gate 			mp->b_datap->db_type = M_IOCNAK;
8020Sstevel@tonic-gate 		}
8030Sstevel@tonic-gate 		qreply(q, mp);
8040Sstevel@tonic-gate 		break;
8050Sstevel@tonic-gate 	}
8060Sstevel@tonic-gate }
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate /*
8090Sstevel@tonic-gate  * This function gets the polled I/O structures from the lower
8100Sstevel@tonic-gate  * keyboard driver.  If any initialization or resource allocation
8110Sstevel@tonic-gate  * needs to be done by the lower driver, it will be done when
8120Sstevel@tonic-gate  * the lower driver services this message.
8130Sstevel@tonic-gate  */
8140Sstevel@tonic-gate static void
8150Sstevel@tonic-gate wc_open_kb_polledio(struct wscons *wscons, queue_t *q, mblk_t *mp)
8160Sstevel@tonic-gate {
8170Sstevel@tonic-gate 	mblk_t *mp2;
8180Sstevel@tonic-gate 	struct iocblk *iocp;
8190Sstevel@tonic-gate 
8200Sstevel@tonic-gate 	DPRINTF(PRINT_L1, PRINT_MASK_ALL,
8210Sstevel@tonic-gate 		("wc_open_kb_polledio: sending CONSOPENPOLLEDIO\n"));
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate 	mp2 = mkiocb(CONSOPENPOLLEDIO);
8240Sstevel@tonic-gate 
8250Sstevel@tonic-gate 	if (mp2 == NULL) {
8260Sstevel@tonic-gate 		/*
8270Sstevel@tonic-gate 		 * If we can't get an mblk, then wait for it.
8280Sstevel@tonic-gate 		 */
8290Sstevel@tonic-gate 		goto nomem;
8300Sstevel@tonic-gate 	}
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 	mp2->b_cont = allocb(sizeof (struct cons_polledio *), BPRI_HI);
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 	if (mp2->b_cont == NULL) {
8350Sstevel@tonic-gate 		/*
8360Sstevel@tonic-gate 		 * If we can't get an mblk, then wait for it, and release
8370Sstevel@tonic-gate 		 * the mblk that we have already allocated.
8380Sstevel@tonic-gate 		 */
8390Sstevel@tonic-gate 		freemsg(mp2);
8400Sstevel@tonic-gate 		goto nomem;
8410Sstevel@tonic-gate 	}
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate 	iocp = (struct iocblk *)mp2->b_rptr;
8440Sstevel@tonic-gate 
8450Sstevel@tonic-gate 	iocp->ioc_count = sizeof (struct cons_polledio *);
8460Sstevel@tonic-gate 	mp2->b_cont->b_wptr = mp2->b_cont->b_rptr +
8470Sstevel@tonic-gate 		sizeof (struct cons_polledio *);
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	wscons->wc_pending_link = mp;
8500Sstevel@tonic-gate 	wscons->wc_kb_getpolledio_id = iocp->ioc_id;
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 	putnext(wscons->wc_kbdqueue, mp2);
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 	return;
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate nomem:
8570Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
8580Sstevel@tonic-gate 	iocp->ioc_error = ENOMEM;
8590Sstevel@tonic-gate 	mp->b_datap->db_type = M_IOCNAK;
8600Sstevel@tonic-gate 	qreply(q, mp);
8610Sstevel@tonic-gate }
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate /*
8640Sstevel@tonic-gate  * This function releases the polled I/O structures from the lower
8650Sstevel@tonic-gate  * keyboard driver.  If any de-initialization needs to be done, or
8660Sstevel@tonic-gate  * any resources need to be released, it will be done when the lower
8670Sstevel@tonic-gate  * driver services this message.
8680Sstevel@tonic-gate  */
8690Sstevel@tonic-gate static void
8700Sstevel@tonic-gate wc_close_kb_polledio(struct wscons *wscons, queue_t *q, mblk_t *mp)
8710Sstevel@tonic-gate {
8720Sstevel@tonic-gate 	mblk_t *mp2;
8730Sstevel@tonic-gate 	struct iocblk *iocp;
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 	DPRINTF(PRINT_L1, PRINT_MASK_ALL,
8760Sstevel@tonic-gate 		("wc_close_kb_polledio: sending CONSCLOSEPOLLEDIO\n"));
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	mp2 = mkiocb(CONSCLOSEPOLLEDIO);
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	if (mp2 == NULL) {
8810Sstevel@tonic-gate 		/*
8820Sstevel@tonic-gate 		 * If we can't get an mblk, then wait for it.
8830Sstevel@tonic-gate 		 */
8840Sstevel@tonic-gate 		goto nomem;
8850Sstevel@tonic-gate 	}
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate 	mp2->b_cont = allocb(sizeof (struct cons_polledio *), BPRI_HI);
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate 	if (mp2->b_cont == NULL) {
8900Sstevel@tonic-gate 		/*
8910Sstevel@tonic-gate 		 * If we can't get an mblk, then wait for it, and release
8920Sstevel@tonic-gate 		 * the mblk that we have already allocated.
8930Sstevel@tonic-gate 		 */
8940Sstevel@tonic-gate 		freemsg(mp2);
8950Sstevel@tonic-gate 
8960Sstevel@tonic-gate 		goto nomem;
8970Sstevel@tonic-gate 	}
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate 	iocp = (struct iocblk *)mp2->b_rptr;
9000Sstevel@tonic-gate 
9010Sstevel@tonic-gate 	iocp->ioc_count = 0;
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 	wscons->wc_pending_link = mp;
9040Sstevel@tonic-gate 	wscons->wc_kb_getpolledio_id = iocp->ioc_id;
9050Sstevel@tonic-gate 
9060Sstevel@tonic-gate 	putnext(wscons->wc_kbdqueue, mp2);
9070Sstevel@tonic-gate 
9080Sstevel@tonic-gate 	return;
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate nomem:
9110Sstevel@tonic-gate 	iocp = (struct iocblk *)mp->b_rptr;
9120Sstevel@tonic-gate 	iocp->ioc_error = ENOMEM;
9130Sstevel@tonic-gate 	mp->b_datap->db_type = M_IOCNAK;
9140Sstevel@tonic-gate 	qreply(q, mp);
9150Sstevel@tonic-gate }
9160Sstevel@tonic-gate 
9171253Slq150181 #ifdef _HAVE_TEM_FIRMWARE
9180Sstevel@tonic-gate /* ARGSUSED */
9190Sstevel@tonic-gate static void
9200Sstevel@tonic-gate wcopoll(void *arg)
9210Sstevel@tonic-gate {
9220Sstevel@tonic-gate 	queue_t *q;
9230Sstevel@tonic-gate 
9240Sstevel@tonic-gate 	q = wscons.wc_ttycommon.t_writeq;
9250Sstevel@tonic-gate 	wscons.wc_timeoutid = 0;
9260Sstevel@tonic-gate 	/* See if we can continue output */
9270Sstevel@tonic-gate 	if ((wscons.wc_flags & WCS_BUSY) && wscons.wc_pendc != -1) {
9280Sstevel@tonic-gate 		if (prom_mayput((char)wscons.wc_pendc) == 0) {
9290Sstevel@tonic-gate 			wscons.wc_pendc = -1;
9300Sstevel@tonic-gate 			wscons.wc_flags &= ~WCS_BUSY;
9310Sstevel@tonic-gate 			if (!(wscons.wc_flags&(WCS_DELAY|WCS_STOPPED)))
9320Sstevel@tonic-gate 				wcstart();
9330Sstevel@tonic-gate 		} else
9340Sstevel@tonic-gate 			wscons.wc_timeoutid = qtimeout(q, wcopoll, NULL, 1);
9350Sstevel@tonic-gate 	}
9360Sstevel@tonic-gate }
9371253Slq150181 #endif	/* _HAVE_TEM_FIRMWARE */
9380Sstevel@tonic-gate 
9390Sstevel@tonic-gate /*
9400Sstevel@tonic-gate  * Restart output on the console after a timeout.
9410Sstevel@tonic-gate  */
9420Sstevel@tonic-gate /* ARGSUSED */
9430Sstevel@tonic-gate static void
9440Sstevel@tonic-gate wcrstrt(void *arg)
9450Sstevel@tonic-gate {
9460Sstevel@tonic-gate 	ASSERT(wscons.wc_ttycommon.t_writeq != NULL);
9470Sstevel@tonic-gate 	wscons.wc_flags &= ~WCS_DELAY;
9480Sstevel@tonic-gate 	wcstart();
9490Sstevel@tonic-gate }
9500Sstevel@tonic-gate 
9510Sstevel@tonic-gate /*
9520Sstevel@tonic-gate  * Start console output
9530Sstevel@tonic-gate  */
9540Sstevel@tonic-gate static void
9550Sstevel@tonic-gate wcstart(void)
9560Sstevel@tonic-gate {
9571253Slq150181 #ifdef _HAVE_TEM_FIRMWARE
9580Sstevel@tonic-gate 	int c;
9590Sstevel@tonic-gate 	ssize_t cc;
9601253Slq150181 #endif /* _HAVE_TEM_FIRMWARE */
9610Sstevel@tonic-gate 	queue_t *q;
9620Sstevel@tonic-gate 	mblk_t *bp;
9630Sstevel@tonic-gate 	mblk_t *nbp;
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 	/*
9660Sstevel@tonic-gate 	 * If we're waiting for something to happen (delay timeout to
9670Sstevel@tonic-gate 	 * expire, current transmission to finish, output to be
9680Sstevel@tonic-gate 	 * restarted, output to finish draining), don't grab anything
9690Sstevel@tonic-gate 	 * new.
9700Sstevel@tonic-gate 	 */
9710Sstevel@tonic-gate 	if (wscons.wc_flags & (WCS_DELAY|WCS_BUSY|WCS_STOPPED))
9721253Slq150181 		return;
9730Sstevel@tonic-gate 
9740Sstevel@tonic-gate 	q = wscons.wc_ttycommon.t_writeq;
9750Sstevel@tonic-gate 	/*
9760Sstevel@tonic-gate 	 * assumes that we have been called by whoever holds the
9770Sstevel@tonic-gate 	 * exclusionary lock on the write-side queue (protects
9780Sstevel@tonic-gate 	 * wc_flags and wc_pendc).
9790Sstevel@tonic-gate 	 */
9800Sstevel@tonic-gate 	for (;;) {
9810Sstevel@tonic-gate 		if ((bp = getq(q)) == NULL)
9821253Slq150181 			return;	/* nothing to transmit */
9830Sstevel@tonic-gate 
9840Sstevel@tonic-gate 		/*
9850Sstevel@tonic-gate 		 * We have a new message to work on.
9860Sstevel@tonic-gate 		 * Check whether it's a delay or an ioctl (the latter
9870Sstevel@tonic-gate 		 * occurs if the ioctl in question was waiting for the output
9880Sstevel@tonic-gate 		 * to drain).  If it's one of those, process it immediately.
9890Sstevel@tonic-gate 		 */
9900Sstevel@tonic-gate 		switch (bp->b_datap->db_type) {
9910Sstevel@tonic-gate 
9920Sstevel@tonic-gate 		case M_DELAY:
9930Sstevel@tonic-gate 			/*
9940Sstevel@tonic-gate 			 * Arrange for "wcrstrt" to be called when the
9950Sstevel@tonic-gate 			 * delay expires; it will turn WCS_DELAY off,
9960Sstevel@tonic-gate 			 * and call "wcstart" to grab the next message.
9970Sstevel@tonic-gate 			 */
9980Sstevel@tonic-gate 			if (wscons.wc_timeoutid != 0)
9990Sstevel@tonic-gate 				(void) quntimeout(q, wscons.wc_timeoutid);
10000Sstevel@tonic-gate 			wscons.wc_timeoutid = qtimeout(q, wcrstrt, NULL,
10010Sstevel@tonic-gate 			    (clock_t)(*(unsigned char *)bp->b_rptr + 6));
10020Sstevel@tonic-gate 			wscons.wc_flags |= WCS_DELAY;
10030Sstevel@tonic-gate 			freemsg(bp);
10041253Slq150181 			return;	/* wait for this to finish */
10050Sstevel@tonic-gate 
10060Sstevel@tonic-gate 		case M_IOCTL:
10070Sstevel@tonic-gate 			/*
10080Sstevel@tonic-gate 			 * This ioctl was waiting for the output ahead of
10090Sstevel@tonic-gate 			 * it to drain; obviously, it has.  Do it, and
10100Sstevel@tonic-gate 			 * then grab the next message after it.
10110Sstevel@tonic-gate 			 */
10120Sstevel@tonic-gate 			wcioctl(q, bp);
10130Sstevel@tonic-gate 			continue;
10140Sstevel@tonic-gate 		}
10150Sstevel@tonic-gate 
10161253Slq150181 #ifdef _HAVE_TEM_FIRMWARE
10171253Slq150181 		if (consmode == CONS_KFB) {
10181253Slq150181 #endif /* _HAVE_TEM_FIRMWARE */
10191253Slq150181 			if (wscons.wc_tem != NULL) {
10201253Slq150181 				for (nbp = bp; nbp != NULL; nbp = nbp->b_cont) {
10211253Slq150181 					if (nbp->b_wptr > nbp->b_rptr) {
10221253Slq150181 						(void) tem_write(wscons.wc_tem,
10231253Slq150181 						    nbp->b_rptr,
10241253Slq150181 						    nbp->b_wptr - nbp->b_rptr,
10251253Slq150181 						    kcred);
10261253Slq150181 					}
10271253Slq150181 				}
10281253Slq150181 				freemsg(bp);
10291253Slq150181 			}
10301253Slq150181 #ifdef _HAVE_TEM_FIRMWARE
10311253Slq150181 			continue;
10320Sstevel@tonic-gate 		}
10331253Slq150181 
10341253Slq150181 		/* consmode = CONS_FW */
10350Sstevel@tonic-gate 		if ((cc = bp->b_wptr - bp->b_rptr) == 0) {
10360Sstevel@tonic-gate 			freemsg(bp);
10370Sstevel@tonic-gate 			continue;
10380Sstevel@tonic-gate 		}
10390Sstevel@tonic-gate 		/*
10400Sstevel@tonic-gate 		 * Direct output to the frame buffer if this device
10410Sstevel@tonic-gate 		 * is not the "hardware" console.
10420Sstevel@tonic-gate 		 */
10430Sstevel@tonic-gate 		if (wscons.wc_defer_output) {
10440Sstevel@tonic-gate 			/*
10450Sstevel@tonic-gate 			 * Never do output here;
10460Sstevel@tonic-gate 			 * it takes forever.
10470Sstevel@tonic-gate 			 */
10480Sstevel@tonic-gate 			wscons.wc_flags |= WCS_BUSY;
10490Sstevel@tonic-gate 			wscons.wc_pendc = -1;
10500Sstevel@tonic-gate 			(void) putbq(q, bp);
10511253Slq150181 			if (q->q_count > 128) { /* do it soon */
10520Sstevel@tonic-gate 				softcall(wconsout, NULL);
10531253Slq150181 			} else {	/* wait a bit */
10540Sstevel@tonic-gate 				if (wscons.wc_timeoutid != 0)
10550Sstevel@tonic-gate 					(void) quntimeout(q,
10560Sstevel@tonic-gate 					    wscons.wc_timeoutid);
10570Sstevel@tonic-gate 				wscons.wc_timeoutid = qtimeout(q, wconsout,
10580Sstevel@tonic-gate 				    NULL, hz / 30);
10590Sstevel@tonic-gate 			}
10601253Slq150181 			return;
10610Sstevel@tonic-gate 		}
10620Sstevel@tonic-gate 		for (;;) {
10630Sstevel@tonic-gate 			c = *bp->b_rptr++;
10640Sstevel@tonic-gate 			cc--;
10650Sstevel@tonic-gate 			if (prom_mayput((char)c) != 0) {
10660Sstevel@tonic-gate 				wscons.wc_flags |= WCS_BUSY;
10670Sstevel@tonic-gate 				wscons.wc_pendc = c;
10680Sstevel@tonic-gate 				if (wscons.wc_timeoutid != 0)
10690Sstevel@tonic-gate 					(void) quntimeout(q,
10700Sstevel@tonic-gate 					    wscons.wc_timeoutid);
10710Sstevel@tonic-gate 				wscons.wc_timeoutid = qtimeout(q, wcopoll,
10720Sstevel@tonic-gate 				    NULL, 1);
10730Sstevel@tonic-gate 				if (bp != NULL)
10741253Slq150181 					/* not done with this message yet */
10750Sstevel@tonic-gate 					(void) putbq(q, bp);
10761253Slq150181 				return;
10770Sstevel@tonic-gate 			}
10780Sstevel@tonic-gate 			while (cc <= 0) {
10790Sstevel@tonic-gate 				nbp = bp;
10800Sstevel@tonic-gate 				bp = bp->b_cont;
10810Sstevel@tonic-gate 				freeb(nbp);
10820Sstevel@tonic-gate 				if (bp == NULL)
10831253Slq150181 					return;
10840Sstevel@tonic-gate 				cc = bp->b_wptr - bp->b_rptr;
10850Sstevel@tonic-gate 			}
10860Sstevel@tonic-gate 		}
10871253Slq150181 #endif /* _HAVE_TEM_FIRMWARE */
10880Sstevel@tonic-gate 	}
10890Sstevel@tonic-gate }
10900Sstevel@tonic-gate 
10911253Slq150181 #ifdef _HAVE_TEM_FIRMWARE
10920Sstevel@tonic-gate /*
10930Sstevel@tonic-gate  * Output to frame buffer console.
10940Sstevel@tonic-gate  * It takes a long time to scroll.
10950Sstevel@tonic-gate  */
10960Sstevel@tonic-gate /* ARGSUSED */
10970Sstevel@tonic-gate static void
10980Sstevel@tonic-gate wconsout(void *dummy)
10990Sstevel@tonic-gate {
11000Sstevel@tonic-gate 	uchar_t *cp;
11010Sstevel@tonic-gate 	ssize_t cc;
11020Sstevel@tonic-gate 	queue_t *q;
11030Sstevel@tonic-gate 	mblk_t *bp;
11040Sstevel@tonic-gate 	mblk_t *nbp;
11050Sstevel@tonic-gate 	char *current_position;
11060Sstevel@tonic-gate 	ssize_t bytes_left;
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate 	if ((q = wscons.wc_ttycommon.t_writeq) == NULL) {
11090Sstevel@tonic-gate 		return;	/* not attached to a stream */
11100Sstevel@tonic-gate 	}
11110Sstevel@tonic-gate 
11120Sstevel@tonic-gate 	/*
11130Sstevel@tonic-gate 	 * Set up to copy up to MAXHIWAT bytes.
11140Sstevel@tonic-gate 	 */
11150Sstevel@tonic-gate 	current_position = &obuf[0];
11160Sstevel@tonic-gate 	bytes_left = MAXHIWAT;
11170Sstevel@tonic-gate 	while ((bp = getq(q)) != NULL) {
11180Sstevel@tonic-gate 		if (bp->b_datap->db_type == M_IOCTL) {
11190Sstevel@tonic-gate 			/*
11200Sstevel@tonic-gate 			 * This ioctl was waiting for the output ahead of
11210Sstevel@tonic-gate 			 * it to drain; obviously, it has.  Put it back
11220Sstevel@tonic-gate 			 * so that "wcstart" can handle it, and transmit
11230Sstevel@tonic-gate 			 * what we've got.
11240Sstevel@tonic-gate 			 */
11250Sstevel@tonic-gate 			(void) putbq(q, bp);
11260Sstevel@tonic-gate 			goto transmit;
11270Sstevel@tonic-gate 		}
11280Sstevel@tonic-gate 
11290Sstevel@tonic-gate 		do {
11300Sstevel@tonic-gate 			cp = bp->b_rptr;
11310Sstevel@tonic-gate 			cc = bp->b_wptr - cp;
11320Sstevel@tonic-gate 			while (cc != 0) {
11330Sstevel@tonic-gate 				if (bytes_left == 0) {
11340Sstevel@tonic-gate 					/*
11350Sstevel@tonic-gate 					 * Out of buffer space; put this
11360Sstevel@tonic-gate 					 * buffer back on the queue, and
11370Sstevel@tonic-gate 					 * transmit what we have.
11380Sstevel@tonic-gate 					 */
11390Sstevel@tonic-gate 					bp->b_rptr = cp;
11400Sstevel@tonic-gate 					(void) putbq(q, bp);
11410Sstevel@tonic-gate 					goto transmit;
11420Sstevel@tonic-gate 				}
11430Sstevel@tonic-gate 				*current_position++ = *cp++;
11440Sstevel@tonic-gate 				cc--;
11450Sstevel@tonic-gate 				bytes_left--;
11460Sstevel@tonic-gate 			}
11470Sstevel@tonic-gate 			nbp = bp;
11480Sstevel@tonic-gate 			bp = bp->b_cont;
11490Sstevel@tonic-gate 			freeb(nbp);
11500Sstevel@tonic-gate 		} while (bp != NULL);
11510Sstevel@tonic-gate 	}
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate transmit:
11540Sstevel@tonic-gate 	if ((cc = MAXHIWAT - bytes_left) != 0)
11550Sstevel@tonic-gate 		console_puts(obuf, cc);
11560Sstevel@tonic-gate 
11570Sstevel@tonic-gate 	wscons.wc_flags &= ~WCS_BUSY;
11580Sstevel@tonic-gate 	wcstart();
11590Sstevel@tonic-gate }
11601253Slq150181 #endif /* _HAVE_TEM_FIRMWARE */
11610Sstevel@tonic-gate 
11620Sstevel@tonic-gate /*
11630Sstevel@tonic-gate  * Put procedure for lower read queue.
11640Sstevel@tonic-gate  * Pass everything up to queue above "upper half".
11650Sstevel@tonic-gate  */
11660Sstevel@tonic-gate static int
11670Sstevel@tonic-gate wclrput(queue_t *q, mblk_t *mp)
11680Sstevel@tonic-gate {
11690Sstevel@tonic-gate 	queue_t *upq;
11700Sstevel@tonic-gate 	struct iocblk *iocp;
11710Sstevel@tonic-gate 
11720Sstevel@tonic-gate 	DPRINTF(PRINT_L1, PRINT_MASK_ALL,
11730Sstevel@tonic-gate 		("wclrput: wclrput type = 0x%x\n", mp->b_datap->db_type));
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate 	switch (mp->b_datap->db_type) {
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 	case M_FLUSH:
11780Sstevel@tonic-gate 		if (*mp->b_rptr == FLUSHW || *mp->b_rptr == FLUSHRW) {
11790Sstevel@tonic-gate 			/*
11800Sstevel@tonic-gate 			 * Flush our write queue.
11810Sstevel@tonic-gate 			 */
11820Sstevel@tonic-gate 			/* XXX doesn't flush M_DELAY */
11830Sstevel@tonic-gate 			flushq(WR(q), FLUSHDATA);
11840Sstevel@tonic-gate 			*mp->b_rptr = FLUSHR;	/* it has been flushed */
11850Sstevel@tonic-gate 		}
11860Sstevel@tonic-gate 		if (*mp->b_rptr == FLUSHR || *mp->b_rptr == FLUSHRW) {
11870Sstevel@tonic-gate 			flushq(q, FLUSHDATA);
11880Sstevel@tonic-gate 			*mp->b_rptr = FLUSHW;	/* it has been flushed */
11890Sstevel@tonic-gate 			qreply(q, mp);	/* give the read queues a crack at it */
11900Sstevel@tonic-gate 		} else
11910Sstevel@tonic-gate 			freemsg(mp);
11920Sstevel@tonic-gate 		break;
11930Sstevel@tonic-gate 
11940Sstevel@tonic-gate 	case M_DATA:
11950Sstevel@tonic-gate 		if ((upq = wscons.wc_ttycommon.t_readq) != NULL) {
11960Sstevel@tonic-gate 			if (!canput(upq->q_next)) {
11970Sstevel@tonic-gate 				ttycommon_qfull(&wscons.wc_ttycommon, upq);
11980Sstevel@tonic-gate 				wcstart();
11990Sstevel@tonic-gate 				freemsg(mp);
12000Sstevel@tonic-gate 			} else
12010Sstevel@tonic-gate 				putnext(upq, mp);
12020Sstevel@tonic-gate 		} else
12030Sstevel@tonic-gate 			freemsg(mp);
12040Sstevel@tonic-gate 		break;
12050Sstevel@tonic-gate 
12060Sstevel@tonic-gate 	case M_IOCACK:
12070Sstevel@tonic-gate 	case M_IOCNAK:
12080Sstevel@tonic-gate 		iocp = (struct iocblk *)mp->b_rptr;
12090Sstevel@tonic-gate 		if (wscons.wc_pending_link != NULL &&
12100Sstevel@tonic-gate 		    iocp->ioc_id == wscons.wc_kb_getpolledio_id) {
12110Sstevel@tonic-gate 			switch (mp->b_datap->db_type) {
12120Sstevel@tonic-gate 
12130Sstevel@tonic-gate 			case M_IOCACK:
12140Sstevel@tonic-gate 				switch (iocp->ioc_cmd) {
12150Sstevel@tonic-gate 
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 				case CONSOPENPOLLEDIO:
12180Sstevel@tonic-gate 					DPRINTF(PRINT_L1, PRINT_MASK_ALL,
12190Sstevel@tonic-gate 						("wclrput: "
12200Sstevel@tonic-gate 						"ACK CONSOPENPOLLEDIO\n"));
12210Sstevel@tonic-gate 					wscons.wc_kb_polledio =
12220Sstevel@tonic-gate 					    *(struct cons_polledio **)
12231253Slq150181 					    mp->b_cont->b_rptr;
12241253Slq150181 					wscons.wc_polledio.
12251253Slq150181 					    cons_polledio_getchar =
12261253Slq150181 						wc_polled_getchar;
12271253Slq150181 					wscons.wc_polledio.
12281253Slq150181 					    cons_polledio_ischar =
12291253Slq150181 						wc_polled_ischar;
12300Sstevel@tonic-gate 					break;
12310Sstevel@tonic-gate 
12320Sstevel@tonic-gate 				case CONSCLOSEPOLLEDIO:
12330Sstevel@tonic-gate 					DPRINTF(PRINT_L1, PRINT_MASK_ALL,
12340Sstevel@tonic-gate 						("wclrput: "
12350Sstevel@tonic-gate 						"ACK CONSCLOSEPOLLEDIO\n"));
12360Sstevel@tonic-gate 					wscons.wc_kb_polledio = NULL;
12370Sstevel@tonic-gate 					wscons.wc_kbdqueue = NULL;
12381253Slq150181 					wscons.wc_polledio.
12391253Slq150181 					    cons_polledio_getchar = NULL;
12401253Slq150181 					wscons.wc_polledio.
12411253Slq150181 					    cons_polledio_ischar = NULL;
12420Sstevel@tonic-gate 					break;
12430Sstevel@tonic-gate 				default:
12440Sstevel@tonic-gate 					DPRINTF(PRINT_L1, PRINT_MASK_ALL,
12450Sstevel@tonic-gate 						("wclrput: "
12460Sstevel@tonic-gate 						"ACK UNKNOWN\n"));
12470Sstevel@tonic-gate 				}
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate 				break;
12500Sstevel@tonic-gate 			case M_IOCNAK:
12510Sstevel@tonic-gate 				/*
12520Sstevel@tonic-gate 				 * Keyboard may or may not support polled I/O.
12530Sstevel@tonic-gate 				 * This ioctl may have been rejected because
12540Sstevel@tonic-gate 				 * we only have the wc->conskbd chain built,
12550Sstevel@tonic-gate 				 * and the keyboard driver has not been linked
12560Sstevel@tonic-gate 				 * underneath conskbd yet.
12570Sstevel@tonic-gate 				 */
12580Sstevel@tonic-gate 				DPRINTF(PRINT_L1, PRINT_MASK_ALL,
12590Sstevel@tonic-gate 					("wclrput: NAK\n"));
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate 				switch (iocp->ioc_cmd) {
12620Sstevel@tonic-gate 
12630Sstevel@tonic-gate 				case CONSCLOSEPOLLEDIO:
12640Sstevel@tonic-gate 					wscons.wc_kb_polledio = NULL;
12650Sstevel@tonic-gate 					wscons.wc_kbdqueue = NULL;
12661253Slq150181 					wscons.wc_polledio.
12671253Slq150181 					    cons_polledio_getchar = NULL;
12681253Slq150181 					wscons.wc_polledio.
12691253Slq150181 					    cons_polledio_ischar = NULL;
12700Sstevel@tonic-gate 					break;
12710Sstevel@tonic-gate 				}
12720Sstevel@tonic-gate 				break;
12730Sstevel@tonic-gate 			}
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate 			/*
12760Sstevel@tonic-gate 			 * Discard the response, replace it with the
12770Sstevel@tonic-gate 			 * pending response to the I_PLINK, then let it
12780Sstevel@tonic-gate 			 * flow upward.
12790Sstevel@tonic-gate 			 */
12800Sstevel@tonic-gate 			freemsg(mp);
12810Sstevel@tonic-gate 			mp = wscons.wc_pending_link;
12820Sstevel@tonic-gate 			wscons.wc_pending_link = NULL;
12830Sstevel@tonic-gate 			wscons.wc_kb_getpolledio_id = 0;
12840Sstevel@tonic-gate 		}
12850Sstevel@tonic-gate 		/* FALLTHROUGH */
12860Sstevel@tonic-gate 
12870Sstevel@tonic-gate 	default:	/* inc M_ERROR, M_HANGUP, M_IOCACK, M_IOCNAK, ... */
12880Sstevel@tonic-gate 		DPRINTF(PRINT_L1, PRINT_MASK_ALL,
12890Sstevel@tonic-gate 			("wclrput: Message DISCARDED\n"));
12900Sstevel@tonic-gate 		if ((upq = wscons.wc_ttycommon.t_readq) != NULL) {
12910Sstevel@tonic-gate 			putnext(upq, mp);
12920Sstevel@tonic-gate 		} else {
12930Sstevel@tonic-gate 			freemsg(mp);
12940Sstevel@tonic-gate 		}
12950Sstevel@tonic-gate 		break;
12960Sstevel@tonic-gate 	}
12970Sstevel@tonic-gate 
12980Sstevel@tonic-gate 	return (0);
12990Sstevel@tonic-gate }
13000Sstevel@tonic-gate 
13010Sstevel@tonic-gate /*
13020Sstevel@tonic-gate  * Auxiliary routines, for allowing the workstation console to be redirected.
13030Sstevel@tonic-gate  */
13040Sstevel@tonic-gate 
13050Sstevel@tonic-gate /*
13060Sstevel@tonic-gate  * Given a minor device number for a wscons instance, return a held vnode for
13070Sstevel@tonic-gate  * it.
13080Sstevel@tonic-gate  *
13090Sstevel@tonic-gate  * We currently support only one instance, for the "workstation console".
13100Sstevel@tonic-gate  */
13110Sstevel@tonic-gate int
13120Sstevel@tonic-gate wcvnget(minor_t unit, vnode_t **vpp)
13130Sstevel@tonic-gate {
13140Sstevel@tonic-gate 	if (unit != 0 || rwsconsvp == NULL)
13150Sstevel@tonic-gate 		return (ENXIO);
13160Sstevel@tonic-gate 
13170Sstevel@tonic-gate 	/*
13180Sstevel@tonic-gate 	 * rwsconsvp is already held, so we don't have to do it here.
13190Sstevel@tonic-gate 	 */
13200Sstevel@tonic-gate 	*vpp = rwsconsvp;
13210Sstevel@tonic-gate 	return (0);
13220Sstevel@tonic-gate }
13230Sstevel@tonic-gate 
13240Sstevel@tonic-gate /*
13250Sstevel@tonic-gate  * Release the vnode that wcvnget returned.
13260Sstevel@tonic-gate  */
13270Sstevel@tonic-gate /* ARGSUSED */
13280Sstevel@tonic-gate void
13290Sstevel@tonic-gate wcvnrele(minor_t unit, vnode_t *vp)
13300Sstevel@tonic-gate {
13310Sstevel@tonic-gate 	/*
13320Sstevel@tonic-gate 	 * Nothing to do, since we only support the workstation console
13330Sstevel@tonic-gate 	 * instance that's held throughout the system's lifetime.
13340Sstevel@tonic-gate 	 */
13350Sstevel@tonic-gate }
13360Sstevel@tonic-gate 
13370Sstevel@tonic-gate /*
13380Sstevel@tonic-gate  * The declaration and initialization of the wscons_srvnops has been
13390Sstevel@tonic-gate  * moved to space.c to allow "wc" to become a loadable module.
13400Sstevel@tonic-gate  */
13410Sstevel@tonic-gate 
13420Sstevel@tonic-gate /*
13431253Slq150181  * These are for systems without OBP, and for devices that cannot be
13441253Slq150181  * shared between Solaris and the OBP.
13450Sstevel@tonic-gate  */
13460Sstevel@tonic-gate 
13470Sstevel@tonic-gate static void
1348*1762Slt200341 wc_polled_putchar(cons_polledio_arg_t arg, unsigned char c)
13490Sstevel@tonic-gate {
13500Sstevel@tonic-gate 	if (c == '\n')
13511253Slq150181 		wc_polled_putchar(arg, '\r');
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate 	if (wscons.wc_tem == NULL) {
13540Sstevel@tonic-gate 		/*
13550Sstevel@tonic-gate 		 * We have no terminal emulator configured.  We have no
13560Sstevel@tonic-gate 		 * recourse but to drop the output on the floor.
13570Sstevel@tonic-gate 		 */
13580Sstevel@tonic-gate 		return;
13590Sstevel@tonic-gate 	}
13600Sstevel@tonic-gate 
13611253Slq150181 	tem_polled_write(wscons.wc_tem, &c, 1);
13620Sstevel@tonic-gate }
13630Sstevel@tonic-gate 
13640Sstevel@tonic-gate /*
13650Sstevel@tonic-gate  * These are for systems without OBP, and for devices that cannot be
13660Sstevel@tonic-gate  * shared between Solaris and the OBP.
13670Sstevel@tonic-gate  */
13680Sstevel@tonic-gate static int
1369*1762Slt200341 wc_polled_getchar(cons_polledio_arg_t arg)
13700Sstevel@tonic-gate {
13710Sstevel@tonic-gate 	struct wscons *wscons = (struct wscons *)arg;
13720Sstevel@tonic-gate 
13730Sstevel@tonic-gate 	if (wscons->wc_kb_polledio == NULL) {
13740Sstevel@tonic-gate 		prom_printf("wscons:  getchar with no keyboard support");
13750Sstevel@tonic-gate 		prom_printf("Halted...");
13760Sstevel@tonic-gate 		for (;;)
13770Sstevel@tonic-gate 			/* HANG FOREVER */;
13780Sstevel@tonic-gate 	}
13790Sstevel@tonic-gate 
13800Sstevel@tonic-gate 	return (wscons->wc_kb_polledio->cons_polledio_getchar(
13811253Slq150181 	    wscons->wc_kb_polledio->cons_polledio_argument));
13820Sstevel@tonic-gate }
13830Sstevel@tonic-gate 
13840Sstevel@tonic-gate static boolean_t
1385*1762Slt200341 wc_polled_ischar(cons_polledio_arg_t arg)
13860Sstevel@tonic-gate {
13870Sstevel@tonic-gate 	struct wscons *wscons = (struct wscons *)arg;
13880Sstevel@tonic-gate 
13890Sstevel@tonic-gate 	if (wscons->wc_kb_polledio == NULL)
13900Sstevel@tonic-gate 		return (B_FALSE);
13910Sstevel@tonic-gate 
13920Sstevel@tonic-gate 	return (wscons->wc_kb_polledio->cons_polledio_ischar(
13931253Slq150181 	    wscons->wc_kb_polledio->cons_polledio_argument));
13940Sstevel@tonic-gate }
13950Sstevel@tonic-gate 
13960Sstevel@tonic-gate static void
1397*1762Slt200341 wc_polled_enter(cons_polledio_arg_t arg)
13980Sstevel@tonic-gate {
13990Sstevel@tonic-gate 	struct wscons *wscons = (struct wscons *)arg;
14000Sstevel@tonic-gate 
14010Sstevel@tonic-gate 	if (wscons->wc_kb_polledio == NULL)
14020Sstevel@tonic-gate 		return;
14030Sstevel@tonic-gate 
14040Sstevel@tonic-gate 	if (wscons->wc_kb_polledio->cons_polledio_enter != NULL) {
14050Sstevel@tonic-gate 		wscons->wc_kb_polledio->cons_polledio_enter(
14061253Slq150181 		    wscons->wc_kb_polledio->cons_polledio_argument);
14070Sstevel@tonic-gate 	}
14080Sstevel@tonic-gate }
14090Sstevel@tonic-gate 
14100Sstevel@tonic-gate static void
1411*1762Slt200341 wc_polled_exit(cons_polledio_arg_t arg)
14120Sstevel@tonic-gate {
14130Sstevel@tonic-gate 	struct wscons *wscons = (struct wscons *)arg;
14140Sstevel@tonic-gate 
14150Sstevel@tonic-gate 	if (wscons->wc_kb_polledio == NULL)
14160Sstevel@tonic-gate 		return;
14170Sstevel@tonic-gate 
14180Sstevel@tonic-gate 	if (wscons->wc_kb_polledio->cons_polledio_exit != NULL) {
14190Sstevel@tonic-gate 		wscons->wc_kb_polledio->cons_polledio_exit(
14201253Slq150181 		    wscons->wc_kb_polledio->cons_polledio_argument);
14210Sstevel@tonic-gate 	}
14220Sstevel@tonic-gate }
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate 
14250Sstevel@tonic-gate #ifdef DEBUG
14260Sstevel@tonic-gate static void
14270Sstevel@tonic-gate wc_dprintf(const char *fmt, ...)
14280Sstevel@tonic-gate {
14290Sstevel@tonic-gate 	char buf[256];
14300Sstevel@tonic-gate 	va_list ap;
14310Sstevel@tonic-gate 
14320Sstevel@tonic-gate 	va_start(ap, fmt);
14330Sstevel@tonic-gate 	(void) vsprintf(buf, fmt, ap);
14340Sstevel@tonic-gate 	va_end(ap);
14350Sstevel@tonic-gate 
14361253Slq150181 	cmn_err(CE_WARN, "wc: %s", buf);
14370Sstevel@tonic-gate }
14380Sstevel@tonic-gate #endif
14390Sstevel@tonic-gate 
14401253Slq150181 static void
14411253Slq150181 update_property(struct wscons *wscons, char *name, ushort_t value)
14420Sstevel@tonic-gate {
14431253Slq150181 	char data[8];
14440Sstevel@tonic-gate 
14451253Slq150181 	(void) snprintf(data, sizeof (data), "%u", value);
14461253Slq150181 	(void) ddi_prop_update_string(wscons->wc_dev, wc_dip, name, data);
14470Sstevel@tonic-gate }
14480Sstevel@tonic-gate 
14490Sstevel@tonic-gate /*
14500Sstevel@tonic-gate  * Gets the number of text rows and columns and the
14510Sstevel@tonic-gate  * width and height (in pixels) of the console.
14520Sstevel@tonic-gate  */
14530Sstevel@tonic-gate static void
14540Sstevel@tonic-gate wc_get_size(struct wscons *wscons)
14550Sstevel@tonic-gate {
14561253Slq150181 	struct winsize *t = &wscons->wc_ttycommon.t_size;
14571253Slq150181 	ushort_t r = LOSCREENLINES, c = LOSCREENCOLS, x = 0, y = 0;
14580Sstevel@tonic-gate 
14591253Slq150181 	if (wscons->wc_tem != NULL)
14601253Slq150181 		tem_get_size(wscons->wc_tem, &r, &c, &x, &y);
14611253Slq150181 #ifdef _HAVE_TEM_FIRMWARE
14621253Slq150181 	else {
14631253Slq150181 		console_get_size(&r, &c, &x, &y);
14641253Slq150181 	}
14651253Slq150181 #endif /* _HAVE_TEM_FIRMWARE */
14660Sstevel@tonic-gate 
14671253Slq150181 	update_property(wscons, "screen-#cols",  t->ws_col = c);
14681253Slq150181 	update_property(wscons, "screen-#rows",  t->ws_row = r);
14691253Slq150181 	update_property(wscons, "screen-width",  t->ws_xpixel = x);
14701253Slq150181 	update_property(wscons, "screen-height", t->ws_ypixel = y);
14711253Slq150181 }
14720Sstevel@tonic-gate 
14731253Slq150181 static void
14741253Slq150181 wc_modechg_cb(tem_modechg_cb_arg_t arg)
14751253Slq150181 {
14761253Slq150181 	wc_get_size((struct wscons *)arg);
14770Sstevel@tonic-gate }
1478