From nobody Tue Dec 2 02:33:08 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0FA4D366DA8; Tue, 18 Nov 2025 17:16:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486200; cv=none; b=RBliPlk7g5Hervcl/ab0CHDIZUH3hsxEbrIDIrP4xvC30I5Y1O6dk2l6jfjHRLZQMVc6UuhLuHa+qdI2b8o0fKUvd4MPjfCXfpnU6fRvZHbYVb/+U5nspkgH8Tw5+rj348AhtWQWovgVJps8xjucHgMov5hdczPibRsRh0jfV8U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486200; c=relaxed/simple; bh=+bWq3HURyE1MXYreeuZmSFsFcM0VOboO22q3l7rZtLU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=c009LNCd9FtVFUmUVuBnAEKvIQvX/xkI+8WNhqTLvMZ5HV+quPfjq1/74bPFKJaa73+iqmrtAVScF5Lw7tDJs0Bm4K4sZvK2IDWugACjU0qO+FMHr/amI+raPCZX7AQ28gxhgkoGx9uWBhmTrnEAIw0+uhT1ihnZutKolTyZ2Bs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hWZLEz5A; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="hWZLEz5A" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 64526C2BC86; Tue, 18 Nov 2025 17:16:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763486199; bh=+bWq3HURyE1MXYreeuZmSFsFcM0VOboO22q3l7rZtLU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=hWZLEz5Atsz06r4S9loei+zufdHSctLrbJwVteS4KGrpagGtZOAhvkaLL3e1vCYRA IMglAWg0Xq3UdV+J3dgw/C25eZqrYfiid09rIvOckZaexnQXo7fGl8a2mhieaqoYrh Pfry+40zwtne89quYQvtS4U0LhIt5oGSyTCaDn/L+xxchgBZlcJD3Anm/KuXQfEI3+ pQ2s4C2hZSrxoo5rKpjq4YuCJaXktHo4exJid7SIetpk7UdgxgVKyHYYsmSU5ELBTI 1mQhbwr7p8l1r4gWhBbErJffshMFUUUDvEfWjVcHlA4o1OL2CFTkXUzagu2WOEXJn7 DCsfIRKIRrgJQ== From: Benjamin Tissoires Date: Tue, 18 Nov 2025 18:16:22 +0100 Subject: [PATCH 01/10] HID: bpf: Add support for the Inspiroy 2M Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251118-wip-sync-udev-hid-bpf-v1-1-0f8105c54835@kernel.org> References: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> In-Reply-To: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> To: Jiri Kosina Cc: linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, Benjamin Tissoires , Peter Hutterer , Benjamin Tissoires X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763486192; l=21243; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=+bWq3HURyE1MXYreeuZmSFsFcM0VOboO22q3l7rZtLU=; b=SxEVyi2XFW0u8OGk8R5UQ89iEcnrusetSrIypE7n9386BHyoKHTC0Vqs0dHxRyXAWeQhagiEJ lRQ4RXDsuNYD4F4WydxUHLENYkzL0/a3s3MWQw3Q1ND2n9ziuUl7rqC X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= There are a differences in the report descriptor to the existing Inspiroy 2S which makes having this as separate file a more efficient approach than merging them together. Signed-off-by: Peter Hutterer Signed-off-by: Benjamin Tissoires Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests= /167 Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/progs/Huion__Inspiroy-2-M.bpf.c | 563 ++++++++++++++++++++= ++++ 1 file changed, 563 insertions(+) diff --git a/drivers/hid/bpf/progs/Huion__Inspiroy-2-M.bpf.c b/drivers/hid/= bpf/progs/Huion__Inspiroy-2-M.bpf.c new file mode 100644 index 0000000000000000000000000000000000000000..183d408d893a2adde7f12e80284= 49c491f4942a2 --- /dev/null +++ b/drivers/hid/bpf/progs/Huion__Inspiroy-2-M.bpf.c @@ -0,0 +1,563 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2024 Red Hat, Inc + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include "hid_report_helpers.h" +#include + +#define VID_HUION 0x256C +#define PID_INSPIROY_2_M 0x0067 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HUION, PID_INSPIROY_2_M), +); + +/* Filled in by udev-hid-bpf */ +char UDEV_PROP_HUION_FIRMWARE_ID[64]; + +/* The prefix of the firmware ID we expect for this device. The full firmw= are + * string has a date suffix, e.g. HUION_T21j_221221 + */ +char EXPECTED_FIRMWARE_ID[] =3D "HUION_T21k_"; + +/* How this BPF program works: the tablet has two modes, firmware mode and + * tablet mode. In firmware mode (out of the box) the tablet sends button = events + * and the dial as keyboard combinations. In tablet mode it uses a vendor = specific + * hid report to report everything instead. + * Depending on the mode some hid reports are never sent and the correspon= ding + * devices are mute. + * + * To switch the tablet use e.g. https://github.com/whot/huion-switcher + * or one of the tools from the digimend project + * + * This BPF works for both modes. The huion-switcher tool sets the + * HUION_FIRMWARE_ID udev property - if that is set then we disable the fi= rmware + * pad and pen reports (by making them vendor collections that are ignored= ). + * If that property is not set we fix all hidraw nodes so the tablet works= in + * either mode though the drawback is that the device will show up twice if + * you bind it to all event nodes + * + * Default report descriptor for the first exposed hidraw node: + * + * # HUION Huion Tablet_H641P + * # Report descriptor length: 18 bytes + * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 0xF= F00) 0 + * # 0x09, 0x01, // Usage (Vendor Usage 0x01) = 3 + * # 0xa1, 0x01, // Collection (Application) = 5 + * # 0x85, 0x08, // Report ID (8) = 7 + * # 0x75, 0x58, // Report Size (88) = 9 + * # 0x95, 0x01, // Report Count (1) = 11 + * # 0x09, 0x01, // Usage (Vendor Usage 0x01) = 13 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 15 + * # 0xc0, // End Collection = 17 + * R: 18 06 00 ff 09 01 a1 01 85 08 75 58 95 01 09 01 81 02 c0 + * + * This rdesc does nothing until the tablet is switched to raw mode, see + * https://github.com/whot/huion-switcher + * + * + * Second hidraw node is the Pen. This one sends events until the tablet is + * switched to raw mode, then it's mute. + * + * # Report descriptor length: 93 bytes + * # 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, 0x45, // Usage (Eraser) 16 + * # 0x09, 0x3c, // Usage (Invert) 18 = <-- has no Invert eraser + * # 0x15, 0x00, // Logical Minimum (0) 20 + * # 0x25, 0x01, // Logical Maximum (1) 22 + * # 0x75, 0x01, // Report Size (1) 24 + * # 0x95, 0x06, // Report Count (6) 26 + * # 0x81, 0x02, // Input (Data,Var,Abs) 28 + * # 0x09, 0x32, // Usage (In Range) 30 + * # 0x75, 0x01, // Report Size (1) 32 + * # 0x95, 0x01, // Report Count (1) 34 + * # 0x81, 0x02, // Input (Data,Var,Abs) 36 + * # 0x81, 0x03, // Input (Cnst,Var,Abs) 38 + * # 0x05, 0x01, // Usage Page (Generic Desktop) 40 + * # 0x09, 0x30, // Usage (X) 42 + * # 0x09, 0x31, // Usage (Y) 44 + * # 0x55, 0x0d, // Unit Exponent (-3) 46 = <-- change to -2 + * # 0x65, 0x33, // Unit (EnglishLinear: in=C2=B3) = 48 <-- change in=C2=B3 to in + * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 50 + * # 0x35, 0x00, // Physical Minimum (0) 53 + * # 0x46, 0x00, 0x08, // Physical Maximum (2048) 55 = <-- invalid size + * # 0x75, 0x10, // Report Size (16) 58 + * # 0x95, 0x02, // Report Count (2) 60 + * # 0x81, 0x02, // Input (Data,Var,Abs) 62 + * # 0x05, 0x0d, // Usage Page (Digitizers) 64 + * # 0x09, 0x30, // Usage (Tip Pressure) 66 + * # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 68 + * # 0x75, 0x10, // Report Size (16) 71 + * # 0x95, 0x01, // Report Count (1) 73 + * # 0x81, 0x02, // Input (Data,Var,Abs) 75 + * # 0x09, 0x3d, // Usage (X Tilt) 77 = <-- No tilt reported + * # 0x09, 0x3e, // Usage (Y Tilt) 79 + * # 0x15, 0x81, // Logical Minimum (-127) 81 + * # 0x25, 0x7f, // Logical Maximum (127) 83 + * # 0x75, 0x08, // Report Size (8) 85 + * # 0x95, 0x02, // Report Count (2) 87 + * # 0x81, 0x02, // Input (Data,Var,Abs) 89 + * # 0xc0, // End Collection 91 + * # 0xc0, // End Collection 92 + * R: 93 05 0d 09 02 a1 01 85 0a 09 20 a1 01 09 42 09 44 09 45 09 3c 15 00= 25 01 7501 95 06 81 02 09 32 75 01 95 01 81 02 81 03 05 01 09 30 09 31 55 = 0d 65 33 26 ff7f 35 00 46 00 08 75 10 95 02 81 02 05 0d 09 30 26 ff 1f 75 1= 0 95 01 81 02 09 3d09 3e 15 81 25 7f 75 08 95 02 81 02 c0 c0 + * + * Third hidraw node is the pad which sends a combination of keyboard shor= tcuts until + * the tablet is switched to raw mode, then it's mute: + * + * # Report descriptor length: 65 bytes + * # 0x05, 0x01, // Usage Page (Generic Desktop) 0 + * # 0x09, 0x06, // Usage (Keyboard) 2 + * # 0xa1, 0x01, // Collection (Application) 4 + * # 0x85, 0x03, // Report ID (3) 6 + * # 0x05, 0x07, // Usage Page (Keyboard/Keypad) 8 + * # 0x19, 0xe0, // UsageMinimum (224) 10 + * # 0x29, 0xe7, // UsageMaximum (231) 12 + * # 0x15, 0x00, // Logical Minimum (0) 14 + * # 0x25, 0x01, // Logical Maximum (1) 16 + * # 0x75, 0x01, // Report Size (1) 18 + * # 0x95, 0x08, // Report Count (8) 20 + * # 0x81, 0x02, // Input (Data,Var,Abs) 22 + * # 0x05, 0x07, // Usage Page (Keyboard/Keypad) 24 + * # 0x19, 0x00, // UsageMinimum (0) 26 + * # 0x29, 0xff, // UsageMaximum (255) 28 + * # 0x26, 0xff, 0x00, // Logical Maximum (255) 30 + * # 0x75, 0x08, // Report Size (8) 33 + * # 0x95, 0x06, // Report Count (6) 35 + * # 0x81, 0x00, // Input (Data,Arr,Abs) 37 + * # 0xc0, // End Collection 39 + * # 0x05, 0x0c, // Usage Page (Consumer) 40 + * # 0x09, 0x01, // Usage (Consumer Control) 42 + * # 0xa1, 0x01, // Collection (Application) 44 + * # 0x85, 0x04, // Report ID (4) 46 + * # 0x19, 0x00, // UsageMinimum (0) 48 + * # 0x2a, 0x3c, 0x02, // UsageMaximum (572) 50 + * # 0x15, 0x00, // Logical Minimum (0) 53 + * # 0x26, 0x3c, 0x02, // Logical Maximum (572) 55 + * # 0x95, 0x01, // Report Count (1) 58 + * # 0x75, 0x10, // Report Size (16) 60 + * # 0x81, 0x00, // Input (Data,Arr,Abs) 62 + * # 0xc0, // End Collection 64 + * R: 65 05 01 09 06 a1 01 85 03 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08= 81 02 0507 19 00 29 ff 26 ff 00 75 08 95 06 81 00 c0 05 0c 09 01 a1 01 85 = 04 19 00 2a 3c02 15 00 26 3c 02 95 01 75 10 81 00 c0 + * N: HUION Huion Tablet_H641P + */ + +#define PAD_REPORT_DESCRIPTOR_LENGTH 133 +#define PEN_REPORT_DESCRIPTOR_LENGTH 93 +#define VENDOR_REPORT_DESCRIPTOR_LENGTH 36 +#define PAD_REPORT_ID 3 +#define PEN_REPORT_ID 10 +#define VENDOR_REPORT_ID 8 +#define PAD_REPORT_LENGTH 8 +#define PEN_REPORT_LENGTH 10 +#define VENDOR_REPORT_LENGTH 12 + + +__u16 last_button_state; + +static const __u8 fixed_rdesc_pad[] =3D { + UsagePage_GenericDesktop + Usage_GD_Keypad + CollectionApplication( + // -- Byte 0 in report + ReportId(PAD_REPORT_ID) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + UsagePage_Digitizers + Usage_Dig_TabletFunctionKeys + CollectionPhysical( + // Byte 1 in report - just exists so we get to be a tablet pad + Usage_Dig_BarrelSwitch // BTN_STYLUS + ReportCount(1) + ReportSize(1) + Input(Var|Abs) + ReportCount(7) // padding + Input(Const) + // Bytes 2/3 in report - just exists so we get to be a tablet pad + UsagePage_GenericDesktop + Usage_GD_X + Usage_GD_Y + ReportCount(2) + ReportSize(8) + Input(Var|Abs) + // Byte 4 in report is the wheel + Usage_GD_Wheel + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) + ReportCount(1) + ReportSize(8) + Input(Var|Rel) + // Byte 5 is the button state + UsagePage_Button + UsageMinimum_i8(0x1) + UsageMaximum_i8(0x8) + LogicalMinimum_i8(0x1) + LogicalMaximum_i8(0x8) + ReportCount(1) + ReportSize(8) + Input(Arr|Abs) + ) + // Make sure we match our original report length + FixedSizeVendorReport(PAD_REPORT_LENGTH) + ) +}; + +static const __u8 fixed_rdesc_pen[] =3D { + UsagePage_Digitizers + Usage_Dig_Pen + CollectionApplication( + // -- Byte 0 in report + ReportId(PEN_REPORT_ID) + Usage_Dig_Pen + CollectionPhysical( + // -- Byte 1 in report + Usage_Dig_TipSwitch + Usage_Dig_BarrelSwitch + Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2 + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + ReportSize(1) + ReportCount(3) + Input(Var|Abs) + ReportCount(4) // Padding + Input(Const) + Usage_Dig_InRange + ReportCount(1) + Input(Var|Abs) + ReportSize(16) + ReportCount(1) + PushPop( + UsagePage_GenericDesktop + Unit(cm) + UnitExponent(-1) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(160) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32767) + Usage_GD_X + Input(Var|Abs) // Bytes 2+3 + 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 + 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) + ReportCount(2) + Input(Const) // Byte 6+7 + ) + ) +}; + +static const __u8 fixed_rdesc_vendor[] =3D { + UsagePage_Digitizers + Usage_Dig_Pen + CollectionApplication( + // Byte 0 + // We leave the pen on the vendor report ID + ReportId(VENDOR_REPORT_ID) + Usage_Dig_Pen + CollectionPhysical( + // Byte 1 are the buttons + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + ReportSize(1) + Usage_Dig_TipSwitch + Usage_Dig_BarrelSwitch + Usage_Dig_SecondaryBarrelSwitch + ReportCount(3) + Input(Var|Abs) + ReportCount(4) // Padding + Input(Const) + Usage_Dig_InRange + ReportCount(1) + Input(Var|Abs) + ReportSize(16) + ReportCount(1) + PushPop( + UsagePage_GenericDesktop + Unit(cm) + UnitExponent(-1) + // Note: reported logical range differs + // from the pen report ID for x and y + LogicalMinimum_i16(0) + LogicalMaximum_i16(32000) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(160) + // Bytes 2/3 in report + Usage_GD_X + Input(Var|Abs) + 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 + LogicalMinimum_i16(0) + LogicalMaximum_i16(8192) + Usage_Dig_TipPressure + Input(Var|Abs) + ) + ) + UsagePage_GenericDesktop + Usage_GD_Keypad + CollectionApplication( + // Byte 0 + ReportId(PAD_REPORT_ID) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + UsagePage_Digitizers + Usage_Dig_TabletFunctionKeys + CollectionPhysical( + // Byte 1 are the buttons + Usage_Dig_BarrelSwitch // BTN_STYLUS, needed so we get to be a tablet = pad + ReportCount(1) + ReportSize(1) + Input(Var|Abs) + ReportCount(7) // Padding + Input(Const) + // Bytes 2/3 - x/y just exist so we get to be a tablet pad + UsagePage_GenericDesktop + Usage_GD_X + Usage_GD_Y + ReportCount(2) + ReportSize(8) + Input(Var|Abs) + // Bytes 4 and 5 are the button state + UsagePage_Button + UsageMinimum_i8(0x1) + UsageMaximum_i8(0xa) + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) + ReportCount(10) + ReportSize(1) + Input(Var|Abs) + Usage_i8(0x31) // maps to BTN_SOUTH + ReportCount(1) + Input(Var|Abs) + ReportCount(5) + Input(Const) + // Byte 6 is the wheel + UsagePage_GenericDesktop + Usage_GD_Wheel + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) + ReportCount(1) + ReportSize(8) + Input(Var|Rel) + ) + // Make sure we match our original report length + FixedSizeVendorReport(VENDOR_REPORT_LENGTH) + ) +}; + +static const __u8 disabled_rdesc_pen[] =3D { + FixedSizeVendorReport(PEN_REPORT_LENGTH) +}; + +static const __u8 disabled_rdesc_pad[] =3D { + FixedSizeVendorReport(PAD_REPORT_LENGTH) +}; + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx) +{ + __u8 *data =3D hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_= SIZE /* size */); + __s32 rdesc_size =3D hctx->size; + __u8 have_fw_id; + + if (!data) + return 0; /* EPERM check */ + + /* If we have a firmware ID and it matches our expected prefix, we + * disable the default pad/pen nodes. They won't send events + * but cause duplicate devices. + */ + have_fw_id =3D __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID, + EXPECTED_FIRMWARE_ID, + sizeof(EXPECTED_FIRMWARE_ID) - 1) =3D=3D 0; + if (rdesc_size =3D=3D PAD_REPORT_DESCRIPTOR_LENGTH) { + if (have_fw_id) { + __builtin_memcpy(data, disabled_rdesc_pad, sizeof(disabled_rdesc_pad)); + return sizeof(disabled_rdesc_pad); + } + + __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad)); + return sizeof(fixed_rdesc_pad); + } + if (rdesc_size =3D=3D PEN_REPORT_DESCRIPTOR_LENGTH) { + if (have_fw_id) { + __builtin_memcpy(data, disabled_rdesc_pen, sizeof(disabled_rdesc_pen)); + return sizeof(disabled_rdesc_pen); + } + + __builtin_memcpy(data, fixed_rdesc_pen, sizeof(fixed_rdesc_pen)); + return sizeof(fixed_rdesc_pen); + } + /* Always fix the vendor mode so the tablet will work even if nothing sets + * the udev property (e.g. huion-switcher run manually) + */ + if (rdesc_size =3D=3D VENDOR_REPORT_DESCRIPTOR_LENGTH) { + __builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor)); + return sizeof(fixed_rdesc_vendor); + } + return 0; +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(inspiroy_2_fix_events, struct hid_bpf_ctx *hctx) +{ + __u8 *data =3D hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* Only sent if tablet is in default mode */ + if (data[0] =3D=3D PAD_REPORT_ID) { + /* Nicely enough, this device only supports one button down at a time so + * the reports are easy to match. Buttons numbered from the top + * Button released: 03 00 00 00 00 00 00 00 + * Button 1: 03 00 05 00 00 00 00 00 -> b + * Button 2: 03 07 11 00 00 00 00 00 -> Ctrl Shift N + * Button 3: 03 00 08 00 00 00 00 00 -> e + * Button 4: 03 00 0c 00 00 00 00 00 -> i + * Button 5: 03 00 2c 00 00 00 00 00 -> space + * Button 6: 03 01 08 00 00 00 00 00 -> Ctrl E + * Button 7: 03 01 16 00 00 00 00 00 -> Ctrl S + * Button 8: 03 05 1d 00 00 00 00 00 -> Ctrl Alt Z + * + * Wheel down: 03 01 2d 00 00 00 00 00 -> Ctrl - + * Wheel up: 03 01 2e 00 00 00 00 00 -> Ctrl =3D + */ + __u8 button =3D 0; + __u8 wheel =3D 0; + + switch (data[1] << 8 | data[2]) { + case 0x0000: + break; + case 0x0005: + button =3D 1; + break; + case 0x0711: + button =3D 2; + break; + case 0x0008: + button =3D 3; + break; + case 0x000c: + button =3D 4; + break; + case 0x002c: + button =3D 5; + break; + case 0x0108: + button =3D 6; + break; + case 0x0116: + button =3D 7; + break; + case 0x051d: + button =3D 8; + break; + case 0x012d: + wheel =3D -1; + break; + case 0x012e: + wheel =3D 1; + break; + } + + __u8 report[6] =3D {PAD_REPORT_ID, 0x0, 0x0, 0x0, wheel, button}; + + __builtin_memcpy(data, report, sizeof(report)); + return sizeof(report); + } + + /* Nothing to do for the PEN_REPORT_ID, it's already mapped */ + + /* Only sent if tablet is in raw mode */ + if (data[0] =3D=3D VENDOR_REPORT_ID) { + /* Pad reports */ + if (data[1] & 0x20) { + /* See fixed_rdesc_pad */ + struct pad_report { + __u8 report_id; + __u8 btn_stylus; + __u8 x; + __u8 y; + __u16 buttons; + __u8 wheel; + } __attribute__((packed)) *pad_report; + __u8 wheel =3D 0; + + /* Wheel report */ + if (data[1] =3D=3D 0xf1) { + if (data[5] =3D=3D 2) + wheel =3D 0xff; + else + wheel =3D data[5]; + } else { + /* data[4] and data[5] are the buttons, mapped correctly */ + last_button_state =3D data[4] | (data[5] << 8); + wheel =3D 0; // wheel + } + + pad_report =3D (struct pad_report *)data; + + pad_report->report_id =3D PAD_REPORT_ID; + pad_report->btn_stylus =3D 0; + pad_report->x =3D 0; + pad_report->y =3D 0; + pad_report->buttons =3D last_button_state; + pad_report->wheel =3D wheel; + + return sizeof(struct pad_report); + } + + /* Pen reports need nothing done */ + } + + return 0; +} + +HID_BPF_OPS(inspiroy_2) =3D { + .hid_device_event =3D (void *)inspiroy_2_fix_events, + .hid_rdesc_fixup =3D (void *)hid_fix_rdesc, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + switch (ctx->rdesc_size) { + case PAD_REPORT_DESCRIPTOR_LENGTH: + case PEN_REPORT_DESCRIPTOR_LENGTH: + case VENDOR_REPORT_DESCRIPTOR_LENGTH: + ctx->retval =3D 0; + break; + default: + ctx->retval =3D -EINVAL; + } + + return 0; +} + +char _license[] SEC("license") =3D "GPL"; --=20 2.51.1 From nobody Tue Dec 2 02:33:08 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C485A366DB3; Tue, 18 Nov 2025 17:16:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486202; cv=none; b=EEkXYSyimPk/rhNHwtOkQcnTqBltJJ6rJjp2zMMegG/L+NiB0hJA5gMbUjsQpljTr3crW5h5OcAzuBwl8/chAL/e7zYmzHDCVQv7xtshijZSUnE7KQnZ9kmu73s7fJlhZ/9wZPq/q2vCj5aRf51auVj2TN8Hd6EYGnHXKAg8crU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486202; c=relaxed/simple; bh=rluPgTQvC4fFtXW29Fzn7TYcmecIBSoVvfW84Agosns=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=G4i/kWuC6jU84+Tk118V7sUDdmgza0hje57etvCL6EdvHzME9Ce+MLf1llHyivG0ZvxcXh+sc/Fskc1TeRKzUUqOcvSMI9J+NXZaGsiJrK8jiCW3Afe3zIz2f6ydaY8CEwBsHVQebTnEtO1Skf7MOH+K1gveNy4LWRngV4rT2bI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ittNkqkY; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ittNkqkY" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EA051C2BCB3; Tue, 18 Nov 2025 17:16:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763486202; bh=rluPgTQvC4fFtXW29Fzn7TYcmecIBSoVvfW84Agosns=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ittNkqkY9j53bD3EcbAVYivN03DKNocYqVu4UzfNV2D8ZSTHKWIWQ/MypB4JZ0HgU LZ9HASXpIA72b9ei0awTI1jUoYHv9OGEK22XPGFPyqwkZaxxsM2qGAxQltzHn05vCM 8etsx3L2rjrTVdJ2olvVGuOYcsX2Cp7Bh9s9CUEDYfaHAnQXOy5PT5biQcbhQt8wD7 8am4WRHYDdETZAeBB+ZYGfcBvNVl8Xye2yXYr5MQ/OzMsV4nEy2flvOlYpA/IE5dcP f7V/9FgH+863n1vG5ku4Q8i0xnfAm88bkwhRcBRqa7F03pvjmBG9n6fRLLC5C6Kho1 AV9TjMyi2ov4g== From: Benjamin Tissoires Date: Tue, 18 Nov 2025 18:16:23 +0100 Subject: [PATCH 02/10] HID: bpf: add support for Huion Kamvas 13 (Gen 3) (model GS1333) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251118-wip-sync-udev-hid-bpf-v1-2-0f8105c54835@kernel.org> References: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> In-Reply-To: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> To: Jiri Kosina Cc: linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, Benjamin Tissoires , Nicholas LaPointe X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763486192; l=71713; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=rluPgTQvC4fFtXW29Fzn7TYcmecIBSoVvfW84Agosns=; b=rJimJnkYxnZN4ZlZVkMQkbTfsLJCUKOKRHp/Bm1appvoiTMlVlcTLbMsogfvhlfVTxenUd1fL mhDMPFgYLzRB8E16Io/L8hPA2s9J9irH8b+gevR+je4u/eg801FvQZT X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= This assumes that the tablet has been switched into vendor mode (by using huion-switcher[1], for example) and is sending events using Huion's proprietary data format. This has been tested using the PW600L pen, which does not have an eraser. There is no expectation that a pen with an eraser will work at this time. [1] https://github.com/whot/huion-switcher Signed-off-by: Nicholas LaPointe Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests= /162 Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/progs/Huion__Kamvas13Gen3.bpf.c | 1395 +++++++++++++++++++= ++++ 1 file changed, 1395 insertions(+) diff --git a/drivers/hid/bpf/progs/Huion__Kamvas13Gen3.bpf.c b/drivers/hid/= bpf/progs/Huion__Kamvas13Gen3.bpf.c new file mode 100644 index 0000000000000000000000000000000000000000..b63f9a48ea450a66fa202c6bcad= 09278dcfef3a8 --- /dev/null +++ b/drivers/hid/bpf/progs/Huion__Kamvas13Gen3.bpf.c @@ -0,0 +1,1395 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2025 Nicholas LaPointe + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include "hid_report_helpers.h" +#include + +#define VID_HUION 0x256c +#define PID_KAMVAS13_GEN3 0x2008 + +#define VENDOR_DESCRIPTOR_LENGTH 36 +#define TABLET_DESCRIPTOR_LENGTH 368 +#define WHEEL_DESCRIPTOR_LENGTH 108 + +#define VENDOR_REPORT_ID 8 +#define VENDOR_REPORT_LENGTH 14 + +#define VENDOR_REPORT_SUBTYPE_PEN 0x08 +#define VENDOR_REPORT_SUBTYPE_PEN_OUT 0x00 +#define VENDOR_REPORT_SUBTYPE_BUTTONS 0x0e +#define VENDOR_REPORT_SUBTYPE_WHEELS 0x0f + +/* For the reports that we create ourselves */ +#define CUSTOM_PAD_REPORT_ID 9 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_ANY, VID_HUION, PID_KAMVAS13_GEN3), +); + + +/* + * This tablet can send reports using one of two different data formats, + * depending on what "mode" the tablet is in. + * + * By default, the tablet will send reports that can be decoded using its + * included HID descriptors (descriptors 1 and 2, shown below). + * This mode will be called "firmware mode" throughout this file. + * + * The HID descriptor that describes pen events in firmware mode (descript= or 1) + * has multiple bugs: + * * "Secondary Tip Switch" instead of "Secondary Barrel Switch" + * * "Invert" instead of (or potentially shared with) third barrel button + * * Specified tablet area of 2048 in=C2=B3 instead of 293.8 x 165.2mm + * * Specified tilt range of -90 to +90 instead of -60 to +60 + * + * While these can be easily patched up by editing the descriptor, a larger + * problem with the firmware mode exists: it is impossible to tell which o= f the + * two wheels are being rotated (or having their central button pressed). + * + * + * By using a tool such as huion-switcher (https://github.com/whot/huion-s= witcher), + * the tablet can be made to send reports using a proprietary format that = is not + * adequately described by its relevant descriptor (descriptor 0, shown be= low). + * This mode will be called "vendor mode" throughout this file. + * + * The reports sent while in vendor mode allow for proper decoding of the = wheels. + * + * For simplicity and maximum functionality, this BPF focuses strictly on + * enabling one to make use of the vendor mode. + */ + +/* + * DESCRIPTORS + * DESCRIPTOR 0 + * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page = FF00) 0 + * # 0x09, 0x01, // Usage (Vendor Usage 0x01) = 3 + * # 0xa1, 0x01, // Collection (Application) = 5 + * # =E2=94=85 0x85, 0x08, // Report ID (8) = 7 + * # 0x75, 0x68, // Report Size (104) = 9 + * # 0x95, 0x01, // Report Count (1) = 11 + * # 0x09, 0x01, // Usage (Vendor Usage 0x01) = 13 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Abs) = 15 + * # 0xc0, // End Collection = 17 + * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page = FF00) 18 + * # 0x09, 0x01, // Usage (Vendor Usage 0x01) = 21 + * # 0xa1, 0x01, // Collection (Application) = 23 + * # =E2=94=85 0x85, 0x16, // Report ID (22) = 25 + * # 0x75, 0x08, // Report Size (8) = 27 + * # 0x95, 0x07, // Report Count (7) = 29 + * # 0x09, 0x01, // Usage (Vendor Usage 0x01) = 31 + * # =E2=95=91 0xb1, 0x02, // Feature (Data,Var,Abs)= 33 + * # 0xc0, // End Collection = 35 + * R: 36 06 00 ff 09 01 a1 01 85 08 75 68 95 01 09 01 81 02 c0 06 00 ff 0= 9 01 a1 01 85 16 75 08 95 07 09 01 b1 02 c0 + * N: HUION Huion Tablet_GS1333 + * I: 3 256c 2008 + * + * DESCRIPTOR 1 + * # 0x05, 0x0d, // Usage Page (Digitizers) = 0 + * # 0x09, 0x02, // Usage (Pen) = 2 + * # 0xa1, 0x01, // Collection (Application) = 4 + * # =E2=94=85 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, 0x43, // Usage (Secondary Tip Switch)= 16 + * # 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, 0x06, // Report Count (6) = 28 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Abs)= 30 + * # 0x09, 0x32, // Usage (In Range) = 32 + * # 0x75, 0x01, // Report Size (1) = 34 + * # 0x95, 0x01, // Report Count (1) = 36 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Abs)= 38 + * # =E2=94=87 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=C2= =B3) 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 + * # =E2=94=87 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 + * # =E2=94=87 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 + * # =E2=94=87 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 + * # =E2=94=85 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 Identifier) = 113 + * # 0x15, 0x00, // Logical Minimum (0) = 115 + * # 0x25, 0x3f, // Logical Maximum (63) = 117 + * # =E2=94=87 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 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Abs)= 129 + * # 0x75, 0x01, // Report Size (1) = 131 + * # 0x95, 0x01, // Report Count (1) = 133 + * # =E2=94=87 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 + * # =E2=94=87 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 + * # =E2=94=87 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 + * # =E2=94=87 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 Identifier) = 193 + * # 0x15, 0x00, // Logical Minimum (0) = 195 + * # 0x25, 0x3f, // Logical Maximum (63) = 197 + * # =E2=94=87 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 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Abs)= 209 + * # 0x75, 0x01, // Report Size (1) = 211 + * # 0x95, 0x01, // Report Count (1) = 213 + * # =E2=94=87 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 + * # =E2=94=87 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 + * # =E2=94=87 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 + * # =E2=94=87 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 + * # =E2=94=87 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 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Abs) = 288 + * # 0x75, 0x08, // Report Size (8) = 290 + * # 0x95, 0x08, // Report Count (8) = 292 + * # =E2=94=87 0x81, 0x03, // Input (Cnst,Var,Abs) = 294 + * # =E2=94=85 0x85, 0x05, // Report ID (5) = 296 + * # 0x09, 0x55, // Usage (Contact Count Maximum) = 298 + * # 0x25, 0x0a, // Logical Maximum (10) = 300 + * # 0x75, 0x08, // Report Size (8) = 302 + * # 0x95, 0x01, // Report Count (1) = 304 + * # =E2=95=91 0xb1, 0x02, // Feature (Data,Var,Abs)= 306 + * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Pag= e FF00) 308 + * # 0x09, 0xc5, // Usage (Vendor Usage 0xc5) = 311 + * # =E2=94=85 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 + * # =E2=95=91 0xb1, 0x02, // Feature (Data,Var,Abs)= 325 + * # 0xc0, // End Collection = 327 + * # 0x05, 0x01, // Usage Page (Generic Desktop) = 328 + * # 0x09, 0x06, // Usage (Keyboard) = 330 + * # 0xa1, 0x01, // Collection (Application) = 332 + * # =E2=94=85 0x85, 0x03, // Report ID (3) = 334 + * # 0x05, 0x07, // Usage Page (Keyboard/Keypad) = 336 + * # 0x19, 0xe0, // UsageMinimum (224) = 338 + * # 0x29, 0xe7, // UsageMaximum (231) = 340 + * # 0x15, 0x00, // Logical Minimum (0) = 342 + * # 0x25, 0x01, // Logical Maximum (1) = 344 + * # 0x75, 0x01, // Report Size (1) = 346 + * # 0x95, 0x08, // Report Count (8) = 348 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Abs) = 350 + * # 0x05, 0x07, // Usage Page (Keyboard/Keypad) = 352 + * # 0x19, 0x00, // UsageMinimum (0) = 354 + * # 0x29, 0xff, // UsageMaximum (255) = 356 + * # 0x26, 0xff, 0x00, // Logical Maximum (255) = 358 + * # 0x75, 0x08, // Report Size (8) = 361 + * # 0x95, 0x06, // Report Count (6) = 363 + * # =E2=94=87 0x81, 0x00, // Input (Data,Arr,Abs) = 365 + * # 0xc0, // End Collection = 367 + * R: 368 05 0d 09 02 a1 01 85 0a 09 20 a1 01 09 42 09 44 09 43 09 3c 09 = 45 15 00 25 01 75 01 95 06 81 02 09 32 75 01 95 01 81 02 81 03 05 01 09 30 = 09 31 55 0d 65 33 26 ff 7f 35 00 46 00 08 75 10 95 02 81 02 05 0d 09 30 26 = ff 3f 75 10 95 01 81 02 09 3d 09 3e 15 a6 25 5a 75 08 95 02 81 02 c0 c0 05 = 0d 09 04 a1 01 85 04 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 = 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 = 30 26 ff 7f 35 00 46 15 0c 81 42 09 31 26 ff 7f 46 cb 06 81 42 05 0d 09 30 = 26 ff 1f 75 10 95 01 81 02 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 = 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 = 55 0e 65 11 09 30 26 ff 7f 35 00 46 15 0c 81 42 09 31 26 ff 7f 46 cb 06 81 = 42 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 c0 05 0d 09 56 55 00 65 00 27 ff = ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 75 08 95 08 81 03 = 85 05 09 55 25 0a 75 08 95 01 b1 02 06 00 ff 09 c5 85 06 15 00 26 ff 00 75 = 08 96 00 01 b1 02 c0 05 01 09 06 a1 01 85 03 05 07 19 e0 29 e7 15 00 25 01 = 75 01 95 08 81 02 05 07 19 00 29 ff 26 ff 00 75 08 95 06 81 00 c0 + * N: HUION Huion Tablet_GS1333 + * I: 3 256c 2008 + * + * DESCRIPTOR 2 + * # 0x05, 0x01, // Usage Page (Generic Desktop) = 0 + * # 0x09, 0x0e, // Usage (System Multi-Axis Control= ler) 2 + * # 0xa1, 0x01, // Collection (Application) = 4 + * # =E2=94=85 0x85, 0x11, // Report ID (17) = 6 + * # 0x05, 0x0d, // Usage Page (Digitizers) = 8 + * # 0x09, 0x21, // Usage (Puck) = 10 + * # 0xa1, 0x02, // Collection (Logical) = 12 + * # 0x15, 0x00, // Logical Minimum (0) = 14 + * # 0x25, 0x01, // Logical Maximum (1) = 16 + * # 0x75, 0x01, // Report Size (1) = 18 + * # 0x95, 0x01, // Report Count (1) = 20 + * # 0xa1, 0x00, // Collection (Physical) = 22 + * # 0x05, 0x09, // Usage Page (Button) = 24 + * # 0x09, 0x01, // Usage (Button 1) = 26 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Ab= s) 28 + * # 0x05, 0x0d, // Usage Page (Digitizers) = 30 + * # 0x09, 0x33, // Usage (Touch) = 32 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Ab= s) 34 + * # 0x95, 0x06, // Report Count (6) = 36 + * # =E2=94=87 0x81, 0x03, // Input (Cnst,Var,Ab= s) 38 + * # 0xa1, 0x02, // Collection (Logical) = 40 + * # 0x05, 0x01, // Usage Page (Generic Desk= top) 42 + * # 0x09, 0x37, // Usage (Dial) = 44 + * # 0x16, 0x00, 0x80, // Logical Minimum (-32768)= 46 + * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) = 49 + * # 0x75, 0x10, // Report Size (16) = 52 + * # 0x95, 0x01, // Report Count (1) = 54 + * # =E2=94=87 0x81, 0x06, // Input (Data,Var,= Rel) 56 + * # 0x35, 0x00, // Physical Minimum (0) = 58 + * # 0x46, 0x10, 0x0e, // Physical Maximum (3600) = 60 + * # 0x15, 0x00, // Logical Minimum (0) = 63 + * # 0x26, 0x10, 0x0e, // Logical Maximum (3600) = 65 + * # 0x09, 0x48, // Usage (Resolution Multip= lier) 68 + * # =E2=95=91 0xb1, 0x02, // Feature (Data,Va= r,Abs) 70 + * # 0x45, 0x00, // Physical Maximum (0) = 72 + * # 0xc0, // End Collection = 74 + * # 0x75, 0x08, // Report Size (8) = 75 + * # 0x95, 0x01, // Report Count (1) = 77 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Ab= s) 79 + * # 0x75, 0x08, // Report Size (8) = 81 + * # 0x95, 0x01, // Report Count (1) = 83 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Ab= s) 85 + * # 0x75, 0x08, // Report Size (8) = 87 + * # 0x95, 0x01, // Report Count (1) = 89 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Ab= s) 91 + * # 0x75, 0x08, // Report Size (8) = 93 + * # 0x95, 0x01, // Report Count (1) = 95 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Ab= s) 97 + * # 0x75, 0x08, // Report Size (8) = 99 + * # 0x95, 0x01, // Report Count (1) = 101 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Ab= s) 103 + * # 0xc0, // End Collection = 105 + * # 0xc0, // End Collection = 106 + * # 0xc0, // End Collection = 107 + * R: 108 05 01 09 0e a1 01 85 11 05 0d 09 21 a1 02 15 00 25 01 75 01 95 = 01 a1 00 05 09 09 01 81 02 05 0d 09 33 81 02 95 06 81 03 a1 02 05 01 09 37 = 16 00 80 26 ff 7f 75 10 95 01 81 06 35 00 46 10 0e 15 00 26 10 0e 09 48 b1 = 02 45 00 c0 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 = 01 81 01 75 08 95 01 81 01 c0 c0 c0 + * N: HUION Huion Tablet_GS1333 + * I: 3 256c 2008 + * + * + * + * + * + * + * + * + * VENDOR MODE + * HUION_FIRMWARE_ID=3D"HUION_M22c_240606" + * HUION_MAGIC_BYTES=3D"140388e500108100ff3fd8130307008008004010" + * + * MAGIC BYTES + * [LogicalMaximum, X] [LogicalMaximum, Y] [LogicalMaximum= , Pressure] [ LPI] + * 14 03 [ 88 e5] 00 [ 10 81] 00 [ = ff 3f] [d8 13] 03 07 00 80 08 00 40 10 + * + * + * HIDRAW 0 + * DESCRIPTIONS + * report_subtype =3D (data[1] >> 4) & 0x0f + * + * REPORT SUBTYPES + * 0x0e Buttons + * (data[4] & 0x01) button 1 + * (data[4] & 0x02) button 2 + * (data[4] & 0x04) button 3 + * (data[4] & 0x08) button 4 + * (data[4] & 0x10) button 5 + * (data[4] & 0x20) button 6 (top wheel button) + * (data[4] & 0x40) button 7 (bottom wheel button) + * + * All tablet buttons release with the same report: + * 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 + * + * Despite data[4] looking like a bit field, only one button + * can be unambiguously tracked at a time. + * (See NOTES ON SIMULTANEOUS BUTTON HOLDS at the end of this + * comment for examples of the confusion this can create.) + * + * All buttons, with the exceptions of 6 and 7, will repeatedly + * report a press event approximately every 225ms while held. + * + * 0x0f Wheels + * data[3] =3D=3D 1: top wheel + * data[3] =3D=3D 2: bottom wheel + * data[5] =3D=3D 1: clockwise + * data[5] =3D=3D 2: counter-clockwise + * + * 0x08/0x00 Pen + * report_subtype =3D=3D 0x08: in-range + * report_subtype =3D=3D 0x00: out-of-range + * For clarity, this is also equivalent to: + * (data[1] & 0x80) in-range + * + * Switches + * (data[1] & 0x01) tip switch + * (data[1] & 0x02) barrel switch + * (data[1] & 0x04) secondary barrel switch + * (data[1] & 0x08) third barrel switch + * + * Unfortunately, I don't have a pen with an eraser, so I can't + * confirm where the invert and eraser bits reside. + * If we guess using the definitions from HID descriptor 1, + * then they might be... + * (data[1] & 0x08) invert (conflicts with third barrel switch) + * (data[1] & 0x10) eraser + * + * data[2], data[3] X (little-endian, maximum 0xe588) + * + * data[4], data[5] Y (little-endian, maximum 0x8110) + * + * data[6], data[7] Pressure (little-endian, maximum 0x3fff) + * + * data[10] X tilt (signed, -60 to +60) + * data[11] Y tilt (signed, -60 to +60, inverted) + * + * + * EXAMPLE REPORTS + * Top wheel button, press, hold, then release + * E: 000000.000040 14 08 e0 01 01 20 00 00 00 00 00 00 00 00 00 + * E: 000001.531559 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 + * + * Bottom wheel button, press, hold, then release + * E: 000002.787603 14 08 e0 01 01 40 00 00 00 00 00 00 00 00 00 + * E: 000004.215609 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 + * + * + * Top wheel rotation, one detent CW + * E: 000194.003899 14 08 f1 01 01 00 01 00 00 00 00 00 00 00 00 + * + * Top wheel rotation, one detent CCW + * E: 000194.997812 14 08 f1 01 01 00 02 00 00 00 00 00 00 00 00 + * + * Bottom wheel rotation, one detent CW + * E: 000196.693840 14 08 f1 01 02 00 01 00 00 00 00 00 00 00 00 + * + * Bottom wheel rotation, one detent CCW + * E: 000197.757895 14 08 f1 01 02 00 02 00 00 00 00 00 00 00 00 + * + * + * Button 1, press, hold, then release + * E: 000000.000149 14 08 e0 01 01 01 00 00 00 00 00 00 00 00 00 < press + * E: 000000.447598 14 08 e0 01 01 01 00 00 00 00 00 00 00 00 00 < star= ting to auto-repeat, every ~225ms + * E: 000000.673586 14 08 e0 01 01 01 00 00 00 00 00 00 00 00 00 + * E: 000000.900582 14 08 e0 01 01 01 00 00 00 00 00 00 00 00 00 + * E: 000001.126703 14 08 e0 01 01 01 00 00 00 00 00 00 00 00 00 + * E: 000001.347706 14 08 e0 01 01 01 00 00 00 00 00 00 00 00 00 + * E: 000001.533721 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 < rele= ase + * + * Button 2, press, hold, then release + * E: 000003.304735 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00 < press + * E: 000003.746743 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00 < star= ting to auto-repeat, every ~225ms + * E: 000003.973741 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00 + * E: 000004.199832 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00 + * E: 000004.426732 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00 + * E: 000004.647738 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00 + * E: 000004.874733 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00 + * E: 000004.930713 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 < rele= ase + * + * Button 3, press, hold, then release + * E: 000006.650346 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00 < press + * E: 000007.051782 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00 < star= ting to auto-repeat, every ~225ms + * E: 000007.273738 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00 + * E: 000007.499794 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00 + * E: 000007.726725 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00 + * E: 000007.947765 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00 + * E: 000008.174755 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00 + * E: 000008.328786 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 < rele= ase + * + * Button 4, press, hold, then release + * E: 000009.893820 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00 < press + * E: 000010.274781 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00 < star= ting to auto-repeat, every ~225ms + * E: 000010.500931 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00 + * E: 000010.722777 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00 + * E: 000010.948778 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00 + * E: 000011.175799 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00 + * E: 000011.401153 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00 + * E: 000011.432114 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 < rele= ase + * + * Button 5, press, hold, then release + * E: 000013.007778 14 08 e0 01 01 10 00 00 00 00 00 00 00 00 00 < press + * E: 000013.424741 14 08 e0 01 01 10 00 00 00 00 00 00 00 00 00 < star= ting to auto-repeat, every ~225ms + * E: 000013.651715 14 08 e0 01 01 10 00 00 00 00 00 00 00 00 00 + * E: 000013.872763 14 08 e0 01 01 10 00 00 00 00 00 00 00 00 00 + * E: 000014.099789 14 08 e0 01 01 10 00 00 00 00 00 00 00 00 00 + * E: 000014.325734 14 08 e0 01 01 10 00 00 00 00 00 00 00 00 00 + * E: 000014.438080 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 < rele= ase + * + * + * Pen: Top-left, then out of range + * E: 000368.572184 14 08 80 00 00 00 00 00 00 00 00 fb ed 03 00 + * E: 000368.573030 14 08 00 00 00 00 00 00 00 00 00 fb ed 03 00 + * + * Pen: Bottom-right, then out of range + * E: 000544.433185 14 08 80 88 e5 10 81 00 00 00 00 00 00 03 00 + * E: 000544.434183 14 08 00 88 e5 10 81 00 00 00 00 00 00 03 00 + * + * Pen: Max Y tilt (tip of pen points down) + * E: 000002.231927 14 08 80 f5 5d 6c 36 00 00 00 00 09 3c 03 00 + * + * Pen: Min Y Tilt (tip of pen points up) + * E: 000657.593338 14 08 80 5f 69 fa 2c 00 00 00 00 fe c4 03 00 + * + * Pen: Max X tilt (tip of pen points left) + * E: 000742.246503 14 08 80 2a 4f c4 38 00 00 00 00 3c ed 03 00 + * + * Pen: Min X Tilt (tip of pen points right) + * E: 000776.404446 14 08 00 18 85 7c 3b 00 00 00 00 c4 ed 03 00 + * + * Pen: Tip switch, max pressure, then low pressure + * E: 001138.935675 14 08 81 d2 66 04 40 ff 3f 00 00 00 08 03 00 + * + * E: 001142.403715 14 08 81 9d 69 47 3e 82 04 00 00 00 07 03 00 + * + * Pen: Barrel switch + * E: 001210.645652 14 08 82 0d 72 ea 2b 00 00 00 00 db c4 03 00 + * + * Pen: Secondary barrel switch + * E: 001211.519729 14 08 84 2c 71 51 2b 00 00 00 00 da c4 03 00 + * + * Pen: Third switch + * E: 001212.443722 14 08 88 1d 72 df 2b 00 00 00 00 dc c4 03 00 + * + * + * HIDRAW 1 + * No reports + * + * + * HIDRAW 2 + * No reports + * + * + * + * + * + * + * + * + * FIRMWARE MODE + * HIDRAW 0 + * No reports + * + * + * HIDRAW 1 + * EXAMPLE REPORTS + * Top wheel button, *release* + * E: 000067.043739 8 03 00 00 00 00 00 00 00 + * + * Bottom wheel button, *release* + * E: 000068.219161 8 03 00 00 00 00 00 00 00 + * + * + * Button 1, press, then release + * E: 000163.767870 8 03 00 05 00 00 00 00 00 + * E: 000165.969193 8 03 00 00 00 00 00 00 00 + * + * Button 2, press, then release + * E: 000261.728935 8 03 05 11 00 00 00 00 00 + * E: 000262.956220 8 03 00 00 00 00 00 00 00 + * + * Button 3, press, then release + * E: 000289.127881 8 03 01 16 00 00 00 00 00 + * E: 000290.014594 8 03 00 00 00 00 00 00 00 + * + * Button 4, press, then release + * E: 000303.025839 8 03 00 2c 00 00 00 00 00 + * E: 000303.994479 8 03 00 00 00 00 00 00 00 + * + * Button 5, press, then release + * E: 000315.500835 8 03 05 1d 00 00 00 00 00 + * E: 000316.603274 8 03 00 00 00 00 00 00 00 + * + * BUTTON SUMMARY + * 1 E: 000163.767870 8 03 00 05 00 00 00 00 00 Keyboard: B + * 2 E: 000261.728935 8 03 05 11 00 00 00 00 00 Keyboard: LCtrl+LAlt= N + * 3 E: 000289.127881 8 03 01 16 00 00 00 00 00 Keyboard: LCtrl S + * 4 E: 000303.025839 8 03 00 2c 00 00 00 00 00 Keyboard: Space + * 5 E: 000315.500835 8 03 05 1d 00 00 00 00 00 Keyboard: LCtrl+LAlt + * + * All buttons (including the wheel buttons) release the same way: + * 03 00 00 00 00 00 00 00 + * + * + * Pen: Top-left, then out of range + * E: 000063.196828 10 0a c0 00 00 00 00 00 00 00 02 + * E: 000063.197762 10 0a 00 00 00 00 00 00 00 00 02 + * + * Pen: Bottom-right, then out of range + * E: 000197.123138 10 0a c0 ff 7f ff 7f 00 00 00 00 + * E: 000197.124915 10 0a 00 ff 7f ff 7f 00 00 00 00 + * + * Pen: Max Y Tilt (tip of pen points up) + * E: 000291.399541 10 0a c0 19 32 0b 58 00 00 00 3c + * + * Pen: Min Y tilt (tip of pen points down) + * E: 000340.888288 10 0a c0 85 40 89 6e 00 00 17 c4 + * + * Pen: Max X tilt (tip of pen points left) + * E: 000165.575115 10 0a c0 a7 34 99 42 00 00 3c f4 + * + * Pen: Min X Tilt (tip of pen points right) + * E: 000129.507883 10 0a c0 ea 4b 08 40 00 00 c4 1a + * + * Pen: Tip switch, max pressure, then low pressure + * E: 000242.077160 10 0a c1 7e 3c 12 31 ff 3f 03 fd + * + * E: 000339.139188 10 0a c1 ee 3a 9e 32 b5 00 06 f6 + * + * Pen: Barrel switch + * E: 000037.949777 10 0a c2 5c 28 47 2a 00 00 f6 3c + * + * Pen: Secondary barrel switch + * E: 000038.320840 10 0a c4 e4 27 fd 29 00 00 f3 38 + * + * Pen: Third switch + * E: 000038.923822 10 0a c8 97 27 5f 29 00 00 f2 33 + * + * + * HIDRAW 2 + * EXAMPLE REPORTS + * Either wheel rotation, one detent CW + * E: 000097.276573 9 11 00 01 00 00 00 00 00 00 + * + * Either wheel rotation, one detent CCW + * E: 000153.416538 9 11 00 ff ff 00 00 00 00 00 + * + * Either wheel rotation, increasing rotation speed CW + * (Note that the wheels on my particular tablet may be + * damaged, so the false rotation direction changes + * that can be observed might not happen on other units.) + * E: 000210.514925 9 11 00 01 00 00 00 00 00 00 + * E: 000210.725718 9 11 00 01 00 00 00 00 00 00 + * E: 000210.924009 9 11 00 01 00 00 00 00 00 00 + * E: 000211.205629 9 11 00 01 00 00 00 00 00 00 + * E: 000211.280521 9 11 00 0b 00 00 00 00 00 00 + * E: 000211.340121 9 11 00 0e 00 00 00 00 00 00 + * E: 000211.404018 9 11 00 0d 00 00 00 00 00 00 + * E: 000211.462060 9 11 00 0e 00 00 00 00 00 00 + * E: 000211.544886 9 11 00 0a 00 00 00 00 00 00 + * E: 000211.606130 9 11 00 0d 00 00 00 00 00 00 + * E: 000211.674560 9 11 00 0c 00 00 00 00 00 00 + * E: 000211.712039 9 11 00 16 00 00 00 00 00 00 + * E: 000211.748076 9 11 00 17 00 00 00 00 00 00 + * E: 000211.786016 9 11 00 17 00 00 00 00 00 00 + * E: 000211.832960 9 11 00 11 00 00 00 00 00 00 + * E: 000211.874081 9 11 00 14 00 00 00 00 00 00 + * E: 000211.925094 9 11 00 10 00 00 00 00 00 00 + * E: 000211.959048 9 11 00 18 00 00 00 00 00 00 + * E: 000212.006937 9 11 00 11 00 00 00 00 00 00 + * E: 000212.050055 9 11 00 13 00 00 00 00 00 00 + * E: 000212.091947 9 11 00 14 00 00 00 00 00 00 + * E: 000212.122989 9 11 00 1a 00 00 00 00 00 00 + * E: 000212.160866 9 11 00 16 00 00 00 00 00 00 + * E: 000212.194002 9 11 00 19 00 00 00 00 00 00 + * E: 000212.242249 9 11 00 11 00 00 00 00 00 00 + * E: 000212.278061 9 11 00 18 00 00 00 00 00 00 + * E: 000212.328899 9 11 00 10 00 00 00 00 00 00 + * E: 000212.354005 9 11 00 22 00 00 00 00 00 00 + * E: 000212.398995 9 11 00 12 00 00 00 00 00 00 + * E: 000212.432050 9 11 00 19 00 00 00 00 00 00 + * E: 000212.471164 9 11 00 16 00 00 00 00 00 00 + * E: 000212.507047 9 11 00 17 00 00 00 00 00 00 + * E: 000212.540964 9 11 00 19 00 00 00 00 00 00 + * E: 000212.567942 9 11 00 1f 00 00 00 00 00 00 + * E: 000212.610007 9 11 00 14 00 00 00 00 00 00 + * E: 000212.641101 9 11 00 1b 00 00 00 00 00 00 + * E: 000212.674113 9 11 00 19 00 00 00 00 00 00 + * E: 000212.674909 9 11 00 01 00 00 00 00 00 00 + * E: 000212.677062 9 11 00 00 02 00 00 00 00 00 + * E: 000212.679048 9 11 00 55 01 00 00 00 00 00 + * E: 000212.682166 9 11 00 55 01 00 00 00 00 00 + * E: 000212.682788 9 11 00 ff ff 00 00 00 00 00 + * E: 000212.683899 9 11 00 01 00 00 00 00 00 00 + * E: 000212.685827 9 11 00 67 fe 00 00 00 00 00 + * E: 000212.686941 9 11 00 00 08 00 00 00 00 00 + * E: 000212.727840 9 11 00 14 00 00 00 00 00 00 + * E: 000212.772884 9 11 00 13 00 00 00 00 00 00 + * E: 000212.810975 9 11 00 16 00 00 00 00 00 00 + * E: 000212.811793 9 11 00 00 08 00 00 00 00 00 + * E: 000212.812683 9 11 00 01 00 00 00 00 00 00 + * E: 000212.813905 9 11 00 01 00 00 00 00 00 00 + * E: 000212.814909 9 11 00 00 04 00 00 00 00 00 + * E: 000212.816942 9 11 00 01 00 00 00 00 00 00 + * E: 000212.817851 9 11 00 ff ff 00 00 00 00 00 + * E: 000212.818752 9 11 00 01 00 00 00 00 00 00 + * E: 000212.819910 9 11 00 56 fd 00 00 00 00 00 + * E: 000212.820781 9 11 00 ff ff 00 00 00 00 00 + * E: 000212.821811 9 11 00 00 04 00 00 00 00 00 + * E: 000212.822920 9 11 00 00 08 00 00 00 00 00 + * E: 000212.823861 9 11 00 00 02 00 00 00 00 00 + * E: 000212.828781 9 11 00 ba 00 00 00 00 00 00 + * E: 000212.874097 9 11 00 12 00 00 00 00 00 00 + * E: 000212.874872 9 11 00 00 fc 00 00 00 00 00 + * E: 000212.876136 9 11 00 00 fc 00 00 00 00 00 + * E: 000212.877036 9 11 00 00 f8 00 00 00 00 00 + * E: 000212.877993 9 11 00 00 f8 00 00 00 00 00 + * E: 000212.879748 9 11 00 01 00 00 00 00 00 00 + * E: 000212.880728 9 11 00 01 00 00 00 00 00 00 + * E: 000212.881956 9 11 00 00 04 00 00 00 00 00 + * E: 000212.885065 9 11 00 ff ff 00 00 00 00 00 + * E: 000212.917060 9 11 00 1a 00 00 00 00 00 00 + * E: 000212.936458 9 11 00 2d 00 00 00 00 00 00 + * E: 000212.957860 9 11 00 25 00 00 00 00 00 00 + * E: 000212.984019 9 11 00 20 00 00 00 00 00 00 + * E: 000213.017915 9 11 00 19 00 00 00 00 00 00 + * E: 000213.039973 9 11 00 27 00 00 00 00 00 00 + * E: 000213.065933 9 11 00 21 00 00 00 00 00 00 + * E: 000213.085807 9 11 00 28 00 00 00 00 00 00 + * E: 000213.108888 9 11 00 25 00 00 00 00 00 00 + * E: 000213.129726 9 11 00 29 00 00 00 00 00 00 + * E: 000213.172043 9 11 00 14 00 00 00 00 00 00 + * E: 000213.195873 9 11 00 23 00 00 00 00 00 00 + * E: 000213.222884 9 11 00 20 00 00 00 00 00 00 + * E: 000213.243220 9 11 00 2a 00 00 00 00 00 00 + * E: 000213.266778 9 11 00 24 00 00 00 00 00 00 + * E: 000213.285951 9 11 00 2b 00 00 00 00 00 00 + * E: 000213.306045 9 11 00 2a 00 00 00 00 00 00 + * E: 000213.306796 9 11 00 ff ff 00 00 00 00 00 + * E: 000213.307755 9 11 00 ff ff 00 00 00 00 00 + * E: 000213.308820 9 11 00 ff ff 00 00 00 00 00 + * E: 000213.309971 9 11 00 ff ff 00 00 00 00 00 + * E: 000213.310980 9 11 00 01 00 00 00 00 00 00 + * E: 000213.311853 9 11 00 01 00 00 00 00 00 00 + * E: 000213.312861 9 11 00 aa 02 00 00 00 00 00 + * E: 000213.313884 9 11 00 00 f8 00 00 00 00 00 + * E: 000213.315111 9 11 00 ff ff 00 00 00 00 00 + * E: 000213.315992 9 11 00 01 00 00 00 00 00 00 + * E: 000213.316955 9 11 00 00 08 00 00 00 00 00 + * E: 000213.346065 9 11 00 1d 00 00 00 00 00 00 + * E: 000213.346963 9 11 00 ff ff 00 00 00 00 00 + * E: 000213.347874 9 11 00 00 08 00 00 00 00 00 + * E: 000213.348736 9 11 00 00 08 00 00 00 00 00 + * E: 000213.349795 9 11 00 00 04 00 00 00 00 00 + * E: 000213.350791 9 11 00 01 00 00 00 00 00 00 + * E: 000213.351791 9 11 00 01 00 00 00 00 00 00 + * E: 000213.352729 9 11 00 00 f8 00 00 00 00 00 + * E: 000213.353811 9 11 00 01 00 00 00 00 00 00 + * E: 000213.354755 9 11 00 00 f8 00 00 00 00 00 + * E: 000213.355795 9 11 00 00 f8 00 00 00 00 00 + * E: 000213.356813 9 11 00 01 00 00 00 00 00 00 + * E: 000213.357817 9 11 00 00 04 00 00 00 00 00 + * E: 000213.393838 9 11 00 17 00 00 00 00 00 00 + * E: 000213.394719 9 11 00 00 04 00 00 00 00 00 + * E: 000213.395682 9 11 00 00 08 00 00 00 00 00 + * E: 000213.396679 9 11 00 00 04 00 00 00 00 00 + * E: 000213.397651 9 11 00 00 fc 00 00 00 00 00 + * E: 000213.398661 9 11 00 ff ff 00 00 00 00 00 + * E: 000213.400308 9 11 00 56 fd 00 00 00 00 00 + * E: 000213.400909 9 11 00 00 f8 00 00 00 00 00 + * E: 000213.401837 9 11 00 01 00 00 00 00 00 00 + * + * Either wheel rotation, increasing rotation speed CCW + * (Note that the wheels on my particular tablet may be + * damaged, so the false rotation direction changes + * that can be observed might not happen on other units.) + * E: 000040.527820 9 11 00 ff ff 00 00 00 00 00 + * E: 000040.816644 9 11 00 ff ff 00 00 00 00 00 + * E: 000040.880423 9 11 00 f3 ff 00 00 00 00 00 + * E: 000040.882570 9 11 00 ff ff 00 00 00 00 00 + * E: 000040.883381 9 11 00 ff ff 00 00 00 00 00 + * E: 000040.885463 9 11 00 aa 02 00 00 00 00 00 + * E: 000040.924106 9 11 00 ea ff 00 00 00 00 00 + * E: 000041.006155 9 11 00 f6 ff 00 00 00 00 00 + * E: 000041.085799 9 11 00 f6 ff 00 00 00 00 00 + * E: 000041.168492 9 11 00 f6 ff 00 00 00 00 00 + * E: 000041.233453 9 11 00 f3 ff 00 00 00 00 00 + * E: 000041.296641 9 11 00 f3 ff 00 00 00 00 00 + * E: 000041.370302 9 11 00 f5 ff 00 00 00 00 00 + * E: 000041.437410 9 11 00 f4 ff 00 00 00 00 00 + * E: 000041.474514 9 11 00 e9 ff 00 00 00 00 00 + * E: 000041.522171 9 11 00 ef ff 00 00 00 00 00 + * E: 000041.568160 9 11 00 ee ff 00 00 00 00 00 + * E: 000041.608146 9 11 00 ec ff 00 00 00 00 00 + * E: 000041.627132 9 11 00 d3 ff 00 00 00 00 00 + * E: 000041.656151 9 11 00 e3 ff 00 00 00 00 00 + * E: 000041.682264 9 11 00 e0 ff 00 00 00 00 00 + * E: 000041.714186 9 11 00 e6 ff 00 00 00 00 00 + * E: 000041.740339 9 11 00 e0 ff 00 00 00 00 00 + * E: 000041.772087 9 11 00 e5 ff 00 00 00 00 00 + * E: 000041.801093 9 11 00 e3 ff 00 00 00 00 00 + * E: 000041.834051 9 11 00 e7 ff 00 00 00 00 00 + * E: 000041.863094 9 11 00 e3 ff 00 00 00 00 00 + * E: 000041.901016 9 11 00 ea ff 00 00 00 00 00 + * E: 000041.901956 9 11 00 00 04 00 00 00 00 00 + * E: 000041.902837 9 11 00 00 fe 00 00 00 00 00 + * E: 000041.903927 9 11 00 01 00 00 00 00 00 00 + * E: 000041.905066 9 11 00 01 00 00 00 00 00 00 + * E: 000041.907214 9 11 00 00 fe 00 00 00 00 00 + * E: 000041.909011 9 11 00 01 00 00 00 00 00 00 + * E: 000041.909953 9 11 00 01 00 00 00 00 00 00 + * E: 000041.910917 9 11 00 00 08 00 00 00 00 00 + * E: 000041.913280 9 11 00 00 fe 00 00 00 00 00 + * E: 000041.914121 9 11 00 56 fd 00 00 00 00 00 + * E: 000041.915346 9 11 00 ff ff 00 00 00 00 00 + * E: 000041.962101 9 11 00 ee ff 00 00 00 00 00 + * E: 000041.964062 9 11 00 56 fd 00 00 00 00 00 + * E: 000041.964978 9 11 00 00 fc 00 00 00 00 00 + * E: 000041.968058 9 11 00 24 01 00 00 00 00 00 + * E: 000041.968880 9 11 00 56 fd 00 00 00 00 00 + * E: 000041.970977 9 11 00 aa 02 00 00 00 00 00 + * E: 000041.971932 9 11 00 ff ff 00 00 00 00 00 + * E: 000041.972943 9 11 00 01 00 00 00 00 00 00 + * E: 000041.975291 9 11 00 ff ff 00 00 00 00 00 + * E: 000041.978274 9 11 00 01 00 00 00 00 00 00 + * E: 000042.035079 9 11 00 01 00 00 00 00 00 00 + * E: 000042.041283 9 11 00 ff ff 00 00 00 00 00 + * E: 000042.042057 9 11 00 00 04 00 00 00 00 00 + * E: 000042.045169 9 11 00 ff ff 00 00 00 00 00 + * E: 000042.051242 9 11 00 ff ff 00 00 00 00 00 + * E: 000042.056099 9 11 00 63 ff 00 00 00 00 00 + * E: 000042.106329 9 11 00 ef ff 00 00 00 00 00 + * E: 000042.108601 9 11 00 ff ff 00 00 00 00 00 + * E: 000042.116259 9 11 00 6b 00 00 00 00 00 00 + * E: 000042.119140 9 11 00 55 01 00 00 00 00 00 + * E: 000042.126101 9 11 00 88 ff 00 00 00 00 00 + * E: 000042.158009 9 11 00 e6 ff 00 00 00 00 00 + * E: 000042.172108 9 11 00 be ff 00 00 00 00 00 + * E: 000042.207417 9 11 00 e8 ff 00 00 00 00 00 + * E: 000042.223155 9 11 00 cc ff 00 00 00 00 00 + * E: 000042.255185 9 11 00 e6 ff 00 00 00 00 00 + * E: 000042.276280 9 11 00 d7 ff 00 00 00 00 00 + * E: 000042.302128 9 11 00 e0 ff 00 00 00 00 00 + * E: 000042.317423 9 11 00 c8 ff 00 00 00 00 00 + * E: 000042.345226 9 11 00 e1 ff 00 00 00 00 00 + * E: 000042.357243 9 11 00 bc ff 00 00 00 00 00 + * E: 000042.381308 9 11 00 dc ff 00 00 00 00 00 + * E: 000042.383180 9 11 00 dc fe 00 00 00 00 00 + * E: 000042.412288 9 11 00 e3 ff 00 00 00 00 00 + * E: 000042.451216 9 11 00 eb ff 00 00 00 00 00 + * E: 000042.478372 9 11 00 e0 ff 00 00 00 00 00 + * E: 000042.502116 9 11 00 dd ff 00 00 00 00 00 + * E: 000042.520105 9 11 00 d3 ff 00 00 00 00 00 + * E: 000042.540345 9 11 00 d6 ff 00 00 00 00 00 + * E: 000042.541021 9 11 00 00 08 00 00 00 00 00 + * E: 000042.542009 9 11 00 01 00 00 00 00 00 00 + * E: 000042.543045 9 11 00 00 04 00 00 00 00 00 + * E: 000042.544279 9 11 00 ff ff 00 00 00 00 00 + * E: 000042.545097 9 11 00 ff ff 00 00 00 00 00 + * E: 000042.546074 9 11 00 00 08 00 00 00 00 00 + * E: 000042.547237 9 11 00 00 08 00 00 00 00 00 + * E: 000042.548029 9 11 00 ff ff 00 00 00 00 00 + * E: 000042.549304 9 11 00 00 f8 00 00 00 00 00 + * E: 000042.553123 9 11 00 00 ff 00 00 00 00 00 + * E: 000042.581186 9 11 00 e1 ff 00 00 00 00 00 + * E: 000042.582238 9 11 00 00 f8 00 00 00 00 00 + * E: 000042.583150 9 11 00 00 fc 00 00 00 00 00 + * E: 000042.584273 9 11 00 00 f8 00 00 00 00 00 + * E: 000042.585019 9 11 00 00 fc 00 00 00 00 00 + * E: 000042.586059 9 11 00 01 00 00 00 00 00 00 + * E: 000042.589012 9 11 00 67 fe 00 00 00 00 00 + * E: 000042.590066 9 11 00 00 fc 00 00 00 00 00 + * E: 000042.592916 9 11 00 dc fe 00 00 00 00 00 + * E: 000042.621124 9 11 00 e1 ff 00 00 00 00 00 + * E: 000042.622092 9 11 00 ff ff 00 00 00 00 00 + * E: 000042.623069 9 11 00 01 00 00 00 00 00 00 + * E: 000042.624030 9 11 00 ff ff 00 00 00 00 00 + * E: 000042.625006 9 11 00 00 08 00 00 00 00 00 + * E: 000042.626068 9 11 00 00 04 00 00 00 00 00 + * E: 000042.626876 9 11 00 00 08 00 00 00 00 00 + * E: 000042.628392 9 11 00 00 08 00 00 00 00 00 + * E: 000042.628918 9 11 00 01 00 00 00 00 00 00 + * E: 000042.630009 9 11 00 ff ff 00 00 00 00 00 + * E: 000042.631934 9 11 00 00 fe 00 00 00 00 00 + * E: 000042.656285 9 11 00 dd ff 00 00 00 00 00 + * E: 000042.659870 9 11 00 cc 00 00 00 00 00 00 + * E: 000042.666128 9 11 00 9d 00 00 00 00 00 00 + * E: 000042.672458 9 11 00 80 ff 00 00 00 00 00 + * E: 000042.696106 9 11 00 dc ff 00 00 00 00 00 + * E: 000042.705129 9 11 00 61 00 00 00 00 00 00 + * E: 000042.731303 9 11 00 e0 ff 00 00 00 00 00 + * E: 000042.741278 9 11 00 ab ff 00 00 00 00 00 + * E: 000042.788181 9 11 00 ee ff 00 00 00 00 00 + * E: 000042.810441 9 11 00 db ff 00 00 00 00 00 + * E: 000042.838073 9 11 00 e1 ff 00 00 00 00 00 + * E: 000042.852235 9 11 00 c4 ff 00 00 00 00 00 + * E: 000042.882290 9 11 00 e4 ff 00 00 00 00 00 + * + * Either wheel button, press, hold, then release + * E: 000202.084982 9 11 02 00 00 00 00 00 00 00 + * E: 000202.090172 9 11 03 00 00 00 00 00 00 00 + * E: 000202.094139 9 11 03 00 00 00 00 00 00 00 + * E: 000202.099172 9 11 03 00 00 00 00 00 00 00 + * E: 000202.105055 9 11 03 00 00 00 00 00 00 00 + * E: 000202.109132 9 11 03 00 00 00 00 00 00 00 + * E: 000202.114185 9 11 03 00 00 00 00 00 00 00 + * E: 000202.119212 9 11 03 00 00 00 00 00 00 00 + * E: 000202.124264 9 11 03 00 00 00 00 00 00 00 + * E: 000202.130147 9 11 03 00 00 00 00 00 00 00 + * E: 000202.135138 9 11 03 00 00 00 00 00 00 00 + * E: 000202.140072 9 11 03 00 00 00 00 00 00 00 + * E: 000202.145146 9 11 03 00 00 00 00 00 00 00 + * E: 000202.150157 9 11 03 00 00 00 00 00 00 00 + * E: 000202.155339 9 11 03 00 00 00 00 00 00 00 + * E: 000202.160064 9 11 03 00 00 00 00 00 00 00 + * E: 000202.165026 9 11 03 00 00 00 00 00 00 00 + * E: 000202.170037 9 11 03 00 00 00 00 00 00 00 + * E: 000202.175154 9 11 03 00 00 00 00 00 00 00 + * E: 000202.180044 9 11 03 00 00 00 00 00 00 00 + * E: 000202.186280 9 11 03 00 00 00 00 00 00 00 + * E: 000202.191281 9 11 03 00 00 00 00 00 00 00 + * E: 000202.196106 9 11 03 00 00 00 00 00 00 00 + * E: 000202.201083 9 11 03 00 00 00 00 00 00 00 + * E: 000202.206166 9 11 03 00 00 00 00 00 00 00 + * E: 000202.211084 9 11 03 00 00 00 00 00 00 00 + * E: 000202.216175 9 11 03 00 00 00 00 00 00 00 + * E: 000202.221036 9 11 03 00 00 00 00 00 00 00 + * E: 000202.226271 9 11 03 00 00 00 00 00 00 00 + * E: 000202.231150 9 11 03 00 00 00 00 00 00 00 + * E: 000202.235924 9 11 03 00 00 00 00 00 00 00 + * E: 000202.242046 9 11 03 00 00 00 00 00 00 00 + * E: 000202.247164 9 11 03 00 00 00 00 00 00 00 + * E: 000202.252359 9 11 03 00 00 00 00 00 00 00 + * E: 000202.257295 9 11 03 00 00 00 00 00 00 00 + * E: 000202.262167 9 11 03 00 00 00 00 00 00 00 + * E: 000202.267081 9 11 03 00 00 00 00 00 00 00 + * E: 000202.272175 9 11 03 00 00 00 00 00 00 00 + * E: 000202.277085 9 11 03 00 00 00 00 00 00 00 + * E: 000202.282596 9 11 03 00 00 00 00 00 00 00 + * E: 000202.287078 9 11 03 00 00 00 00 00 00 00 + * E: 000202.292191 9 11 03 00 00 00 00 00 00 00 + * E: 000202.298196 9 11 03 00 00 00 00 00 00 00 + * E: 000202.303004 9 11 03 00 00 00 00 00 00 00 + * E: 000202.308113 9 11 03 00 00 00 00 00 00 00 + * E: 000202.313079 9 11 03 00 00 00 00 00 00 00 + * E: 000202.318243 9 11 03 00 00 00 00 00 00 00 + * E: 000202.323309 9 11 03 00 00 00 00 00 00 00 + * E: 000202.328190 9 11 03 00 00 00 00 00 00 00 + * E: 000202.333050 9 11 03 00 00 00 00 00 00 00 + * E: 000202.338162 9 11 03 00 00 00 00 00 00 00 + * E: 000202.343022 9 11 03 00 00 00 00 00 00 00 + * E: 000202.348113 9 11 03 00 00 00 00 00 00 00 + * E: 000202.354133 9 11 03 00 00 00 00 00 00 00 + * E: 000202.359132 9 11 03 00 00 00 00 00 00 00 + * E: 000202.364053 9 11 03 00 00 00 00 00 00 00 + * E: 000202.369034 9 11 03 00 00 00 00 00 00 00 + * E: 000202.374144 9 11 03 00 00 00 00 00 00 00 + * E: 000202.379027 9 11 03 00 00 00 00 00 00 00 + * E: 000202.384238 9 11 03 00 00 00 00 00 00 00 + * E: 000202.389249 9 11 03 00 00 00 00 00 00 00 + * E: 000202.394049 9 11 03 00 00 00 00 00 00 00 + * E: 000202.398949 9 11 03 00 00 00 00 00 00 00 + * E: 000202.404203 9 11 03 00 00 00 00 00 00 00 + * E: 000202.410098 9 11 03 00 00 00 00 00 00 00 + * E: 000202.415237 9 11 00 00 00 00 00 00 00 00 + * + * + * Top wheel button press and release while holding bottom wheel button + * (The reverse action (a bottom wheel button press while holding the t= op wheel button) is invisible.) + * E: 000071.126966 9 11 03 00 00 00 00 00 00 00 + * E: 000071.133117 9 11 03 00 00 00 00 00 00 00 + * E: 000071.137481 9 11 03 00 00 00 00 00 00 00 + * E: 000071.142036 9 11 03 00 00 00 00 00 00 00 + * E: 000071.147027 9 11 03 00 00 00 00 00 00 00 + * E: 000071.151988 9 11 03 00 00 00 00 00 00 00 + * E: 000071.157945 9 11 03 00 00 00 00 00 00 00 + * E: 000071.163657 9 11 03 00 00 00 00 00 00 00 + * E: 000071.168240 9 11 03 00 00 00 00 00 00 00 + * E: 000071.173109 9 11 02 00 00 00 00 00 00 00 < top wheel button pre= ss? + * E: 000071.178119 9 11 03 00 00 00 00 00 00 00 + * E: 000071.183046 9 11 03 00 00 00 00 00 00 00 + * E: 000071.187983 9 11 03 00 00 00 00 00 00 00 + * E: 000071.192996 9 11 03 00 00 00 00 00 00 00 + * E: 000071.198341 9 11 03 00 00 00 00 00 00 00 + * E: 000071.203122 9 11 03 00 00 00 00 00 00 00 + * E: 000071.208998 9 11 03 00 00 00 00 00 00 00 + * E: 000071.214037 9 11 03 00 00 00 00 00 00 00 + * E: 000071.218945 9 11 03 00 00 00 00 00 00 00 + * E: 000071.223835 9 11 03 00 00 00 00 00 00 00 + * E: 000071.228987 9 11 03 00 00 00 00 00 00 00 + * E: 000071.234082 9 11 03 00 00 00 00 00 00 00 + * E: 000071.239028 9 11 03 00 00 00 00 00 00 00 + * E: 000071.244307 9 11 00 00 00 00 00 00 00 00 < top wheel button rel= ease? + * E: 000071.245867 9 11 03 00 00 00 00 00 00 00 < continued hold of bo= ttom button + * E: 000071.249959 9 11 03 00 00 00 00 00 00 00 + * E: 000071.255032 9 11 03 00 00 00 00 00 00 00 + * E: 000071.259972 9 11 03 00 00 00 00 00 00 00 + * E: 000071.265409 9 11 03 00 00 00 00 00 00 00 + * E: 000071.270156 9 11 03 00 00 00 00 00 00 00 + * E: 000071.275530 9 11 03 00 00 00 00 00 00 00 + * E: 000071.279975 9 11 03 00 00 00 00 00 00 00 + * E: 000071.285046 9 11 03 00 00 00 00 00 00 00 + * E: 000071.290906 9 11 03 00 00 00 00 00 00 00 + * E: 000071.296146 9 11 03 00 00 00 00 00 00 00 + * E: 000071.301288 9 11 03 00 00 00 00 00 00 00 + * + * Top wheel button hold while top wheel rotate CCW + * (I did not test the other combinations of this) + * E: 000022.253144 9 11 03 00 00 00 00 00 00 00 + * E: 000022.258157 9 11 03 00 00 00 00 00 00 00 + * E: 000022.262011 9 11 00 ff ff 00 00 00 00 00 + * E: 000022.264015 9 11 03 00 00 00 00 00 00 00 + * E: 000022.268976 9 11 03 00 00 00 00 00 00 00 + * + * + * + * + * + * + * + * + * NOTES ON SIMULTANEOUS BUTTON HOLDS + * (applies to vendor mode only) + * Value replacements for ease of reading: + * .7 =3D 0x40 (button 7, a wheel button) + * .1 =3D 0x01 (button 1, a pad button) + * rr =3D 0x00 (no buttons pressed) + * + * Press 7 + * Press 1 + * Release 7 + * Release 1 + * B: 000000.000152 42 08 e0 01 01 .7 00 00 00 00 00 00 00 00 00 + * B: 000000.781784 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000000.869845 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000001.095688 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000001.322635 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000001.543643 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000001.770652 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000001.885659 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 relea= se of 7 + * B: 000001.993620 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000002.220671 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000002.446589 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000002.672559 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000002.765183 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 relea= se of 1 + * + * Press 7 + * Press 1 + * Release 1 + * Release 7 + * B: 000017.071517 42 08 e0 01 01 .7 00 00 00 00 00 00 00 00 00 + * B: 000018.270461 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000018.419486 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000018.646438 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000018.872493 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000019.094422 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000019.320488 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000020.360505 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 relea= se of 1 is not reported until 7 is released, then both are rapidly reported + * B: 000020.361091 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 + * + * Press 1 + * Press 7 + * Release 7 + * Release 1 + * B: 000031.516315 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000031.922299 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000032.144165 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000032.370262 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000032.396242 42 08 e0 01 01 .7 00 00 00 00 00 00 00 00 00 + * B: 000032.597270 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000032.818187 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000033.045143 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000033.267535 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 relea= se of 7 + * B: 000033.272602 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000033.494246 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000033.721266 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000033.947237 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000034.169294 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000034.183585 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 relea= se of 1 + * + * Press 1 + * Press 7 + * Release 1 + * Release 7 + * B: 000056.628429 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000057.046348 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000057.272044 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000057.494434 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000057.601224 42 08 e0 01 01 .7 00 00 00 00 00 00 00 00 00 + * B: 000057.719262 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000057.946941 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000058.172346 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000058.393994 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00 + * B: 000059.434576 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 relea= se of 1 is not reported until 7 is released, then both are rapidly reported + * B: 000059.435857 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 + */ + + +/* Filled in by udev-hid-bpf */ +char UDEV_PROP_HUION_FIRMWARE_ID[64]; + +char EXPECTED_FIRMWARE_ID[] =3D "HUION_M22c_"; + +__u8 last_button_state; + +static const __u8 disabled_rdesc_tablet[] =3D { + FixedSizeVendorReport(28) /* Input report 4 */ +}; + +static const __u8 disabled_rdesc_wheel[] =3D { + FixedSizeVendorReport(9) /* Input report 17 */ +}; + +static const __u8 fixed_rdesc_vendor[] =3D { + UsagePage_Digitizers + Usage_Dig_Pen + CollectionApplication( + ReportId(VENDOR_REPORT_ID) + UsagePage_Digitizers + Usage_Dig_Pen + CollectionPhysical( + /* + * I have only examined the tablet's behavior while using + * the PW600L pen, which does not have an eraser. + * Because of this, I don't know where the Eraser and Invert + * bits will go, or if they work as one would expect. + * + * For the time being, there is no expectation that a pen + * with an eraser will work without modifications here. + */ + ReportSize(1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + ReportCount(3) + Usage_Dig_TipSwitch + Usage_Dig_BarrelSwitch + Usage_Dig_SecondaryBarrelSwitch + Input(Var|Abs) + PushPop( + ReportCount(1) + UsagePage_Button + Usage_i8(0x4a) /* (BTN_STYLUS3 + 1) & 0xff */ + Input(Var|Abs) + ) + ReportCount(3) + Input(Const) + ReportCount(1) + Usage_Dig_InRange + Input(Var|Abs) + ReportSize(16) + ReportCount(1) + PushPop( + UsagePage_GenericDesktop + Unit(cm) + UnitExponent(-2) + LogicalMinimum_i16(0) + PhysicalMinimum_i16(0) + /* + * The tablet has a logical maximum of 58760 x 33040 + * and a claimed resolution of 5080 LPI (200 L/mm) + * This works out to a physical maximum of + * 293.8 x 165.2mm, which matches Huion's advertised + * active area dimensions from + * https://www.huion.com/products/pen_display/Kamvas/kamvas-13-gen-3.h= tml + */ + LogicalMaximum_i16(58760) + PhysicalMaximum_i16(2938) + Usage_GD_X + Input(Var|Abs) + LogicalMaximum_i16(33040) + PhysicalMaximum_i16(1652) + Usage_GD_Y + Input(Var|Abs) + ) + LogicalMinimum_i16(0) + LogicalMaximum_i16(16383) + Usage_Dig_TipPressure + Input(Var|Abs) + ReportCount(1) + Input(Const) + ReportSize(8) + ReportCount(2) + PushPop( + Unit(deg) + UnitExponent(0) + LogicalMinimum_i8(-60) + PhysicalMinimum_i8(-60) + LogicalMaximum_i8(60) + PhysicalMaximum_i8(60) + Usage_Dig_XTilt + Usage_Dig_YTilt + Input(Var|Abs) + ) + ) + ) + UsagePage_GenericDesktop + Usage_GD_Keypad + CollectionApplication( + ReportId(CUSTOM_PAD_REPORT_ID) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + UsagePage_Digitizers + Usage_Dig_TabletFunctionKeys + CollectionPhysical( + /* + * The first 3 bytes are somewhat vestigial and will + * always be set to zero. Their presence here is needed + * to ensure that this device will be detected as a + * tablet pad by software that otherwise wouldn't know + * any better. + */ + /* (data[1] & 0x01) barrel switch */ + ReportSize(1) + ReportCount(1) + Usage_Dig_BarrelSwitch + Input(Var|Abs) + ReportCount(7) + Input(Const) + /* data[2] X */ + /* data[3] Y */ + ReportSize(8) + ReportCount(2) + UsagePage_GenericDesktop + Usage_GD_X + Usage_GD_Y + Input(Var|Abs) + /* + * (data[4] & 0x01) button 1 + * (data[4] & 0x02) button 2 + * (data[4] & 0x04) button 3 + * (data[4] & 0x08) button 4 + * (data[4] & 0x10) button 5 + * (data[4] & 0x20) button 6 (top wheel button) + * (data[4] & 0x40) button 7 (bottom wheel button) + */ + ReportSize(1) + ReportCount(7) + UsagePage_Button + UsageMinimum_i8(1) + UsageMaximum_i8(7) + Input(Var|Abs) + ReportCount(1) + Input(Const) + /* data[5] top wheel (signed, positive clockwise) */ + ReportSize(8) + ReportCount(1) + UsagePage_GenericDesktop + Usage_GD_Wheel + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) + Input(Var|Rel) + /* data[6] bottom wheel (signed, positive clockwise) */ + UsagePage_Consumer + Usage_Con_ACPan + Input(Var|Rel) + ) + /* + * The kernel will drop reports that are bigger than the + * largest report specified in the HID descriptor. + * Therefore, our modified descriptor needs to have at least one + * HID report that is as long as, or longer than, the largest + * report in the original descriptor. + * + * This macro expands to a no-op report that is padded to the + * provided length. + */ + FixedSizeVendorReport(VENDOR_REPORT_LENGTH) + ) +}; + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc_huion_kamvas13_gen3, struct hid_bpf_ctx *hid_ct= x) +{ + __u8 *data =3D hid_bpf_get_data(hid_ctx, 0 /* offset */, HID_MAX_DESCRIPT= OR_SIZE /* size */); + __s32 rdesc_size =3D hid_ctx->size; + __u8 have_fw_id; + + if (!data) + return 0; /* EPERM check */ + + have_fw_id =3D __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID, + EXPECTED_FIRMWARE_ID, + sizeof(EXPECTED_FIRMWARE_ID) - 1) =3D=3D 0; + + if (have_fw_id) { + /* + * Tablet should be in vendor mode. + * Disable the unused devices + */ + if (rdesc_size =3D=3D TABLET_DESCRIPTOR_LENGTH) { + __builtin_memcpy(data, disabled_rdesc_tablet, + sizeof(disabled_rdesc_tablet)); + return sizeof(disabled_rdesc_tablet); + } + + if (rdesc_size =3D=3D WHEEL_DESCRIPTOR_LENGTH) { + __builtin_memcpy(data, disabled_rdesc_wheel, + sizeof(disabled_rdesc_wheel)); + return sizeof(disabled_rdesc_wheel); + } + } + + /* + * Regardless of which mode the tablet is in, always fix the vendor + * descriptor in case the udev property just happened to not be set + */ + if (rdesc_size =3D=3D VENDOR_DESCRIPTOR_LENGTH) { + __builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor)); + return sizeof(fixed_rdesc_vendor); + } + + return 0; +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(hid_fix_event_huion_kamvas13_gen3, struct hid_bpf_ctx *hid_ct= x) +{ + __u8 *data =3D hid_bpf_get_data(hid_ctx, 0 /* offset */, VENDOR_REPORT_LE= NGTH /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* Handle vendor reports only */ + if (hid_ctx->size !=3D VENDOR_REPORT_LENGTH) + return 0; + if (data[0] !=3D VENDOR_REPORT_ID) + return 0; + + __u8 report_subtype =3D (data[1] >> 4) & 0x0f; + + if (report_subtype =3D=3D VENDOR_REPORT_SUBTYPE_PEN || + report_subtype =3D=3D VENDOR_REPORT_SUBTYPE_PEN_OUT) { + /* Invert Y tilt */ + data[11] =3D -data[11]; + + } else if (report_subtype =3D=3D VENDOR_REPORT_SUBTYPE_BUTTONS || + report_subtype =3D=3D VENDOR_REPORT_SUBTYPE_WHEELS) { + struct pad_report { + __u8 report_id; + __u8 btn_stylus:1; + __u8 padding:7; + __u8 x; + __u8 y; + __u8 buttons; + __s8 top_wheel; + __s8 bottom_wheel; + } __attribute__((packed)) *pad_report; + + __s8 top_wheel =3D 0; + __s8 bottom_wheel =3D 0; + + switch (report_subtype) { + case VENDOR_REPORT_SUBTYPE_WHEELS: + /* + * The wheel direction byte is 1 for clockwise rotation + * and 2 for counter-clockwise. + * Change it to 1 and -1, respectively. + */ + switch (data[3]) { + case 1: + top_wheel =3D (data[5] =3D=3D 1) ? 1 : -1; + break; + case 2: + bottom_wheel =3D (data[5] =3D=3D 1) ? 1 : -1; + break; + } + break; + + case VENDOR_REPORT_SUBTYPE_BUTTONS: + /* + * If a button is already being held, ignore any new + * button event unless it's a release. + * + * The tablet only cleanly handles one button being held + * at a time, and trying to hold multiple buttons + * (particularly wheel+pad buttons) can result in sequences + * of reports that look like imaginary presses and releases. + * + * This is an imperfect way to filter out some of these + * reports. + */ + if (last_button_state !=3D 0x00 && data[4] !=3D 0x00) + break; + + last_button_state =3D data[4]; + break; + } + + + pad_report =3D (struct pad_report *)data; + + pad_report->report_id =3D CUSTOM_PAD_REPORT_ID; + pad_report->btn_stylus =3D 0; + pad_report->x =3D 0; + pad_report->y =3D 0; + pad_report->buttons =3D last_button_state; + pad_report->top_wheel =3D top_wheel; + pad_report->bottom_wheel =3D bottom_wheel; + + return sizeof(struct pad_report); + } + + return 0; +} + +HID_BPF_OPS(huion_kamvas13_gen3) =3D { + .hid_device_event =3D (void *)hid_fix_event_huion_kamvas13_gen3, + .hid_rdesc_fixup =3D (void *)hid_fix_rdesc_huion_kamvas13_gen3, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + switch (ctx->rdesc_size) { + case VENDOR_DESCRIPTOR_LENGTH: + case TABLET_DESCRIPTOR_LENGTH: + case WHEEL_DESCRIPTOR_LENGTH: + ctx->retval =3D 0; + break; + default: + ctx->retval =3D -EINVAL; + } + + return 0; +} + +char _license[] SEC("license") =3D "GPL"; --=20 2.51.1 From nobody Tue Dec 2 02:33:08 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DFC1B366DCB; Tue, 18 Nov 2025 17:16:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486206; cv=none; b=E4xVfv1AR11uVVnKV77GGQXRY3CapX1Cc9GP7BdGNLKZ+ULMtU4hmI8/SVt64Fs/UqEm/Cm7ehTTyfGXTHe34YZmVTr9GkJr9q52a9hc4HrDpZqnFmRlWHOhgWlmMT9U3uWUqRQ9ucN8MaLVfFZV+twWs7YoIg5ToKx7OUCQStw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486206; c=relaxed/simple; bh=rxdZoZDvhdKIaiQO2oECoH7iQAmMgObFPSG7C0azz/U=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YaoSUHH+dYNd11oJoqh6DK528QfDCfGTRUAQKjha/xwE4QRuwB6Y7QODYMM7mURMZbL8gKTvGxfFQc0Kgfjh4bh1os/QskX63HwWhiLBBO4/AJznyPuKH7FJveJKGqE1r5kanHb9yrl3VPWIqVbzU+2+vUgu04UaJ4I/1PqbE4M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=cN6Y3LJH; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="cN6Y3LJH" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 80B38C2BCC6; Tue, 18 Nov 2025 17:16:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763486204; bh=rxdZoZDvhdKIaiQO2oECoH7iQAmMgObFPSG7C0azz/U=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=cN6Y3LJH/FN+bHbWTCuUYhwpbu7XR71Bcqnu7PmaL5/uEFj0o97n9S/8sw5x49AwE PozKVc6ns68uNu+YlVtL39Qfpg6j0dDVYzLLibqf2ZEnqVCQqtII9ZvFTjJ5RuwQUO r01ejTSKSlusIDhpxKgf5q3wd7eXwN07YoXMruwNJyXPVpzuUxHtldQgf6ZWqpA6+u ipDuRPmKDNnZQvO1BRq1DqAqOaNcJ/6EMp27pVsCLrGUIGoAUIULRoH/yBLfhoGJif sfZwPhulDrV6KZfhk0Ufh+veKiVchs/P61XjXoBZ6scCCQoOItyWGqmwnMGqeai7db 6pE6Xg0Y1XlRg== From: Benjamin Tissoires Date: Tue, 18 Nov 2025 18:16:24 +0100 Subject: [PATCH 03/10] HID: bpf: support for Huion Kamvas 16 Gen 3 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251118-wip-sync-udev-hid-bpf-v1-3-0f8105c54835@kernel.org> References: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> In-Reply-To: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> To: Jiri Kosina Cc: linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, Benjamin Tissoires , Higgins Dragon , Benjamin Tissoires X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763486192; l=37841; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=rxdZoZDvhdKIaiQO2oECoH7iQAmMgObFPSG7C0azz/U=; b=aiCENf4yPwBoNL/VLWViuoIDSvsRsNVFtCvSkizP//SZWSE7dTJM2wmt4ATDsp3Fjg6kzfXGn 5xpucQx0Yw2BkNLpLcp3JgSeWun6XBKg1L6dZz7oT4fKlooa7PSagj5 X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= Another Huion Kamvas tablet support. Again it's safer to duplicate the code source in a separate file to ensure we are not breaking any existing device. Signed-off-by: Higgins Dragon Signed-off-by: Benjamin Tissoires Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests= /207 Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/progs/Huion__Kamvas16Gen3.bpf.c | 724 ++++++++++++++++++++= ++++ 1 file changed, 724 insertions(+) diff --git a/drivers/hid/bpf/progs/Huion__Kamvas16Gen3.bpf.c b/drivers/hid/= bpf/progs/Huion__Kamvas16Gen3.bpf.c new file mode 100644 index 0000000000000000000000000000000000000000..ac66c6e65eb4954cbfa096dc972= 4b652b913accd --- /dev/null +++ b/drivers/hid/bpf/progs/Huion__Kamvas16Gen3.bpf.c @@ -0,0 +1,724 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2025 Nicholas LaPointe + * Copyright (c) 2025 Higgins Dragon + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include "hid_report_helpers.h" +#include + +#define VID_HUION 0x256c +#define PID_KAMVAS16_GEN3 0x2009 + +#define VENDOR_DESCRIPTOR_LENGTH 36 +#define TABLET_DESCRIPTOR_LENGTH 328 +#define WHEEL_DESCRIPTOR_LENGTH 200 + +#define VENDOR_REPORT_ID 8 +#define VENDOR_REPORT_LENGTH 14 + +#define VENDOR_REPORT_SUBTYPE_PEN 0x08 +#define VENDOR_REPORT_SUBTYPE_PEN_OUT 0x00 +#define VENDOR_REPORT_SUBTYPE_BUTTONS 0x0e +#define VENDOR_REPORT_SUBTYPE_WHEELS 0x0f + +/* For the reports that we create ourselves */ +#define CUSTOM_PAD_REPORT_ID 9 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_ANY, VID_HUION, PID_KAMVAS16_GEN3), +); + +/* + * This tablet can send reports using one of two different data formats, + * depending on what "mode" the tablet is in. + * + * By default, the tablet will send reports that can be decoded using its + * included HID descriptors (descriptors 1 and 2, shown below). + * This mode will be called "firmware mode" throughout this file. + * + * The HID descriptor that describes pen events in firmware mode (descript= or 1) + * has multiple bugs: + * * "Secondary Tip Switch" instead of "Secondary Barrel Switch" + * * "Invert" instead of (or potentially shared with) third barrel button + * * Specified tablet area of 2048 in=C2=B3 instead of 293.8 x 165.2mm + * * Specified tilt range of -90 to +90 instead of -60 to +60 + * + * While these can be easily patched up by editing the descriptor, a larger + * problem with the firmware mode exists: it is impossible to tell which o= f the + * two wheels are being rotated (or having their central button pressed). + * + * + * By using a tool such as huion-switcher (https://github.com/whot/huion-s= witcher), + * the tablet can be made to send reports using a proprietary format that = is not + * adequately described by its relevant descriptor (descriptor 0, shown be= low). + * This mode will be called "vendor mode" throughout this file. + * + * The reports sent while in vendor mode allow for proper decoding of the = wheels. + * + * For simplicity and maximum functionality, this BPF focuses strictly on + * enabling one to make use of the vendor mode. + */ + +/* + * DESCRIPTORS + * DESCRIPTOR 0 + * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1)= 0 + * # 0x09, 0x01, // Usage (Vendor Usage 1) = 3 + * # 0xa1, 0x01, // Collection (Application) = 5 + * # 0x85, 0x08, // Report ID (8) = 7 + * # 0x75, 0x68, // Report Size (104) = 9 + * # 0x95, 0x01, // Report Count (1) = 11 + * # 0x09, 0x01, // Usage (Vendor Usage 1) = 13 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 15 + * # 0xc0, // End Collection = 17 + * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1)= 18 + * # 0x09, 0x01, // Usage (Vendor Usage 1) = 21 + * # 0xa1, 0x01, // Collection (Application) = 23 + * # 0x85, 0x16, // Report ID (22) = 25 + * # 0x75, 0x08, // Report Size (8) = 27 + * # 0x95, 0x07, // Report Count (7) = 29 + * # 0x09, 0x01, // Usage (Vendor Usage 1) = 31 + * # 0xb1, 0x02, // Feature (Data,Var,Abs) = 33 + * # 0xc0, // End Collection = 35 + * # + * R: 36 06 00 ff 09 01 a1 01 85 08 75 68 95 01 09 01 81 02 c0 06 00 ff 0= 9 01 a1 01 85 16 75 08 95 07 09 01 b1 02 c0 + * N: HUION Huion Tablet_GS1563 + * I: 3 256c 2009 + * + * + * DESCRIPTOR 1 + * # 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, 0x43, // Usage (Secondary Tip Switch) = 16 + * # 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, 0x06, // Report Count (6) = 28 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 30 + * # 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=C2=B3) = 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 + * # + * R: 328 05 0d 09 02 a1 01 85 0a 09 20 a1 01 09 42 09 44 09 43 09 3c 09 = 45 15 00 25 01 75 01 95 06 81 02 09 32 75 01 95 01 81 02 81 03 05 01 09 30 = 09 31 55 0d 65 33 26 ff 7f 35 00 46 00 08 75 10 95 02 81 02 05 0d 09 30 26 = ff 3f 75 10 95 01 81 02 09 3d 09 3e 15 a6 25 5a 75 08 95 02 81 02 c0 c0 05 = 0d 09 04 a1 01 85 04 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 = 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 = 30 26 ff 7f 35 00 46 15 0c 81 42 09 31 26 ff 7f 46 cb 06 81 42 05 0d 09 30 = 26 ff 1f 75 10 95 01 81 02 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 = 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 = 55 0e 65 11 09 30 26 ff 7f 35 00 46 15 0c 81 42 09 31 26 ff 7f 46 cb 06 81 = 42 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 c0 05 0d 09 56 55 00 65 00 27 ff = ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 75 08 95 08 81 03 = 85 05 09 55 25 0a 75 08 95 01 b1 02 06 00 ff 09 c5 85 06 15 00 26 ff 00 75 = 08 96 00 01 b1 02 c0 + * N: HUION Huion Tablet_GS1563 + * I: 3 256c 2009 + * + * DESCRIPTOR 2 + * # 0x05, 0x01, // Usage Page (Generic Desktop) = 0 + * # 0x09, 0x0e, // Usage (System Multi-Axis Controlle= r) 2 + * # 0xa1, 0x01, // Collection (Application) = 4 + * # 0x85, 0x11, // Report ID (17) = 6 + * # 0x05, 0x0d, // Usage Page (Digitizers) = 8 + * # 0x09, 0x21, // Usage (Puck) = 10 + * # 0xa1, 0x02, // Collection (Logical) = 12 + * # 0x15, 0x00, // Logical Minimum (0) = 14 + * # 0x25, 0x01, // Logical Maximum (1) = 16 + * # 0x75, 0x01, // Report Size (1) = 18 + * # 0x95, 0x01, // Report Count (1) = 20 + * # 0xa1, 0x00, // Collection (Physical) = 22 + * # 0x05, 0x09, // Usage Page (Button) = 24 + * # 0x09, 0x01, // Usage (Vendor Usage 0x01) = 26 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 28 + * # 0x05, 0x0d, // Usage Page (Digitizers) = 30 + * # 0x09, 0x33, // Usage (Touch) = 32 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 34 + * # 0x95, 0x06, // Report Count (6) = 36 + * # 0x81, 0x03, // Input (Cnst,Var,Abs) = 38 + * # 0xa1, 0x02, // Collection (Logical) = 40 + * # 0x05, 0x01, // Usage Page (Generic Desktop) = 42 + * # 0x09, 0x37, // Usage (Dial) = 44 + * # 0x16, 0x00, 0x80, // Logical Minimum (-32768) = 46 + * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) = 49 + * # 0x75, 0x10, // Report Size (16) = 52 + * # 0x95, 0x01, // Report Count (1) = 54 + * # 0x81, 0x06, // Input (Data,Var,Rel) = 56 + * # 0x35, 0x00, // Physical Minimum (0) = 58 + * # 0x46, 0x10, 0x0e, // Physical Maximum (3600) = 60 + * # 0x15, 0x00, // Logical Minimum (0) = 63 + * # 0x26, 0x10, 0x0e, // Logical Maximum (3600) = 65 + * # 0x09, 0x48, // Usage (Resolution Multiplier) = 68 + * # 0xb1, 0x02, // Feature (Data,Var,Abs) = 70 + * # 0x45, 0x00, // Physical Maximum (0) = 72 + * # 0xc0, // End Collection = 74 + * # 0x75, 0x08, // Report Size (8) = 75 + * # 0x95, 0x01, // Report Count (1) = 77 + * # 0x81, 0x01, // Input (Cnst,Arr,Abs) = 79 + * # 0x75, 0x08, // Report Size (8) = 81 + * # 0x95, 0x01, // Report Count (1) = 83 + * # 0x81, 0x01, // Input (Cnst,Arr,Abs) = 85 + * # 0x75, 0x08, // Report Size (8) = 87 + * # 0x95, 0x01, // Report Count (1) = 89 + * # 0x81, 0x01, // Input (Cnst,Arr,Abs) = 91 + * # 0x75, 0x08, // Report Size (8) = 93 + * # 0x95, 0x01, // Report Count (1) = 95 + * # 0x81, 0x01, // Input (Cnst,Arr,Abs) = 97 + * # 0x75, 0x08, // Report Size (8) = 99 + * # 0x95, 0x01, // Report Count (1) = 101 + * # 0x81, 0x01, // Input (Cnst,Arr,Abs) = 103 + * # 0xc0, // End Collection = 105 + * # 0xc0, // End Collection = 106 + * # 0xc0, // End Collection = 107 + * # 0x05, 0x01, // Usage Page (Generic Desktop) = 108 + * # 0x09, 0x06, // Usage (Keyboard) = 110 + * # 0xa1, 0x01, // Collection (Application) = 112 + * # 0x85, 0x03, // Report ID (3) = 114 + * # 0x05, 0x07, // Usage Page (Keyboard) = 116 + * # 0x19, 0xe0, // Usage Minimum (224) = 118 + * # 0x29, 0xe7, // Usage Maximum (231) = 120 + * # 0x15, 0x00, // Logical Minimum (0) = 122 + * # 0x25, 0x01, // Logical Maximum (1) = 124 + * # 0x75, 0x01, // Report Size (1) = 126 + * # 0x95, 0x08, // Report Count (8) = 128 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 130 + * # 0x05, 0x07, // Usage Page (Keyboard) = 132 + * # 0x19, 0x00, // Usage Minimum (0) = 134 + * # 0x29, 0xff, // Usage Maximum (255) = 136 + * # 0x26, 0xff, 0x00, // Logical Maximum (255) = 138 + * # 0x75, 0x08, // Report Size (8) = 141 + * # 0x95, 0x06, // Report Count (6) = 143 + * # 0x81, 0x00, // Input (Data,Arr,Abs) = 145 + * # 0xc0, // End Collection = 147 + * # 0x05, 0x0c, // Usage Page (Consumer Devices) = 148 + * # 0x09, 0x01, // Usage (Consumer Control) = 150 + * # 0xa1, 0x01, // Collection (Application) = 152 + * # 0x85, 0x04, // Report ID (4) = 154 + * # 0x19, 0x01, // Usage Minimum (1) = 156 + * # 0x2a, 0x9c, 0x02, // Usage Maximum (668) = 158 + * # 0x15, 0x01, // Logical Minimum (1) = 161 + * # 0x26, 0x9c, 0x02, // Logical Maximum (668) = 163 + * # 0x95, 0x01, // Report Count (1) = 166 + * # 0x75, 0x10, // Report Size (16) = 168 + * # 0x81, 0x00, // Input (Data,Arr,Abs) = 170 + * # 0xc0, // End Collection = 172 + * # 0x05, 0x01, // Usage Page (Generic Desktop) = 173 + * # 0x09, 0x80, // Usage (System Control) = 175 + * # 0xa1, 0x01, // Collection (Application) = 177 + * # 0x85, 0x05, // Report ID (5) = 179 + * # 0x19, 0x81, // Usage Minimum (129) = 181 + * # 0x29, 0x83, // Usage Maximum (131) = 183 + * # 0x15, 0x00, // Logical Minimum (0) = 185 + * # 0x25, 0x01, // Logical Maximum (1) = 187 + * # 0x75, 0x01, // Report Size (1) = 189 + * # 0x95, 0x03, // Report Count (3) = 191 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 193 + * # 0x95, 0x05, // Report Count (5) = 195 + * # 0x81, 0x01, // Input (Cnst,Arr,Abs) = 197 + * # 0xc0, // End Collection = 199 + * # + * R: 200 05 01 09 0e a1 01 85 11 05 0d 09 21 a1 02 15 00 25 01 75 01 95 = 01 a1 00 05 09 09 01 81 02 05 0d 09 33 81 02 95 06 81 03 a1 02 05 01 09 37 = 16 00 80 26 ff 7f 75 10 95 01 81 06 35 00 46 10 0e 15 00 26 10 0e 09 48 b1 = 02 45 00 c0 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 = 01 81 01 75 08 95 01 81 01 c0 c0 c0 05 01 09 06 a1 01 85 03 05 07 19 e0 29 = e7 15 00 25 01 75 01 95 08 81 02 05 07 19 00 29 ff 26 ff 00 75 08 95 06 81 = 00 c0 05 0c 09 01 a1 01 85 04 19 01 2a 9c 02 15 01 26 9c 02 95 01 75 10 81 = 00 c0 05 01 09 80 a1 01 85 05 19 81 29 83 15 00 25 01 75 01 95 03 81 02 95 = 05 81 01 c0 + * N: HUION Huion Tablet_GS1563 + * I: 3 256c 2009 + * + * + * + * VENDOR MODE + * HUION_FIRMWARE_ID=3D"HUION_M22d_241101" + * HUION_MAGIC_BYTES=3D"1403201101ac9900ff3fd81305080080083c4010" + * + * MAGIC BYTES + * [LogicalMaximum, X ] [LogicalMaximum, Y ] [LogicalMaximum= , Pressure] [ LPI] + * 14 03 [ 20 11 01] [ ac 99 00] [ = ff 3f] [d8 13] 05 08 00 80 08 3c 40 10 + * + * See Huion__Kamvas13Gen3.bpf.c for more details on detailed button/dial = reports and caveats. It's very + * similar to the Kamvas 16 Gen 3. + */ + + +/* Filled in by udev-hid-bpf */ +char UDEV_PROP_HUION_FIRMWARE_ID[64]; + +char EXPECTED_FIRMWARE_ID[] =3D "HUION_M22d_"; + +__u8 last_button_state; + +static const __u8 disabled_rdesc_tablet[] =3D { + FixedSizeVendorReport(28) /* Input report 4 */ +}; + +static const __u8 disabled_rdesc_wheel[] =3D { + FixedSizeVendorReport(9) /* Input report 17 */ +}; + +static const __u8 fixed_rdesc_vendor[] =3D { + UsagePage_Digitizers + Usage_Dig_Pen + CollectionApplication( + ReportId(VENDOR_REPORT_ID) + UsagePage_Digitizers + Usage_Dig_Pen + CollectionPhysical( + /* + * I have only examined the tablet's behavior while using + * the PW600L pen, which does not have an eraser. + * Because of this, I don't know where the Eraser and Invert + * bits will go, or if they work as one would expect. + * + * For the time being, there is no expectation that a pen + * with an eraser will work without modifications here. + */ + ReportSize(1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + ReportCount(3) + Usage_Dig_TipSwitch + Usage_Dig_BarrelSwitch + Usage_Dig_SecondaryBarrelSwitch + Input(Var|Abs) + PushPop( + ReportCount(1) + UsagePage_Button + Usage_i8(0x4a) /* (BTN_STYLUS3 + 1) & 0xff */ + Input(Var|Abs) + ) + ReportCount(3) + Input(Const) + ReportCount(1) + Usage_Dig_InRange + Input(Var|Abs) + ReportSize(16) + ReportCount(1) + PushPop( + UsagePage_GenericDesktop + Unit(cm) + UnitExponent(-2) + LogicalMinimum_i16(0) + PhysicalMinimum_i16(0) + /* + * The tablet has a logical maximum of 69920 x 39340 + * and a claimed resolution of 5080 LPI (200 L/mm) + * This works out to a physical maximum of + * 349.6 x 196.7mm, which matches Huion's advertised + * (rounded) active area dimensions from + * https://www.huion.com/products/pen_display/Kamvas/kamvas-16-gen-3.h= tml + * + * The Kamvas uses data[8] for the 3rd byte of the X-axis, and adding + * that after data[2] and data[3] makes a contiguous little-endian + * 24-bit value. (See BPF_PROG below) + */ + ReportSize(24) + LogicalMaximum_i32(69920) + PhysicalMaximum_i16(3496) + Usage_GD_X + Input(Var|Abs) + ReportSize(16) + LogicalMaximum_i16(39340) + PhysicalMaximum_i16(1967) + Usage_GD_Y + Input(Var|Abs) + ) + ReportSize(16) + LogicalMinimum_i16(0) + LogicalMaximum_i16(16383) + Usage_Dig_TipPressure + Input(Var|Abs) + ReportSize(8) + ReportCount(1) + Input(Const) + ReportCount(2) + PushPop( + Unit(deg) + UnitExponent(0) + LogicalMinimum_i8(-60) + PhysicalMinimum_i8(-60) + LogicalMaximum_i8(60) + PhysicalMaximum_i8(60) + Usage_Dig_XTilt + Usage_Dig_YTilt + Input(Var|Abs) + ) + ) + ) + UsagePage_GenericDesktop + Usage_GD_Keypad + CollectionApplication( + ReportId(CUSTOM_PAD_REPORT_ID) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + UsagePage_Digitizers + Usage_Dig_TabletFunctionKeys + CollectionPhysical( + /* + * The first 3 bytes are somewhat vestigial and will + * always be set to zero. Their presence here is needed + * to ensure that this device will be detected as a + * tablet pad by software that otherwise wouldn't know + * any better. + */ + /* (data[1] & 0x01) barrel switch */ + ReportSize(1) + ReportCount(1) + Usage_Dig_BarrelSwitch + Input(Var|Abs) + ReportCount(7) + Input(Const) + /* data[2] X */ + /* data[3] Y */ + ReportSize(8) + ReportCount(2) + UsagePage_GenericDesktop + Usage_GD_X + Usage_GD_Y + Input(Var|Abs) + /* + * (data[4] & 0x01) button 1 + * (data[4] & 0x02) button 2 + * (data[4] & 0x04) button 3 + * (data[4] & 0x08) button 4 + * (data[4] & 0x10) button 5 + * (data[4] & 0x20) button 6 + * (data[4] & 0x40) button 7 (top wheel button) + * (data[4] & 0x80) button 8 (bottom wheel button) + */ + ReportSize(1) + ReportCount(8) + UsagePage_Button + UsageMinimum_i8(1) + UsageMaximum_i8(8) + Input(Var|Abs) + /* data[5] top wheel (signed, positive clockwise) */ + ReportSize(8) + ReportCount(1) + UsagePage_GenericDesktop + Usage_GD_Wheel + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) + Input(Var|Rel) + /* data[6] bottom wheel (signed, positive clockwise) */ + UsagePage_Consumer + Usage_Con_ACPan + Input(Var|Rel) + ) + /* + * The kernel will drop reports that are bigger than the + * largest report specified in the HID descriptor. + * Therefore, our modified descriptor needs to have at least one + * HID report that is as long as, or longer than, the largest + * report in the original descriptor. + * + * This macro expands to a no-op report that is padded to the + * provided length. + */ + FixedSizeVendorReport(VENDOR_REPORT_LENGTH) + ) +}; + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc_huion_kamvas16_gen3, struct hid_bpf_ctx *hid_ct= x) +{ + __u8 *data =3D hid_bpf_get_data(hid_ctx, 0 /* offset */, HID_MAX_DESCRIPT= OR_SIZE /* size */); + __s32 rdesc_size =3D hid_ctx->size; + __u8 have_fw_id; + + if (!data) + return 0; /* EPERM check */ + + have_fw_id =3D __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID, + EXPECTED_FIRMWARE_ID, + sizeof(EXPECTED_FIRMWARE_ID) - 1) =3D=3D 0; + + if (have_fw_id) { + /* + * Tablet should be in vendor mode. + * Disable the unused devices + */ + if (rdesc_size =3D=3D TABLET_DESCRIPTOR_LENGTH) { + __builtin_memcpy(data, disabled_rdesc_tablet, + sizeof(disabled_rdesc_tablet)); + return sizeof(disabled_rdesc_tablet); + } + + if (rdesc_size =3D=3D WHEEL_DESCRIPTOR_LENGTH) { + __builtin_memcpy(data, disabled_rdesc_wheel, + sizeof(disabled_rdesc_wheel)); + return sizeof(disabled_rdesc_wheel); + } + } + + /* + * Regardless of which mode the tablet is in, always fix the vendor + * descriptor in case the udev property just happened to not be set + */ + if (rdesc_size =3D=3D VENDOR_DESCRIPTOR_LENGTH) { + __builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor)); + return sizeof(fixed_rdesc_vendor); + } + + return 0; +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(hid_fix_event_huion_kamvas16_gen3, struct hid_bpf_ctx *hid_ct= x) +{ + __u8 *data =3D hid_bpf_get_data(hid_ctx, 0 /* offset */, VENDOR_REPORT_LE= NGTH /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* Handle vendor reports only */ + if (hid_ctx->size !=3D VENDOR_REPORT_LENGTH) + return 0; + if (data[0] !=3D VENDOR_REPORT_ID) + return 0; + + __u8 report_subtype =3D (data[1] >> 4) & 0x0f; + + if (report_subtype =3D=3D VENDOR_REPORT_SUBTYPE_PEN || + report_subtype =3D=3D VENDOR_REPORT_SUBTYPE_PEN_OUT) { + /* Invert Y tilt */ + data[11] =3D -data[11]; + + /* + * Rearrange the bytes of the report so that + * [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] + * will be arranged as + * [0, 1, 2, 3, 8, 4, 5, 6, 7, 9, 10, 11, 12, 13] + */ + __u8 x_24 =3D data[8]; + + data[8] =3D data[7]; + data[7] =3D data[6]; + data[6] =3D data[5]; + data[5] =3D data[4]; + + data[4] =3D x_24; + + } else if (report_subtype =3D=3D VENDOR_REPORT_SUBTYPE_BUTTONS || + report_subtype =3D=3D VENDOR_REPORT_SUBTYPE_WHEELS) { + struct pad_report { + __u8 report_id; + __u8 btn_stylus:1; + __u8 padding:7; + __u8 x; + __u8 y; + __u8 buttons; + __s8 top_wheel; + __s8 bottom_wheel; + } __attribute__((packed)) *pad_report; + + __s8 top_wheel =3D 0; + __s8 bottom_wheel =3D 0; + + switch (report_subtype) { + case VENDOR_REPORT_SUBTYPE_WHEELS: + /* + * The wheel direction byte is 1 for clockwise rotation + * and 2 for counter-clockwise. + * Change it to 1 and -1, respectively. + */ + switch (data[3]) { + case 1: + top_wheel =3D (data[5] =3D=3D 1) ? 1 : -1; + break; + case 2: + bottom_wheel =3D (data[5] =3D=3D 1) ? 1 : -1; + break; + } + break; + + case VENDOR_REPORT_SUBTYPE_BUTTONS: + /* + * If a button is already being held, ignore any new + * button event unless it's a release. + * + * The tablet only cleanly handles one button being held + * at a time, and trying to hold multiple buttons + * (particularly wheel+pad buttons) can result in sequences + * of reports that look like imaginary presses and releases. + * + * This is an imperfect way to filter out some of these + * reports. + */ + if (last_button_state !=3D 0x00 && data[4] !=3D 0x00) + break; + + last_button_state =3D data[4]; + break; + } + + pad_report =3D (struct pad_report *)data; + + pad_report->report_id =3D CUSTOM_PAD_REPORT_ID; + pad_report->btn_stylus =3D 0; + pad_report->x =3D 0; + pad_report->y =3D 0; + pad_report->buttons =3D last_button_state; + pad_report->top_wheel =3D top_wheel; + pad_report->bottom_wheel =3D bottom_wheel; + + return sizeof(struct pad_report); + } + + return 0; +} + +HID_BPF_OPS(huion_kamvas16_gen3) =3D { + .hid_device_event =3D (void *)hid_fix_event_huion_kamvas16_gen3, + .hid_rdesc_fixup =3D (void *)hid_fix_rdesc_huion_kamvas16_gen3, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + switch (ctx->rdesc_size) { + case VENDOR_DESCRIPTOR_LENGTH: + case TABLET_DESCRIPTOR_LENGTH: + case WHEEL_DESCRIPTOR_LENGTH: + ctx->retval =3D 0; + break; + default: + ctx->retval =3D -EINVAL; + } + + return 0; +} + +char _license[] SEC("license") =3D "GPL"; --=20 2.51.1 From nobody Tue Dec 2 02:33:08 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 52F75366DA8; Tue, 18 Nov 2025 17:16:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486209; cv=none; b=b/lFBkYmoxEcnqTzdwKYKCvbZdTvH27s2YT85Pq4pBlmVz94aG0XxVcD7sEIsOQFF/URtD8tPwpYYdyxUA1U8izgEiL4nXA7U9jfeCe3+CLBeCo5qB2NgTytw7IbsQ+K/qrUUEA82IA00N1qYctKe/V6R9D1lxhQhHt7w9YwsAA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486209; c=relaxed/simple; bh=ZpPaF4qbwEKUKvpZAen5k1CfPWViDBBKeH5nCFrmMVY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=KL+Xve0xXBu91Fw+djekTWRYelc4mdiTJingZmjpwh11t+yEmubJn5XvsqMZrD6/+y1rRKkXhyBCepGGkRoNAUCyO80REC4pp29CkSprpUwMywJBNQIfeYhwCWd5Kc36ou2wx9yvI9DFHunh6Al+6A44MpghwrxmS1ibEKP7zwM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=TsvgAQeo; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="TsvgAQeo" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 65CE6C2BCFD; Tue, 18 Nov 2025 17:16:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763486206; bh=ZpPaF4qbwEKUKvpZAen5k1CfPWViDBBKeH5nCFrmMVY=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=TsvgAQeonhRi2/N4zp6zlZr/HA0cw8NSloi99Lr5gs+iTV3S4l9Rta5Kavj7sfdjV mikGHbHJWVyUxW4uOJoyIQVcoc/kU82ycqzUQ8E4wOn7j58z5ix03UtTYRn5LVM1qp e6eRpVM8bpfSerK0kiBVYdO3d78IVXaO3upHtlLC0uVx3yyBdSWizlV7DK/mbpSXIF wj9XyRCFkpVEjzWhXM5pPy6LrTEuSR5Xl5bDDyViaKgi835+S9AwplSSmsTTjae/OB h9Df2h9VMcW6IoGi7eI8tl2VATg9eCNMQCbMgWyGkBUwo/U85mCO+fsgwKs5XEyJup DJUCH3DwNmRHw== From: Benjamin Tissoires Date: Tue, 18 Nov 2025 18:16:25 +0100 Subject: [PATCH 04/10] HID: bpf: Add fixup for Logitech SpaceNavigator variants Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251118-wip-sync-udev-hid-bpf-v1-4-0f8105c54835@kernel.org> References: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> In-Reply-To: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> To: Jiri Kosina Cc: linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, Benjamin Tissoires , Curran Muhlberger X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763486192; l=3908; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=ZpPaF4qbwEKUKvpZAen5k1CfPWViDBBKeH5nCFrmMVY=; b=0dToVlc/BJ804HWaj8Wpjiye6ov+Q8DLtSG7fZG6wvv0Yb4TkklpEDogxAQgovC1Szi+OEV8g 1H7funUtF0UCmojEMqF64D2n4ldEoENJVfVyF78ZMdFDVbbNGl/93tk X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= The 3Dconnexion SpaceNavigator HID report descriptor declares its axis data to be "relative" when it is actually "absolute". This quirk was addressed in the kernel in 2.6.33, but some SpaceNavigator variants have a slightly different report descriptor whose axis input items are at different offsets than those assumed by the kernel fixup. Add a BPF fixup to handle both sets of offsets for known SpaceNavigator variants if the descriptor has not already been fixed by the kernel. Signed-off-by: Curran Muhlberger Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests= /181 Signed-off-by: Benjamin Tissoires --- .../hid/bpf/progs/Logitech__SpaceNavigator.bpf.c | 86 ++++++++++++++++++= ++++ 1 file changed, 86 insertions(+) diff --git a/drivers/hid/bpf/progs/Logitech__SpaceNavigator.bpf.c b/drivers= /hid/bpf/progs/Logitech__SpaceNavigator.bpf.c new file mode 100644 index 0000000000000000000000000000000000000000..b17719d6d9c71e7b538d75c7a71= 97b4e1fead213 --- /dev/null +++ b/drivers/hid/bpf/progs/Logitech__SpaceNavigator.bpf.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2025 Curran Muhlberger + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include + +#define VID_LOGITECH 0x046D +#define PID_SPACENAVIGATOR 0xC626 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_ANY, VID_LOGITECH, PID_SPACENAVIGATOR) +); + +/* + * The 3Dconnexion SpaceNavigator 3D Mouse is a multi-axis controller with= 6 + * axes (grouped as X,Y,Z and Rx,Ry,Rz). Axis data is absolute, but the r= eport + * descriptor erroneously declares it to be relative. We fix the report + * descriptor to mark both axis collections as absolute. + * + * The kernel attempted to fix this in commit 24985cf68612 (HID: support + * Logitech/3DConnexion SpaceTraveler and SpaceNavigator), but the descrip= tor + * data offsets are incorrect for at least some SpaceNavigator units. + */ + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx) +{ + __u8 *data =3D hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* Offset of Input item in X,Y,Z and Rx,Ry,Rz collections for all known + * firmware variants. + * - 2009 model: X,Y,Z @ 32-33, Rx,Ry,Rz @ 49-50 (fixup originally + * applied in kernel) + * - 2016 model (size=3D=3D228): X,Y,Z @ 36-37, Rx,Ry,Rz @ 53-54 + * + * The descriptor size of the 2009 model is not known, and there is evide= nce + * for at least two other variants (with sizes 202 & 217) besides the 2016 + * model, so we try all known offsets regardless of descriptor size. + */ + const u8 offsets[] =3D {32, 36, 49, 53}; + + for (size_t idx =3D 0; idx < ARRAY_SIZE(offsets); idx++) { + u8 offset =3D offsets[idx]; + + /* if Input (Data,Var,Rel) , make it Input (Data,Var,Abs) */ + if (data[offset] =3D=3D 0x81 && data[offset + 1] =3D=3D 0x06) + data[offset + 1] =3D 0x02; + } + + return 0; +} + +HID_BPF_OPS(logitech_spacenavigator) =3D { + .hid_rdesc_fixup =3D (void *)hid_fix_rdesc, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + /* Ensure report descriptor size matches one of the known variants. */ + if (ctx->rdesc_size !=3D 202 && + ctx->rdesc_size !=3D 217 && + ctx->rdesc_size !=3D 228) { + ctx->retval =3D -EINVAL; + return 0; + } + + /* Check whether the kernel has already applied the fix. */ + if ((ctx->rdesc[32] =3D=3D 0x81 && ctx->rdesc[33] =3D=3D 0x02 && + ctx->rdesc[49] =3D=3D 0x81 && ctx->rdesc[50] =3D=3D 0x02) || + (ctx->rdesc[36] =3D=3D 0x81 && ctx->rdesc[37] =3D=3D 0x02 && + ctx->rdesc[53] =3D=3D 0x81 && ctx->rdesc[54] =3D=3D 0x02)) + ctx->retval =3D -EINVAL; + else + ctx->retval =3D 0; + + return 0; +} + +char _license[] SEC("license") =3D "GPL"; --=20 2.51.1 From nobody Tue Dec 2 02:33:08 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3BFE2369210; Tue, 18 Nov 2025 17:16:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486210; cv=none; b=JWWJ+kgllEg6tUkEc1maFA8cNq2SYXybXh1pfS4srT2vZvxFQHMi74gnKkurBkjMp6ZeExUvo9A80ygRmCxH0jUShhyeUFDPr5lGxh47M7EmtKrQECH3vbA9SG3BMTqhPmWGI+cxjB8yxjekFDDt1xKVx5nmYaXFz7PxlmzzK9I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486210; c=relaxed/simple; bh=lo1Qd7kH11xyLAQbymgjV8k5Aew4Vg0gLPSH35Zi2ss=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NJpBkiM7xkrXI5OmpqfcWCU3h7G3xkdzXHs5CIWu9iLQLdGFWgiHqgE6BkL3iFrlH0RVADEbJ+9yrgOAWlZpLUFAhgMQgx+dbEeHT0bYbMeYkig+1ld6u+tK3FEdH6olS72mrN9apbWhBnpL1GkaO5KwMmhm05dYPAalpq2juoY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ICXyGa/o; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ICXyGa/o" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8BFF0C2BCB8; Tue, 18 Nov 2025 17:16:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763486210; bh=lo1Qd7kH11xyLAQbymgjV8k5Aew4Vg0gLPSH35Zi2ss=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=ICXyGa/oAEUQ/FA26g0qiopmGOU0yBWN2XPn4XFhw2wXQgKZPzcBy9Yfj2V7K2X/j rNe14FUDbb/q2P26XMgI8GPKIHfEniAEJpkUimvk0ZHS4YdKnxuUa3OhKLjfJtJQRU x75CAOVu7e6top++s89MKomC7REGI/VizOh7qz2OhFYcFr9hwUBfYYmcRErB+WEevv Q2ty2TI9gjIr73pLlT2NzduduG9PDzppKjkg6jzJE3vnsVfvVMxKPT+Rc66CkAyw8e bzHh10k2Tklm9vsZkBNHp98GLB2npUbe8tjxKJjXIWxHHlyT7xx04WJSq52YPLJzb5 rwXY5pwAwc7YQ== From: Benjamin Tissoires Date: Tue, 18 Nov 2025 18:16:26 +0100 Subject: [PATCH 05/10] HID: bpf: Add support for the Waltop Batteryless Tablet Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251118-wip-sync-udev-hid-bpf-v1-5-0f8105c54835@kernel.org> References: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> In-Reply-To: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> To: Jiri Kosina Cc: linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, Benjamin Tissoires , Peter Hutterer , Jan Felix Langenbach X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763486192; l=15751; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=lo1Qd7kH11xyLAQbymgjV8k5Aew4Vg0gLPSH35Zi2ss=; b=FyLZVMj8Ipk8feH14Bx0qslREd8P6nEnHEquXbsaha/yxUwH+guoT7bzTQ02IcewkW7DfBwtY d/j1Z+hWRlnD0po0gU8G0q8M2D9uqLQ9SIZx6kVnS0MgHw6j1jmLkgq X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= Several bugs as outlined in udev-hid-bpf#66 and udev-hid-bpf!198: - pressure curve is far from linear - tilt range is =C2=B160, not =C2=B1127 - pressing the second button sets both tip down and barrel switch Fix the second button by adding a Secondary Barrel Switch in the existing padding and check for the tip down/barrel switch down combo. When both values become true at the same time, set the Secondary Barrel Switch instead. Implement a custom pressure curve that maps the hardware range 0-102 linearly to the logical range 0-1224, and maps the hardware range 103-2047 logarithmically to the logical range 1232-2047. This mapping isn=E2=80=99t perfect, but it=E2=80=99s way more natural than the stock con= figuration. Signed-off-by: Peter Hutterer Signed-off-by: Jan Felix Langenbach Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests= /200 Signed-off-by: Benjamin Tissoires --- .../hid/bpf/progs/WALTOP__Batteryless-Tablet.bpf.c | 321 +++++++++++++++++= ++++ 1 file changed, 321 insertions(+) diff --git a/drivers/hid/bpf/progs/WALTOP__Batteryless-Tablet.bpf.c b/drive= rs/hid/bpf/progs/WALTOP__Batteryless-Tablet.bpf.c new file mode 100644 index 0000000000000000000000000000000000000000..156d75af516d7d0d49d0f5ee1ff= 5d3ebb720c7d9 --- /dev/null +++ b/drivers/hid/bpf/progs/WALTOP__Batteryless-Tablet.bpf.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2025 Red Hat + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include + +#define VID_WALTOP 0x172F +#define PID_BATTERYLESS_TABLET 0x0505 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_ANY, VID_WALTOP, PID_BATTERYLESS_TABLET) +); + +#define EXPECTED_RDESC_SIZE 335 +#define PEN_REPORT_ID 16 + +#define TIP_SWITCH BIT(0) +#define BARREL_SWITCH BIT(1) +#define SECONDARY_BARREL_SWITCH BIT(5) + +static __u8 last_button_state; + +static const __u8 fixed_rdesc[] =3D { + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x02, // Usage (Mouse) + 0xa1, 0x01, // Collection (Application) + 0x85, 0x01, // Report ID (1) + 0x09, 0x01, // Usage (Pointer) + 0xa1, 0x00, // Collection (Physical) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (1) + 0x29, 0x05, // Usage Maximum (5) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x05, // Report Count (5) + 0x81, 0x02, // Input (Data,Var,Abs) + 0x75, 0x03, // Report Size (3) + 0x95, 0x01, // Report Count (1) + 0x81, 0x03, // Input (Cnst,Var,Abs) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x38, // Usage (Wheel) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7f, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8) + 0x95, 0x03, // Report Count (3) + 0x81, 0x06, // Input (Data,Var,Rel) + 0x05, 0x0c, // Usage Page (Consumer) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7f, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x0a, 0x38, 0x02, // Usage (AC Pan) + 0x81, 0x06, // Input (Data,Var,Rel) + 0xc0, // End Collection + 0xc0, // End Collection + 0x05, 0x0d, // Usage Page (Digitizers) + 0x09, 0x02, // Usage (Pen) + 0xa1, 0x01, // Collection (Application) + 0x85, 0x02, // Report ID (2) + 0x09, 0x20, // Usage (Stylus) + 0xa1, 0x00, // Collection (Physical) + 0x09, 0x00, // Usage (0x0000) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xff, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x09, // Report Count (9) + 0x81, 0x02, // Input (Data,Var,Abs) + 0x09, 0x3f, // Usage (Azimuth) + 0x09, 0x40, // Usage (Altitude) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xff, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x02, // Report Count (2) + 0xb1, 0x02, // Feature (Data,Var,Abs) + 0xc0, // End Collection + 0x85, 0x05, // Report ID (5) + 0x05, 0x0d, // Usage Page (Digitizers) + 0x09, 0x20, // Usage (Stylus) + 0xa1, 0x00, // Collection (Physical) + 0x09, 0x00, // Usage (0x0000) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xff, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x07, // Report Count (7) + 0x81, 0x02, // Input (Data,Var,Abs) + 0xc0, // End Collection + 0x85, 0x0a, // Report ID (10) + 0x05, 0x0d, // Usage Page (Digitizers) + 0x09, 0x20, // Usage (Stylus) + 0xa1, 0x00, // Collection (Physical) + 0x09, 0x00, // Usage (0x0000) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xff, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x07, // Report Count (7) + 0x81, 0x02, // Input (Data,Var,Abs) + 0xc0, // End Collection + 0x85, 0x10, // Report ID (16) + 0x09, 0x20, // Usage (Stylus) + 0xa1, 0x00, // Collection (Physical) + 0x09, 0x42, // Usage (Tip Switch) + 0x09, 0x44, // Usage (Barrel Switch) + 0x09, 0x3c, // Usage (Invert) + 0x09, 0x45, // Usage (Eraser) + 0x09, 0x32, // Usage (In Range) + 0x09, 0x5a, // Usage (Secondary Barrel Switch) <-= - added + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x06, // Report Count (6) <-= -- changed from 5 + 0x81, 0x02, // Input (Data,Var,Abs) + 0x95, 0x02, // Report Count (2) <-= -- changed from 3 + 0x81, 0x03, // Input (Cnst,Var,Abs) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (X) + 0x75, 0x10, // Report Size (16) + 0x95, 0x01, // Report Count (1) + 0xa4, // Push + 0x55, 0x0d, // Unit Exponent (-3) + 0x65, 0x33, // Unit (EnglishLinear: in=C2=B3) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0x00, 0x7d, // Logical Maximum (32000) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x00, 0x7d, // Physical Maximum (32000) + 0x81, 0x02, // Input (Data,Var,Abs) + 0x09, 0x31, // Usage (Y) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0x20, 0x4e, // Logical Maximum (20000) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x20, 0x4e, // Physical Maximum (20000) + 0x81, 0x02, // Input (Data,Var,Abs) + 0x05, 0x0d, // Usage Page (Digitizers) + 0x09, 0x30, // Usage (Tip Pressure) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xff, 0x07, // Logical Maximum (2047) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0xff, 0x07, // Physical Maximum (2047) + 0x81, 0x02, // Input (Data,Var,Abs) + 0x05, 0x0d, // Usage Page (Digitizers) + 0x09, 0x3d, // Usage (X Tilt) + 0x09, 0x3e, // Usage (Y Tilt) + 0x15, 0xc4, // Logical Minimum (-60) <-= changed from -127 + 0x25, 0x3c, // Logical Maximum (60) <-= changed from 127 + 0x75, 0x08, // Report Size (8) + 0x95, 0x02, // Report Count (2) + 0x81, 0x02, // Input (Data,Var,Abs) + 0xc0, // End Collection + 0xc0, // End Collection + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x06, // Usage (Keyboard) + 0xa1, 0x01, // Collection (Application) + 0x85, 0x0d, // Report ID (13) + 0x05, 0x07, // Usage Page (Keyboard/Keypad) + 0x19, 0xe0, // Usage Minimum (224) + 0x29, 0xe7, // Usage Maximum (231) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x08, // Report Count (8) + 0x81, 0x02, // Input (Data,Var,Abs) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x81, 0x01, // Input (Cnst,Arr,Abs) + 0x05, 0x07, // Usage Page (Keyboard/Keypad) + 0x19, 0x00, // Usage Minimum (0) + 0x29, 0x65, // Usage Maximum (101) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x65, // Logical Maximum (101) + 0x75, 0x08, // Report Size (8) + 0x95, 0x05, // Report Count (5) + 0x81, 0x00, // Input (Data,Arr,Abs) + 0xc0, // End Collection + 0x05, 0x0c, // Usage Page (Consumer) + 0x09, 0x01, // Usage (Consumer Control) + 0xa1, 0x01, // Collection (Application) + 0x85, 0x0c, // Report ID (12) + 0x09, 0xe9, // Usage (Volume Increment) + 0x09, 0xea, // Usage (Volume Decrement) + 0x09, 0xe2, // Usage (Mute) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x03, // Report Count (3) + 0x81, 0x06, // Input (Data,Var,Rel) + 0x75, 0x05, // Report Size (5) + 0x95, 0x01, // Report Count (1) + 0x81, 0x07, // Input (Cnst,Var,Rel) + 0xc0, // End Collection +}; + +static inline unsigned int bitwidth32(__u32 x) +{ + return 32 - __builtin_clzg(x, 32); +} + +static inline unsigned int floor_log2_32(__u32 x) +{ + return bitwidth32(x) - 1; +} + +/* Maps the interval [0, 2047] to itself using a scaled + * approximation of the function log2(x+1). + */ +static unsigned int scaled_log2(__u16 v) +{ + const unsigned int XMAX =3D 2047; + const unsigned int YMAX =3D 11; /* log2(2048) =3D 11 */ + + unsigned int x =3D v + 1; + unsigned int n =3D floor_log2_32(x); + unsigned int b =3D 1 << n; + + /* Fixed-point fraction in [0, 1), linearly + * interpolated using delta-y =3D 1 and + * delta-x =3D (2b - b) =3D b. + */ + unsigned int frac =3D (x - b) << YMAX; + unsigned int lerp =3D frac / b; + unsigned int log2 =3D (n << YMAX) + lerp; + + return ((log2 * XMAX) / YMAX) >> YMAX; +} + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx) +{ + __u8 *data =3D hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */); + + if (!data) + return 0; /* EPERM check */ + + __builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc)); + + return sizeof(fixed_rdesc); +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(waltop_fix_events, struct hid_bpf_ctx *hctx) +{ + __u8 *data =3D hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */); + + if (!data) + return 0; /* EPERM check */ + + __u8 report_id =3D data[0]; + + if (report_id !=3D PEN_REPORT_ID) + return 0; + + /* On this tablet if the secondary barrel switch is pressed, + * the tablet sends tip down and barrel down. Change this to + * just secondary barrel down when there is no ambiguity. + * + * It's possible that there is a bug in the firmware and the + * device intends to set invert + eraser instead (i.e. the + * pysical button is an eraser button) but since + * the pressure is always zero, said eraser button + * would be useless anyway. + * + * So let's just change the button to secondary barrel down. + */ + + __u8 tip_switch =3D data[1] & TIP_SWITCH; + __u8 barrel_switch =3D data[1] & BARREL_SWITCH; + + __u8 tip_held =3D last_button_state & TIP_SWITCH; + __u8 barrel_held =3D last_button_state & BARREL_SWITCH; + + if (tip_switch && barrel_switch && !tip_held && !barrel_held) { + data[1] &=3D ~(TIP_SWITCH | BARREL_SWITCH); /* release tip and barrel */ + data[1] |=3D SECONDARY_BARREL_SWITCH; /* set secondary barrel switch */ + } + + last_button_state =3D data[1]; + + /* The pressure sensor on this tablet maps around half of the + * logical pressure range into the interval [0-100]. Further + * pressure causes the sensor value to increase exponentially + * up to a maximum value of 2047. + * + * The values 12 and 102 were chosen to have an integer slope + * with smooth transition between the two curves around the + * value 100. + */ + + __u16 pressure =3D (((__u16)data[6]) << 0) | (((__u16)data[7]) << 8); + + if (pressure <=3D 102) + pressure *=3D 12; + else + pressure =3D scaled_log2(pressure); + + data[6] =3D pressure >> 0; + data[7] =3D pressure >> 8; + + return 0; +} + +HID_BPF_OPS(waltop_batteryless) =3D { + .hid_device_event =3D (void *)waltop_fix_events, + .hid_rdesc_fixup =3D (void *)hid_fix_rdesc, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + if (ctx->rdesc_size =3D=3D EXPECTED_RDESC_SIZE) + ctx->retval =3D 0; + else + ctx->retval =3D -EINVAL; + + return 0; +} + +char _license[] SEC("license") =3D "GPL"; --=20 2.51.1 From nobody Tue Dec 2 02:33:08 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 34177366DD5; Tue, 18 Nov 2025 17:16:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486213; cv=none; b=pDif1OknvYGVGaD5ozL4PR+g2epg/PQ5yKvbQjf9mcsKKTvvw0GvB+PcoCL2whqRzIRoBE/RKkt6js/LbAmiIG8B/J4dMo+Q+zdmFumcud2zOSUY3EWA8yWMYq2yQ+rRyW9aOB9xl3UjLXNq6KAa32C9JM8UwhWENii9Vi53Biw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486213; c=relaxed/simple; bh=URa2e2yAUNaYsYhv8sibtLI7zatZhUaCOzybqlxmwFo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=PG8fVQzovIniIkR6k4/kp/fAsZEtxshsWPuzOae2ka132TKmY0JOwnHmDQQn02ImwbkmcNJqOWL9399yGGCCgzewelHiVtG9D60kSWXcWVGHs123ZW8fOfzQnrNLSzMdFVjrd4UGuHFPsQoTetNlpdx1c6yWSkrkg+zXM1VMHts= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=AA795E2m; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="AA795E2m" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 84E9FC4CEF1; Tue, 18 Nov 2025 17:16:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763486212; bh=URa2e2yAUNaYsYhv8sibtLI7zatZhUaCOzybqlxmwFo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=AA795E2mX79txxkhN+LDNWnjwKJn8sJnZS3/0puEahwM4YMn8OzhfrPO7elHH/UQi lcuzc0p3o+gBlvUu+6CzdtMxujWKVcDTEuImaW/HZL6Rcs8MoCX/B3hEcDXzPZmt8V +Cl/mGcentb7zrzFEHWzVKPL+FdmwH1cDOtzJ0JyvG3CNR/l9mOTYUP744sm/ZpuUm i8z7zqjtusKvHPDS8jQltnXC4T7qPOPE38XqB4Wr++nUEP+yOBHz5ABeJArK3qPOzu Y++/zm7DtxtLV1jbbopKZntQufsXbmwKHWXZ8ZLSwAUOmbSTEljUFU1GAatR8CJkwn 0t6C6way6tXNQ== From: Benjamin Tissoires Date: Tue, 18 Nov 2025 18:16:27 +0100 Subject: [PATCH 06/10] HID: bpf: Add support for the XP-Pen Deco 01 V3 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251118-wip-sync-udev-hid-bpf-v1-6-0f8105c54835@kernel.org> References: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> In-Reply-To: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> To: Jiri Kosina Cc: linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, Benjamin Tissoires , Peter Hutterer X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763486192; l=15974; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=URa2e2yAUNaYsYhv8sibtLI7zatZhUaCOzybqlxmwFo=; b=xY5lYU9BkXyrphaykTGJTFQsPuY+ReZx2xHX2GALVYzIND2tnuGLwmW9yMr10FehlLr2wRDJ/ riyzYLywzriDHaQKRGifvTzZZVa0eJN7o6QXdkQuhReGiC0W0Yy36FG X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= This device needs a fix for the tilt range on the pen report descriptor and the usual conversion of the pad keys from the firmware's hardcoded keyboard shortcuts to actual pad buttons. Signed-off-by: Peter Hutterer Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests= /185 Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c | 305 ++++++++++++++++++++++++= ++++ 1 file changed, 305 insertions(+) diff --git a/drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c b/drivers/hid/bpf/= progs/XPPen__Deco01V3.bpf.c new file mode 100644 index 0000000000000000000000000000000000000000..2502fcc9ede69b856e2115a1530= 7096951953571 --- /dev/null +++ b/drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2025 Red Hat + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include "hid_report_helpers.h" +#include + +#define VID_UGEE 0x28BD /* VID is shared with SinoWealth and Glorious and = prob others */ +#define PID_DECO_01_V3 0x0947 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_DECO_01_V3), +); + +/* + * Default report descriptor reports: + * - a report descriptor for the pad buttons, reported as key sequences + * - a report descriptor for the pen + * - a vendor-specific report descriptor + * + * The Pad report descriptor, see + * https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/issues/54 + * + * # Report descriptor length: 102 bytes + * 0x05, 0x01, // Usage Page (Generic Desktop) = 0 + * 0x09, 0x02, // Usage (Mouse) = 2 + * 0xa1, 0x01, // Collection (Application) = 4 + * 0x85, 0x09, // Report ID (9) = 6 + * 0x09, 0x01, // Usage (Pointer) = 8 + * 0xa1, 0x00, // Collection (Physical) = 10 + * 0x05, 0x09, // Usage Page (Button) = 12 + * 0x19, 0x01, // UsageMinimum (1) = 14 + * 0x29, 0x03, // UsageMaximum (3) = 16 + * 0x15, 0x00, // Logical Minimum (0) = 18 + * 0x25, 0x01, // Logical Maximum (1) = 20 + * 0x95, 0x03, // Report Count (3) = 22 + * 0x75, 0x01, // Report Size (1) = 24 + * 0x81, 0x02, // Input (Data,Var,Abs) = 26 + * 0x95, 0x05, // Report Count (5) = 28 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) = 30 + * 0x05, 0x01, // Usage Page (Generic Desktop) = 32 + * 0x09, 0x30, // Usage (X) = 34 + * 0x09, 0x31, // Usage (Y) = 36 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) = 38 + * 0x95, 0x02, // Report Count (2) = 41 + * 0x75, 0x10, // Report Size (16) = 43 + * 0x81, 0x02, // Input (Data,Var,Abs) = 45 + * 0x05, 0x0d, // Usage Page (Digitizers) = 47 + * 0x09, 0x30, // Usage (Tip Pressure) = 49 + * 0x26, 0xff, 0x07, // Logical Maximum (2047) = 51 + * 0x95, 0x01, // Report Count (1) = 54 + * 0x75, 0x10, // Report Size (16) = 56 + * 0x81, 0x02, // Input (Data,Var,Abs) = 58 + * 0xc0, // End Collection = 60 + * 0xc0, // End Collection = 61 + * 0x05, 0x01, // Usage Page (Generic Desktop) = 62 + * 0x09, 0x06, // Usage (Keyboard) = 64 + * 0xa1, 0x01, // Collection (Application) = 66 + * 0x85, 0x06, // Report ID (6) = 68 + * 0x05, 0x07, // Usage Page (Keyboard/Keypad) = 70 + * 0x19, 0xe0, // UsageMinimum (224) = 72 + * 0x29, 0xe7, // UsageMaximum (231) = 74 + * 0x15, 0x00, // Logical Minimum (0) = 76 + * 0x25, 0x01, // Logical Maximum (1) = 78 + * 0x75, 0x01, // Report Size (1) = 80 + * 0x95, 0x08, // Report Count (8) = 82 + * 0x81, 0x02, // Input (Data,Var,Abs) = 84 + * 0x05, 0x07, // Usage Page (Keyboard/Keypad) = 86 + * 0x19, 0x00, // UsageMinimum (0) = 88 + * 0x29, 0xff, // UsageMaximum (255) = 90 + * 0x26, 0xff, 0x00, // Logical Maximum (255) = 92 + * 0x75, 0x08, // Report Size (8) = 95 + * 0x95, 0x06, // Report Count (6) = 97 + * 0x81, 0x00, // Input (Data,Arr,Abs) = 99 + * 0xc0, // End Collection = 101 + * + * And key events for buttons top->bottom are: + * Buttons released: 06 00 00 00 00 00 00 00 + * Button1: 06 00 05 00 00 00 00 00 -> b + * Button2: 06 00 08 00 00 00 00 00 -> e + * Button3: 06 04 00 00 00 00 00 00 -> LAlt + * Button4: 06 00 2c 00 00 00 00 00 -> Space + * Button5: 06 01 16 00 00 00 00 00 -> LControl + s + * Button6: 06 01 1d 00 00 00 00 00 -> LControl + z + * Button7: 06 01 57 00 00 00 00 00 -> LControl + Keypad Plus + * Button8: 06 01 56 00 00 00 00 00 -> LControl + Keypad Dash + * + * When multiple buttons are pressed at the same time, the values used to + * identify the buttons are identical, but they appear in different bytes = of the + * record. For example, when button 2 (0x08) and button 1 (0x05) are press= ed, + * this is the report: + * + * Buttons 2 and 1: 06 00 08 05 00 00 00 00 -> e + b + * + * Buttons 1, 2, 4, 5 and 6 can be matched by finding their values in the + * report. + * + * Button 3 is pressed when the 3rd bit is 1. For example, pressing button= s 3 + * and 5 generates this report: + * + * Buttons 3 and 5: 06 05 16 00 00 00 00 00 -> LControl + LAlt + s + * -- -- + * | | + * | `- Button 5 (0x16) + * `- 0x05 =3D 0101. Button 3 is pressed + * ^ + * + * pad_buttons contains a list of buttons that can be matched in + * HID_BPF_DEVICE_EVENT. Button 3 as it has a dedicated bit. + * + * + * The Pen report descriptor announces a wrong tilt range: + * + * Report descriptor length: 109 bytes + * 0x05, 0x0d, // Usage Page (Digitizers) = 0 + * 0x09, 0x02, // Usage (Pen) = 2 + * 0xa1, 0x01, // Collection (Application) = 4 + * 0x85, 0x07, // Report ID (7) = 6 + * 0x09, 0x20, // Usage (Stylus) = 8 + * 0xa1, 0x01, // Collection (Application) = 10 + * 0x09, 0x42, // Usage (Tip Switch) = 12 + * 0x09, 0x44, // Usage (Barrel Switch) = 14 + * 0x09, 0x45, // Usage (Eraser) = 16 + * 0x09, 0x3c, // Usage (Invert) = 18 + * 0x15, 0x00, // Logical Minimum (0) = 20 + * 0x25, 0x01, // Logical Maximum (1) = 22 + * 0x75, 0x01, // Report Size (1) = 24 + * 0x95, 0x04, // Report Count (4) = 26 + * 0x81, 0x02, // Input (Data,Var,Abs) = 28 + * 0x95, 0x01, // Report Count (1) = 30 + * 0x81, 0x03, // Input (Cnst,Var,Abs) = 32 + * 0x09, 0x32, // Usage (In Range) = 34 + * 0x95, 0x01, // Report Count (1) = 36 + * 0x81, 0x02, // Input (Data,Var,Abs) = 38 + * 0x95, 0x02, // Report Count (2) = 40 + * 0x81, 0x03, // Input (Cnst,Var,Abs) = 42 + * 0x75, 0x10, // Report Size (16) = 44 + * 0x95, 0x01, // Report Count (1) = 46 + * 0x35, 0x00, // Physical Minimum (0) = 48 + * 0xa4, // Push = 50 + * 0x05, 0x01, // Usage Page (Generic Desktop) = 51 + * 0x09, 0x30, // Usage (X) = 53 + * 0x65, 0x13, // Unit (EnglishLinear: in) = 55 + * 0x55, 0x0d, // Unit Exponent (-3) = 57 + * 0x46, 0x10, 0x27, // Physical Maximum (10000) = 59 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) = 62 + * 0x81, 0x02, // Input (Data,Var,Abs) = 65 + * 0x09, 0x31, // Usage (Y) = 67 + * 0x46, 0x6a, 0x18, // Physical Maximum (6250) = 69 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) = 72 + * 0x81, 0x02, // Input (Data,Var,Abs) = 75 + * 0xb4, // Pop = 77 + * 0x09, 0x30, // Usage (X) = 78 + * 0x45, 0x00, // Physical Maximum (0) = 80 + * 0x26, 0xff, 0x3f, // Logical Maximum (16383) = 82 + * 0x81, 0x42, // Input (Data,Var,Abs,Null) = 85 + * 0x09, 0x3d, // Usage (Start) = 87 + * 0x15, 0x81, // Logical Minimum (-127) = 89 <- Change from -127 to -60 + * 0x25, 0x7f, // Logical Maximum (127) = 91 <- Change from 127 to 60 + * 0x75, 0x08, // Report Size (8) = 93 + * 0x95, 0x01, // Report Count (1) = 95 + * 0x81, 0x02, // Input (Data,Var,Abs) = 97 + * 0x09, 0x3e, // Usage (Select) = 99 + * 0x15, 0x81, // Logical Minimum (-127) = 101 <- Change from -127 to -60 + * 0x25, 0x7f, // Logical Maximum (127) = 103 <- Change from 127 to 60 + * 0x81, 0x02, // Input (Data,Var,Abs) = 105 + * 0xc0, // End Collection = 107 + * 0xc0, // End Collection = 108 + */ + +#define PEN_REPORT_DESCRIPTOR_LENGTH 109 +#define PAD_REPORT_DESCRIPTOR_LENGTH 102 +#define PAD_REPORT_LENGTH 8 +#define PAD_REPORT_ID 6 +#define PAD_NUM_BUTTONS 8 + +static const __u8 fixed_rdesc_pad[] =3D { + UsagePage_GenericDesktop + Usage_GD_Keypad + CollectionApplication( + // Byte 0 in report is the report ID + ReportId(PAD_REPORT_ID) + ReportCount(1) + ReportSize(8) + UsagePage_Digitizers + Usage_Dig_TabletFunctionKeys + CollectionPhysical( + // Byte 1 is the button state + UsagePage_Button + UsageMinimum_i8(0x01) + UsageMaximum_i8(PAD_NUM_BUTTONS) + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) + ReportCount(PAD_NUM_BUTTONS) + ReportSize(1) + Input(Var|Abs) + // Byte 2 in report - just exists so we get to be a tablet pad + UsagePage_Digitizers + Usage_Dig_BarrelSwitch // BTN_STYLUS + ReportCount(1) + ReportSize(1) + Input(Var|Abs) + ReportCount(7) // padding + Input(Const) + // Bytes 3/4 in report - just exists so we get to be a tablet pad + UsagePage_GenericDesktop + Usage_GD_X + Usage_GD_Y + ReportCount(2) + ReportSize(8) + Input(Var|Abs) + // Byte 5-7 are padding so we match the original report lengtth + ReportCount(3) + ReportSize(8) + Input(Const) + ) + ) +}; + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(xppen_deco01v3_rdesc_fixup, struct hid_bpf_ctx *hctx) +{ + __u8 *data =3D hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_= SIZE /* size */); + + const __u8 wrong_logical_range[] =3D {0x15, 0x81, 0x25, 0x7f}; + const __u8 correct_logical_range[] =3D {0x15, 0xc4, 0x25, 0x3c}; + + if (!data) + return 0; /* EPERM check */ + + switch (hctx->size) { + case PAD_REPORT_DESCRIPTOR_LENGTH: + __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad)); + return sizeof(fixed_rdesc_pad); + case PEN_REPORT_DESCRIPTOR_LENGTH: + if (__builtin_memcmp(&data[89], wrong_logical_range, + sizeof(wrong_logical_range)) =3D=3D 0) + __builtin_memcpy(&data[89], correct_logical_range, + sizeof(correct_logical_range)); + if (__builtin_memcmp(&data[101], wrong_logical_range, + sizeof(wrong_logical_range)) =3D=3D 0) + __builtin_memcpy(&data[101], correct_logical_range, + sizeof(correct_logical_range)); + break; + } + + return 0; +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(xppen_deco01v3_device_event, struct hid_bpf_ctx *hctx) +{ + static const __u8 pad_buttons[] =3D { 0x05, 0x08, 0x00, 0x2c, 0x16, 0x1d,= 0x57, 0x56 }; + __u8 *data =3D hid_bpf_get_data(hctx, 0 /* offset */, PAD_REPORT_LENGTH /= * size */); + + if (!data) + return 0; /* EPERM check */ + + if (data[0] =3D=3D PAD_REPORT_ID) { + __u8 button_mask =3D 0; + size_t d, b; + + /* data[1] stores the status of BTN_2 in the 3rd bit*/ + if (data[1] & BIT(2)) + button_mask |=3D BIT(2); + + /* The rest of the descriptor stores the buttons as in pad_buttons */ + for (d =3D 2; d < 8; d++) { + for (b =3D 0; b < sizeof(pad_buttons); b++) { + if (data[d] !=3D 0 && data[d] =3D=3D pad_buttons[b]) + button_mask |=3D BIT(b); + } + } + + __u8 report[8] =3D {PAD_REPORT_ID, button_mask, 0x00}; + + __builtin_memcpy(data, report, sizeof(report)); + } + return 0; +} + +HID_BPF_OPS(xppen_deco01v3) =3D { + .hid_rdesc_fixup =3D (void *)xppen_deco01v3_rdesc_fixup, + .hid_device_event =3D (void *)xppen_deco01v3_device_event, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + switch (ctx->rdesc_size) { + case PAD_REPORT_DESCRIPTOR_LENGTH: + case PEN_REPORT_DESCRIPTOR_LENGTH: + ctx->retval =3D 0; + break; + default: + ctx->retval =3D -EINVAL; + } + + return 0; +} + +char _license[] SEC("license") =3D "GPL"; --=20 2.51.1 From nobody Tue Dec 2 02:33:08 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B4CBF393DE3; Tue, 18 Nov 2025 17:16:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486215; cv=none; b=n5O++9wDPkObdyiK/OTqGj7ITXTQ35WbA9rf3zDdNAmVqIClHV+LsFxXsuiwI4qlQZwmDmkUcAfE9AtfE7vodkgf+8N98lMPhuTz5riVSC30Is2PNwKfhDaln7abmaXWJ+GEZEcwbzsTOsAK1bjwgi34o3dFN9c7syI/YJslcmY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486215; c=relaxed/simple; bh=ikD1utO8ElymAeYf14LmzSmYqoHptPAX/7NxcDlmVJg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=uf0O7/Fnf+UKO9t1BBUHODARYgfintHF9Ffz8WKwYLkItPzwkogBOZFC76aIeJ5l/q7MV67tqG8ZfsqY0tNOjTFwZD2RStQ2hSgoLzhFg7dR0ALmGpWOnlccC3zw933/pdCEhThm85SLRfZGAQY6FelHJtUEwhs4Ed/kMvDRI84= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=UtQAnDpj; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="UtQAnDpj" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4E2FEC4CEF5; Tue, 18 Nov 2025 17:16:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763486215; bh=ikD1utO8ElymAeYf14LmzSmYqoHptPAX/7NxcDlmVJg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=UtQAnDpjZ/8asGL3qXcNiwzE8twaHInpRnIUlqePxKv6FKz7dM71RwMVsoTESO1vo 2cobsOh/z3L6q38rjMytFgMFQJ1RLfzcTKnAhsQ0EGbFCnxQU6GIXwMpBAsELWfz34 hF3G3oIvs7bzaj/aoiFGwrJELQqdgydWFVE4veZsB3w/a3Nx3q8EHHy7YViQYhs5rT CeU4MFLACmB8owoEqpJ4U4GBrJXuKH+lqcRa+llLWfuzSMjtiM0SlbNEV0DJnWESc+ NXZnsUk0ShRdHSWVNyadZmrCPUXYE02zmAerc6dMAqqAkblhfoaYNSfF1ojDduHMze yyus5xYptJRFQ== From: Benjamin Tissoires Date: Tue, 18 Nov 2025 18:16:28 +0100 Subject: [PATCH 07/10] HID: bpf: Add support for XP-Pen Deco02 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251118-wip-sync-udev-hid-bpf-v1-7-0f8105c54835@kernel.org> References: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> In-Reply-To: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> To: Jiri Kosina Cc: linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, Benjamin Tissoires , Hannah Pittman X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763486193; l=16016; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=ikD1utO8ElymAeYf14LmzSmYqoHptPAX/7NxcDlmVJg=; b=XG0iz7vLp+kzzc7WK32mr/GIRL7XD0fwSc+XrWINZ+odKmsNSGi+pmp7hRbz3cCwepKAtd171 MAafGcTdJvhAaU9WrdLMYgbsWq9bHlf/uFD+5bOJvQg3FpKfQ9jGE91 X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= Modifies report to have tablet buttons report as buttons, rather than as keyboard key combinations. The dial is also converted to a relative input, using the dedicated bit previously reserved for modifier key information. Signed-off-by: Hannah Pittman Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests= /203 Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/progs/XPPen__Deco02.bpf.c | 359 ++++++++++++++++++++++++++= ++++ 1 file changed, 359 insertions(+) diff --git a/drivers/hid/bpf/progs/XPPen__Deco02.bpf.c b/drivers/hid/bpf/pr= ogs/XPPen__Deco02.bpf.c new file mode 100644 index 0000000000000000000000000000000000000000..4b2549031e56daf7e905db4ee1e= 59fcfdd8d4dae --- /dev/null +++ b/drivers/hid/bpf/progs/XPPen__Deco02.bpf.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include "hid_report_helpers.h" +#include + +#define VID_UGEE 0x28BD +#define PID_DECO_02 0x0803 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_DECO_02), +); + +/* + * Devices are: + * - Pad input, including pen (This is the only one we are interested in) + * - Pen input as mouse + * - Vendor + * + * Descriptors on main device are: + * - 7: Pen + * - 6: Vendor settings? Unclear + * - 3: Keyboard (This is what we want to modify) + * - 5: Feature report + * + * This creates three event nodes: + * - XP-PEN DECO 02 Stylus + * - XP-PEN DECO 02 + * - XP-PEN DECO 02 Keyboard (Again, what we want to modify) + * + * # Report descriptor length: 188 bytes + * # 0x05, 0x0d, // Usage Page (Digitizers) = 0 + * # 0x09, 0x02, // Usage (Pen) = 2 + * # 0xa1, 0x01, // Collection (Application) = 4 + * # 0x85, 0x07, // Report ID (7) = 6 + * # 0x09, 0x20, // Usage (Stylus) = 8 + * # 0xa1, 0x00, // Collection (Physical) = 10 + * # 0x09, 0x42, // Usage (Tip Switch) = 12 + * # 0x09, 0x44, // Usage (Barrel Switch) = 14 + * # 0x09, 0x45, // Usage (Eraser) = 16 + * # 0x09, 0x3c, // Usage (Invert) = 18 + * # 0x09, 0x32, // Usage (In Range) = 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 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 30 + * # 0x95, 0x03, // Report Count (3) = 32 + * # 0x81, 0x03, // Input (Cnst,Var,Abs) = 34 + * # 0x05, 0x01, // Usage Page (Generic Desktop) = 36 + * # 0x09, 0x30, // Usage (X) = 38 + * # 0x15, 0x00, // Logical Minimum (0) = 40 + * # 0x26, 0x50, 0x57, // Logical Maximum (22352) = 42 + * # 0x55, 0x0d, // Unit Exponent (-3) = 45 + * # 0x65, 0x13, // Unit (EnglishLinear: in) = 47 + * # 0x35, 0x00, // Physical Minimum (0) = 49 + * # 0x46, 0x50, 0x57, // Physical Maximum (22352) = 51 + * # 0x75, 0x10, // Report Size (16) = 54 + * # 0x95, 0x01, // Report Count (1) = 56 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 58 + * # 0x09, 0x31, // Usage (Y) = 60 + * # 0x15, 0x00, // Logical Minimum (0) = 62 + * # 0x26, 0x92, 0x36, // Logical Maximum (13970) = 64 + * # 0x55, 0x0d, // Unit Exponent (-3) = 67 + * # 0x65, 0x13, // Unit (EnglishLinear: in) = 69 + * # 0x35, 0x00, // Physical Minimum (0) = 71 + * # 0x46, 0x92, 0x36, // Physical Maximum (13970) = 73 + * # 0x75, 0x10, // Report Size (16) = 76 + * # 0x95, 0x01, // Report Count (1) = 78 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 80 + * # 0x05, 0x0d, // Usage Page (Digitizers) = 82 + * # 0x09, 0x30, // Usage (Tip Pressure) = 84 + * # 0x15, 0x00, // Logical Minimum (0) = 86 + * # 0x26, 0xff, 0x1f, // Logical Maximum (8191) = 88 + * # 0x75, 0x10, // Report Size (16) = 91 + * # 0x95, 0x01, // Report Count (1) = 93 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 95 + * # 0xc0, // End Collection = 97 + * # 0xc0, // End Collection = 98 + * # 0x09, 0x0e, // Usage (Device Configuration) = 99 + * # 0xa1, 0x01, // Collection (Application) = 101 + * # 0x85, 0x05, // Report ID (5) = 103 + * # 0x09, 0x23, // Usage (Device Settings) = 105 + * # 0xa1, 0x02, // Collection (Logical) = 107 + * # 0x09, 0x52, // Usage (Inputmode) = 109 + * # 0x09, 0x53, // Usage (Device Index) = 111 + * # 0x25, 0x0a, // Logical Maximum (10) = 113 + * # 0x75, 0x08, // Report Size (8) = 115 + * # 0x95, 0x02, // Report Count (2) = 117 + * # 0xb1, 0x02, // Feature (Data,Var,Abs) = 119 + * # 0xc0, // End Collection = 121 + * # 0xc0, // End Collection = 122 + * # 0x05, 0x0c, // Usage Page (Consumer Devices) = 123 + * # 0x09, 0x36, // Usage (Function Buttons) = 125 + * # 0xa1, 0x00, // Collection (Physical) = 127 + * # 0x85, 0x06, // Report ID (6) = 129 + * # 0x05, 0x09, // Usage Page (Button) = 131 + * # 0x19, 0x01, // Usage Minimum (1) = 133 + * # 0x29, 0x20, // Usage Maximum (32) = 135 + * # 0x15, 0x00, // Logical Minimum (0) = 137 + * # 0x25, 0x01, // Logical Maximum (1) = 139 + * # 0x95, 0x20, // Report Count (32) = 141 + * # 0x75, 0x01, // Report Size (1) = 143 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 145 + * # 0xc0, // End Collection = 147 + * # 0x05, 0x01, // Usage Page (Generic Desktop) = 148 + * # 0x09, 0x06, // Usage (Keyboard) = 150 + * # 0xa1, 0x01, // Collection (Application) = 152 + * # 0x85, 0x03, // Report ID (3) = 154 + * # 0x05, 0x07, // Usage Page (Keyboard) = 156 + * # 0x19, 0xe0, // Usage Minimum (224) = 158 + * # 0x29, 0xe7, // Usage Maximum (231) = 160 + * # 0x15, 0x00, // Logical Minimum (0) = 162 + * # 0x25, 0x01, // Logical Maximum (1) = 164 + * # 0x75, 0x01, // Report Size (1) = 166 + * # 0x95, 0x08, // Report Count (8) = 168 + * # 0x81, 0x02, // Input (Data,Var,Abs) = 170 + * # 0x05, 0x07, // Usage Page (Keyboard) = 172 + * # 0x19, 0x00, // Usage Minimum (0) = 174 + * # 0x29, 0xff, // Usage Maximum (255) = 176 + * # 0x26, 0xff, 0x00, // Logical Maximum (255) = 178 + * # 0x75, 0x08, // Report Size (8) = 181 + * # 0x95, 0x06, // Report Count (6) = 183 + * # 0x81, 0x00, // Input (Data,Arr,Abs) = 185 + * # 0xc0, // End Collection = 187 + * + * Key events; top to bottom: + * Buttons released: 03 00 00 00 00 00 00 00 + * Button1: 03 00 05 00 00 00 00 00 -> 'b and B' + * Button2: 03 00 2c 00 00 00 00 00 -> 'Spacebar' + * Button3: 03 00 08 00 00 00 00 00 -> 'e and E' + * Button4: 03 00 0c 00 00 00 00 00 -> 'i and I' + * Button5: 03 05 1d 00 00 00 00 00 -> LeftControl + LeftAlt + 'z= and Z' + * Button6: 03 01 16 00 00 00 00 00 -> LeftControl + 's and S' + * + * Dial Events: + * Clockwise: 03 01 2e 00 00 00 00 00 -> LeftControl + '=3D and +' + * Anticlockwise: 03 01 2d 00 00 00 00 00 -> LeftControl + '- and (unde= rscore)' + * + * NOTE: Input event descriptions begin at byte 2, and progressively build + * towards byte 7 as each new key is pressed maintaining the press order. + * For example: + * BTN1 followed by BTN2 is 03 00 05 2c 00 00 00 00 + * BTN2 followed by BTN1 is 03 00 2c 05 00 00 00 00 + * + * Releasing a button causes its byte to be freed, and the next item in th= e list + * is pushed forwards. Dial events are released immediately after an event= is + * registered (i.e. after each "click"), so will continually appear pushed + * backwards in the report. + * + * When a button with a modifier key is pressed, the button identifier sta= cks in + * an abnormal way, where the highest modifier byte always supersedes othe= rs. + * In these cases, the button with the higher modifier is always last. + * For example: + * BTN6 followed by BTN5 is 03 05 1d 16 00 00 00 00 + * BTN5 followed by BTN6 is 03 05 1d 16 00 00 00 00 + * BTN5 followed by BTN1 is 03 05 05 1d 00 00 00 00 + * + * For three button presses in order, demonstrating strictly above rules: + * BTN6, BTN1, BTN5 is 03 05 05 1d 16 00 00 00 + * BTN5, BTN1, BTN6 is 03 05 05 1d 16 00 00 00 + * + * In short, when BTN5/6 are pressed, the order of operations is lost, as = they + * will always float to the end when pressed in combination with others. + * + * Fortunately, all states are recorded in the same way, with no overlaps. + * Byte 1 can be used as a spare for the wheel, since this is for mod keys. + */ + +#define RDESC_SIZE_PAD 188 +#define REPORT_SIZE_PAD 8 +#define REPORT_ID_BUTTONS 3 +#define PAD_BUTTON_COUNT 6 +#define RDESC_KEYBOARD_OFFSET 148 + +static const __u8 fixed_rdesc_pad[] =3D { + /* Copy of pen descriptor to avoid losing functionality */ + UsagePage_Digitizers + Usage_Dig_Pen + CollectionApplication( + ReportId(7) + Usage_Dig_Stylus + CollectionPhysical( + Usage_Dig_TipSwitch + Usage_Dig_BarrelSwitch + Usage_Dig_Eraser + Usage_Dig_Invert + Usage_Dig_InRange + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + ReportSize(1) + ReportCount(5) + Input(Var|Abs) + ReportCount(3) + Input(Const) /* Input (Const, Var, Abs) */ + UsagePage_GenericDesktop + Usage_GD_X + LogicalMinimum_i16(0) + LogicalMaximum_i16(22352) + UnitExponent(-3) + Unit(in) /* (EnglishLinear: in) */ + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(22352) + ReportSize(16) + ReportCount(1) + Input(Var|Abs) + Usage_GD_Y + LogicalMinimum_i16(0) + LogicalMaximum_i16(13970) + UnitExponent(-3) + Unit(in) /* (EnglishLinear: in) */ + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(13970) + ReportSize(16) + ReportCount(1) + Input(Var|Abs) + UsagePage_Digitizers + Usage_Dig_TipPressure + LogicalMinimum_i16(0) + LogicalMaximum_i16(8191) + ReportSize(16) + ReportCount(1) + Input(Var|Abs) + ) + ) + + /* FIXES BEGIN */ + UsagePage_GenericDesktop + Usage_GD_Keypad + CollectionApplication( + ReportId(REPORT_ID_BUTTONS) /* Retain original ID on byte 0 */ + ReportCount(1) + ReportSize(REPORT_SIZE_PAD) + UsagePage_Digitizers + Usage_Dig_TabletFunctionKeys + CollectionPhysical( + /* Byte 1: Dial state */ + UsagePage_GenericDesktop + Usage_GD_Dial + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) + ReportCount(1) + ReportSize(REPORT_SIZE_PAD) + Input(Var|Rel) + /* Byte 2: Button state */ + UsagePage_Button + ReportSize(1) + ReportCount(PAD_BUTTON_COUNT) + UsageMinimum_i8(0x01) + UsageMaximum_i8(PAD_BUTTON_COUNT) /* Number of buttons */ + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) + Input(Var|Abs) + /* Byte 3: Exists to be tablet pad */ + UsagePage_Digitizers + Usage_Dig_BarrelSwitch + ReportCount(1) + ReportSize(1) + Input(Var|Abs) + ReportCount(7) /* Padding, to fill full report space */ + Input(Const) + /* Byte 4/5: Exists to be a tablet pad */ + UsagePage_GenericDesktop + Usage_GD_X + Usage_GD_Y + ReportCount(2) + ReportSize(8) + Input(Var|Abs) + /* Bytes 6/7: Padding, to match original length */ + ReportCount(2) + ReportSize(8) + Input(Const) + ) + FixedSizeVendorReport(RDESC_SIZE_PAD) + ) +}; + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(xppen_deco02_rdesc_fixup, struct hid_bpf_ctx *hctx) +{ + __u8 *data =3D hid_bpf_get_data(hctx, 0, HID_MAX_DESCRIPTOR_SIZE); + + if (!data) + return 0; /* EPERM Check */ + + if (hctx->size =3D=3D RDESC_SIZE_PAD) { + __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad)); + return sizeof(fixed_rdesc_pad); + } + + return 0; +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(xppen_deco02_device_event, struct hid_bpf_ctx *hctx) +{ + __u8 *data =3D hid_bpf_get_data(hctx, 0, REPORT_SIZE_PAD); + + if (!data || data[0] !=3D REPORT_ID_BUTTONS) + return 0; /* EPERM or wrong report */ + + __u8 dial_code =3D 0; + __u8 button_mask =3D 0; + size_t d; + + /* Start from 2; 0 is report ID, 1 is modifier keys, replaced by dial */ + for (d =3D 2; d < 8; d++) { + switch (data[d]) { + case 0x2e: + dial_code =3D 1; + break; + case 0x2d: + dial_code =3D -1; + break; + /* below are buttons, top to bottom */ + case 0x05: + button_mask |=3D BIT(0); + break; + case 0x2c: + button_mask |=3D BIT(1); + break; + case 0x08: + button_mask |=3D BIT(2); + break; + case 0x0c: + button_mask |=3D BIT(3); + break; + case 0x1d: + button_mask |=3D BIT(4); + break; + case 0x16: + button_mask |=3D BIT(05); + break; + default: + break; + } + } + + __u8 report[8] =3D { REPORT_ID_BUTTONS, dial_code, button_mask, 0x00 }; + + __builtin_memcpy(data, report, sizeof(report)); + return 0; +} + +HID_BPF_OPS(xppen_deco02) =3D { + .hid_rdesc_fixup =3D (void *)xppen_deco02_rdesc_fixup, + .hid_device_event =3D (void *)xppen_deco02_device_event, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + ctx->retval =3D ctx->rdesc_size !=3D RDESC_SIZE_PAD ? -EINVAL : 0; + return 0; +} + +char _license[] SEC("license") =3D "GPL"; --=20 2.51.1 From nobody Tue Dec 2 02:33:08 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5914E3A9C04; Tue, 18 Nov 2025 17:16:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486218; cv=none; b=AXW/GKvrjMopPhzfY3EnRfjtI2GFRpLySfIhgytnwJNp0/00NvEgcpA7iPtZbeVHRJ5SndUEke0w6i89GGkNfiBuZfT/JSvv/tOQryYogStiWUuQ+Xl8bpOnCXOT4Hu8JqUa9OeRUxNmTJmWipFvhPwDkttYe0O7J2NTJOMDuxI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486218; c=relaxed/simple; bh=zqHDSso1zbAMBvwDevfZJfvdkTIsD8J5jTjG37l7Qo0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qo2zaVtDnDFOOFFDnoJWjmrIwS6m/h4N9i6aqXXuIT5TSUd0FUCHDVzITcSLPd8O3j3a2QDtF+97Gaso0vs6TldDbOfNyJ9wbtdHSXcFuwkeNusddF8LnOP8jpZM2KEEJU5qQeJXcEyxKOHXRdbduLO4ScHjc/28CaPzfOIs49A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WHoWDQ6t; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WHoWDQ6t" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D6C62C2BCB1; Tue, 18 Nov 2025 17:16:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763486217; bh=zqHDSso1zbAMBvwDevfZJfvdkTIsD8J5jTjG37l7Qo0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=WHoWDQ6tMXQLUiURLeKoWX7FNx0sH1OjOzT+HfjbTlrvvFHaKzEegw8LwkQQbV/ld yrnCnaLRKo89b0K1dxEs4269jcRLt8fP+UCd7bAqtgDGbb2lrcYw6qMoOyJtJCO3ZQ 5lmBQJFlVvbLrhUsP3shAB+2AAAu237go/lYzp7e9ahJ9Y9vSEqY0KuQMQRpsj8mJd d0GEMgjE1+e9MRMRJAMx5T1cwX77fbLFW93FkuqoX3fSME3NRkhTqqwSA6KKKFGiN1 hWUTpovwoGCcJQiUzYzoCeBPEAlvJ0b7+Pldlzm8paJWdZYLDInw9BRNO+t2sY3/qk 0v7S4jOA4eDfg== From: Benjamin Tissoires Date: Tue, 18 Nov 2025 18:16:29 +0100 Subject: [PATCH 08/10] HID: bpf: add heuristics to the Huion Inspiroy 2S eraser button Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251118-wip-sync-udev-hid-bpf-v1-8-0f8105c54835@kernel.org> References: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> In-Reply-To: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> To: Jiri Kosina Cc: linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, Benjamin Tissoires , Peter Hutterer X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763486193; l=2512; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=zqHDSso1zbAMBvwDevfZJfvdkTIsD8J5jTjG37l7Qo0=; b=EPmK8rAezKLSCsHgMkY3kJYw75D+DsNbuMeGwKQ+eViGEHeVqbNESyaWQ5C2NA/TSEqtXn7sq 6fId+oMAOusBtlOpjgaaZ+PHDTEwlA6/KMva3xwJwhKnSoWYTml4p3M X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= When pressing the phsyical eraser button (remapped by us to the Secondary Barrel Switch) while the tip is down, the device gives us several false reports with a Tip Switch 0: press| |release SBS: [0 0 ... 1 1 1 ... 1 0 0 0 0 0 0 ...] TS: [1 1 ... 1 0 1 ... 1 1 0 0 0 1 1 ...] In both press/release the number of Tip Switch 0 reports can be up to 4 and *sometimes* the Tip Switch is released in the same report as the button press/release event. Paper over this by forcing the tip down for a few reports if it was down before the button toggled. Signed-off-by: Peter Hutterer Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests= /195 Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c | 29 +++++++++++++++++++++= ++-- 1 file changed, 27 insertions(+), 2 deletions(-) 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 13f64fb49800b16f6d4d48c378f065fcdf51202a..79453362bf979f42559d6c57e96= 41a27cda0af40 100644 --- a/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c +++ b/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c @@ -163,6 +163,9 @@ char EXPECTED_FIRMWARE_ID[] =3D "HUION_T21j_"; =20 =20 __u8 last_button_state; +__u8 last_tip_state; +__u8 last_sec_barrel_state; +__u8 force_tip_down_count; =20 static const __u8 fixed_rdesc_pad[] =3D { UsagePage_GenericDesktop @@ -522,9 +525,31 @@ int BPF_PROG(inspiroy_2_fix_events, struct hid_bpf_ctx= *hctx) pad_report->wheel =3D wheel; =20 return sizeof(struct pad_report); - } + } else if (data[1] & 0x80) { /* Pen reports with InRange 1 */ + __u8 tip_state =3D data[1] & 0x1; + __u8 sec_barrel_state =3D data[1] & 0x4; + + if (force_tip_down_count > 0) { + data[1] |=3D 0x1; + --force_tip_down_count; + if (tip_state) + force_tip_down_count =3D 0; + } =20 - /* Pen reports need nothing done */ + /* Tip was down and we just pressed or released the + * secondary barrel switch (the physical eraser + * button). The device will send up to 4 + * reports with Tip Switch 0 and sometimes + * this report has Tip Switch 0. + */ + if (last_tip_state && + last_sec_barrel_state !=3D sec_barrel_state) { + force_tip_down_count =3D 4; + data[1] |=3D 0x1; + } + last_tip_state =3D tip_state; + last_sec_barrel_state =3D sec_barrel_state; + } } =20 return 0; --=20 2.51.1 From nobody Tue Dec 2 02:33:08 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C4D233AA185; Tue, 18 Nov 2025 17:17:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486220; cv=none; b=JjpM+pHCBxaii9D/XCLU7ixtkY0hulzhDvyR4MoZroPjh+Fd/yFrIA0IAyrYFkjFRo78zjP4OYwfBpmuLzz6RojDTNimW12dpgQeLxN/gtHM3cPwPCeuo41q12Md+LZy2srnIU6kcViKqgB1BiqroBz6l+rp32IMFdTtYx2pIYo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486220; c=relaxed/simple; bh=LatW49ws46HDBvfzOY1y4K96RPgnuMSTYcEnI7aQVs0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=a5fblWVla8SpMmsVsW7RsWCfaiZE/66mB/zwMS8VsZ/LYkkeq3CVBg0ULODEvpDE2HVsxPzjJSgOVBwD/w3SBd/HPZwWeHtlcgEJr+NnfqOSynZH+a/bK9ly9jCOM2J7xXou1RLmOicS2Ias4uEcY9H/t8LuFteBwDyAQd/C1d0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Ssomm/iQ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Ssomm/iQ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 6A8A1C2BCB8; Tue, 18 Nov 2025 17:16:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763486220; bh=LatW49ws46HDBvfzOY1y4K96RPgnuMSTYcEnI7aQVs0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=Ssomm/iQBRsjopdba4r2Jr9JPizHd+RoqJ+YvldCrmEnmeOqyz4wBqy+jb77LwUol Cncf/Th+ZtjE2OrcjjUlnEQfvbZHALKGu5SBZBwICqdmHGXjGIlSBCJJT+aRKlfZ0F /8810TPPl2OwMbhDP+H6Q/01yGHOuvWi8i/LWGXuBNy8S4ZE+yc7mkvXv6W34fS3gp KR6Ro9rzikiDLiov6/xZfqeimT3hfsJM8pEbUH2qaX3TivQBtzQiWlbdd2GsHWk78p DR9BuQeYkWAw+JnOsnMv4v8YZwqv+5nrRmyR3aHQMMz2X1j2/1/5Xytix2OBLRdn8e +zf/q/AfUQIOw== From: Benjamin Tissoires Date: Tue, 18 Nov 2025 18:16:30 +0100 Subject: [PATCH 09/10] HID: bpf: add the Huion Kamvas 27 Pro Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251118-wip-sync-udev-hid-bpf-v1-9-0f8105c54835@kernel.org> References: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> In-Reply-To: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> To: Jiri Kosina Cc: linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, Benjamin Tissoires , Peter Hutterer X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763486193; l=1813; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=LatW49ws46HDBvfzOY1y4K96RPgnuMSTYcEnI7aQVs0=; b=9AvYeGYz8edWI+enthjyrFJvB6dfu/e018OId79nMHhi4k7/oDeaVNnRn7FItVoc422kTFT8B kIGRZ68msx2BeMCF3bF8S/T1Il5bJfDV+bG7jaNssFsoVnWe2Y9XZvQ X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= Same issues with a secondary tip switch instead of secondary barrel switch as the Kamvas 19. Copy the stable Kamvas 19 support back into testing and add the vid/pid for the Kamvas 27. Signed-off-by: Peter Hutterer Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests= /189 Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c b/drivers/hid= /bpf/progs/Huion__Kamvas-Pro-19.bpf.c index 489cb4fcc2cd2702e4d996ad9435ba0ca454b6d9..5f43e407184854d860eeed4e857= acee8bcbd272d 100644 --- a/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c +++ b/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c @@ -9,12 +9,15 @@ =20 #define VID_HUION 0x256C #define PID_KAMVAS_PRO_19 0x006B +#define PID_KAMVAS_PRO_27 0x006c #define NAME_KAMVAS_PRO_19 "HUION Huion Tablet_GT1902" +#define NAME_KAMVAS_PRO_27 "HUION Huion Tablet_GT2701" =20 #define TEST_PREFIX "uhid test " =20 HID_BPF_CONFIG( HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, VID_HUION, PID_KAMVAS_PRO= _19), + HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, VID_HUION, PID_KAMVAS_PRO= _27), ); =20 bool prev_was_out_of_range; @@ -351,7 +354,8 @@ int probe(struct hid_bpf_probe_args *ctx) if (!__builtin_memcmp(name, TEST_PREFIX, sizeof(TEST_PREFIX) - 1)) name +=3D sizeof(TEST_PREFIX) - 1; =20 - if (__builtin_memcmp(name, NAME_KAMVAS_PRO_19, sizeof(NAME_KAMVAS_PRO_19)= )) + if (__builtin_memcmp(name, NAME_KAMVAS_PRO_19, sizeof(NAME_KAMVAS_PRO_19)= ) && + __builtin_memcmp(name, NAME_KAMVAS_PRO_27, sizeof(NAME_KAMVAS_PRO_27)= )) ctx->retval =3D -EINVAL; =20 hid_bpf_release_context(hctx); --=20 2.51.1 From nobody Tue Dec 2 02:33:08 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BE65C3AA1BE; Tue, 18 Nov 2025 17:17:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486222; cv=none; b=uPRJsK22JIl+qXAl6MkLUzKFBkq2UC3X5yQqLb98trFvuc+HJz+V1x9PakG/KNQW3+XX+0Kxb8naXNYm08XyWLmSLWOnRtiLCpx+3Obua0Pj4m6HUcIatggMeqXFZlaUku02bY7jiUiMgxlbu7/gsnQe+ifAMDatssxZl5dNsH4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763486222; c=relaxed/simple; bh=Q8noAz8wnMu6ZbJ6hQMyPilvy4vghJihogOufWECNWo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=MiB8K8ZyoRNmGvXQDG8FDhkBwHrP8qgVgb/jnKmctpjLqIYGPjfMTmr/t9YXiuxT3fvKzz7vvE8cgApjDS7BpRedByRJ959XI9ksKciuivzN2sz3naWUVfjHzTIQF7SQbQoWl0qijK8WOvhvpq/iMe+9AWFkvMgnDbw2Zi2et5o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=qGfBUttg; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="qGfBUttg" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A5BE3C19422; Tue, 18 Nov 2025 17:17:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1763486222; bh=Q8noAz8wnMu6ZbJ6hQMyPilvy4vghJihogOufWECNWo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=qGfBUttgNyvP7kNZTsPXcIcgdqA4Szh5sLlkWyrYIbDnvQLt0C7Xp+VVvOZc8c8Gz oEWxFuozQUwKKuiVPgknHH6yLlOgT55ViHwXsZrdXgiSAo/5KRyMD5T0fd0YJe92Ej lSVps8jzizqTnRwsNg1U/qaIIzxTTFhFetOccFZyXeCyISjifwvm5W7UeJa9iTSzak p7FQ1hGHJiMeT8pb7c58yYdOsVrLiiaiH4sWd6X9HY42f994rRN8jlz8zvGce6P0e8 Ya7TdvU3yAXe0QNlQO+ZyGMQkmEyqHaWXBEh7R4F4e8Nwc2ffQQ4Gxh7JSNlmHFgYd eCXz2qKZAJJsw== From: Benjamin Tissoires Date: Tue, 18 Nov 2025 18:16:31 +0100 Subject: [PATCH 10/10] HID: bpf: fix typo in HID usage table Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20251118-wip-sync-udev-hid-bpf-v1-10-0f8105c54835@kernel.org> References: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> In-Reply-To: <20251118-wip-sync-udev-hid-bpf-v1-0-0f8105c54835@kernel.org> To: Jiri Kosina Cc: linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, Benjamin Tissoires , Colin Ian King , Benjamin Tissoires X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1763486193; l=2666; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=Q8noAz8wnMu6ZbJ6hQMyPilvy4vghJihogOufWECNWo=; b=QIKD9vmBI5d7bVN8wqHsaXkF3uiZ7G3CXH5FK9nyOp60t801WVK+sbG+3ttx8LIQvVj0CCQlv wFaxn1W0OFSB4mXDMeBcNkueUKxcTwodVAGbW6NMx52iDD6sXwUT3Ys X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= We could go to the USB consortium, but it's probably easier that way. And update HID usage table json generated file from https://usb.org/sites/default/files/hut1_6.pdf updated: 01/30/2025 Reported-by: Colin Ian King Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests= /191 Signed-off-by: Benjamin Tissoires Signed-off-by: Benjamin Tissoires --- drivers/hid/bpf/progs/hid_report_helpers.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/hid/bpf/progs/hid_report_helpers.h b/drivers/hid/bpf/p= rogs/hid_report_helpers.h index 9b2a48e4a311b39ca20e7d9819beef8fb70315a6..9944ff54d31d27d664be554afed= 2ce3710b48c75 100644 --- a/drivers/hid/bpf/progs/hid_report_helpers.h +++ b/drivers/hid/bpf/progs/hid_report_helpers.h @@ -143,8 +143,11 @@ * report with Report ID 0xac of the given size in bytes. * The size is inclusive of the 1 byte Report ID prefix. * - * HID-BPF requires that at least one report has - * the same size as the original report from the device. + * The kernel discards any HID reports that are larger + * than the largest report in a HID report descriptor. + * Thus at least one report must have (at least) + * the same size as the largest original report from + * the device. * The easy way to ensure that is to add this * macro as the last element of your CollectionApplication * other reports can be of any size less than this. @@ -295,6 +298,7 @@ #define Usage_GD_SystemSpeakerMute Usage_i8(0xa7) #define Usage_GD_SystemHibernate Usage_i8(0xa8) #define Usage_GD_SystemMicrophoneMute Usage_i8(0xa9) +#define Usage_GD_SystemAccessibilityBinding Usage_i8(0xaa) #define Usage_GD_SystemDisplayInvert Usage_i8(0xb0) #define Usage_GD_SystemDisplayInternal Usage_i8(0xb1) #define Usage_GD_SystemDisplayExternal Usage_i8(0xb2) @@ -2669,7 +2673,7 @@ #define Usage_BS_iDeviceName Usage_i8(0x88) #define Usage_BS_iDeviceChemistry Usage_i8(0x89) #define Usage_BS_ManufacturerData Usage_i8(0x8a) -#define Usage_BS_Rechargable Usage_i8(0x8b) +#define Usage_BS_Rechargeable Usage_i8(0x8b) #define Usage_BS_WarningCapacityLimit Usage_i8(0x8c) #define Usage_BS_CapacityGranularity1 Usage_i8(0x8d) #define Usage_BS_CapacityGranularity2 Usage_i8(0x8e) --=20 2.51.1