On Tue, Aug 17, 2021 at 08:13:41PM +0200, Anton Lindqvist wrote:
> Hi,
> Here's a new driver for USB HID Consumer Control keyboards. Such
> 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).
> 
> ucc(4) attaches a wskbd(4) keyboard "on top" making it appear like an
> ordinary keyboard, which also makes it possible to inject key
> press/release input. It supports both translating and raw mode making it
> compatible with the ordinary console and X11.
> 
> My keyboard for instance exposes 42 keys in its input report. I only
> care about the volume and audio related ones and therefore only added
> mappings for those. Additional mappings should be trivial to add if
> desired.
> 
> Testing would be much appreciated.
> 
> Comments? OK?
> 

hi.

the doc parts look good.
jmc

> diff --git share/man/man4/Makefile share/man/man4/Makefile
> index 6a0ecb20653..63b33660159 100644
> --- share/man/man4/Makefile
> +++ share/man/man4/Makefile
> @@ -84,7 +84,7 @@ MAN=        aac.4 abcrtc.4 abl.4 ac97.4 acphy.4 acrtc.4 \
>       tlphy.4 thmc.4 tpm.4 tpmr.4 tqphy.4 trm.4 trunk.4 tsl.4 tty.4 \
>       tun.4 tap.4 twe.4 \
>       txp.4 txphy.4 uaudio.4 uark.4 uath.4 ubcmtp.4 uberry.4 ubsa.4 \
> -     ubsec.4 ucom.4 uchcom.4 ucrcom.4 ucycom.4 ukspan.4 uslhcom.4 \
> +     ubsec.4 ucc.4 ucom.4 uchcom.4 ucrcom.4 ucycom.4 ukspan.4 uslhcom.4 \
>       udav.4 udcf.4 udl.4 udp.4 udsbr.4 \
>       uftdi.4 ugen.4 ugl.4 ugold.4 uguru.4 uhci.4 uhid.4 uhidev.4 uhidpp.4 \
>       uipaq.4 ujoy.4 uk.4 ukbd.4 \
> diff --git share/man/man4/ucc.4 share/man/man4/ucc.4
> new file mode 100644
> index 00000000000..413c88aa6af
> --- /dev/null
> +++ share/man/man4/ucc.4
> @@ -0,0 +1,45 @@
> +.\"  $OpenBSD$
> +.\"
> +.\" Copyright (c) 2021 Anton Lindqvist <an...@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.
> +.\"
> +.Dd $Mdocdate$
> +.Dt UCC 4
> +.Os
> +.Sh NAME
> +.Nm ucc
> +.Nd Consumer Control keyboards
> +.Sh SYNOPSIS
> +.Cd "ucc* at uhidev?"
> +.Cd "wsbkd* at ucc? mux 1"
> +.Sh DESCRIPTION
> +The
> +.Nm
> +driver provides support for Consumer Control pseudo keyboards, often used to
> +expose audio and application launch keys.
> +.Sh SEE ALSO
> +.Xr intro 4 ,
> +.Xr uhidev 4 ,
> +.Xr usb 4 ,
> +.Xr wskbd 4
> +.Sh HISTORY
> +The
> +.Nm
> +driver first appeared in
> +.Ox 7.0 .
> +.Sh AUTHORS
> +The
> +.Nm
> +driver was written by
> +.An Anton Lindqvist Aq Mt an...@openbsd.org .
> diff --git share/man/man4/uhidev.4 share/man/man4/uhidev.4
> index 02252789a3f..d398c564bd5 100644
> --- share/man/man4/uhidev.4
> +++ share/man/man4/uhidev.4
> @@ -37,6 +37,7 @@
>  .Sh SYNOPSIS
>  .Cd "uhidev*  at uhub?"
>  .Cd "fido*    at uhidev?"
> +.Cd "ucc*     at uhidev?"
>  .Cd "ucycom*  at uhidev?"
>  .Cd "ugold*   at uhidev?"
>  .Cd "uhid*    at uhidev?"
> @@ -72,6 +73,7 @@ only dispatches data to them based on the report id.
>  .Sh SEE ALSO
>  .Xr fido 4 ,
>  .Xr intro 4 ,
> +.Xr ucc 4 ,
>  .Xr ucycom 4 ,
>  .Xr ugold 4 ,
>  .Xr uhid 4 ,
> diff --git share/man/man4/usb.4 share/man/man4/usb.4
> index dad3d3a97d9..d159d8b27f3 100644
> --- share/man/man4/usb.4
> +++ share/man/man4/usb.4
> @@ -249,6 +249,8 @@ D-Link DSB-R100 USB radio device
>  FIDO/U2F security keys
>  .It Xr ubcmtp 4
>  Broadcom trackpad mouse
> +.It Xr ucc 4
> +USB Consumer Control keyboards
>  .It Xr ugold 4
>  TEMPer gold HID thermometer and hygrometer
>  .It Xr uhid 4
> diff --git sys/arch/alpha/conf/GENERIC sys/arch/alpha/conf/GENERIC
> index 8af652ce301..54d4a45cd4e 100644
> --- sys/arch/alpha/conf/GENERIC
> +++ sys/arch/alpha/conf/GENERIC
> @@ -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
> diff --git sys/arch/amd64/conf/GENERIC sys/arch/amd64/conf/GENERIC
> index c0c16f7c986..0d278739ccf 100644
> --- sys/arch/amd64/conf/GENERIC
> +++ sys/arch/amd64/conf/GENERIC
> @@ -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
> diff --git sys/arch/arm64/conf/GENERIC sys/arch/arm64/conf/GENERIC
> index ff54bee0a9a..0cb8f4edb05 100644
> --- sys/arch/arm64/conf/GENERIC
> +++ sys/arch/arm64/conf/GENERIC
> @@ -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
> diff --git sys/arch/armv7/conf/GENERIC sys/arch/armv7/conf/GENERIC
> index 1af5eb8dd85..271177bf2ab 100644
> --- sys/arch/armv7/conf/GENERIC
> +++ sys/arch/armv7/conf/GENERIC
> @@ -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
> diff --git sys/arch/hppa/conf/GENERIC sys/arch/hppa/conf/GENERIC
> index 1a27dbb18c0..91e0888af4c 100644
> --- sys/arch/hppa/conf/GENERIC
> +++ sys/arch/hppa/conf/GENERIC
> @@ -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
> diff --git sys/arch/i386/conf/GENERIC sys/arch/i386/conf/GENERIC
> index b49353a92d0..7e3144dba53 100644
> --- sys/arch/i386/conf/GENERIC
> +++ sys/arch/i386/conf/GENERIC
> @@ -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
> diff --git sys/arch/landisk/conf/GENERIC sys/arch/landisk/conf/GENERIC
> index a2091413514..133218afe22 100644
> --- sys/arch/landisk/conf/GENERIC
> +++ sys/arch/landisk/conf/GENERIC
> @@ -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
> diff --git sys/arch/loongson/conf/GENERIC sys/arch/loongson/conf/GENERIC
> index d213bd976f9..f3ab1b34cf1 100644
> --- sys/arch/loongson/conf/GENERIC
> +++ sys/arch/loongson/conf/GENERIC
> @@ -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
> diff --git sys/arch/macppc/conf/GENERIC sys/arch/macppc/conf/GENERIC
> index 34014204a23..872e74601a2 100644
> --- sys/arch/macppc/conf/GENERIC
> +++ sys/arch/macppc/conf/GENERIC
> @@ -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
> diff --git sys/arch/octeon/conf/GENERIC sys/arch/octeon/conf/GENERIC
> index ec5424938cb..260b5fda752 100644
> --- sys/arch/octeon/conf/GENERIC
> +++ sys/arch/octeon/conf/GENERIC
> @@ -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
> diff --git sys/arch/powerpc64/conf/GENERIC sys/arch/powerpc64/conf/GENERIC
> index 5a41fb2ae72..311355bce1a 100644
> --- sys/arch/powerpc64/conf/GENERIC
> +++ sys/arch/powerpc64/conf/GENERIC
> @@ -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
> diff --git sys/arch/sparc64/conf/GENERIC sys/arch/sparc64/conf/GENERIC
> index 71e92dc9016..757a637cc09 100644
> --- sys/arch/sparc64/conf/GENERIC
> +++ sys/arch/sparc64/conf/GENERIC
> @@ -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
> diff --git sys/dev/hid/hid.h sys/dev/hid/hid.h
> index dd9586d7cc2..23f8f7979ec 100644
> --- sys/dev/hid/hid.h
> +++ sys/dev/hid/hid.h
> @@ -397,6 +397,9 @@ int       hid_is_collection(const void *, int, uint8_t, 
> int32_t);
>  
>  /* Usages, Consumer */
>  #define HUC_CONTROL          0x0001
> +#define HUC_TRACK_NEXT               0x00b5
> +#define HUC_TRACK_PREV               0x00b6
> +#define HUC_STOP             0x00b7
>  #define HUC_PLAY_PAUSE               0x00cd
>  #define HUC_MUTE             0x00e2
>  #define HUC_VOL_INC          0x00e9
> diff --git sys/dev/usb/files.usb sys/dev/usb/files.usb
> index 4d79c5ea21a..c4c4688f00e 100644
> --- sys/dev/usb/files.usb
> +++ sys/dev/usb/files.usb
> @@ -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 sys/dev/usb/ucc.c sys/dev/usb/ucc.c
> new file mode 100644
> index 00000000000..8eb82a09cfd
> --- /dev/null
> +++ sys/dev/usb/ucc.c
> @@ -0,0 +1,470 @@
> +/*   $OpenBSD$       */
> +
> +/*
> + * Copyright (c) 2021 Anton Lindqvist <an...@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 {
> +     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
> + * corrensponding raw scan code.
> + */
> +static const struct ucc_keysym ucc_keysyms[] = {
> +     { HUC_MUTE,             KS_AudioMute,   0 },
> +     { HUC_VOL_INC,          KS_AudioRaise,  0 },
> +     { HUC_VOL_DEC,          KS_AudioLower,  0 },
> +     { HUC_TRACK_NEXT,       0,              153 /* I19 = XF86AudioNext */ },
> +     { HUC_TRACK_PREV,       0,              144 /* I10 = XF86AudioPrev */ },
> +     { HUC_STOP,             0,              164 /* I24 = XF86AudioStop */ },
> +     { HUC_PLAY_PAUSE,       0,              162 /* I22 = XF86AudioPlay, 
> XF86AudioPause */ },
> +};
> +
> +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(" keys %d, mappings %d\n", sc->sc_nkeys, sc->sc_rawlen);
> +
> +     /* 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) {
> +#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 %0x, key %0x\n", __func__,
> +                 bit, HID_GET_USAGE(hi.usage), us->us_key);
> +     }
> +     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
> 

Reply via email to