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