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