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 78 struct 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 103 static int wcopen(queue_t *, dev_t *, int, int, cred_t *); 104 static int wcclose(queue_t *, int, cred_t *); 105 static int wcuwput(queue_t *, mblk_t *); 106 static int wclrput(queue_t *, mblk_t *); 107 108 static struct module_info wcm_info = { 109 0, 110 "wc", 111 0, 112 INFPSZ, 113 2048, 114 128 115 }; 116 117 static struct qinit wcurinit = { 118 putq, 119 NULL, 120 wcopen, 121 wcclose, 122 NULL, 123 &wcm_info, 124 NULL 125 }; 126 127 static struct qinit wcuwinit = { 128 wcuwput, 129 NULL, 130 wcopen, 131 wcclose, 132 NULL, 133 &wcm_info, 134 NULL 135 }; 136 137 static 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 */ 150 static struct qinit wclwinit = { 151 NULL, 152 NULL, 153 NULL, 154 NULL, 155 NULL, 156 &wcm_info, 157 NULL 158 }; 159 160 static struct streamtab wcinfo = { 161 &wcurinit, 162 &wcuwinit, 163 &wclrinit, 164 &wclwinit, 165 }; 166 167 static int wc_info(dev_info_t *, ddi_info_cmd_t, void *, void **result); 168 static int wc_attach(dev_info_t *, ddi_attach_cmd_t); 169 static dev_info_t *wc_dip; 170 171 DDI_DEFINE_STREAM_OPS(wc_ops, nulldev, nulldev, wc_attach, nodev, nodev, 172 wc_info, D_MTPERMOD | D_MP, &wcinfo, ddi_quiesce_not_supported); 173 174 static void wcreioctl(void *); 175 static void wcioctl(queue_t *, mblk_t *); 176 #ifdef _HAVE_TEM_FIRMWARE 177 static void wcopoll(void *); 178 static void wconsout(void *); 179 #endif /* _HAVE_TEM_FIRMWARE */ 180 static void wcrstrt(void *); 181 static void wcstart(void); 182 static void wc_open_kb_polledio(struct wscons *wc, queue_t *q, mblk_t *mp); 183 static void wc_close_kb_polledio(struct wscons *wc, queue_t *q, mblk_t *mp); 184 static void wc_polled_putchar(cons_polledio_arg_t arg, unsigned char c); 185 static boolean_t wc_polled_ischar(cons_polledio_arg_t arg); 186 static int wc_polled_getchar(cons_polledio_arg_t arg); 187 static void wc_polled_enter(cons_polledio_arg_t arg); 188 static void wc_polled_exit(cons_polledio_arg_t arg); 189 static void wc_get_size(struct wscons *wscons); 190 static 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 199 static struct dev_ops wc_ops; 200 201 /* 202 * Debug printing 203 */ 204 #ifndef DPRINTF 205 #ifdef DEBUG 206 /*PRINTFLIKE1*/ 207 static 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 224 uint_t wc_errmask = PRINT_MASK_ALL; 225 uint_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 236 static 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 242 static struct modlinkage modlinkage = { 243 MODREV_1, 244 &modldrv, 245 NULL 246 }; 247 248 int 249 _init(void) 250 { 251 return (mod_install(&modlinkage)); 252 } 253 254 int 255 _fini(void) 256 { 257 return (mod_remove(&modlinkage)); 258 } 259 260 int 261 _info(struct modinfo *modinfop) 262 { 263 return (mod_info(&modlinkage, modinfop)); 264 } 265 266 /*ARGSUSED*/ 267 static int 268 wc_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 */ 283 static int 284 wc_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 313 static char obuf[MAXHIWAT]; 314 #endif /* _HAVE_TEM_FIRMWARE */ 315 316 /*ARGSUSED*/ 317 static int 318 wcopen(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*/ 400 static int 401 wcclose(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 */ 424 static int 425 wcuwput(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*/ 560 static void 561 wcreioctl(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 575 static int 576 wc_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 */ 618 static void 619 wcioctl(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 745 open_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 764 close_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 */ 812 static void 813 wc_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 854 nomem: 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 */ 867 static void 868 wc_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 908 nomem: 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 */ 917 static void 918 wcopoll(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 */ 941 static void 942 wcrstrt(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 */ 952 static void 953 wcstart(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 */ 1095 static void 1096 wconsout(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 1151 transmit: 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 */ 1164 static int 1165 wclrput(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 */ 1302 static void 1303 wc_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 */ 1323 static int 1324 wc_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 1339 static boolean_t 1340 wc_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 1351 static void 1352 wc_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 1365 static void 1366 wc_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 1381 static void 1382 wc_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 1395 static void 1396 update_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 */ 1408 static void 1409 wc_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 1428 static void 1429 wc_modechg_cb(tem_modechg_cb_arg_t arg) 1430 { 1431 wc_get_size((struct wscons *)arg); 1432 } 1433