linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Benjamin Tissoires <bentiss@kernel.org>
To: Jiri Kosina <jikos@kernel.org>,
	Benjamin Tissoires <bentiss@kernel.org>,
	 Shuah Khan <shuah@kernel.org>,
	Peter Hutterer <peter.hutterer@who-t.net>
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	 linux-kselftest@vger.kernel.org
Subject: [PATCH 08/18] HID: bpf: add in-tree HID-BPF fix for the Huion Kamvas Pro 19
Date: Wed, 10 Apr 2024 19:19:28 +0200	[thread overview]
Message-ID: <20240410-bpf_sources-v1-8-a8bf16033ef8@kernel.org> (raw)
In-Reply-To: <20240410-bpf_sources-v1-0-a8bf16033ef8@kernel.org>

This tablets gets a lot of things wrong:
- the secondary button is reported through Secondary Tip Switch
- the third button is reported through Invert

Fortunately, before entering eraser mode, (so Invert = 1),
the tablet always sends an out-of-proximity event.
So we can detect that single event and:
- if there was none but the invert bit was toggled: this is the
  third button
- if there was this out-of-proximity event, we are entering
  eraser mode, and we will until the next out-of-proximity.

Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
 drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c | 290 +++++++++++++++++++++++
 1 file changed, 290 insertions(+)

diff --git a/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c b/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c
new file mode 100644
index 000000000000..ff759f2276f9
--- /dev/null
+++ b/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2024 Benjamin Tissoires
+ */
+
+#include "vmlinux.h"
+#include "hid_bpf.h"
+#include "hid_bpf_helpers.h"
+#include <bpf/bpf_tracing.h>
+
+#define VID_HUION 0x256C
+#define PID_KAMVAS_PRO_19 0x006B
+#define NAME_KAMVAS_PRO_19 "HUION Huion Tablet_GT1902"
+
+#define TEST_PREFIX "uhid test "
+
+HID_BPF_CONFIG(
+	HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, VID_HUION, PID_KAMVAS_PRO_19),
+);
+
+bool prev_was_out_of_range;
+bool in_eraser_mode;
+
+/*
+ * We need to amend the report descriptor for the following:
+ * - the second button is reported through Secondary Tip Switch instead of Secondary Barrel Switch
+ * - the third button is reported through Invert, and we need some room to report it.
+ *
+ */
+static const __u8 fixed_rdesc[] = {
+	0x05, 0x0d,                    // Usage Page (Digitizers)             0
+	0x09, 0x02,                    // Usage (Pen)                         2
+	0xa1, 0x01,                    // Collection (Application)            4
+	0x85, 0x0a,                    //  Report ID (10)                     6
+	0x09, 0x20,                    //  Usage (Stylus)                     8
+	0xa1, 0x01,                    //  Collection (Application)           10
+	0x09, 0x42,                    //   Usage (Tip Switch)                12
+	0x09, 0x44,                    //   Usage (Barrel Switch)             14
+	0x09, 0x5a,                    //   Usage (Secondary Barrel Switch)   16 /* changed from Secondary Tip Switch */
+	0x09, 0x3c,                    //   Usage (Invert)                    18
+	0x09, 0x45,                    //   Usage (Eraser)                    20
+	0x15, 0x00,                    //   Logical Minimum (0)               22
+	0x25, 0x01,                    //   Logical Maximum (1)               24
+	0x75, 0x01,                    //   Report Size (1)                   26
+	0x95, 0x05,                    //   Report Count (5)                  28 /* changed (was 5) */
+	0x81, 0x02,                    //   Input (Data,Var,Abs)              30
+	0x05, 0x09,                    //   Usage Page (Button)                  /* inserted */
+	0x09, 0x4a,                    //   Usage (0x4a)                         /* inserted to be translated as input usage 0x149: BTN_STYLUS3 */
+	0x95, 0x01,                    //   Report Count (1)                     /* inserted */
+	0x81, 0x02,                    //   Input (Data,Var,Abs)                 /* inserted */
+	0x05, 0x0d,                    //   Usage Page (Digitizers)              /* inserted */
+	0x09, 0x32,                    //   Usage (In Range)                  32
+	0x75, 0x01,                    //   Report Size (1)                   34
+	0x95, 0x01,                    //   Report Count (1)                  36
+	0x81, 0x02,                    //   Input (Data,Var,Abs)              38
+	0x81, 0x03,                    //   Input (Cnst,Var,Abs)              40
+	0x05, 0x01,                    //   Usage Page (Generic Desktop)      42
+	0x09, 0x30,                    //   Usage (X)                         44
+	0x09, 0x31,                    //   Usage (Y)                         46
+	0x55, 0x0d,                    //   Unit Exponent (-3)                48
+	0x65, 0x33,                    //   Unit (EnglishLinear: in³)         50
+	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           52
+	0x35, 0x00,                    //   Physical Minimum (0)              55
+	0x46, 0x00, 0x08,              //   Physical Maximum (2048)           57
+	0x75, 0x10,                    //   Report Size (16)                  60
+	0x95, 0x02,                    //   Report Count (2)                  62
+	0x81, 0x02,                    //   Input (Data,Var,Abs)              64
+	0x05, 0x0d,                    //   Usage Page (Digitizers)           66
+	0x09, 0x30,                    //   Usage (Tip Pressure)              68
+	0x26, 0xff, 0x3f,              //   Logical Maximum (16383)           70
+	0x75, 0x10,                    //   Report Size (16)                  73
+	0x95, 0x01,                    //   Report Count (1)                  75
+	0x81, 0x02,                    //   Input (Data,Var,Abs)              77
+	0x09, 0x3d,                    //   Usage (X Tilt)                    79
+	0x09, 0x3e,                    //   Usage (Y Tilt)                    81
+	0x15, 0xa6,                    //   Logical Minimum (-90)             83
+	0x25, 0x5a,                    //   Logical Maximum (90)              85
+	0x75, 0x08,                    //   Report Size (8)                   87
+	0x95, 0x02,                    //   Report Count (2)                  89
+	0x81, 0x02,                    //   Input (Data,Var,Abs)              91
+	0xc0,                          //  End Collection                     93
+	0xc0,                          // End Collection                      94
+	0x05, 0x0d,                    // Usage Page (Digitizers)             95
+	0x09, 0x04,                    // Usage (Touch Screen)                97
+	0xa1, 0x01,                    // Collection (Application)            99
+	0x85, 0x04,                    //  Report ID (4)                      101
+	0x09, 0x22,                    //  Usage (Finger)                     103
+	0xa1, 0x02,                    //  Collection (Logical)               105
+	0x05, 0x0d,                    //   Usage Page (Digitizers)           107
+	0x95, 0x01,                    //   Report Count (1)                  109
+	0x75, 0x06,                    //   Report Size (6)                   111
+	0x09, 0x51,                    //   Usage (Contact Id)                113
+	0x15, 0x00,                    //   Logical Minimum (0)               115
+	0x25, 0x3f,                    //   Logical Maximum (63)              117
+	0x81, 0x02,                    //   Input (Data,Var,Abs)              119
+	0x09, 0x42,                    //   Usage (Tip Switch)                121
+	0x25, 0x01,                    //   Logical Maximum (1)               123
+	0x75, 0x01,                    //   Report Size (1)                   125
+	0x95, 0x01,                    //   Report Count (1)                  127
+	0x81, 0x02,                    //   Input (Data,Var,Abs)              129
+	0x75, 0x01,                    //   Report Size (1)                   131
+	0x95, 0x01,                    //   Report Count (1)                  133
+	0x81, 0x03,                    //   Input (Cnst,Var,Abs)              135
+	0x05, 0x01,                    //   Usage Page (Generic Desktop)      137
+	0x75, 0x10,                    //   Report Size (16)                  139
+	0x55, 0x0e,                    //   Unit Exponent (-2)                141
+	0x65, 0x11,                    //   Unit (SILinear: cm)               143
+	0x09, 0x30,                    //   Usage (X)                         145
+	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           147
+	0x35, 0x00,                    //   Physical Minimum (0)              150
+	0x46, 0x15, 0x0c,              //   Physical Maximum (3093)           152
+	0x81, 0x42,                    //   Input (Data,Var,Abs,Null)         155
+	0x09, 0x31,                    //   Usage (Y)                         157
+	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           159
+	0x46, 0xcb, 0x06,              //   Physical Maximum (1739)           162
+	0x81, 0x42,                    //   Input (Data,Var,Abs,Null)         165
+	0x05, 0x0d,                    //   Usage Page (Digitizers)           167
+	0x09, 0x30,                    //   Usage (Tip Pressure)              169
+	0x26, 0xff, 0x1f,              //   Logical Maximum (8191)            171
+	0x75, 0x10,                    //   Report Size (16)                  174
+	0x95, 0x01,                    //   Report Count (1)                  176
+	0x81, 0x02,                    //   Input (Data,Var,Abs)              178
+	0xc0,                          //  End Collection                     180
+	0x05, 0x0d,                    //  Usage Page (Digitizers)            181
+	0x09, 0x22,                    //  Usage (Finger)                     183
+	0xa1, 0x02,                    //  Collection (Logical)               185
+	0x05, 0x0d,                    //   Usage Page (Digitizers)           187
+	0x95, 0x01,                    //   Report Count (1)                  189
+	0x75, 0x06,                    //   Report Size (6)                   191
+	0x09, 0x51,                    //   Usage (Contact Id)                193
+	0x15, 0x00,                    //   Logical Minimum (0)               195
+	0x25, 0x3f,                    //   Logical Maximum (63)              197
+	0x81, 0x02,                    //   Input (Data,Var,Abs)              199
+	0x09, 0x42,                    //   Usage (Tip Switch)                201
+	0x25, 0x01,                    //   Logical Maximum (1)               203
+	0x75, 0x01,                    //   Report Size (1)                   205
+	0x95, 0x01,                    //   Report Count (1)                  207
+	0x81, 0x02,                    //   Input (Data,Var,Abs)              209
+	0x75, 0x01,                    //   Report Size (1)                   211
+	0x95, 0x01,                    //   Report Count (1)                  213
+	0x81, 0x03,                    //   Input (Cnst,Var,Abs)              215
+	0x05, 0x01,                    //   Usage Page (Generic Desktop)      217
+	0x75, 0x10,                    //   Report Size (16)                  219
+	0x55, 0x0e,                    //   Unit Exponent (-2)                221
+	0x65, 0x11,                    //   Unit (SILinear: cm)               223
+	0x09, 0x30,                    //   Usage (X)                         225
+	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           227
+	0x35, 0x00,                    //   Physical Minimum (0)              230
+	0x46, 0x15, 0x0c,              //   Physical Maximum (3093)           232
+	0x81, 0x42,                    //   Input (Data,Var,Abs,Null)         235
+	0x09, 0x31,                    //   Usage (Y)                         237
+	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           239
+	0x46, 0xcb, 0x06,              //   Physical Maximum (1739)           242
+	0x81, 0x42,                    //   Input (Data,Var,Abs,Null)         245
+	0x05, 0x0d,                    //   Usage Page (Digitizers)           247
+	0x09, 0x30,                    //   Usage (Tip Pressure)              249
+	0x26, 0xff, 0x1f,              //   Logical Maximum (8191)            251
+	0x75, 0x10,                    //   Report Size (16)                  254
+	0x95, 0x01,                    //   Report Count (1)                  256
+	0x81, 0x02,                    //   Input (Data,Var,Abs)              258
+	0xc0,                          //  End Collection                     260
+	0x05, 0x0d,                    //  Usage Page (Digitizers)            261
+	0x09, 0x56,                    //  Usage (Scan Time)                  263
+	0x55, 0x00,                    //  Unit Exponent (0)                  265
+	0x65, 0x00,                    //  Unit (None)                        267
+	0x27, 0xff, 0xff, 0xff, 0x7f,  //  Logical Maximum (2147483647)       269
+	0x95, 0x01,                    //  Report Count (1)                   274
+	0x75, 0x20,                    //  Report Size (32)                   276
+	0x81, 0x02,                    //  Input (Data,Var,Abs)               278
+	0x09, 0x54,                    //  Usage (Contact Count)              280
+	0x25, 0x7f,                    //  Logical Maximum (127)              282
+	0x95, 0x01,                    //  Report Count (1)                   284
+	0x75, 0x08,                    //  Report Size (8)                    286
+	0x81, 0x02,                    //  Input (Data,Var,Abs)               288
+	0x75, 0x08,                    //  Report Size (8)                    290
+	0x95, 0x08,                    //  Report Count (8)                   292
+	0x81, 0x03,                    //  Input (Cnst,Var,Abs)               294
+	0x85, 0x05,                    //  Report ID (5)                      296
+	0x09, 0x55,                    //  Usage (Contact Max)                298
+	0x25, 0x0a,                    //  Logical Maximum (10)               300
+	0x75, 0x08,                    //  Report Size (8)                    302
+	0x95, 0x01,                    //  Report Count (1)                   304
+	0xb1, 0x02,                    //  Feature (Data,Var,Abs)             306
+	0x06, 0x00, 0xff,              //  Usage Page (Vendor Defined Page 1) 308
+	0x09, 0xc5,                    //  Usage (Vendor Usage 0xc5)          311
+	0x85, 0x06,                    //  Report ID (6)                      313
+	0x15, 0x00,                    //  Logical Minimum (0)                315
+	0x26, 0xff, 0x00,              //  Logical Maximum (255)              317
+	0x75, 0x08,                    //  Report Size (8)                    320
+	0x96, 0x00, 0x01,              //  Report Count (256)                 322
+	0xb1, 0x02,                    //  Feature (Data,Var,Abs)             325
+	0xc0,                          // End Collection                      327
+};
+
+SEC("fmod_ret/hid_bpf_rdesc_fixup")
+int BPF_PROG(hid_fix_rdesc_huion_kamvas_pro_19, struct hid_bpf_ctx *hctx)
+{
+	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
+
+	if (!data)
+		return 0; /* EPERM check */
+
+	__builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));
+
+	return sizeof(fixed_rdesc);
+}
+
+/*
+ * This tablet reports the 3rd button through invert, but this conflict
+ * with the normal eraser mode.
+ * Fortunately, before entering eraser mode, (so Invert = 1),
+ * the tablet always sends an out-of-proximity event.
+ * So we can detect that single event and:
+ * - if there was none but the invert bit was toggled: this is the
+ *   third button
+ * - if there was this out-of-proximity event, we are entering
+ *   eraser mode, and we will until the next out-of-proximity.
+ */
+SEC("fmod_ret/hid_bpf_device_event")
+int BPF_PROG(kamvas_pro_19_fix_3rd_button, struct hid_bpf_ctx *hctx)
+{
+	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
+
+	if (!data)
+		return 0; /* EPERM check */
+
+	if (data[0] != 0x0a) /* not the pen report ID */
+		return 0;
+
+	/* stylus is out of range */
+	if (!(data[1] & 0x40)) {
+		prev_was_out_of_range = true;
+		in_eraser_mode = false;
+		return 0;
+	}
+
+	/* going into eraser mode (Invert = 1) only happens after an
+	 * out of range event
+	 */
+	if (prev_was_out_of_range && (data[1] & 0x18))
+		in_eraser_mode = true;
+
+	/* eraser mode works fine */
+	if (in_eraser_mode)
+		return 0;
+
+	/* copy the Invert bit reported for the 3rd button in bit 7 */
+	if (data[1] & 0x08)
+		data[1] |= 0x20;
+
+	/* clear Invert bit now that it was copied */
+	data[1] &= 0xf7;
+
+	prev_was_out_of_range = false;
+
+	return 0;
+}
+
+SEC("syscall")
+int probe(struct hid_bpf_probe_args *ctx)
+{
+	ctx->retval = ctx->rdesc_size != 328;
+	if (ctx->retval)
+		ctx->retval = -EINVAL;
+
+	/* ensure the kernel isn't fixed already */
+	if (ctx->rdesc[17] != 0x43) /* Secondary Tip Switch */
+		ctx->retval = -EINVAL;
+
+	struct hid_bpf_ctx *hctx = hid_bpf_allocate_context(ctx->hid);
+
+	if (!hctx) {
+		return ctx->retval = -EINVAL;
+		return 0;
+	}
+
+	const char *name = hctx->hid->name;
+
+	/* strip out TEST_PREFIX */
+	if (!__builtin_memcmp(name, TEST_PREFIX, sizeof(TEST_PREFIX) - 1))
+		name += sizeof(TEST_PREFIX) - 1;
+
+	if (__builtin_memcmp(name, NAME_KAMVAS_PRO_19, sizeof(NAME_KAMVAS_PRO_19)))
+		ctx->retval = -EINVAL;
+
+	hid_bpf_release_context(hctx);
+
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";

-- 
2.44.0


  parent reply	other threads:[~2024-04-10 17:20 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-10 17:19 [PATCH 00/18] HID: Include current HID-BPF fixes in tree Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 01/18] HID: do not assume HAT Switch logical max < 8 Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 02/18] HID: bpf: add first in-tree HID-BPF fix for the XPPen Artist 24 Benjamin Tissoires
2024-04-11  7:09   ` Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 03/18] HID: bpf: add in-tree HID-BPF fix for the XPPen Artist 16 Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 04/18] HID: bpf: add in-tree HID-BPF fix for the HP Elite Presenter Mouse Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 05/18] HID: bpf: add in-tree HID-BPF fix for the IOGear Kaliber Gaming MMOmentum mouse Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 06/18] HID: bpf: add in-tree HID-BPF fix for the Wacom ArtPen Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 07/18] HID: bpf: add in-tree HID-BPF fix for the XBox Elite 2 over Bluetooth Benjamin Tissoires
2024-04-10 17:19 ` Benjamin Tissoires [this message]
2024-04-10 17:19 ` [PATCH 09/18] HID: bpf: add in-tree HID-BPF fix for the Raptor Mach 2 Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 10/18] selftests/hid: import base_device.py from hid-tools Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 11/18] selftests/hid: add support for HID-BPF pre-loading before starting a test Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 12/18] selftests/hid: tablets: reduce the number of pen state Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 13/18] selftests/hid: tablets: add a couple of XP-PEN tablets Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 14/18] selftests/hid: tablets: also check for XP-Pen offset correction Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 15/18] selftests/hid: add Huion Kamvas Pro 19 tests Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 16/18] selftests/hid: import base_gamepad.py from hid-tools Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 17/18] selftests/hid: move the gamepads definitions in the test file Benjamin Tissoires
2024-04-10 17:19 ` [PATCH 18/18] selftests/hid: add tests for the Raptor Mach 2 joystick Benjamin Tissoires
2024-05-06 14:36 ` [PATCH 19/18] selftests/hid: skip tests with HID-BPF if udev-hid-bpf is not installed bentiss
2024-05-07  5:43   ` Peter Hutterer
2024-05-07 15:02 ` [PATCH 00/18] HID: Include current HID-BPF fixes in tree Benjamin Tissoires

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240410-bpf_sources-v1-8-a8bf16033ef8@kernel.org \
    --to=bentiss@kernel.org \
    --cc=jikos@kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=peter.hutterer@who-t.net \
    --cc=shuah@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).