diff options
| author | Bruce Evans <bde@FreeBSD.org> | 2016-08-31 11:10:39 +0000 |
|---|---|---|
| committer | Bruce Evans <bde@FreeBSD.org> | 2016-08-31 11:10:39 +0000 |
| commit | a95582c6fdf5c116d8a113272474dd7604e68cad (patch) | |
| tree | 0afa8e3fd7b8d9936a7654df1620861c27d219f6 /sys/dev/syscons/syscons.c | |
| parent | 63dc81d861300d66d9a004954202e39e92a136f0 (diff) | |
Notes
Diffstat (limited to 'sys/dev/syscons/syscons.c')
| -rw-r--r-- | sys/dev/syscons/syscons.c | 44 |
1 files changed, 40 insertions, 4 deletions
diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c index e4906cf4ac3b..7333b3c7c1ce 100644 --- a/sys/dev/syscons/syscons.c +++ b/sys/dev/syscons/syscons.c @@ -1649,11 +1649,33 @@ sc_cnterm(struct consdev *cp) static void sccnclose(sc_softc_t *sc, struct sc_cnstate *sp); static int sc_cngetc_locked(struct sc_cnstate *sp); +static void sccnkbdlock(sc_softc_t *sc, struct sc_cnstate *sp); +static void sccnkbdunlock(sc_softc_t *sc, struct sc_cnstate *sp); static void sccnopen(sc_softc_t *sc, struct sc_cnstate *sp, int flags); static void sccnscrlock(sc_softc_t *sc, struct sc_cnstate *sp); static void sccnscrunlock(sc_softc_t *sc, struct sc_cnstate *sp); static void +sccnkbdlock(sc_softc_t *sc, struct sc_cnstate *sp) +{ + /* + * Locking method: hope for the best. + * The keyboard is supposed to be Giant locked. We can't handle that + * in general. The kdb_active case here is not safe, and we will + * proceed without the lock in all cases. + */ + sp->kbd_locked = !kdb_active && mtx_trylock(&Giant); +} + +static void +sccnkbdunlock(sc_softc_t *sc, struct sc_cnstate *sp) +{ + if (sp->kbd_locked) + mtx_unlock(&Giant); + sp->kbd_locked = FALSE; +} + +static void sccnscrlock(sc_softc_t *sc, struct sc_cnstate *sp) { SC_VIDEO_LOCK(sc); @@ -1674,11 +1696,14 @@ sccnopen(sc_softc_t *sc, struct sc_cnstate *sp, int flags) sp->kbd_opened = FALSE; sp->scr_opened = FALSE; + sp->kbd_locked = FALSE; /* Opening the keyboard is optional. */ if (!(flags & 1) || sc->kbd == NULL) goto over_keyboard; + sccnkbdlock(sc, sp); + /* * Make sure the keyboard is accessible even when the kbd device * driver is disabled. @@ -1726,6 +1751,7 @@ sccnclose(sc_softc_t *sc, struct sc_cnstate *sp) kbdd_disable(sc->kbd); sp->kbd_opened = FALSE; + sccnkbdunlock(sc, sp); } /* @@ -1751,6 +1777,7 @@ sc_cngrab(struct consdev *cp) if (lev >= 0 && lev < 2) { sccnopen(sc, &sc->grab_state[lev], 1 | 2); sccnscrunlock(sc, &sc->grab_state[lev]); + sccnkbdunlock(sc, &sc->grab_state[lev]); } } @@ -1763,6 +1790,7 @@ sc_cnungrab(struct consdev *cp) sc = sc_console->sc; lev = atomic_load_acq_int(&sc->grab_level) - 1; if (lev >= 0 && lev < 2) { + sccnkbdlock(sc, &sc->grab_state[lev]); sccnscrlock(sc, &sc->grab_state[lev]); sccnclose(sc, &sc->grab_state[lev]); } @@ -1825,16 +1853,20 @@ sc_cnputc(struct consdev *cd, int c) static int sc_cngetc(struct consdev *cd) { + struct sc_cnstate st; int c, s; /* assert(sc_console != NULL) */ + sccnopen(sc_console->sc, &st, 1); s = spltty(); /* block sckbdevent and scrn_timer while we poll */ - if (sc_console->sc->kbd == NULL) { + if (!st.kbd_opened) { splx(s); - return -1; + sccnclose(sc_console->sc, &st); + return -1; /* means no keyboard since we fudged the locking */ } - c = sc_cngetc_locked(NULL); + c = sc_cngetc_locked(&st); splx(s); + sccnclose(sc_console->sc, &st); return c; } @@ -1858,7 +1890,7 @@ sc_cngetc_locked(struct sc_cnstate *sp) if (fkeycp < fkey.len) return fkey.str[fkeycp++]; - c = scgetc(scp->sc, SCGETC_CN | SCGETC_NONBLOCK, NULL); + c = scgetc(scp->sc, SCGETC_CN | SCGETC_NONBLOCK, sp); switch (KEYFLAGS(c)) { case 0: /* normal char */ @@ -3464,7 +3496,11 @@ next_code: scp = sc->cur_scp; /* first see if there is something in the keyboard port */ for (;;) { + if (flags & SCGETC_CN) + sccnscrunlock(sc, sp); c = kbdd_read_char(sc->kbd, !(flags & SCGETC_NONBLOCK)); + if (flags & SCGETC_CN) + sccnscrlock(sc, sp); if (c == ERRKEY) { if (!(flags & SCGETC_CN)) sc_bell(scp, bios_value.bell_pitch, BELL_DURATION); |
