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