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