Add ucc(4), a driver for USB HID Consumer Control keyboards. Such
authoranton <anton@openbsd.org>
Fri, 20 Aug 2021 05:23:18 +0000 (05:23 +0000)
committeranton <anton@openbsd.org>
Fri, 20 Aug 2021 05:23:18 +0000 (05:23 +0000)
keyboard is a pseudo device which is used to expose audio and
application launch keys. My prime motivation is to get the volume mute,
increment and decrement keys to just work on my keyboard without the
need to use usbhidaction(1).

Looks reasonable to kettenis@ mpi@ and ok jcs@

14 files changed:
sys/arch/alpha/conf/GENERIC
sys/arch/amd64/conf/GENERIC
sys/arch/arm64/conf/GENERIC
sys/arch/armv7/conf/GENERIC
sys/arch/hppa/conf/GENERIC
sys/arch/i386/conf/GENERIC
sys/arch/landisk/conf/GENERIC
sys/arch/loongson/conf/GENERIC
sys/arch/macppc/conf/GENERIC
sys/arch/octeon/conf/GENERIC
sys/arch/powerpc64/conf/GENERIC
sys/arch/sparc64/conf/GENERIC
sys/dev/usb/files.usb
sys/dev/usb/ucc.c [new file with mode: 0644]

index 8af652c..b917872 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.268 2021/02/04 16:25:38 anton Exp $
+#      $OpenBSD: GENERIC,v 1.269 2021/08/20 05:23:18 anton Exp $
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -107,6 +107,8 @@ uslhcom* at uhidev?                 # Silicon Labs CP2110 USB HID UART
 ucom*  at uslhcom?
 uhid*  at uhidev?                      # USB generic HID support
 fido*  at uhidev?                      # FIDO/U2F security key support
+ucc*   at uhidev?                      # Consumer Control keyboards
+wskbd* at ucc? mux 1
 ujoy*  at uhidev?                      # USB joystick/gamecontroller support
 uhidpp*        at uhidev?              # Logitech HID++ Devices
 upd*   at uhidev?                      # USB Power Devices sensors
index c0c16f7..89316c6 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.498 2021/04/28 11:32:59 bluhm Exp $
+#      $OpenBSD: GENERIC,v 1.499 2021/08/20 05:23:18 anton Exp $
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -286,6 +286,8 @@ uslhcom* at uhidev?         # Silicon Labs CP2110 USB HID UART
 ucom*  at uslhcom?
 uhid*  at uhidev?              # USB generic HID support
 fido*  at uhidev?              # FIDO/U2F security key support
+ucc*   at uhidev?              # Consumer Control keyboards
+wskbd* at ucc? mux 1
 ujoy*  at uhidev?              # USB joystick/gamecontroller support
 uhidpp*        at uhidev?              # Logitech HID++ Devices
 upd*   at uhidev?              # USB Power Devices sensors
index ff54bee..5ee5cfb 100644 (file)
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.203 2021/06/29 12:43:33 patrick Exp $
+# $OpenBSD: GENERIC,v 1.204 2021/08/20 05:23:18 anton Exp $
 #
 # GENERIC machine description file
 #
@@ -392,6 +392,8 @@ uslhcom*    at uhidev?              # Silicon Labs CP2110 USB HID UART
 ucom*          at uslhcom?
 uhid*          at uhidev?              # USB generic HID support
 fido*          at uhidev?              # FIDO/U2F security key support
+ucc*           at uhidev?              # Consumer Control keyboards
+wskbd*         at ucc? mux 1
 ujoy*          at uhidev?              # USB joystick/gamecontroller support
 uhidpp*                at uhidev?              # Logitech HID++ Devices
 upd*           at uhidev?              # USB Power Devices sensors
index 1af5eb8..d8cf637 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.138 2021/05/28 15:52:11 visa Exp $
+#      $OpenBSD: GENERIC,v 1.139 2021/08/20 05:23:18 anton Exp $
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -327,6 +327,8 @@ uslhcom* at uhidev?         # Silicon Labs CP2110 USB HID UART
 ucom*  at uslhcom?
 uhid*  at uhidev?              # USB generic HID support
 fido*  at uhidev?              # FIDO/U2F security key support
+ucc*   at uhidev?              # Consumer Control keyboards
+wskbd* at ucc? mux 1
 ujoy*  at uhidev?              # USB joystick/gamecontroller support
 uhidpp*        at uhidev?              # Logitech HID++ Devices
 upd*   at uhidev?              # USB Power Devices sensors
index 1a27dbb..f533dd3 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.182 2021/02/04 16:25:39 anton Exp $
+#      $OpenBSD: GENERIC,v 1.183 2021/08/20 05:23:18 anton Exp $
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -111,6 +111,8 @@ ukbd*       at uhidev?              # USB keyboard
 wskbd* at ukbd? mux 1
 uhid*  at uhidev?              # USB generic HID support
 fido*  at uhidev?              # FIDO/U2F security key support
+ucc*   at uhidev?              # Consumer Control keyboards
+wskbd* at ucc? mux 1
 ujoy*  at uhidev?              # USB joystick/gamecontroller support
 uhidpp*        at uhidev?              # Logitech HID++ Devices
 upd*   at uhidev?              # USB Power Devices sensors
index b49353a..d82693d 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.856 2021/04/28 11:32:59 bluhm Exp $
+#      $OpenBSD: GENERIC,v 1.857 2021/08/20 05:23:18 anton Exp $
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -284,6 +284,8 @@ uticom* at uhub?            # TI serial
 ucom*  at uticom?
 uhid*  at uhidev?              # USB generic HID support
 fido*  at uhidev?              # FIDO/U2F security key support
+ucc*   at uhidev?              # Consumer Control keyboards
+wskbd* at ucc? mux 1
 ujoy*  at uhidev?              # USB joystick/gamecontroller support
 uhidpp*        at uhidev?              # Logitech HID++ Devices
 upd*   at uhidev?              # USB Power Devices sensors
index a209141..01a5ef1 100644 (file)
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.57 2021/02/04 16:25:39 anton Exp $
+# $OpenBSD: GENERIC,v 1.58 2021/08/20 05:23:18 anton Exp $
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -137,6 +137,8 @@ uslhcom* at uhidev?         # Silicon Labs CP2110 USB HID UART
 ucom*  at uslhcom?
 uhid*  at uhidev?              # USB generic HID support
 fido*  at uhidev?              # FIDO/U2F security key support
+ucc*   at uhidev?              # Consumer Control keyboards
+wskbd* at ucc? mux 1
 ujoy*  at uhidev?              # USB joystick/gamecontroller support
 uhidpp*        at uhidev?              # Logitech HID++ Devices
 upd*   at uhidev?              # USB Power Devices sensors
index d213bd9..6204519 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.64 2021/02/04 16:25:39 anton Exp $
+#      $OpenBSD: GENERIC,v 1.65 2021/08/20 05:23:18 anton Exp $
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -164,6 +164,8 @@ uslhcom*    at uhidev?      # Silicon Labs CP2110 USB HID UART
 ucom*          at uslhcom?
 uhid*          at uhidev?      # USB generic HID support
 fido*          at uhidev?      # FIDO/U2F security key support
+ucc*           at uhidev?      # Consumer Control keyboards
+wskbd*         at ucc? mux 1
 ujoy*          at uhidev?      # USB joystick/gamecontroller support
 uhidpp*                at uhidev?      # Logitech HID++ Devices
 upd*           at uhidev?      # USB Power Devices sensors
index 3401420..7c364fe 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.273 2021/02/04 16:25:39 anton Exp $g
+#      $OpenBSD: GENERIC,v 1.274 2021/08/20 05:23:18 anton Exp $g
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -260,6 +260,8 @@ uslhcom* at uhidev?         # Silicon Labs CP2110 USB HID UART
 ucom*  at uslhcom?
 uhid*  at uhidev?              # USB generic HID support
 fido*  at uhidev?              # FIDO/U2F security key support
+ucc*   at uhidev?              # Consumer Control keyboards
+wskbd* at ucc? mux 1
 ujoy*  at uhidev?              # USB joystick/gamecontroller support
 uhidpp*        at uhidev?              # Logitech HID++ Devices
 upd*   at uhidev?              # USB Power Devices sensors
index ec54249..e66788d 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.58 2021/02/04 16:25:39 anton Exp $
+#      $OpenBSD: GENERIC,v 1.59 2021/08/20 05:23:19 anton Exp $
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -156,6 +156,8 @@ uslhcom*    at uhidev?      # Silicon Labs CP2110 USB HID UART
 ucom*          at uslhcom?
 uhid*          at uhidev?      # USB generic HID support
 fido*          at uhidev?      # FIDO/U2F security key support
+ucc*           at uhidev?      # Consumer Control keyboards
+wskbd*         at ucc? mux 1
 ujoy*          at uhidev?      # USB joystick/gamecontroller support
 uhidpp*                at uhidev?      # Logitech HID++ Devices
 upd*           at uhidev?      # USB Power Devices sensors
index 5a41fb2..6144255 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.26 2021/04/28 11:32:59 bluhm Exp $
+#      $OpenBSD: GENERIC,v 1.27 2021/08/20 05:23:19 anton Exp $
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -127,6 +127,8 @@ uslhcom* at uhidev?         # Silicon Labs CP2110 USB HID UART
 ucom*  at uslhcom?
 uhid*  at uhidev?              # USB generic HID support
 fido*  at uhidev?              # FIDO/U2F security key support
+ucc*   at uhidev?              # Consumer Control keyboards
+wskbd* at ucc? mux 1
 ujoy*  at uhidev?              # USB joystick/gamecontroller support
 uhidpp*        at uhidev?              # Logitech HID++ Devices
 upd*   at uhidev?              # USB Power Devices sensors
index 71e92dc..d3ec9a0 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: GENERIC,v 1.318 2021/07/10 07:04:59 mpi Exp $
+#      $OpenBSD: GENERIC,v 1.319 2021/08/20 05:23:19 anton Exp $
 #
 # For further information on compiling OpenBSD kernels, see the config(8)
 # man page.
@@ -224,6 +224,8 @@ umsm*       at uhub?                # Qualcomm MSM EVDO
 ucom*  at umsm?
 uhid*  at uhidev?              # USB generic HID support
 fido*  at uhidev?              # FIDO/U2F security key support
+ucc*   at uhidev?              # Consumer Control keyboards
+wskbd* at ucc? mux 1
 ujoy*  at uhidev?              # USB joystick/gamecontroller support
 uhidpp*        at uhidev?              # Logitech HID++ Devices
 upd*   at uhidev?              # USB Power Devices sensors
index 4d79c5e..02bf653 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files.usb,v 1.145 2021/02/04 16:25:39 anton Exp $
+#      $OpenBSD: files.usb,v 1.146 2021/08/20 05:23:19 anton Exp $
 #      $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $
 #
 # Config file and device description for machine-independent USB code.
@@ -488,3 +488,8 @@ file        dev/usb/umstc.c                 umstc
 device uhidpp: hid
 attach uhidpp at uhidbus
 file   dev/usb/uhidpp.c                uhidpp
+
+# Consumer Control Keyboards
+device ucc: hid, wskbddev
+attach ucc at uhidbus
+file   dev/usb/ucc.c                   ucc
diff --git a/sys/dev/usb/ucc.c b/sys/dev/usb/ucc.c
new file mode 100644 (file)
index 0000000..9b747bf
--- /dev/null
@@ -0,0 +1,489 @@
+/*     $OpenBSD: ucc.c,v 1.1 2021/08/20 05:23:19 anton Exp $   */
+
+/*
+ * Copyright (c) 2021 Anton Lindqvist <anton@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/uhidev.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wskbdvar.h>
+#include <dev/wscons/wsksymdef.h>
+#include <dev/wscons/wsksymvar.h>
+
+/* #define UCC_DEBUG */
+#ifdef UCC_DEBUG
+#define DPRINTF(x...)  do { if (ucc_debug) printf(x); } while (0)
+void   ucc_dump(const char *, u_char *, u_int);
+int    ucc_debug = 1;
+#else
+#define DPRINTF(x...)
+#define ucc_dump(prefix, data, len)
+#endif
+
+struct ucc_softc {
+       struct uhidev            sc_hdev;
+       struct device           *sc_wskbddev;
+
+       /* Key mappings used in translating mode. */
+       keysym_t                *sc_map;
+       u_int                    sc_maplen;
+       u_int                    sc_mapsiz;
+       u_int                    sc_nkeys;
+
+       /* Key mappings used in raw mode. */
+       struct ucc_keyraw       *sc_raw;
+       u_int                    sc_rawlen;
+       u_int                    sc_rawsiz;
+
+       int                      sc_mode;
+
+       /* Last pressed key. */
+       union {
+               int     sc_last_translate;
+               u_char  sc_last_raw;
+       };
+
+       struct wscons_keydesc    sc_keydesc[2];
+       struct wskbd_mapdata     sc_keymap;
+};
+
+struct ucc_keysym {
+       const char      *us_name;
+       int32_t          us_usage;
+       keysym_t         us_key;
+       u_char           us_raw;
+};
+
+struct ucc_keyraw {
+       u_int   ur_bit;
+       u_char  ur_raw;
+};
+
+int    ucc_match(struct device *, void *, void *);
+void   ucc_attach(struct device *, struct device *, void *);
+int    ucc_detach(struct device *, int);
+void   ucc_intr(struct uhidev *, void *, u_int);
+
+void   ucc_attach_wskbd(struct ucc_softc *);
+int    ucc_enable(void *, int);
+void   ucc_set_leds(void *, int);
+int    ucc_ioctl(void *, u_long, caddr_t, int, struct proc *);
+
+int    ucc_parse_hid(struct ucc_softc *, void *, int);
+int    ucc_bit_to_raw(struct ucc_softc *, u_int, u_char *);
+int    ucc_usage_to_sym(int32_t, const struct ucc_keysym **);
+void   ucc_raw_to_scancode(u_char *, int *, u_char, int);
+void   ucc_input(struct ucc_softc *, u_int, int);
+void   ucc_rawinput(struct ucc_softc *, u_char, int);
+int    ucc_setbits(u_char *, int, u_int *);
+
+struct cfdriver ucc_cd = {
+       NULL, "ucc", DV_DULL
+};
+
+const struct cfattach ucc_ca = {
+       sizeof(struct ucc_softc),
+       ucc_match,
+       ucc_attach,
+       ucc_detach,
+};
+
+/*
+ * Mapping of HID consumer control usages to key symbols.
+ * The raw scan codes are taken from X11, see the media_common symbols in
+ * dist/xkeyboard-config/symbols/inet.
+ * Then use dist/xkeyboard-config/keycodes/xfree86 to resolve keys to the
+ * corresponding raw scan code.
+ */
+static const struct ucc_keysym ucc_keysyms[] = {
+#ifdef UCC_DEBUG
+#define U(x)   #x, x
+#else
+#define U(x)   NULL, x
+#endif
+       { U(HUC_MUTE),          KS_AudioMute,   0 },
+       { U(HUC_VOL_INC),       KS_AudioRaise,  0 },
+       { U(HUC_VOL_DEC),       KS_AudioLower,  0 },
+       { U(HUC_TRACK_NEXT),    0,              153 /* I19 = XF86AudioNext */ },
+       { U(HUC_TRACK_PREV),    0,              144 /* I10 = XF86AudioPrev */ },
+       { U(HUC_STOP),          0,              164 /* I24 = XF86AudioStop */ },
+       { U(HUC_PLAY_PAUSE),    0,              162 /* I22 = XF86AudioPlay, XF86AudioPause */ },
+#undef U
+};
+
+int
+ucc_match(struct device *parent, void *match, void *aux)
+{
+       struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
+       void *desc;
+       int size;
+
+       uhidev_get_report_desc(uha->parent, &desc, &size);
+       if (!hid_is_collection(desc, size, uha->reportid,
+           HID_USAGE2(HUP_CONSUMER, HUC_CONTROL)))
+               return UMATCH_NONE;
+
+       return UMATCH_IFACECLASS;
+}
+
+void
+ucc_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct ucc_softc *sc = (struct ucc_softc *)self;
+       struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
+       void *desc;
+       int error, repid, size;
+
+       sc->sc_mode = WSKBD_TRANSLATED;
+       sc->sc_last_translate = -1;
+
+       sc->sc_hdev.sc_intr = ucc_intr;
+       sc->sc_hdev.sc_parent = uha->parent;
+       sc->sc_hdev.sc_udev = uha->uaa->device;
+       sc->sc_hdev.sc_report_id = uha->reportid;
+
+       uhidev_get_report_desc(uha->parent, &desc, &size);
+       repid = uha->reportid;
+       sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid);
+       sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid);
+       sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid);
+
+       error = ucc_parse_hid(sc, desc, size);
+       if (error) {
+               printf(" hid error %d\n", error);
+               return;
+       }
+
+       printf(" %d key%s, %d mapping%s\n",
+           sc->sc_nkeys, sc->sc_nkeys == 1 ? "" : "s",
+           sc->sc_rawlen, sc->sc_rawlen == 1 ? "" : "s");
+
+       /* Cannot load an empty map. */
+       if (sc->sc_maplen > 0)
+               ucc_attach_wskbd(sc);
+}
+
+int
+ucc_detach(struct device *self, int flags)
+{
+       struct ucc_softc *sc = (struct ucc_softc *)self;
+       int error = 0;
+
+       if (sc->sc_wskbddev != NULL)
+               error = config_detach(sc->sc_wskbddev, flags);
+       uhidev_close(&sc->sc_hdev);
+       free(sc->sc_map, M_USBDEV, sc->sc_mapsiz);
+       free(sc->sc_raw, M_USBDEV, sc->sc_rawsiz);
+       return error;
+}
+
+void
+ucc_intr(struct uhidev *addr, void *data, u_int len)
+{
+       struct ucc_softc *sc = (struct ucc_softc *)addr;
+       int raw = sc->sc_mode == WSKBD_RAW;
+       u_int bit = 0;
+
+       ucc_dump(__func__, data, len);
+
+       if (ucc_setbits(data, len, &bit)) {
+               /* All zeroes, assume key up event. */
+               if (raw) {
+                       if (sc->sc_last_raw != 0) {
+                               ucc_rawinput(sc, sc->sc_last_raw, 1);
+                               sc->sc_last_raw = 0;
+                       }
+               } else {
+                       if (sc->sc_last_translate != -1) {
+                               ucc_input(sc, sc->sc_last_translate, 1);
+                               sc->sc_last_translate = -1;
+                       }
+               }
+               return;
+       }
+       if (bit >= sc->sc_nkeys)
+               goto unknown;
+
+       if (raw) {
+               u_char c;
+
+               if (ucc_bit_to_raw(sc, bit, &c))
+                       goto unknown;
+               if (c != 0) {
+                       ucc_rawinput(sc, c, 0);
+                       sc->sc_last_raw = c;
+                       return;
+               }
+
+               /*
+                * The pressed key does not have a corresponding raw scan code
+                * which implies that wsbkd must handle the pressed key as if
+                * being in translating mode, hence the fall through. This is
+                * only the case for volume related keys.
+                */
+       }
+
+       ucc_input(sc, bit, 0);
+       if (!raw)
+               sc->sc_last_translate = bit;
+       return;
+
+unknown:
+       DPRINTF("%s: unknown key: bit %d\n", __func__, bit);
+}
+
+void
+ucc_attach_wskbd(struct ucc_softc *sc)
+{
+       static const struct wskbd_accessops accessops = {
+               .enable         = ucc_enable,
+               .set_leds       = ucc_set_leds,
+               .ioctl          = ucc_ioctl,
+       };
+       struct wskbddev_attach_args a = {
+               .console        = 0,
+               .keymap         = &sc->sc_keymap,
+               .accessops      = &accessops,
+               .accesscookie   = sc,
+       };
+
+       sc->sc_keydesc[0].name = KB_US;
+       sc->sc_keydesc[0].base = 0;
+       sc->sc_keydesc[0].map_size = sc->sc_maplen;
+       sc->sc_keydesc[0].map = sc->sc_map;
+       sc->sc_keymap.keydesc = sc->sc_keydesc;
+       sc->sc_keymap.layout = KB_US | KB_DEFAULT;
+       sc->sc_wskbddev = config_found(&sc->sc_hdev.sc_dev, &a, wskbddevprint);
+}
+
+int
+ucc_enable(void *v, int on)
+{
+       struct ucc_softc *sc = (struct ucc_softc *)v;
+       int error = 0;
+
+       if (on)
+               error = uhidev_open(&sc->sc_hdev);
+       else
+               uhidev_close(&sc->sc_hdev);
+       return error;
+}
+
+void
+ucc_set_leds(void *v, int leds)
+{
+}
+
+int
+ucc_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
+{
+       struct ucc_softc *sc = (struct ucc_softc *)v;
+
+       switch (cmd) {
+       /* wsconsctl(8) stub */
+       case WSKBDIO_GTYPE:
+               *(int *)data = WSKBD_TYPE_USB;
+               return 0;
+
+       /* wsconsctl(8) stub */
+       case WSKBDIO_GETLEDS:
+               *(int *)data = 0;
+               return 0;
+
+#ifdef WSDISPLAY_COMPAT_RAWKBD
+       case WSKBDIO_SETMODE:
+               sc->sc_mode = *(int *)data;
+               return 0;
+#endif
+       }
+
+       return -1;
+}
+
+/*
+ * Parse the HID report and construct a mapping between the bits in the input
+ * report and the corresponding pressed key.
+ */
+int
+ucc_parse_hid(struct ucc_softc *sc, void *desc, int descsiz)
+{
+       struct hid_item hi;
+       struct hid_data *hd;
+       int isize;
+
+       /*
+        * The size of the input report is expressed in bytes where each bit in
+        * turn represents a pressed key. It's likely that the number of keys is
+        * less than this generous estimate.
+        */
+       isize = sc->sc_hdev.sc_isize * 8;
+       if (isize == 0)
+               return ENXIO;
+
+       /*
+        * Create mapping between each input bit and the corresponding key used
+        * in translating mode. Two entries are needed per bit in order
+        * construct a mapping.
+        */
+       sc->sc_mapsiz = isize * 2 * sizeof(*sc->sc_map);
+       sc->sc_map = mallocarray(isize, 2 * sizeof(*sc->sc_map), M_USBDEV,
+           M_WAITOK | M_ZERO);
+
+       /*
+        * Create mapping between each input bit and the corresponding scan
+        * code used in raw mode.
+        */
+       sc->sc_rawsiz = isize * sizeof(*sc->sc_raw);
+       sc->sc_raw = mallocarray(isize, sizeof(*sc->sc_raw), M_USBDEV,
+           M_WAITOK | M_ZERO);
+
+       hd = hid_start_parse(desc, descsiz, hid_input);
+       while (hid_get_item(hd, &hi)) {
+               const struct ucc_keysym *us;
+               int bit;
+
+               if (HID_GET_USAGE_PAGE(hi.usage) != HUP_CONSUMER ||
+                   HID_GET_USAGE(hi.usage) == HUC_CONTROL)
+                       continue;
+
+               bit = sc->sc_nkeys++;
+               if (ucc_usage_to_sym(HID_GET_USAGE(hi.usage), &us))
+                       continue;
+
+               if (sc->sc_maplen + 2 >= sc->sc_mapsiz)
+                       return ENOMEM;
+               sc->sc_map[sc->sc_maplen++] = KS_KEYCODE(bit);
+               sc->sc_map[sc->sc_maplen++] = us->us_key;
+
+               if (sc->sc_rawlen + 1 >= sc->sc_rawsiz)
+                       return ENOMEM;
+               sc->sc_raw[sc->sc_rawlen].ur_bit = bit;
+               sc->sc_raw[sc->sc_rawlen].ur_raw = us->us_raw;
+               sc->sc_rawlen++;
+
+               DPRINTF("%s: bit %d, usage %s\n", __func__,
+                   bit, us->us_name);
+       }
+       hid_end_parse(hd);
+
+       return 0;
+}
+
+int
+ucc_bit_to_raw(struct ucc_softc *sc, u_int bit, u_char *raw)
+{
+       u_int i;
+
+       for (i = 0; i < sc->sc_rawlen; i++) {
+               const struct ucc_keyraw *ur = &sc->sc_raw[i];
+
+               if (ur->ur_bit == bit) {
+                       *raw = ur->ur_raw;
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+int
+ucc_usage_to_sym(int32_t usage, const struct ucc_keysym **us)
+{
+       int len = nitems(ucc_keysyms);
+       int i;
+
+       for (i = 0; i < len; i++) {
+               if (ucc_keysyms[i].us_usage == usage) {
+                       *us = &ucc_keysyms[i];
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+void
+ucc_input(struct ucc_softc *sc, u_int bit, int release)
+{
+       int s;
+
+       s = spltty();
+       wskbd_input(sc->sc_wskbddev,
+           release ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN, bit);
+       splx(s);
+}
+
+void
+ucc_rawinput(struct ucc_softc *sc, u_char c, int release)
+{
+       u_char buf[2];
+       int len = 0;
+       int s;
+
+       if (c & 0x80)
+               buf[len++] = 0xe0;
+       buf[len++] = c & 0x7f;
+       if (release)
+               buf[len - 1] |= 0x80;
+
+       s = spltty();
+       wskbd_rawinput(sc->sc_wskbddev, buf, len);
+       splx(s);
+}
+
+int
+ucc_setbits(u_char *data, int len, u_int *bit)
+{
+       int i, j;
+
+       for (i = 0; i < len; i++) {
+               if (data[i] == 0)
+                       continue;
+
+               for (j = 0; j < 8; j++) {
+                       if (data[i] & (1 << j)) {
+                               *bit = (i * 8) + j;
+                               return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+#ifdef UCC_DEBUG
+
+void
+ucc_dump(const char *prefix, u_char *data, u_int len)
+{
+       u_int i;
+
+       if (ucc_debug == 0)
+               return;
+
+       printf("%s:", prefix);
+       for (i = 0; i < len; i++)
+               printf(" %02x", data[i]);
+       printf("\n");
+}
+
+#endif