diff --git a/sys/dev/hyperv/input/hv_kbd.c b/sys/dev/hyperv/input/hv_kbd.c --- a/sys/dev/hyperv/input/hv_kbd.c +++ b/sys/dev/hyperv/input/hv_kbd.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -81,6 +82,7 @@ #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 @@ 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 @@ 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 @@ 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); + +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); } - 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); + 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); - } + /* 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 - } else { - if (bootverbose) - device_printf(sc->dev, "Unsupported mode: %d\n", sc->sc_mode); } +#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 @@ { 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 @@ #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 @@ 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 diff --git a/sys/dev/hyperv/input/hv_kbdc.h b/sys/dev/hyperv/input/hv_kbdc.h --- a/sys/dev/hyperv/input/hv_kbdc.h +++ b/sys/dev/hyperv/input/hv_kbdc.h @@ -90,6 +90,9 @@ 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;