1/* $NetBSD: wsdisplay.c,v 1.166 2023/03/01 08:42:33 riastradh Exp $ */ 2 3/* 4 * Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Christopher G. Demetriou 17 * for the NetBSD Project. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33#include <sys/cdefs.h> 34__KERNEL_RCSID(0, "$NetBSD: wsdisplay.c,v 1.166 2023/03/01 08:42:33 riastradh Exp $"); 35 36#ifdef _KERNEL_OPT 37#include "opt_wsdisplay_compat.h" 38#include "opt_wsmsgattrs.h" 39#endif 40 41#include "wskbd.h" 42#include "wsmux.h" 43#include "wsdisplay.h" 44 45#include <sys/param.h> 46#include <sys/conf.h> 47#include <sys/device.h> 48#include <sys/ioctl.h> 49#include <sys/poll.h> 50#include <sys/kernel.h> 51#include <sys/proc.h> 52#include <sys/malloc.h> 53#include <sys/syslog.h> 54#include <sys/systm.h> 55#include <sys/tty.h> 56#include <sys/signalvar.h> 57#include <sys/errno.h> 58#include <sys/fcntl.h> 59#include <sys/vnode.h> 60#include <sys/kauth.h> 61#include <sys/sysctl.h> 62 63#include <dev/wscons/wsconsio.h> 64#include <dev/wscons/wseventvar.h> 65#include <dev/wscons/wsmuxvar.h> 66#include <dev/wscons/wsdisplayvar.h> 67#include <dev/wscons/wsksymvar.h> 68#include <dev/wscons/wsksymdef.h> 69#include <dev/wscons/wsemulvar.h> 70#include <dev/wscons/wscons_callbacks.h> 71#include <dev/cons.h> 72 73#include "locators.h" 74#include "ioconf.h" 75 76#ifdef WSDISPLAY_MULTICONS 77static bool wsdisplay_multicons_enable = true; 78static bool wsdisplay_multicons_suspended = false; 79#endif 80 81/* Console device before replaced by wsdisplay */ 82static struct consdev *wsdisplay_ocn; 83 84struct wsscreen_internal { 85 const struct wsdisplay_emulops *emulops; 86 void *emulcookie; 87 88 const struct wsscreen_descr *scrdata; 89 90 const struct wsemul_ops *wsemul; 91 void *wsemulcookie; 92}; 93 94struct wsscreen { 95 struct wsscreen_internal *scr_dconf; 96 97 struct tty *scr_tty; 98 int scr_hold_screen; /* hold tty output */ 99 100 int scr_flags; 101#define SCR_OPEN 1 /* is it open? */ 102#define SCR_WAITACTIVE 2 /* someone waiting on activation */ 103#define SCR_GRAPHICS 4 /* graphics mode, no text (emulation) output */ 104#define SCR_DUMBFB 8 /* in use as a dumb fb (iff SCR_GRAPHICS) */ 105 const struct wscons_syncops *scr_syncops; 106 void *scr_synccookie; 107 108#ifdef WSDISPLAY_COMPAT_RAWKBD 109 int scr_rawkbd; 110#endif 111 112#ifdef WSDISPLAY_MULTICONS 113 callout_t scr_getc_ch; 114#endif 115 116 struct wsdisplay_softc *sc; 117 118 /* XXX this is to support a hack in emulinput, see comment below */ 119 int scr_in_ttyoutput; 120}; 121 122static struct wsscreen *wsscreen_attach(struct wsdisplay_softc *, int, 123 const char *, 124 const struct wsscreen_descr *, void *, 125 int, int, long); 126static void wsscreen_detach(struct wsscreen *); 127static int wsdisplay_addscreen(struct wsdisplay_softc *, int, const char *, const char *); 128static void wsdisplay_addscreen_print(struct wsdisplay_softc *, int, int); 129static void wsdisplay_closescreen(struct wsdisplay_softc *, struct wsscreen *); 130static int wsdisplay_delscreen(struct wsdisplay_softc *, int, int); 131 132#define WSDISPLAY_MAXSCREEN 8 133 134struct wsdisplay_softc { 135 device_t sc_dev; 136 137 const struct wsdisplay_accessops *sc_accessops; 138 void *sc_accesscookie; 139 140 const struct wsscreen_list *sc_scrdata; 141#ifdef WSDISPLAY_SCROLLSUPPORT 142 struct wsdisplay_scroll_data sc_scroll_values; 143#endif 144 145 struct wsscreen *sc_scr[WSDISPLAY_MAXSCREEN]; 146 int sc_focusidx; /* available only if sc_focus isn't null */ 147 struct wsscreen *sc_focus; 148 149 struct wseventvar evar; 150 151 int sc_isconsole; 152 153 int sc_flags; 154#define SC_SWITCHPENDING 1 155#define SC_SWITCHERROR 2 156#define SC_XATTACHED 4 /* X server active */ 157 kmutex_t sc_flagsmtx; /* for flags, might also be used for focus */ 158 kcondvar_t sc_flagscv; 159 160 int sc_screenwanted, sc_oldscreen; /* valid with SC_SWITCHPENDING */ 161 162#if NWSKBD > 0 163 struct wsevsrc *sc_input; 164#ifdef WSDISPLAY_COMPAT_RAWKBD 165 int sc_rawkbd; 166#endif 167#endif /* NWSKBD > 0 */ 168}; 169 170#ifdef WSDISPLAY_SCROLLSUPPORT 171 172struct wsdisplay_scroll_data wsdisplay_default_scroll_values = { 173 WSDISPLAY_SCROLL_DOALL, 174 25, 175 2, 176}; 177#endif 178 179/* Autoconfiguration definitions. */ 180static int wsdisplay_emul_match(device_t , cfdata_t, void *); 181static void wsdisplay_emul_attach(device_t, device_t, void *); 182static int wsdisplay_emul_detach(device_t, int); 183static int wsdisplay_noemul_match(device_t, cfdata_t, void *); 184static void wsdisplay_noemul_attach(device_t, device_t, void *); 185static bool wsdisplay_suspend(device_t, const pmf_qual_t *); 186 187CFATTACH_DECL_NEW(wsdisplay_emul, sizeof (struct wsdisplay_softc), 188 wsdisplay_emul_match, wsdisplay_emul_attach, wsdisplay_emul_detach, NULL); 189 190CFATTACH_DECL_NEW(wsdisplay_noemul, sizeof (struct wsdisplay_softc), 191 wsdisplay_noemul_match, wsdisplay_noemul_attach, NULL, NULL); 192 193dev_type_open(wsdisplayopen); 194dev_type_close(wsdisplayclose); 195dev_type_read(wsdisplayread); 196dev_type_write(wsdisplaywrite); 197dev_type_ioctl(wsdisplayioctl); 198dev_type_stop(wsdisplaystop); 199dev_type_tty(wsdisplaytty); 200dev_type_poll(wsdisplaypoll); 201dev_type_mmap(wsdisplaymmap); 202dev_type_kqfilter(wsdisplaykqfilter); 203 204const struct cdevsw wsdisplay_cdevsw = { 205 .d_open = wsdisplayopen, 206 .d_close = wsdisplayclose, 207 .d_read = wsdisplayread, 208 .d_write = wsdisplaywrite, 209 .d_ioctl = wsdisplayioctl, 210 .d_stop = wsdisplaystop, 211 .d_tty = wsdisplaytty, 212 .d_poll = wsdisplaypoll, 213 .d_mmap = wsdisplaymmap, 214 .d_kqfilter = wsdisplaykqfilter, 215 .d_discard = nodiscard, 216 .d_flag = D_TTY 217}; 218 219static void wsdisplaystart(struct tty *); 220static int wsdisplayparam(struct tty *, struct termios *); 221 222 223#define WSDISPLAYUNIT(dev) (minor(dev) >> 8) 224#define WSDISPLAYSCREEN(dev) (minor(dev) & 0xff) 225#define ISWSDISPLAYSTAT(dev) (WSDISPLAYSCREEN(dev) == 254) 226#define ISWSDISPLAYCTL(dev) (WSDISPLAYSCREEN(dev) == 255) 227#define WSDISPLAYMINOR(unit, screen) (((unit) << 8) | (screen)) 228 229#define WSSCREEN_HAS_EMULATOR(scr) ((scr)->scr_dconf->wsemul != NULL) 230#define WSSCREEN_HAS_TTY(scr) ((scr)->scr_tty != NULL) 231 232static void wsdisplay_common_attach(struct wsdisplay_softc *sc, 233 int console, int kbdmux, const struct wsscreen_list *, 234 const struct wsdisplay_accessops *accessops, 235 void *accesscookie); 236 237#ifdef WSDISPLAY_COMPAT_RAWKBD 238int wsdisplay_update_rawkbd(struct wsdisplay_softc *, 239 struct wsscreen *); 240#endif 241 242static int wsdisplay_console_initted; 243static int wsdisplay_console_attached; 244static struct wsdisplay_softc *wsdisplay_console_device; 245static struct wsscreen_internal wsdisplay_console_conf; 246 247static int wsdisplay_getc(dev_t); 248static void wsdisplay_pollc(dev_t, int); 249 250static int wsdisplay_cons_pollmode; 251static int (*wsdisplay_cons_kbd_getc)(dev_t); 252static void (*wsdisplay_cons_kbd_pollc)(dev_t, int); 253 254static struct consdev wsdisplay_cons = { 255 .cn_getc = wsdisplay_getc, 256 .cn_putc = wsdisplay_cnputc, 257 .cn_pollc = wsdisplay_pollc, 258 .cn_dev = NODEV, 259 .cn_pri = CN_NORMAL 260}; 261 262#ifndef WSDISPLAY_DEFAULTSCREENS 263# define WSDISPLAY_DEFAULTSCREENS 0 264#endif 265int wsdisplay_defaultscreens = WSDISPLAY_DEFAULTSCREENS; 266 267static int wsdisplay_switch1(device_t, int, int); 268static void wsdisplay_switch1_cb(void *, int, int); 269static int wsdisplay_switch2(device_t, int, int); 270static void wsdisplay_switch2_cb(void *, int, int); 271static int wsdisplay_switch3(device_t, int, int); 272static void wsdisplay_switch3_cb(void *, int, int); 273 274static void wsdisplay_swdone_cb(void *, int, int); 275static int wsdisplay_dosync(struct wsdisplay_softc *, int); 276 277int wsdisplay_clearonclose; 278 279#ifdef WSDISPLAY_MULTICONS 280/* 281 * Replace cn_isconsole() so that we can enter DDB from old console. 282 */ 283bool 284wsdisplay_cn_isconsole(dev_t dev) 285{ 286 287 return (cn_tab != NULL && cn_tab->cn_dev == dev) || 288 (cn_tab == &wsdisplay_cons && !wsdisplay_multicons_suspended && 289 wsdisplay_multicons_enable && wsdisplay_ocn != NULL && 290 wsdisplay_ocn->cn_dev == dev); 291} 292 293static void 294wsscreen_getc_poll(void *priv) 295{ 296 struct wsscreen *scr = priv; 297 int c; 298 299 if (wsdisplay_multicons_enable && 300 wsdisplay_ocn && wsdisplay_ocn->cn_getc && 301 WSSCREEN_HAS_EMULATOR(scr) && WSSCREEN_HAS_TTY(scr)) { 302 struct tty *tp = scr->scr_tty; 303 do { 304 c = wsdisplay_ocn->cn_getc(wsdisplay_ocn->cn_dev); 305 if (c >= 0) 306 (*tp->t_linesw->l_rint)((unsigned char)c, tp); 307 } while (c >= 0); 308 } 309 310 callout_schedule(&scr->scr_getc_ch, mstohz(10)); 311} 312#endif 313 314static struct wsscreen * 315wsscreen_attach(struct wsdisplay_softc *sc, int console, const char *emul, 316 const struct wsscreen_descr *type, void *cookie, int ccol, 317 int crow, long defattr) 318{ 319 struct wsscreen_internal *dconf; 320 struct wsscreen *scr; 321 322 scr = malloc(sizeof(struct wsscreen), M_DEVBUF, M_WAITOK); 323 324 if (console) { 325 dconf = &wsdisplay_console_conf; 326 /* 327 * If there's an emulation, tell it about the callback argument. 328 * The other stuff is already there. 329 */ 330 if (dconf->wsemul != NULL) 331 (*dconf->wsemul->attach)(1, 0, 0, 0, 0, scr, 0); 332 } else { /* not console */ 333 dconf = malloc(sizeof(struct wsscreen_internal), 334 M_DEVBUF, M_WAITOK); 335 dconf->emulops = type->textops; 336 dconf->emulcookie = cookie; 337 if (dconf->emulops) { 338 dconf->wsemul = wsemul_pick(emul); 339 if (dconf->wsemul == NULL) { 340 free(dconf, M_DEVBUF); 341 free(scr, M_DEVBUF); 342 return NULL; 343 } 344 dconf->wsemulcookie = 345 (*dconf->wsemul->attach)(0, type, cookie, 346 ccol, crow, scr, defattr); 347 } else 348 dconf->wsemul = NULL; 349 dconf->scrdata = type; 350 } 351 352 scr->scr_dconf = dconf; 353 354 scr->scr_tty = tty_alloc(); 355 tty_attach(scr->scr_tty); 356 scr->scr_hold_screen = 0; 357 if (WSSCREEN_HAS_EMULATOR(scr)) 358 scr->scr_flags = 0; 359 else 360 scr->scr_flags = SCR_GRAPHICS; 361 362 scr->scr_syncops = 0; 363 scr->sc = sc; 364#ifdef WSDISPLAY_COMPAT_RAWKBD 365 scr->scr_rawkbd = 0; 366#endif 367#ifdef WSDISPLAY_MULTICONS 368 callout_init(&scr->scr_getc_ch, 0); 369 callout_setfunc(&scr->scr_getc_ch, wsscreen_getc_poll, scr); 370 if (console) 371 callout_schedule(&scr->scr_getc_ch, mstohz(10)); 372#endif 373 return scr; 374} 375 376static void 377wsscreen_detach(struct wsscreen *scr) 378{ 379 u_int ccol, crow; /* XXX */ 380 381 if (WSSCREEN_HAS_TTY(scr)) { 382 tty_detach(scr->scr_tty); 383 tty_free(scr->scr_tty); 384 } 385 if (WSSCREEN_HAS_EMULATOR(scr)) { 386 (*scr->scr_dconf->wsemul->detach)(scr->scr_dconf->wsemulcookie, 387 &ccol, &crow); 388 wsemul_drop(scr->scr_dconf->wsemul); 389 } 390 if (scr->scr_dconf->scrdata->capabilities & WSSCREEN_FREE) 391 free(__UNCONST(scr->scr_dconf->scrdata), M_DEVBUF); 392#ifdef WSDISPLAY_MULTICONS 393 callout_halt(&scr->scr_getc_ch, NULL); 394 callout_destroy(&scr->scr_getc_ch); 395#endif 396 free(scr->scr_dconf, M_DEVBUF); 397 free(scr, M_DEVBUF); 398} 399 400const struct wsscreen_descr * 401wsdisplay_screentype_pick(const struct wsscreen_list *scrdata, const char *name) 402{ 403 int i; 404 const struct wsscreen_descr *scr; 405 406 KASSERT(scrdata->nscreens > 0); 407 if (name == NULL) 408 return scrdata->screens[0]; 409 410 for (i = 0; i < scrdata->nscreens; i++) { 411 scr = scrdata->screens[i]; 412 if (!strcmp(name, scr->name)) 413 return scr; 414 } 415 416 return 0; 417} 418 419/* 420 * print info about attached screen 421 */ 422static void 423wsdisplay_addscreen_print(struct wsdisplay_softc *sc, int idx, int count) 424{ 425 aprint_verbose_dev(sc->sc_dev, "screen %d", idx); 426 if (count > 1) 427 aprint_verbose("-%d", idx + (count-1)); 428 aprint_verbose(" added (%s", sc->sc_scr[idx]->scr_dconf->scrdata->name); 429 if (WSSCREEN_HAS_EMULATOR(sc->sc_scr[idx])) { 430 aprint_verbose(", %s emulation", 431 sc->sc_scr[idx]->scr_dconf->wsemul->name); 432 } 433 aprint_verbose(")\n"); 434} 435 436static int 437wsdisplay_addscreen(struct wsdisplay_softc *sc, int idx, 438 const char *screentype, const char *emul) 439{ 440 const struct wsscreen_descr *scrdesc; 441 struct wsscreen_descr *scrdescr2; 442 int error; 443 void *cookie; 444 int ccol, crow; 445 long defattr; 446 struct wsscreen *scr; 447 int s; 448 449 if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN) 450 return EINVAL; 451 if (sc->sc_scr[idx] != NULL) 452 return EBUSY; 453 scrdesc = wsdisplay_screentype_pick(sc->sc_scrdata, screentype); 454 if (!scrdesc) 455 return ENXIO; 456 457 /* 458 * if this screen can resize we need to copy the descr so each screen 459 * gets its own 460 */ 461 if (scrdesc->capabilities & WSSCREEN_RESIZE) { 462 /* we want per screen wsscreen_descr */ 463 scrdescr2 = malloc(sizeof(struct wsscreen_descr), M_DEVBUF, M_WAITOK); 464 memcpy(scrdescr2, scrdesc, sizeof(struct wsscreen_descr)); 465 scrdescr2->capabilities |= WSSCREEN_FREE; 466 scrdesc = scrdescr2; 467 } 468 469 error = (*sc->sc_accessops->alloc_screen)(sc->sc_accesscookie, 470 scrdesc, &cookie, &ccol, &crow, &defattr); 471 if (error) 472 return error; 473 474 scr = wsscreen_attach(sc, 0, emul, scrdesc, 475 cookie, ccol, crow, defattr); 476 if (scr == NULL) { 477 (*sc->sc_accessops->free_screen)(sc->sc_accesscookie, 478 cookie); 479 return ENXIO; 480 } 481 482 sc->sc_scr[idx] = scr; 483 484 /* if no screen has focus yet, activate the first we get */ 485 s = spltty(); 486 if (!sc->sc_focus) { 487 (*sc->sc_accessops->show_screen)(sc->sc_accesscookie, 488 scr->scr_dconf->emulcookie, 489 0, 0, 0); 490 sc->sc_focusidx = idx; 491 sc->sc_focus = scr; 492 } 493 splx(s); 494 return 0; 495} 496 497static void 498wsdisplay_closescreen(struct wsdisplay_softc *sc, struct wsscreen *scr) 499{ 500 int maj, mn, idx; 501 502 /* hangup */ 503 if (WSSCREEN_HAS_TTY(scr)) { 504 struct tty *tp = scr->scr_tty; 505 (*tp->t_linesw->l_modem)(tp, 0); 506 } 507 508 /* locate the major number */ 509 maj = cdevsw_lookup_major(&wsdisplay_cdevsw); 510 /* locate the screen index */ 511 for (idx = 0; idx < WSDISPLAY_MAXSCREEN; idx++) 512 if (scr == sc->sc_scr[idx]) 513 break; 514#ifdef DIAGNOSTIC 515 if (idx == WSDISPLAY_MAXSCREEN) 516 panic("wsdisplay_forceclose: bad screen"); 517#endif 518 519 /* nuke the vnodes */ 520 mn = WSDISPLAYMINOR(device_unit(sc->sc_dev), idx); 521 vdevgone(maj, mn, mn, VCHR); 522} 523 524#ifdef WSDISPLAY_SCROLLSUPPORT 525void 526wsdisplay_scroll(void *arg, int op) 527{ 528 device_t dv = arg; 529 struct wsdisplay_softc *sc = device_private(dv); 530 struct wsscreen *scr; 531 int lines; 532 533 scr = sc->sc_focus; 534 535 if (!scr) 536 return; 537 538 if (op == WSDISPLAY_SCROLL_RESET) 539 lines = 0; 540 else { 541 lines = (op & WSDISPLAY_SCROLL_LOW) ? 542 sc->sc_scroll_values.slowlines : 543 sc->sc_scroll_values.fastlines; 544 if (op & WSDISPLAY_SCROLL_BACKWARD) 545 lines = -(lines); 546 } 547 548 if (sc->sc_accessops->scroll) { 549 (*sc->sc_accessops->scroll)(sc->sc_accesscookie, 550 sc->sc_focus->scr_dconf->emulcookie, lines); 551 } 552} 553#endif 554 555static int 556wsdisplay_delscreen(struct wsdisplay_softc *sc, int idx, int flags) 557{ 558 struct wsscreen *scr; 559 int s; 560 void *cookie; 561 562 if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN) 563 return EINVAL; 564 if ((scr = sc->sc_scr[idx]) == NULL) 565 return ENXIO; 566 567 if (scr->scr_dconf == &wsdisplay_console_conf || 568 scr->scr_syncops || 569 ((scr->scr_flags & SCR_OPEN) && !(flags & WSDISPLAY_DELSCR_FORCE))) 570 return EBUSY; 571 572 wsdisplay_closescreen(sc, scr); 573 574 /* 575 * delete pointers, so neither device entries 576 * nor keyboard input can reference it anymore 577 */ 578 s = spltty(); 579 if (sc->sc_focus == scr) { 580 sc->sc_focus = 0; 581#ifdef WSDISPLAY_COMPAT_RAWKBD 582 wsdisplay_update_rawkbd(sc, 0); 583#endif 584 } 585 sc->sc_scr[idx] = 0; 586 splx(s); 587 588 /* 589 * Wake up processes waiting for the screen to 590 * be activated. Sleepers must check whether 591 * the screen still exists. 592 */ 593 if (scr->scr_flags & SCR_WAITACTIVE) 594 wakeup(scr); 595 596 /* save a reference to the graphics screen */ 597 cookie = scr->scr_dconf->emulcookie; 598 599 wsscreen_detach(scr); 600 601 (*sc->sc_accessops->free_screen)(sc->sc_accesscookie, 602 cookie); 603 604 aprint_verbose_dev(sc->sc_dev, "screen %d deleted\n", idx); 605 return 0; 606} 607 608/* 609 * Autoconfiguration functions. 610 */ 611int 612wsdisplay_emul_match(device_t parent, cfdata_t match, void *aux) 613{ 614 struct wsemuldisplaydev_attach_args *ap = aux; 615 616 if (match->cf_loc[WSEMULDISPLAYDEVCF_CONSOLE] != 617 WSEMULDISPLAYDEVCF_CONSOLE_DEFAULT) { 618 /* 619 * If console-ness of device specified, either match 620 * exactly (at high priority), or fail. 621 */ 622 if (match->cf_loc[WSEMULDISPLAYDEVCF_CONSOLE] != 0 && 623 ap->console != 0) 624 return 10; 625 else 626 return 0; 627 } 628 629 /* If console-ness unspecified, it wins. */ 630 return 1; 631} 632 633void 634wsdisplay_emul_attach(device_t parent, device_t self, void *aux) 635{ 636 struct wsdisplay_softc *sc = device_private(self); 637 struct wsemuldisplaydev_attach_args *ap = aux; 638 639 sc->sc_dev = self; 640 641 /* Don't allow more than one console to attach */ 642 if (wsdisplay_console_attached && ap->console) 643 ap->console = 0; 644 645 wsdisplay_common_attach(sc, ap->console, 646 device_cfdata(self)->cf_loc[WSEMULDISPLAYDEVCF_KBDMUX], 647 ap->scrdata, ap->accessops, ap->accesscookie); 648 649 if (ap->console) { 650 int maj; 651 652 /* locate the major number */ 653 maj = cdevsw_lookup_major(&wsdisplay_cdevsw); 654 655 cn_tab->cn_dev = makedev(maj, WSDISPLAYMINOR(device_unit(self), 656 0)); 657 } 658} 659 660/* Print function (for parent devices). */ 661int 662wsemuldisplaydevprint(void *aux, const char *pnp) 663{ 664#if 0 /* -Wunused */ 665 struct wsemuldisplaydev_attach_args *ap = aux; 666#endif 667 668 if (pnp) 669 aprint_normal("wsdisplay at %s", pnp); 670#if 0 /* don't bother; it's ugly */ 671 aprint_normal(" console %d", ap->console); 672#endif 673 674 return UNCONF; 675} 676 677int 678wsdisplay_emul_detach(device_t dev, int how) 679{ 680 struct wsdisplay_softc *sc = device_private(dev); 681 int flag, i, res; 682 683 flag = (how & DETACH_FORCE ? WSDISPLAY_DELSCR_FORCE : 0); 684 for (i = 0; i < WSDISPLAY_MAXSCREEN; i++) 685 if (sc->sc_scr[i]) { 686 res = wsdisplay_delscreen(sc, i, flag); 687 if (res) 688 return res; 689 } 690 691 cv_destroy(&sc->sc_flagscv); 692 mutex_destroy(&sc->sc_flagsmtx); 693 return 0; 694} 695 696int 697wsdisplay_noemul_match(device_t parent, cfdata_t match, void *aux) 698{ 699#if 0 /* -Wunused */ 700 struct wsdisplaydev_attach_args *ap = aux; 701#endif 702 703 /* Always match. */ 704 return 1; 705} 706 707void 708wsdisplay_noemul_attach(device_t parent, device_t self, void *aux) 709{ 710 struct wsdisplay_softc *sc = device_private(self); 711 struct wsdisplaydev_attach_args *ap = aux; 712 713 sc->sc_dev = self; 714 715 wsdisplay_common_attach(sc, 0, 716 device_cfdata(self)->cf_loc[WSDISPLAYDEVCF_KBDMUX], NULL, 717 ap->accessops, ap->accesscookie); 718} 719 720static void 721wsdisplay_swdone_cb(void *arg, int error, int waitok) 722{ 723 struct wsdisplay_softc *sc = arg; 724 725 mutex_enter(&sc->sc_flagsmtx); 726 KASSERT(sc->sc_flags & SC_SWITCHPENDING); 727 if (error) 728 sc->sc_flags |= SC_SWITCHERROR; 729 sc->sc_flags &= ~SC_SWITCHPENDING; 730 cv_signal(&sc->sc_flagscv); 731 mutex_exit(&sc->sc_flagsmtx); 732} 733 734static int 735wsdisplay_dosync(struct wsdisplay_softc *sc, int attach) 736{ 737 struct wsscreen *scr; 738 int (*op)(void *, int, void (*)(void *, int, int), void *); 739 int res; 740 741 scr = sc->sc_focus; 742 if (!scr || !scr->scr_syncops) 743 return 0; /* XXX check SCR_GRAPHICS? */ 744 745 sc->sc_flags |= SC_SWITCHPENDING; 746 sc->sc_flags &= ~SC_SWITCHERROR; 747 if (attach) 748 op = scr->scr_syncops->attach; 749 else 750 op = scr->scr_syncops->detach; 751 res = (*op)(scr->scr_synccookie, 1, wsdisplay_swdone_cb, sc); 752 if (res == EAGAIN) { 753 /* wait for callback */ 754 mutex_enter(&sc->sc_flagsmtx); 755 while (sc->sc_flags & SC_SWITCHPENDING) 756 cv_wait_sig(&sc->sc_flagscv, &sc->sc_flagsmtx); 757 mutex_exit(&sc->sc_flagsmtx); 758 if (sc->sc_flags & SC_SWITCHERROR) 759 return EIO; /* XXX pass real error */ 760 } else { 761 sc->sc_flags &= ~SC_SWITCHPENDING; 762 if (res) 763 return res; 764 } 765 if (attach) 766 sc->sc_flags |= SC_XATTACHED; 767 else 768 sc->sc_flags &= ~SC_XATTACHED; 769 return 0; 770} 771 772int 773wsdisplay_handlex(int resume) 774{ 775 int i, res; 776 device_t dv; 777 778 for (i = 0; i < wsdisplay_cd.cd_ndevs; i++) { 779 dv = device_lookup(&wsdisplay_cd, i); 780 if (!dv) 781 continue; 782 res = wsdisplay_dosync(device_private(dv), resume); 783 if (res) 784 return res; 785 } 786 return 0; 787} 788 789static bool 790wsdisplay_suspend(device_t dv, const pmf_qual_t *qual) 791{ 792 struct wsdisplay_softc *sc = device_private(dv); 793 struct wsscreen *scr = sc->sc_focus; 794 795 if (sc->sc_flags & SC_XATTACHED) { 796 KASSERT(scr); 797 KASSERT(scr->scr_syncops); 798 } 799 800#if 1 801 /* 802 * XXX X servers should have been detached earlier. 803 * pmf currently ignores our return value and suspends the system 804 * after device suspend failures. We try to avoid bigger damage 805 * and try to detach the X server here. This is not safe because 806 * other parts of the system which the X server deals with 807 * might already be suspended. 808 */ 809 if (sc->sc_flags & SC_XATTACHED) { 810 printf("%s: emergency X server detach\n", device_xname(dv)); 811 wsdisplay_dosync(sc, 0); 812 } 813#endif 814 return !(sc->sc_flags & SC_XATTACHED); 815} 816 817/* Print function (for parent devices). */ 818int 819wsdisplaydevprint(void *aux, const char *pnp) 820{ 821#if 0 /* -Wunused */ 822 struct wsdisplaydev_attach_args *ap = aux; 823#endif 824 825 if (pnp) 826 aprint_normal("wsdisplay at %s", pnp); 827 828 return UNCONF; 829} 830 831static void 832wsdisplay_common_attach(struct wsdisplay_softc *sc, int console, int kbdmux, 833 const struct wsscreen_list *scrdata, 834 const struct wsdisplay_accessops *accessops, 835 void *accesscookie) 836{ 837 int i, start=0; 838#if NWSKBD > 0 839 struct wsevsrc *kme; 840#if NWSMUX > 0 841 struct wsmux_softc *mux; 842 843 if (kbdmux >= 0) 844 mux = wsmux_getmux(kbdmux); 845 else 846 mux = wsmux_create("dmux", device_unit(sc->sc_dev)); 847 sc->sc_input = &mux->sc_base; 848 mux->sc_base.me_dispdv = sc->sc_dev; 849 aprint_normal(" kbdmux %d", kbdmux); 850#else 851 if (kbdmux >= 0) 852 aprint_normal(" (kbdmux ignored)"); 853#endif 854#endif 855 856 sc->sc_isconsole = console; 857 858 if (console) { 859 KASSERT(wsdisplay_console_initted); 860 KASSERT(wsdisplay_console_device == NULL); 861 862 sc->sc_scr[0] = wsscreen_attach(sc, 1, 0, 0, 0, 0, 0, 0); 863 wsdisplay_console_device = sc; 864 865 aprint_normal(": console (%s, %s emulation)", 866 wsdisplay_console_conf.scrdata->name, 867 wsdisplay_console_conf.wsemul->name); 868 869#if NWSKBD > 0 870 kme = wskbd_set_console_display(sc->sc_dev, sc->sc_input); 871 if (kme != NULL) 872 aprint_normal(", using %s", device_xname(kme->me_dv)); 873#if NWSMUX == 0 874 sc->sc_input = kme; 875#endif 876#endif 877 878 sc->sc_focusidx = 0; 879 sc->sc_focus = sc->sc_scr[0]; 880 start = 1; 881 882 wsdisplay_console_attached = 1; 883 } 884 aprint_normal("\n"); 885 aprint_naive("\n"); 886 887#if NWSKBD > 0 && NWSMUX > 0 888 wsmux_set_display(mux, sc->sc_dev); 889#endif 890 891 mutex_init(&sc->sc_flagsmtx, MUTEX_DEFAULT, IPL_NONE); 892 cv_init(&sc->sc_flagscv, "wssw"); 893 894 sc->sc_accessops = accessops; 895 sc->sc_accesscookie = accesscookie; 896 sc->sc_scrdata = scrdata; 897 898#ifdef WSDISPLAY_SCROLLSUPPORT 899 sc->sc_scroll_values = wsdisplay_default_scroll_values; 900#endif 901 902 /* 903 * Set up a number of virtual screens if wanted. The 904 * WSDISPLAYIO_ADDSCREEN ioctl is more flexible, so this code 905 * is for special cases like installation kernels. 906 */ 907 for (i = start; i < wsdisplay_defaultscreens; i++) { 908 if (wsdisplay_addscreen(sc, i, 0, 0)) 909 break; 910 } 911 912 if (i > start) 913 wsdisplay_addscreen_print(sc, start, i-start); 914 915 if (!pmf_device_register(sc->sc_dev, wsdisplay_suspend, NULL)) 916 aprint_error_dev(sc->sc_dev, 917 "couldn't establish power handler\n"); 918} 919 920void 921wsdisplay_cnattach(const struct wsscreen_descr *type, void *cookie, 922 int ccol, int crow, long defattr) 923{ 924 const struct wsemul_ops *wsemul; 925 926 KASSERT(wsdisplay_console_initted < 2); 927 KASSERT(type->nrows > 0); 928 KASSERT(type->ncols > 0); 929 KASSERT(crow < type->nrows); 930 KASSERT(ccol < type->ncols); 931 932 wsdisplay_console_conf.emulops = type->textops; 933 wsdisplay_console_conf.emulcookie = cookie; 934 wsdisplay_console_conf.scrdata = type; 935 936 wsemul = wsemul_pick(0); /* default */ 937 wsdisplay_console_conf.wsemul = wsemul; 938 wsdisplay_console_conf.wsemulcookie = (*wsemul->cnattach)(type, cookie, 939 ccol, crow, 940 defattr); 941 942 if (cn_tab != &wsdisplay_cons) 943 wsdisplay_ocn = cn_tab; 944 945 if (wsdisplay_ocn != NULL && wsdisplay_ocn->cn_halt != NULL) 946 wsdisplay_ocn->cn_halt(wsdisplay_ocn->cn_dev); 947 948 cn_tab = &wsdisplay_cons; 949 wsdisplay_console_initted = 2; 950} 951 952void 953wsdisplay_preattach(const struct wsscreen_descr *type, void *cookie, 954 int ccol, int crow, long defattr) 955{ 956 const struct wsemul_ops *wsemul; 957 958 KASSERT(!wsdisplay_console_initted); 959 KASSERT(type->nrows > 0); 960 KASSERT(type->ncols > 0); 961 KASSERT(crow < type->nrows); 962 KASSERT(ccol < type->ncols); 963 964 wsdisplay_console_conf.emulops = type->textops; 965 wsdisplay_console_conf.emulcookie = cookie; 966 wsdisplay_console_conf.scrdata = type; 967 968 wsemul = wsemul_pick(0); /* default */ 969 wsdisplay_console_conf.wsemul = wsemul; 970 wsdisplay_console_conf.wsemulcookie = (*wsemul->cnattach)(type, cookie, 971 ccol, crow, 972 defattr); 973 974 if (cn_tab != &wsdisplay_cons) 975 wsdisplay_ocn = cn_tab; 976 977 if (wsdisplay_ocn != NULL && wsdisplay_ocn->cn_halt != NULL) 978 wsdisplay_ocn->cn_halt(wsdisplay_ocn->cn_dev); 979 980 cn_tab = &wsdisplay_cons; 981 wsdisplay_console_initted = 1; 982} 983 984void 985wsdisplay_predetach(void) 986{ 987 KASSERT(wsdisplay_console_initted == 1); 988 989 cn_tab = wsdisplay_ocn; 990 wsdisplay_console_initted = 0; 991} 992 993void 994wsdisplay_cndetach(void) 995{ 996 KASSERT(wsdisplay_console_initted == 2); 997 998 cn_tab = wsdisplay_ocn; 999 wsdisplay_console_initted = 0; 1000} 1001 1002/* 1003 * Tty and cdevsw functions. 1004 */ 1005int 1006wsdisplayopen(dev_t dev, int flag, int mode, struct lwp *l) 1007{ 1008 struct wsdisplay_softc *sc; 1009 struct tty *tp; 1010 int newopen, error; 1011 struct wsscreen *scr; 1012 1013 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1014 if (sc == NULL) /* make sure it was attached */ 1015 return ENXIO; 1016 1017 if (ISWSDISPLAYSTAT(dev)) { 1018 wsevent_init(&sc->evar, l->l_proc); 1019 return 0; 1020 } 1021 1022 if (ISWSDISPLAYCTL(dev)) 1023 return 0; 1024 1025 if (WSDISPLAYSCREEN(dev) >= WSDISPLAY_MAXSCREEN) 1026 return ENXIO; 1027 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1028 return ENXIO; 1029 1030 if (WSSCREEN_HAS_TTY(scr)) { 1031 tp = scr->scr_tty; 1032 tp->t_oproc = wsdisplaystart; 1033 tp->t_param = wsdisplayparam; 1034 tp->t_dev = dev; 1035 newopen = (tp->t_state & TS_ISOPEN) == 0; 1036 1037 if (kauth_authorize_device_tty(l->l_cred, 1038 KAUTH_DEVICE_TTY_OPEN, tp)) 1039 return EBUSY; 1040 1041 if (newopen) { 1042 ttychars(tp); 1043 tp->t_iflag = TTYDEF_IFLAG; 1044 tp->t_oflag = TTYDEF_OFLAG; 1045 tp->t_cflag = TTYDEF_CFLAG; 1046 tp->t_lflag = TTYDEF_LFLAG; 1047 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 1048 wsdisplayparam(tp, &tp->t_termios); 1049 ttsetwater(tp); 1050 } 1051 tp->t_state |= TS_CARR_ON; 1052 1053 error = ((*tp->t_linesw->l_open)(dev, tp)); 1054 if (error) 1055 return error; 1056 1057 if (newopen && WSSCREEN_HAS_EMULATOR(scr)) { 1058 /* set window sizes as appropriate, and reset 1059 the emulation */ 1060 tp->t_winsize.ws_row = scr->scr_dconf->scrdata->nrows; 1061 tp->t_winsize.ws_col = scr->scr_dconf->scrdata->ncols; 1062 1063 /* wsdisplay_set_emulation() */ 1064 } 1065 } 1066 1067 scr->scr_flags |= SCR_OPEN; 1068 return 0; 1069} 1070 1071int 1072wsdisplayclose(dev_t dev, int flag, int mode, struct lwp *l) 1073{ 1074 device_t dv; 1075 struct wsdisplay_softc *sc; 1076 struct tty *tp; 1077 struct wsscreen *scr; 1078 1079 dv = device_lookup(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1080 sc = device_private(dv); 1081 1082 if (ISWSDISPLAYSTAT(dev)) { 1083 wsevent_fini(&sc->evar); 1084 return 0; 1085 } 1086 1087 if (ISWSDISPLAYCTL(dev)) 1088 return 0; 1089 1090 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1091 return 0; 1092 1093 if (WSSCREEN_HAS_TTY(scr)) { 1094 if (scr->scr_hold_screen) { 1095 int s; 1096 1097 /* XXX RESET KEYBOARD LEDS, etc. */ 1098 s = spltty(); /* avoid conflict with keyboard */ 1099 wsdisplay_kbdholdscreen(dv, 0); 1100 splx(s); 1101 } 1102 tp = scr->scr_tty; 1103 (*tp->t_linesw->l_close)(tp, flag); 1104 ttyclose(tp); 1105 } 1106 1107 if (scr->scr_syncops) 1108 (*scr->scr_syncops->destroy)(scr->scr_synccookie); 1109 1110 if (WSSCREEN_HAS_EMULATOR(scr)) { 1111 scr->scr_flags &= ~SCR_GRAPHICS; 1112 (*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie, 1113 WSEMUL_RESET); 1114 if (wsdisplay_clearonclose) 1115 (*scr->scr_dconf->wsemul->reset) 1116 (scr->scr_dconf->wsemulcookie, 1117 WSEMUL_CLEARSCREEN); 1118 } 1119 1120#ifdef WSDISPLAY_COMPAT_RAWKBD 1121 if (scr->scr_rawkbd) { 1122 int kbmode = WSKBD_TRANSLATED; 1123 (void)wsdisplay_internal_ioctl(sc, scr, WSKBDIO_SETMODE, 1124 (void *)&kbmode, 0, l); 1125 } 1126#endif 1127 1128 scr->scr_flags &= ~SCR_OPEN; 1129 1130 return 0; 1131} 1132 1133int 1134wsdisplayread(dev_t dev, struct uio *uio, int flag) 1135{ 1136 struct wsdisplay_softc *sc; 1137 struct tty *tp; 1138 struct wsscreen *scr; 1139 int error; 1140 1141 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1142 1143 if (ISWSDISPLAYSTAT(dev)) { 1144 error = wsevent_read(&sc->evar, uio, flag); 1145 return error; 1146 } 1147 1148 if (ISWSDISPLAYCTL(dev)) 1149 return 0; 1150 1151 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1152 return ENXIO; 1153 1154 if (!WSSCREEN_HAS_TTY(scr)) 1155 return ENODEV; 1156 1157 tp = scr->scr_tty; 1158 return (*tp->t_linesw->l_read)(tp, uio, flag); 1159} 1160 1161int 1162wsdisplaywrite(dev_t dev, struct uio *uio, int flag) 1163{ 1164 struct wsdisplay_softc *sc; 1165 struct tty *tp; 1166 struct wsscreen *scr; 1167 1168 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1169 1170 if (ISWSDISPLAYSTAT(dev)) { 1171 return 0; 1172 } 1173 1174 if (ISWSDISPLAYCTL(dev)) 1175 return 0; 1176 1177 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1178 return ENXIO; 1179 1180 if (!WSSCREEN_HAS_TTY(scr)) 1181 return ENODEV; 1182 1183 tp = scr->scr_tty; 1184 return (*tp->t_linesw->l_write)(tp, uio, flag); 1185} 1186 1187int 1188wsdisplaypoll(dev_t dev, int events, struct lwp *l) 1189{ 1190 struct wsdisplay_softc *sc; 1191 struct tty *tp; 1192 struct wsscreen *scr; 1193 1194 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1195 1196 if (ISWSDISPLAYSTAT(dev)) 1197 return wsevent_poll(&sc->evar, events, l); 1198 1199 if (ISWSDISPLAYCTL(dev)) 1200 return 0; 1201 1202 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1203 return POLLHUP; 1204 1205 if (!WSSCREEN_HAS_TTY(scr)) 1206 return POLLERR; 1207 1208 tp = scr->scr_tty; 1209 return (*tp->t_linesw->l_poll)(tp, events, l); 1210} 1211 1212int 1213wsdisplaykqfilter(dev_t dev, struct knote *kn) 1214{ 1215 struct wsdisplay_softc *sc; 1216 struct wsscreen *scr; 1217 1218 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1219 1220 if (ISWSDISPLAYCTL(dev)) 1221 return 1; 1222 1223 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1224 return 1; 1225 1226 1227 if (WSSCREEN_HAS_TTY(scr)) 1228 return ttykqfilter(dev, kn); 1229 else 1230 return 1; 1231} 1232 1233struct tty * 1234wsdisplaytty(dev_t dev) 1235{ 1236 struct wsdisplay_softc *sc; 1237 struct wsscreen *scr; 1238 1239 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1240 1241 if (ISWSDISPLAYSTAT(dev)) 1242 panic("wsdisplaytty() on status device"); 1243 1244 if (ISWSDISPLAYCTL(dev)) 1245 panic("wsdisplaytty() on ctl device"); 1246 1247 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1248 return NULL; 1249 1250 return scr->scr_tty; 1251} 1252 1253int 1254wsdisplayioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 1255{ 1256 device_t dv; 1257 struct wsdisplay_softc *sc; 1258 struct tty *tp; 1259 int error; 1260 struct wsscreen *scr; 1261 1262 dv = device_lookup(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1263 sc = device_private(dv); 1264 1265#ifdef WSDISPLAY_COMPAT_USL 1266 error = wsdisplay_usl_ioctl1(dv, cmd, data, flag, l); 1267 if (error != EPASSTHROUGH) 1268 return error; 1269#endif 1270 1271 if (ISWSDISPLAYSTAT(dev)) 1272 return wsdisplay_stat_ioctl(sc, cmd, data, flag, l); 1273 1274 if (ISWSDISPLAYCTL(dev)) 1275 return wsdisplay_cfg_ioctl(sc, cmd, data, flag, l); 1276 1277 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1278 return ENXIO; 1279 1280 if (WSSCREEN_HAS_TTY(scr)) { 1281 tp = scr->scr_tty; 1282 1283 /* do the line discipline ioctls first */ 1284 error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l); 1285 if (error != EPASSTHROUGH) 1286 return error; 1287 1288 /* then the tty ioctls */ 1289 error = ttioctl(tp, cmd, data, flag, l); 1290 if (error != EPASSTHROUGH) 1291 return error; 1292 } 1293 1294#ifdef WSDISPLAY_COMPAT_USL 1295 error = wsdisplay_usl_ioctl2(sc, scr, cmd, data, flag, l); 1296 if (error != EPASSTHROUGH) 1297 return error; 1298#endif 1299 1300 return wsdisplay_internal_ioctl(sc, scr, cmd, data, flag, l); 1301} 1302 1303int 1304wsdisplay_param(device_t dv, u_long cmd, struct wsdisplay_param *dp) 1305{ 1306 struct wsdisplay_softc *sc = device_private(dv); 1307 return (*sc->sc_accessops->ioctl)(sc->sc_accesscookie, 1308 sc->sc_focus->scr_dconf->emulcookie, 1309 cmd, (void *)dp, 0, NULL); 1310} 1311 1312int 1313wsdisplay_internal_ioctl(struct wsdisplay_softc *sc, struct wsscreen *scr, 1314 u_long cmd, void *data, int flag, struct lwp *l) 1315{ 1316 int error; 1317 char namebuf[32]; 1318 struct wsdisplay_font fd; 1319#ifdef WSDISPLAY_SCROLLSUPPORT 1320 struct wsdisplay_scroll_data *ksdp, *usdp; 1321#endif 1322 1323#if NWSKBD > 0 1324 struct wsevsrc *inp; 1325 1326#ifdef WSDISPLAY_COMPAT_RAWKBD 1327 switch (cmd) { 1328 case WSKBDIO_SETMODE: 1329 scr->scr_rawkbd = (*(int *)data == WSKBD_RAW); 1330 return wsdisplay_update_rawkbd(sc, scr); 1331 case WSKBDIO_GETMODE: 1332 *(int *)data = (scr->scr_rawkbd ? 1333 WSKBD_RAW : WSKBD_TRANSLATED); 1334 return 0; 1335 } 1336#endif 1337 inp = sc->sc_input; 1338 if (inp == NULL) 1339 return ENXIO; 1340 error = wsevsrc_display_ioctl(inp, cmd, data, flag, l); 1341 if (error != EPASSTHROUGH) 1342 return error; 1343#endif /* NWSKBD > 0 */ 1344 1345 switch (cmd) { 1346 case WSDISPLAYIO_GMODE: 1347 if (scr->scr_flags & SCR_GRAPHICS) { 1348 if (scr->scr_flags & SCR_DUMBFB) 1349 *(u_int *)data = WSDISPLAYIO_MODE_DUMBFB; 1350 else 1351 *(u_int *)data = WSDISPLAYIO_MODE_MAPPED; 1352 } else 1353 *(u_int *)data = WSDISPLAYIO_MODE_EMUL; 1354 return 0; 1355 1356 case WSDISPLAYIO_SMODE: 1357#define d (*(int *)data) 1358 if (d != WSDISPLAYIO_MODE_EMUL && 1359 d != WSDISPLAYIO_MODE_MAPPED && 1360 d != WSDISPLAYIO_MODE_DUMBFB) 1361 return EINVAL; 1362 1363 if (WSSCREEN_HAS_EMULATOR(scr)) { 1364 scr->scr_flags &= ~SCR_GRAPHICS; 1365 if (d == WSDISPLAYIO_MODE_MAPPED || 1366 d == WSDISPLAYIO_MODE_DUMBFB) 1367 scr->scr_flags |= SCR_GRAPHICS | 1368 ((d == WSDISPLAYIO_MODE_DUMBFB) ? SCR_DUMBFB : 0); 1369 } else if (d == WSDISPLAYIO_MODE_EMUL) 1370 return EINVAL; 1371 1372 (void)(*sc->sc_accessops->ioctl)(sc->sc_accesscookie, 1373 scr->scr_dconf->emulcookie, cmd, data, flag, l); 1374 1375 return 0; 1376#undef d 1377 1378#ifdef WSDISPLAY_SCROLLSUPPORT 1379#define SETSCROLLLINES(dstp, srcp, dfltp) \ 1380 do { \ 1381 (dstp)->fastlines = ((srcp)->which & \ 1382 WSDISPLAY_SCROLL_DOFASTLINES) ? \ 1383 (srcp)->fastlines : (dfltp)->fastlines; \ 1384 (dstp)->slowlines = ((srcp)->which & \ 1385 WSDISPLAY_SCROLL_DOSLOWLINES) ? \ 1386 (srcp)->slowlines : (dfltp)->slowlines; \ 1387 (dstp)->which = WSDISPLAY_SCROLL_DOALL; \ 1388 } while (0) 1389 1390 1391 case WSDISPLAYIO_DSSCROLL: 1392 usdp = (struct wsdisplay_scroll_data *)data; 1393 ksdp = &sc->sc_scroll_values; 1394 SETSCROLLLINES(ksdp, usdp, ksdp); 1395 return 0; 1396 1397 case WSDISPLAYIO_DGSCROLL: 1398 usdp = (struct wsdisplay_scroll_data *)data; 1399 ksdp = &sc->sc_scroll_values; 1400 SETSCROLLLINES(usdp, ksdp, ksdp); 1401 return 0; 1402#else 1403 case WSDISPLAYIO_DSSCROLL: 1404 case WSDISPLAYIO_DGSCROLL: 1405 return ENODEV; 1406#endif 1407 1408 case WSDISPLAYIO_SFONT: 1409#define d ((struct wsdisplay_usefontdata *)data) 1410 if (!sc->sc_accessops->load_font) 1411 return EINVAL; 1412 if (d->name) { 1413 error = copyinstr(d->name, namebuf, sizeof(namebuf), 0); 1414 if (error) 1415 return error; 1416 fd.name = namebuf; 1417 } else 1418 fd.name = 0; 1419 fd.data = 0; 1420 error = (*sc->sc_accessops->load_font)(sc->sc_accesscookie, 1421 scr->scr_dconf->emulcookie, &fd); 1422 if (!error && WSSCREEN_HAS_EMULATOR(scr)) { 1423 (*scr->scr_dconf->wsemul->reset) 1424 (scr->scr_dconf->wsemulcookie, WSEMUL_SYNCFONT); 1425 if (scr->scr_dconf->wsemul->resize) { 1426 (*scr->scr_dconf->wsemul->resize) 1427 (scr->scr_dconf->wsemulcookie, 1428 scr->scr_dconf->scrdata); 1429 /* update the tty's size */ 1430 scr->scr_tty->t_winsize.ws_row = 1431 scr->scr_dconf->scrdata->nrows; 1432 scr->scr_tty->t_winsize.ws_col = 1433 scr->scr_dconf->scrdata->ncols; 1434 /* send SIGWINCH to the process group on our tty */ 1435 kpreempt_disable(); 1436 ttysig(scr->scr_tty, TTYSIG_PG1, SIGWINCH); 1437 kpreempt_enable(); 1438 } 1439 } 1440 return error; 1441#undef d 1442 1443#ifdef WSDISPLAY_CUSTOM_OUTPUT 1444 case WSDISPLAYIO_GMSGATTRS: 1445#define d ((struct wsdisplay_msgattrs *)data) 1446 (*scr->scr_dconf->wsemul->getmsgattrs) 1447 (scr->scr_dconf->wsemulcookie, d); 1448 return 0; 1449#undef d 1450 1451 case WSDISPLAYIO_SMSGATTRS: { 1452#define d ((struct wsdisplay_msgattrs *)data) 1453 int i; 1454 for (i = 0; i < WSDISPLAY_MAXSCREEN; i++) 1455 if (sc->sc_scr[i] != NULL) 1456 (*sc->sc_scr[i]->scr_dconf->wsemul->setmsgattrs) 1457 (sc->sc_scr[i]->scr_dconf->wsemulcookie, 1458 sc->sc_scr[i]->scr_dconf->scrdata, 1459 d); 1460 } 1461 return 0; 1462#undef d 1463#else 1464 case WSDISPLAYIO_GMSGATTRS: 1465 case WSDISPLAYIO_SMSGATTRS: 1466 return ENODEV; 1467#endif 1468 case WSDISPLAYIO_SETVERSION: 1469 return wsevent_setversion(&sc->evar, *(int *)data); 1470 } 1471 1472 /* check ioctls for display */ 1473 return (*sc->sc_accessops->ioctl)(sc->sc_accesscookie, 1474 scr->scr_dconf->emulcookie, cmd, data, flag, l); 1475} 1476 1477int 1478wsdisplay_stat_ioctl(struct wsdisplay_softc *sc, u_long cmd, void *data, 1479 int flag, struct lwp *l) 1480{ 1481 switch (cmd) { 1482 case WSDISPLAYIO_GETACTIVESCREEN: 1483 *(int*)data = wsdisplay_getactivescreen(sc); 1484 return 0; 1485 } 1486 1487 return EPASSTHROUGH; 1488} 1489 1490int 1491wsdisplay_cfg_ioctl(struct wsdisplay_softc *sc, u_long cmd, void *data, 1492 int flag, struct lwp *l) 1493{ 1494 int error; 1495 char *type, typebuf[16], *emul, emulbuf[16]; 1496 void *tbuf; 1497 u_int fontsz; 1498#if defined(COMPAT_14) && NWSKBD > 0 1499 struct wsmux_device wsmuxdata; 1500#endif 1501#if NWSKBD > 0 1502 struct wsevsrc *inp; 1503#endif 1504 1505 switch (cmd) { 1506 case WSDISPLAYIO_ADDSCREEN: 1507#define d ((struct wsdisplay_addscreendata *)data) 1508 if (d->screentype) { 1509 error = copyinstr(d->screentype, typebuf, 1510 sizeof(typebuf), 0); 1511 if (error) 1512 return error; 1513 type = typebuf; 1514 } else 1515 type = 0; 1516 if (d->emul) { 1517 error = copyinstr(d->emul, emulbuf, sizeof(emulbuf),0); 1518 if (error) 1519 return error; 1520 emul = emulbuf; 1521 } else 1522 emul = 0; 1523 1524 if ((error = wsdisplay_addscreen(sc, d->idx, type, emul)) == 0) 1525 wsdisplay_addscreen_print(sc, d->idx, 0); 1526 return error; 1527#undef d 1528 case WSDISPLAYIO_DELSCREEN: 1529#define d ((struct wsdisplay_delscreendata *)data) 1530 return wsdisplay_delscreen(sc, d->idx, d->flags); 1531#undef d 1532 case WSDISPLAYIO_LDFONT: 1533#define d ((struct wsdisplay_font *)data) 1534 if (!sc->sc_accessops->load_font) 1535 return EINVAL; 1536 if (d->name) { 1537 error = copyinstr(d->name, typebuf, sizeof(typebuf), 0); 1538 if (error) 1539 return error; 1540 d->name = typebuf; 1541 } else 1542 d->name = "loaded"; /* ??? */ 1543 fontsz = d->fontheight * d->stride * d->numchars; 1544 if (fontsz > WSDISPLAY_MAXFONTSZ) 1545 return EINVAL; 1546 1547 tbuf = malloc(fontsz, M_DEVBUF, M_WAITOK); 1548 error = copyin(d->data, tbuf, fontsz); 1549 if (error) { 1550 free(tbuf, M_DEVBUF); 1551 return error; 1552 } 1553 d->data = tbuf; 1554 error = 1555 (*sc->sc_accessops->load_font)(sc->sc_accesscookie, 0, d); 1556 free(tbuf, M_DEVBUF); 1557#undef d 1558 return error; 1559 1560#if NWSKBD > 0 1561#ifdef COMPAT_14 1562 case _O_WSDISPLAYIO_SETKEYBOARD: 1563#define d ((struct wsdisplay_kbddata *)data) 1564 inp = sc->sc_input; 1565 if (inp == NULL) 1566 return ENXIO; 1567 switch (d->op) { 1568 case _O_WSDISPLAY_KBD_ADD: 1569 if (d->idx == -1) { 1570 d->idx = wskbd_pickfree(); 1571 if (d->idx == -1) 1572 return ENXIO; 1573 } 1574 wsmuxdata.type = WSMUX_KBD; 1575 wsmuxdata.idx = d->idx; 1576 return wsevsrc_ioctl(inp, WSMUX_ADD_DEVICE, 1577 &wsmuxdata, flag, l); 1578 case _O_WSDISPLAY_KBD_DEL: 1579 wsmuxdata.type = WSMUX_KBD; 1580 wsmuxdata.idx = d->idx; 1581 return wsevsrc_ioctl(inp, WSMUX_REMOVE_DEVICE, 1582 &wsmuxdata, flag, l); 1583 default: 1584 return EINVAL; 1585 } 1586#undef d 1587#endif 1588 1589 case WSMUXIO_ADD_DEVICE: 1590#define d ((struct wsmux_device *)data) 1591 if (d->idx == -1 && d->type == WSMUX_KBD) 1592 d->idx = wskbd_pickfree(); 1593#undef d 1594 /* FALLTHROUGH */ 1595 case WSMUXIO_INJECTEVENT: 1596 case WSMUXIO_REMOVE_DEVICE: 1597 case WSMUXIO_LIST_DEVICES: 1598 inp = sc->sc_input; 1599 if (inp == NULL) 1600 return ENXIO; 1601 return wsevsrc_ioctl(inp, cmd, data, flag, l); 1602#endif /* NWSKBD > 0 */ 1603 1604 } 1605 return EPASSTHROUGH; 1606} 1607 1608int 1609wsdisplay_stat_inject(device_t dv, u_int type, int value) 1610{ 1611 struct wsdisplay_softc *sc = device_private(dv); 1612 struct wseventvar *evar; 1613 struct wscons_event event; 1614 1615 evar = &sc->evar; 1616 1617 if (evar == NULL) 1618 return 0; 1619 1620 if (evar->q == NULL) 1621 return 1; 1622 1623 event.type = type; 1624 event.value = value; 1625 if (wsevent_inject(evar, &event, 1) != 0) { 1626 log(LOG_WARNING, "wsdisplay: event queue overflow\n"); 1627 return 1; 1628 } 1629 1630 return 0; 1631} 1632 1633paddr_t 1634wsdisplaymmap(dev_t dev, off_t offset, int prot) 1635{ 1636 struct wsdisplay_softc *sc; 1637 struct wsscreen *scr; 1638 1639 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1640 1641 if (ISWSDISPLAYSTAT(dev)) 1642 return -1; 1643 1644 if (ISWSDISPLAYCTL(dev)) 1645 return -1; 1646 1647 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1648 return -1; 1649 1650 if (!(scr->scr_flags & SCR_GRAPHICS)) 1651 return -1; 1652 1653 /* pass mmap to display */ 1654 return (*sc->sc_accessops->mmap)(sc->sc_accesscookie, 1655 scr->scr_dconf->emulcookie, offset, prot); 1656} 1657 1658void 1659wsdisplaystart(struct tty *tp) 1660{ 1661 struct wsdisplay_softc *sc; 1662 struct wsscreen *scr; 1663 int s, n; 1664 u_char *tbuf; 1665 1666 s = spltty(); 1667 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { 1668 splx(s); 1669 return; 1670 } 1671 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(tp->t_dev)); 1672 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(tp->t_dev)]) == NULL) { 1673 splx(s); 1674 return; 1675 } 1676 1677 if (scr->scr_hold_screen) { 1678 tp->t_state |= TS_TIMEOUT; 1679 splx(s); 1680 return; 1681 } 1682 tp->t_state |= TS_BUSY; 1683 splx(s); 1684 1685#ifdef DIAGNOSTIC 1686 scr->scr_in_ttyoutput = 1; 1687#endif 1688 1689 /* 1690 * Drain output from ring buffer. 1691 * The output will normally be in one contiguous chunk, but when the 1692 * ring wraps, it will be in two pieces.. one at the end of the ring, 1693 * the other at the start. For performance, rather than loop here, 1694 * we output one chunk, see if there's another one, and if so, output 1695 * it too. 1696 */ 1697 1698 n = ndqb(&tp->t_outq, 0); 1699 tbuf = tp->t_outq.c_cf; 1700 1701 if (!(scr->scr_flags & SCR_GRAPHICS)) { 1702 KASSERT(WSSCREEN_HAS_EMULATOR(scr)); 1703 (*scr->scr_dconf->wsemul->output)(scr->scr_dconf->wsemulcookie, 1704 tbuf, n, 0); 1705#ifdef WSDISPLAY_MULTICONS 1706 if (wsdisplay_multicons_enable && 1707 scr->scr_dconf == &wsdisplay_console_conf && 1708 wsdisplay_ocn && wsdisplay_ocn->cn_putc) { 1709 for (int i = 0; i < n; i++) 1710 wsdisplay_ocn->cn_putc( 1711 wsdisplay_ocn->cn_dev, tbuf[i]); 1712 } 1713#endif 1714 } 1715 ndflush(&tp->t_outq, n); 1716 1717 if ((n = ndqb(&tp->t_outq, 0)) > 0) { 1718 tbuf = tp->t_outq.c_cf; 1719 1720 if (!(scr->scr_flags & SCR_GRAPHICS)) { 1721 KASSERT(WSSCREEN_HAS_EMULATOR(scr)); 1722 (*scr->scr_dconf->wsemul->output) 1723 (scr->scr_dconf->wsemulcookie, tbuf, n, 0); 1724 1725#ifdef WSDISPLAY_MULTICONS 1726 if (wsdisplay_multicons_enable && 1727 scr->scr_dconf == &wsdisplay_console_conf && 1728 wsdisplay_ocn && wsdisplay_ocn->cn_putc) { 1729 for (int i = 0; i < n; i++) 1730 wsdisplay_ocn->cn_putc( 1731 wsdisplay_ocn->cn_dev, tbuf[i]); 1732 } 1733#endif 1734 } 1735 ndflush(&tp->t_outq, n); 1736 } 1737 1738#ifdef DIAGNOSTIC 1739 scr->scr_in_ttyoutput = 0; 1740#endif 1741 1742 s = spltty(); 1743 tp->t_state &= ~TS_BUSY; 1744 /* Come back if there's more to do */ 1745 if (ttypull(tp)) { 1746 tp->t_state |= TS_TIMEOUT; 1747 callout_schedule(&tp->t_rstrt_ch, (hz > 128) ? (hz / 128) : 1); 1748 } 1749 splx(s); 1750} 1751 1752void 1753wsdisplaystop(struct tty *tp, int flag) 1754{ 1755 int s; 1756 1757 s = spltty(); 1758 if (ISSET(tp->t_state, TS_BUSY)) 1759 if (!ISSET(tp->t_state, TS_TTSTOP)) 1760 SET(tp->t_state, TS_FLUSH); 1761 splx(s); 1762} 1763 1764/* Set line parameters. */ 1765int 1766wsdisplayparam(struct tty *tp, struct termios *t) 1767{ 1768 1769 tp->t_ispeed = t->c_ispeed; 1770 tp->t_ospeed = t->c_ospeed; 1771 tp->t_cflag = t->c_cflag; 1772 return 0; 1773} 1774 1775/* 1776 * Callbacks for the emulation code. 1777 */ 1778void 1779wsdisplay_emulbell(void *v) 1780{ 1781 struct wsscreen *scr = v; 1782 1783 if (scr == NULL) /* console, before real attach */ 1784 return; 1785 1786 if (scr->scr_flags & SCR_GRAPHICS) /* can this happen? */ 1787 return; 1788 1789 (void) wsdisplay_internal_ioctl(scr->sc, scr, WSKBDIO_BELL, NULL, 1790 FWRITE, NULL); 1791} 1792 1793void 1794wsdisplay_emulinput(void *v, const u_char *data, u_int count) 1795{ 1796 struct wsscreen *scr = v; 1797 struct tty *tp; 1798 int (*ifcn)(int, struct tty *); 1799 1800 if (v == NULL) /* console, before real attach */ 1801 return; 1802 1803 if (scr->scr_flags & SCR_GRAPHICS) /* XXX can't happen */ 1804 return; 1805 if (!WSSCREEN_HAS_TTY(scr)) 1806 return; 1807 1808 tp = scr->scr_tty; 1809 1810 /* 1811 * XXX bad hack to work around locking problems in tty.c: 1812 * ttyinput() will try to lock again, causing deadlock. 1813 * We assume that wsdisplay_emulinput() can only be called 1814 * from within wsdisplaystart(), and thus the tty lock 1815 * is already held. Use an entry point which doesn't lock. 1816 */ 1817 KASSERT(scr->scr_in_ttyoutput); 1818 ifcn = tp->t_linesw->l_rint; 1819 if (ifcn == ttyinput) 1820 ifcn = ttyinput_wlock; 1821 1822 while (count-- > 0) 1823 (*ifcn)(*data++, tp); 1824} 1825 1826/* 1827 * Calls from the keyboard interface. 1828 */ 1829void 1830wsdisplay_kbdinput(device_t dv, keysym_t ks) 1831{ 1832 struct wsdisplay_softc *sc = device_private(dv); 1833 struct wsscreen *scr; 1834 const char *dp; 1835 int count; 1836 struct tty *tp; 1837 1838 KASSERT(sc != NULL); 1839 1840 scr = sc->sc_focus; 1841 1842 if (!scr || !WSSCREEN_HAS_TTY(scr)) 1843 return; 1844 1845 tp = scr->scr_tty; 1846 1847 if (KS_GROUP(ks) == KS_GROUP_Plain && KS_VALUE(ks) <= 0x7f) 1848 (*tp->t_linesw->l_rint)(KS_VALUE(ks), tp); 1849 else if (WSSCREEN_HAS_EMULATOR(scr)) { 1850 count = (*scr->scr_dconf->wsemul->translate) 1851 (scr->scr_dconf->wsemulcookie, ks, &dp); 1852 while (count-- > 0) 1853 (*tp->t_linesw->l_rint)((unsigned char)(*dp++), tp); 1854 } 1855} 1856 1857#if defined(WSDISPLAY_COMPAT_RAWKBD) 1858int 1859wsdisplay_update_rawkbd(struct wsdisplay_softc *sc, struct wsscreen *scr) 1860{ 1861#if NWSKBD > 0 1862 int s, raw, data, error; 1863 struct wsevsrc *inp; 1864 1865 s = spltty(); 1866 1867 raw = (scr ? scr->scr_rawkbd : 0); 1868 1869 if (scr != sc->sc_focus || 1870 sc->sc_rawkbd == raw) { 1871 splx(s); 1872 return 0; 1873 } 1874 1875 data = raw ? WSKBD_RAW : WSKBD_TRANSLATED; 1876 inp = sc->sc_input; 1877 if (inp == NULL) { 1878 splx(s); 1879 return ENXIO; 1880 } 1881 error = wsevsrc_display_ioctl(inp, WSKBDIO_SETMODE, &data, 0, 0); 1882 if (!error) 1883 sc->sc_rawkbd = raw; 1884 splx(s); 1885 return error; 1886#else 1887 return 0; 1888#endif 1889} 1890#endif 1891 1892static void 1893wsdisplay_switch3_cb(void *arg, int error, int waitok) 1894{ 1895 device_t dv = arg; 1896 1897 wsdisplay_switch3(dv, error, waitok); 1898} 1899 1900static int 1901wsdisplay_switch3(device_t dv, int error, int waitok) 1902{ 1903 struct wsdisplay_softc *sc = device_private(dv); 1904 int no; 1905 struct wsscreen *scr; 1906 1907 if (!(sc->sc_flags & SC_SWITCHPENDING)) { 1908 aprint_error_dev(dv, "wsdisplay_switch3: not switching\n"); 1909 return EINVAL; 1910 } 1911 1912 no = sc->sc_screenwanted; 1913 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 1914 panic("wsdisplay_switch3: invalid screen %d", no); 1915 scr = sc->sc_scr[no]; 1916 if (!scr) { 1917 aprint_error_dev(dv, 1918 "wsdisplay_switch3: screen %d disappeared\n", no); 1919 error = ENXIO; 1920 } 1921 1922 if (error) { 1923 /* try to recover, avoid recursion */ 1924 1925 if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) { 1926 aprint_error_dev(dv, "wsdisplay_switch3: giving up\n"); 1927 sc->sc_focus = 0; 1928#ifdef WSDISPLAY_COMPAT_RAWKBD 1929 wsdisplay_update_rawkbd(sc, 0); 1930#endif 1931 sc->sc_flags &= ~SC_SWITCHPENDING; 1932 return error; 1933 } 1934 1935 sc->sc_screenwanted = sc->sc_oldscreen; 1936 sc->sc_oldscreen = WSDISPLAY_NULLSCREEN; 1937 return wsdisplay_switch1(dv, 0, waitok); 1938 } 1939 1940 if (scr->scr_syncops && !error) 1941 sc->sc_flags |= SC_XATTACHED; 1942 1943 sc->sc_flags &= ~SC_SWITCHPENDING; 1944 1945 if (!error && (scr->scr_flags & SCR_WAITACTIVE)) 1946 wakeup(scr); 1947 return error; 1948} 1949 1950static void 1951wsdisplay_switch2_cb(void *arg, int error, int waitok) 1952{ 1953 device_t dv = arg; 1954 1955 wsdisplay_switch2(dv, error, waitok); 1956} 1957 1958static int 1959wsdisplay_switch2(device_t dv, int error, int waitok) 1960{ 1961 struct wsdisplay_softc *sc = device_private(dv); 1962 int no; 1963 struct wsscreen *scr; 1964 1965 if (!(sc->sc_flags & SC_SWITCHPENDING)) { 1966 aprint_error_dev(dv, "wsdisplay_switch2: not switching\n"); 1967 return EINVAL; 1968 } 1969 1970 no = sc->sc_screenwanted; 1971 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 1972 panic("wsdisplay_switch2: invalid screen %d", no); 1973 scr = sc->sc_scr[no]; 1974 if (!scr) { 1975 aprint_error_dev(dv, 1976 "wsdisplay_switch2: screen %d disappeared\n", no); 1977 error = ENXIO; 1978 } 1979 1980 if (error) { 1981 /* try to recover, avoid recursion */ 1982 1983 if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) { 1984 aprint_error_dev(dv, "wsdisplay_switch2: giving up\n"); 1985 sc->sc_focus = 0; 1986 sc->sc_flags &= ~SC_SWITCHPENDING; 1987 return error; 1988 } 1989 1990 sc->sc_screenwanted = sc->sc_oldscreen; 1991 sc->sc_oldscreen = WSDISPLAY_NULLSCREEN; 1992 return wsdisplay_switch1(dv, 0, waitok); 1993 } 1994 1995 sc->sc_focusidx = no; 1996 sc->sc_focus = scr; 1997 1998#ifdef WSDISPLAY_COMPAT_RAWKBD 1999 (void) wsdisplay_update_rawkbd(sc, scr); 2000#endif 2001 /* keyboard map??? */ 2002 2003 if (scr->scr_syncops && 2004 !(sc->sc_isconsole && wsdisplay_cons_pollmode)) { 2005 error = (*scr->scr_syncops->attach)(scr->scr_synccookie, waitok, 2006 wsdisplay_switch3_cb, dv); 2007 if (error == EAGAIN) { 2008 /* switch will be done asynchronously */ 2009 return 0; 2010 } 2011 } 2012 2013 return wsdisplay_switch3(dv, error, waitok); 2014} 2015 2016static void 2017wsdisplay_switch1_cb(void *arg, int error, int waitok) 2018{ 2019 device_t dv = arg; 2020 2021 wsdisplay_switch1(dv, error, waitok); 2022} 2023 2024static int 2025wsdisplay_switch1(device_t dv, int error, int waitok) 2026{ 2027 struct wsdisplay_softc *sc = device_private(dv); 2028 int no; 2029 struct wsscreen *scr; 2030 2031 if (!(sc->sc_flags & SC_SWITCHPENDING)) { 2032 aprint_error_dev(dv, "wsdisplay_switch1: not switching\n"); 2033 return EINVAL; 2034 } 2035 2036 no = sc->sc_screenwanted; 2037 if (no == WSDISPLAY_NULLSCREEN) { 2038 sc->sc_flags &= ~SC_SWITCHPENDING; 2039 if (!error) { 2040 sc->sc_flags &= ~SC_XATTACHED; 2041 sc->sc_focus = 0; 2042 } 2043 wakeup(sc); 2044 return error; 2045 } 2046 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 2047 panic("wsdisplay_switch1: invalid screen %d", no); 2048 scr = sc->sc_scr[no]; 2049 if (!scr) { 2050 aprint_error_dev(dv, 2051 "wsdisplay_switch1: screen %d disappeared\n", no); 2052 error = ENXIO; 2053 } 2054 2055 if (error) { 2056 sc->sc_flags &= ~SC_SWITCHPENDING; 2057 return error; 2058 } 2059 2060 sc->sc_flags &= ~SC_XATTACHED; 2061 2062 error = (*sc->sc_accessops->show_screen)(sc->sc_accesscookie, 2063 scr->scr_dconf->emulcookie, 2064 waitok, 2065 sc->sc_isconsole && wsdisplay_cons_pollmode ? 0 : wsdisplay_switch2_cb, dv); 2066 if (error == EAGAIN) { 2067 /* switch will be done asynchronously */ 2068 return 0; 2069 } 2070 2071 return wsdisplay_switch2(dv, error, waitok); 2072} 2073 2074int 2075wsdisplay_switch(device_t dv, int no, int waitok) 2076{ 2077 struct wsdisplay_softc *sc = device_private(dv); 2078 int s, res = 0; 2079 struct wsscreen *scr; 2080 2081 if (no != WSDISPLAY_NULLSCREEN) { 2082 if ((no < 0 || no >= WSDISPLAY_MAXSCREEN)) 2083 return EINVAL; 2084 if (sc->sc_scr[no] == NULL) 2085 return ENXIO; 2086 } 2087 2088 wsdisplay_stat_inject(dv, WSCONS_EVENT_SCREEN_SWITCH, no); 2089 2090 s = spltty(); 2091 2092 if ((sc->sc_focus && no == sc->sc_focusidx) || 2093 (sc->sc_focus == NULL && no == WSDISPLAY_NULLSCREEN)) { 2094 splx(s); 2095 return 0; 2096 } 2097 2098 if (sc->sc_flags & SC_SWITCHPENDING) { 2099 splx(s); 2100 return EBUSY; 2101 } 2102 2103 sc->sc_flags |= SC_SWITCHPENDING; 2104 sc->sc_screenwanted = no; 2105 2106 splx(s); 2107 2108 scr = sc->sc_focus; 2109 if (!scr) { 2110 sc->sc_oldscreen = WSDISPLAY_NULLSCREEN; 2111 return wsdisplay_switch1(dv, 0, waitok); 2112 } else 2113 sc->sc_oldscreen = sc->sc_focusidx; 2114 2115 if (scr->scr_syncops) { 2116 if (!(sc->sc_flags & SC_XATTACHED) || 2117 (sc->sc_isconsole && wsdisplay_cons_pollmode)) { 2118 /* nothing to do here */ 2119 return wsdisplay_switch1(dv, 0, waitok); 2120 } 2121 res = (*scr->scr_syncops->detach)(scr->scr_synccookie, waitok, 2122 wsdisplay_switch1_cb, dv); 2123 if (res == EAGAIN) { 2124 /* switch will be done asynchronously */ 2125 return 0; 2126 } 2127 } else if (scr->scr_flags & SCR_GRAPHICS) { 2128 /* no way to save state */ 2129 res = EBUSY; 2130 } 2131 2132 return wsdisplay_switch1(dv, res, waitok); 2133} 2134 2135void 2136wsdisplay_reset(device_t dv, enum wsdisplay_resetops op) 2137{ 2138 struct wsdisplay_softc *sc = device_private(dv); 2139 struct wsscreen *scr; 2140 2141 KASSERT(sc != NULL); 2142 scr = sc->sc_focus; 2143 2144 if (!scr) 2145 return; 2146 2147 switch (op) { 2148 case WSDISPLAY_RESETEMUL: 2149 if (!WSSCREEN_HAS_EMULATOR(scr)) 2150 break; 2151 (*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie, 2152 WSEMUL_RESET); 2153 break; 2154 case WSDISPLAY_RESETCLOSE: 2155 wsdisplay_closescreen(sc, scr); 2156 break; 2157 } 2158} 2159 2160 2161bool 2162wsdisplay_isconsole(struct wsdisplay_softc *sc) 2163{ 2164 return sc->sc_isconsole; 2165} 2166 2167/* 2168 * Interface for (external) VT switch / process synchronization code 2169 */ 2170int 2171wsscreen_attach_sync(struct wsscreen *scr, const struct wscons_syncops *ops, 2172 void *cookie) 2173{ 2174 if (scr->scr_syncops) { 2175 /* 2176 * The screen is already claimed. 2177 * Check if the owner is still alive. 2178 */ 2179 if ((*scr->scr_syncops->check)(scr->scr_synccookie)) 2180 return EBUSY; 2181 } 2182 scr->scr_syncops = ops; 2183 scr->scr_synccookie = cookie; 2184 if (scr == scr->sc->sc_focus) 2185 scr->sc->sc_flags |= SC_XATTACHED; 2186 return 0; 2187} 2188 2189int 2190wsscreen_detach_sync(struct wsscreen *scr) 2191{ 2192 if (!scr->scr_syncops) 2193 return EINVAL; 2194 scr->scr_syncops = 0; 2195 if (scr == scr->sc->sc_focus) 2196 scr->sc->sc_flags &= ~SC_XATTACHED; 2197 return 0; 2198} 2199 2200int 2201wsscreen_lookup_sync(struct wsscreen *scr, 2202 const struct wscons_syncops *ops, /* used as ID */ 2203 void **cookiep) 2204{ 2205 if (!scr->scr_syncops || ops != scr->scr_syncops) 2206 return EINVAL; 2207 *cookiep = scr->scr_synccookie; 2208 return 0; 2209} 2210 2211/* 2212 * Interface to virtual screen stuff 2213 */ 2214int 2215wsdisplay_maxscreenidx(struct wsdisplay_softc *sc) 2216{ 2217 return (WSDISPLAY_MAXSCREEN - 1); 2218} 2219 2220int 2221wsdisplay_screenstate(struct wsdisplay_softc *sc, int idx) 2222{ 2223 if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN) 2224 return EINVAL; 2225 if (!sc->sc_scr[idx]) 2226 return ENXIO; 2227 return ((sc->sc_scr[idx]->scr_flags & SCR_OPEN) ? EBUSY : 0); 2228} 2229 2230int 2231wsdisplay_getactivescreen(struct wsdisplay_softc *sc) 2232{ 2233 return (sc->sc_focus ? sc->sc_focusidx : WSDISPLAY_NULLSCREEN); 2234} 2235 2236int 2237wsscreen_switchwait(struct wsdisplay_softc *sc, int no) 2238{ 2239 struct wsscreen *scr; 2240 int s, res = 0; 2241 2242 if (no == WSDISPLAY_NULLSCREEN) { 2243 s = spltty(); 2244 while (sc->sc_focus && res == 0) { 2245 res = tsleep(sc, PCATCH, "wswait", 0); 2246 } 2247 splx(s); 2248 return res; 2249 } 2250 2251 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 2252 return ENXIO; 2253 scr = sc->sc_scr[no]; 2254 if (!scr) 2255 return ENXIO; 2256 2257 s = spltty(); 2258 if (scr != sc->sc_focus) { 2259 scr->scr_flags |= SCR_WAITACTIVE; 2260 res = tsleep(scr, PCATCH, "wswait", 0); 2261 if (scr != sc->sc_scr[no]) 2262 res = ENXIO; /* disappeared in the meantime */ 2263 else 2264 scr->scr_flags &= ~SCR_WAITACTIVE; 2265 } 2266 splx(s); 2267 return res; 2268} 2269 2270void 2271wsdisplay_kbdholdscreen(device_t dv, int hold) 2272{ 2273 struct wsdisplay_softc *sc = device_private(dv); 2274 struct wsscreen *scr; 2275 2276 scr = sc->sc_focus; 2277 2278 if (!scr) 2279 return; 2280 2281 if (hold) 2282 scr->scr_hold_screen = 1; 2283 else { 2284 scr->scr_hold_screen = 0; 2285 callout_schedule(&scr->scr_tty->t_rstrt_ch, 0); 2286 } 2287} 2288 2289#if NWSKBD > 0 2290void 2291wsdisplay_set_console_kbd(struct wsevsrc *src) 2292{ 2293 if (wsdisplay_console_device == NULL) { 2294 src->me_dispdv = NULL; 2295 return; 2296 } 2297#if NWSMUX > 0 2298 if (wsmux_attach_sc((struct wsmux_softc *) 2299 wsdisplay_console_device->sc_input, src)) { 2300 src->me_dispdv = NULL; 2301 return; 2302 } 2303#else 2304 wsdisplay_console_device->sc_input = src; 2305#endif 2306 src->me_dispdv = wsdisplay_console_device->sc_dev; 2307} 2308#endif /* NWSKBD > 0 */ 2309 2310/* 2311 * Console interface. 2312 */ 2313void 2314wsdisplay_cnputc(dev_t dev, int i) 2315{ 2316 struct wsscreen_internal *dc; 2317 u_char c = i; 2318 2319 if (!wsdisplay_console_initted) 2320 return; 2321 2322 if ((wsdisplay_console_device != NULL) && 2323 (wsdisplay_console_device->sc_scr[0] != NULL) && 2324 (wsdisplay_console_device->sc_scr[0]->scr_flags & SCR_GRAPHICS)) 2325 return; 2326 2327 dc = &wsdisplay_console_conf; 2328 (*dc->wsemul->output)(dc->wsemulcookie, &c, 1, 1); 2329 2330#ifdef WSDISPLAY_MULTICONS 2331 if (!wsdisplay_multicons_suspended && 2332 wsdisplay_multicons_enable && wsdisplay_ocn && wsdisplay_ocn->cn_putc) 2333 wsdisplay_ocn->cn_putc(wsdisplay_ocn->cn_dev, i); 2334#endif 2335} 2336 2337static int 2338wsdisplay_getc(dev_t dev) 2339{ 2340 int c; 2341 2342 if (wsdisplay_cons_kbd_getc) { 2343 c = wsdisplay_cons_kbd_getc(wsdisplay_cons.cn_dev); 2344 if (c >= 0) 2345 return c; 2346 } 2347 2348#ifdef WSDISPLAY_MULTICONS 2349 if (!wsdisplay_multicons_suspended && 2350 wsdisplay_multicons_enable && wsdisplay_ocn && wsdisplay_ocn->cn_getc) { 2351 c = wsdisplay_ocn->cn_getc(wsdisplay_ocn->cn_dev); 2352 if (c >= 0) 2353 return c; 2354 } 2355#endif 2356 return -1; 2357} 2358 2359static void 2360wsdisplay_pollc(dev_t dev, int on) 2361{ 2362 2363 wsdisplay_cons_pollmode = on; 2364 2365 /* notify to fb drivers */ 2366 if (wsdisplay_console_device != NULL && 2367 wsdisplay_console_device->sc_accessops->pollc != NULL) 2368 (*wsdisplay_console_device->sc_accessops->pollc) 2369 (wsdisplay_console_device->sc_accesscookie, on); 2370 2371 /* notify to kbd drivers */ 2372 if (wsdisplay_cons_kbd_pollc) 2373 (*wsdisplay_cons_kbd_pollc)(NODEV, on); 2374 2375#ifdef WSDISPLAY_MULTICONS 2376 /* notify to old console driver */ 2377 if (!wsdisplay_multicons_suspended && 2378 wsdisplay_multicons_enable && wsdisplay_ocn && wsdisplay_ocn->cn_pollc) 2379 wsdisplay_ocn->cn_pollc(wsdisplay_ocn->cn_dev, on); 2380#endif 2381} 2382 2383void 2384wsdisplay_set_cons_kbd(int (*get)(dev_t), void (*poll)(dev_t, int), 2385 void (*bell)(dev_t, u_int, u_int, u_int)) 2386{ 2387 wsdisplay_cons.cn_bell = bell; 2388 wsdisplay_cons_kbd_getc = get; 2389 wsdisplay_cons_kbd_pollc = poll; 2390} 2391 2392void 2393wsdisplay_unset_cons_kbd(void) 2394{ 2395 wsdisplay_cons.cn_bell = NULL; 2396 wsdisplay_cons_kbd_getc = NULL; 2397 wsdisplay_cons_kbd_pollc = NULL; 2398} 2399 2400#ifdef WSDISPLAY_MULTICONS 2401void 2402wsdisplay_multicons_suspend(bool suspend) 2403{ 2404 wsdisplay_multicons_suspended = suspend; 2405} 2406#endif 2407 2408#ifdef WSDISPLAY_MULTICONS 2409SYSCTL_SETUP(sysctl_hw_wsdisplay_setup, "sysctl hw.wsdisplay subtree setup") 2410{ 2411 const struct sysctlnode *wsdisplay_node; 2412 2413 if (sysctl_createv(clog, 0, NULL, &wsdisplay_node, 2414 CTLFLAG_PERMANENT, 2415 CTLTYPE_NODE, "wsdisplay", NULL, 2416 NULL, 0, NULL, 0, 2417 CTL_HW, CTL_CREATE, CTL_EOL) != 0) 2418 return; 2419 2420 sysctl_createv(clog, 0, NULL, NULL, 2421 CTLFLAG_READWRITE, 2422 CTLTYPE_BOOL, "multicons", 2423 SYSCTL_DESCR("Enable wsdisplay multicons"), 2424 NULL, 0, &wsdisplay_multicons_enable, 0, 2425 CTL_HW, wsdisplay_node->sysctl_num, CTL_CREATE, CTL_EOL); 2426} 2427#endif 2428