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