aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/syscons/syscons.c
diff options
context:
space:
mode:
authorBruce Evans <bde@FreeBSD.org>2016-08-31 11:10:39 +0000
committerBruce Evans <bde@FreeBSD.org>2016-08-31 11:10:39 +0000
commita95582c6fdf5c116d8a113272474dd7604e68cad (patch)
tree0afa8e3fd7b8d9936a7654df1620861c27d219f6 /sys/dev/syscons/syscons.c
parent63dc81d861300d66d9a004954202e39e92a136f0 (diff)
Notes
Diffstat (limited to 'sys/dev/syscons/syscons.c')
-rw-r--r--sys/dev/syscons/syscons.c44
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);