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