Hi, An interrupt report contains the state of all items (Input, Output and Feature) from the corresponding descriptor report for a given report ID. The ordering of the items is identical in both the descriptor and interrupt report. As the interrupt report can cover more than Consumer Control related key presses, ucc must be more careful while examining the interrupt report in order to not confuse other items as key presses.
While parsing the descriptor report, take note of the bits that represents Consumer Control key presses and use it to slice the interrupt report. Testing would be much appreciated. Note that UCC_DEBUG is intentionally enabled in the diff below. Please send me your dmesg after pressing at least one recognized key. Index: dev/usb/ucc.c =================================================================== RCS file: /cvs/src/sys/dev/usb/ucc.c,v retrieving revision 1.13 diff -u -p -r1.13 ucc.c --- dev/usb/ucc.c 26 Aug 2021 10:32:35 -0000 1.13 +++ dev/usb/ucc.c 27 Aug 2021 05:03:33 -0000 @@ -31,7 +31,7 @@ #include <dev/wscons/wsksymdef.h> #include <dev/wscons/wsksymvar.h> -/* #define UCC_DEBUG */ +#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); @@ -58,6 +58,14 @@ struct ucc_softc { int sc_isarray; int sc_mode; + /* Slice of the interrupt buffer which represents a pressed key. */ + struct { + uint8_t *i_buf; + uint32_t i_bufsiz; + uint32_t i_off; /* offset in bits */ + uint32_t i_len; /* length in bits */ + } sc_input; + /* Last pressed key. */ union { int sc_last_translate; @@ -92,6 +100,7 @@ int ucc_add_key(struct ucc_softc *, int3 int ucc_bit_to_sym(struct ucc_softc *, u_int, const struct ucc_keysym **); int ucc_usage_to_sym(int32_t, const struct ucc_keysym **); int ucc_bits_to_usage(u_char *, u_int, int32_t *); +int ucc_intr_slice(struct ucc_softc *, u_char *, 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 *); @@ -194,6 +203,7 @@ ucc_detach(struct device *self, int flag if (sc->sc_wskbddev != NULL) error = config_detach(sc->sc_wskbddev, flags); uhidev_close(&sc->sc_hdev); + free(sc->sc_input.i_buf, M_USBDEV, sc->sc_input.i_bufsiz); free(sc->sc_map, M_USBDEV, sc->sc_mapsiz * sizeof(*sc->sc_map)); free(sc->sc_raw, M_USBDEV, sc->sc_rawsiz * sizeof(*sc->sc_raw)); return error; @@ -204,13 +214,30 @@ ucc_intr(struct uhidev *addr, void *data { struct ucc_softc *sc = (struct ucc_softc *)addr; const struct ucc_keysym *us; + u_char *buf = sc->sc_input.i_buf; int raw = sc->sc_mode == WSKBD_RAW; + int error; u_int bit = 0; u_char c = 0; ucc_dump(__func__, data, len); - if (ucc_setbits(data, len, &bit)) { + if (len > sc->sc_input.i_bufsiz) { + DPRINTF("%s: too much data: len %d, bufsiz %d\n", __func__, + len, sc->sc_input.i_bufsiz); + return; + } + + error = ucc_intr_slice(sc, data, buf, &len); + if (error) { + DPRINTF("%s: slice failure: error %d\n", __func__, error); + return; + } + + /* Dump the buffer again after slicing. */ + ucc_dump(__func__, buf, len); + + if (ucc_setbits(buf, len, &bit)) { /* All zeroes, assume key up event. */ if (raw) { if (sc->sc_last_raw != 0) { @@ -227,7 +254,7 @@ ucc_intr(struct uhidev *addr, void *data } else if (sc->sc_isarray) { int32_t usage; - if (ucc_bits_to_usage(data, len, &usage) || + if (ucc_bits_to_usage(buf, len, &usage) || ucc_usage_to_sym(usage, &us)) goto unknown; bit = us->us_usage; @@ -338,10 +365,12 @@ ucc_ioctl(void *v, u_long cmd, caddr_t d int ucc_hid_parse(struct ucc_softc *sc, void *desc, int descsiz) { + enum { OFFSET, LENGTH } istate = OFFSET; struct hid_item hi; struct hid_data *hd; - int nsyms = nitems(ucc_keysyms); int error = 0; + int nsyms = nitems(ucc_keysyms); + int repid = sc->sc_hdev.sc_report_id; int isize; /* @@ -353,6 +382,10 @@ ucc_hid_parse(struct ucc_softc *sc, void if (isize == 0) return ENXIO; + /* Allocate buffer used to slice interrupt data. */ + sc->sc_input.i_bufsiz = sc->sc_hdev.sc_isize; + sc->sc_input.i_buf = malloc(sc->sc_input.i_bufsiz, M_USBDEV, M_WAITOK); + /* * Create mapping between each input bit and the corresponding usage, * used in translating mode. Two entries are needed per bit in order @@ -375,10 +408,20 @@ ucc_hid_parse(struct ucc_softc *sc, void while (hid_get_item(hd, &hi)) { u_int bit; - if (hi.kind != hid_input || - HID_GET_USAGE_PAGE(hi.usage) != HUP_CONSUMER) + if (hi.report_ID != repid || hi.kind != hid_input) continue; + if (HID_GET_USAGE_PAGE(hi.usage) != HUP_CONSUMER) { + if (istate == OFFSET) + sc->sc_input.i_off = hi.loc.pos + + hi.loc.size * hi.loc.count; + continue; + } + + /* Signal that the input offset is reached. */ + istate = LENGTH; + sc->sc_input.i_len += hi.loc.size * hi.loc.count; + /* * The usages could be expressed as an array instead of * enumerating all supported ones. @@ -395,6 +438,9 @@ ucc_hid_parse(struct ucc_softc *sc, void } hid_end_parse(hd); + DPRINTF("%s: input: off %d, len %d\n", __func__, + sc->sc_input.i_off, sc->sc_input.i_len); + return error; } @@ -482,25 +528,49 @@ int ucc_bits_to_usage(u_char *buf, u_int buflen, int32_t *usage) { int32_t x = 0; - int maxlen = sizeof(*usage); int i; - if (buflen == 0) + if (buflen == 0 || buflen > sizeof(*usage)) return 1; - /* - * XXX should only look at the bits given by the report size and report - * count associated with the Consumer usage page. - */ - if (buflen > maxlen) - buflen = maxlen; - for (i = buflen - 1; i >= 0; i--) { x |= buf[i]; if (i > 0) x <<= 8; } *usage = x; + return 0; +} + +int +ucc_intr_slice(struct ucc_softc *sc, u_char *src, u_char *dst, int *len) +{ + int ilen = sc->sc_input.i_len; + int ioff = sc->sc_input.i_off; + int maxlen = *len; + int di, si; + + if (maxlen == 0) + return 1; + + memset(dst, 0, maxlen); + si = ioff; + di = 0; + for (; ilen > 0; ilen--) { + int db, sb; + + sb = si / 8; + db = di / 8; + if (sb >= maxlen || db >= maxlen) + return 1; + + if (src[sb] & (1 << (si % 8))) + dst[db] |= 1 << (di % 8); + si++; + di++; + } + + *len = (sc->sc_input.i_len + 7) / 8; return 0; }