summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hid/bpf/hid_bpf_dispatch.c9
-rw-r--r--drivers/hid/bpf/hid_bpf_struct_ops.c1
-rw-r--r--drivers/hid/bpf/progs/Huion__Dial-2.bpf.c66
-rw-r--r--drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c60
-rw-r--r--drivers/hid/bpf/progs/Mistel__MD770.bpf.c154
-rw-r--r--drivers/hid/bpf/progs/Rapoo__M50-Plus-Silent.bpf.c148
-rw-r--r--drivers/hid/bpf/progs/hid_report_helpers.h36
-rw-r--r--drivers/hid/hid-core.c84
-rw-r--r--drivers/hid/hid-generic.c3
9 files changed, 488 insertions, 73 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/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/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/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 <bpf/bpf_tracing.h>
+
+#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";
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 <bpf/bpf_tracing.h>
+
+#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";
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 LogicalMaximum_i8(max_) 0x25, i8(max_),
+#define LogicalMaximum_i16(max_) 0x26, LE16(max_),
+#define LogicalMaximum_i32(max_) 0x27, LE32(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 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 UsageMinimum_i8(min_) 0x19, i8(min_),
+#define UsageMinimum_i16(min_) 0x1a, LE16(min_),
-#define UsageRange_i8(min_, max_) 0x19, i8(min_), 0x29, i8(max_),
-#define UsageRange_i16(min_, max_) 0x1a, LE16(min_), 0x2a, LE16(max_),
+#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_),
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 910cf0728d0d..81d6c734c8bc 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -713,7 +713,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;
@@ -726,6 +733,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.
*/
@@ -735,6 +750,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);
}
@@ -1227,7 +1243,6 @@ int hid_open_report(struct hid_device *device)
struct hid_item item;
unsigned int size;
const __u8 *start;
- __u8 *buf;
const __u8 *end;
const __u8 *next;
int ret;
@@ -1243,25 +1258,34 @@ 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);
- 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.
+ */
+ __u8 *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);
- 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;
@@ -2656,9 +2680,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)
@@ -2666,6 +2691,27 @@ 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) {
+ 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);
+
+ /* 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 (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))
return -ENODEV;
@@ -2673,8 +2719,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) {
@@ -2922,9 +2966,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/drivers/hid/hid-generic.c b/drivers/hid/hid-generic.c
index d2439399fb35..9e04c6d0fcc8 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;