Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <ishitatsuyuki@gmail.com>
Acked-by: Jiri Kosina <jkosina@suse.com>
Link: https://patch.msgid.link/20241017-import_bpf_6-13-v2-1-6a7acb89a97f@kernel.org
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>

+154
+154
drivers/hid/bpf/progs/Mistel__MD770.bpf.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Copyright (c) 2024 Tatsuyuki Ishi 3 + */ 4 + 5 + #include "vmlinux.h" 6 + #include "hid_bpf.h" 7 + #include "hid_bpf_helpers.h" 8 + #include <bpf/bpf_tracing.h> 9 + 10 + #define VID_HOLTEK 0x04D9 11 + #define PID_MD770 0x0339 12 + #define RDESC_SIZE 203 13 + 14 + HID_BPF_CONFIG( 15 + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HOLTEK, PID_MD770) 16 + ); 17 + 18 + /* 19 + * The Mistel MD770 keyboard reports the first 6 simultaneous key presses 20 + * through the first interface, and anything beyond that through a second 21 + * interface. Unfortunately, the second interface's report descriptor has an 22 + * error, causing events to be malformed and ignored. This HID-BPF driver 23 + * fixes the descriptor to allow NKRO to work again. 24 + * 25 + * For reference, this is the original report descriptor: 26 + * 27 + * 0x05, 0x01, // Usage Page (Generic Desktop) 0 28 + * 0x09, 0x80, // Usage (System Control) 2 29 + * 0xa1, 0x01, // Collection (Application) 4 30 + * 0x85, 0x01, // Report ID (1) 6 31 + * 0x19, 0x81, // Usage Minimum (129) 8 32 + * 0x29, 0x83, // Usage Maximum (131) 10 33 + * 0x15, 0x00, // Logical Minimum (0) 12 34 + * 0x25, 0x01, // Logical Maximum (1) 14 35 + * 0x95, 0x03, // Report Count (3) 16 36 + * 0x75, 0x01, // Report Size (1) 18 37 + * 0x81, 0x02, // Input (Data,Var,Abs) 20 38 + * 0x95, 0x01, // Report Count (1) 22 39 + * 0x75, 0x05, // Report Size (5) 24 40 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) 26 41 + * 0xc0, // End Collection 28 42 + * 0x05, 0x0c, // Usage Page (Consumer Devices) 29 43 + * 0x09, 0x01, // Usage (Consumer Control) 31 44 + * 0xa1, 0x01, // Collection (Application) 33 45 + * 0x85, 0x02, // Report ID (2) 35 46 + * 0x15, 0x00, // Logical Minimum (0) 37 47 + * 0x25, 0x01, // Logical Maximum (1) 39 48 + * 0x95, 0x12, // Report Count (18) 41 49 + * 0x75, 0x01, // Report Size (1) 43 50 + * 0x0a, 0x83, 0x01, // Usage (AL Consumer Control Config) 45 51 + * 0x0a, 0x8a, 0x01, // Usage (AL Email Reader) 48 52 + * 0x0a, 0x92, 0x01, // Usage (AL Calculator) 51 53 + * 0x0a, 0x94, 0x01, // Usage (AL Local Machine Browser) 54 54 + * 0x09, 0xcd, // Usage (Play/Pause) 57 55 + * 0x09, 0xb7, // Usage (Stop) 59 56 + * 0x09, 0xb6, // Usage (Scan Previous Track) 61 57 + * 0x09, 0xb5, // Usage (Scan Next Track) 63 58 + * 0x09, 0xe2, // Usage (Mute) 65 59 + * 0x09, 0xea, // Usage (Volume Down) 67 60 + * 0x09, 0xe9, // Usage (Volume Up) 69 61 + * 0x0a, 0x21, 0x02, // Usage (AC Search) 71 62 + * 0x0a, 0x23, 0x02, // Usage (AC Home) 74 63 + * 0x0a, 0x24, 0x02, // Usage (AC Back) 77 64 + * 0x0a, 0x25, 0x02, // Usage (AC Forward) 80 65 + * 0x0a, 0x26, 0x02, // Usage (AC Stop) 83 66 + * 0x0a, 0x27, 0x02, // Usage (AC Refresh) 86 67 + * 0x0a, 0x2a, 0x02, // Usage (AC Bookmarks) 89 68 + * 0x81, 0x02, // Input (Data,Var,Abs) 92 69 + * 0x95, 0x01, // Report Count (1) 94 70 + * 0x75, 0x0e, // Report Size (14) 96 71 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) 98 72 + * 0xc0, // End Collection 100 73 + * 0x05, 0x01, // Usage Page (Generic Desktop) 101 74 + * 0x09, 0x02, // Usage (Mouse) 103 75 + * 0xa1, 0x01, // Collection (Application) 105 76 + * 0x09, 0x01, // Usage (Pointer) 107 77 + * 0xa1, 0x00, // Collection (Physical) 109 78 + * 0x85, 0x03, // Report ID (3) 111 79 + * 0x05, 0x09, // Usage Page (Button) 113 80 + * 0x19, 0x01, // Usage Minimum (1) 115 81 + * 0x29, 0x08, // Usage Maximum (8) 117 82 + * 0x15, 0x00, // Logical Minimum (0) 119 83 + * 0x25, 0x01, // Logical Maximum (1) 121 84 + * 0x75, 0x01, // Report Size (1) 123 85 + * 0x95, 0x08, // Report Count (8) 125 86 + * 0x81, 0x02, // Input (Data,Var,Abs) 127 87 + * 0x05, 0x01, // Usage Page (Generic Desktop) 129 88 + * 0x09, 0x30, // Usage (X) 131 89 + * 0x09, 0x31, // Usage (Y) 133 90 + * 0x16, 0x01, 0x80, // Logical Minimum (-32767) 135 91 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 138 92 + * 0x75, 0x10, // Report Size (16) 141 93 + * 0x95, 0x02, // Report Count (2) 143 94 + * 0x81, 0x06, // Input (Data,Var,Rel) 145 95 + * 0x09, 0x38, // Usage (Wheel) 147 96 + * 0x15, 0x81, // Logical Minimum (-127) 149 97 + * 0x25, 0x7f, // Logical Maximum (127) 151 98 + * 0x75, 0x08, // Report Size (8) 153 99 + * 0x95, 0x01, // Report Count (1) 155 100 + * 0x81, 0x06, // Input (Data,Var,Rel) 157 101 + * 0x05, 0x0c, // Usage Page (Consumer Devices) 159 102 + * 0x0a, 0x38, 0x02, // Usage (AC Pan) 161 103 + * 0x95, 0x01, // Report Count (1) 164 104 + * 0x81, 0x06, // Input (Data,Var,Rel) 166 105 + * 0xc0, // End Collection 168 106 + * 0xc0, // End Collection 169 107 + * 0x05, 0x01, // Usage Page (Generic Desktop) 170 108 + * 0x09, 0x06, // Usage (Keyboard) 172 109 + * 0xa1, 0x01, // Collection (Application) 174 110 + * 0x85, 0x04, // Report ID (4) 176 111 + * 0x05, 0x07, // Usage Page (Keyboard) 178 112 + * 0x95, 0x01, // Report Count (1) 180 113 + * 0x75, 0x08, // Report Size (8) 182 114 + * 0x81, 0x03, // Input (Cnst,Var,Abs) 184 115 + * 0x95, 0xe8, // Report Count (232) 186 116 + * 0x75, 0x01, // Report Size (1) 188 117 + * 0x15, 0x00, // Logical Minimum (0) 190 118 + * 0x25, 0x01, // Logical Maximum (1) 192 119 + * 0x05, 0x07, // Usage Page (Keyboard) 194 120 + * 0x19, 0x00, // Usage Minimum (0) 196 121 + * 0x29, 0xe7, // Usage Maximum (231) 198 122 + * 0x81, 0x00, // Input (Data,Arr,Abs) 200 <- change to 0x81, 0x02 (Data,Var,Abs) 123 + * 0xc0, // End Collection 202 124 + */ 125 + 126 + SEC(HID_BPF_RDESC_FIXUP) 127 + int BPF_PROG(hid_rdesc_fixup_mistel_md770, struct hid_bpf_ctx *hctx) 128 + { 129 + __u8 *data = hid_bpf_get_data(hctx, 0, HID_MAX_DESCRIPTOR_SIZE); 130 + 131 + if (!data) 132 + return 0; /* EPERM check */ 133 + 134 + if (data[201] == 0x00) 135 + data[201] = 0x02; 136 + 137 + return 0; 138 + } 139 + 140 + HID_BPF_OPS(mistel_md770) = { 141 + .hid_rdesc_fixup = (void *)hid_rdesc_fixup_mistel_md770, 142 + }; 143 + 144 + SEC("syscall") 145 + int probe(struct hid_bpf_probe_args *ctx) 146 + { 147 + ctx->retval = ctx->rdesc_size != RDESC_SIZE; 148 + if (ctx->retval) 149 + ctx->retval = -EINVAL; 150 + 151 + return 0; 152 + } 153 + 154 + char _license[] SEC("license") = "GPL";