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