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