hv_kbd: Add support for K_XLATE and K_CODE modes for gen 2 VMs

That fixes disabled keyboard input after Xorg server has been stopped.

Reviewed by:	whu
MFC after:	1 month
Differential revision:	https://reviews.freebsd.org/D28171
This commit is contained in:
Vladimir Kondratyev 2021-04-12 02:08:36 +03:00
parent c2a159286c
commit e4643aa4c4
2 changed files with 275 additions and 58 deletions

View File

@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/taskqueue.h>
#include <sys/selinfo.h>
@ -81,6 +82,7 @@ __FBSDID("$FreeBSD$");
#define HVKBD_UNLOCK() HVKBD_MTX_UNLOCK(&Giant)
#define HVKBD_LOCK_ASSERT() HVKBD_MTX_ASSERT(&Giant, MA_OWNED)
#define HVKBD_FLAG_COMPOSE 0x00000001 /* compose char flag */
#define HVKBD_FLAG_POLLING 0x00000002
#ifdef EVDEV_SUPPORT
@ -237,6 +239,8 @@ hvkbd_check_char_locked(keyboard_t *kbd)
return (FALSE);
hv_kbd_sc *sc = kbd->kb_data;
if (!(sc->sc_flags & HVKBD_FLAG_COMPOSE) && sc->sc_composed_char != 0)
return (TRUE);
if (sc->sc_flags & HVKBD_FLAG_POLLING)
hvkbd_do_poll(sc, 0);
if (hv_kbd_prod_is_ready(sc)) {
@ -262,6 +266,7 @@ static uint32_t
hvkbd_read_char_locked(keyboard_t *kbd, int wait)
{
uint32_t scancode = NOKEY;
uint32_t action;
keystroke ks;
hv_kbd_sc *sc = kbd->kb_data;
#ifdef EVDEV_SUPPORT
@ -271,67 +276,268 @@ hvkbd_read_char_locked(keyboard_t *kbd, int wait)
if (!KBD_IS_ACTIVE(kbd) || !hv_kbd_prod_is_ready(sc))
return (NOKEY);
if (sc->sc_mode == K_RAW) {
if (hv_kbd_fetch_top(sc, &ks)) {
return (NOKEY);
}
if ((ks.info & IS_E0) || (ks.info & IS_E1)) {
/**
* Emulate the generation of E0 or E1 scancode,
* the real scancode will be consumed next time.
*/
if (ks.info & IS_E0) {
scancode = XTKBD_EMUL0;
ks.info &= ~IS_E0;
} else if (ks.info & IS_E1) {
scancode = XTKBD_EMUL1;
ks.info &= ~IS_E1;
}
/**
* Change the top item to avoid encountering
* E0 or E1 twice.
*/
hv_kbd_modify_top(sc, &ks);
} else if (ks.info & IS_UNICODE) {
/**
* XXX: Hyperv host send unicode to VM through
* 'Type clipboard text', the mapping from
* unicode to scancode depends on the keymap.
* It is so complicated that we do not plan to
* support it yet.
*/
if (bootverbose)
device_printf(sc->dev, "Unsupported unicode\n");
hv_kbd_remove_top(sc);
return (NOKEY);
} else {
scancode = ks.makecode;
if (ks.info & IS_BREAK) {
scancode |= XTKBD_RELEASE;
}
hv_kbd_remove_top(sc);
}
#ifdef EVDEV_SUPPORT
/* push evdev event */
if (evdev_rcpt_mask & EVDEV_RCPT_HW_KBD &&
sc->ks_evdev != NULL) {
keycode = evdev_scancode2key(&sc->ks_evdev_state,
scancode);
if (keycode != KEY_RESERVED) {
evdev_push_event(sc->ks_evdev, EV_KEY,
(uint16_t)keycode, scancode & 0x80 ? 0 : 1);
evdev_sync(sc->ks_evdev);
}
next_code:
/* do we have a composed char to return? */
if (!(sc->sc_flags & HVKBD_FLAG_COMPOSE) && sc->sc_composed_char > 0) {
action = sc->sc_composed_char;
sc->sc_composed_char = 0;
if (action > UCHAR_MAX) {
return (ERRKEY);
}
#endif
} else {
if (bootverbose)
device_printf(sc->dev, "Unsupported mode: %d\n", sc->sc_mode);
return (action);
}
if (hv_kbd_fetch_top(sc, &ks)) {
return (NOKEY);
}
if ((ks.info & IS_E0) || (ks.info & IS_E1)) {
/**
* Emulate the generation of E0 or E1 scancode,
* the real scancode will be consumed next time.
*/
if (ks.info & IS_E0) {
scancode = XTKBD_EMUL0;
ks.info &= ~IS_E0;
} else if (ks.info & IS_E1) {
scancode = XTKBD_EMUL1;
ks.info &= ~IS_E1;
}
/**
* Change the top item to avoid encountering
* E0 or E1 twice.
*/
hv_kbd_modify_top(sc, &ks);
} else if (ks.info & IS_UNICODE) {
/**
* XXX: Hyperv host send unicode to VM through
* 'Type clipboard text', the mapping from
* unicode to scancode depends on the keymap.
* It is so complicated that we do not plan to
* support it yet.
*/
if (bootverbose)
device_printf(sc->dev, "Unsupported unicode\n");
hv_kbd_remove_top(sc);
return (NOKEY);
} else {
scancode = ks.makecode;
if (ks.info & IS_BREAK) {
scancode |= XTKBD_RELEASE;
}
hv_kbd_remove_top(sc);
}
#ifdef EVDEV_SUPPORT
/* push evdev event */
if (evdev_rcpt_mask & EVDEV_RCPT_HW_KBD &&
sc->ks_evdev != NULL) {
keycode = evdev_scancode2key(&sc->ks_evdev_state,
scancode);
if (keycode != KEY_RESERVED) {
evdev_push_event(sc->ks_evdev, EV_KEY,
(uint16_t)keycode, scancode & 0x80 ? 0 : 1);
evdev_sync(sc->ks_evdev);
}
}
#endif
++kbd->kb_count;
DEBUG_HVKBD(kbd, "read scan: 0x%x\n", scancode);
return scancode;
/* return the byte as is for the K_RAW mode */
if (sc->sc_mode == K_RAW)
return scancode;
/* translate the scan code into a keycode */
keycode = scancode & 0x7F;
switch (sc->sc_prefix) {
case 0x00: /* normal scancode */
switch(scancode) {
case 0xB8: /* left alt (compose key) released */
if (sc->sc_flags & HVKBD_FLAG_COMPOSE) {
sc->sc_flags &= ~HVKBD_FLAG_COMPOSE;
if (sc->sc_composed_char > UCHAR_MAX)
sc->sc_composed_char = 0;
}
break;
case 0x38: /* left alt (compose key) pressed */
if (!(sc->sc_flags & HVKBD_FLAG_COMPOSE)) {
sc->sc_flags |= HVKBD_FLAG_COMPOSE;
sc->sc_composed_char = 0;
}
break;
case 0xE0:
case 0xE1:
sc->sc_prefix = scancode;
goto next_code;
}
break;
case 0xE0: /* 0xE0 prefix */
sc->sc_prefix = 0;
switch (keycode) {
case 0x1C: /* right enter key */
keycode = 0x59;
break;
case 0x1D: /* right ctrl key */
keycode = 0x5A;
break;
case 0x35: /* keypad divide key */
keycode = 0x5B;
break;
case 0x37: /* print scrn key */
keycode = 0x5C;
break;
case 0x38: /* right alt key (alt gr) */
keycode = 0x5D;
break;
case 0x46: /* ctrl-pause/break on AT 101 (see below) */
keycode = 0x68;
break;
case 0x47: /* grey home key */
keycode = 0x5E;
break;
case 0x48: /* grey up arrow key */
keycode = 0x5F;
break;
case 0x49: /* grey page up key */
keycode = 0x60;
break;
case 0x4B: /* grey left arrow key */
keycode = 0x61;
break;
case 0x4D: /* grey right arrow key */
keycode = 0x62;
break;
case 0x4F: /* grey end key */
keycode = 0x63;
break;
case 0x50: /* grey down arrow key */
keycode = 0x64;
break;
case 0x51: /* grey page down key */
keycode = 0x65;
break;
case 0x52: /* grey insert key */
keycode = 0x66;
break;
case 0x53: /* grey delete key */
keycode = 0x67;
break;
/* the following 3 are only used on the MS "Natural" keyboard */
case 0x5b: /* left Window key */
keycode = 0x69;
break;
case 0x5c: /* right Window key */
keycode = 0x6a;
break;
case 0x5d: /* menu key */
keycode = 0x6b;
break;
case 0x5e: /* power key */
keycode = 0x6d;
break;
case 0x5f: /* sleep key */
keycode = 0x6e;
break;
case 0x63: /* wake key */
keycode = 0x6f;
break;
default: /* ignore everything else */
goto next_code;
}
break;
case 0xE1: /* 0xE1 prefix */
/*
* The pause/break key on the 101 keyboard produces:
* E1-1D-45 E1-9D-C5
* Ctrl-pause/break produces:
* E0-46 E0-C6 (See above.)
*/
sc->sc_prefix = 0;
if (keycode == 0x1D)
sc->sc_prefix = 0x1D;
goto next_code;
/* NOT REACHED */
case 0x1D: /* pause / break */
sc->sc_prefix = 0;
if (keycode != 0x45)
goto next_code;
keycode = 0x68;
break;
}
/* XXX assume 101/102 keys AT keyboard */
switch (keycode) {
case 0x5c: /* print screen */
if (sc->sc_flags & ALTS)
keycode = 0x54; /* sysrq */
break;
case 0x68: /* pause/break */
if (sc->sc_flags & CTLS)
keycode = 0x6c; /* break */
break;
}
/* return the key code in the K_CODE mode */
if (sc->sc_mode == K_CODE)
return (keycode | (scancode & 0x80));
/* compose a character code */
if (sc->sc_flags & HVKBD_FLAG_COMPOSE) {
switch (keycode | (scancode & 0x80)) {
/* key pressed, process it */
case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */
sc->sc_composed_char *= 10;
sc->sc_composed_char += keycode - 0x40;
if (sc->sc_composed_char > UCHAR_MAX)
return ERRKEY;
goto next_code;
case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */
sc->sc_composed_char *= 10;
sc->sc_composed_char += keycode - 0x47;
if (sc->sc_composed_char > UCHAR_MAX)
return ERRKEY;
goto next_code;
case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */
sc->sc_composed_char *= 10;
sc->sc_composed_char += keycode - 0x4E;
if (sc->sc_composed_char > UCHAR_MAX)
return ERRKEY;
goto next_code;
case 0x52: /* keypad 0 */
sc->sc_composed_char *= 10;
if (sc->sc_composed_char > UCHAR_MAX)
return ERRKEY;
goto next_code;
/* key released, no interest here */
case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */
case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */
case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */
case 0xD2: /* keypad 0 */
goto next_code;
case 0x38: /* left alt key */
break;
default:
if (sc->sc_composed_char > 0) {
sc->sc_flags &= ~HVKBD_FLAG_COMPOSE;
sc->sc_composed_char = 0;
return (ERRKEY);
}
break;
}
}
/* keycode to key action */
action = genkbd_keyaction(kbd, keycode, scancode & 0x80,
&sc->sc_state, &sc->sc_accents);
if (action == NOKEY)
goto next_code;
else
return (action);
}
/* Currently wait is always false. */
@ -353,7 +559,9 @@ hvkbd_clear_state(keyboard_t *kbd)
{
hv_kbd_sc *sc = kbd->kb_data;
sc->sc_state &= LOCK_MASK; /* preserve locking key state */
sc->sc_flags &= ~HVKBD_FLAG_POLLING;
sc->sc_flags &= ~(HVKBD_FLAG_POLLING | HVKBD_FLAG_COMPOSE);
sc->sc_accents = 0;
sc->sc_composed_char = 0;
}
static int
@ -453,6 +661,12 @@ hvkbd_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
#endif
KBD_LED_VAL(kbd) = *(int *)arg;
break;
case PIO_KEYMAP: /* set keyboard translation table */
case OPIO_KEYMAP: /* set keyboard translation table (compat) */
case PIO_KEYMAPENT: /* set keyboard translation table entry */
case PIO_DEADKEYMAP: /* set accent key translation table */
sc->sc_accents = 0;
/* FALLTHROUGH */
default:
return (genkbd_commonioctl(kbd, cmd, arg));
}
@ -578,7 +792,7 @@ hv_kbd_drv_attach(device_t dev)
hvkbd_clear_state(kbd);
KBD_PROBE_DONE(kbd);
KBD_INIT_DONE(kbd);
sc->sc_mode = K_RAW;
sc->sc_mode = K_XLATE;
(*sw->enable)(kbd);
#ifdef EVDEV_SUPPORT

View File

@ -90,6 +90,9 @@ typedef struct hv_kbd_sc_t {
keyboard_t sc_kbd;
int sc_mode;
int sc_state;
uint32_t sc_accents; /* accent key index (> 0) */
uint32_t sc_composed_char; /* composed char code */
uint8_t sc_prefix; /* AT scan code prefix */
int sc_polling; /* polling recursion count */
uint32_t sc_flags;
int debug;