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