Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F107357912
D10196.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
30 KB
Referenced Files
None
Subscribers
None
D10196.diff
View Options
Index: head/sys/conf/files.amd64
===================================================================
--- head/sys/conf/files.amd64
+++ head/sys/conf/files.amd64
@@ -303,6 +303,8 @@
dev/hwpmc/hwpmc_piv.c optional hwpmc
dev/hwpmc/hwpmc_tsc.c optional hwpmc
dev/hwpmc/hwpmc_x86.c optional hwpmc
+dev/hyperv/input/hv_kbd.c optional hyperv
+dev/hyperv/input/hv_kbdc.c optional hyperv
dev/hyperv/pcib/vmbus_pcib.c optional hyperv pci
dev/hyperv/netvsc/hn_nvs.c optional hyperv
dev/hyperv/netvsc/hn_rndis.c optional hyperv
Index: head/sys/dev/hyperv/input/hv_kbd.c
===================================================================
--- head/sys/dev/hyperv/input/hv_kbd.c
+++ head/sys/dev/hyperv/input/hv_kbd.c
@@ -0,0 +1,564 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/taskqueue.h>
+#include <sys/selinfo.h>
+#include <sys/sysctl.h>
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysproto.h>
+#include <sys/sema.h>
+#include <sys/signal.h>
+#include <sys/syslog.h>
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/callout.h>
+
+#include <sys/kbio.h>
+#include <dev/kbd/kbdreg.h>
+#include <dev/kbd/kbdtables.h>
+
+#include "dev/hyperv/input/hv_kbdc.h"
+
+#define HVKBD_MTX_LOCK(_m) do { \
+ mtx_lock(_m); \
+} while (0)
+
+#define HVKBD_MTX_UNLOCK(_m) do { \
+ mtx_unlock(_m); \
+} while (0)
+
+#define HVKBD_MTX_ASSERT(_m, _t) do { \
+ mtx_assert(_m, _t); \
+} while (0)
+
+#define HVKBD_LOCK() HVKBD_MTX_LOCK(&Giant)
+#define HVKBD_UNLOCK() HVKBD_MTX_UNLOCK(&Giant)
+#define HVKBD_LOCK_ASSERT() HVKBD_MTX_ASSERT(&Giant, MA_OWNED)
+
+#define HVKBD_FLAG_POLLING 0x00000002
+
+/* early keyboard probe, not supported */
+static int
+hvkbd_configure(int flags)
+{
+ return (0);
+}
+
+/* detect a keyboard, not used */
+static int
+hvkbd_probe(int unit, void *arg, int flags)
+{
+ return (ENXIO);
+}
+
+/* reset and initialize the device, not used */
+static int
+hvkbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
+{
+ DEBUG_HVKBD(*kbdp, "%s\n", __func__);
+ return (ENXIO);
+}
+
+/* test the interface to the device, not used */
+static int
+hvkbd_test_if(keyboard_t *kbd)
+{
+ DEBUG_HVKBD(kbd, "%s\n", __func__);
+ return (0);
+}
+
+/* finish using this keyboard, not used */
+static int
+hvkbd_term(keyboard_t *kbd)
+{
+ DEBUG_HVKBD(kbd, "%s\n", __func__);
+ return (ENXIO);
+}
+
+/* keyboard interrupt routine, not used */
+static int
+hvkbd_intr(keyboard_t *kbd, void *arg)
+{
+ DEBUG_HVKBD(kbd, "%s\n", __func__);
+ return (0);
+}
+
+/* lock the access to the keyboard, not used */
+static int
+hvkbd_lock(keyboard_t *kbd, int lock)
+{
+ DEBUG_HVKBD(kbd, "%s\n", __func__);
+ return (1);
+}
+
+/* save the internal state, not used */
+static int
+hvkbd_get_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ DEBUG_HVKBD(kbd,"%s\n", __func__);
+ return (len == 0) ? 1 : -1;
+}
+
+/* set the internal state, not used */
+static int
+hvkbd_set_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ DEBUG_HVKBD(kbd, "%s\n", __func__);
+ return (EINVAL);
+}
+
+static int
+hvkbd_poll(keyboard_t *kbd, int on)
+{
+ hv_kbd_sc *sc = kbd->kb_data;
+
+ HVKBD_LOCK();
+ /*
+ * Keep a reference count on polling to allow recursive
+ * cngrab() during a panic for example.
+ */
+ if (on)
+ sc->sc_polling++;
+ else if (sc->sc_polling > 0)
+ sc->sc_polling--;
+
+ if (sc->sc_polling != 0) {
+ sc->sc_flags |= HVKBD_FLAG_POLLING;
+ } else {
+ sc->sc_flags &= ~HVKBD_FLAG_POLLING;
+ }
+ HVKBD_UNLOCK();
+ return (0);
+}
+
+/*
+ * Enable the access to the device; until this function is called,
+ * the client cannot read from the keyboard.
+ */
+static int
+hvkbd_enable(keyboard_t *kbd)
+{
+ HVKBD_LOCK();
+ KBD_ACTIVATE(kbd);
+ HVKBD_UNLOCK();
+ return (0);
+}
+
+/* disallow the access to the device */
+static int
+hvkbd_disable(keyboard_t *kbd)
+{
+ DEBUG_HVKBD(kbd, "%s\n", __func__);
+ HVKBD_LOCK();
+ KBD_DEACTIVATE(kbd);
+ HVKBD_UNLOCK();
+ return (0);
+}
+
+static void
+hvkbd_do_poll(hv_kbd_sc *sc, uint8_t wait)
+{
+ while (!hv_kbd_prod_is_ready(sc)) {
+ hv_kbd_read_channel(NULL, sc);
+ if (!wait)
+ break;
+ }
+}
+
+/* check if data is waiting */
+/* Currently unused. */
+static int
+hvkbd_check(keyboard_t *kbd)
+{
+ DEBUG_HVKBD(kbd, "%s\n", __func__);
+ return (0);
+}
+
+/* check if char is waiting */
+static int
+hvkbd_check_char_locked(keyboard_t *kbd)
+{
+ HVKBD_LOCK_ASSERT();
+ if (!KBD_IS_ACTIVE(kbd))
+ return (FALSE);
+
+ hv_kbd_sc *sc = kbd->kb_data;
+ if (sc->sc_flags & HVKBD_FLAG_POLLING)
+ hvkbd_do_poll(sc, 0);
+ if (hv_kbd_prod_is_ready(sc)) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+static int
+hvkbd_check_char(keyboard_t *kbd)
+{
+ int result;
+
+ HVKBD_LOCK();
+ result = hvkbd_check_char_locked(kbd);
+ HVKBD_UNLOCK();
+
+ return (result);
+}
+
+/* read char from the keyboard */
+static uint32_t
+hvkbd_read_char_locked(keyboard_t *kbd, int wait)
+{
+ uint32_t scancode = NOKEY;
+ keystroke ks;
+ hv_kbd_sc *sc = kbd->kb_data;
+ HVKBD_LOCK_ASSERT();
+
+ 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);
+ }
+ } else {
+ if (bootverbose)
+ device_printf(sc->dev, "Unsupported mode: %d\n", sc->sc_mode);
+ }
+ ++kbd->kb_count;
+ DEBUG_HVKBD(kbd, "read scan: 0x%x\n", scancode);
+ return scancode;
+}
+
+/* Currently wait is always false. */
+static uint32_t
+hvkbd_read_char(keyboard_t *kbd, int wait)
+{
+ uint32_t keycode;
+
+ HVKBD_LOCK();
+ keycode = hvkbd_read_char_locked(kbd, wait);
+ HVKBD_UNLOCK();
+
+ return (keycode);
+}
+
+/* clear the internal state of the keyboard */
+static void
+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;
+}
+
+static int
+hvkbd_ioctl_locked(keyboard_t *kbd, u_long cmd, caddr_t arg)
+{
+ int i;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ int ival;
+#endif
+ hv_kbd_sc *sc = kbd->kb_data;
+ switch (cmd) {
+ case KDGKBMODE:
+ *(int *)arg = sc->sc_mode;
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 7):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSKBMODE: /* set keyboard mode */
+ DEBUG_HVKBD(kbd, "expected mode: %x\n", *(int *)arg);
+ switch (*(int *)arg) {
+ case K_XLATE:
+ if (sc->sc_mode != K_XLATE) {
+ /* make lock key state and LED state match */
+ sc->sc_state &= ~LOCK_MASK;
+ sc->sc_state |= KBD_LED_VAL(kbd);
+ }
+ /* FALLTHROUGH */
+ case K_RAW:
+ case K_CODE:
+ if (sc->sc_mode != *(int *)arg) {
+ DEBUG_HVKBD(kbd, "mod changed to %x\n", *(int *)arg);
+ if ((sc->sc_flags & HVKBD_FLAG_POLLING) == 0)
+ hvkbd_clear_state(kbd);
+ sc->sc_mode = *(int *)arg;
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+ break;
+ case KDGKBSTATE: /* get lock key state */
+ *(int *)arg = sc->sc_state & LOCK_MASK;
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 20):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSKBSTATE: /* set lock key state */
+ if (*(int *)arg & ~LOCK_MASK) {
+ return (EINVAL);
+ }
+ sc->sc_state &= ~LOCK_MASK;
+ sc->sc_state |= *(int *)arg;
+ return hvkbd_ioctl_locked(kbd, KDSETLED, arg);
+ case KDGETLED: /* get keyboard LED */
+ *(int *)arg = KBD_LED_VAL(kbd);
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 66):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSETLED: /* set keyboard LED */
+ /* NOTE: lock key state in "sc_state" won't be changed */
+ if (*(int *)arg & ~LOCK_MASK)
+ return (EINVAL);
+
+ i = *(int *)arg;
+
+ /* replace CAPS LED with ALTGR LED for ALTGR keyboards */
+ if (sc->sc_mode == K_XLATE &&
+ kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
+ if (i & ALKED)
+ i |= CLKED;
+ else
+ i &= ~CLKED;
+ }
+ if (KBD_HAS_DEVICE(kbd)) {
+ DEBUG_HVSC(sc, "setled 0x%x\n", *(int *)arg);
+ }
+
+ KBD_LED_VAL(kbd) = *(int *)arg;
+ break;
+ default:
+ return (genkbd_commonioctl(kbd, cmd, arg));
+ }
+ return (0);
+}
+
+/* some useful control functions */
+static int
+hvkbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
+{
+ DEBUG_HVKBD(kbd, "%s: %lx start\n", __func__, cmd);
+ HVKBD_LOCK();
+ int ret = hvkbd_ioctl_locked(kbd, cmd, arg);
+ HVKBD_UNLOCK();
+ DEBUG_HVKBD(kbd, "%s: %lx end %d\n", __func__, cmd, ret);
+ return (ret);
+}
+
+/* read one byte from the keyboard if it's allowed */
+/* Currently unused. */
+static int
+hvkbd_read(keyboard_t *kbd, int wait)
+{
+ DEBUG_HVKBD(kbd, "%s\n", __func__);
+ HVKBD_LOCK_ASSERT();
+ if (!KBD_IS_ACTIVE(kbd))
+ return (-1);
+ return hvkbd_read_char_locked(kbd, wait);
+}
+
+static keyboard_switch_t hvkbdsw = {
+ hvkbd_probe, /* not used */
+ hvkbd_init,
+ hvkbd_term, /* not used */
+ hvkbd_intr, /* not used */
+ hvkbd_test_if, /* not used */
+ hvkbd_enable,
+ hvkbd_disable,
+ hvkbd_read,
+ hvkbd_check,
+ hvkbd_read_char,
+ hvkbd_check_char,
+ hvkbd_ioctl,
+ hvkbd_lock, /* not used */
+ hvkbd_clear_state,
+ hvkbd_get_state, /* not used */
+ hvkbd_set_state, /* not used */
+ genkbd_get_fkeystr,
+ hvkbd_poll,
+ genkbd_diag,
+};
+
+KEYBOARD_DRIVER(hvkbd, hvkbdsw, hvkbd_configure);
+
+void
+hv_kbd_intr(hv_kbd_sc *sc)
+{
+ uint32_t c;
+ if ((sc->sc_flags & HVKBD_FLAG_POLLING) != 0)
+ return;
+
+ if (KBD_IS_ACTIVE(&sc->sc_kbd) &&
+ KBD_IS_BUSY(&sc->sc_kbd)) {
+ /* let the callback function process the input */
+ (sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
+ sc->sc_kbd.kb_callback.kc_arg);
+ } else {
+ /* read and discard the input, no one is waiting for it */
+ do {
+ c = hvkbd_read_char(&sc->sc_kbd, 0);
+ } while (c != NOKEY);
+ }
+}
+
+int
+hvkbd_driver_load(module_t mod, int what, void *arg)
+{
+ switch (what) {
+ case MOD_LOAD:
+ kbd_add_driver(&hvkbd_kbd_driver);
+ break;
+ case MOD_UNLOAD:
+ kbd_delete_driver(&hvkbd_kbd_driver);
+ break;
+ }
+ return (0);
+}
+
+int
+hv_kbd_drv_attach(device_t dev)
+{
+ hv_kbd_sc *sc = device_get_softc(dev);
+ int unit = device_get_unit(dev);
+ keyboard_t *kbd = &sc->sc_kbd;
+ keyboard_switch_t *sw;
+ sw = kbd_get_switch(HVKBD_DRIVER_NAME);
+ if (sw == NULL) {
+ return (ENXIO);
+ }
+
+ kbd_init_struct(kbd, HVKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0);
+ kbd->kb_data = (void *)sc;
+ kbd_set_maps(kbd, &key_map, &accent_map, fkey_tab, nitems(fkey_tab));
+ KBD_FOUND_DEVICE(kbd);
+ hvkbd_clear_state(kbd);
+ KBD_PROBE_DONE(kbd);
+ KBD_INIT_DONE(kbd);
+ sc->sc_mode = K_RAW;
+ (*sw->enable)(kbd);
+
+ if (kbd_register(kbd) < 0) {
+ goto detach;
+ }
+ KBD_CONFIG_DONE(kbd);
+#ifdef KBD_INSTALL_CDEV
+ if (kbd_attach(kbd)) {
+ goto detach;
+ }
+#endif
+ if (bootverbose) {
+ genkbd_diag(kbd, bootverbose);
+ }
+ return (0);
+detach:
+ hv_kbd_drv_detach(dev);
+ return (ENXIO);
+}
+
+int
+hv_kbd_drv_detach(device_t dev)
+{
+ int error = 0;
+ hv_kbd_sc *sc = device_get_softc(dev);
+ hvkbd_disable(&sc->sc_kbd);
+ if (KBD_IS_CONFIGURED(&sc->sc_kbd)) {
+ error = kbd_unregister(&sc->sc_kbd);
+ if (error) {
+ device_printf(dev, "WARNING: kbd_unregister() "
+ "returned non-zero! (ignored)\n");
+ }
+ }
+#ifdef KBD_INSTALL_CDEV
+ error = kbd_detach(&sc->sc_kbd);
+#endif
+ return (error);
+}
+
Index: head/sys/dev/hyperv/input/hv_kbdc.h
===================================================================
--- head/sys/dev/hyperv/input/hv_kbdc.h
+++ head/sys/dev/hyperv/input/hv_kbdc.h
@@ -0,0 +1,104 @@
+/*-
+ * 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$
+ */
+
+#ifndef _HV_KBD_H
+#define _HV_KBD_H
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/systm.h>
+
+#include <dev/kbd/kbdreg.h>
+
+#define HVKBD_DRIVER_NAME "hvkbd"
+#define IS_UNICODE (1)
+#define IS_BREAK (2)
+#define IS_E0 (4)
+#define IS_E1 (8)
+
+#define XTKBD_EMUL0 (0xe0)
+#define XTKBD_EMUL1 (0xe1)
+#define XTKBD_RELEASE (0x80)
+
+#define DEBUG_HVSC(sc, ...) do { \
+ if (sc->debug > 0) { \
+ device_printf(sc->dev, __VA_ARGS__); \
+ } \
+} while (0)
+#define DEBUG_HVKBD(kbd, ...) do { \
+ hv_kbd_sc *sc = (kbd)->kb_data; \
+ DEBUG_HVSC(sc, __VA_ARGS__); \
+} while (0)
+
+struct vmbus_channel;
+struct vmbus_xact_ctx;
+
+typedef struct keystroke_t {
+ uint16_t makecode;
+ uint32_t info;
+} keystroke;
+
+typedef struct keystroke_info {
+ LIST_ENTRY(keystroke_info) link;
+ STAILQ_ENTRY(keystroke_info) slink;
+ keystroke ks;
+} keystroke_info;
+
+typedef struct hv_kbd_sc_t {
+ struct vmbus_channel *hs_chan;
+ device_t dev;
+ struct vmbus_xact_ctx *hs_xact_ctx;
+ int32_t buflen;
+ uint8_t *buf;
+
+ struct mtx ks_mtx;
+ LIST_HEAD(, keystroke_info) ks_free_list;
+ STAILQ_HEAD(, keystroke_info) ks_queue; /* keystroke info queue */
+
+ keyboard_t sc_kbd;
+ int sc_mode;
+ int sc_state;
+ int sc_polling; /* polling recursion count */
+ uint32_t sc_flags;
+ int debug;
+} hv_kbd_sc;
+
+int hv_kbd_produce_ks(hv_kbd_sc *sc, const keystroke *ks);
+int hv_kbd_fetch_top(hv_kbd_sc *sc, keystroke *top);
+int hv_kbd_modify_top(hv_kbd_sc *sc, keystroke *top);
+int hv_kbd_remove_top(hv_kbd_sc *sc);
+int hv_kbd_prod_is_ready(hv_kbd_sc *sc);
+void hv_kbd_read_channel(struct vmbus_channel *, void *);
+
+int hv_kbd_drv_attach(device_t dev);
+int hv_kbd_drv_detach(device_t dev);
+
+int hvkbd_driver_load(module_t, int, void *);
+void hv_kbd_intr(hv_kbd_sc *sc);
+#endif
Index: head/sys/dev/hyperv/input/hv_kbdc.c
===================================================================
--- head/sys/dev/hyperv/input/hv_kbdc.c
+++ head/sys/dev/hyperv/input/hv_kbdc.c
@@ -0,0 +1,532 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/taskqueue.h>
+#include <sys/selinfo.h>
+#include <sys/sysctl.h>
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/syscallsubr.h>
+#include <sys/sysproto.h>
+#include <sys/systm.h>
+#include <sys/mutex.h>
+
+#include <sys/kbio.h>
+#include <dev/kbd/kbdreg.h>
+#include <dev/kbd/kbdtables.h>
+
+#include <dev/hyperv/include/hyperv.h>
+#include <dev/hyperv/utilities/hv_utilreg.h>
+#include <dev/hyperv/utilities/vmbus_icreg.h>
+#include <dev/hyperv/utilities/vmbus_icvar.h>
+#include <dev/hyperv/include/vmbus_xact.h>
+
+#include "dev/hyperv/input/hv_kbdc.h"
+#include "vmbus_if.h"
+
+#define HV_KBD_VER_MAJOR (1)
+#define HV_KBD_VER_MINOR (0)
+
+#define HV_KBD_VER (HV_KBD_VER_MINOR | (HV_KBD_VER_MAJOR) << 16)
+
+#define HV_KBD_PROTO_ACCEPTED (1)
+
+#define HV_BUFF_SIZE (4*PAGE_SIZE)
+#define HV_KBD_RINGBUFF_SEND_SZ (10*PAGE_SIZE)
+#define HV_KBD_RINGBUFF_RECV_SZ (10*PAGE_SIZE)
+
+enum hv_kbd_msg_type_t {
+ HV_KBD_PROTO_REQUEST = 1,
+ HV_KBD_PROTO_RESPONSE = 2,
+ HV_KBD_PROTO_EVENT = 3,
+ HV_KBD_PROTO_LED_INDICATORS = 4,
+};
+
+typedef struct hv_kbd_msg_hdr_t {
+ uint32_t type;
+} hv_kbd_msg_hdr;
+
+typedef struct hv_kbd_msg_t {
+ hv_kbd_msg_hdr hdr;
+ char data[];
+} hv_kbd_msg;
+
+typedef struct hv_kbd_proto_req_t {
+ hv_kbd_msg_hdr hdr;
+ uint32_t ver;
+} hv_kbd_proto_req;
+
+typedef struct hv_kbd_proto_resp_t {
+ hv_kbd_msg_hdr hdr;
+ uint32_t status;
+} hv_kbd_proto_resp;
+
+#define HV_KBD_PROTO_REQ_SZ (sizeof(hv_kbd_proto_req))
+#define HV_KBD_PROTO_RESP_SZ (sizeof(hv_kbd_proto_resp))
+
+/**
+ * the struct in win host:
+ * typedef struct _HK_MESSAGE_KEYSTROKE
+ * {
+ * HK_MESSAGE_HEADER Header;
+ * UINT16 MakeCode;
+ * UINT32 IsUnicode:1;
+ * UINT32 IsBreak:1;
+ * UINT32 IsE0:1;
+ * UINT32 IsE1:1;
+ * UINT32 Reserved:28;
+ * } HK_MESSAGE_KEYSTROKE
+ */
+typedef struct hv_kbd_keystroke_t {
+ hv_kbd_msg_hdr hdr;
+ keystroke ks;
+} hv_kbd_keystroke;
+
+static const struct vmbus_ic_desc vmbus_kbd_descs[] = {
+ {
+ .ic_guid = { .hv_guid = {
+ 0x6d, 0xad, 0x12, 0xf9, 0x17, 0x2b, 0xea, 0x48,
+ 0xbd, 0x65, 0xf9, 0x27, 0xa6, 0x1c, 0x76, 0x84} },
+ .ic_desc = "Hyper-V KBD"
+ },
+ VMBUS_IC_DESC_END
+};
+
+static int hv_kbd_attach(device_t dev);
+static int hv_kbd_detach(device_t dev);
+
+/**
+ * return 1 if producer is ready
+ */
+int
+hv_kbd_prod_is_ready(hv_kbd_sc *sc)
+{
+ int ret;
+ mtx_lock(&sc->ks_mtx);
+ ret = !STAILQ_EMPTY(&sc->ks_queue);
+ mtx_unlock(&sc->ks_mtx);
+ return (ret);
+}
+
+int
+hv_kbd_produce_ks(hv_kbd_sc *sc, const keystroke *ks)
+{
+ int ret = 0;
+ keystroke_info *ksi;
+ mtx_lock(&sc->ks_mtx);
+ if (LIST_EMPTY(&sc->ks_free_list)) {
+ DEBUG_HVSC(sc, "NO buffer!\n");
+ ret = 1;
+ } else {
+ ksi = LIST_FIRST(&sc->ks_free_list);
+ LIST_REMOVE(ksi, link);
+ ksi->ks = *ks;
+ STAILQ_INSERT_TAIL(&sc->ks_queue, ksi, slink);
+ }
+ mtx_unlock(&sc->ks_mtx);
+ return (ret);
+}
+
+/**
+ * return 0 if successfully get the 1st item of queue without removing it
+ */
+int
+hv_kbd_fetch_top(hv_kbd_sc *sc, keystroke *result)
+{
+ int ret = 0;
+ keystroke_info *ksi = NULL;
+ mtx_lock(&sc->ks_mtx);
+ if (STAILQ_EMPTY(&sc->ks_queue)) {
+ DEBUG_HVSC(sc, "Empty queue!\n");
+ ret = 1;
+ } else {
+ ksi = STAILQ_FIRST(&sc->ks_queue);
+ *result = ksi->ks;
+ }
+ mtx_unlock(&sc->ks_mtx);
+ return (ret);
+}
+
+/**
+ * return 0 if successfully removing the top item
+ */
+int
+hv_kbd_remove_top(hv_kbd_sc *sc)
+{
+ int ret = 0;
+ keystroke_info *ksi = NULL;
+ mtx_lock(&sc->ks_mtx);
+ if (STAILQ_EMPTY(&sc->ks_queue)) {
+ DEBUG_HVSC(sc, "Empty queue!\n");
+ ret = 1;
+ } else {
+ ksi = STAILQ_FIRST(&sc->ks_queue);
+ STAILQ_REMOVE_HEAD(&sc->ks_queue, slink);
+ LIST_INSERT_HEAD(&sc->ks_free_list, ksi, link);
+ }
+ mtx_unlock(&sc->ks_mtx);
+ return (ret);
+}
+
+/**
+ * return 0 if successfully modify the 1st item of queue
+ */
+int
+hv_kbd_modify_top(hv_kbd_sc *sc, keystroke *top)
+{
+ int ret = 0;
+ keystroke_info *ksi = NULL;
+ mtx_lock(&sc->ks_mtx);
+ if (STAILQ_EMPTY(&sc->ks_queue)) {
+ DEBUG_HVSC(sc, "Empty queue!\n");
+ ret = 1;
+ } else {
+ ksi = STAILQ_FIRST(&sc->ks_queue);
+ ksi->ks = *top;
+ }
+ mtx_unlock(&sc->ks_mtx);
+ return (ret);
+}
+
+static int
+hv_kbd_probe(device_t dev)
+{
+ device_t bus = device_get_parent(dev);
+ const struct vmbus_ic_desc *d;
+
+ if (resource_disabled(device_get_name(dev), 0))
+ return (ENXIO);
+
+ for (d = vmbus_kbd_descs; d->ic_desc != NULL; ++d) {
+ if (VMBUS_PROBE_GUID(bus, dev, &d->ic_guid) == 0) {
+ device_set_desc(dev, d->ic_desc);
+ return (BUS_PROBE_DEFAULT);
+ }
+ }
+ return (ENXIO);
+}
+
+static void
+hv_kbd_on_response(hv_kbd_sc *sc, struct vmbus_chanpkt_hdr *pkt)
+{
+ struct vmbus_xact_ctx *xact = sc->hs_xact_ctx;
+ if (xact != NULL) {
+ DEBUG_HVSC(sc, "hvkbd is ready\n");
+ vmbus_xact_ctx_wakeup(xact, VMBUS_CHANPKT_CONST_DATA(pkt),
+ VMBUS_CHANPKT_DATALEN(pkt));
+ }
+}
+
+static void
+hv_kbd_on_received(hv_kbd_sc *sc, struct vmbus_chanpkt_hdr *pkt)
+{
+
+ const hv_kbd_msg *msg = VMBUS_CHANPKT_CONST_DATA(pkt);
+ const hv_kbd_proto_resp *resp =
+ VMBUS_CHANPKT_CONST_DATA(pkt);
+ const hv_kbd_keystroke *keystroke =
+ VMBUS_CHANPKT_CONST_DATA(pkt);
+ uint32_t msg_len = VMBUS_CHANPKT_DATALEN(pkt);
+ enum hv_kbd_msg_type_t msg_type;
+ uint32_t info;
+ uint16_t scan_code;
+
+ if (msg_len <= sizeof(hv_kbd_msg)) {
+ device_printf(sc->dev, "Illegal packet\n");
+ return;
+ }
+ msg_type = msg->hdr.type;
+ switch (msg_type) {
+ case HV_KBD_PROTO_RESPONSE:
+ hv_kbd_on_response(sc, pkt);
+ DEBUG_HVSC(sc, "keyboard resp: 0x%x\n",
+ resp->status);
+ break;
+ case HV_KBD_PROTO_EVENT:
+ info = keystroke->ks.info;
+ scan_code = keystroke->ks.makecode;
+ DEBUG_HVSC(sc, "keystroke info: 0x%x, scan: 0x%x\n",
+ info, scan_code);
+ hv_kbd_produce_ks(sc, &keystroke->ks);
+ hv_kbd_intr(sc);
+ default:
+ break;
+ }
+}
+
+void
+hv_kbd_read_channel(struct vmbus_channel *channel, void *context)
+{
+ uint8_t *buf;
+ uint32_t buflen = 0;
+ int ret = 0;
+
+ hv_kbd_sc *sc = (hv_kbd_sc*)context;
+ channel = vmbus_get_channel(sc->dev);
+ buf = sc->buf;
+ buflen = sc->buflen;
+ for (;;) {
+ struct vmbus_chanpkt_hdr *pkt = (struct vmbus_chanpkt_hdr *)buf;
+ uint32_t rxed = buflen;
+
+ ret = vmbus_chan_recv_pkt(channel, pkt, &rxed);
+ if (__predict_false(ret == ENOBUFS)) {
+ buflen = sc->buflen * 2;
+ while (buflen < rxed)
+ buflen *= 2;
+ buf = malloc(buflen, M_DEVBUF, M_WAITOK | M_ZERO);
+ device_printf(sc->dev, "expand recvbuf %d -> %d\n",
+ sc->buflen, buflen);
+ free(sc->buf, M_DEVBUF);
+ sc->buf = buf;
+ sc->buflen = buflen;
+ continue;
+ } else if (__predict_false(ret == EAGAIN)) {
+ /* No more channel packets; done! */
+ break;
+ }
+ KASSERT(!ret, ("vmbus_chan_recv_pkt failed: %d", ret));
+
+ DEBUG_HVSC(sc, "event: 0x%x\n", pkt->cph_type);
+ switch (pkt->cph_type) {
+ case VMBUS_CHANPKT_TYPE_COMP:
+ case VMBUS_CHANPKT_TYPE_RXBUF:
+ device_printf(sc->dev, "unhandled event: %d\n",
+ pkt->cph_type);
+ break;
+ case VMBUS_CHANPKT_TYPE_INBAND:
+ hv_kbd_on_received(sc, pkt);
+ break;
+ default:
+ device_printf(sc->dev, "unknown event: %d\n",
+ pkt->cph_type);
+ break;
+ }
+ }
+}
+
+static int
+hv_kbd_connect_vsp(hv_kbd_sc *sc)
+{
+ int ret;
+ size_t resplen;
+ struct vmbus_xact *xact;
+ hv_kbd_proto_req *req;
+ const hv_kbd_proto_resp *resp;
+
+ xact = vmbus_xact_get(sc->hs_xact_ctx, sizeof(*req));
+ if (xact == NULL) {
+ device_printf(sc->dev, "no xact for kbd init");
+ return (ENODEV);
+ }
+ req = vmbus_xact_req_data(xact);
+ req->hdr.type = HV_KBD_PROTO_REQUEST;
+ req->ver = HV_KBD_VER;
+
+ vmbus_xact_activate(xact);
+ ret = vmbus_chan_send(sc->hs_chan,
+ VMBUS_CHANPKT_TYPE_INBAND,
+ VMBUS_CHANPKT_FLAG_RC,
+ req, sizeof(hv_kbd_proto_req),
+ (uint64_t)(uintptr_t)xact);
+ if (ret) {
+ device_printf(sc->dev, "fail to send\n");
+ vmbus_xact_deactivate(xact);
+ return (ret);
+ }
+ resp = vmbus_chan_xact_wait(sc->hs_chan, xact, &resplen, true);
+ if (resplen < HV_KBD_PROTO_RESP_SZ) {
+ device_printf(sc->dev, "hv_kbd init communicate failed\n");
+ ret = ENODEV;
+ goto clean;
+ }
+
+ if (!(resp->status & HV_KBD_PROTO_ACCEPTED)) {
+ device_printf(sc->dev, "hv_kbd protocol request failed\n");
+ ret = ENODEV;
+ }
+clean:
+ vmbus_xact_put(xact);
+ DEBUG_HVSC(sc, "finish connect vsp\n");
+ return (ret);
+}
+
+static int
+hv_kbd_attach1(device_t dev, vmbus_chan_callback_t cb)
+{
+ int ret;
+ hv_kbd_sc *sc;
+
+ sc = device_get_softc(dev);
+ sc->buflen = HV_BUFF_SIZE;
+ sc->buf = malloc(sc->buflen, M_DEVBUF, M_WAITOK | M_ZERO);
+ vmbus_chan_set_readbatch(sc->hs_chan, false);
+ ret = vmbus_chan_open(
+ sc->hs_chan,
+ HV_KBD_RINGBUFF_SEND_SZ,
+ HV_KBD_RINGBUFF_RECV_SZ,
+ NULL, 0,
+ cb,
+ sc);
+ if (ret != 0) {
+ free(sc->buf, M_DEVBUF);
+ }
+ return (ret);
+}
+
+static int
+hv_kbd_detach1(device_t dev)
+{
+ hv_kbd_sc *sc = device_get_softc(dev);
+ vmbus_chan_close(vmbus_get_channel(dev));
+ free(sc->buf, M_DEVBUF);
+ return (0);
+}
+
+static void
+hv_kbd_init(hv_kbd_sc *sc)
+{
+ const int max_list = 16;
+ int i;
+ keystroke_info *ksi;
+
+ mtx_init(&sc->ks_mtx, "hv_kbdc mutex", NULL, MTX_DEF);
+ LIST_INIT(&sc->ks_free_list);
+ STAILQ_INIT(&sc->ks_queue);
+ for (i = 0; i < max_list; i++) {
+ ksi = malloc(sizeof(keystroke_info),
+ M_DEVBUF, M_WAITOK|M_ZERO);
+ LIST_INSERT_HEAD(&sc->ks_free_list, ksi, link);
+ }
+}
+
+static void
+hv_kbd_fini(hv_kbd_sc *sc)
+{
+ keystroke_info *ksi;
+ while (!LIST_EMPTY(&sc->ks_free_list)) {
+ ksi = LIST_FIRST(&sc->ks_free_list);
+ LIST_REMOVE(ksi, link);
+ free(ksi, M_DEVBUF);
+ }
+ while (!STAILQ_EMPTY(&sc->ks_queue)) {
+ ksi = STAILQ_FIRST(&sc->ks_queue);
+ STAILQ_REMOVE_HEAD(&sc->ks_queue, slink);
+ free(ksi, M_DEVBUF);
+ }
+ mtx_destroy(&sc->ks_mtx);
+}
+
+static void
+hv_kbd_sysctl(device_t dev)
+{
+ struct sysctl_oid_list *child;
+ struct sysctl_ctx_list *ctx;
+ hv_kbd_sc *sc;
+
+ sc = device_get_softc(dev);
+ ctx = device_get_sysctl_ctx(dev);
+ child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+ SYSCTL_ADD_INT(ctx, child, OID_AUTO, "debug", CTLFLAG_RW,
+ &sc->debug, 0, "debug hyperv keyboard");
+}
+
+static int
+hv_kbd_attach(device_t dev)
+{
+ int error = 0;
+ hv_kbd_sc *sc;
+
+ sc = device_get_softc(dev);
+ sc->hs_chan = vmbus_get_channel(dev);
+ sc->dev = dev;
+ hv_kbd_init(sc);
+ sc->hs_xact_ctx = vmbus_xact_ctx_create(bus_get_dma_tag(dev),
+ HV_KBD_PROTO_REQ_SZ, HV_KBD_PROTO_RESP_SZ, 0);
+ if (sc->hs_xact_ctx == NULL) {
+ error = ENOMEM;
+ goto failed;
+ }
+
+ error = hv_kbd_attach1(dev, hv_kbd_read_channel);
+ if (error)
+ goto failed;
+ error = hv_kbd_connect_vsp(sc);
+ if (error)
+ goto failed;
+
+ error = hv_kbd_drv_attach(dev);
+ if (error)
+ goto failed;
+ hv_kbd_sysctl(dev);
+ return (0);
+failed:
+ hv_kbd_detach(dev);
+ return (error);
+}
+
+static int
+hv_kbd_detach(device_t dev)
+{
+ int ret;
+ hv_kbd_sc *sc = device_get_softc(dev);
+ hv_kbd_fini(sc);
+ if (sc->hs_xact_ctx != NULL)
+ vmbus_xact_ctx_destroy(sc->hs_xact_ctx);
+ ret = hv_kbd_detach1(dev);
+ if (!ret)
+ device_printf(dev, "Fail to detach\n");
+ return hv_kbd_drv_detach(dev);
+}
+
+static device_method_t kbd_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, hv_kbd_probe),
+ DEVMETHOD(device_attach, hv_kbd_attach),
+ DEVMETHOD(device_detach, hv_kbd_detach),
+ { 0, 0 }
+};
+
+static driver_t kbd_driver = {HVKBD_DRIVER_NAME , kbd_methods, sizeof(hv_kbd_sc)};
+
+static devclass_t kbd_devclass;
+
+DRIVER_MODULE(hv_kbd, vmbus, kbd_driver, kbd_devclass, hvkbd_driver_load, NULL);
+MODULE_VERSION(hv_kbd, 1);
+MODULE_DEPEND(hv_kbd, vmbus, 1, 1, 1);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Jan 14, 12:17 AM (21 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15789124
Default Alt Text
D10196.diff (30 KB)
Attached To
Mode
D10196: implement initial Hyperv keyboard synthetic driver
Attached
Detach File
Event Timeline
Log In to Comment