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