Hi, this is a port of NetBSD's hvkbd(4). While Hyper-V Gen1 provides pckbc(4) as keyboard, Gen2's no-legcay-HW approach needs us to make use of the Synthetic Keyboard if we want to be able to type in the framebuffer console.
One thing I'm not so sure about is if we really need another wskbd type (WSKBD_TYPE_HYPERV). Comments? ok? Patrick diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC index ecccd1323d9..85a95e11eae 100644 --- a/sys/arch/amd64/conf/GENERIC +++ b/sys/arch/amd64/conf/GENERIC @@ -92,6 +92,8 @@ xnf* at xen? # Xen Netfront xbf* at xen? # Xen Blkfront hyperv0 at pvbus? # Hyper-V guest +hvkbd* at hyperv? # Hyper-V Keyboard +wskbd* at hvkbd? mux 1 hvn* at hyperv? # Hyper-V NetVSC hvs* at hyperv? # Hyper-V StorVSC diff --git a/sys/dev/pv/files.pv b/sys/dev/pv/files.pv index e081897d582..0c0ab1a23e0 100644 --- a/sys/dev/pv/files.pv +++ b/sys/dev/pv/files.pv @@ -40,6 +40,11 @@ attach hyperv at pvbus file dev/pv/hyperv.c hyperv needs-flag file dev/pv/hypervic.c hyperv +# Hyper-V Keyboard +device hvkbd: wskbddev +attach hvkbd at hyperv +file dev/pv/hvkbd.c hvkbd + # Hyper-V NetVSC device hvn: ether, ifnet, ifmedia attach hvn at hyperv diff --git a/sys/dev/pv/hvkbd.c b/sys/dev/pv/hvkbd.c new file mode 100644 index 00000000000..f8dc1202033 --- /dev/null +++ b/sys/dev/pv/hvkbd.c @@ -0,0 +1,593 @@ +/* $OpenBSD$ */ +/* $NetBSD: hvkbd.c,v 1.9 2021/08/07 16:19:11 thorpej Exp $ */ + +/*- + * Copyright (c) 2017 Microsoft Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: head/sys/dev/hyperv/input/hv_kbd.c 317821 2017-05-05 03:28:30Z sephe $ + * $FreeBSD: head/sys/dev/hyperv/input/hv_kbdc.c 320490 2017-06-30 03:01:22Z sephe $ + * $FreeBSD: head/sys/dev/hyperv/input/hv_kbdc.h 316515 2017-04-05 05:01:23Z sephe $ + */ + +/* __KERNEL_RCSID(0, "$NetBSD: hvkbd.c,v 1.9 2021/08/07 16:19:11 thorpej Exp $"); */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/mutex.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/queue.h> +#include <sys/task.h> +#include <sys/timeout.h> + +#include <machine/bus.h> +#include <machine/biosvar.h> + +#include <dev/pv/hypervreg.h> +#include <dev/pv/hypervvar.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wskbdvar.h> +#include <dev/wscons/wsksymdef.h> +#include <dev/wscons/wsksymvar.h> +#include <dev/pckbc/wskbdmap_mfii.h> + +#define HVKBD_BUFSIZE (4 * PAGE_SIZE) +#define HVKBD_TX_RING_SIZE (10 * PAGE_SIZE) +#define HVKBD_RX_RING_SIZE (10 * PAGE_SIZE) + +#define HVKBD_VER_MAJOR (1) +#define HVKBD_VER_MINOR (0) +#define HVKBD_VERSION ((HVKBD_VER_MAJOR << 16) | HVKBD_VER_MINOR) + +enum hvkbd_msg_type { + HVKBD_PROTO_REQUEST = 1, + HVKBD_PROTO_RESPONSE = 2, + HVKBD_PROTO_EVENT = 3, + HVKBD_PROTO_LED_INDICATORS = 4 +}; + +struct hvkbd_msg_hdr { + uint32_t type; +} __packed; + +struct hvkbd_proto_req { + struct hvkbd_msg_hdr hdr; + uint32_t ver; +} __packed; + +struct hvkbd_proto_resp { + struct hvkbd_msg_hdr hdr; + uint32_t status; +#define RESP_STATUS_ACCEPTED (1 << 0) +} __packed; + +struct keystroke { + uint16_t makecode; + uint16_t pad0; + uint32_t info; +#define KS_INFO_UNICODE (1 << 0) +#define KS_INFO_BREAK (1 << 1) +#define KS_INFO_E0 (1 << 2) +#define KS_INFO_E1 (1 << 3) +} __packed; + +struct hvkbd_keystroke { + struct hvkbd_msg_hdr hdr; + struct keystroke ks; +} __packed; + +struct hvkbd_keystroke_info { + LIST_ENTRY(hvkbd_keystroke_info) link; + STAILQ_ENTRY(hvkbd_keystroke_info) slink; + struct keystroke ks; +}; + +#define HVKBD_KEYBUF_SIZE 16 + +struct hvkbd_softc { + struct device sc_dev; + + struct hv_channel *sc_chan; + void *sc_buf; + + struct mutex sc_ks_lock; + LIST_HEAD(, hvkbd_keystroke_info) sc_ks_free; + STAILQ_HEAD(, hvkbd_keystroke_info) sc_ks_queue; + + int sc_enabled; + int sc_polling; + int sc_console_keyboard; +#if defined(WSDISPLAY_COMPAT_RAWKBD) + int sc_rawkbd; +#endif + + int sc_connected; + uint32_t sc_connect_status; + + struct device * sc_wskbddev; +}; + +static int hvkbd_match(struct device *, void *, void *); +static void hvkbd_attach(struct device *, struct device *, void *); + +struct cfdriver hvkbd_cd = { + NULL, "hvkbd", DV_DULL +}; + +const struct cfattach hvkbd_ca = { + sizeof(struct hvkbd_softc), hvkbd_match, hvkbd_attach +}; + +static int hvkbd_alloc_keybuf(struct hvkbd_softc *); +static void hvkbd_free_keybuf(struct hvkbd_softc *); + +static int hvkbd_enable(void *, int); +static void hvkbd_set_leds(void *, int); +static int hvkbd_ioctl(void *, u_long, caddr_t, int, struct proc *); + +static const struct wskbd_accessops hvkbd_accessops = { + hvkbd_enable, + hvkbd_set_leds, + hvkbd_ioctl, +}; + +static const struct wskbd_mapdata hvkbd_keymapdata = { + pckbd_keydesctab, +#if defined(PCKBD_LAYOUT) + PCKBD_LAYOUT, +#else + KB_US, +#endif +}; + +static int hvkbd_connect(struct hvkbd_softc *); +static void hvkbd_intr(void *); + +static void hvkbd_cngetc(void *, u_int *, int *); +static void hvkbd_cnpollc(void *, int); + +static const struct wskbd_consops hvkbd_consops = { + .getc = hvkbd_cngetc, + .pollc = hvkbd_cnpollc, + .bell = NULL, +}; + +static int hvkbd_is_console; + +static int +hvkbd_match(struct device *parent, void *match, void *aux) +{ + struct hv_attach_args *aa = aux; + + if (memcmp(aa->aa_type, &hv_guid_kbd, sizeof(*aa->aa_type)) != 0) + return 0; + + /* We use pckbd(4) in Gen.1 VM. */ + if (bios_efiinfo == NULL) + return 0; + + return 1; +} + +static void +hvkbd_attach(struct device *parent, struct device *self, void *aux) +{ + struct hvkbd_softc *sc = (struct hvkbd_softc *)self; + struct hv_attach_args *aa = aux; + struct wskbddev_attach_args a; + + sc->sc_chan = aa->aa_chan; + + printf("\n"); + + mtx_init(&sc->sc_ks_lock, IPL_TTY); + LIST_INIT(&sc->sc_ks_free); + STAILQ_INIT(&sc->sc_ks_queue); + hvkbd_alloc_keybuf(sc); + + sc->sc_buf = malloc(HVKBD_BUFSIZE, M_DEVBUF, M_WAITOK | M_ZERO); + + sc->sc_chan->ch_flags &= ~CHF_BATCHED; + if (hv_channel_open(sc->sc_chan, + HVKBD_TX_RING_SIZE + HVKBD_RX_RING_SIZE, NULL, 0, hvkbd_intr, sc)) { + printf("%s: failed to open channel\n", sc->sc_dev.dv_xname); + goto free_buf; + } + + if (hvkbd_connect(sc)) + goto free_buf; + + sc->sc_console_keyboard = hvkbd_is_console; + if (hvkbd_is_console) + hvkbd_is_console = 0; + + if (sc->sc_console_keyboard) { + wskbd_cnattach(&hvkbd_consops, sc, &hvkbd_keymapdata); + hvkbd_enable(sc, 1); + } + + a.console = sc->sc_console_keyboard; + a.keymap = &hvkbd_keymapdata; + a.accessops = &hvkbd_accessops; + a.accesscookie = sc; + sc->sc_wskbddev = config_found(self, &a, wskbddevprint); + return; + +free_buf: + if (sc->sc_buf != NULL) { + free(sc->sc_buf, M_DEVBUF, HVKBD_BUFSIZE); + sc->sc_buf = NULL; + } + hvkbd_free_keybuf(sc); +} + +static int +hvkbd_alloc_keybuf(struct hvkbd_softc *sc) +{ + struct hvkbd_keystroke_info *ksi; + int i; + + for (i = 0; i < HVKBD_KEYBUF_SIZE; i++) { + ksi = malloc(sizeof(*ksi), M_DEVBUF, M_WAITOK | M_ZERO); + LIST_INSERT_HEAD(&sc->sc_ks_free, ksi, link); + } + + return 0; +} + +static void +hvkbd_free_keybuf(struct hvkbd_softc *sc) +{ + struct hvkbd_keystroke_info *ksi; + + while ((ksi = STAILQ_FIRST(&sc->sc_ks_queue)) != NULL) { + STAILQ_REMOVE(&sc->sc_ks_queue, ksi, hvkbd_keystroke_info, + slink); + free(ksi, M_DEVBUF, sizeof(*ksi)); + } + while ((ksi = LIST_FIRST(&sc->sc_ks_free)) != NULL) { + LIST_REMOVE(ksi, link); + free(ksi, M_DEVBUF, sizeof(*ksi)); + } +} + +int +hvkbd_enable(void *v, int on) +{ + struct hvkbd_softc *sc = v; + + sc->sc_enabled = on; + + return 0; +} + +static void +hvkbd_set_leds(void *v, int leds) +{ +} + +static int +hvkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) +{ +#if defined(WSDISPLAY_COMPAT_RAWKBD) + struct hvkbd_softc *sc = v; +#endif + + switch (cmd) { + case WSKBDIO_GTYPE: + *(int *)data = WSKBD_TYPE_HYPERV; + return 0; + + case WSKBDIO_SETLEDS: + hvkbd_set_leds(v, *(int *)data); + return 0; + + case WSKBDIO_GETLEDS: + *(int *)data = 0; + return 0; + +#if defined(WSDISPLAY_COMPAT_RAWKBD) + case WSKBDIO_SETMODE: + sc->sc_rawkbd = (*(int *)data == WSKBD_RAW); + return 0; +#endif + } + + return -1; +} + +static int +hvkbd_connect(struct hvkbd_softc *sc) +{ + struct hvkbd_proto_req req; + int timo = 100; + int error, s; + + sc->sc_connected = 0; + + memset(&req, 0, sizeof(req)); + req.hdr.type = HVKBD_PROTO_REQUEST; + req.ver = HVKBD_VERSION; + error = hv_channel_send(sc->sc_chan, &req, sizeof(req), + 0, VMBUS_CHANPKT_TYPE_INBAND, VMBUS_CHANPKT_FLAG_RC); + if (error) { + printf("%s: failed to send connect: %d\n", + sc->sc_dev.dv_xname, error); + return error; + } + + do { + if (cold) { + delay(1000); + s = spltty(); + hvkbd_intr(sc); + splx(s); + } else + tsleep_nsec(sc, PRIBIO | PCATCH, "hvkbdcon", + MSEC_TO_NSEC(1)); + } while (--timo > 0 && sc->sc_connected == 0); + + if (timo == 0 && sc->sc_connected == 0) { + printf("%s: connect timed out\n", sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + + if (!(sc->sc_connect_status & RESP_STATUS_ACCEPTED)) { + printf("%s: protocol request failed\n", sc->sc_dev.dv_xname); + return ENODEV; + } + + return 0; +} + +static int +hvkbd_keybuf_add_keystroke(struct hvkbd_softc *sc, const struct keystroke *ks) +{ + struct hvkbd_keystroke_info *ksi; + + mtx_enter(&sc->sc_ks_lock); + ksi = LIST_FIRST(&sc->sc_ks_free); + if (ksi != NULL) { + LIST_REMOVE(ksi, link); + ksi->ks = *ks; + STAILQ_INSERT_TAIL(&sc->sc_ks_queue, ksi, slink); + } + mtx_leave(&sc->sc_ks_lock); + + return (ksi != NULL) ? 0 : 1; +} + +static int +hvkbd_decode(struct hvkbd_softc *sc, u_int *type, int *scancode) +{ + struct hvkbd_keystroke_info *ksi; + struct keystroke ks; + + mtx_enter(&sc->sc_ks_lock); + ksi = STAILQ_FIRST(&sc->sc_ks_queue); + if (ksi != NULL) { + STAILQ_REMOVE(&sc->sc_ks_queue, ksi, hvkbd_keystroke_info, + slink); + ks = ksi->ks; + LIST_INSERT_HEAD(&sc->sc_ks_free, ksi, link); + } + mtx_leave(&sc->sc_ks_lock); + + if (ksi == NULL) + return 0; + + /* + * XXX: Hyper-V 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 (ks.info & KS_INFO_UNICODE) + return 0; + + *type = (ks.info & KS_INFO_BREAK) ? + WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; + *scancode = ks.makecode; + return 1; +} + +#if defined(WSDISPLAY_COMPAT_RAWKBD) +static int +hvkbd_encode(struct hvkbd_softc *sc, u_char *buf, int *len) +{ + struct hvkbd_keystroke_info *ksi; + struct keystroke ks; + int i; + + mtx_enter(&sc->sc_ks_lock); + ksi = STAILQ_FIRST(&sc->sc_ks_queue); + if (ksi != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_ks_queue, slink); + ks = ksi->ks; + LIST_INSERT_HEAD(&sc->sc_ks_free, ksi, link); + } + mtx_leave(&sc->sc_ks_lock); + + if (ksi == NULL) + return 0; + + /* + * XXX: Hyper-V 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 (ks.info & KS_INFO_UNICODE) + return 0; + + i = 0; + if (ks.info & (KS_INFO_E0|KS_INFO_E1)) { + if (ks.info & KS_INFO_E0) + buf[i++] = 0xe0; + else + buf[i++] = 0xe1; + } + if (ks.info & KS_INFO_BREAK) + buf[i++] = (u_char)ks.makecode & 0x80; + else + buf[i++] = (u_char)ks.makecode; + + KDASSERT(i <= *len); + *len = i; + + return 1; +} +#endif + +static void +hvkbd_intr(void *xsc) +{ + struct hvkbd_softc *sc = xsc; + struct vmbus_chanpkt_hdr *cph; + const struct hvkbd_msg_hdr *hdr; + const struct hvkbd_proto_resp *rsp; + const struct hvkbd_keystroke *ks; + uint64_t rid; + uint32_t rlen; + u_int type; + int key, error; + + for (;;) { + error = hv_channel_recv(sc->sc_chan, sc->sc_buf, + HVKBD_BUFSIZE, &rlen, &rid, 1); + if (error != 0 || rlen == 0) { + if (error != EAGAIN) + printf("%s: failed to receive a reply packet\n", + sc->sc_dev.dv_xname); + return; + } + + cph = (struct vmbus_chanpkt_hdr *)sc->sc_buf; + switch (cph->cph_type) { + case VMBUS_CHANPKT_TYPE_INBAND: + hdr = VMBUS_CHANPKT_CONST_DATA(cph); + if (rlen < sizeof(*hdr)) { + printf("%s: Illegal packet\n", + sc->sc_dev.dv_xname); + continue; + } + + switch (hdr->type) { + case HVKBD_PROTO_RESPONSE: + if (!sc->sc_connected) { + rsp = VMBUS_CHANPKT_CONST_DATA(cph); + if (rlen < sizeof(*rsp)) { + printf( + "%s: Illegal resp packet\n", + sc->sc_dev.dv_xname); + break; + } + sc->sc_connect_status = rsp->status; + sc->sc_connected = 1; + wakeup(sc); + } + break; + + case HVKBD_PROTO_EVENT: + if (sc->sc_wskbddev == NULL || !sc->sc_enabled) + break; + + ks = VMBUS_CHANPKT_CONST_DATA(cph); + hvkbd_keybuf_add_keystroke(sc, &ks->ks); + if (sc->sc_polling) + break; + +#if defined(WSDISPLAY_COMPAT_RAWKBD) + if (sc->sc_rawkbd) { + u_char buf[2]; + int len; + + len = sizeof(buf); + if (hvkbd_encode(sc, buf, &len)) { + wskbd_rawinput(sc->sc_wskbddev, + buf, len); + } + break; + } +#endif + if (hvkbd_decode(sc, &type, &key)) { + KERNEL_LOCK(); + wskbd_input(sc->sc_wskbddev, type, key); + KERNEL_UNLOCK(); + } + break; + + case HVKBD_PROTO_REQUEST: + case HVKBD_PROTO_LED_INDICATORS: + printf("%s: unhandled message: %d\n", + sc->sc_dev.dv_xname, hdr->type); + break; + + default: + printf("%s: unknown message: %d\n", + sc->sc_dev.dv_xname, hdr->type); + break; + } + break; + + case VMBUS_CHANPKT_TYPE_COMP: + case VMBUS_CHANPKT_TYPE_RXBUF: + printf("%s: unhandled event: %d\n", + sc->sc_dev.dv_xname, cph->cph_type); + break; + + default: + printf("%s: unknown event: %d\n", + sc->sc_dev.dv_xname, cph->cph_type); + break; + } + } +} + +int +hvkbd_cnattach(void) +{ + hvkbd_is_console = 1; + + return 0; +} + +static void +hvkbd_cngetc(void *v, u_int *type, int *data) +{ + struct hvkbd_softc *sc = v; + + while (!hvkbd_decode(sc, type, data)) + hvkbd_intr(sc); +} + +static void +hvkbd_cnpollc(void *v, int on) +{ + struct hvkbd_softc *sc = v; + + sc->sc_polling = on; +} diff --git a/sys/dev/pv/hyperv.c b/sys/dev/pv/hyperv.c index dcebd3ae681..0b2260aed11 100644 --- a/sys/dev/pv/hyperv.c +++ b/sys/dev/pv/hyperv.c @@ -194,6 +194,11 @@ const struct hv_guid hv_guid_kvp = { 0xb8, 0x27, 0x8a, 0x84, 0x1e, 0x8c, 0x03, 0xe6 } }; +const struct hv_guid hv_guid_kbd = { + { 0x6d, 0xad, 0x12, 0xf9, 0x17, 0x2b, 0xea, 0x48, + 0xbd, 0x65, 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84 } +}; + #ifdef HYPERV_DEBUG const struct hv_guid hv_guid_vss = { { 0x29, 0x2e, 0xfa, 0x35, 0x23, 0xea, 0x36, 0x42, @@ -210,11 +215,6 @@ const struct hv_guid hv_guid_mouse = { 0xb9, 0x8b, 0x8b, 0xa1, 0xa1, 0xf3, 0xf9, 0x5a } }; -const struct hv_guid hv_guid_kbd = { - { 0x6d, 0xad, 0x12, 0xf9, 0x17, 0x2b, 0xea, 0x48, - 0xbd, 0x65, 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84 } -}; - const struct hv_guid hv_guid_video = { { 0x02, 0x78, 0x0a, 0xda, 0x77, 0xe3, 0xac, 0x4a, 0x8e, 0x77, 0x05, 0x58, 0xeb, 0x10, 0x73, 0xf8 } diff --git a/sys/dev/pv/hypervic.c b/sys/dev/pv/hypervic.c index ad5fc9b0c0b..447d001b82b 100644 --- a/sys/dev/pv/hypervic.c +++ b/sys/dev/pv/hypervic.c @@ -103,6 +103,7 @@ struct hv_kvp { int hv_heartbeat_attach(struct hv_ic_dev *); void hv_heartbeat(void *); +int hv_kbd_attach(struct hv_ic_dev *); int hv_kvp_attach(struct hv_ic_dev *); void hv_kvp(void *); int hv_kvop(void *, int, char *, char *, size_t); @@ -120,6 +121,12 @@ static struct hv_ic_dev { uint8_t *dv_buf; void *dv_priv; } hv_ic_devs[] = { + { + "keyboard", + &hv_guid_kbd, + hv_kbd_attach, + NULL + }, { "heartbeat", &hv_guid_heartbeat, @@ -1178,3 +1185,22 @@ hv_kvop(void *arg, int op, char *key, char *val, size_t vallen) return (error); } + +int +hv_kbd_attach(struct hv_ic_dev *dv) +{ + struct hv_channel *ch = dv->dv_ch; + struct hv_softc *sc = ch->ch_sc; + struct hv_attach_args aa; + + aa.aa_parent = sc; + aa.aa_type = &ch->ch_type; + aa.aa_inst = &ch->ch_inst; + aa.aa_ident = ch->ch_ident; + aa.aa_chan = ch; + aa.aa_dmat = sc->sc_dmat; + config_found((struct device *)sc, &aa, NULL); + + /* Return non-zero to stop it from opening a channel. */ + return (-1); +} diff --git a/sys/dev/pv/hypervvar.h b/sys/dev/pv/hypervvar.h index 9cd8036095d..34cddac6b01 100644 --- a/sys/dev/pv/hypervvar.h +++ b/sys/dev/pv/hypervvar.h @@ -187,11 +187,11 @@ extern const struct hv_guid hv_guid_shutdown; extern const struct hv_guid hv_guid_timesync; extern const struct hv_guid hv_guid_heartbeat; extern const struct hv_guid hv_guid_kvp; +extern const struct hv_guid hv_guid_kbd; #ifdef HYPERV_DEBUG extern const struct hv_guid hv_guid_vss; extern const struct hv_guid hv_guid_dynmem; extern const struct hv_guid hv_guid_mouse; -extern const struct hv_guid hv_guid_kbd; extern const struct hv_guid hv_guid_video; extern const struct hv_guid hv_guid_fc; extern const struct hv_guid hv_guid_fcopy; diff --git a/sys/dev/wscons/wsconsio.h b/sys/dev/wscons/wsconsio.h index 9a3fbb142dc..2d30c78318f 100644 --- a/sys/dev/wscons/wsconsio.h +++ b/sys/dev/wscons/wsconsio.h @@ -143,6 +143,7 @@ struct wscons_event { #define WSKBD_TYPE_BLUETOOTH 18 /* Bluetooth keyboard */ #define WSKBD_TYPE_KPC 19 /* Palm keypad */ #define WSKBD_TYPE_SGI 20 /* SGI serial keyboard */ +#define WSKBD_TYPE_HYPERV 21 /* Hyper-V synthetic keyboard */ /* Manipulate the keyboard bell. */ struct wskbd_bell_data {