1 /* $NetBSD: wsdisplay.c,v 1.160 2019/12/06 07:12:39 maxv 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.160 2019/12/06 07:12:39 maxv 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 75 #ifdef WSDISPLAY_MULTICONS 76 static bool wsdisplay_multicons_enable = true; 77 static bool wsdisplay_multicons_suspended = false; 78 #endif 79 80 /* Console device before replaced by wsdisplay */ 81 static struct consdev *wsdisplay_ocn; 82 83 struct wsscreen_internal { 84 const struct wsdisplay_emulops *emulops; 85 void *emulcookie; 86 87 const struct wsscreen_descr *scrdata; 88 89 const struct wsemul_ops *wsemul; 90 void *wsemulcookie; 91 }; 92 93 struct wsscreen { 94 struct wsscreen_internal *scr_dconf; 95 96 struct tty *scr_tty; 97 int scr_hold_screen; /* hold tty output */ 98 99 int scr_flags; 100 #define SCR_OPEN 1 /* is it open? */ 101 #define SCR_WAITACTIVE 2 /* someone waiting on activation */ 102 #define SCR_GRAPHICS 4 /* graphics mode, no text (emulation) output */ 103 #define SCR_DUMBFB 8 /* in use as a dumb fb (iff SCR_GRAPHICS) */ 104 const struct wscons_syncops *scr_syncops; 105 void *scr_synccookie; 106 107 #ifdef WSDISPLAY_COMPAT_RAWKBD 108 int scr_rawkbd; 109 #endif 110 111 #ifdef WSDISPLAY_MULTICONS 112 callout_t scr_getc_ch; 113 #endif 114 115 struct wsdisplay_softc *sc; 116 117 #ifdef DIAGNOSTIC 118 /* XXX this is to support a hack in emulinput, see comment below */ 119 int scr_in_ttyoutput; 120 #endif 121 }; 122 123 static struct wsscreen *wsscreen_attach(struct wsdisplay_softc *, int, 124 const char *, 125 const struct wsscreen_descr *, void *, 126 int, int, long); 127 static void wsscreen_detach(struct wsscreen *); 128 static int wsdisplay_addscreen(struct wsdisplay_softc *, int, const char *, const char *); 129 static void wsdisplay_addscreen_print(struct wsdisplay_softc *, int, int); 130 static void wsdisplay_closescreen(struct wsdisplay_softc *, struct wsscreen *); 131 static int wsdisplay_delscreen(struct wsdisplay_softc *, int, int); 132 133 #define WSDISPLAY_MAXSCREEN 8 134 135 struct wsdisplay_softc { 136 device_t sc_dev; 137 138 const struct wsdisplay_accessops *sc_accessops; 139 void *sc_accesscookie; 140 141 const struct wsscreen_list *sc_scrdata; 142 #ifdef WSDISPLAY_SCROLLSUPPORT 143 struct wsdisplay_scroll_data sc_scroll_values; 144 #endif 145 146 struct wsscreen *sc_scr[WSDISPLAY_MAXSCREEN]; 147 int sc_focusidx; /* available only if sc_focus isn't null */ 148 struct wsscreen *sc_focus; 149 150 struct wseventvar evar; 151 152 int sc_isconsole; 153 154 int sc_flags; 155 #define SC_SWITCHPENDING 1 156 #define SC_SWITCHERROR 2 157 #define SC_XATTACHED 4 /* X server active */ 158 kmutex_t sc_flagsmtx; /* for flags, might also be used for focus */ 159 kcondvar_t sc_flagscv; 160 161 int sc_screenwanted, sc_oldscreen; /* valid with SC_SWITCHPENDING */ 162 163 #if NWSKBD > 0 164 struct wsevsrc *sc_input; 165 #ifdef WSDISPLAY_COMPAT_RAWKBD 166 int sc_rawkbd; 167 #endif 168 #endif /* NWSKBD > 0 */ 169 }; 170 171 #ifdef WSDISPLAY_SCROLLSUPPORT 172 173 struct wsdisplay_scroll_data wsdisplay_default_scroll_values = { 174 WSDISPLAY_SCROLL_DOALL, 175 25, 176 2, 177 }; 178 #endif 179 180 extern struct cfdriver wsdisplay_cd; 181 182 /* Autoconfiguration definitions. */ 183 static int wsdisplay_emul_match(device_t , cfdata_t, void *); 184 static void wsdisplay_emul_attach(device_t, device_t, void *); 185 static int wsdisplay_emul_detach(device_t, int); 186 static int wsdisplay_noemul_match(device_t, cfdata_t, void *); 187 static void wsdisplay_noemul_attach(device_t, device_t, void *); 188 static bool wsdisplay_suspend(device_t, const pmf_qual_t *); 189 190 CFATTACH_DECL_NEW(wsdisplay_emul, sizeof (struct wsdisplay_softc), 191 wsdisplay_emul_match, wsdisplay_emul_attach, wsdisplay_emul_detach, NULL); 192 193 CFATTACH_DECL_NEW(wsdisplay_noemul, sizeof (struct wsdisplay_softc), 194 wsdisplay_noemul_match, wsdisplay_noemul_attach, NULL, NULL); 195 196 dev_type_open(wsdisplayopen); 197 dev_type_close(wsdisplayclose); 198 dev_type_read(wsdisplayread); 199 dev_type_write(wsdisplaywrite); 200 dev_type_ioctl(wsdisplayioctl); 201 dev_type_stop(wsdisplaystop); 202 dev_type_tty(wsdisplaytty); 203 dev_type_poll(wsdisplaypoll); 204 dev_type_mmap(wsdisplaymmap); 205 dev_type_kqfilter(wsdisplaykqfilter); 206 207 const struct cdevsw wsdisplay_cdevsw = { 208 .d_open = wsdisplayopen, 209 .d_close = wsdisplayclose, 210 .d_read = wsdisplayread, 211 .d_write = wsdisplaywrite, 212 .d_ioctl = wsdisplayioctl, 213 .d_stop = wsdisplaystop, 214 .d_tty = wsdisplaytty, 215 .d_poll = wsdisplaypoll, 216 .d_mmap = wsdisplaymmap, 217 .d_kqfilter = wsdisplaykqfilter, 218 .d_discard = nodiscard, 219 .d_flag = D_TTY 220 }; 221 222 static void wsdisplaystart(struct tty *); 223 static int wsdisplayparam(struct tty *, struct termios *); 224 225 226 #define WSDISPLAYUNIT(dev) (minor(dev) >> 8) 227 #define WSDISPLAYSCREEN(dev) (minor(dev) & 0xff) 228 #define ISWSDISPLAYSTAT(dev) (WSDISPLAYSCREEN(dev) == 254) 229 #define ISWSDISPLAYCTL(dev) (WSDISPLAYSCREEN(dev) == 255) 230 #define WSDISPLAYMINOR(unit, screen) (((unit) << 8) | (screen)) 231 232 #define WSSCREEN_HAS_EMULATOR(scr) ((scr)->scr_dconf->wsemul != NULL) 233 #define WSSCREEN_HAS_TTY(scr) ((scr)->scr_tty != NULL) 234 235 static void wsdisplay_common_attach(struct wsdisplay_softc *sc, 236 int console, int kbdmux, const struct wsscreen_list *, 237 const struct wsdisplay_accessops *accessops, 238 void *accesscookie); 239 240 #ifdef WSDISPLAY_COMPAT_RAWKBD 241 int wsdisplay_update_rawkbd(struct wsdisplay_softc *, 242 struct wsscreen *); 243 #endif 244 245 static int wsdisplay_console_initted; 246 static int wsdisplay_console_attached; 247 static struct wsdisplay_softc *wsdisplay_console_device; 248 static struct wsscreen_internal wsdisplay_console_conf; 249 250 static int wsdisplay_getc(dev_t); 251 static void wsdisplay_pollc(dev_t, int); 252 253 static int wsdisplay_cons_pollmode; 254 static int (*wsdisplay_cons_kbd_getc)(dev_t); 255 static void (*wsdisplay_cons_kbd_pollc)(dev_t, int); 256 257 static struct consdev wsdisplay_cons = { 258 NULL, NULL, wsdisplay_getc, wsdisplay_cnputc, 259 wsdisplay_pollc, NULL, NULL, NULL, NODEV, CN_NORMAL 260 }; 261 262 #ifndef WSDISPLAY_DEFAULTSCREENS 263 # define WSDISPLAY_DEFAULTSCREENS 0 264 #endif 265 int wsdisplay_defaultscreens = WSDISPLAY_DEFAULTSCREENS; 266 267 static int wsdisplay_switch1(device_t, int, int); 268 static void wsdisplay_switch1_cb(void *, int, int); 269 static int wsdisplay_switch2(device_t, int, int); 270 static void wsdisplay_switch2_cb(void *, int, int); 271 static int wsdisplay_switch3(device_t, int, int); 272 static void wsdisplay_switch3_cb(void *, int, int); 273 274 static void wsdisplay_swdone_cb(void *, int, int); 275 static int wsdisplay_dosync(struct wsdisplay_softc *, int); 276 277 int wsdisplay_clearonclose; 278 279 #ifdef WSDISPLAY_MULTICONS 280 /* 281 * Replace cn_isconsole() so that we can enter DDB from old console. 282 */ 283 bool 284 wsdisplay_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 293 static void 294 wsscreen_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 314 static struct wsscreen * 315 wsscreen_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 376 static void 377 wsscreen_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 400 const struct wsscreen_descr * 401 wsdisplay_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 */ 422 static void 423 wsdisplay_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 436 static int 437 wsdisplay_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 497 static void 498 wsdisplay_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 525 void 526 wsdisplay_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 555 static int 556 wsdisplay_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 */ 611 int 612 wsdisplay_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 633 void 634 wsdisplay_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). */ 661 int 662 wsemuldisplaydevprint(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 677 int 678 wsdisplay_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 696 int 697 wsdisplay_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 707 void 708 wsdisplay_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 720 static void 721 wsdisplay_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 734 static int 735 wsdisplay_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 772 int 773 wsdisplay_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 789 static bool 790 wsdisplay_suspend(device_t dv, const pmf_qual_t *qual) 791 { 792 struct wsdisplay_softc *sc = device_private(dv); 793 #ifdef DIAGNOSTIC 794 struct wsscreen *scr = sc->sc_focus; 795 if (sc->sc_flags & SC_XATTACHED) { 796 KASSERT(scr && scr->scr_syncops); 797 } 798 #endif 799 #if 1 800 /* 801 * XXX X servers should have been detached earlier. 802 * pmf currently ignores our return value and suspends the system 803 * after device suspend failures. We try to avoid bigger damage 804 * and try to detach the X server here. This is not safe because 805 * other parts of the system which the X server deals with 806 * might already be suspended. 807 */ 808 if (sc->sc_flags & SC_XATTACHED) { 809 printf("%s: emergency X server detach\n", device_xname(dv)); 810 wsdisplay_dosync(sc, 0); 811 } 812 #endif 813 return (!(sc->sc_flags & SC_XATTACHED)); 814 } 815 816 /* Print function (for parent devices). */ 817 int 818 wsdisplaydevprint(void *aux, const char *pnp) 819 { 820 #if 0 /* -Wunused */ 821 struct wsdisplaydev_attach_args *ap = aux; 822 #endif 823 824 if (pnp) 825 aprint_normal("wsdisplay at %s", pnp); 826 827 return (UNCONF); 828 } 829 830 static void 831 wsdisplay_common_attach(struct wsdisplay_softc *sc, int console, int kbdmux, 832 const struct wsscreen_list *scrdata, 833 const struct wsdisplay_accessops *accessops, 834 void *accesscookie) 835 { 836 int i, start=0; 837 #if NWSKBD > 0 838 struct wsevsrc *kme; 839 #if NWSMUX > 0 840 struct wsmux_softc *mux; 841 842 if (kbdmux >= 0) 843 mux = wsmux_getmux(kbdmux); 844 else 845 mux = wsmux_create("dmux", device_unit(sc->sc_dev)); 846 sc->sc_input = &mux->sc_base; 847 mux->sc_base.me_dispdv = sc->sc_dev; 848 aprint_normal(" kbdmux %d", kbdmux); 849 #else 850 if (kbdmux >= 0) 851 aprint_normal(" (kbdmux ignored)"); 852 #endif 853 #endif 854 855 sc->sc_isconsole = console; 856 857 if (console) { 858 KASSERT(wsdisplay_console_initted); 859 KASSERT(wsdisplay_console_device == NULL); 860 861 sc->sc_scr[0] = wsscreen_attach(sc, 1, 0, 0, 0, 0, 0, 0); 862 wsdisplay_console_device = sc; 863 864 aprint_normal(": console (%s, %s emulation)", 865 wsdisplay_console_conf.scrdata->name, 866 wsdisplay_console_conf.wsemul->name); 867 868 #if NWSKBD > 0 869 kme = wskbd_set_console_display(sc->sc_dev, sc->sc_input); 870 if (kme != NULL) 871 aprint_normal(", using %s", device_xname(kme->me_dv)); 872 #if NWSMUX == 0 873 sc->sc_input = kme; 874 #endif 875 #endif 876 877 sc->sc_focusidx = 0; 878 sc->sc_focus = sc->sc_scr[0]; 879 start = 1; 880 881 wsdisplay_console_attached = 1; 882 } 883 aprint_normal("\n"); 884 aprint_naive("\n"); 885 886 #if NWSKBD > 0 && NWSMUX > 0 887 wsmux_set_display(mux, sc->sc_dev); 888 #endif 889 890 mutex_init(&sc->sc_flagsmtx, MUTEX_DEFAULT, IPL_NONE); 891 cv_init(&sc->sc_flagscv, "wssw"); 892 893 sc->sc_accessops = accessops; 894 sc->sc_accesscookie = accesscookie; 895 sc->sc_scrdata = scrdata; 896 897 #ifdef WSDISPLAY_SCROLLSUPPORT 898 sc->sc_scroll_values = wsdisplay_default_scroll_values; 899 #endif 900 901 /* 902 * Set up a number of virtual screens if wanted. The 903 * WSDISPLAYIO_ADDSCREEN ioctl is more flexible, so this code 904 * is for special cases like installation kernels. 905 */ 906 for (i = start; i < wsdisplay_defaultscreens; i++) { 907 if (wsdisplay_addscreen(sc, i, 0, 0)) 908 break; 909 } 910 911 if (i > start) 912 wsdisplay_addscreen_print(sc, start, i-start); 913 914 if (!pmf_device_register(sc->sc_dev, wsdisplay_suspend, NULL)) 915 aprint_error_dev(sc->sc_dev, 916 "couldn't establish power handler\n"); 917 } 918 919 void 920 wsdisplay_cnattach(const struct wsscreen_descr *type, void *cookie, 921 int ccol, int crow, long defattr) 922 { 923 const struct wsemul_ops *wsemul; 924 925 KASSERT(wsdisplay_console_initted < 2); 926 KASSERT(type->nrows > 0); 927 KASSERT(type->ncols > 0); 928 KASSERT(crow < type->nrows); 929 KASSERT(ccol < type->ncols); 930 931 wsdisplay_console_conf.emulops = type->textops; 932 wsdisplay_console_conf.emulcookie = cookie; 933 wsdisplay_console_conf.scrdata = type; 934 935 wsemul = wsemul_pick(0); /* default */ 936 wsdisplay_console_conf.wsemul = wsemul; 937 wsdisplay_console_conf.wsemulcookie = (*wsemul->cnattach)(type, cookie, 938 ccol, crow, 939 defattr); 940 941 if (cn_tab != &wsdisplay_cons) 942 wsdisplay_ocn = cn_tab; 943 944 if (wsdisplay_ocn != NULL && wsdisplay_ocn->cn_halt != NULL) 945 wsdisplay_ocn->cn_halt(wsdisplay_ocn->cn_dev); 946 947 cn_tab = &wsdisplay_cons; 948 wsdisplay_console_initted = 2; 949 } 950 951 void 952 wsdisplay_preattach(const struct wsscreen_descr *type, void *cookie, 953 int ccol, int crow, long defattr) 954 { 955 const struct wsemul_ops *wsemul; 956 957 KASSERT(!wsdisplay_console_initted); 958 KASSERT(type->nrows > 0); 959 KASSERT(type->ncols > 0); 960 KASSERT(crow < type->nrows); 961 KASSERT(ccol < type->ncols); 962 963 wsdisplay_console_conf.emulops = type->textops; 964 wsdisplay_console_conf.emulcookie = cookie; 965 wsdisplay_console_conf.scrdata = type; 966 967 wsemul = wsemul_pick(0); /* default */ 968 wsdisplay_console_conf.wsemul = wsemul; 969 wsdisplay_console_conf.wsemulcookie = (*wsemul->cnattach)(type, cookie, 970 ccol, crow, 971 defattr); 972 973 if (cn_tab != &wsdisplay_cons) 974 wsdisplay_ocn = cn_tab; 975 976 if (wsdisplay_ocn != NULL && wsdisplay_ocn->cn_halt != NULL) 977 wsdisplay_ocn->cn_halt(wsdisplay_ocn->cn_dev); 978 979 cn_tab = &wsdisplay_cons; 980 wsdisplay_console_initted = 1; 981 } 982 983 void 984 wsdisplay_cndetach(void) 985 { 986 KASSERT(wsdisplay_console_initted == 2); 987 988 cn_tab = wsdisplay_ocn; 989 wsdisplay_console_initted = 0; 990 } 991 992 /* 993 * Tty and cdevsw functions. 994 */ 995 int 996 wsdisplayopen(dev_t dev, int flag, int mode, struct lwp *l) 997 { 998 struct wsdisplay_softc *sc; 999 struct tty *tp; 1000 int newopen, error; 1001 struct wsscreen *scr; 1002 1003 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1004 if (sc == NULL) /* make sure it was attached */ 1005 return (ENXIO); 1006 1007 if (ISWSDISPLAYSTAT(dev)) { 1008 wsevent_init(&sc->evar, l->l_proc); 1009 return (0); 1010 } 1011 1012 if (ISWSDISPLAYCTL(dev)) 1013 return (0); 1014 1015 if (WSDISPLAYSCREEN(dev) >= WSDISPLAY_MAXSCREEN) 1016 return (ENXIO); 1017 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1018 return (ENXIO); 1019 1020 if (WSSCREEN_HAS_TTY(scr)) { 1021 tp = scr->scr_tty; 1022 tp->t_oproc = wsdisplaystart; 1023 tp->t_param = wsdisplayparam; 1024 tp->t_dev = dev; 1025 newopen = (tp->t_state & TS_ISOPEN) == 0; 1026 1027 if (kauth_authorize_device_tty(l->l_cred, 1028 KAUTH_DEVICE_TTY_OPEN, tp)) 1029 return (EBUSY); 1030 1031 if (newopen) { 1032 ttychars(tp); 1033 tp->t_iflag = TTYDEF_IFLAG; 1034 tp->t_oflag = TTYDEF_OFLAG; 1035 tp->t_cflag = TTYDEF_CFLAG; 1036 tp->t_lflag = TTYDEF_LFLAG; 1037 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 1038 wsdisplayparam(tp, &tp->t_termios); 1039 ttsetwater(tp); 1040 } 1041 tp->t_state |= TS_CARR_ON; 1042 1043 error = ((*tp->t_linesw->l_open)(dev, tp)); 1044 if (error) 1045 return (error); 1046 1047 if (newopen && WSSCREEN_HAS_EMULATOR(scr)) { 1048 /* set window sizes as appropriate, and reset 1049 the emulation */ 1050 tp->t_winsize.ws_row = scr->scr_dconf->scrdata->nrows; 1051 tp->t_winsize.ws_col = scr->scr_dconf->scrdata->ncols; 1052 1053 /* wsdisplay_set_emulation() */ 1054 } 1055 } 1056 1057 scr->scr_flags |= SCR_OPEN; 1058 return (0); 1059 } 1060 1061 int 1062 wsdisplayclose(dev_t dev, int flag, int mode, struct lwp *l) 1063 { 1064 device_t dv; 1065 struct wsdisplay_softc *sc; 1066 struct tty *tp; 1067 struct wsscreen *scr; 1068 1069 dv = device_lookup(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1070 sc = device_private(dv); 1071 1072 if (ISWSDISPLAYSTAT(dev)) { 1073 wsevent_fini(&sc->evar); 1074 return (0); 1075 } 1076 1077 if (ISWSDISPLAYCTL(dev)) 1078 return (0); 1079 1080 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1081 return (0); 1082 1083 if (WSSCREEN_HAS_TTY(scr)) { 1084 if (scr->scr_hold_screen) { 1085 int s; 1086 1087 /* XXX RESET KEYBOARD LEDS, etc. */ 1088 s = spltty(); /* avoid conflict with keyboard */ 1089 wsdisplay_kbdholdscreen(dv, 0); 1090 splx(s); 1091 } 1092 tp = scr->scr_tty; 1093 (*tp->t_linesw->l_close)(tp, flag); 1094 ttyclose(tp); 1095 } 1096 1097 if (scr->scr_syncops) 1098 (*scr->scr_syncops->destroy)(scr->scr_synccookie); 1099 1100 if (WSSCREEN_HAS_EMULATOR(scr)) { 1101 scr->scr_flags &= ~SCR_GRAPHICS; 1102 (*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie, 1103 WSEMUL_RESET); 1104 if (wsdisplay_clearonclose) 1105 (*scr->scr_dconf->wsemul->reset) 1106 (scr->scr_dconf->wsemulcookie, 1107 WSEMUL_CLEARSCREEN); 1108 } 1109 1110 #ifdef WSDISPLAY_COMPAT_RAWKBD 1111 if (scr->scr_rawkbd) { 1112 int kbmode = WSKBD_TRANSLATED; 1113 (void)wsdisplay_internal_ioctl(sc, scr, WSKBDIO_SETMODE, 1114 (void *)&kbmode, 0, l); 1115 } 1116 #endif 1117 1118 scr->scr_flags &= ~SCR_OPEN; 1119 1120 return (0); 1121 } 1122 1123 int 1124 wsdisplayread(dev_t dev, struct uio *uio, int flag) 1125 { 1126 struct wsdisplay_softc *sc; 1127 struct tty *tp; 1128 struct wsscreen *scr; 1129 int error; 1130 1131 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1132 1133 if (ISWSDISPLAYSTAT(dev)) { 1134 error = wsevent_read(&sc->evar, uio, flag); 1135 return (error); 1136 } 1137 1138 if (ISWSDISPLAYCTL(dev)) 1139 return (0); 1140 1141 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1142 return (ENXIO); 1143 1144 if (!WSSCREEN_HAS_TTY(scr)) 1145 return (ENODEV); 1146 1147 tp = scr->scr_tty; 1148 return ((*tp->t_linesw->l_read)(tp, uio, flag)); 1149 } 1150 1151 int 1152 wsdisplaywrite(dev_t dev, struct uio *uio, int flag) 1153 { 1154 struct wsdisplay_softc *sc; 1155 struct tty *tp; 1156 struct wsscreen *scr; 1157 1158 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1159 1160 if (ISWSDISPLAYSTAT(dev)) { 1161 return (0); 1162 } 1163 1164 if (ISWSDISPLAYCTL(dev)) 1165 return (0); 1166 1167 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1168 return (ENXIO); 1169 1170 if (!WSSCREEN_HAS_TTY(scr)) 1171 return (ENODEV); 1172 1173 tp = scr->scr_tty; 1174 return ((*tp->t_linesw->l_write)(tp, uio, flag)); 1175 } 1176 1177 int 1178 wsdisplaypoll(dev_t dev, int events, struct lwp *l) 1179 { 1180 struct wsdisplay_softc *sc; 1181 struct tty *tp; 1182 struct wsscreen *scr; 1183 1184 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1185 1186 if (ISWSDISPLAYSTAT(dev)) 1187 return (wsevent_poll(&sc->evar, events, l)); 1188 1189 if (ISWSDISPLAYCTL(dev)) 1190 return (0); 1191 1192 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1193 return (POLLHUP); 1194 1195 if (!WSSCREEN_HAS_TTY(scr)) 1196 return (POLLERR); 1197 1198 tp = scr->scr_tty; 1199 return ((*tp->t_linesw->l_poll)(tp, events, l)); 1200 } 1201 1202 int 1203 wsdisplaykqfilter(dev_t dev, struct knote *kn) 1204 { 1205 struct wsdisplay_softc *sc; 1206 struct wsscreen *scr; 1207 1208 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1209 1210 if (ISWSDISPLAYCTL(dev)) 1211 return (1); 1212 1213 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1214 return (1); 1215 1216 1217 if (WSSCREEN_HAS_TTY(scr)) 1218 return (ttykqfilter(dev, kn)); 1219 else 1220 return (1); 1221 } 1222 1223 struct tty * 1224 wsdisplaytty(dev_t dev) 1225 { 1226 struct wsdisplay_softc *sc; 1227 struct wsscreen *scr; 1228 1229 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1230 1231 if (ISWSDISPLAYSTAT(dev)) 1232 panic("wsdisplaytty() on status device"); 1233 1234 if (ISWSDISPLAYCTL(dev)) 1235 panic("wsdisplaytty() on ctl device"); 1236 1237 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1238 return NULL; 1239 1240 return (scr->scr_tty); 1241 } 1242 1243 int 1244 wsdisplayioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 1245 { 1246 device_t dv; 1247 struct wsdisplay_softc *sc; 1248 struct tty *tp; 1249 int error; 1250 struct wsscreen *scr; 1251 1252 dv = device_lookup(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1253 sc = device_private(dv); 1254 1255 #ifdef WSDISPLAY_COMPAT_USL 1256 error = wsdisplay_usl_ioctl1(dv, cmd, data, flag, l); 1257 if (error != EPASSTHROUGH) 1258 return (error); 1259 #endif 1260 1261 if (ISWSDISPLAYSTAT(dev)) 1262 return (wsdisplay_stat_ioctl(sc, cmd, data, flag, l)); 1263 1264 if (ISWSDISPLAYCTL(dev)) 1265 return (wsdisplay_cfg_ioctl(sc, cmd, data, flag, l)); 1266 1267 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1268 return (ENXIO); 1269 1270 if (WSSCREEN_HAS_TTY(scr)) { 1271 tp = scr->scr_tty; 1272 1273 /* do the line discipline ioctls first */ 1274 error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l); 1275 if (error != EPASSTHROUGH) 1276 return (error); 1277 1278 /* then the tty ioctls */ 1279 error = ttioctl(tp, cmd, data, flag, l); 1280 if (error != EPASSTHROUGH) 1281 return (error); 1282 } 1283 1284 #ifdef WSDISPLAY_COMPAT_USL 1285 error = wsdisplay_usl_ioctl2(sc, scr, cmd, data, flag, l); 1286 if (error != EPASSTHROUGH) 1287 return (error); 1288 #endif 1289 1290 return (wsdisplay_internal_ioctl(sc, scr, cmd, data, flag, l)); 1291 } 1292 1293 int 1294 wsdisplay_param(device_t dv, u_long cmd, struct wsdisplay_param *dp) 1295 { 1296 struct wsdisplay_softc *sc = device_private(dv); 1297 return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, 1298 sc->sc_focus->scr_dconf->emulcookie, 1299 cmd, (void *)dp, 0, NULL)); 1300 } 1301 1302 int 1303 wsdisplay_internal_ioctl(struct wsdisplay_softc *sc, struct wsscreen *scr, 1304 u_long cmd, void *data, int flag, struct lwp *l) 1305 { 1306 int error; 1307 char namebuf[32]; 1308 struct wsdisplay_font fd; 1309 #ifdef WSDISPLAY_SCROLLSUPPORT 1310 struct wsdisplay_scroll_data *ksdp, *usdp; 1311 #endif 1312 1313 #if NWSKBD > 0 1314 struct wsevsrc *inp; 1315 1316 #ifdef WSDISPLAY_COMPAT_RAWKBD 1317 switch (cmd) { 1318 case WSKBDIO_SETMODE: 1319 scr->scr_rawkbd = (*(int *)data == WSKBD_RAW); 1320 return (wsdisplay_update_rawkbd(sc, scr)); 1321 case WSKBDIO_GETMODE: 1322 *(int *)data = (scr->scr_rawkbd ? 1323 WSKBD_RAW : WSKBD_TRANSLATED); 1324 return (0); 1325 } 1326 #endif 1327 inp = sc->sc_input; 1328 if (inp == NULL) 1329 return (ENXIO); 1330 error = wsevsrc_display_ioctl(inp, cmd, data, flag, l); 1331 if (error != EPASSTHROUGH) 1332 return (error); 1333 #endif /* NWSKBD > 0 */ 1334 1335 switch (cmd) { 1336 case WSDISPLAYIO_GMODE: 1337 if (scr->scr_flags & SCR_GRAPHICS) { 1338 if (scr->scr_flags & SCR_DUMBFB) 1339 *(u_int *)data = WSDISPLAYIO_MODE_DUMBFB; 1340 else 1341 *(u_int *)data = WSDISPLAYIO_MODE_MAPPED; 1342 } else 1343 *(u_int *)data = WSDISPLAYIO_MODE_EMUL; 1344 return (0); 1345 1346 case WSDISPLAYIO_SMODE: 1347 #define d (*(int *)data) 1348 if (d != WSDISPLAYIO_MODE_EMUL && 1349 d != WSDISPLAYIO_MODE_MAPPED && 1350 d != WSDISPLAYIO_MODE_DUMBFB) 1351 return (EINVAL); 1352 1353 if (WSSCREEN_HAS_EMULATOR(scr)) { 1354 scr->scr_flags &= ~SCR_GRAPHICS; 1355 if (d == WSDISPLAYIO_MODE_MAPPED || 1356 d == WSDISPLAYIO_MODE_DUMBFB) 1357 scr->scr_flags |= SCR_GRAPHICS | 1358 ((d == WSDISPLAYIO_MODE_DUMBFB) ? SCR_DUMBFB : 0); 1359 } else if (d == WSDISPLAYIO_MODE_EMUL) 1360 return (EINVAL); 1361 1362 (void)(*sc->sc_accessops->ioctl)(sc->sc_accesscookie, 1363 scr->scr_dconf->emulcookie, cmd, data, flag, l); 1364 1365 return (0); 1366 #undef d 1367 1368 #ifdef WSDISPLAY_SCROLLSUPPORT 1369 #define SETSCROLLLINES(dstp, srcp, dfltp) \ 1370 do { \ 1371 (dstp)->fastlines = ((srcp)->which & \ 1372 WSDISPLAY_SCROLL_DOFASTLINES) ? \ 1373 (srcp)->fastlines : (dfltp)->fastlines; \ 1374 (dstp)->slowlines = ((srcp)->which & \ 1375 WSDISPLAY_SCROLL_DOSLOWLINES) ? \ 1376 (srcp)->slowlines : (dfltp)->slowlines; \ 1377 (dstp)->which = WSDISPLAY_SCROLL_DOALL; \ 1378 } while (0) 1379 1380 1381 case WSDISPLAYIO_DSSCROLL: 1382 usdp = (struct wsdisplay_scroll_data *)data; 1383 ksdp = &sc->sc_scroll_values; 1384 SETSCROLLLINES(ksdp, usdp, ksdp); 1385 return (0); 1386 1387 case WSDISPLAYIO_DGSCROLL: 1388 usdp = (struct wsdisplay_scroll_data *)data; 1389 ksdp = &sc->sc_scroll_values; 1390 SETSCROLLLINES(usdp, ksdp, ksdp); 1391 return (0); 1392 #else 1393 case WSDISPLAYIO_DSSCROLL: 1394 case WSDISPLAYIO_DGSCROLL: 1395 return ENODEV; 1396 #endif 1397 1398 case WSDISPLAYIO_SFONT: 1399 #define d ((struct wsdisplay_usefontdata *)data) 1400 if (!sc->sc_accessops->load_font) 1401 return (EINVAL); 1402 if (d->name) { 1403 error = copyinstr(d->name, namebuf, sizeof(namebuf), 0); 1404 if (error) 1405 return (error); 1406 fd.name = namebuf; 1407 } else 1408 fd.name = 0; 1409 fd.data = 0; 1410 error = (*sc->sc_accessops->load_font)(sc->sc_accesscookie, 1411 scr->scr_dconf->emulcookie, &fd); 1412 if (!error && WSSCREEN_HAS_EMULATOR(scr)) { 1413 (*scr->scr_dconf->wsemul->reset) 1414 (scr->scr_dconf->wsemulcookie, WSEMUL_SYNCFONT); 1415 if (scr->scr_dconf->wsemul->resize) { 1416 (*scr->scr_dconf->wsemul->resize) 1417 (scr->scr_dconf->wsemulcookie, 1418 scr->scr_dconf->scrdata); 1419 /* update the tty's size */ 1420 scr->scr_tty->t_winsize.ws_row = 1421 scr->scr_dconf->scrdata->nrows; 1422 scr->scr_tty->t_winsize.ws_col = 1423 scr->scr_dconf->scrdata->ncols; 1424 /* send SIGWINCH to the process group on our tty */ 1425 kpreempt_disable(); 1426 ttysig(scr->scr_tty, TTYSIG_PG1, SIGWINCH); 1427 kpreempt_enable(); 1428 } 1429 } 1430 return (error); 1431 #undef d 1432 1433 #ifdef WSDISPLAY_CUSTOM_OUTPUT 1434 case WSDISPLAYIO_GMSGATTRS: 1435 #define d ((struct wsdisplay_msgattrs *)data) 1436 (*scr->scr_dconf->wsemul->getmsgattrs) 1437 (scr->scr_dconf->wsemulcookie, d); 1438 return (0); 1439 #undef d 1440 1441 case WSDISPLAYIO_SMSGATTRS: { 1442 #define d ((struct wsdisplay_msgattrs *)data) 1443 int i; 1444 for (i = 0; i < WSDISPLAY_MAXSCREEN; i++) 1445 if (sc->sc_scr[i] != NULL) 1446 (*sc->sc_scr[i]->scr_dconf->wsemul->setmsgattrs) 1447 (sc->sc_scr[i]->scr_dconf->wsemulcookie, 1448 sc->sc_scr[i]->scr_dconf->scrdata, 1449 d); 1450 } 1451 return (0); 1452 #undef d 1453 #else 1454 case WSDISPLAYIO_GMSGATTRS: 1455 case WSDISPLAYIO_SMSGATTRS: 1456 return (ENODEV); 1457 #endif 1458 case WSDISPLAYIO_SETVERSION: 1459 return wsevent_setversion(&sc->evar, *(int *)data); 1460 } 1461 1462 /* check ioctls for display */ 1463 return ((*sc->sc_accessops->ioctl)(sc->sc_accesscookie, 1464 scr->scr_dconf->emulcookie, cmd, data, flag, l)); 1465 } 1466 1467 int 1468 wsdisplay_stat_ioctl(struct wsdisplay_softc *sc, u_long cmd, void *data, 1469 int flag, struct lwp *l) 1470 { 1471 switch (cmd) { 1472 case WSDISPLAYIO_GETACTIVESCREEN: 1473 *(int*)data = wsdisplay_getactivescreen(sc); 1474 return (0); 1475 } 1476 1477 return (EPASSTHROUGH); 1478 } 1479 1480 int 1481 wsdisplay_cfg_ioctl(struct wsdisplay_softc *sc, u_long cmd, void *data, 1482 int flag, struct lwp *l) 1483 { 1484 int error; 1485 char *type, typebuf[16], *emul, emulbuf[16]; 1486 void *tbuf; 1487 u_int fontsz; 1488 #if defined(COMPAT_14) && NWSKBD > 0 1489 struct wsmux_device wsmuxdata; 1490 #endif 1491 #if NWSKBD > 0 1492 struct wsevsrc *inp; 1493 #endif 1494 1495 switch (cmd) { 1496 case WSDISPLAYIO_ADDSCREEN: 1497 #define d ((struct wsdisplay_addscreendata *)data) 1498 if (d->screentype) { 1499 error = copyinstr(d->screentype, typebuf, 1500 sizeof(typebuf), 0); 1501 if (error) 1502 return (error); 1503 type = typebuf; 1504 } else 1505 type = 0; 1506 if (d->emul) { 1507 error = copyinstr(d->emul, emulbuf, sizeof(emulbuf),0); 1508 if (error) 1509 return (error); 1510 emul = emulbuf; 1511 } else 1512 emul = 0; 1513 1514 if ((error = wsdisplay_addscreen(sc, d->idx, type, emul)) == 0) 1515 wsdisplay_addscreen_print(sc, d->idx, 0); 1516 return (error); 1517 #undef d 1518 case WSDISPLAYIO_DELSCREEN: 1519 #define d ((struct wsdisplay_delscreendata *)data) 1520 return (wsdisplay_delscreen(sc, d->idx, d->flags)); 1521 #undef d 1522 case WSDISPLAYIO_LDFONT: 1523 #define d ((struct wsdisplay_font *)data) 1524 if (!sc->sc_accessops->load_font) 1525 return (EINVAL); 1526 if (d->name) { 1527 error = copyinstr(d->name, typebuf, sizeof(typebuf), 0); 1528 if (error) 1529 return (error); 1530 d->name = typebuf; 1531 } else 1532 d->name = "loaded"; /* ??? */ 1533 fontsz = d->fontheight * d->stride * d->numchars; 1534 if (fontsz > WSDISPLAY_MAXFONTSZ) 1535 return (EINVAL); 1536 1537 tbuf = malloc(fontsz, M_DEVBUF, M_WAITOK); 1538 error = copyin(d->data, tbuf, fontsz); 1539 if (error) { 1540 free(tbuf, M_DEVBUF); 1541 return (error); 1542 } 1543 d->data = tbuf; 1544 error = 1545 (*sc->sc_accessops->load_font)(sc->sc_accesscookie, 0, d); 1546 free(tbuf, M_DEVBUF); 1547 #undef d 1548 return (error); 1549 1550 #if NWSKBD > 0 1551 #ifdef COMPAT_14 1552 case _O_WSDISPLAYIO_SETKEYBOARD: 1553 #define d ((struct wsdisplay_kbddata *)data) 1554 inp = sc->sc_input; 1555 if (inp == NULL) 1556 return (ENXIO); 1557 switch (d->op) { 1558 case _O_WSDISPLAY_KBD_ADD: 1559 if (d->idx == -1) { 1560 d->idx = wskbd_pickfree(); 1561 if (d->idx == -1) 1562 return (ENXIO); 1563 } 1564 wsmuxdata.type = WSMUX_KBD; 1565 wsmuxdata.idx = d->idx; 1566 return (wsevsrc_ioctl(inp, WSMUX_ADD_DEVICE, 1567 &wsmuxdata, flag, l)); 1568 case _O_WSDISPLAY_KBD_DEL: 1569 wsmuxdata.type = WSMUX_KBD; 1570 wsmuxdata.idx = d->idx; 1571 return (wsevsrc_ioctl(inp, WSMUX_REMOVE_DEVICE, 1572 &wsmuxdata, flag, l)); 1573 default: 1574 return (EINVAL); 1575 } 1576 #undef d 1577 #endif 1578 1579 case WSMUXIO_ADD_DEVICE: 1580 #define d ((struct wsmux_device *)data) 1581 if (d->idx == -1 && d->type == WSMUX_KBD) 1582 d->idx = wskbd_pickfree(); 1583 #undef d 1584 /* FALLTHROUGH */ 1585 case WSMUXIO_INJECTEVENT: 1586 case WSMUXIO_REMOVE_DEVICE: 1587 case WSMUXIO_LIST_DEVICES: 1588 inp = sc->sc_input; 1589 if (inp == NULL) 1590 return (ENXIO); 1591 return (wsevsrc_ioctl(inp, cmd, data, flag, l)); 1592 #endif /* NWSKBD > 0 */ 1593 1594 } 1595 return (EPASSTHROUGH); 1596 } 1597 1598 int 1599 wsdisplay_stat_inject(device_t dv, u_int type, int value) 1600 { 1601 struct wsdisplay_softc *sc = device_private(dv); 1602 struct wseventvar *evar; 1603 struct wscons_event event; 1604 1605 evar = &sc->evar; 1606 1607 if (evar == NULL) 1608 return (0); 1609 1610 if (evar->q == NULL) 1611 return (1); 1612 1613 event.type = type; 1614 event.value = value; 1615 if (wsevent_inject(evar, &event, 1) != 0) { 1616 log(LOG_WARNING, "wsdisplay: event queue overflow\n"); 1617 return (1); 1618 } 1619 1620 return (0); 1621 } 1622 1623 paddr_t 1624 wsdisplaymmap(dev_t dev, off_t offset, int prot) 1625 { 1626 struct wsdisplay_softc *sc; 1627 struct wsscreen *scr; 1628 1629 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(dev)); 1630 1631 if (ISWSDISPLAYSTAT(dev)) 1632 return (-1); 1633 1634 if (ISWSDISPLAYCTL(dev)) 1635 return (-1); 1636 1637 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(dev)]) == NULL) 1638 return (-1); 1639 1640 if (!(scr->scr_flags & SCR_GRAPHICS)) 1641 return (-1); 1642 1643 /* pass mmap to display */ 1644 return ((*sc->sc_accessops->mmap)(sc->sc_accesscookie, 1645 scr->scr_dconf->emulcookie, offset, prot)); 1646 } 1647 1648 void 1649 wsdisplaystart(struct tty *tp) 1650 { 1651 struct wsdisplay_softc *sc; 1652 struct wsscreen *scr; 1653 int s, n; 1654 u_char *tbuf; 1655 1656 s = spltty(); 1657 if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { 1658 splx(s); 1659 return; 1660 } 1661 sc = device_lookup_private(&wsdisplay_cd, WSDISPLAYUNIT(tp->t_dev)); 1662 if ((scr = sc->sc_scr[WSDISPLAYSCREEN(tp->t_dev)]) == NULL) { 1663 splx(s); 1664 return; 1665 } 1666 1667 if (scr->scr_hold_screen) { 1668 tp->t_state |= TS_TIMEOUT; 1669 splx(s); 1670 return; 1671 } 1672 tp->t_state |= TS_BUSY; 1673 splx(s); 1674 1675 #ifdef DIAGNOSTIC 1676 scr->scr_in_ttyoutput = 1; 1677 #endif 1678 1679 /* 1680 * Drain output from ring buffer. 1681 * The output will normally be in one contiguous chunk, but when the 1682 * ring wraps, it will be in two pieces.. one at the end of the ring, 1683 * the other at the start. For performance, rather than loop here, 1684 * we output one chunk, see if there's another one, and if so, output 1685 * it too. 1686 */ 1687 1688 n = ndqb(&tp->t_outq, 0); 1689 tbuf = tp->t_outq.c_cf; 1690 1691 if (!(scr->scr_flags & SCR_GRAPHICS)) { 1692 KASSERT(WSSCREEN_HAS_EMULATOR(scr)); 1693 (*scr->scr_dconf->wsemul->output)(scr->scr_dconf->wsemulcookie, 1694 tbuf, n, 0); 1695 #ifdef WSDISPLAY_MULTICONS 1696 if (wsdisplay_multicons_enable && 1697 scr->scr_dconf == &wsdisplay_console_conf && 1698 wsdisplay_ocn && wsdisplay_ocn->cn_putc) { 1699 for (int i = 0; i < n; i++) 1700 wsdisplay_ocn->cn_putc( 1701 wsdisplay_ocn->cn_dev, tbuf[i]); 1702 } 1703 #endif 1704 } 1705 ndflush(&tp->t_outq, n); 1706 1707 if ((n = ndqb(&tp->t_outq, 0)) > 0) { 1708 tbuf = tp->t_outq.c_cf; 1709 1710 if (!(scr->scr_flags & SCR_GRAPHICS)) { 1711 KASSERT(WSSCREEN_HAS_EMULATOR(scr)); 1712 (*scr->scr_dconf->wsemul->output) 1713 (scr->scr_dconf->wsemulcookie, tbuf, n, 0); 1714 1715 #ifdef WSDISPLAY_MULTICONS 1716 if (wsdisplay_multicons_enable && 1717 scr->scr_dconf == &wsdisplay_console_conf && 1718 wsdisplay_ocn && wsdisplay_ocn->cn_putc) { 1719 for (int i = 0; i < n; i++) 1720 wsdisplay_ocn->cn_putc( 1721 wsdisplay_ocn->cn_dev, tbuf[i]); 1722 } 1723 #endif 1724 } 1725 ndflush(&tp->t_outq, n); 1726 } 1727 1728 #ifdef DIAGNOSTIC 1729 scr->scr_in_ttyoutput = 0; 1730 #endif 1731 1732 s = spltty(); 1733 tp->t_state &= ~TS_BUSY; 1734 /* Come back if there's more to do */ 1735 if (ttypull(tp)) { 1736 tp->t_state |= TS_TIMEOUT; 1737 callout_schedule(&tp->t_rstrt_ch, (hz > 128) ? (hz / 128) : 1); 1738 } 1739 splx(s); 1740 } 1741 1742 void 1743 wsdisplaystop(struct tty *tp, int flag) 1744 { 1745 int s; 1746 1747 s = spltty(); 1748 if (ISSET(tp->t_state, TS_BUSY)) 1749 if (!ISSET(tp->t_state, TS_TTSTOP)) 1750 SET(tp->t_state, TS_FLUSH); 1751 splx(s); 1752 } 1753 1754 /* Set line parameters. */ 1755 int 1756 wsdisplayparam(struct tty *tp, struct termios *t) 1757 { 1758 1759 tp->t_ispeed = t->c_ispeed; 1760 tp->t_ospeed = t->c_ospeed; 1761 tp->t_cflag = t->c_cflag; 1762 return 0; 1763 } 1764 1765 /* 1766 * Callbacks for the emulation code. 1767 */ 1768 void 1769 wsdisplay_emulbell(void *v) 1770 { 1771 struct wsscreen *scr = v; 1772 1773 if (scr == NULL) /* console, before real attach */ 1774 return; 1775 1776 if (scr->scr_flags & SCR_GRAPHICS) /* can this happen? */ 1777 return; 1778 1779 (void) wsdisplay_internal_ioctl(scr->sc, scr, WSKBDIO_BELL, NULL, 1780 FWRITE, NULL); 1781 } 1782 1783 void 1784 wsdisplay_emulinput(void *v, const u_char *data, u_int count) 1785 { 1786 struct wsscreen *scr = v; 1787 struct tty *tp; 1788 int (*ifcn)(int, struct tty *); 1789 1790 if (v == NULL) /* console, before real attach */ 1791 return; 1792 1793 if (scr->scr_flags & SCR_GRAPHICS) /* XXX can't happen */ 1794 return; 1795 if (!WSSCREEN_HAS_TTY(scr)) 1796 return; 1797 1798 tp = scr->scr_tty; 1799 1800 /* 1801 * XXX bad hack to work around locking problems in tty.c: 1802 * ttyinput() will try to lock again, causing deadlock. 1803 * We assume that wsdisplay_emulinput() can only be called 1804 * from within wsdisplaystart(), and thus the tty lock 1805 * is already held. Use an entry point which doesn't lock. 1806 */ 1807 KASSERT(scr->scr_in_ttyoutput); 1808 ifcn = tp->t_linesw->l_rint; 1809 if (ifcn == ttyinput) 1810 ifcn = ttyinput_wlock; 1811 1812 while (count-- > 0) 1813 (*ifcn)(*data++, tp); 1814 } 1815 1816 /* 1817 * Calls from the keyboard interface. 1818 */ 1819 void 1820 wsdisplay_kbdinput(device_t dv, keysym_t ks) 1821 { 1822 struct wsdisplay_softc *sc = device_private(dv); 1823 struct wsscreen *scr; 1824 const char *dp; 1825 int count; 1826 struct tty *tp; 1827 1828 KASSERT(sc != NULL); 1829 1830 scr = sc->sc_focus; 1831 1832 if (!scr || !WSSCREEN_HAS_TTY(scr)) 1833 return; 1834 1835 tp = scr->scr_tty; 1836 1837 if (KS_GROUP(ks) == KS_GROUP_Plain && KS_VALUE(ks) <= 0x7f) 1838 (*tp->t_linesw->l_rint)(KS_VALUE(ks), tp); 1839 else if (WSSCREEN_HAS_EMULATOR(scr)) { 1840 count = (*scr->scr_dconf->wsemul->translate) 1841 (scr->scr_dconf->wsemulcookie, ks, &dp); 1842 while (count-- > 0) 1843 (*tp->t_linesw->l_rint)((unsigned char)(*dp++), tp); 1844 } 1845 } 1846 1847 #if defined(WSDISPLAY_COMPAT_RAWKBD) 1848 int 1849 wsdisplay_update_rawkbd(struct wsdisplay_softc *sc, struct wsscreen *scr) 1850 { 1851 #if NWSKBD > 0 1852 int s, raw, data, error; 1853 struct wsevsrc *inp; 1854 1855 s = spltty(); 1856 1857 raw = (scr ? scr->scr_rawkbd : 0); 1858 1859 if (scr != sc->sc_focus || 1860 sc->sc_rawkbd == raw) { 1861 splx(s); 1862 return (0); 1863 } 1864 1865 data = raw ? WSKBD_RAW : WSKBD_TRANSLATED; 1866 inp = sc->sc_input; 1867 if (inp == NULL) { 1868 splx(s); 1869 return (ENXIO); 1870 } 1871 error = wsevsrc_display_ioctl(inp, WSKBDIO_SETMODE, &data, 0, 0); 1872 if (!error) 1873 sc->sc_rawkbd = raw; 1874 splx(s); 1875 return (error); 1876 #else 1877 return (0); 1878 #endif 1879 } 1880 #endif 1881 1882 static void 1883 wsdisplay_switch3_cb(void *arg, int error, int waitok) 1884 { 1885 device_t dv = arg; 1886 1887 wsdisplay_switch3(dv, error, waitok); 1888 } 1889 1890 static int 1891 wsdisplay_switch3(device_t dv, int error, int waitok) 1892 { 1893 struct wsdisplay_softc *sc = device_private(dv); 1894 int no; 1895 struct wsscreen *scr; 1896 1897 if (!(sc->sc_flags & SC_SWITCHPENDING)) { 1898 aprint_error_dev(dv, "wsdisplay_switch3: not switching\n"); 1899 return (EINVAL); 1900 } 1901 1902 no = sc->sc_screenwanted; 1903 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 1904 panic("wsdisplay_switch3: invalid screen %d", no); 1905 scr = sc->sc_scr[no]; 1906 if (!scr) { 1907 aprint_error_dev(dv, 1908 "wsdisplay_switch3: screen %d disappeared\n", no); 1909 error = ENXIO; 1910 } 1911 1912 if (error) { 1913 /* try to recover, avoid recursion */ 1914 1915 if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) { 1916 aprint_error_dev(dv, "wsdisplay_switch3: giving up\n"); 1917 sc->sc_focus = 0; 1918 #ifdef WSDISPLAY_COMPAT_RAWKBD 1919 wsdisplay_update_rawkbd(sc, 0); 1920 #endif 1921 sc->sc_flags &= ~SC_SWITCHPENDING; 1922 return (error); 1923 } 1924 1925 sc->sc_screenwanted = sc->sc_oldscreen; 1926 sc->sc_oldscreen = WSDISPLAY_NULLSCREEN; 1927 return (wsdisplay_switch1(dv, 0, waitok)); 1928 } 1929 1930 if (scr->scr_syncops && !error) 1931 sc->sc_flags |= SC_XATTACHED; 1932 1933 sc->sc_flags &= ~SC_SWITCHPENDING; 1934 1935 if (!error && (scr->scr_flags & SCR_WAITACTIVE)) 1936 wakeup(scr); 1937 return (error); 1938 } 1939 1940 static void 1941 wsdisplay_switch2_cb(void *arg, int error, int waitok) 1942 { 1943 device_t dv = arg; 1944 1945 wsdisplay_switch2(dv, error, waitok); 1946 } 1947 1948 static int 1949 wsdisplay_switch2(device_t dv, int error, int waitok) 1950 { 1951 struct wsdisplay_softc *sc = device_private(dv); 1952 int no; 1953 struct wsscreen *scr; 1954 1955 if (!(sc->sc_flags & SC_SWITCHPENDING)) { 1956 aprint_error_dev(dv, "wsdisplay_switch2: not switching\n"); 1957 return (EINVAL); 1958 } 1959 1960 no = sc->sc_screenwanted; 1961 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 1962 panic("wsdisplay_switch2: invalid screen %d", no); 1963 scr = sc->sc_scr[no]; 1964 if (!scr) { 1965 aprint_error_dev(dv, 1966 "wsdisplay_switch2: screen %d disappeared\n", no); 1967 error = ENXIO; 1968 } 1969 1970 if (error) { 1971 /* try to recover, avoid recursion */ 1972 1973 if (sc->sc_oldscreen == WSDISPLAY_NULLSCREEN) { 1974 aprint_error_dev(dv, "wsdisplay_switch2: giving up\n"); 1975 sc->sc_focus = 0; 1976 sc->sc_flags &= ~SC_SWITCHPENDING; 1977 return (error); 1978 } 1979 1980 sc->sc_screenwanted = sc->sc_oldscreen; 1981 sc->sc_oldscreen = WSDISPLAY_NULLSCREEN; 1982 return (wsdisplay_switch1(dv, 0, waitok)); 1983 } 1984 1985 sc->sc_focusidx = no; 1986 sc->sc_focus = scr; 1987 1988 #ifdef WSDISPLAY_COMPAT_RAWKBD 1989 (void) wsdisplay_update_rawkbd(sc, scr); 1990 #endif 1991 /* keyboard map??? */ 1992 1993 if (scr->scr_syncops && 1994 !(sc->sc_isconsole && wsdisplay_cons_pollmode)) { 1995 error = (*scr->scr_syncops->attach)(scr->scr_synccookie, waitok, 1996 wsdisplay_switch3_cb, dv); 1997 if (error == EAGAIN) { 1998 /* switch will be done asynchronously */ 1999 return (0); 2000 } 2001 } 2002 2003 return (wsdisplay_switch3(dv, error, waitok)); 2004 } 2005 2006 static void 2007 wsdisplay_switch1_cb(void *arg, int error, int waitok) 2008 { 2009 device_t dv = arg; 2010 2011 wsdisplay_switch1(dv, error, waitok); 2012 } 2013 2014 static int 2015 wsdisplay_switch1(device_t dv, int error, int waitok) 2016 { 2017 struct wsdisplay_softc *sc = device_private(dv); 2018 int no; 2019 struct wsscreen *scr; 2020 2021 if (!(sc->sc_flags & SC_SWITCHPENDING)) { 2022 aprint_error_dev(dv, "wsdisplay_switch1: not switching\n"); 2023 return (EINVAL); 2024 } 2025 2026 no = sc->sc_screenwanted; 2027 if (no == WSDISPLAY_NULLSCREEN) { 2028 sc->sc_flags &= ~SC_SWITCHPENDING; 2029 if (!error) { 2030 sc->sc_flags &= ~SC_XATTACHED; 2031 sc->sc_focus = 0; 2032 } 2033 wakeup(sc); 2034 return (error); 2035 } 2036 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 2037 panic("wsdisplay_switch1: invalid screen %d", no); 2038 scr = sc->sc_scr[no]; 2039 if (!scr) { 2040 aprint_error_dev(dv, 2041 "wsdisplay_switch1: screen %d disappeared\n", no); 2042 error = ENXIO; 2043 } 2044 2045 if (error) { 2046 sc->sc_flags &= ~SC_SWITCHPENDING; 2047 return (error); 2048 } 2049 2050 sc->sc_flags &= ~SC_XATTACHED; 2051 2052 error = (*sc->sc_accessops->show_screen)(sc->sc_accesscookie, 2053 scr->scr_dconf->emulcookie, 2054 waitok, 2055 sc->sc_isconsole && wsdisplay_cons_pollmode ? 0 : wsdisplay_switch2_cb, dv); 2056 if (error == EAGAIN) { 2057 /* switch will be done asynchronously */ 2058 return (0); 2059 } 2060 2061 return (wsdisplay_switch2(dv, error, waitok)); 2062 } 2063 2064 int 2065 wsdisplay_switch(device_t dv, int no, int waitok) 2066 { 2067 struct wsdisplay_softc *sc = device_private(dv); 2068 int s, res = 0; 2069 struct wsscreen *scr; 2070 2071 if (no != WSDISPLAY_NULLSCREEN) { 2072 if ((no < 0 || no >= WSDISPLAY_MAXSCREEN)) 2073 return (EINVAL); 2074 if (sc->sc_scr[no] == NULL) 2075 return (ENXIO); 2076 } 2077 2078 wsdisplay_stat_inject(dv, WSCONS_EVENT_SCREEN_SWITCH, no); 2079 2080 s = spltty(); 2081 2082 if ((sc->sc_focus && no == sc->sc_focusidx) || 2083 (sc->sc_focus == NULL && no == WSDISPLAY_NULLSCREEN)) { 2084 splx(s); 2085 return (0); 2086 } 2087 2088 if (sc->sc_flags & SC_SWITCHPENDING) { 2089 splx(s); 2090 return (EBUSY); 2091 } 2092 2093 sc->sc_flags |= SC_SWITCHPENDING; 2094 sc->sc_screenwanted = no; 2095 2096 splx(s); 2097 2098 scr = sc->sc_focus; 2099 if (!scr) { 2100 sc->sc_oldscreen = WSDISPLAY_NULLSCREEN; 2101 return (wsdisplay_switch1(dv, 0, waitok)); 2102 } else 2103 sc->sc_oldscreen = sc->sc_focusidx; 2104 2105 if (scr->scr_syncops) { 2106 if (!(sc->sc_flags & SC_XATTACHED) || 2107 (sc->sc_isconsole && wsdisplay_cons_pollmode)) { 2108 /* nothing to do here */ 2109 return (wsdisplay_switch1(dv, 0, waitok)); 2110 } 2111 res = (*scr->scr_syncops->detach)(scr->scr_synccookie, waitok, 2112 wsdisplay_switch1_cb, dv); 2113 if (res == EAGAIN) { 2114 /* switch will be done asynchronously */ 2115 return (0); 2116 } 2117 } else if (scr->scr_flags & SCR_GRAPHICS) { 2118 /* no way to save state */ 2119 res = EBUSY; 2120 } 2121 2122 return (wsdisplay_switch1(dv, res, waitok)); 2123 } 2124 2125 void 2126 wsdisplay_reset(device_t dv, enum wsdisplay_resetops op) 2127 { 2128 struct wsdisplay_softc *sc = device_private(dv); 2129 struct wsscreen *scr; 2130 2131 KASSERT(sc != NULL); 2132 scr = sc->sc_focus; 2133 2134 if (!scr) 2135 return; 2136 2137 switch (op) { 2138 case WSDISPLAY_RESETEMUL: 2139 if (!WSSCREEN_HAS_EMULATOR(scr)) 2140 break; 2141 (*scr->scr_dconf->wsemul->reset)(scr->scr_dconf->wsemulcookie, 2142 WSEMUL_RESET); 2143 break; 2144 case WSDISPLAY_RESETCLOSE: 2145 wsdisplay_closescreen(sc, scr); 2146 break; 2147 } 2148 } 2149 2150 2151 bool 2152 wsdisplay_isconsole(struct wsdisplay_softc *sc) 2153 { 2154 return sc->sc_isconsole; 2155 } 2156 2157 /* 2158 * Interface for (external) VT switch / process synchronization code 2159 */ 2160 int 2161 wsscreen_attach_sync(struct wsscreen *scr, const struct wscons_syncops *ops, 2162 void *cookie) 2163 { 2164 if (scr->scr_syncops) { 2165 /* 2166 * The screen is already claimed. 2167 * Check if the owner is still alive. 2168 */ 2169 if ((*scr->scr_syncops->check)(scr->scr_synccookie)) 2170 return (EBUSY); 2171 } 2172 scr->scr_syncops = ops; 2173 scr->scr_synccookie = cookie; 2174 if (scr == scr->sc->sc_focus) 2175 scr->sc->sc_flags |= SC_XATTACHED; 2176 return (0); 2177 } 2178 2179 int 2180 wsscreen_detach_sync(struct wsscreen *scr) 2181 { 2182 if (!scr->scr_syncops) 2183 return (EINVAL); 2184 scr->scr_syncops = 0; 2185 if (scr == scr->sc->sc_focus) 2186 scr->sc->sc_flags &= ~SC_XATTACHED; 2187 return (0); 2188 } 2189 2190 int 2191 wsscreen_lookup_sync(struct wsscreen *scr, 2192 const struct wscons_syncops *ops, /* used as ID */ 2193 void **cookiep) 2194 { 2195 if (!scr->scr_syncops || ops != scr->scr_syncops) 2196 return (EINVAL); 2197 *cookiep = scr->scr_synccookie; 2198 return (0); 2199 } 2200 2201 /* 2202 * Interface to virtual screen stuff 2203 */ 2204 int 2205 wsdisplay_maxscreenidx(struct wsdisplay_softc *sc) 2206 { 2207 return (WSDISPLAY_MAXSCREEN - 1); 2208 } 2209 2210 int 2211 wsdisplay_screenstate(struct wsdisplay_softc *sc, int idx) 2212 { 2213 if (idx < 0 || idx >= WSDISPLAY_MAXSCREEN) 2214 return (EINVAL); 2215 if (!sc->sc_scr[idx]) 2216 return (ENXIO); 2217 return ((sc->sc_scr[idx]->scr_flags & SCR_OPEN) ? EBUSY : 0); 2218 } 2219 2220 int 2221 wsdisplay_getactivescreen(struct wsdisplay_softc *sc) 2222 { 2223 return (sc->sc_focus ? sc->sc_focusidx : WSDISPLAY_NULLSCREEN); 2224 } 2225 2226 int 2227 wsscreen_switchwait(struct wsdisplay_softc *sc, int no) 2228 { 2229 struct wsscreen *scr; 2230 int s, res = 0; 2231 2232 if (no == WSDISPLAY_NULLSCREEN) { 2233 s = spltty(); 2234 while (sc->sc_focus && res == 0) { 2235 res = tsleep(sc, PCATCH, "wswait", 0); 2236 } 2237 splx(s); 2238 return (res); 2239 } 2240 2241 if (no < 0 || no >= WSDISPLAY_MAXSCREEN) 2242 return (ENXIO); 2243 scr = sc->sc_scr[no]; 2244 if (!scr) 2245 return (ENXIO); 2246 2247 s = spltty(); 2248 if (scr != sc->sc_focus) { 2249 scr->scr_flags |= SCR_WAITACTIVE; 2250 res = tsleep(scr, PCATCH, "wswait", 0); 2251 if (scr != sc->sc_scr[no]) 2252 res = ENXIO; /* disappeared in the meantime */ 2253 else 2254 scr->scr_flags &= ~SCR_WAITACTIVE; 2255 } 2256 splx(s); 2257 return (res); 2258 } 2259 2260 void 2261 wsdisplay_kbdholdscreen(device_t dv, int hold) 2262 { 2263 struct wsdisplay_softc *sc = device_private(dv); 2264 struct wsscreen *scr; 2265 2266 scr = sc->sc_focus; 2267 2268 if (!scr) 2269 return; 2270 2271 if (hold) 2272 scr->scr_hold_screen = 1; 2273 else { 2274 scr->scr_hold_screen = 0; 2275 callout_schedule(&scr->scr_tty->t_rstrt_ch, 0); 2276 } 2277 } 2278 2279 #if NWSKBD > 0 2280 void 2281 wsdisplay_set_console_kbd(struct wsevsrc *src) 2282 { 2283 if (wsdisplay_console_device == NULL) { 2284 src->me_dispdv = NULL; 2285 return; 2286 } 2287 #if NWSMUX > 0 2288 if (wsmux_attach_sc((struct wsmux_softc *) 2289 wsdisplay_console_device->sc_input, src)) { 2290 src->me_dispdv = NULL; 2291 return; 2292 } 2293 #else 2294 wsdisplay_console_device->sc_input = src; 2295 #endif 2296 src->me_dispdv = wsdisplay_console_device->sc_dev; 2297 } 2298 #endif /* NWSKBD > 0 */ 2299 2300 /* 2301 * Console interface. 2302 */ 2303 void 2304 wsdisplay_cnputc(dev_t dev, int i) 2305 { 2306 struct wsscreen_internal *dc; 2307 u_char c = i; 2308 2309 if (!wsdisplay_console_initted) 2310 return; 2311 2312 if ((wsdisplay_console_device != NULL) && 2313 (wsdisplay_console_device->sc_scr[0] != NULL) && 2314 (wsdisplay_console_device->sc_scr[0]->scr_flags & SCR_GRAPHICS)) 2315 return; 2316 2317 dc = &wsdisplay_console_conf; 2318 (*dc->wsemul->output)(dc->wsemulcookie, &c, 1, 1); 2319 2320 #ifdef WSDISPLAY_MULTICONS 2321 if (!wsdisplay_multicons_suspended && 2322 wsdisplay_multicons_enable && wsdisplay_ocn && wsdisplay_ocn->cn_putc) 2323 wsdisplay_ocn->cn_putc(wsdisplay_ocn->cn_dev, i); 2324 #endif 2325 } 2326 2327 static int 2328 wsdisplay_getc(dev_t dev) 2329 { 2330 int c; 2331 2332 if (wsdisplay_cons_kbd_getc) { 2333 c = wsdisplay_cons_kbd_getc(wsdisplay_cons.cn_dev); 2334 if (c >= 0) 2335 return c; 2336 } 2337 2338 #ifdef WSDISPLAY_MULTICONS 2339 if (!wsdisplay_multicons_suspended && 2340 wsdisplay_multicons_enable && wsdisplay_ocn && wsdisplay_ocn->cn_getc) { 2341 c = wsdisplay_ocn->cn_getc(wsdisplay_ocn->cn_dev); 2342 if (c >= 0) 2343 return c; 2344 } 2345 #endif 2346 return -1; 2347 } 2348 2349 static void 2350 wsdisplay_pollc(dev_t dev, int on) 2351 { 2352 2353 wsdisplay_cons_pollmode = on; 2354 2355 /* notify to fb drivers */ 2356 if (wsdisplay_console_device != NULL && 2357 wsdisplay_console_device->sc_accessops->pollc != NULL) 2358 (*wsdisplay_console_device->sc_accessops->pollc) 2359 (wsdisplay_console_device->sc_accesscookie, on); 2360 2361 /* notify to kbd drivers */ 2362 if (wsdisplay_cons_kbd_pollc) 2363 (*wsdisplay_cons_kbd_pollc)(NODEV, on); 2364 2365 #ifdef WSDISPLAY_MULTICONS 2366 /* notify to old console driver */ 2367 if (!wsdisplay_multicons_suspended && 2368 wsdisplay_multicons_enable && wsdisplay_ocn && wsdisplay_ocn->cn_pollc) 2369 wsdisplay_ocn->cn_pollc(wsdisplay_ocn->cn_dev, on); 2370 #endif 2371 } 2372 2373 void 2374 wsdisplay_set_cons_kbd(int (*get)(dev_t), void (*poll)(dev_t, int), 2375 void (*bell)(dev_t, u_int, u_int, u_int)) 2376 { 2377 wsdisplay_cons.cn_bell = bell; 2378 wsdisplay_cons_kbd_getc = get; 2379 wsdisplay_cons_kbd_pollc = poll; 2380 } 2381 2382 void 2383 wsdisplay_unset_cons_kbd(void) 2384 { 2385 wsdisplay_cons.cn_bell = NULL; 2386 wsdisplay_cons_kbd_getc = NULL; 2387 wsdisplay_cons_kbd_pollc = NULL; 2388 } 2389 2390 #ifdef WSDISPLAY_MULTICONS 2391 void 2392 wsdisplay_multicons_suspend(bool suspend) 2393 { 2394 wsdisplay_multicons_suspended = suspend; 2395 } 2396 #endif 2397 2398 #ifdef WSDISPLAY_MULTICONS 2399 SYSCTL_SETUP(sysctl_hw_wsdisplay_setup, "sysctl hw.wsdisplay subtree setup") 2400 { 2401 const struct sysctlnode *wsdisplay_node; 2402 2403 if (sysctl_createv(clog, 0, NULL, &wsdisplay_node, 2404 CTLFLAG_PERMANENT, 2405 CTLTYPE_NODE, "wsdisplay", NULL, 2406 NULL, 0, NULL, 0, 2407 CTL_HW, CTL_CREATE, CTL_EOL) != 0) 2408 return; 2409 2410 sysctl_createv(clog, 0, NULL, NULL, 2411 CTLFLAG_READWRITE, 2412 CTLTYPE_BOOL, "multicons", 2413 SYSCTL_DESCR("Enable wsdisplay multicons"), 2414 NULL, 0, &wsdisplay_multicons_enable, 0, 2415 CTL_HW, wsdisplay_node->sysctl_num, CTL_CREATE, CTL_EOL); 2416 } 2417 #endif 2418