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 2006 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 = (struct iocblk *)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 = (struct linkblk *)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 = (struct linkblk *)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 *)mp->b_rptr)->ioc_flag; 582 583 STRUCT_DECL(cons_getterm, wcterm); 584 STRUCT_INIT(wcterm, flag); 585 586 arg = *((intptr_t *)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 = (struct iocblk *)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 **)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 || 711 mp->b_cont->b_cont != NULL) 712 goto open_fail; 713 714 /* 715 * If there's no null terminator in the string, fail. 716 */ 717 len = mp->b_cont->b_wptr - mp->b_cont->b_rptr; 718 if (memchr(mp->b_cont->b_rptr, 0, len) == NULL) 719 goto open_fail; 720 721 /* 722 * NOTE: should eventually get default 723 * dimensions from a property, e.g. screen-#rows. 724 */ 725 iocp->ioc_error = tem_init(&wscons.wc_tem, 726 (char *)mp->b_cont->b_rptr, iocp->ioc_cr); 727 /* 728 * Of course, if the terminal emulator initialization 729 * failed, fail. 730 */ 731 if (iocp->ioc_error != 0) 732 goto open_fail; 733 734 tem_register_modechg_cb(wscons.wc_tem, wc_modechg_cb, 735 (tem_modechg_cb_arg_t)&wscons); 736 737 /* 738 * Refresh terminal size with info from terminal emulator. 739 */ 740 wc_get_size(&wscons); 741 742 /* 743 * ... and succeed. 744 */ 745 mp->b_datap->db_type = M_IOCACK; 746 747 open_fail: 748 qreply(q, mp); 749 break; 750 751 case WC_CLOSE_FB: 752 /* 753 * There's nothing that can call this, so it's not 754 * really implemented. 755 */ 756 mp->b_datap->db_type = M_IOCNAK; 757 /* 758 * However, if it were implemented, it would clearly 759 * be root-only. 760 */ 761 if ((iocp->ioc_error = secpolicy_console(iocp->ioc_cr)) != 0) 762 goto close_fail; 763 764 iocp->ioc_error = EINVAL; 765 766 close_fail: 767 qreply(q, mp); 768 break; 769 770 default: 771 772 /* 773 * The only way in which "ttycommon_ioctl" can fail is 774 * if the "ioctl" requires a response containing data 775 * to be returned to the user, and no mblk could be 776 * allocated for the data. No such "ioctl" alters our 777 * state. Thus, we always go ahead and do any 778 * state-changes the "ioctl" calls for. If we couldn't 779 * allocate the data, "ttycommon_ioctl" has stashed the 780 * "ioctl" away safely, so we just call "qbufcall" to 781 * request that we be called back when we stand a 782 * better chance of allocating the data. 783 */ 784 datasize = ttycommon_ioctl(&wscons.wc_ttycommon, q, mp, &error); 785 if (datasize != 0) { 786 if (wscons.wc_bufcallid != 0) 787 qunbufcall(q, wscons.wc_bufcallid); 788 wscons.wc_bufcallid = qbufcall(q, datasize, BPRI_HI, 789 wcreioctl, NULL); 790 return; 791 } 792 793 if (error < 0) { 794 if (iocp->ioc_cmd == TCSBRK) 795 error = 0; 796 else 797 error = EINVAL; 798 } 799 if (error != 0) { 800 iocp->ioc_error = error; 801 mp->b_datap->db_type = M_IOCNAK; 802 } 803 qreply(q, mp); 804 break; 805 } 806 } 807 808 /* 809 * This function gets the polled I/O structures from the lower 810 * keyboard driver. If any initialization or resource allocation 811 * needs to be done by the lower driver, it will be done when 812 * the lower driver services this message. 813 */ 814 static void 815 wc_open_kb_polledio(struct wscons *wscons, queue_t *q, mblk_t *mp) 816 { 817 mblk_t *mp2; 818 struct iocblk *iocp; 819 820 DPRINTF(PRINT_L1, PRINT_MASK_ALL, 821 ("wc_open_kb_polledio: sending CONSOPENPOLLEDIO\n")); 822 823 mp2 = mkiocb(CONSOPENPOLLEDIO); 824 825 if (mp2 == NULL) { 826 /* 827 * If we can't get an mblk, then wait for it. 828 */ 829 goto nomem; 830 } 831 832 mp2->b_cont = allocb(sizeof (struct cons_polledio *), BPRI_HI); 833 834 if (mp2->b_cont == NULL) { 835 /* 836 * If we can't get an mblk, then wait for it, and release 837 * the mblk that we have already allocated. 838 */ 839 freemsg(mp2); 840 goto nomem; 841 } 842 843 iocp = (struct iocblk *)mp2->b_rptr; 844 845 iocp->ioc_count = sizeof (struct cons_polledio *); 846 mp2->b_cont->b_wptr = mp2->b_cont->b_rptr + 847 sizeof (struct cons_polledio *); 848 849 wscons->wc_pending_link = mp; 850 wscons->wc_kb_getpolledio_id = iocp->ioc_id; 851 852 putnext(wscons->wc_kbdqueue, mp2); 853 854 return; 855 856 nomem: 857 iocp = (struct iocblk *)mp->b_rptr; 858 iocp->ioc_error = ENOMEM; 859 mp->b_datap->db_type = M_IOCNAK; 860 qreply(q, mp); 861 } 862 863 /* 864 * This function releases the polled I/O structures from the lower 865 * keyboard driver. If any de-initialization needs to be done, or 866 * any resources need to be released, it will be done when the lower 867 * driver services this message. 868 */ 869 static void 870 wc_close_kb_polledio(struct wscons *wscons, queue_t *q, mblk_t *mp) 871 { 872 mblk_t *mp2; 873 struct iocblk *iocp; 874 875 DPRINTF(PRINT_L1, PRINT_MASK_ALL, 876 ("wc_close_kb_polledio: sending CONSCLOSEPOLLEDIO\n")); 877 878 mp2 = mkiocb(CONSCLOSEPOLLEDIO); 879 880 if (mp2 == NULL) { 881 /* 882 * If we can't get an mblk, then wait for it. 883 */ 884 goto nomem; 885 } 886 887 mp2->b_cont = allocb(sizeof (struct cons_polledio *), BPRI_HI); 888 889 if (mp2->b_cont == NULL) { 890 /* 891 * If we can't get an mblk, then wait for it, and release 892 * the mblk that we have already allocated. 893 */ 894 freemsg(mp2); 895 896 goto nomem; 897 } 898 899 iocp = (struct iocblk *)mp2->b_rptr; 900 901 iocp->ioc_count = 0; 902 903 wscons->wc_pending_link = mp; 904 wscons->wc_kb_getpolledio_id = iocp->ioc_id; 905 906 putnext(wscons->wc_kbdqueue, mp2); 907 908 return; 909 910 nomem: 911 iocp = (struct iocblk *)mp->b_rptr; 912 iocp->ioc_error = ENOMEM; 913 mp->b_datap->db_type = M_IOCNAK; 914 qreply(q, mp); 915 } 916 917 #ifdef _HAVE_TEM_FIRMWARE 918 /* ARGSUSED */ 919 static void 920 wcopoll(void *arg) 921 { 922 queue_t *q; 923 924 q = wscons.wc_ttycommon.t_writeq; 925 wscons.wc_timeoutid = 0; 926 /* See if we can continue output */ 927 if ((wscons.wc_flags & WCS_BUSY) && wscons.wc_pendc != -1) { 928 if (prom_mayput((char)wscons.wc_pendc) == 0) { 929 wscons.wc_pendc = -1; 930 wscons.wc_flags &= ~WCS_BUSY; 931 if (!(wscons.wc_flags&(WCS_DELAY|WCS_STOPPED))) 932 wcstart(); 933 } else 934 wscons.wc_timeoutid = qtimeout(q, wcopoll, NULL, 1); 935 } 936 } 937 #endif /* _HAVE_TEM_FIRMWARE */ 938 939 /* 940 * Restart output on the console after a timeout. 941 */ 942 /* ARGSUSED */ 943 static void 944 wcrstrt(void *arg) 945 { 946 ASSERT(wscons.wc_ttycommon.t_writeq != NULL); 947 wscons.wc_flags &= ~WCS_DELAY; 948 wcstart(); 949 } 950 951 /* 952 * Start console output 953 */ 954 static void 955 wcstart(void) 956 { 957 #ifdef _HAVE_TEM_FIRMWARE 958 int c; 959 ssize_t cc; 960 #endif /* _HAVE_TEM_FIRMWARE */ 961 queue_t *q; 962 mblk_t *bp; 963 mblk_t *nbp; 964 965 /* 966 * If we're waiting for something to happen (delay timeout to 967 * expire, current transmission to finish, output to be 968 * restarted, output to finish draining), don't grab anything 969 * new. 970 */ 971 if (wscons.wc_flags & (WCS_DELAY|WCS_BUSY|WCS_STOPPED)) 972 return; 973 974 q = wscons.wc_ttycommon.t_writeq; 975 /* 976 * assumes that we have been called by whoever holds the 977 * exclusionary lock on the write-side queue (protects 978 * wc_flags and wc_pendc). 979 */ 980 for (;;) { 981 if ((bp = getq(q)) == NULL) 982 return; /* nothing to transmit */ 983 984 /* 985 * We have a new message to work on. 986 * Check whether it's a delay or an ioctl (the latter 987 * occurs if the ioctl in question was waiting for the output 988 * to drain). If it's one of those, process it immediately. 989 */ 990 switch (bp->b_datap->db_type) { 991 992 case M_DELAY: 993 /* 994 * Arrange for "wcrstrt" to be called when the 995 * delay expires; it will turn WCS_DELAY off, 996 * and call "wcstart" to grab the next message. 997 */ 998 if (wscons.wc_timeoutid != 0) 999 (void) quntimeout(q, wscons.wc_timeoutid); 1000 wscons.wc_timeoutid = qtimeout(q, wcrstrt, NULL, 1001 (clock_t)(*(unsigned char *)bp->b_rptr + 6)); 1002 wscons.wc_flags |= WCS_DELAY; 1003 freemsg(bp); 1004 return; /* wait for this to finish */ 1005 1006 case M_IOCTL: 1007 /* 1008 * This ioctl was waiting for the output ahead of 1009 * it to drain; obviously, it has. Do it, and 1010 * then grab the next message after it. 1011 */ 1012 wcioctl(q, bp); 1013 continue; 1014 } 1015 1016 #ifdef _HAVE_TEM_FIRMWARE 1017 if (consmode == CONS_KFB) { 1018 #endif /* _HAVE_TEM_FIRMWARE */ 1019 if (wscons.wc_tem != NULL) { 1020 for (nbp = bp; nbp != NULL; nbp = nbp->b_cont) { 1021 if (nbp->b_wptr > nbp->b_rptr) { 1022 (void) tem_write(wscons.wc_tem, 1023 nbp->b_rptr, 1024 nbp->b_wptr - nbp->b_rptr, 1025 kcred); 1026 } 1027 } 1028 freemsg(bp); 1029 } 1030 #ifdef _HAVE_TEM_FIRMWARE 1031 continue; 1032 } 1033 1034 /* consmode = CONS_FW */ 1035 if ((cc = bp->b_wptr - bp->b_rptr) == 0) { 1036 freemsg(bp); 1037 continue; 1038 } 1039 /* 1040 * Direct output to the frame buffer if this device 1041 * is not the "hardware" console. 1042 */ 1043 if (wscons.wc_defer_output) { 1044 /* 1045 * Never do output here; 1046 * it takes forever. 1047 */ 1048 wscons.wc_flags |= WCS_BUSY; 1049 wscons.wc_pendc = -1; 1050 (void) putbq(q, bp); 1051 if (q->q_count > 128) { /* do it soon */ 1052 softcall(wconsout, NULL); 1053 } else { /* wait a bit */ 1054 if (wscons.wc_timeoutid != 0) 1055 (void) quntimeout(q, 1056 wscons.wc_timeoutid); 1057 wscons.wc_timeoutid = qtimeout(q, wconsout, 1058 NULL, hz / 30); 1059 } 1060 return; 1061 } 1062 for (;;) { 1063 c = *bp->b_rptr++; 1064 cc--; 1065 if (prom_mayput((char)c) != 0) { 1066 wscons.wc_flags |= WCS_BUSY; 1067 wscons.wc_pendc = c; 1068 if (wscons.wc_timeoutid != 0) 1069 (void) quntimeout(q, 1070 wscons.wc_timeoutid); 1071 wscons.wc_timeoutid = qtimeout(q, wcopoll, 1072 NULL, 1); 1073 if (bp != NULL) 1074 /* not done with this message yet */ 1075 (void) putbq(q, bp); 1076 return; 1077 } 1078 while (cc <= 0) { 1079 nbp = bp; 1080 bp = bp->b_cont; 1081 freeb(nbp); 1082 if (bp == NULL) 1083 return; 1084 cc = bp->b_wptr - bp->b_rptr; 1085 } 1086 } 1087 #endif /* _HAVE_TEM_FIRMWARE */ 1088 } 1089 } 1090 1091 #ifdef _HAVE_TEM_FIRMWARE 1092 /* 1093 * Output to frame buffer console. 1094 * It takes a long time to scroll. 1095 */ 1096 /* ARGSUSED */ 1097 static void 1098 wconsout(void *dummy) 1099 { 1100 uchar_t *cp; 1101 ssize_t cc; 1102 queue_t *q; 1103 mblk_t *bp; 1104 mblk_t *nbp; 1105 char *current_position; 1106 ssize_t bytes_left; 1107 1108 if ((q = wscons.wc_ttycommon.t_writeq) == NULL) { 1109 return; /* not attached to a stream */ 1110 } 1111 1112 /* 1113 * Set up to copy up to MAXHIWAT bytes. 1114 */ 1115 current_position = &obuf[0]; 1116 bytes_left = MAXHIWAT; 1117 while ((bp = getq(q)) != NULL) { 1118 if (bp->b_datap->db_type == M_IOCTL) { 1119 /* 1120 * This ioctl was waiting for the output ahead of 1121 * it to drain; obviously, it has. Put it back 1122 * so that "wcstart" can handle it, and transmit 1123 * what we've got. 1124 */ 1125 (void) putbq(q, bp); 1126 goto transmit; 1127 } 1128 1129 do { 1130 cp = bp->b_rptr; 1131 cc = bp->b_wptr - cp; 1132 while (cc != 0) { 1133 if (bytes_left == 0) { 1134 /* 1135 * Out of buffer space; put this 1136 * buffer back on the queue, and 1137 * transmit what we have. 1138 */ 1139 bp->b_rptr = cp; 1140 (void) putbq(q, bp); 1141 goto transmit; 1142 } 1143 *current_position++ = *cp++; 1144 cc--; 1145 bytes_left--; 1146 } 1147 nbp = bp; 1148 bp = bp->b_cont; 1149 freeb(nbp); 1150 } while (bp != NULL); 1151 } 1152 1153 transmit: 1154 if ((cc = MAXHIWAT - bytes_left) != 0) 1155 console_puts(obuf, cc); 1156 1157 wscons.wc_flags &= ~WCS_BUSY; 1158 wcstart(); 1159 } 1160 #endif /* _HAVE_TEM_FIRMWARE */ 1161 1162 /* 1163 * Put procedure for lower read queue. 1164 * Pass everything up to queue above "upper half". 1165 */ 1166 static int 1167 wclrput(queue_t *q, mblk_t *mp) 1168 { 1169 queue_t *upq; 1170 struct iocblk *iocp; 1171 1172 DPRINTF(PRINT_L1, PRINT_MASK_ALL, 1173 ("wclrput: wclrput type = 0x%x\n", mp->b_datap->db_type)); 1174 1175 switch (mp->b_datap->db_type) { 1176 1177 case M_FLUSH: 1178 if (*mp->b_rptr == FLUSHW || *mp->b_rptr == FLUSHRW) { 1179 /* 1180 * Flush our write queue. 1181 */ 1182 /* XXX doesn't flush M_DELAY */ 1183 flushq(WR(q), FLUSHDATA); 1184 *mp->b_rptr = FLUSHR; /* it has been flushed */ 1185 } 1186 if (*mp->b_rptr == FLUSHR || *mp->b_rptr == FLUSHRW) { 1187 flushq(q, FLUSHDATA); 1188 *mp->b_rptr = FLUSHW; /* it has been flushed */ 1189 qreply(q, mp); /* give the read queues a crack at it */ 1190 } else 1191 freemsg(mp); 1192 break; 1193 1194 case M_DATA: 1195 if ((upq = wscons.wc_ttycommon.t_readq) != NULL) { 1196 if (!canput(upq->q_next)) { 1197 ttycommon_qfull(&wscons.wc_ttycommon, upq); 1198 wcstart(); 1199 freemsg(mp); 1200 } else 1201 putnext(upq, mp); 1202 } else 1203 freemsg(mp); 1204 break; 1205 1206 case M_IOCACK: 1207 case M_IOCNAK: 1208 iocp = (struct iocblk *)mp->b_rptr; 1209 if (wscons.wc_pending_link != NULL && 1210 iocp->ioc_id == wscons.wc_kb_getpolledio_id) { 1211 switch (mp->b_datap->db_type) { 1212 1213 case M_IOCACK: 1214 switch (iocp->ioc_cmd) { 1215 1216 1217 case CONSOPENPOLLEDIO: 1218 DPRINTF(PRINT_L1, PRINT_MASK_ALL, 1219 ("wclrput: " 1220 "ACK CONSOPENPOLLEDIO\n")); 1221 wscons.wc_kb_polledio = 1222 *(struct cons_polledio **) 1223 mp->b_cont->b_rptr; 1224 wscons.wc_polledio. 1225 cons_polledio_getchar = 1226 wc_polled_getchar; 1227 wscons.wc_polledio. 1228 cons_polledio_ischar = 1229 wc_polled_ischar; 1230 break; 1231 1232 case CONSCLOSEPOLLEDIO: 1233 DPRINTF(PRINT_L1, PRINT_MASK_ALL, 1234 ("wclrput: " 1235 "ACK CONSCLOSEPOLLEDIO\n")); 1236 wscons.wc_kb_polledio = NULL; 1237 wscons.wc_kbdqueue = NULL; 1238 wscons.wc_polledio. 1239 cons_polledio_getchar = NULL; 1240 wscons.wc_polledio. 1241 cons_polledio_ischar = NULL; 1242 break; 1243 default: 1244 DPRINTF(PRINT_L1, PRINT_MASK_ALL, 1245 ("wclrput: " 1246 "ACK UNKNOWN\n")); 1247 } 1248 1249 break; 1250 case M_IOCNAK: 1251 /* 1252 * Keyboard may or may not support polled I/O. 1253 * This ioctl may have been rejected because 1254 * we only have the wc->conskbd chain built, 1255 * and the keyboard driver has not been linked 1256 * underneath conskbd yet. 1257 */ 1258 DPRINTF(PRINT_L1, PRINT_MASK_ALL, 1259 ("wclrput: NAK\n")); 1260 1261 switch (iocp->ioc_cmd) { 1262 1263 case CONSCLOSEPOLLEDIO: 1264 wscons.wc_kb_polledio = NULL; 1265 wscons.wc_kbdqueue = NULL; 1266 wscons.wc_polledio. 1267 cons_polledio_getchar = NULL; 1268 wscons.wc_polledio. 1269 cons_polledio_ischar = NULL; 1270 break; 1271 } 1272 break; 1273 } 1274 1275 /* 1276 * Discard the response, replace it with the 1277 * pending response to the I_PLINK, then let it 1278 * flow upward. 1279 */ 1280 freemsg(mp); 1281 mp = wscons.wc_pending_link; 1282 wscons.wc_pending_link = NULL; 1283 wscons.wc_kb_getpolledio_id = 0; 1284 } 1285 /* FALLTHROUGH */ 1286 1287 default: /* inc M_ERROR, M_HANGUP, M_IOCACK, M_IOCNAK, ... */ 1288 DPRINTF(PRINT_L1, PRINT_MASK_ALL, 1289 ("wclrput: Message DISCARDED\n")); 1290 if ((upq = wscons.wc_ttycommon.t_readq) != NULL) { 1291 putnext(upq, mp); 1292 } else { 1293 freemsg(mp); 1294 } 1295 break; 1296 } 1297 1298 return (0); 1299 } 1300 1301 /* 1302 * Auxiliary routines, for allowing the workstation console to be redirected. 1303 */ 1304 1305 /* 1306 * Given a minor device number for a wscons instance, return a held vnode for 1307 * it. 1308 * 1309 * We currently support only one instance, for the "workstation console". 1310 */ 1311 int 1312 wcvnget(minor_t unit, vnode_t **vpp) 1313 { 1314 if (unit != 0 || rwsconsvp == NULL) 1315 return (ENXIO); 1316 1317 /* 1318 * rwsconsvp is already held, so we don't have to do it here. 1319 */ 1320 *vpp = rwsconsvp; 1321 return (0); 1322 } 1323 1324 /* 1325 * Release the vnode that wcvnget returned. 1326 */ 1327 /* ARGSUSED */ 1328 void 1329 wcvnrele(minor_t unit, vnode_t *vp) 1330 { 1331 /* 1332 * Nothing to do, since we only support the workstation console 1333 * instance that's held throughout the system's lifetime. 1334 */ 1335 } 1336 1337 /* 1338 * The declaration and initialization of the wscons_srvnops has been 1339 * moved to space.c to allow "wc" to become a loadable module. 1340 */ 1341 1342 /* 1343 * These are for systems without OBP, and for devices that cannot be 1344 * shared between Solaris and the OBP. 1345 */ 1346 1347 static void 1348 wc_polled_putchar(cons_polledio_arg_t arg, unsigned char c) 1349 { 1350 if (c == '\n') 1351 wc_polled_putchar(arg, '\r'); 1352 1353 if (wscons.wc_tem == NULL) { 1354 /* 1355 * We have no terminal emulator configured. We have no 1356 * recourse but to drop the output on the floor. 1357 */ 1358 return; 1359 } 1360 1361 tem_polled_write(wscons.wc_tem, &c, 1); 1362 } 1363 1364 /* 1365 * These are for systems without OBP, and for devices that cannot be 1366 * shared between Solaris and the OBP. 1367 */ 1368 static int 1369 wc_polled_getchar(cons_polledio_arg_t arg) 1370 { 1371 struct wscons *wscons = (struct wscons *)arg; 1372 1373 if (wscons->wc_kb_polledio == NULL) { 1374 prom_printf("wscons: getchar with no keyboard support"); 1375 prom_printf("Halted..."); 1376 for (;;) 1377 /* HANG FOREVER */; 1378 } 1379 1380 return (wscons->wc_kb_polledio->cons_polledio_getchar( 1381 wscons->wc_kb_polledio->cons_polledio_argument)); 1382 } 1383 1384 static boolean_t 1385 wc_polled_ischar(cons_polledio_arg_t arg) 1386 { 1387 struct wscons *wscons = (struct wscons *)arg; 1388 1389 if (wscons->wc_kb_polledio == NULL) 1390 return (B_FALSE); 1391 1392 return (wscons->wc_kb_polledio->cons_polledio_ischar( 1393 wscons->wc_kb_polledio->cons_polledio_argument)); 1394 } 1395 1396 static void 1397 wc_polled_enter(cons_polledio_arg_t arg) 1398 { 1399 struct wscons *wscons = (struct wscons *)arg; 1400 1401 if (wscons->wc_kb_polledio == NULL) 1402 return; 1403 1404 if (wscons->wc_kb_polledio->cons_polledio_enter != NULL) { 1405 wscons->wc_kb_polledio->cons_polledio_enter( 1406 wscons->wc_kb_polledio->cons_polledio_argument); 1407 } 1408 } 1409 1410 static void 1411 wc_polled_exit(cons_polledio_arg_t arg) 1412 { 1413 struct wscons *wscons = (struct wscons *)arg; 1414 1415 if (wscons->wc_kb_polledio == NULL) 1416 return; 1417 1418 if (wscons->wc_kb_polledio->cons_polledio_exit != NULL) { 1419 wscons->wc_kb_polledio->cons_polledio_exit( 1420 wscons->wc_kb_polledio->cons_polledio_argument); 1421 } 1422 } 1423 1424 1425 #ifdef DEBUG 1426 static void 1427 wc_dprintf(const char *fmt, ...) 1428 { 1429 char buf[256]; 1430 va_list ap; 1431 1432 va_start(ap, fmt); 1433 (void) vsprintf(buf, fmt, ap); 1434 va_end(ap); 1435 1436 cmn_err(CE_WARN, "wc: %s", buf); 1437 } 1438 #endif 1439 1440 static void 1441 update_property(struct wscons *wscons, char *name, ushort_t value) 1442 { 1443 char data[8]; 1444 1445 (void) snprintf(data, sizeof (data), "%u", value); 1446 (void) ddi_prop_update_string(wscons->wc_dev, wc_dip, name, data); 1447 } 1448 1449 /* 1450 * Gets the number of text rows and columns and the 1451 * width and height (in pixels) of the console. 1452 */ 1453 static void 1454 wc_get_size(struct wscons *wscons) 1455 { 1456 struct winsize *t = &wscons->wc_ttycommon.t_size; 1457 ushort_t r = LOSCREENLINES, c = LOSCREENCOLS, x = 0, y = 0; 1458 1459 if (wscons->wc_tem != NULL) 1460 tem_get_size(wscons->wc_tem, &r, &c, &x, &y); 1461 #ifdef _HAVE_TEM_FIRMWARE 1462 else { 1463 console_get_size(&r, &c, &x, &y); 1464 } 1465 #endif /* _HAVE_TEM_FIRMWARE */ 1466 1467 update_property(wscons, "screen-#cols", t->ws_col = c); 1468 update_property(wscons, "screen-#rows", t->ws_row = r); 1469 update_property(wscons, "screen-width", t->ws_xpixel = x); 1470 update_property(wscons, "screen-height", t->ws_ypixel = y); 1471 } 1472 1473 static void 1474 wc_modechg_cb(tem_modechg_cb_arg_t arg) 1475 { 1476 wc_get_size((struct wscons *)arg); 1477 } 1478