diff options
author | Anton Lindqvist <anton@cvs.openbsd.org> | 2021-08-24 10:53:44 +0000 |
---|---|---|
committer | Anton Lindqvist <anton@cvs.openbsd.org> | 2021-08-24 10:53:44 +0000 |
commit | a89aff485bccc208f028824e660454002ebd9876 (patch) | |
tree | c2e7ffc94a6eb233f4b689abaf6bc3d7e3676602 | |
parent | 880361cfa8007fa1e6a91ea34ae3a5d1bcd5fe16 (diff) |
Some consumer control descriptor reports uses an array of usages instead
of enumerating all supported ones. This representation is more compact
as all supported usages are given by the array min and max boundaries.
In enumerating mode, the interrupt report has one level indirection as
it includes the bit offset of the usage requiring the driver to map bits
to usages in order to resolve the key press.
In array mode, the interrupt report includes the usage without any
indirection, removing the need map bits to usages.
ucc(4) can now operate either in enumerating or array mode depending on
the descriptor report. This should make more keyboards usable.
Thanks to Alessandro De Laurenzis <just22 at atlantide dot mooo dot com>
for testing.
-rw-r--r-- | sys/dev/usb/ucc.c | 165 |
1 files changed, 119 insertions, 46 deletions
diff --git a/sys/dev/usb/ucc.c b/sys/dev/usb/ucc.c index 49700267b01..332a31e708d 100644 --- a/sys/dev/usb/ucc.c +++ b/sys/dev/usb/ucc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ucc.c,v 1.5 2021/08/23 17:50:26 anton Exp $ */ +/* $OpenBSD: ucc.c,v 1.6 2021/08/24 10:53:43 anton Exp $ */ /* * Copyright (c) 2021 Anton Lindqvist <anton@openbsd.org> @@ -49,13 +49,13 @@ struct ucc_softc { keysym_t *sc_map; u_int sc_maplen; u_int sc_mapsiz; - u_int sc_nkeys; /* Key mappings used in raw mode. */ const struct ucc_keysym **sc_raw; - u_int sc_rawlen; u_int sc_rawsiz; + u_int sc_nusages; + int sc_isarray; int sc_mode; /* Last pressed key. */ @@ -85,10 +85,12 @@ 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_hid_parse(struct ucc_softc *, void *, int); +int ucc_hid_parse_array(struct ucc_softc *, const struct hid_item *); +int ucc_add_key(struct ucc_softc *, int32_t, u_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); +int ucc_intr_to_usage(u_char *, u_int, int32_t *); 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 *); @@ -164,15 +166,16 @@ ucc_attach(struct device *parent, struct device *self, void *aux) 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); + error = ucc_hid_parse(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"); + printf(": %d usage%s, %d key%s, %s\n", + sc->sc_nusages, sc->sc_nusages == 1 ? "" : "s", + sc->sc_maplen / 2, sc->sc_maplen / 2 == 1 ? "" : "s", + sc->sc_isarray ? "array" : "enum"); /* Cannot load an empty map. */ if (sc->sc_maplen > 0) @@ -188,8 +191,8 @@ ucc_detach(struct device *self, int flags) 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); + 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; } @@ -199,6 +202,7 @@ 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; + u_char c = 0; ucc_dump(__func__, data, len); @@ -216,15 +220,21 @@ ucc_intr(struct uhidev *addr, void *data, u_int len) } } return; - } - if (bit >= sc->sc_nkeys) - goto unknown; - - if (raw) { - u_char c; + } else if (sc->sc_isarray) { + const struct ucc_keysym *us; + int32_t usage; + if (ucc_intr_to_usage(data, len, &usage) || + ucc_usage_to_sym(usage, &us)) + goto unknown; + bit = us->us_usage; + c = us->us_raw; + } else if (raw) { if (ucc_bit_to_raw(sc, bit, &c)) goto unknown; + } + + if (raw) { if (c != 0) { ucc_rawinput(sc, c, 0); sc->sc_last_raw = c; @@ -322,11 +332,11 @@ ucc_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) * report and the corresponding pressed key. */ int -ucc_parse_hid(struct ucc_softc *sc, void *desc, int descsiz) +ucc_hid_parse(struct ucc_softc *sc, void *desc, int descsiz) { struct hid_item hi; struct hid_data *hd; - u_int mapsiz, rawsiz; + int nsyms = nitems(ucc_keysyms); int isize; /* @@ -339,59 +349,104 @@ ucc_parse_hid(struct ucc_softc *sc, void *desc, int descsiz) 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. + * Create mapping between each input bit and the corresponding usage, + * used in translating mode. Two entries are needed per bit in order + * construct a mapping. Note that at most all known usages as given by + * ucc_keysyms can be inserted into this map. */ - mapsiz = isize * 2; - sc->sc_mapsiz = mapsiz * sizeof(*sc->sc_map); - sc->sc_map = mallocarray(isize, 2 * sizeof(*sc->sc_map), M_USBDEV, + sc->sc_mapsiz = nsyms * 2; + sc->sc_map = mallocarray(nsyms, 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. + * Create mapping between each input bit and the corresponding usage, + * used in raw mode. */ - rawsiz = isize; - sc->sc_rawsiz = rawsiz * sizeof(*sc->sc_raw); + sc->sc_rawsiz = isize; 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; + u_int bit; + int error; - if (HID_GET_USAGE_PAGE(hi.usage) != HUP_CONSUMER || - HID_GET_USAGE(hi.usage) == HUC_CONTROL) + if (HID_GET_USAGE_PAGE(hi.usage) != HUP_CONSUMER) continue; - bit = sc->sc_nkeys++; - if (ucc_usage_to_sym(HID_GET_USAGE(hi.usage), &us)) + /* + * The usages could be expressed as an array instead of + * enumerating all supported ones. + */ + if (HID_GET_USAGE(hi.usage) == HUC_CONTROL) { + error = ucc_hid_parse_array(sc, &hi); + if (error) + return error; continue; + } - if (sc->sc_maplen + 2 >= mapsiz) - return ENOMEM; - sc->sc_map[sc->sc_maplen++] = KS_KEYCODE(bit); - sc->sc_map[sc->sc_maplen++] = us->us_key; + bit = sc->sc_nusages++; + error = ucc_add_key(sc, HID_GET_USAGE(hi.usage), bit); + if (error) + return error; + } + hid_end_parse(hd); + + return 0; +} + +int +ucc_hid_parse_array(struct ucc_softc *sc, const struct hid_item *hi) +{ + int32_t max, min, usage; + + min = HID_GET_USAGE(hi->usage_minimum); + max = HID_GET_USAGE(hi->usage_maximum); + if (min < 0 || max < 0 || min >= max) + return 0; + + sc->sc_isarray = 1; - if (bit >= rawsiz) + for (usage = min; usage <= max; usage++) { + int error; + + sc->sc_nusages++; + error = ucc_add_key(sc, usage, 0); + if (error) + return error; + } + + return 0; +} + +int +ucc_add_key(struct ucc_softc *sc, int32_t usage, u_int bit) +{ + const struct ucc_keysym *us; + + if (ucc_usage_to_sym(usage, &us)) + return 0; + + if (sc->sc_maplen + 2 > sc->sc_mapsiz) + return ENOMEM; + sc->sc_map[sc->sc_maplen++] = KS_KEYCODE(sc->sc_isarray ? usage : bit); + sc->sc_map[sc->sc_maplen++] = us->us_key; + + if (!sc->sc_isarray) { + if (bit >= sc->sc_rawsiz) return ENOMEM; sc->sc_raw[bit] = us; - sc->sc_rawlen++; - - DPRINTF("%s: bit %d, usage %s\n", __func__, - bit, us->us_name); } - hid_end_parse(hd); + DPRINTF("%s: bit %d, usage %s\n", __func__, + bit, us->us_name); return 0; } int ucc_bit_to_raw(struct ucc_softc *sc, u_int bit, u_char *raw) { - if (bit >= sc->sc_nkeys) + if (bit >= sc->sc_rawsiz) return 1; *raw = sc->sc_raw[bit]->us_raw; return 0; @@ -412,6 +467,24 @@ ucc_usage_to_sym(int32_t usage, const struct ucc_keysym **us) return 1; } +int +ucc_intr_to_usage(u_char *buf, u_int buflen, int32_t *usage) +{ + int32_t x = 0; + int i; + + if (buflen == 0 || buflen > sizeof(*usage)) + return 1; + + for (i = buflen - 1; i >= 0; i--) { + x |= buf[i]; + if (i > 0) + x <<= 8; + } + *usage = x; + return 0; +} + void ucc_input(struct ucc_softc *sc, u_int bit, int release) { |