From 8b7fd6a15f8c32760c2026a62dcf55219b4da15b Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 1 Oct 2024 16:30:05 +0200 Subject: [PATCH 01/12] HID: bpf: move HID-BPF report descriptor fixup earlier Currently, hid_bpf_rdesc_fixup() is called once the match between the HID device and the driver is done. This can be problematic in case the driver selected by the kernel would change the report descriptor after the fact. To give a chance for hid_bpf_rdesc_fixup() to provide hints on to how to select a dedicated driver or not, move the call to that BPF hook earlier in the .probe() process, when we get the first match. However, this means that we might get called more than once (typically once for hid-generic, and once for hid-vendor-specific). So we store the result of HID-BPF fixup in struct hid_device. Basically, this means that ->bpf_rdesc can replace ->dev_rdesc when it was used in the code. In order to not grow struct hid_device, some fields are re-ordered. This was the output of pahole for the first 128 bytes: struct hid_device { __u8 * dev_rdesc; /* 0 8 */ unsigned int dev_rsize; /* 8 4 */ /* XXX 4 bytes hole, try to pack */ __u8 * rdesc; /* 16 8 */ unsigned int rsize; /* 24 4 */ /* XXX 4 bytes hole, try to pack */ struct hid_collection * collection; /* 32 8 */ unsigned int collection_size; /* 40 4 */ unsigned int maxcollection; /* 44 4 */ unsigned int maxapplication; /* 48 4 */ __u16 bus; /* 52 2 */ __u16 group; /* 54 2 */ __u32 vendor; /* 56 4 */ __u32 product; /* 60 4 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 version; /* 64 4 */ enum hid_type type; /* 68 4 */ unsigned int country; /* 72 4 */ /* XXX 4 bytes hole, try to pack */ struct hid_report_enum report_enum[3]; /* 80 6216 */ Basically, we got three holes of 4 bytes. We can reorder things a little and makes those 3 holes a continuous 12 bytes hole, which can be replaced by the new pointer and the new unsigned int we need. In terms of code allocation, when not using HID-BPF, we are back to kernel v6.2 in hid_open_report(). These multiple kmemdup() calls will be fixed in a later commit. Link: https://patch.msgid.link/20241001-hid-bpf-hid-generic-v3-1-2ef1019468df@kernel.org Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/hid_bpf_dispatch.c | 9 ++++++--- drivers/hid/hid-core.c | 30 ++++++++++++++++++++++++++---- include/linux/hid.h | 18 ++++++++++-------- include/linux/hid_bpf.h | 11 +++-------- 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 8420c227e21b..961b7f35aa67 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -148,7 +148,7 @@ out: } EXPORT_SYMBOL_GPL(dispatch_hid_bpf_output_report); -u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size) +const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size) { int ret; struct hid_bpf_ctx_kern ctx_kern = { @@ -183,7 +183,7 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned ignore_bpf: kfree(ctx_kern.data); - return kmemdup(rdesc, *size, GFP_KERNEL); + return rdesc; } EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup); @@ -260,8 +260,11 @@ int hid_bpf_allocate_event_data(struct hid_device *hdev) int hid_bpf_reconnect(struct hid_device *hdev) { - if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) + if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) { + /* trigger call to call_hid_bpf_rdesc_fixup() during the next probe */ + hdev->bpf_rsize = 0; return device_reprobe(&hdev->dev); + } return 0; } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 30de92d0bf0f..d6bf933623e8 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -698,6 +698,14 @@ static void hid_close_report(struct hid_device *device) device->status &= ~HID_STAT_PARSED; } +static inline void hid_free_bpf_rdesc(struct hid_device *hdev) +{ + /* bpf_rdesc is either equal to dev_rdesc or allocated by call_hid_bpf_rdesc_fixup() */ + if (hdev->bpf_rdesc != hdev->dev_rdesc) + kfree(hdev->bpf_rdesc); + hdev->bpf_rdesc = NULL; +} + /* * Free a device structure, all reports, and all fields. */ @@ -707,6 +715,7 @@ void hiddev_free(struct kref *ref) struct hid_device *hid = container_of(ref, struct hid_device, ref); hid_close_report(hid); + hid_free_bpf_rdesc(hid); kfree(hid->dev_rdesc); kfree(hid); } @@ -1221,13 +1230,12 @@ int hid_open_report(struct hid_device *device) if (WARN_ON(device->status & HID_STAT_PARSED)) return -EBUSY; - start = device->dev_rdesc; + start = device->bpf_rdesc; if (WARN_ON(!start)) return -ENODEV; - size = device->dev_rsize; + size = device->bpf_rsize; - /* call_hid_bpf_rdesc_fixup() ensures we work on a copy of rdesc */ - buf = call_hid_bpf_rdesc_fixup(device, start, &size); + buf = kmemdup(start, size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; @@ -2684,6 +2692,18 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) const struct hid_device_id *id; int ret; + if (!hdev->bpf_rsize) { + /* in case a bpf program gets detached, we need to free the old one */ + hid_free_bpf_rdesc(hdev); + + /* keep this around so we know we called it once */ + hdev->bpf_rsize = hdev->dev_rsize; + + /* call_hid_bpf_rdesc_fixup will always return a valid pointer */ + hdev->bpf_rdesc = call_hid_bpf_rdesc_fixup(hdev, hdev->dev_rdesc, + &hdev->bpf_rsize); + } + if (!hid_check_device_match(hdev, hdrv, &id)) return -ENODEV; @@ -2940,9 +2960,11 @@ static void hid_remove_device(struct hid_device *hdev) hid_debug_unregister(hdev); hdev->status &= ~HID_STAT_ADDED; } + hid_free_bpf_rdesc(hdev); kfree(hdev->dev_rdesc); hdev->dev_rdesc = NULL; hdev->dev_rsize = 0; + hdev->bpf_rsize = 0; } /** diff --git a/include/linux/hid.h b/include/linux/hid.h index 121d5b8bc867..ff58b5ceb62e 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -599,15 +599,17 @@ enum hid_battery_status { struct hid_driver; struct hid_ll_driver; -struct hid_device { /* device report descriptor */ - const __u8 *dev_rdesc; - unsigned dev_rsize; - const __u8 *rdesc; - unsigned rsize; +struct hid_device { + const __u8 *dev_rdesc; /* device report descriptor */ + const __u8 *bpf_rdesc; /* bpf modified report descriptor, if any */ + const __u8 *rdesc; /* currently used report descriptor */ + unsigned int dev_rsize; + unsigned int bpf_rsize; + unsigned int rsize; + unsigned int collection_size; /* Number of allocated hid_collections */ struct hid_collection *collection; /* List of HID collections */ - unsigned collection_size; /* Number of allocated hid_collections */ - unsigned maxcollection; /* Number of parsed collections */ - unsigned maxapplication; /* Number of applications */ + unsigned int maxcollection; /* Number of parsed collections */ + unsigned int maxapplication; /* Number of applications */ __u16 bus; /* BUS ID */ __u16 group; /* Report group */ __u32 vendor; /* Vendor ID */ diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index 6a47223e6460..a6876ab29004 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -212,7 +212,7 @@ int hid_bpf_connect_device(struct hid_device *hdev); void hid_bpf_disconnect_device(struct hid_device *hdev); void hid_bpf_destroy_device(struct hid_device *hid); int hid_bpf_device_init(struct hid_device *hid); -u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size); +const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size); #else /* CONFIG_HID_BPF */ static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 *size, int interrupt, @@ -228,13 +228,8 @@ static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; } static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {} static inline void hid_bpf_destroy_device(struct hid_device *hid) {} static inline int hid_bpf_device_init(struct hid_device *hid) { return 0; } -/* - * This specialized allocator has to be a macro for its allocations to be - * accounted separately (to have a separate alloc_tag). The typecast is - * intentional to enforce typesafety. - */ -#define call_hid_bpf_rdesc_fixup(_hdev, _rdesc, _size) \ - ((u8 *)kmemdup(_rdesc, *(_size), GFP_KERNEL)) +static inline const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, + unsigned int *size) { return rdesc; } #endif /* CONFIG_HID_BPF */ From 52cd1906ef6b93d638a78a34765c38c7edadd2ff Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 1 Oct 2024 16:30:06 +0200 Subject: [PATCH 02/12] HID: core: save one kmemdup during .probe() Turns out the first kmemdup is only required for the .report_fixup() driver callback. There is no need to do two kmemdup() in a row in case .report_fixup() is not present. Reviewed-by: Peter Hutterer Link: https://patch.msgid.link/20241001-hid-bpf-hid-generic-v3-2-2ef1019468df@kernel.org Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-core.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index d6bf933623e8..6053e7cdc0c1 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1214,7 +1214,7 @@ int hid_open_report(struct hid_device *device) struct hid_item item; unsigned int size; const __u8 *start; - __u8 *buf; + __u8 *buf = NULL; const __u8 *end; const __u8 *next; int ret; @@ -1235,14 +1235,18 @@ int hid_open_report(struct hid_device *device) return -ENODEV; size = device->bpf_rsize; - buf = kmemdup(start, size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; + if (device->driver->report_fixup) { + /* + * device->driver->report_fixup() needs to work + * on a copy of our report descriptor so it can + * change it. + */ + buf = kmemdup(start, size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; - if (device->driver->report_fixup) start = device->driver->report_fixup(device, buf, &size); - else - start = buf; + } start = kmemdup(start, size, GFP_KERNEL); kfree(buf); From 7316fef4b993c4435f9fe55e7f2590baf25621ec Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 1 Oct 2024 16:30:07 +0200 Subject: [PATCH 03/12] HID: core: remove one more kmemdup on .probe() That last kmemdup while opening the report descriptor was required to have a common kfree() on it. Move that kmemdup in the only special case it's required (if there is a .report_fixup()), and add a more elaborated check before freeing hdev->rdesc, to avoid a double free. Reviewed-by: Peter Hutterer Link: https://patch.msgid.link/20241001-hid-bpf-hid-generic-v3-3-2ef1019468df@kernel.org Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-core.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6053e7cdc0c1..b0a22e173502 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -685,7 +685,14 @@ static void hid_close_report(struct hid_device *device) INIT_LIST_HEAD(&report_enum->report_list); } - kfree(device->rdesc); + /* + * If the HID driver had a rdesc_fixup() callback, dev->rdesc + * will be allocated by hid-core and needs to be freed. + * Otherwise, it is either equal to dev_rdesc or bpf_rdesc, in + * which cases it'll be freed later on device removal or destroy. + */ + if (device->rdesc != device->dev_rdesc && device->rdesc != device->bpf_rdesc) + kfree(device->rdesc); device->rdesc = NULL; device->rsize = 0; @@ -1214,7 +1221,6 @@ int hid_open_report(struct hid_device *device) struct hid_item item; unsigned int size; const __u8 *start; - __u8 *buf = NULL; const __u8 *end; const __u8 *next; int ret; @@ -1241,17 +1247,23 @@ int hid_open_report(struct hid_device *device) * on a copy of our report descriptor so it can * change it. */ - buf = kmemdup(start, size, GFP_KERNEL); + __u8 *buf = kmemdup(start, size, GFP_KERNEL); + if (buf == NULL) return -ENOMEM; start = device->driver->report_fixup(device, buf, &size); - } - start = kmemdup(start, size, GFP_KERNEL); - kfree(buf); - if (start == NULL) - return -ENOMEM; + /* + * The second kmemdup is required in case report_fixup() returns + * a static read-only memory, but we have no idea if that memory + * needs to be cleaned up or not at the end. + */ + start = kmemdup(start, size, GFP_KERNEL); + kfree(buf); + if (start == NULL) + return -ENOMEM; + } device->rdesc = start; device->rsize = size; From 6fd47effe92b794c32f08504c2c64d1e40bbb543 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 1 Oct 2024 16:30:08 +0200 Subject: [PATCH 04/12] HID: bpf: allow write access to quirks field in struct hid_device This allows to give more control from BPF during report descriptor fixup. We already reset the quirks before calling ->probe(), so now we reset it once before calling hid_bpf_rdesc_fixup(). Reviewed-by: Peter Hutterer Link: https://patch.msgid.link/20241001-hid-bpf-hid-generic-v3-4-2ef1019468df@kernel.org Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/hid_bpf_struct_ops.c | 1 + drivers/hid/hid-core.c | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c index 702c22fae136..0e611a9d79d7 100644 --- a/drivers/hid/bpf/hid_bpf_struct_ops.c +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c @@ -79,6 +79,7 @@ static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log, WRITE_RANGE(hid_device, name, true), WRITE_RANGE(hid_device, uniq, true), WRITE_RANGE(hid_device, phys, true), + WRITE_RANGE(hid_device, quirks, false), }; #undef WRITE_RANGE const struct btf_type *state = NULL; diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b0a22e173502..8e879937e956 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2709,6 +2709,12 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) int ret; if (!hdev->bpf_rsize) { + unsigned int quirks; + + /* reset the quirks that has been previously set */ + quirks = hid_lookup_quirk(hdev); + hdev->quirks = quirks; + /* in case a bpf program gets detached, we need to free the old one */ hid_free_bpf_rdesc(hdev); @@ -2718,6 +2724,9 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) /* call_hid_bpf_rdesc_fixup will always return a valid pointer */ hdev->bpf_rdesc = call_hid_bpf_rdesc_fixup(hdev, hdev->dev_rdesc, &hdev->bpf_rsize); + if (quirks ^ hdev->quirks) + hid_info(hdev, "HID-BPF toggled quirks on the device: %04x", + quirks ^ hdev->quirks); } if (!hid_check_device_match(hdev, hdrv, &id)) @@ -2727,8 +2736,6 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) if (!hdev->devres_group_id) return -ENOMEM; - /* reset the quirks that has been previously set */ - hdev->quirks = hid_lookup_quirk(hdev); hdev->driver = hdrv; if (hdrv->probe) { From 0b838d768ccdbdfbcaed5f4b18b4bf63e53a0e0d Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 1 Oct 2024 16:30:09 +0200 Subject: [PATCH 05/12] selftests/hid: add dependency on hid_common.h Allows to recompile the C tests when that file changes Reviewed-by: Peter Hutterer Acked-by: Shuah Khan Link: https://patch.msgid.link/20241001-hid-bpf-hid-generic-v3-5-2ef1019468df@kernel.org Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile index 72be55ac4bdf..9399fa3f2f9d 100644 --- a/tools/testing/selftests/hid/Makefile +++ b/tools/testing/selftests/hid/Makefile @@ -229,7 +229,7 @@ $(BPF_SKELS): %.skel.h: %.bpf.o $(BPFTOOL) | $(OUTPUT) $(Q)$(BPFTOOL) gen object $(<:.o=.linked1.o) $< $(Q)$(BPFTOOL) gen skeleton $(<:.o=.linked1.o) name $(notdir $(<:.bpf.o=)) > $@ -$(OUTPUT)/%.o: %.c $(BPF_SKELS) +$(OUTPUT)/%.o: %.c $(BPF_SKELS) hid_common.h $(call msg,CC,,$@) $(Q)$(CC) $(CFLAGS) -c $(filter %.c,$^) $(LDLIBS) -o $@ From 4fb41dfde0699796b955eb94e7b8037a67b4b3a5 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 1 Oct 2024 16:30:10 +0200 Subject: [PATCH 06/12] selftests/hid: cleanup C tests by adding a common struct uhid_device Allows to have an abstract class uhid_device which handles all of the uhid part without having to mess up with individual fds. struct attach_prog_args is now never used in hid_bpf.c, so drop it as well Acked-by: Shuah Khan Link: https://patch.msgid.link/20241001-hid-bpf-hid-generic-v3-6-2ef1019468df@kernel.org Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/hid_bpf.c | 77 +++++++++--------------- tools/testing/selftests/hid/hid_common.h | 74 +++++++++++++++-------- tools/testing/selftests/hid/hidraw.c | 36 +++-------- 3 files changed, 87 insertions(+), 100 deletions(-) diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 86f4d66379f7..31ff92e0debd 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -4,13 +4,6 @@ #include "hid_common.h" #include -struct attach_prog_args { - int prog_fd; - unsigned int hid; - int retval; - int insert_head; -}; - struct hid_hw_request_syscall_args { __u8 data[10]; unsigned int hid; @@ -21,11 +14,8 @@ struct hid_hw_request_syscall_args { }; FIXTURE(hid_bpf) { - int dev_id; - int uhid_fd; + struct uhid_device hid; int hidraw_fd; - int hid_id; - pthread_t tid; struct hid *skel; struct bpf_link *hid_links[3]; /* max number of programs loaded in a single test */ }; @@ -54,10 +44,10 @@ static void detach_bpf(FIXTURE_DATA(hid_bpf) * self) FIXTURE_TEARDOWN(hid_bpf) { void *uhid_err; - uhid_destroy(_metadata, self->uhid_fd); + uhid_destroy(_metadata, &self->hid); detach_bpf(self); - pthread_join(self->tid, &uhid_err); + pthread_join(self->hid.tid, &uhid_err); } #define TEARDOWN_LOG(fmt, ...) do { \ TH_LOG(fmt, ##__VA_ARGS__); \ @@ -66,23 +56,10 @@ FIXTURE_TEARDOWN(hid_bpf) { FIXTURE_SETUP(hid_bpf) { - time_t t; int err; - /* initialize random number generator */ - srand((unsigned int)time(&t)); - - self->dev_id = rand() % 1024; - - self->uhid_fd = setup_uhid(_metadata, self->dev_id); - - /* locate the uev, self, variant);ent file of the created device */ - self->hid_id = get_hid_id(self->dev_id); - ASSERT_GT(self->hid_id, 0) - TEARDOWN_LOG("Could not locate uhid device id: %d", self->hid_id); - - err = uhid_start_listener(_metadata, &self->tid, self->uhid_fd); - ASSERT_EQ(0, err) TEARDOWN_LOG("could not start udev listener: %d", err); + err = setup_uhid(_metadata, &self->hid); + ASSERT_OK(err); } struct test_program { @@ -129,7 +106,7 @@ static void load_programs(const struct test_program programs[], ops_hid_id = bpf_map__initial_value(map, NULL); ASSERT_OK_PTR(ops_hid_id) TH_LOG("unable to retrieve struct_ops data"); - *ops_hid_id = self->hid_id; + *ops_hid_id = self->hid.hid_id; } /* we disable the auto-attach feature of all maps because we @@ -157,7 +134,7 @@ static void load_programs(const struct test_program programs[], hid__attach(self->skel); - self->hidraw_fd = open_hidraw(self->dev_id); + self->hidraw_fd = open_hidraw(&self->hid); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); } @@ -192,7 +169,7 @@ TEST_F(hid_bpf, raw_event) /* inject one event */ buf[0] = 1; buf[1] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* check that hid_first_event() was executed */ ASSERT_EQ(self->skel->data->callback_check, 42) TH_LOG("callback_check1"); @@ -208,7 +185,7 @@ TEST_F(hid_bpf, raw_event) memset(buf, 0, sizeof(buf)); buf[0] = 1; buf[1] = 47; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* check that hid_first_event() was executed */ ASSERT_EQ(self->skel->data->callback_check, 47) TH_LOG("callback_check1"); @@ -239,7 +216,7 @@ TEST_F(hid_bpf, subprog_raw_event) /* inject one event */ buf[0] = 1; buf[1] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -252,7 +229,7 @@ TEST_F(hid_bpf, subprog_raw_event) memset(buf, 0, sizeof(buf)); buf[0] = 1; buf[1] = 47; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -303,7 +280,7 @@ TEST_F(hid_bpf, test_attach_detach) /* inject one event */ buf[0] = 1; buf[1] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -326,14 +303,14 @@ TEST_F(hid_bpf, test_attach_detach) /* detach the program */ detach_bpf(self); - self->hidraw_fd = open_hidraw(self->dev_id); + self->hidraw_fd = open_hidraw(&self->hid); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); /* inject another event */ memset(buf, 0, sizeof(buf)); buf[0] = 1; buf[1] = 47; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -352,7 +329,7 @@ TEST_F(hid_bpf, test_attach_detach) memset(buf, 0, sizeof(buf)); buf[0] = 1; buf[1] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -382,7 +359,7 @@ TEST_F(hid_bpf, test_hid_change_report) /* inject one event */ buf[0] = 1; buf[1] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -412,7 +389,7 @@ TEST_F(hid_bpf, test_hid_user_input_report_call) LOAD_BPF; - args.hid = self->hid_id; + args.hid = self->hid.hid_id; args.data[0] = 1; /* report ID */ args.data[1] = 2; /* report ID */ args.data[2] = 42; /* report ID */ @@ -458,7 +435,7 @@ TEST_F(hid_bpf, test_hid_user_output_report_call) LOAD_BPF; - args.hid = self->hid_id; + args.hid = self->hid.hid_id; args.data[0] = 1; /* report ID */ args.data[1] = 2; /* report ID */ args.data[2] = 42; /* report ID */ @@ -506,7 +483,7 @@ TEST_F(hid_bpf, test_hid_user_raw_request_call) LOAD_BPF; - args.hid = self->hid_id; + args.hid = self->hid.hid_id; args.data[0] = 1; /* report ID */ prog_fd = bpf_program__fd(self->skel->progs.hid_user_raw_request); @@ -539,7 +516,7 @@ TEST_F(hid_bpf, test_hid_filter_raw_request_call) /* inject one event */ buf[0] = 1; buf[1] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -565,7 +542,7 @@ TEST_F(hid_bpf, test_hid_filter_raw_request_call) /* detach the program */ detach_bpf(self); - self->hidraw_fd = open_hidraw(self->dev_id); + self->hidraw_fd = open_hidraw(&self->hid); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf); @@ -641,7 +618,7 @@ TEST_F(hid_bpf, test_hid_filter_output_report_call) /* inject one event */ buf[0] = 1; buf[1] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -667,7 +644,7 @@ TEST_F(hid_bpf, test_hid_filter_output_report_call) /* detach the program */ detach_bpf(self); - self->hidraw_fd = open_hidraw(self->dev_id); + self->hidraw_fd = open_hidraw(&self->hid); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); err = write(self->hidraw_fd, buf, 3); @@ -742,7 +719,7 @@ TEST_F(hid_bpf, test_multiply_events_wq) /* inject one event */ buf[0] = 1; buf[1] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -780,7 +757,7 @@ TEST_F(hid_bpf, test_multiply_events) /* inject one event */ buf[0] = 1; buf[1] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -816,7 +793,7 @@ TEST_F(hid_bpf, test_hid_infinite_loop_input_report_call) buf[1] = 2; buf[2] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -867,7 +844,7 @@ TEST_F(hid_bpf, test_hid_attach_flags) /* inject one event */ buf[0] = 1; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); diff --git a/tools/testing/selftests/hid/hid_common.h b/tools/testing/selftests/hid/hid_common.h index f151f151a1ed..a7d836a35bb1 100644 --- a/tools/testing/selftests/hid/hid_common.h +++ b/tools/testing/selftests/hid/hid_common.h @@ -19,6 +19,13 @@ __typeof__(b) _b = (b); \ _a < _b ? _a : _b; }) +struct uhid_device { + int dev_id; /* uniq (random) number to identify the device */ + int uhid_fd; + int hid_id; /* HID device id in the system */ + pthread_t tid; /* thread for reading uhid events */ +}; + static unsigned char rdesc[] = { 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 0x09, 0x21, /* Usage (Vendor Usage 0x21) */ @@ -146,14 +153,14 @@ static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb) return uhid_write(_metadata, fd, &ev); } -static void uhid_destroy(struct __test_metadata *_metadata, int fd) +static void uhid_destroy(struct __test_metadata *_metadata, struct uhid_device *hid) { struct uhid_event ev; memset(&ev, 0, sizeof(ev)); ev.type = UHID_DESTROY; - uhid_write(_metadata, fd, &ev); + uhid_write(_metadata, hid->uhid_fd, &ev); } static int uhid_event(struct __test_metadata *_metadata, int fd) @@ -281,7 +288,8 @@ static int uhid_start_listener(struct __test_metadata *_metadata, pthread_t *tid return 0; } -static int uhid_send_event(struct __test_metadata *_metadata, int fd, __u8 *buf, size_t size) +static int uhid_send_event(struct __test_metadata *_metadata, struct uhid_device *hid, + __u8 *buf, size_t size) { struct uhid_event ev; @@ -294,25 +302,7 @@ static int uhid_send_event(struct __test_metadata *_metadata, int fd, __u8 *buf, memcpy(ev.u.input2.data, buf, size); - return uhid_write(_metadata, fd, &ev); -} - -static int setup_uhid(struct __test_metadata *_metadata, int rand_nb) -{ - int fd; - const char *path = "/dev/uhid"; - int ret; - - fd = open(path, O_RDWR | O_CLOEXEC); - ASSERT_GE(fd, 0) TH_LOG("open uhid-cdev failed; %d", fd); - - ret = uhid_create(_metadata, fd, rand_nb); - ASSERT_EQ(0, ret) { - TH_LOG("create uhid device failed: %d", ret); - close(fd); - } - - return fd; + return uhid_write(_metadata, hid->uhid_fd, &ev); } static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *dir) @@ -421,12 +411,12 @@ static int get_hidraw(int dev_id) return found; } -static int open_hidraw(int dev_id) +static int open_hidraw(struct uhid_device *hid) { int hidraw_number; char hidraw_path[64] = { 0 }; - hidraw_number = get_hidraw(dev_id); + hidraw_number = get_hidraw(hid->dev_id); if (hidraw_number < 0) return hidraw_number; @@ -434,3 +424,39 @@ static int open_hidraw(int dev_id) sprintf(hidraw_path, "/dev/hidraw%d", hidraw_number); return open(hidraw_path, O_RDWR | O_NONBLOCK); } + +static int setup_uhid(struct __test_metadata *_metadata, struct uhid_device *hid) +{ + const char *path = "/dev/uhid"; + time_t t; + int ret; + + /* initialize random number generator */ + srand((unsigned int)time(&t)); + + hid->dev_id = rand() % 1024; + + hid->uhid_fd = open(path, O_RDWR | O_CLOEXEC); + ASSERT_GE(hid->uhid_fd, 0) TH_LOG("open uhid-cdev failed; %d", hid->uhid_fd); + + ret = uhid_create(_metadata, hid->uhid_fd, hid->dev_id); + ASSERT_EQ(0, ret) { + TH_LOG("create uhid device failed: %d", ret); + close(hid->uhid_fd); + return ret; + } + + /* locate the uevent file of the created device */ + hid->hid_id = get_hid_id(hid->dev_id); + ASSERT_GT(hid->hid_id, 0) + TH_LOG("Could not locate uhid device id: %d", hid->hid_id); + + ret = uhid_start_listener(_metadata, &hid->tid, hid->uhid_fd); + ASSERT_EQ(0, ret) { + TH_LOG("could not start udev listener: %d", ret); + close(hid->uhid_fd); + return ret; + } + + return 0; +} diff --git a/tools/testing/selftests/hid/hidraw.c b/tools/testing/selftests/hid/hidraw.c index f8b4f7ff292c..5934818b2036 100644 --- a/tools/testing/selftests/hid/hidraw.c +++ b/tools/testing/selftests/hid/hidraw.c @@ -9,11 +9,8 @@ #endif /* HIDIOCREVOKE */ FIXTURE(hidraw) { - int dev_id; - int uhid_fd; + struct uhid_device hid; int hidraw_fd; - int hid_id; - pthread_t tid; }; static void close_hidraw(FIXTURE_DATA(hidraw) * self) { @@ -25,10 +22,10 @@ static void close_hidraw(FIXTURE_DATA(hidraw) * self) FIXTURE_TEARDOWN(hidraw) { void *uhid_err; - uhid_destroy(_metadata, self->uhid_fd); + uhid_destroy(_metadata, &self->hid); close_hidraw(self); - pthread_join(self->tid, &uhid_err); + pthread_join(self->hid.tid, &uhid_err); } #define TEARDOWN_LOG(fmt, ...) do { \ TH_LOG(fmt, ##__VA_ARGS__); \ @@ -37,25 +34,12 @@ FIXTURE_TEARDOWN(hidraw) { FIXTURE_SETUP(hidraw) { - time_t t; int err; - /* initialize random number generator */ - srand((unsigned int)time(&t)); + err = setup_uhid(_metadata, &self->hid); + ASSERT_OK(err); - self->dev_id = rand() % 1024; - - self->uhid_fd = setup_uhid(_metadata, self->dev_id); - - /* locate the uev, self, variant);ent file of the created device */ - self->hid_id = get_hid_id(self->dev_id); - ASSERT_GT(self->hid_id, 0) - TEARDOWN_LOG("Could not locate uhid device id: %d", self->hid_id); - - err = uhid_start_listener(_metadata, &self->tid, self->uhid_fd); - ASSERT_EQ(0, err) TEARDOWN_LOG("could not start udev listener: %d", err); - - self->hidraw_fd = open_hidraw(self->dev_id); + self->hidraw_fd = open_hidraw(&self->hid); ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw"); } @@ -79,7 +63,7 @@ TEST_F(hidraw, raw_event) /* inject one event */ buf[0] = 1; buf[1] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -101,7 +85,7 @@ TEST_F(hidraw, raw_event_revoked) /* inject one event */ buf[0] = 1; buf[1] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -117,7 +101,7 @@ TEST_F(hidraw, raw_event_revoked) /* inject one other event */ buf[0] = 1; buf[1] = 43; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); /* read the data from hidraw */ memset(buf, 0, sizeof(buf)); @@ -161,7 +145,7 @@ TEST_F(hidraw, poll_revoked) /* inject one event */ buf[0] = 1; buf[1] = 42; - uhid_send_event(_metadata, self->uhid_fd, buf, 6); + uhid_send_event(_metadata, &self->hid, buf, 6); while (true) { ready = poll(pfds, 1, 5000); From 72c55473fc8c82b06df79473f04c5231d51580d7 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 1 Oct 2024 16:30:11 +0200 Subject: [PATCH 07/12] selftests/hid: allow to parametrize bus/vid/pid/rdesc on the test device This will be useful to introduce variants in tests to test the interactions between HID-BPF and some kernel modules. Reviewed-by: Peter Hutterer Acked-by: Shuah Khan Link: https://patch.msgid.link/20241001-hid-bpf-hid-generic-v3-7-2ef1019468df@kernel.org Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/hid_bpf.c | 2 +- tools/testing/selftests/hid/hid_common.h | 46 +++++++++++++++--------- tools/testing/selftests/hid/hidraw.c | 2 +- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 31ff92e0debd..1e979fb3542b 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -58,7 +58,7 @@ FIXTURE_SETUP(hid_bpf) { int err; - err = setup_uhid(_metadata, &self->hid); + err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a36, rdesc, sizeof(rdesc)); ASSERT_OK(err); } diff --git a/tools/testing/selftests/hid/hid_common.h b/tools/testing/selftests/hid/hid_common.h index a7d836a35bb1..f77f69c6657d 100644 --- a/tools/testing/selftests/hid/hid_common.h +++ b/tools/testing/selftests/hid/hid_common.h @@ -23,6 +23,9 @@ struct uhid_device { int dev_id; /* uniq (random) number to identify the device */ int uhid_fd; int hid_id; /* HID device id in the system */ + __u16 bus; + __u32 vid; + __u32 pid; pthread_t tid; /* thread for reading uhid events */ }; @@ -129,7 +132,9 @@ static int uhid_write(struct __test_metadata *_metadata, int fd, const struct uh } } -static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb) +static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb, + __u16 bus, __u32 vid, __u32 pid, __u8 *rdesc, + size_t rdesc_size) { struct uhid_event ev; char buf[25]; @@ -140,10 +145,10 @@ static int uhid_create(struct __test_metadata *_metadata, int fd, int rand_nb) ev.type = UHID_CREATE; strcpy((char *)ev.u.create.name, buf); ev.u.create.rd_data = rdesc; - ev.u.create.rd_size = sizeof(rdesc); - ev.u.create.bus = BUS_USB; - ev.u.create.vendor = 0x0001; - ev.u.create.product = 0x0a37; + ev.u.create.rd_size = rdesc_size; + ev.u.create.bus = bus; + ev.u.create.vendor = vid; + ev.u.create.product = pid; ev.u.create.version = 0; ev.u.create.country = 0; @@ -305,15 +310,17 @@ static int uhid_send_event(struct __test_metadata *_metadata, struct uhid_device return uhid_write(_metadata, hid->uhid_fd, &ev); } -static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *dir) +static bool match_sysfs_device(struct uhid_device *hid, const char *workdir, struct dirent *dir) { - const char *target = "0003:0001:0A37.*"; + char target[20] = ""; char phys[512]; char uevent[1024]; char temp[512]; int fd, nread; bool found = false; + snprintf(target, sizeof(target), "%04X:%04X:%04X.*", hid->bus, hid->vid, hid->pid); + if (fnmatch(target, dir->d_name, 0)) return false; @@ -324,7 +331,7 @@ static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *d if (fd < 0) return false; - sprintf(phys, "PHYS=%d", dev_id); + sprintf(phys, "PHYS=%d", hid->dev_id); nread = read(fd, temp, ARRAY_SIZE(temp)); if (nread > 0 && (strstr(temp, phys)) != NULL) @@ -335,7 +342,7 @@ static bool match_sysfs_device(int dev_id, const char *workdir, struct dirent *d return found; } -static int get_hid_id(int dev_id) +static int get_hid_id(struct uhid_device *hid) { const char *workdir = "/sys/devices/virtual/misc/uhid"; const char *str_id; @@ -350,10 +357,10 @@ static int get_hid_id(int dev_id) d = opendir(workdir); if (d) { while ((dir = readdir(d)) != NULL) { - if (!match_sysfs_device(dev_id, workdir, dir)) + if (!match_sysfs_device(hid, workdir, dir)) continue; - str_id = dir->d_name + sizeof("0003:0001:0A37."); + str_id = dir->d_name + sizeof("0000:0000:0000."); found = (int)strtol(str_id, NULL, 16); break; @@ -367,7 +374,7 @@ static int get_hid_id(int dev_id) return found; } -static int get_hidraw(int dev_id) +static int get_hidraw(struct uhid_device *hid) { const char *workdir = "/sys/devices/virtual/misc/uhid"; char sysfs[1024]; @@ -384,7 +391,7 @@ static int get_hidraw(int dev_id) continue; while ((dir = readdir(d)) != NULL) { - if (!match_sysfs_device(dev_id, workdir, dir)) + if (!match_sysfs_device(hid, workdir, dir)) continue; sprintf(sysfs, "%s/%s/hidraw", workdir, dir->d_name); @@ -416,7 +423,7 @@ static int open_hidraw(struct uhid_device *hid) int hidraw_number; char hidraw_path[64] = { 0 }; - hidraw_number = get_hidraw(hid->dev_id); + hidraw_number = get_hidraw(hid); if (hidraw_number < 0) return hidraw_number; @@ -425,7 +432,8 @@ static int open_hidraw(struct uhid_device *hid) return open(hidraw_path, O_RDWR | O_NONBLOCK); } -static int setup_uhid(struct __test_metadata *_metadata, struct uhid_device *hid) +static int setup_uhid(struct __test_metadata *_metadata, struct uhid_device *hid, + __u16 bus, __u32 vid, __u32 pid, const __u8 *rdesc, size_t rdesc_size) { const char *path = "/dev/uhid"; time_t t; @@ -435,11 +443,15 @@ static int setup_uhid(struct __test_metadata *_metadata, struct uhid_device *hid srand((unsigned int)time(&t)); hid->dev_id = rand() % 1024; + hid->bus = bus; + hid->vid = vid; + hid->pid = pid; hid->uhid_fd = open(path, O_RDWR | O_CLOEXEC); ASSERT_GE(hid->uhid_fd, 0) TH_LOG("open uhid-cdev failed; %d", hid->uhid_fd); - ret = uhid_create(_metadata, hid->uhid_fd, hid->dev_id); + ret = uhid_create(_metadata, hid->uhid_fd, hid->dev_id, bus, vid, pid, + (__u8 *)rdesc, rdesc_size); ASSERT_EQ(0, ret) { TH_LOG("create uhid device failed: %d", ret); close(hid->uhid_fd); @@ -447,7 +459,7 @@ static int setup_uhid(struct __test_metadata *_metadata, struct uhid_device *hid } /* locate the uevent file of the created device */ - hid->hid_id = get_hid_id(hid->dev_id); + hid->hid_id = get_hid_id(hid); ASSERT_GT(hid->hid_id, 0) TH_LOG("Could not locate uhid device id: %d", hid->hid_id); diff --git a/tools/testing/selftests/hid/hidraw.c b/tools/testing/selftests/hid/hidraw.c index 5934818b2036..821db37ba4bb 100644 --- a/tools/testing/selftests/hid/hidraw.c +++ b/tools/testing/selftests/hid/hidraw.c @@ -36,7 +36,7 @@ FIXTURE_SETUP(hidraw) { int err; - err = setup_uhid(_metadata, &self->hid); + err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a37, rdesc, sizeof(rdesc)); ASSERT_OK(err); self->hidraw_fd = open_hidraw(&self->hid); From 645c224ac5f6e0013931c342ea707b398d24d410 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 1 Oct 2024 16:30:12 +0200 Subject: [PATCH 08/12] HID: add per device quirk to force bind to hid-generic We already have the possibility to force not binding to hid-generic and rely on a dedicated driver, but we couldn't do the other way around. This is useful for BPF programs where we are fixing the report descriptor and the events, but want to avoid a specialized driver to come after BPF which would unwind everything that is done there. Reviewed-by: Peter Hutterer Link: https://patch.msgid.link/20241001-hid-bpf-hid-generic-v3-8-2ef1019468df@kernel.org Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-core.c | 5 +++-- drivers/hid/hid-generic.c | 3 +++ include/linux/hid.h | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8e879937e956..f1c23b21e96a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2698,9 +2698,10 @@ static bool hid_check_device_match(struct hid_device *hdev, /* * hid-generic implements .match(), so we must be dealing with a * different HID driver here, and can simply check if - * hid_ignore_special_drivers is set or not. + * hid_ignore_special_drivers or HID_QUIRK_IGNORE_SPECIAL_DRIVER + * are set or not. */ - return !hid_ignore_special_drivers; + return !hid_ignore_special_drivers && !(hdev->quirks & HID_QUIRK_IGNORE_SPECIAL_DRIVER); } static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) diff --git a/drivers/hid/hid-generic.c b/drivers/hid/hid-generic.c index f9db991d3c5a..88882c1bfffe 100644 --- a/drivers/hid/hid-generic.c +++ b/drivers/hid/hid-generic.c @@ -40,6 +40,9 @@ static bool hid_generic_match(struct hid_device *hdev, if (ignore_special_driver) return true; + if (hdev->quirks & HID_QUIRK_IGNORE_SPECIAL_DRIVER) + return true; + if (hdev->quirks & HID_QUIRK_HAVE_SPECIAL_DRIVER) return false; diff --git a/include/linux/hid.h b/include/linux/hid.h index ff58b5ceb62e..63330d623335 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -359,6 +359,7 @@ struct hid_item { * | @HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP: * | @HID_QUIRK_HAVE_SPECIAL_DRIVER: * | @HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE: + * | @HID_QUIRK_IGNORE_SPECIAL_DRIVER * | @HID_QUIRK_FULLSPEED_INTERVAL: * | @HID_QUIRK_NO_INIT_REPORTS: * | @HID_QUIRK_NO_IGNORE: @@ -384,6 +385,7 @@ struct hid_item { #define HID_QUIRK_HAVE_SPECIAL_DRIVER BIT(19) #define HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE BIT(20) #define HID_QUIRK_NOINVERT BIT(21) +#define HID_QUIRK_IGNORE_SPECIAL_DRIVER BIT(22) #define HID_QUIRK_FULLSPEED_INTERVAL BIT(28) #define HID_QUIRK_NO_INIT_REPORTS BIT(29) #define HID_QUIRK_NO_IGNORE BIT(30) From e14e0eaeb040899f7cb363cdfdf8fbee84a45f08 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 1 Oct 2024 16:30:13 +0200 Subject: [PATCH 09/12] selftests/hid: add test for assigning a given device to hid-generic We use a well known VID/PID on a driver that doesn't need to talk to the device, ensures we created the device against the target driver, then load our program and ensure we have unbound to this driver and use hid-generic instead. Reviewed-by: Peter Hutterer Acked-by: Shuah Khan Link: https://patch.msgid.link/20241001-hid-bpf-hid-generic-v3-9-2ef1019468df@kernel.org Signed-off-by: Benjamin Tissoires --- tools/testing/selftests/hid/hid_bpf.c | 80 ++++++++++++++++++- tools/testing/selftests/hid/progs/hid.c | 12 +++ .../selftests/hid/progs/hid_bpf_helpers.h | 6 +- 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index 1e979fb3542b..ca58bfa3ca65 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -54,11 +54,41 @@ FIXTURE_TEARDOWN(hid_bpf) { hid_bpf_teardown(_metadata, self, variant); \ } while (0) +struct specific_device { + const char test_name[64]; + __u16 bus; + __u32 vid; + __u32 pid; +}; + FIXTURE_SETUP(hid_bpf) { + const struct specific_device *match = NULL; int err; - err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a36, rdesc, sizeof(rdesc)); + const struct specific_device devices[] = { + { + .test_name = "test_hid_driver_probe", + .bus = BUS_BLUETOOTH, + .vid = 0x05ac, /* USB_VENDOR_ID_APPLE */ + .pid = 0x022c, /* USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI */ + }, { + .test_name = "*", + .bus = BUS_USB, + .vid = 0x0001, + .pid = 0x0a36, + }}; + + for (int i = 0; i < ARRAY_SIZE(devices); i++) { + match = &devices[i]; + if (!strncmp(_metadata->name, devices[i].test_name, sizeof(devices[i].test_name))) + break; + } + + ASSERT_OK_PTR(match); + + err = setup_uhid(_metadata, &self->hid, match->bus, match->vid, match->pid, + rdesc, sizeof(rdesc)); ASSERT_OK(err); } @@ -855,6 +885,54 @@ TEST_F(hid_bpf, test_hid_attach_flags) ASSERT_EQ(buf[3], 3); } +static bool is_using_driver(struct __test_metadata *_metadata, struct uhid_device *hid, + const char *driver) +{ + char driver_line[512]; + char uevent[1024]; + char temp[512]; + int fd, nread; + bool found = false; + + sprintf(uevent, "/sys/bus/hid/devices/%04X:%04X:%04X.%04X/uevent", + hid->bus, hid->vid, hid->pid, hid->hid_id); + + fd = open(uevent, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + TH_LOG("couldn't open '%s': %d, %d", uevent, fd, errno); + return false; + } + + sprintf(driver_line, "DRIVER=%s", driver); + + nread = read(fd, temp, ARRAY_SIZE(temp)); + if (nread > 0 && (strstr(temp, driver_line)) != NULL) + found = true; + + close(fd); + + return found; +} + +/* + * Attach hid_driver_probe to the given uhid device, + * check that the device is now using hid-generic. + */ +TEST_F(hid_bpf, test_hid_driver_probe) +{ + const struct test_program progs[] = { + { + .name = "hid_test_driver_probe", + }, + }; + + ASSERT_TRUE(is_using_driver(_metadata, &self->hid, "apple")); + + LOAD_PROGRAMS(progs); + + ASSERT_TRUE(is_using_driver(_metadata, &self->hid, "hid-generic")); +} + /* * Attach hid_rdesc_fixup to the given uhid device, * retrieve and open the matching hidraw node, diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c index 5ecc845ef792..9b22e9a0e658 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -598,3 +598,15 @@ SEC(".struct_ops.link") struct hid_bpf_ops test_infinite_loop_input_report = { .hid_device_event = (void *)hid_test_infinite_loop_input_report, }; + +SEC("?struct_ops.s/hid_rdesc_fixup") +int BPF_PROG(hid_test_driver_probe, struct hid_bpf_ctx *hid_ctx) +{ + hid_ctx->hid->quirks |= HID_QUIRK_IGNORE_SPECIAL_DRIVER; + return 0; +} + +SEC(".struct_ops.link") +struct hid_bpf_ops test_driver_probe = { + .hid_rdesc_fixup = (void *)hid_test_driver_probe, +}; diff --git a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h index e5db897586bb..1a645684a117 100644 --- a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h +++ b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h @@ -84,10 +84,14 @@ struct hid_bpf_ops { struct hid_device *hdev; }; +#define BIT(n) (1U << n) + #ifndef BPF_F_BEFORE -#define BPF_F_BEFORE (1U << 3) +#define BPF_F_BEFORE BIT(3) #endif +#define HID_QUIRK_IGNORE_SPECIAL_DRIVER BIT(22) + /* following are kfuncs exported by HID for HID-BPF */ extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, From 9bc089307e8dff7797233308372b4a90ce8f79be Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 17 Oct 2024 18:34:58 +0200 Subject: [PATCH 10/12] HID: bpf: Fix NKRO on Mistel MD770 Mistel MD770 keyboard (using Holtek Semiconductor, Inc. controller) has a quirk in report descriptor in one of its interfaces (more detail in the source file). Fix up the descriptor to allow NKRO to work again. Tested by loading the BPF program and confirming that 8 simultaneous keypresses work. Link: https://bugzilla.kernel.org/show_bug.cgi?id=218495 Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests/122 Signed-off-by: Tatsuyuki Ishi Acked-by: Jiri Kosina Link: https://patch.msgid.link/20241017-import_bpf_6-13-v2-1-6a7acb89a97f@kernel.org Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/progs/Mistel__MD770.bpf.c | 154 ++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 drivers/hid/bpf/progs/Mistel__MD770.bpf.c diff --git a/drivers/hid/bpf/progs/Mistel__MD770.bpf.c b/drivers/hid/bpf/progs/Mistel__MD770.bpf.c new file mode 100644 index 000000000000..fb8b5a6968b1 --- /dev/null +++ b/drivers/hid/bpf/progs/Mistel__MD770.bpf.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2024 Tatsuyuki Ishi + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include + +#define VID_HOLTEK 0x04D9 +#define PID_MD770 0x0339 +#define RDESC_SIZE 203 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HOLTEK, PID_MD770) +); + +/* + * The Mistel MD770 keyboard reports the first 6 simultaneous key presses + * through the first interface, and anything beyond that through a second + * interface. Unfortunately, the second interface's report descriptor has an + * error, causing events to be malformed and ignored. This HID-BPF driver + * fixes the descriptor to allow NKRO to work again. + * + * For reference, this is the original report descriptor: + * + * 0x05, 0x01, // Usage Page (Generic Desktop) 0 + * 0x09, 0x80, // Usage (System Control) 2 + * 0xa1, 0x01, // Collection (Application) 4 + * 0x85, 0x01, // Report ID (1) 6 + * 0x19, 0x81, // Usage Minimum (129) 8 + * 0x29, 0x83, // Usage Maximum (131) 10 + * 0x15, 0x00, // Logical Minimum (0) 12 + * 0x25, 0x01, // Logical Maximum (1) 14 + * 0x95, 0x03, // Report Count (3) 16 + * 0x75, 0x01, // Report Size (1) 18 + * 0x81, 0x02, // Input (Data,Var,Abs) 20 + * 0x95, 0x01, // Report Count (1) 22 + * 0x75, 0x05, // Report Size (5) 24 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) 26 + * 0xc0, // End Collection 28 + * 0x05, 0x0c, // Usage Page (Consumer Devices) 29 + * 0x09, 0x01, // Usage (Consumer Control) 31 + * 0xa1, 0x01, // Collection (Application) 33 + * 0x85, 0x02, // Report ID (2) 35 + * 0x15, 0x00, // Logical Minimum (0) 37 + * 0x25, 0x01, // Logical Maximum (1) 39 + * 0x95, 0x12, // Report Count (18) 41 + * 0x75, 0x01, // Report Size (1) 43 + * 0x0a, 0x83, 0x01, // Usage (AL Consumer Control Config) 45 + * 0x0a, 0x8a, 0x01, // Usage (AL Email Reader) 48 + * 0x0a, 0x92, 0x01, // Usage (AL Calculator) 51 + * 0x0a, 0x94, 0x01, // Usage (AL Local Machine Browser) 54 + * 0x09, 0xcd, // Usage (Play/Pause) 57 + * 0x09, 0xb7, // Usage (Stop) 59 + * 0x09, 0xb6, // Usage (Scan Previous Track) 61 + * 0x09, 0xb5, // Usage (Scan Next Track) 63 + * 0x09, 0xe2, // Usage (Mute) 65 + * 0x09, 0xea, // Usage (Volume Down) 67 + * 0x09, 0xe9, // Usage (Volume Up) 69 + * 0x0a, 0x21, 0x02, // Usage (AC Search) 71 + * 0x0a, 0x23, 0x02, // Usage (AC Home) 74 + * 0x0a, 0x24, 0x02, // Usage (AC Back) 77 + * 0x0a, 0x25, 0x02, // Usage (AC Forward) 80 + * 0x0a, 0x26, 0x02, // Usage (AC Stop) 83 + * 0x0a, 0x27, 0x02, // Usage (AC Refresh) 86 + * 0x0a, 0x2a, 0x02, // Usage (AC Bookmarks) 89 + * 0x81, 0x02, // Input (Data,Var,Abs) 92 + * 0x95, 0x01, // Report Count (1) 94 + * 0x75, 0x0e, // Report Size (14) 96 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) 98 + * 0xc0, // End Collection 100 + * 0x05, 0x01, // Usage Page (Generic Desktop) 101 + * 0x09, 0x02, // Usage (Mouse) 103 + * 0xa1, 0x01, // Collection (Application) 105 + * 0x09, 0x01, // Usage (Pointer) 107 + * 0xa1, 0x00, // Collection (Physical) 109 + * 0x85, 0x03, // Report ID (3) 111 + * 0x05, 0x09, // Usage Page (Button) 113 + * 0x19, 0x01, // Usage Minimum (1) 115 + * 0x29, 0x08, // Usage Maximum (8) 117 + * 0x15, 0x00, // Logical Minimum (0) 119 + * 0x25, 0x01, // Logical Maximum (1) 121 + * 0x75, 0x01, // Report Size (1) 123 + * 0x95, 0x08, // Report Count (8) 125 + * 0x81, 0x02, // Input (Data,Var,Abs) 127 + * 0x05, 0x01, // Usage Page (Generic Desktop) 129 + * 0x09, 0x30, // Usage (X) 131 + * 0x09, 0x31, // Usage (Y) 133 + * 0x16, 0x01, 0x80, // Logical Minimum (-32767) 135 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 138 + * 0x75, 0x10, // Report Size (16) 141 + * 0x95, 0x02, // Report Count (2) 143 + * 0x81, 0x06, // Input (Data,Var,Rel) 145 + * 0x09, 0x38, // Usage (Wheel) 147 + * 0x15, 0x81, // Logical Minimum (-127) 149 + * 0x25, 0x7f, // Logical Maximum (127) 151 + * 0x75, 0x08, // Report Size (8) 153 + * 0x95, 0x01, // Report Count (1) 155 + * 0x81, 0x06, // Input (Data,Var,Rel) 157 + * 0x05, 0x0c, // Usage Page (Consumer Devices) 159 + * 0x0a, 0x38, 0x02, // Usage (AC Pan) 161 + * 0x95, 0x01, // Report Count (1) 164 + * 0x81, 0x06, // Input (Data,Var,Rel) 166 + * 0xc0, // End Collection 168 + * 0xc0, // End Collection 169 + * 0x05, 0x01, // Usage Page (Generic Desktop) 170 + * 0x09, 0x06, // Usage (Keyboard) 172 + * 0xa1, 0x01, // Collection (Application) 174 + * 0x85, 0x04, // Report ID (4) 176 + * 0x05, 0x07, // Usage Page (Keyboard) 178 + * 0x95, 0x01, // Report Count (1) 180 + * 0x75, 0x08, // Report Size (8) 182 + * 0x81, 0x03, // Input (Cnst,Var,Abs) 184 + * 0x95, 0xe8, // Report Count (232) 186 + * 0x75, 0x01, // Report Size (1) 188 + * 0x15, 0x00, // Logical Minimum (0) 190 + * 0x25, 0x01, // Logical Maximum (1) 192 + * 0x05, 0x07, // Usage Page (Keyboard) 194 + * 0x19, 0x00, // Usage Minimum (0) 196 + * 0x29, 0xe7, // Usage Maximum (231) 198 + * 0x81, 0x00, // Input (Data,Arr,Abs) 200 <- change to 0x81, 0x02 (Data,Var,Abs) + * 0xc0, // End Collection 202 + */ + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_rdesc_fixup_mistel_md770, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0, HID_MAX_DESCRIPTOR_SIZE); + + if (!data) + return 0; /* EPERM check */ + + if (data[201] == 0x00) + data[201] = 0x02; + + return 0; +} + +HID_BPF_OPS(mistel_md770) = { + .hid_rdesc_fixup = (void *)hid_rdesc_fixup_mistel_md770, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + ctx->retval = ctx->rdesc_size != RDESC_SIZE; + if (ctx->retval) + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; From cee9faff2f65d00fe8a56c1223c9c52435eb525d Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 17 Oct 2024 18:34:59 +0200 Subject: [PATCH 11/12] HID: bpf: Fix Rapoo M50 Plus Silent side buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Rapoo M50 Plus Silent mouse has 2 side buttons in addition to the left, right and middles buttons. However, its original HID descriptor has a Usage Maximum of 3, preventing the side buttons to work. This HID-BPF driver changes that usage to 5. Link: https://gitlab.freedesktop.org/libinput/libinput/-/issues/1015 Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests/116 Signed-off-by: José Expósito Acked-by: Jiri Kosina Link: https://patch.msgid.link/20241017-import_bpf_6-13-v2-2-6a7acb89a97f@kernel.org Signed-off-by: Benjamin Tissoires --- .../bpf/progs/Rapoo__M50-Plus-Silent.bpf.c | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 drivers/hid/bpf/progs/Rapoo__M50-Plus-Silent.bpf.c diff --git a/drivers/hid/bpf/progs/Rapoo__M50-Plus-Silent.bpf.c b/drivers/hid/bpf/progs/Rapoo__M50-Plus-Silent.bpf.c new file mode 100644 index 000000000000..6b379e45f531 --- /dev/null +++ b/drivers/hid/bpf/progs/Rapoo__M50-Plus-Silent.bpf.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2024 José Expósito + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include + +#define VID_RAPOO 0x24AE +#define PID_M50 0x2015 +#define RDESC_SIZE 186 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_RAPOO, PID_M50) +); + +/* + * The Rapoo M50 Plus Silent mouse has 2 side buttons in addition to the left, + * right and middle buttons. However, its original HID descriptor has a Usage + * Maximum of 3, preventing the side buttons to work. This HID-BPF driver + * changes that usage to 5. + * + * For reference, this is the original report descriptor: + * + * 0x05, 0x01, // Usage Page (Generic Desktop) 0 + * 0x09, 0x02, // Usage (Mouse) 2 + * 0xa1, 0x01, // Collection (Application) 4 + * 0x85, 0x01, // Report ID (1) 6 + * 0x09, 0x01, // Usage (Pointer) 8 + * 0xa1, 0x00, // Collection (Physical) 10 + * 0x05, 0x09, // Usage Page (Button) 12 + * 0x19, 0x01, // Usage Minimum (1) 14 + * 0x29, 0x03, // Usage Maximum (3) 16 <- change to 0x05 + * 0x15, 0x00, // Logical Minimum (0) 18 + * 0x25, 0x01, // Logical Maximum (1) 20 + * 0x75, 0x01, // Report Size (1) 22 + * 0x95, 0x05, // Report Count (5) 24 + * 0x81, 0x02, // Input (Data,Var,Abs) 26 + * 0x75, 0x03, // Report Size (3) 28 + * 0x95, 0x01, // Report Count (1) 30 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) 32 + * 0x05, 0x01, // Usage Page (Generic Desktop) 34 + * 0x09, 0x30, // Usage (X) 36 + * 0x09, 0x31, // Usage (Y) 38 + * 0x16, 0x01, 0x80, // Logical Minimum (-32767) 40 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 43 + * 0x75, 0x10, // Report Size (16) 46 + * 0x95, 0x02, // Report Count (2) 48 + * 0x81, 0x06, // Input (Data,Var,Rel) 50 + * 0x09, 0x38, // Usage (Wheel) 52 + * 0x15, 0x81, // Logical Minimum (-127) 54 + * 0x25, 0x7f, // Logical Maximum (127) 56 + * 0x75, 0x08, // Report Size (8) 58 + * 0x95, 0x01, // Report Count (1) 60 + * 0x81, 0x06, // Input (Data,Var,Rel) 62 + * 0xc0, // End Collection 64 + * 0xc0, // End Collection 65 + * 0x05, 0x0c, // Usage Page (Consumer Devices) 66 + * 0x09, 0x01, // Usage (Consumer Control) 68 + * 0xa1, 0x01, // Collection (Application) 70 + * 0x85, 0x02, // Report ID (2) 72 + * 0x75, 0x10, // Report Size (16) 74 + * 0x95, 0x01, // Report Count (1) 76 + * 0x15, 0x01, // Logical Minimum (1) 78 + * 0x26, 0x8c, 0x02, // Logical Maximum (652) 80 + * 0x19, 0x01, // Usage Minimum (1) 83 + * 0x2a, 0x8c, 0x02, // Usage Maximum (652) 85 + * 0x81, 0x00, // Input (Data,Arr,Abs) 88 + * 0xc0, // End Collection 90 + * 0x05, 0x01, // Usage Page (Generic Desktop) 91 + * 0x09, 0x80, // Usage (System Control) 93 + * 0xa1, 0x01, // Collection (Application) 95 + * 0x85, 0x03, // Report ID (3) 97 + * 0x09, 0x82, // Usage (System Sleep) 99 + * 0x09, 0x81, // Usage (System Power Down) 101 + * 0x09, 0x83, // Usage (System Wake Up) 103 + * 0x15, 0x00, // Logical Minimum (0) 105 + * 0x25, 0x01, // Logical Maximum (1) 107 + * 0x19, 0x01, // Usage Minimum (1) 109 + * 0x29, 0x03, // Usage Maximum (3) 111 + * 0x75, 0x01, // Report Size (1) 113 + * 0x95, 0x03, // Report Count (3) 115 + * 0x81, 0x02, // Input (Data,Var,Abs) 117 + * 0x95, 0x05, // Report Count (5) 119 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) 121 + * 0xc0, // End Collection 123 + * 0x05, 0x01, // Usage Page (Generic Desktop) 124 + * 0x09, 0x00, // Usage (Undefined) 126 + * 0xa1, 0x01, // Collection (Application) 128 + * 0x85, 0x05, // Report ID (5) 130 + * 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 132 + * 0x09, 0x01, // Usage (Vendor Usage 1) 135 + * 0x15, 0x81, // Logical Minimum (-127) 137 + * 0x25, 0x7f, // Logical Maximum (127) 139 + * 0x75, 0x08, // Report Size (8) 141 + * 0x95, 0x07, // Report Count (7) 143 + * 0xb1, 0x02, // Feature (Data,Var,Abs) 145 + * 0xc0, // End Collection 147 + * 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 148 + * 0x09, 0x0e, // Usage (Vendor Usage 0x0e) 151 + * 0xa1, 0x01, // Collection (Application) 153 + * 0x85, 0xba, // Report ID (186) 155 + * 0x95, 0x1f, // Report Count (31) 157 + * 0x75, 0x08, // Report Size (8) 159 + * 0x26, 0xff, 0x00, // Logical Maximum (255) 161 + * 0x15, 0x00, // Logical Minimum (0) 164 + * 0x09, 0x01, // Usage (Vendor Usage 1) 166 + * 0x91, 0x02, // Output (Data,Var,Abs) 168 + * 0x85, 0xba, // Report ID (186) 170 + * 0x95, 0x1f, // Report Count (31) 172 + * 0x75, 0x08, // Report Size (8) 174 + * 0x26, 0xff, 0x00, // Logical Maximum (255) 176 + * 0x15, 0x00, // Logical Minimum (0) 179 + * 0x09, 0x01, // Usage (Vendor Usage 1) 181 + * 0x81, 0x02, // Input (Data,Var,Abs) 183 + * 0xc0, // End Collection 185 + */ + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_rdesc_fixup_rapoo_m50, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0, HID_MAX_DESCRIPTOR_SIZE); + + if (!data) + return 0; /* EPERM check */ + + if (data[17] == 0x03) + data[17] = 0x05; + + return 0; +} + +HID_BPF_OPS(rapoo_m50) = { + .hid_rdesc_fixup = (void *)hid_rdesc_fixup_rapoo_m50, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + ctx->retval = ctx->rdesc_size != RDESC_SIZE; + if (ctx->retval) + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; From b6d8c474e26517f944208e4645a2a09a6eeee88e Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 17 Oct 2024 18:35:00 +0200 Subject: [PATCH 12/12] HID: bpf: drop use of Logical|Physical|UsageRange Replace with individual Minimum/Maximum calls to match the HID report descriptor - HID doesn't have a Range field. Abstracting this is good for hand-written descriptors but almost all tools will output min/max instead so let's stick with that. Signed-off-by: Peter Hutterer Acked-by: Jiri Kosina Link: https://patch.msgid.link/20241017-import_bpf_6-13-v2-3-6a7acb89a97f@kernel.org Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/progs/Huion__Dial-2.bpf.c | 66 ++++++++++++------- .../hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c | 60 +++++++++++------ drivers/hid/bpf/progs/hid_report_helpers.h | 36 +++++++--- 3 files changed, 111 insertions(+), 51 deletions(-) diff --git a/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c b/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c index 2411dec6db08..9670e5ef8d54 100644 --- a/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c +++ b/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c @@ -214,7 +214,8 @@ static const __u8 fixed_rdesc_pad[] = { CollectionApplication( // -- Byte 0 in report ReportId(PAD_REPORT_ID) - LogicalRange_i8(0, 1) + LogicalMaximum_i8(0) + LogicalMaximum_i8(1) UsagePage_Digitizers Usage_Dig_TabletFunctionKeys CollectionPhysical( @@ -234,14 +235,17 @@ static const __u8 fixed_rdesc_pad[] = { Input(Var|Abs) // Byte 4 in report is the dial Usage_GD_Wheel - LogicalRange_i8(-1, 1) + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) ReportCount(1) ReportSize(8) Input(Var|Rel) // Byte 5 is the button state UsagePage_Button - UsageRange_i8(0x01, 0x8) - LogicalRange_i8(0x0, 0x1) + UsageMinimum_i8(0x01) + UsageMaximum_i8(0x08) + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) ReportCount(7) ReportSize(1) Input(Var|Abs) @@ -265,7 +269,8 @@ static const __u8 fixed_rdesc_pen[] = { Usage_Dig_TipSwitch Usage_Dig_BarrelSwitch Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2 - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) ReportSize(1) ReportCount(3) Input(Var|Abs) @@ -280,22 +285,28 @@ static const __u8 fixed_rdesc_pen[] = { UsagePage_GenericDesktop Unit(cm) UnitExponent(-1) - PhysicalRange_i16(0, 266) - LogicalRange_i16(0, 32767) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(266) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32767) Usage_GD_X Input(Var|Abs) // Bytes 2+3 - PhysicalRange_i16(0, 166) - LogicalRange_i16(0, 32767) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(166) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32767) Usage_GD_Y Input(Var|Abs) // Bytes 4+5 ) UsagePage_Digitizers Usage_Dig_TipPressure - LogicalRange_i16(0, 8191) + LogicalMinimum_i16(0) + LogicalMaximum_i16(8191) Input(Var|Abs) // Byte 6+7 ReportSize(8) ReportCount(2) - LogicalRange_i8(-60, 60) + LogicalMinimum_i8(-60) + LogicalMaximum_i8(60) Usage_Dig_XTilt Usage_Dig_YTilt Input(Var|Abs) // Byte 8+9 @@ -313,7 +324,8 @@ static const __u8 fixed_rdesc_vendor[] = { Usage_Dig_Pen CollectionPhysical( // Byte 1 are the buttons - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) ReportSize(1) Usage_Dig_TipSwitch Usage_Dig_BarrelSwitch @@ -333,25 +345,31 @@ static const __u8 fixed_rdesc_vendor[] = { UnitExponent(-1) // Note: reported logical range differs // from the pen report ID for x and y - LogicalRange_i16(0, 53340) - PhysicalRange_i16(0, 266) + LogicalMinimum_i16(0) + LogicalMaximum_i16(53340) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(266) // Bytes 2/3 in report Usage_GD_X Input(Var|Abs) - LogicalRange_i16(0, 33340) - PhysicalRange_i16(0, 166) + LogicalMinimum_i16(0) + LogicalMaximum_i16(33340) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(166) // Bytes 4/5 in report Usage_GD_Y Input(Var|Abs) ) // Bytes 6/7 in report - LogicalRange_i16(0, 8191) + LogicalMinimum_i16(0) + LogicalMaximum_i16(8191) Usage_Dig_TipPressure Input(Var|Abs) // Bytes 8/9 in report ReportCount(1) // Padding Input(Const) - LogicalRange_i8(-60, 60) + LogicalMinimum_i8(-60) + LogicalMaximum_i8(60) // Byte 10 in report Usage_Dig_XTilt // Byte 11 in report @@ -366,7 +384,8 @@ static const __u8 fixed_rdesc_vendor[] = { CollectionApplication( // Byte 0 ReportId(PAD_REPORT_ID) - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) UsagePage_Digitizers Usage_Dig_TabletFunctionKeys CollectionPhysical( @@ -386,15 +405,18 @@ static const __u8 fixed_rdesc_vendor[] = { Input(Var|Abs) // Byte 4 is the button state UsagePage_Button - UsageRange_i8(0x01, 0x8) - LogicalRange_i8(0x0, 0x1) + UsageMinimum_i8(0x1) + UsageMaximum_i8(0x8) + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) ReportCount(8) ReportSize(1) Input(Var|Abs) // Byte 5 is the top dial UsagePage_GenericDesktop Usage_GD_Wheel - LogicalRange_i8(-1, 1) + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) ReportCount(1) ReportSize(8) Input(Var|Rel) diff --git a/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c b/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c index b09b80132368..13f64fb49800 100644 --- a/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c +++ b/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c @@ -170,7 +170,8 @@ static const __u8 fixed_rdesc_pad[] = { CollectionApplication( // -- Byte 0 in report ReportId(PAD_REPORT_ID) - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) UsagePage_Digitizers Usage_Dig_TabletFunctionKeys CollectionPhysical( @@ -190,14 +191,17 @@ static const __u8 fixed_rdesc_pad[] = { Input(Var|Abs) // Byte 4 in report is the wheel Usage_GD_Wheel - LogicalRange_i8(-1, 1) + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) ReportCount(1) ReportSize(8) Input(Var|Rel) // Byte 5 is the button state UsagePage_Button - UsageRange_i8(0x01, 0x6) - LogicalRange_i8(0x01, 0x6) + UsageMinimum_i8(0x1) + UsageMaximum_i8(0x6) + LogicalMinimum_i8(0x1) + LogicalMaximum_i8(0x6) ReportCount(1) ReportSize(8) Input(Arr|Abs) @@ -219,7 +223,8 @@ static const __u8 fixed_rdesc_pen[] = { Usage_Dig_TipSwitch Usage_Dig_BarrelSwitch Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2 - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) ReportSize(1) ReportCount(3) Input(Var|Abs) @@ -234,18 +239,23 @@ static const __u8 fixed_rdesc_pen[] = { UsagePage_GenericDesktop Unit(cm) UnitExponent(-1) - PhysicalRange_i16(0, 160) - LogicalRange_i16(0, 32767) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(160) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32767) Usage_GD_X Input(Var|Abs) // Bytes 2+3 - PhysicalRange_i16(0, 100) - LogicalRange_i16(0, 32767) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(100) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32767) Usage_GD_Y Input(Var|Abs) // Bytes 4+5 ) UsagePage_Digitizers Usage_Dig_TipPressure - LogicalRange_i16(0, 8191) + LogicalMinimum_i16(0) + LogicalMaximum_i16(8191) Input(Var|Abs) // Byte 6+7 // Two bytes padding so we don't need to change the report at all ReportSize(8) @@ -265,7 +275,8 @@ static const __u8 fixed_rdesc_vendor[] = { Usage_Dig_Pen CollectionPhysical( // Byte 1 are the buttons - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) ReportSize(1) Usage_Dig_TipSwitch Usage_Dig_BarrelSwitch @@ -285,19 +296,24 @@ static const __u8 fixed_rdesc_vendor[] = { UnitExponent(-1) // Note: reported logical range differs // from the pen report ID for x and y - LogicalRange_i16(0, 32000) - PhysicalRange_i16(0, 160) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32000) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(160) // Bytes 2/3 in report Usage_GD_X Input(Var|Abs) - LogicalRange_i16(0, 20000) - PhysicalRange_i16(0, 100) + LogicalMinimum_i16(0) + LogicalMaximum_i16(20000) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(100) // Bytes 4/5 in report Usage_GD_Y Input(Var|Abs) ) // Bytes 6/7 in report - LogicalRange_i16(0, 8192) + LogicalMinimum_i16(0) + LogicalMaximum_i16(8192) Usage_Dig_TipPressure Input(Var|Abs) ) @@ -307,7 +323,8 @@ static const __u8 fixed_rdesc_vendor[] = { CollectionApplication( // Byte 0 ReportId(PAD_REPORT_ID) - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) UsagePage_Digitizers Usage_Dig_TabletFunctionKeys CollectionPhysical( @@ -327,8 +344,10 @@ static const __u8 fixed_rdesc_vendor[] = { Input(Var|Abs) // Byte 4 is the button state UsagePage_Button - UsageRange_i8(0x01, 0x6) - LogicalRange_i8(0x0, 0x1) + UsageMinimum_i8(0x1) + UsageMaximum_i8(0x6) + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) ReportCount(6) ReportSize(1) Input(Var|Abs) @@ -337,7 +356,8 @@ static const __u8 fixed_rdesc_vendor[] = { // Byte 5 is the wheel UsagePage_GenericDesktop Usage_GD_Wheel - LogicalRange_i8(-1, 1) + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) ReportCount(1) ReportSize(8) Input(Var|Rel) diff --git a/drivers/hid/bpf/progs/hid_report_helpers.h b/drivers/hid/bpf/progs/hid_report_helpers.h index 0aa1df438eeb..9b2a48e4a311 100644 --- a/drivers/hid/bpf/progs/hid_report_helpers.h +++ b/drivers/hid/bpf/progs/hid_report_helpers.h @@ -52,7 +52,8 @@ * Usage_GD_Keyboard * CollectionApplication( ← Open the collection * ReportId(3) - * LogicalRange_i8(0, 1) + * LogicalMinimum_i8(0) + * LogicalMaximum_i8(1) * // other fields * ) ← End EndCollection * @@ -74,26 +75,43 @@ #define Arr 0x0 #define Abs 0x0 #define Rel 0x4 +#define Null 0x40 +#define Buff 0x0100 /* Use like this: Input(Var|Abs) */ #define Input(i_) 0x081, i8(i_), #define Output(i_) 0x091, i8(i_), #define Feature(i_) 0x0b1, i8(i_), +#define Input_i16(i_) 0x082, LE16(i_), +#define Output_i16(i_) 0x092, LE16(i_), +#define Feature_i16(i_) 0x0b2, LE16(i_), + #define ReportId(id_) 0x85, i8(id_), #define ReportSize(sz_) 0x75, i8(sz_), #define ReportCount(cnt_) 0x95, i8(cnt_), -#define LogicalRange_i8(min_, max_) 0x15, i8(min_), 0x25, i8(max_), -#define LogicalRange_i16(min_, max_) 0x16, LE16(min_), 0x26, LE16(max_), -#define LogicalRange_i32(min_, max_) 0x17, LE32(min_), 0x27, LE32(max_), +#define LogicalMinimum_i8(min_) 0x15, i8(min_), +#define LogicalMinimum_i16(min_) 0x16, LE16(min_), +#define LogicalMinimum_i32(min_) 0x17, LE32(min_), -#define PhysicalRange_i8(min_, max_) 0x35, i8(min_), 0x45, i8(max_), -#define PhysicalRange_i16(min_, max_) 0x36, LE16(min_), 0x46, LE16(max_), -#define PhysicalRange_i32(min_, max_) 0x37, LE32(min_), 0x47, LE32(max_), +#define LogicalMaximum_i8(max_) 0x25, i8(max_), +#define LogicalMaximum_i16(max_) 0x26, LE16(max_), +#define LogicalMaximum_i32(max_) 0x27, LE32(max_), -#define UsageRange_i8(min_, max_) 0x19, i8(min_), 0x29, i8(max_), -#define UsageRange_i16(min_, max_) 0x1a, LE16(min_), 0x2a, LE16(max_), +#define PhysicalMinimum_i8(min_) 0x35, i8(min_), +#define PhysicalMinimum_i16(min_) 0x36, LE16(min_), +#define PhysicalMinimum_i32(min_) 0x37, LE32(min_), + +#define PhysicalMaximum_i8(max_) 0x45, i8(max_), +#define PhysicalMaximum_i16(max_) 0x46, LE16(max_), +#define PhysicalMaximum_i32(max_) 0x47, LE32(max_), + +#define UsageMinimum_i8(min_) 0x19, i8(min_), +#define UsageMinimum_i16(min_) 0x1a, LE16(min_), + +#define UsageMaximum_i8(max_) 0x29, i8(max_), +#define UsageMaximum_i16(max_) 0x2a, LE16(max_), #define UsagePage_i8(p_) 0x05, i8(p_), #define UsagePage_i16(p_) 0x06, LE16(p_),