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