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