Skip to content

Commit 8ba327d

Browse files
Benjamin TissoiresJiri Kosina
authored andcommitted
HID: bpf: Add support for the XP-Pen Deco 01 V3
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 <peter.hutterer@who-t.net> Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests/185 Signed-off-by: Benjamin Tissoires <bentiss@kernel.org> Signed-off-by: Jiri Kosina <jkosina@suse.com>
1 parent 040adbe commit 8ba327d

1 file changed

Lines changed: 305 additions & 0 deletions

File tree

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/* Copyright (c) 2025 Red Hat
3+
*/
4+
5+
#include "vmlinux.h"
6+
#include "hid_bpf.h"
7+
#include "hid_bpf_helpers.h"
8+
#include "hid_report_helpers.h"
9+
#include <bpf/bpf_tracing.h>
10+
11+
#define VID_UGEE 0x28BD /* VID is shared with SinoWealth and Glorious and prob others */
12+
#define PID_DECO_01_V3 0x0947
13+
14+
HID_BPF_CONFIG(
15+
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_DECO_01_V3),
16+
);
17+
18+
/*
19+
* Default report descriptor reports:
20+
* - a report descriptor for the pad buttons, reported as key sequences
21+
* - a report descriptor for the pen
22+
* - a vendor-specific report descriptor
23+
*
24+
* The Pad report descriptor, see
25+
* https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/issues/54
26+
*
27+
* # Report descriptor length: 102 bytes
28+
* 0x05, 0x01, // Usage Page (Generic Desktop) 0
29+
* 0x09, 0x02, // Usage (Mouse) 2
30+
* 0xa1, 0x01, // Collection (Application) 4
31+
* 0x85, 0x09, // Report ID (9) 6
32+
* 0x09, 0x01, // Usage (Pointer) 8
33+
* 0xa1, 0x00, // Collection (Physical) 10
34+
* 0x05, 0x09, // Usage Page (Button) 12
35+
* 0x19, 0x01, // UsageMinimum (1) 14
36+
* 0x29, 0x03, // UsageMaximum (3) 16
37+
* 0x15, 0x00, // Logical Minimum (0) 18
38+
* 0x25, 0x01, // Logical Maximum (1) 20
39+
* 0x95, 0x03, // Report Count (3) 22
40+
* 0x75, 0x01, // Report Size (1) 24
41+
* 0x81, 0x02, // Input (Data,Var,Abs) 26
42+
* 0x95, 0x05, // Report Count (5) 28
43+
* 0x81, 0x01, // Input (Cnst,Arr,Abs) 30
44+
* 0x05, 0x01, // Usage Page (Generic Desktop) 32
45+
* 0x09, 0x30, // Usage (X) 34
46+
* 0x09, 0x31, // Usage (Y) 36
47+
* 0x26, 0xff, 0x7f, // Logical Maximum (32767) 38
48+
* 0x95, 0x02, // Report Count (2) 41
49+
* 0x75, 0x10, // Report Size (16) 43
50+
* 0x81, 0x02, // Input (Data,Var,Abs) 45
51+
* 0x05, 0x0d, // Usage Page (Digitizers) 47
52+
* 0x09, 0x30, // Usage (Tip Pressure) 49
53+
* 0x26, 0xff, 0x07, // Logical Maximum (2047) 51
54+
* 0x95, 0x01, // Report Count (1) 54
55+
* 0x75, 0x10, // Report Size (16) 56
56+
* 0x81, 0x02, // Input (Data,Var,Abs) 58
57+
* 0xc0, // End Collection 60
58+
* 0xc0, // End Collection 61
59+
* 0x05, 0x01, // Usage Page (Generic Desktop) 62
60+
* 0x09, 0x06, // Usage (Keyboard) 64
61+
* 0xa1, 0x01, // Collection (Application) 66
62+
* 0x85, 0x06, // Report ID (6) 68
63+
* 0x05, 0x07, // Usage Page (Keyboard/Keypad) 70
64+
* 0x19, 0xe0, // UsageMinimum (224) 72
65+
* 0x29, 0xe7, // UsageMaximum (231) 74
66+
* 0x15, 0x00, // Logical Minimum (0) 76
67+
* 0x25, 0x01, // Logical Maximum (1) 78
68+
* 0x75, 0x01, // Report Size (1) 80
69+
* 0x95, 0x08, // Report Count (8) 82
70+
* 0x81, 0x02, // Input (Data,Var,Abs) 84
71+
* 0x05, 0x07, // Usage Page (Keyboard/Keypad) 86
72+
* 0x19, 0x00, // UsageMinimum (0) 88
73+
* 0x29, 0xff, // UsageMaximum (255) 90
74+
* 0x26, 0xff, 0x00, // Logical Maximum (255) 92
75+
* 0x75, 0x08, // Report Size (8) 95
76+
* 0x95, 0x06, // Report Count (6) 97
77+
* 0x81, 0x00, // Input (Data,Arr,Abs) 99
78+
* 0xc0, // End Collection 101
79+
*
80+
* And key events for buttons top->bottom are:
81+
* Buttons released: 06 00 00 00 00 00 00 00
82+
* Button1: 06 00 05 00 00 00 00 00 -> b
83+
* Button2: 06 00 08 00 00 00 00 00 -> e
84+
* Button3: 06 04 00 00 00 00 00 00 -> LAlt
85+
* Button4: 06 00 2c 00 00 00 00 00 -> Space
86+
* Button5: 06 01 16 00 00 00 00 00 -> LControl + s
87+
* Button6: 06 01 1d 00 00 00 00 00 -> LControl + z
88+
* Button7: 06 01 57 00 00 00 00 00 -> LControl + Keypad Plus
89+
* Button8: 06 01 56 00 00 00 00 00 -> LControl + Keypad Dash
90+
*
91+
* When multiple buttons are pressed at the same time, the values used to
92+
* identify the buttons are identical, but they appear in different bytes of the
93+
* record. For example, when button 2 (0x08) and button 1 (0x05) are pressed,
94+
* this is the report:
95+
*
96+
* Buttons 2 and 1: 06 00 08 05 00 00 00 00 -> e + b
97+
*
98+
* Buttons 1, 2, 4, 5 and 6 can be matched by finding their values in the
99+
* report.
100+
*
101+
* Button 3 is pressed when the 3rd bit is 1. For example, pressing buttons 3
102+
* and 5 generates this report:
103+
*
104+
* Buttons 3 and 5: 06 05 16 00 00 00 00 00 -> LControl + LAlt + s
105+
* -- --
106+
* | |
107+
* | `- Button 5 (0x16)
108+
* `- 0x05 = 0101. Button 3 is pressed
109+
* ^
110+
*
111+
* pad_buttons contains a list of buttons that can be matched in
112+
* HID_BPF_DEVICE_EVENT. Button 3 as it has a dedicated bit.
113+
*
114+
*
115+
* The Pen report descriptor announces a wrong tilt range:
116+
*
117+
* Report descriptor length: 109 bytes
118+
* 0x05, 0x0d, // Usage Page (Digitizers) 0
119+
* 0x09, 0x02, // Usage (Pen) 2
120+
* 0xa1, 0x01, // Collection (Application) 4
121+
* 0x85, 0x07, // Report ID (7) 6
122+
* 0x09, 0x20, // Usage (Stylus) 8
123+
* 0xa1, 0x01, // Collection (Application) 10
124+
* 0x09, 0x42, // Usage (Tip Switch) 12
125+
* 0x09, 0x44, // Usage (Barrel Switch) 14
126+
* 0x09, 0x45, // Usage (Eraser) 16
127+
* 0x09, 0x3c, // Usage (Invert) 18
128+
* 0x15, 0x00, // Logical Minimum (0) 20
129+
* 0x25, 0x01, // Logical Maximum (1) 22
130+
* 0x75, 0x01, // Report Size (1) 24
131+
* 0x95, 0x04, // Report Count (4) 26
132+
* 0x81, 0x02, // Input (Data,Var,Abs) 28
133+
* 0x95, 0x01, // Report Count (1) 30
134+
* 0x81, 0x03, // Input (Cnst,Var,Abs) 32
135+
* 0x09, 0x32, // Usage (In Range) 34
136+
* 0x95, 0x01, // Report Count (1) 36
137+
* 0x81, 0x02, // Input (Data,Var,Abs) 38
138+
* 0x95, 0x02, // Report Count (2) 40
139+
* 0x81, 0x03, // Input (Cnst,Var,Abs) 42
140+
* 0x75, 0x10, // Report Size (16) 44
141+
* 0x95, 0x01, // Report Count (1) 46
142+
* 0x35, 0x00, // Physical Minimum (0) 48
143+
* 0xa4, // Push 50
144+
* 0x05, 0x01, // Usage Page (Generic Desktop) 51
145+
* 0x09, 0x30, // Usage (X) 53
146+
* 0x65, 0x13, // Unit (EnglishLinear: in) 55
147+
* 0x55, 0x0d, // Unit Exponent (-3) 57
148+
* 0x46, 0x10, 0x27, // Physical Maximum (10000) 59
149+
* 0x26, 0xff, 0x7f, // Logical Maximum (32767) 62
150+
* 0x81, 0x02, // Input (Data,Var,Abs) 65
151+
* 0x09, 0x31, // Usage (Y) 67
152+
* 0x46, 0x6a, 0x18, // Physical Maximum (6250) 69
153+
* 0x26, 0xff, 0x7f, // Logical Maximum (32767) 72
154+
* 0x81, 0x02, // Input (Data,Var,Abs) 75
155+
* 0xb4, // Pop 77
156+
* 0x09, 0x30, // Usage (X) 78
157+
* 0x45, 0x00, // Physical Maximum (0) 80
158+
* 0x26, 0xff, 0x3f, // Logical Maximum (16383) 82
159+
* 0x81, 0x42, // Input (Data,Var,Abs,Null) 85
160+
* 0x09, 0x3d, // Usage (Start) 87
161+
* 0x15, 0x81, // Logical Minimum (-127) 89 <- Change from -127 to -60
162+
* 0x25, 0x7f, // Logical Maximum (127) 91 <- Change from 127 to 60
163+
* 0x75, 0x08, // Report Size (8) 93
164+
* 0x95, 0x01, // Report Count (1) 95
165+
* 0x81, 0x02, // Input (Data,Var,Abs) 97
166+
* 0x09, 0x3e, // Usage (Select) 99
167+
* 0x15, 0x81, // Logical Minimum (-127) 101 <- Change from -127 to -60
168+
* 0x25, 0x7f, // Logical Maximum (127) 103 <- Change from 127 to 60
169+
* 0x81, 0x02, // Input (Data,Var,Abs) 105
170+
* 0xc0, // End Collection 107
171+
* 0xc0, // End Collection 108
172+
*/
173+
174+
#define PEN_REPORT_DESCRIPTOR_LENGTH 109
175+
#define PAD_REPORT_DESCRIPTOR_LENGTH 102
176+
#define PAD_REPORT_LENGTH 8
177+
#define PAD_REPORT_ID 6
178+
#define PAD_NUM_BUTTONS 8
179+
180+
static const __u8 fixed_rdesc_pad[] = {
181+
UsagePage_GenericDesktop
182+
Usage_GD_Keypad
183+
CollectionApplication(
184+
// Byte 0 in report is the report ID
185+
ReportId(PAD_REPORT_ID)
186+
ReportCount(1)
187+
ReportSize(8)
188+
UsagePage_Digitizers
189+
Usage_Dig_TabletFunctionKeys
190+
CollectionPhysical(
191+
// Byte 1 is the button state
192+
UsagePage_Button
193+
UsageMinimum_i8(0x01)
194+
UsageMaximum_i8(PAD_NUM_BUTTONS)
195+
LogicalMinimum_i8(0x0)
196+
LogicalMaximum_i8(0x1)
197+
ReportCount(PAD_NUM_BUTTONS)
198+
ReportSize(1)
199+
Input(Var|Abs)
200+
// Byte 2 in report - just exists so we get to be a tablet pad
201+
UsagePage_Digitizers
202+
Usage_Dig_BarrelSwitch // BTN_STYLUS
203+
ReportCount(1)
204+
ReportSize(1)
205+
Input(Var|Abs)
206+
ReportCount(7) // padding
207+
Input(Const)
208+
// Bytes 3/4 in report - just exists so we get to be a tablet pad
209+
UsagePage_GenericDesktop
210+
Usage_GD_X
211+
Usage_GD_Y
212+
ReportCount(2)
213+
ReportSize(8)
214+
Input(Var|Abs)
215+
// Byte 5-7 are padding so we match the original report lengtth
216+
ReportCount(3)
217+
ReportSize(8)
218+
Input(Const)
219+
)
220+
)
221+
};
222+
223+
SEC(HID_BPF_RDESC_FIXUP)
224+
int BPF_PROG(xppen_deco01v3_rdesc_fixup, struct hid_bpf_ctx *hctx)
225+
{
226+
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
227+
228+
const __u8 wrong_logical_range[] = {0x15, 0x81, 0x25, 0x7f};
229+
const __u8 correct_logical_range[] = {0x15, 0xc4, 0x25, 0x3c};
230+
231+
if (!data)
232+
return 0; /* EPERM check */
233+
234+
switch (hctx->size) {
235+
case PAD_REPORT_DESCRIPTOR_LENGTH:
236+
__builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad));
237+
return sizeof(fixed_rdesc_pad);
238+
case PEN_REPORT_DESCRIPTOR_LENGTH:
239+
if (__builtin_memcmp(&data[89], wrong_logical_range,
240+
sizeof(wrong_logical_range)) == 0)
241+
__builtin_memcpy(&data[89], correct_logical_range,
242+
sizeof(correct_logical_range));
243+
if (__builtin_memcmp(&data[101], wrong_logical_range,
244+
sizeof(wrong_logical_range)) == 0)
245+
__builtin_memcpy(&data[101], correct_logical_range,
246+
sizeof(correct_logical_range));
247+
break;
248+
}
249+
250+
return 0;
251+
}
252+
253+
SEC(HID_BPF_DEVICE_EVENT)
254+
int BPF_PROG(xppen_deco01v3_device_event, struct hid_bpf_ctx *hctx)
255+
{
256+
static const __u8 pad_buttons[] = { 0x05, 0x08, 0x00, 0x2c, 0x16, 0x1d, 0x57, 0x56 };
257+
__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, PAD_REPORT_LENGTH /* size */);
258+
259+
if (!data)
260+
return 0; /* EPERM check */
261+
262+
if (data[0] == PAD_REPORT_ID) {
263+
__u8 button_mask = 0;
264+
size_t d, b;
265+
266+
/* data[1] stores the status of BTN_2 in the 3rd bit*/
267+
if (data[1] & BIT(2))
268+
button_mask |= BIT(2);
269+
270+
/* The rest of the descriptor stores the buttons as in pad_buttons */
271+
for (d = 2; d < 8; d++) {
272+
for (b = 0; b < sizeof(pad_buttons); b++) {
273+
if (data[d] != 0 && data[d] == pad_buttons[b])
274+
button_mask |= BIT(b);
275+
}
276+
}
277+
278+
__u8 report[8] = {PAD_REPORT_ID, button_mask, 0x00};
279+
280+
__builtin_memcpy(data, report, sizeof(report));
281+
}
282+
return 0;
283+
}
284+
285+
HID_BPF_OPS(xppen_deco01v3) = {
286+
.hid_rdesc_fixup = (void *)xppen_deco01v3_rdesc_fixup,
287+
.hid_device_event = (void *)xppen_deco01v3_device_event,
288+
};
289+
290+
SEC("syscall")
291+
int probe(struct hid_bpf_probe_args *ctx)
292+
{
293+
switch (ctx->rdesc_size) {
294+
case PAD_REPORT_DESCRIPTOR_LENGTH:
295+
case PEN_REPORT_DESCRIPTOR_LENGTH:
296+
ctx->retval = 0;
297+
break;
298+
default:
299+
ctx->retval = -EINVAL;
300+
}
301+
302+
return 0;
303+
}
304+
305+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)