summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorAnton Lindqvist <anton@cvs.openbsd.org>2021-08-29 18:20:19 +0000
committerAnton Lindqvist <anton@cvs.openbsd.org>2021-08-29 18:20:19 +0000
commitcdd0cde836800e68ad507d78bc9f5be6d72327dd (patch)
tree6b21ab6437d6ff24614508871b5354c6c808e731 /sys/dev
parent9681c211d9323f71e0a3cea4555b4dfbbd4bfa00 (diff)
Some reports embeds multiple report IDs inside the same collection
causing ucc to only being able to attach to the last report ID. This in turn is caused by hid_is_collection() only being able to observe an end of collection item with the last report ID for the same collection. Instead, change the matching of ucc to only consider report IDs with at least one corresponding Consumer Control usage. Fixes gnezdo@'s Google Pixel earbuds.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/usb/ucc.c32
1 files changed, 28 insertions, 4 deletions
diff --git a/sys/dev/usb/ucc.c b/sys/dev/usb/ucc.c
index 4bf6b04fdb6..d7aba7b5f82 100644
--- a/sys/dev/usb/ucc.c
+++ b/sys/dev/usb/ucc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ucc.c,v 1.15 2021/08/29 18:19:09 anton Exp $ */
+/* $OpenBSD: ucc.c,v 1.16 2021/08/29 18:20:18 anton Exp $ */
/*
* Copyright (c) 2021 Anton Lindqvist <anton@openbsd.org>
@@ -98,6 +98,7 @@ int ucc_enable(void *, int);
void ucc_set_leds(void *, int);
int ucc_ioctl(void *, u_long, caddr_t, int, struct proc *);
+int ucc_hid_match(void *, int, uint8_t);
int ucc_hid_parse(struct ucc_softc *, void *, int);
int ucc_hid_parse_array(struct ucc_softc *, const struct hid_item *);
int ucc_hid_is_array(const struct hid_item *);
@@ -619,11 +620,10 @@ ucc_match(struct device *parent, void *match, void *aux)
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;
if (hid_report_size(desc, size, hid_input, uha->reportid) == 0)
return UMATCH_NONE;
+ if (!ucc_hid_match(desc, size, uha->reportid))
+ return UMATCH_NONE;
return UMATCH_IFACECLASS;
}
@@ -831,6 +831,30 @@ ucc_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
}
/*
+ * Returns non-zero if the given report ID has at least one Consumer Control
+ * usage.
+ */
+int
+ucc_hid_match(void *desc, int descsiz, uint8_t repid)
+{
+ struct hid_item hi;
+ struct hid_data *hd;
+ int match = 0;
+
+ hd = hid_start_parse(desc, descsiz, hid_input);
+ while (hid_get_item(hd, &hi)) {
+ if (hi.report_ID == repid &&
+ hi.kind == hid_input &&
+ HID_GET_USAGE_PAGE(hi.usage) == HUP_CONSUMER) {
+ match = 1;
+ break;
+ }
+ }
+ hid_end_parse(hd);
+ return match;
+}
+
+/*
* Parse the HID report and construct a mapping between the bits in the input
* report and the corresponding pressed key.
*/