From nobody Tue Apr 7 13:56:50 2026 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 AB7A83D47CF; Fri, 3 Apr 2026 16:12: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=1775232762; cv=none; b=b5K/dlATw/p7kQ0AxZeFQnq10IRhFnbSqcwj+bNwlepKBSCxJNRMYqmoIcYrjJFmPJH1Z+UwhOkXDWvIBsjQ1mlZulCr87t9bzxJhnlaN+jQye+4TvE8jtPsKsw6W19yJRRnkll5vFPIHwEHsiQwErmi/4QPu63fShaGejH9DsU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775232762; c=relaxed/simple; bh=SPRIlRuP8izqpkd3/PIU6esUl0qNtIi1fr1gvdXaqVE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=qF+F38b4ylVlgg78z3Obiilj9gLvVuIkbESkf5DgIpQU8Facjk8sRpYC9iNB0D43cAqrzFPAp4zswMz0eOwiPq4xH88g9lMm2mXevHwM2tgqbHXrLrTNY2f8IAi2ctKbwpxCOylmsxe4lDaktrKVJGCI9gWLSlnfSalWYIUw38o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=GZKgXHNc; 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="GZKgXHNc" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 50B4DC19421; Fri, 3 Apr 2026 16:12:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1775232762; bh=SPRIlRuP8izqpkd3/PIU6esUl0qNtIi1fr1gvdXaqVE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=GZKgXHNcnTbQ+CQW72ctQt3WmVQaA9n49e401Y7PYuy/226qMvLUGXWgA1VNBe7WK aty6BCb3wPTboIOT7CJwXqbLCsn372SnsfwIy9t2WhpqM3xN47vZQWerVlkYJMzzth 0Nkoan6CZ4tFhMRv+MESlxJhoj0yvvpFrWJ6HPfsW/v6TgG8IQuZzH492MYkBVtv5x Z++hz/DUhI/G93bcR9/mmI35V+W3P1stZKAkMvWRItoAZXSvRqHjhQmN+H0FvLuHoV Kn7BpnluYBXjkLpimOCwDaVCFD3Kwhbb5IRdmOD3u0ye4MLl3CdIxiFMHcEuuEyGUX GF3W+W4yKfcNg== From: Benjamin Tissoires Date: Fri, 03 Apr 2026 18:12:25 +0200 Subject: [PATCH 7/8] HID: bpf: Add support for the Huion KeyDial K20 over bluetooth 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: <20260403-wip-sync-udev-hid-bpf-2026-04-v1-7-978cedb9a074@kernel.org> References: <20260403-wip-sync-udev-hid-bpf-2026-04-v1-0-978cedb9a074@kernel.org> In-Reply-To: <20260403-wip-sync-udev-hid-bpf-2026-04-v1-0-978cedb9a074@kernel.org> To: Jiri Kosina Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Benjamin Tissoires , Peter Hutterer X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1775232750; l=27393; i=bentiss@kernel.org; s=20230215; h=from:subject:message-id; bh=SPRIlRuP8izqpkd3/PIU6esUl0qNtIi1fr1gvdXaqVE=; b=mEPMPxSK/juOexxtpM4cIrycsWjiIzKFmuKvaja7bmDUuk4NhyhXbx/MRtWa55bp/mJQ/Wd5O PVrhycHtT9IBYMtyK3qmP263bjjSbI0iLOVgLPPiJu699tJh47cdRzV X-Developer-Key: i=bentiss@kernel.org; a=ed25519; pk=7D1DyAVh6ajCkuUTudt/chMuXWIJHlv2qCsRkIizvFw= When connected over bluetooth this device is just different enough that forcing it into the same source file as the USB connection doesn't gain us much benefit. So let's duplicate this. Code and tests originally produced by Claude code. Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/work_items/69 Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests= /201 Signed-off-by: Peter Hutterer Signed-off-by: Benjamin Tissoires --- .../bpf/progs/Huion__KeydialK20-Bluetooth.bpf.c | 492 +++++++++++++++++= ++++ 1 file changed, 492 insertions(+) diff --git a/drivers/hid/bpf/progs/Huion__KeydialK20-Bluetooth.bpf.c b/driv= ers/hid/bpf/progs/Huion__KeydialK20-Bluetooth.bpf.c new file mode 100644 index 000000000000..d0769e990039 --- /dev/null +++ b/drivers/hid/bpf/progs/Huion__KeydialK20-Bluetooth.bpf.c @@ -0,0 +1,492 @@ +// 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_KEYDIAL_K20_BLUETOOTH 0x8251 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC, VID_HUION, PID_KEYDIAL_K20_B= LUETOOTH), +); + +/* This is the same device as in 0010-Huion__KeydialK20 but connected via = Bluetooth. + * It does not need (to support?) switching to a vendor mode so we just mo= dify the + * existing mode. + * + * By default it exports two hidraw nodes, only the second one sends event= s. + * + * This is the first hidraw node which we disable: + * + * # Keydial mini-050 + * # Report descriptor length: 114 bytes + * # Bytes // Field Name = Offset + * # ---------------------------------------------------------------------= ------------- + * # =F0=9F=AE=A5 0x05, 0x01, // Usage Page (Generic De= sktop) 0 + * # =F0=9F=AD=AC 0x09, 0x0e, // Usage (System Multi-Ax= is Controller) 2 + * # 0xa1, 0x01, // Collection (Application) = 4 + * # =E2=94=85 0x85, 0x03, // Report ID (3) = 6 + * # =F0=9F=AE=A5 0x05, 0x0d, // Usage Page (Digitize= rs) 8 + * # 0x75, 0x08, // Report Size (8) = 10 + * # 0x95, 0x01, // Report Count (1) = 12 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Abs) = 14 + * # =F0=9F=AD=AC 0x09, 0x21, // Usage (Puck) = 16 + * # 0xa1, 0x02, // Collection (Logical) = 18 + * # 0x15, 0x00, // Logical Minimum (0) = 20 + * # 0x25, 0x01, // Logical Maximum (1) = 22 + * # 0x75, 0x01, // Report Size (1) = 24 + * # 0x95, 0x01, // Report Count (1) = 26 + * # 0xa1, 0x00, // Collection (Physical) = 28 + * # =F0=9F=AE=A5 0x05, 0x09, // Usage Page (Butt= on) 30 + * # =F0=9F=AD=AC 0x09, 0x01, // Usage (Button 1)= 32 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Abs= ) 34 + * # =F0=9F=AE=A5 0x05, 0x0d, // Usage Page (Digi= tizers) 36 + * # =F0=9F=AD=AC 0x09, 0x33, // Usage (Touch) = 38 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Abs= ) 40 + * # 0x95, 0x06, // Report Count (6) = 42 + * # =E2=94=87 0x81, 0x03, // Input (Cnst,Var,Abs= ) 44 + * # 0xa1, 0x02, // Collection (Logical) = 46 + * # =F0=9F=AE=A5 0x05, 0x01, // Usage Page (Ge= neric Desktop) 48 + * # =F0=9F=AD=AC 0x09, 0x37, // Usage (Dial) = 50 + * # 0x16, 0x00, 0x80, // Logical Minimum (32768) = 52 + * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) = 55 + * # 0x75, 0x10, // Report Size (16) = 58 + * # 0x95, 0x01, // Report Count (1) = 60 + * # =E2=94=87 0x81, 0x06, // Input (Data,Var,R= el) 62 + * # 0x35, 0x00, // Physical Minimum (0) = 64 + * # 0x46, 0x10, 0x0e, // Physical Maximum (3600) = 66 + * # 0x15, 0x00, // Logical Minimum (0) = 69 + * # 0x26, 0x10, 0x0e, // Logical Maximum (3600) = 71 + * # =F0=9F=AD=AC 0x09, 0x48, // Usage (Resolut= ion Multiplier) 74 + * # =E2=95=91 0xb1, 0x02, // Feature (Data,Var= ,Abs) 76 + * # 0x45, 0x00, // Physical Maximum (0) = 78 + * # 0xc0, // End Collection = 80 + * # 0x75, 0x08, // Report Size (8) = 81 + * # 0x95, 0x01, // Report Count (1) = 83 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Abs= ) 85 + * # 0x75, 0x08, // Report Size (8) = 87 + * # 0x95, 0x01, // Report Count (1) = 89 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Abs= ) 91 + * # 0x75, 0x08, // Report Size (8) = 93 + * # 0x95, 0x01, // Report Count (1) = 95 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Abs= ) 97 + * # 0x75, 0x08, // Report Size (8) = 99 + * # 0x95, 0x01, // Report Count (1) = 101 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Abs= ) 103 + * # 0x75, 0x08, // Report Size (8) = 105 + * # 0x95, 0x01, // Report Count (1) = 107 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Abs= ) 109 + * # 0xc0, // End Collection = 111 + * # 0xc0, // End Collection = 112 + * # 0xc0, // End Collection = 113 + * R: 114 05 01 09 0e a1 01 85 03 05 0d 75 08 95 01 81 01 09 21 a1 02 15 0= 0 25 01 75 01 95 01 a1 00 05 09 09 01 81 02 05 0d 09 33 81 02 95 06 81 03 a= 1 02 05 01 09 37 16 00 80 26 ff 7f 75 10 95 01 81 06 35 00 46 10 0e 15 00 2= 6 10 0e 09 48 b1 02 45 00 c0 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 0= 1 81 01 75 08 95 01 81 01 75 08 95 01 81 01 c0 c0 c0 + * N: Keydial mini-050 + * I: 5 256c 8251 + * + * The second hidraw node is what sends events: + * + * # Keydial mini-050 + * # Report descriptor length: 160 bytes + * # Bytes // Field Name = Offset + * # ---------------------------------------------------------------------= ------------- + * # =F0=9F=AE=A5 0x05, 0x01, // Usage Page (Generic De= sktop) 0 + * # =F0=9F=AD=AC 0x09, 0x06, // Usage (Keyboard) = 2 + * # 0xa1, 0x01, // Collection (Application) = 4 + * # =E2=94=85 0x85, 0x01, // Report ID (1) = 6 + * # =F0=9F=AE=A5 0x05, 0x07, // Usage Page (Keyboard= /Keypad) 8 + * # =F0=9F=AD=AC 0x19, 0xe0, // Usage Minimum (224) = 10 + * # =F0=9F=AD=AC 0x29, 0xe7, // Usage Maximum (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 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Abs) = 22 + * # 0x95, 0x01, // Report Count (1) = 24 + * # 0x75, 0x08, // Report Size (8) = 26 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Abs) = 28 + * # 0x95, 0x05, // Report Count (5) = 30 + * # 0x75, 0x01, // Report Size (1) = 32 + * # =F0=9F=AE=A5 0x05, 0x08, // Usage Page (LED) = 34 + * # =F0=9F=AD=AC 0x19, 0x01, // Usage Minimum (1) = 36 + * # =F0=9F=AD=AC 0x29, 0x05, // Usage Maximum (5) = 38 + * # =E2=94=8A 0x91, 0x02, // Output (Data,Var,Abs) = 40 + * # 0x95, 0x01, // Report Count (1) = 42 + * # 0x75, 0x03, // Report Size (3) = 44 + * # =E2=94=8A 0x91, 0x01, // Output (Cnst,Arr,Abs) = 46 + * # 0x95, 0x06, // Report Count (6) = 48 + * # 0x75, 0x08, // Report Size (8) = 50 + * # 0x15, 0x00, // Logical Minimum (0) = 52 + * # 0x25, 0xf1, // Logical Maximum (241) = 54 + * # =F0=9F=AE=A5 0x05, 0x07, // Usage Page (Keyboard= /Keypad) 56 + * # =F0=9F=AD=AC 0x19, 0x00, // Usage Minimum (0) = 58 + * # =F0=9F=AD=AC 0x29, 0xf1, // Usage Maximum (241) = 60 + * # =E2=94=87 0x81, 0x00, // Input (Data,Arr,Abs) = 62 + * # 0xc0, // End Collection = 64 + * # =F0=9F=AE=A5 0x05, 0x0c, // Usage Page (Consumer) = 65 + * # =F0=9F=AD=AC 0x09, 0x01, // Usage (Consumer Contro= l) 67 + * # 0xa1, 0x01, // Collection (Application) = 69 + * # =E2=94=85 0x85, 0x02, // Report ID (2) = 71 + * # =F0=9F=AE=A5 0x05, 0x0c, // Usage Page (Consumer= ) 73 + * # =F0=9F=AD=AC 0x19, 0x00, // Usage Minimum (0) = 75 + * # =F0=9F=AD=AC 0x2a, 0x80, 0x03, // Usage Maximum (896) = 77 + * # 0x15, 0x00, // Logical Minimum (0) = 80 + * # 0x26, 0x80, 0x03, // Logical Maximum (896) = 82 + * # 0x75, 0x10, // Report Size (16) = 85 + * # 0x95, 0x01, // Report Count (1) = 87 + * # =E2=94=87 0x81, 0x00, // Input (Data,Arr,Abs) = 89 + * # 0xc0, // End Collection = 91 + * # =F0=9F=AE=A5 0x05, 0x01, // Usage Page (Generic De= sktop) 92 + * # =F0=9F=AD=AC 0x09, 0x02, // Usage (Mouse) = 94 + * # 0xa1, 0x01, // Collection (Application) = 96 + * # =F0=9F=AD=AC 0x09, 0x01, // Usage (Pointer) = 98 + * # =E2=94=85 0x85, 0x05, // Report ID (5) = 100 + * # 0xa1, 0x00, // Collection (Physical) = 102 + * # =F0=9F=AE=A5 0x05, 0x09, // Usage Page (Button= ) 104 + * # =F0=9F=AD=AC 0x19, 0x01, // Usage Minimum (1) = 106 + * # =F0=9F=AD=AC 0x29, 0x05, // Usage Maximum (5) = 108 + * # 0x15, 0x00, // Logical Minimum (0) = 110 + * # 0x25, 0x01, // Logical Maximum (1) = 112 + * # 0x95, 0x05, // Report Count (5) = 114 + * # 0x75, 0x01, // Report Size (1) = 116 + * # =E2=94=87 0x81, 0x02, // Input (Data,Var,Abs) = 118 + * # 0x95, 0x01, // Report Count (1) = 120 + * # 0x75, 0x03, // Report Size (3) = 122 + * # =E2=94=87 0x81, 0x01, // Input (Cnst,Arr,Abs) = 124 + * # =F0=9F=AE=A5 0x05, 0x01, // Usage Page (Generi= c Desktop) 126 + * # =F0=9F=AD=AC 0x09, 0x30, // Usage (X) = 128 + * # =F0=9F=AD=AC 0x09, 0x31, // Usage (Y) = 130 + * # 0x16, 0x01, 0x80, // Logical Minimum (32769) = 132 + * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) = 135 + * # 0x75, 0x10, // Report Size (16) = 138 + * # 0x95, 0x02, // Report Count (2) = 140 + * # =E2=94=87 0x81, 0x06, // Input (Data,Var,Rel) = 142 + * # =F0=9F=AE=A5 0x05, 0x01, // Usage Page (Generi= c Desktop) 144 + * # =F0=9F=AD=AC 0x09, 0x38, // Usage (Wheel) = 146 + * # 0x15, 0x81, // Logical Minimum (129) = 148 + * # 0x25, 0x7f, // Logical Maximum (127) = 150 + * # 0x95, 0x01, // Report Count (1) = 152 + * # 0x75, 0x08, // Report Size (8) = 154 + * # =E2=94=87 0x81, 0x06, // Input (Data,Var,Rel) = 156 + * # 0xc0, // End Collection = 158 + * # 0xc0, // End Collection = 159 + * R: 160 05 01 09 06 a1 01 85 01 05 07 19 e0 29 e7 15 00 25 01 75 01 95 0= 8 81 02 95 01 75 08 81 01 95 05 75 01 05 08 19 01 29 05 91 02 95 01 75 03 9= 1 01 95 06 75 08 15 00 25 f1 05 07 19 00 29 f1 81 00 c0 05 0c 09 01 a1 01 8= 5 02 05 0c 19 00 2a 80 03 15 00 26 80 03 75 10 95 01 81 00 c0 05 01 09 02 a= 1 01 09 01 85 05 a1 00 05 09 19 01 29 05 15 00 25 01 95 05 75 01 81 02 95 0= 1 75 03 81 01 05 01 09 30 09 31 16 01 80 26 ff 7f 75 10 95 02 81 06 05 01 0= 9 38 15 81 25 7f 95 01 75 08 81 06 c0 c0 + * N: Keydial mini-050 + * I: 5 256c 8251 + * # Report descriptor: + * # ------- Input Report ------- + * # =E2=96=91 Report ID: 1 + * # =E2=96=91 | Report size: 72 bits + * # =E2=96=91 Bit: 8 Usage: 0007/00e0: Keyboard/Keypad / Keyboar= d LeftControl Logical Range: 0..=3D1 + * # =E2=96=91 Bit: 9 Usage: 0007/00e1: Keyboard/Keypad / Keyboar= d LeftShift Logical Range: 0..=3D1 + * # =E2=96=91 Bit: 10 Usage: 0007/00e2: Keyboard/Keypad / Keyboar= d LeftAlt Logical Range: 0..=3D1 + * # =E2=96=91 Bit: 11 Usage: 0007/00e3: Keyboard/Keypad / Keyboar= d Left GUI Logical Range: 0..=3D1 + * # =E2=96=91 Bit: 12 Usage: 0007/00e4: Keyboard/Keypad / Keyboar= d RightControl Logical Range: 0..=3D1 + * # =E2=96=91 Bit: 13 Usage: 0007/00e5: Keyboard/Keypad / Keyboar= d RightShift Logical Range: 0..=3D1 + * # =E2=96=91 Bit: 14 Usage: 0007/00e6: Keyboard/Keypad / Keyboar= d RightAlt Logical Range: 0..=3D1 + * # =E2=96=91 Bit: 15 Usage: 0007/00e7: Keyboard/Keypad / Keyboar= d Right GUI Logical Range: 0..=3D1 + * # =E2=96=91 Bits: 16..=3D23 ######### Padding + * # =E2=96=91 Bits: 24..=3D71 Usages: = Logical Range: 0..=3D241 + * # =E2=96=91 0007/0000: + * # =E2=96=91 0007/0001: Keyboard/Keypad / ErrorRollOver + * # =E2=96=91 0007/0002: Keyboard/Keypad / POSTFail + * # =E2=96=91 0007/0003: Keyboard/Keypad / ErrorUndefined + * # =E2=96=91 0007/0004: Keyboard/Keypad / Keyboard A + * # =E2=96=91 ... use --full to see all usages + * # ------- Input Report ------- + * # =E2=96=92 Report ID: 2 + * # =E2=96=92 | Report size: 24 bits + * # =E2=96=92 Bits: 8..=3D23 Usages: = Logical Range: 0..=3D896 + * # =E2=96=92 000c/0000: + * # =E2=96=92 000c/0001: Consumer / Consumer Control + * # =E2=96=92 000c/0002: Consumer / Numeric Key Pad + * # =E2=96=92 000c/0003: Consumer / Programmable Buttons + * # =E2=96=92 000c/0004: Consumer / Microphone + * # =E2=96=92 ... use --full to see all usages + * # ------- Input Report ------- + * # =E2=96=9E Report ID: 5 + * # =E2=96=9E | Report size: 56 bits + * # =E2=96=9E Bit: 8 Usage: 0009/0001: Button / Button 1 = Logical Range: 0..=3D1 + * # =E2=96=9E Bit: 9 Usage: 0009/0002: Button / Button 2 = Logical Range: 0..=3D1 + * # =E2=96=9E Bit: 10 Usage: 0009/0003: Button / Button 3 = Logical Range: 0..=3D1 + * # =E2=96=9E Bit: 11 Usage: 0009/0004: Button / Button 4 = Logical Range: 0..=3D1 + * # =E2=96=9E Bit: 12 Usage: 0009/0005: Button / Button 5 = Logical Range: 0..=3D1 + * # =E2=96=9E Bits: 13..=3D15 ######### Padding + * # =E2=96=9E Bits: 16..=3D31 Usage: 0001/0030: Generic Desktop / X = Logical Range: 32769..=3D32767 + * # =E2=96=9E Bits: 32..=3D47 Usage: 0001/0031: Generic Desktop / Y = Logical Range: 32769..=3D32767 + * # =E2=96=9E Bits: 48..=3D55 Usage: 0001/0038: Generic Desktop / Wheel= Logical Range: 129..=3D127 + * # ------- Output Report ------- + * # =E2=96=91 Report ID: 1 + * # =E2=96=91 | Report size: 16 bits + * # =E2=96=91 Bit: 8 Usage: 0008/0001: LED / Num Lock = Logical Range: 0..=3D1 + * # =E2=96=91 Bit: 9 Usage: 0008/0002: LED / Caps Lock = Logical Range: 0..=3D1 + * # =E2=96=91 Bit: 10 Usage: 0008/0003: LED / Scroll Lock = Logical Range: 0..=3D1 + * # =E2=96=91 Bit: 11 Usage: 0008/0004: LED / Compose = Logical Range: 0..=3D1 + * # =E2=96=91 Bit: 12 Usage: 0008/0005: LED / Kana = Logical Range: 0..=3D1 + * # =E2=96=91 Bits: 13..=3D15 ######### Padding + * #######################################################################= ####### + * # Event nodes: + * # - /dev/input/event12: "Keydial mini-050 Keyboard" + * # - /dev/input/event14: "Keydial mini-050 Mouse" + * #######################################################################= ####### + * # Recorded events below in format: + * # E: . [bytes ...] + * # + * + * - Report ID 1 sends keyboard shortcuts when pressing the buttons, e.g. + * + * # =E2=96=91 Report ID: 1 / + * # =E2=96=91 Keyboard LeftControl: 0 |Keyboard LeftShi= ft: 0 |Keyboard LeftAlt: 0 |Keyboard Left GUI: 0 |Keyboard Righ= tControl: 0 |Keyboard RightShift: 0 |Keyboard RightAlt: 0 |Keyb= oard Right GUI: 0 |<8 bits padding> |0007/0000: 0| Keyboard K: 1= 4| 0007/0000: 0| 0007/0000: 0| 0007/0000: 0| 0007/0000: 0 + * E: 000000.000292 9 01 00 00 00 0e 00 00 00 00 + * + * - Report ID 2 sends the button inside the wheel/dial thing + * # =E2=96=92 Report ID: 2 / + * # =E2=96=92 Play/Pause: 205 + * E: 000134.347845 3 02 cd 00 + * # =E2=96=92 Report ID: 2 / + * # =E2=96=92 000c/0000: 0 + * E: 000134.444965 3 02 00 00 + * + * - Report ID 5 sends the wheel relative events (always a double-event wi= th the second as zero) + * # =E2=96=9E Report ID: 5 / + * # =E2=96=9E Button 1: 0 |Button 2: 0 |Button 3: = 0 |Button 4: 0 |Button 5: 0 |<3 bits padding> |X: 0 |Y: = 0 |Wheel: 255 + * E: 000064.859915 7 05 00 00 00 00 00 ff + * # =E2=96=9E Report ID: 5 / + * # =E2=96=9E Button 1: 0 |Button 2: 0 |Button 3: = 0 |Button 4: 0 |Button 5: 0 |<3 bits padding> |X: 0 |Y: = 0 |Wheel: 0 + * E: 000064.882009 7 05 00 00 00 00 00 00 + */ + +#define BT_PAD_REPORT_DESCRIPTOR_LENGTH 160 +#define BT_PUCK_REPORT_DESCRIPTOR_LENGTH 114 // This one doesn't send eve= nts +#define BT_PAD_KBD_REPORT_ID 1 +#define BT_PAD_CC_REPORT_ID 2 +#define BT_PAD_MOUSE_REPORT_ID 5 +#define BT_PAD_KBD_REPORT_LENGTH 9 +#define BT_PAD_CC_REPORT_LENGTH 3 +#define BT_PAD_MOUSE_REPORT_LENGTH 7 +#define OUR_REPORT_ID 11 /* "randomly" picked report ID for our reports */ + +__u32 last_button_state =3D 0; + +static const __u8 disabled_rdesc_puck[] =3D { + FixedSizeVendorReport(BT_PUCK_REPORT_DESCRIPTOR_LENGTH) +}; + +static const __u8 fixed_rdesc_pad[] =3D { + UsagePage_GenericDesktop + Usage_GD_Keypad + CollectionApplication( + // Byte 0 + ReportId(OUR_REPORT_ID) + UsagePage_Digitizers + Usage_Dig_TabletFunctionKeys + CollectionPhysical( + // Byte 1 is a button so we look like a tablet + 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 + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) + ReportCount(2) + ReportSize(8) + Input(Var|Abs) + // Bytes 4-7 are the button state for 19 buttons + pad out to u32 + // We send the first 10 buttons as buttons 1-10 which is BTN_0 -> BTN_9 + UsagePage_Button + UsageMinimum_i8(1) + UsageMaximum_i8(10) + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) + ReportCount(10) + ReportSize(1) + Input(Var|Abs) + // We send the other 9 buttons as buttons 0x31 and above -> BTN_A - BTN= _TL2 + UsageMinimum_i8(0x31) + UsageMaximum_i8(0x3a) + ReportCount(9) + ReportSize(1) + Input(Var|Abs) + ReportCount(13) + ReportSize(1) + Input(Const) // padding + // Byte 8 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(BT_PAD_KBD_REPORT_LENGTH) + ) +}; + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(k20_bt_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; + + if (!data) + return 0; /* EPERM check */ + + if (rdesc_size =3D=3D BT_PAD_REPORT_DESCRIPTOR_LENGTH) { + __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad)); + return sizeof(fixed_rdesc_pad); + } + if (rdesc_size =3D=3D BT_PUCK_REPORT_DESCRIPTOR_LENGTH) { + // This hidraw node doesn't send anything and can be ignored + __builtin_memcpy(data, disabled_rdesc_puck, sizeof(disabled_rdesc_puck)); + return sizeof(disabled_rdesc_puck); + } + + return 0; +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(k20_bt_fix_events, struct hid_bpf_ctx *hctx) +{ + __u8 *data =3D hid_bpf_get_data(hctx, 0 /* offset */, 12 /* size */); + struct pad_report { + __u8 report_id; + __u8 btn_stylus:1; + __u8 pad:7; + __u8 x; + __u8 y; + __u32 buttons; + __u8 wheel; + } __packed * pad_report =3D (struct pad_report *)data; + + if (!data) + return 0; /* EPERM check */ + + /* Report ID 1 - Keyboard events (button presses) */ + if (data[0] =3D=3D BT_PAD_KBD_REPORT_ID) { + const __u8 button_mapping[] =3D { + 0x0e, /* Button 1: K */ + 0x0a, /* Button 2: G */ + 0x0f, /* Button 3: L */ + 0x4c, /* Button 4: Delete */ + 0x0c, /* Button 5: I */ + 0x07, /* Button 6: D */ + 0x05, /* Button 7: B */ + 0x08, /* Button 8: E */ + 0x16, /* Button 9: S */ + 0x1d, /* Button 10: Z */ + 0x06, /* Button 11: C */ + 0x19, /* Button 12: V */ + 0xff, /* Button 13: LeftControl */ + 0xff, /* Button 14: LeftAlt */ + 0xff, /* Button 15: LeftShift */ + 0x28, /* Button 16: Return Enter */ + 0x2c, /* Button 17: Spacebar */ + 0x11, /* Button 18: N */ + }; + + __u8 modifiers =3D data[1]; + __u32 buttons =3D 0; + + if (modifiers & 0x01) { /* Control */ + buttons |=3D BIT(12); + } + if (modifiers & 0x02) { /* Shift */ + buttons |=3D BIT(14); + } + if (modifiers & 0x04) { /* Alt */ + buttons |=3D BIT(13); + } + + for (int i =3D 4; i < BT_PAD_KBD_REPORT_LENGTH; i++) { + if (!data[i]) + break; + + for (size_t b =3D 0; b < ARRAY_SIZE(button_mapping); b++) { + if (data[i] !=3D 0xff && data[i] =3D=3D button_mapping[b]) { + buttons |=3D BIT(b); + break; + } + } + } + + last_button_state =3D buttons; + + pad_report->report_id =3D OUR_REPORT_ID; + pad_report->btn_stylus =3D 0; + pad_report->x =3D 0; + pad_report->y =3D 0; + pad_report->buttons =3D buttons; + pad_report->wheel =3D 0; + + return sizeof(struct pad_report); + } + + /* Report ID 2 - Consumer control events (the button inside the wheel) */ + if (data[0] =3D=3D BT_PAD_CC_REPORT_ID) { + const __u8 PlayPause =3D 0xcd; + + if (data[1] =3D=3D PlayPause) + last_button_state |=3D BIT(18); + else + last_button_state &=3D ~BIT(18); + + pad_report->report_id =3D OUR_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 0; + + return sizeof(struct pad_report); + } + + /* Report ID 5 - Mouse events (wheel rotation) */ + if (data[0] =3D=3D BT_PAD_MOUSE_REPORT_ID) { + __u8 wheel_delta =3D data[6]; + + pad_report->report_id =3D OUR_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_delta; + + return sizeof(struct pad_report); + } + + return 0; +} + +HID_BPF_OPS(keydial_k20_bluetooth) =3D { + .hid_device_event =3D (void *)k20_bt_fix_events, + .hid_rdesc_fixup =3D (void *)k20_bt_fix_rdesc, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + switch (ctx->rdesc_size) { + case BT_PAD_REPORT_DESCRIPTOR_LENGTH: + case BT_PUCK_REPORT_DESCRIPTOR_LENGTH: + ctx->retval =3D 0; + break; + default: + ctx->retval =3D -EINVAL; + } + + return 0; +} + +char _license[] SEC("license") =3D "GPL"; --=20 2.53.0