summaryrefslogtreecommitdiff
path: root/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c
blob: a4a4f324aedd5658bb9b5114b2513d37ed719c0f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2024 Benjamin Tissoires
 */

#include "vmlinux.h"
#include "hid_bpf.h"
#include "hid_bpf_helpers.h"
#include <bpf/bpf_tracing.h>

#define VID_HUION 0x256C
#define PID_KAMVAS_PRO_19 0x006B
#define NAME_KAMVAS_PRO_19 "HUION Huion Tablet_GT1902"

#define TEST_PREFIX "uhid test "

HID_BPF_CONFIG(
	HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, VID_HUION, PID_KAMVAS_PRO_19),
);

bool prev_was_out_of_range;
bool in_eraser_mode;

/*
 * We need to amend the report descriptor for the following:
 * - the second button is reported through Secondary Tip Switch instead of Secondary Barrel Switch
 * - the third button is reported through Invert, and we need some room to report it.
 *
 */
static const __u8 fixed_rdesc[] = {
	0x05, 0x0d,                    // Usage Page (Digitizers)             0
	0x09, 0x02,                    // Usage (Pen)                         2
	0xa1, 0x01,                    // Collection (Application)            4
	0x85, 0x0a,                    //  Report ID (10)                     6
	0x09, 0x20,                    //  Usage (Stylus)                     8
	0xa1, 0x01,                    //  Collection (Application)           10
	0x09, 0x42,                    //   Usage (Tip Switch)                12
	0x09, 0x44,                    //   Usage (Barrel Switch)             14
	0x09, 0x5a,                    //   Usage (Secondary Barrel Switch)   16 /* changed from Secondary Tip Switch */
	0x09, 0x3c,                    //   Usage (Invert)                    18
	0x09, 0x45,                    //   Usage (Eraser)                    20
	0x15, 0x00,                    //   Logical Minimum (0)               22
	0x25, 0x01,                    //   Logical Maximum (1)               24
	0x75, 0x01,                    //   Report Size (1)                   26
	0x95, 0x05,                    //   Report Count (5)                  28 /* changed (was 5) */
	0x81, 0x02,                    //   Input (Data,Var,Abs)              30
	0x05, 0x09,                    //   Usage Page (Button)                  /* inserted */
	0x09, 0x4a,                    //   Usage (0x4a)                         /* inserted to be translated as input usage 0x149: BTN_STYLUS3 */
	0x95, 0x01,                    //   Report Count (1)                     /* inserted */
	0x81, 0x02,                    //   Input (Data,Var,Abs)                 /* inserted */
	0x05, 0x0d,                    //   Usage Page (Digitizers)              /* inserted */
	0x09, 0x32,                    //   Usage (In Range)                  32
	0x75, 0x01,                    //   Report Size (1)                   34
	0x95, 0x01,                    //   Report Count (1)                  36
	0x81, 0x02,                    //   Input (Data,Var,Abs)              38
	0x81, 0x03,                    //   Input (Cnst,Var,Abs)              40
	0x05, 0x01,                    //   Usage Page (Generic Desktop)      42
	0x09, 0x30,                    //   Usage (X)                         44
	0x09, 0x31,                    //   Usage (Y)                         46
	0x55, 0x0d,                    //   Unit Exponent (-3)                48
	0x65, 0x33,                    //   Unit (EnglishLinear: in³)         50
	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           52
	0x35, 0x00,                    //   Physical Minimum (0)              55
	0x46, 0x00, 0x08,              //   Physical Maximum (2048)           57
	0x75, 0x10,                    //   Report Size (16)                  60
	0x95, 0x02,                    //   Report Count (2)                  62
	0x81, 0x02,                    //   Input (Data,Var,Abs)              64
	0x05, 0x0d,                    //   Usage Page (Digitizers)           66
	0x09, 0x30,                    //   Usage (Tip Pressure)              68
	0x26, 0xff, 0x3f,              //   Logical Maximum (16383)           70
	0x75, 0x10,                    //   Report Size (16)                  73
	0x95, 0x01,                    //   Report Count (1)                  75
	0x81, 0x02,                    //   Input (Data,Var,Abs)              77
	0x09, 0x3d,                    //   Usage (X Tilt)                    79
	0x09, 0x3e,                    //   Usage (Y Tilt)                    81
	0x15, 0xa6,                    //   Logical Minimum (-90)             83
	0x25, 0x5a,                    //   Logical Maximum (90)              85
	0x75, 0x08,                    //   Report Size (8)                   87
	0x95, 0x02,                    //   Report Count (2)                  89
	0x81, 0x02,                    //   Input (Data,Var,Abs)              91
	0xc0,                          //  End Collection                     93
	0xc0,                          // End Collection                      94
	0x05, 0x0d,                    // Usage Page (Digitizers)             95
	0x09, 0x04,                    // Usage (Touch Screen)                97
	0xa1, 0x01,                    // Collection (Application)            99
	0x85, 0x04,                    //  Report ID (4)                      101
	0x09, 0x22,                    //  Usage (Finger)                     103
	0xa1, 0x02,                    //  Collection (Logical)               105
	0x05, 0x0d,                    //   Usage Page (Digitizers)           107
	0x95, 0x01,                    //   Report Count (1)                  109
	0x75, 0x06,                    //   Report Size (6)                   111
	0x09, 0x51,                    //   Usage (Contact Id)                113
	0x15, 0x00,                    //   Logical Minimum (0)               115
	0x25, 0x3f,                    //   Logical Maximum (63)              117
	0x81, 0x02,                    //   Input (Data,Var,Abs)              119
	0x09, 0x42,                    //   Usage (Tip Switch)                121
	0x25, 0x01,                    //   Logical Maximum (1)               123
	0x75, 0x01,                    //   Report Size (1)                   125
	0x95, 0x01,                    //   Report Count (1)                  127
	0x81, 0x02,                    //   Input (Data,Var,Abs)              129
	0x75, 0x01,                    //   Report Size (1)                   131
	0x95, 0x01,                    //   Report Count (1)                  133
	0x81, 0x03,                    //   Input (Cnst,Var,Abs)              135
	0x05, 0x01,                    //   Usage Page (Generic Desktop)      137
	0x75, 0x10,                    //   Report Size (16)                  139
	0x55, 0x0e,                    //   Unit Exponent (-2)                141
	0x65, 0x11,                    //   Unit (SILinear: cm)               143
	0x09, 0x30,                    //   Usage (X)                         145
	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           147
	0x35, 0x00,                    //   Physical Minimum (0)              150
	0x46, 0x15, 0x0c,              //   Physical Maximum (3093)           152
	0x81, 0x42,                    //   Input (Data,Var,Abs,Null)         155
	0x09, 0x31,                    //   Usage (Y)                         157
	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           159
	0x46, 0xcb, 0x06,              //   Physical Maximum (1739)           162
	0x81, 0x42,                    //   Input (Data,Var,Abs,Null)         165
	0x05, 0x0d,                    //   Usage Page (Digitizers)           167
	0x09, 0x30,                    //   Usage (Tip Pressure)              169
	0x26, 0xff, 0x1f,              //   Logical Maximum (8191)            171
	0x75, 0x10,                    //   Report Size (16)                  174
	0x95, 0x01,                    //   Report Count (1)                  176
	0x81, 0x02,                    //   Input (Data,Var,Abs)              178
	0xc0,                          //  End Collection                     180
	0x05, 0x0d,                    //  Usage Page (Digitizers)            181
	0x09, 0x22,                    //  Usage (Finger)                     183
	0xa1, 0x02,                    //  Collection (Logical)               185
	0x05, 0x0d,                    //   Usage Page (Digitizers)           187
	0x95, 0x01,                    //   Report Count (1)                  189
	0x75, 0x06,                    //   Report Size (6)                   191
	0x09, 0x51,                    //   Usage (Contact Id)                193
	0x15, 0x00,                    //   Logical Minimum (0)               195
	0x25, 0x3f,                    //   Logical Maximum (63)              197
	0x81, 0x02,                    //   Input (Data,Var,Abs)              199
	0x09, 0x42,                    //   Usage (Tip Switch)                201
	0x25, 0x01,                    //   Logical Maximum (1)               203
	0x75, 0x01,                    //   Report Size (1)                   205
	0x95, 0x01,                    //   Report Count (1)                  207
	0x81, 0x02,                    //   Input (Data,Var,Abs)              209
	0x75, 0x01,                    //   Report Size (1)                   211
	0x95, 0x01,                    //   Report Count (1)                  213
	0x81, 0x03,                    //   Input (Cnst,Var,Abs)              215
	0x05, 0x01,                    //   Usage Page (Generic Desktop)      217
	0x75, 0x10,                    //   Report Size (16)                  219
	0x55, 0x0e,                    //   Unit Exponent (-2)                221
	0x65, 0x11,                    //   Unit (SILinear: cm)               223
	0x09, 0x30,                    //   Usage (X)                         225
	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           227
	0x35, 0x00,                    //   Physical Minimum (0)              230
	0x46, 0x15, 0x0c,              //   Physical Maximum (3093)           232
	0x81, 0x42,                    //   Input (Data,Var,Abs,Null)         235
	0x09, 0x31,                    //   Usage (Y)                         237
	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           239
	0x46, 0xcb, 0x06,              //   Physical Maximum (1739)           242
	0x81, 0x42,                    //   Input (Data,Var,Abs,Null)         245
	0x05, 0x0d,                    //   Usage Page (Digitizers)           247
	0x09, 0x30,                    //   Usage (Tip Pressure)              249
	0x26, 0xff, 0x1f,              //   Logical Maximum (8191)            251
	0x75, 0x10,                    //   Report Size (16)                  254
	0x95, 0x01,                    //   Report Count (1)                  256
	0x81, 0x02,                    //   Input (Data,Var,Abs)              258
	0xc0,                          //  End Collection                     260
	0x05, 0x0d,                    //  Usage Page (Digitizers)            261
	0x09, 0x56,                    //  Usage (Scan Time)                  263
	0x55, 0x00,                    //  Unit Exponent (0)                  265
	0x65, 0x00,                    //  Unit (None)                        267
	0x27, 0xff, 0xff, 0xff, 0x7f,  //  Logical Maximum (2147483647)       269
	0x95, 0x01,                    //  Report Count (1)                   274
	0x75, 0x20,                    //  Report Size (32)                   276
	0x81, 0x02,                    //  Input (Data,Var,Abs)               278
	0x09, 0x54,                    //  Usage (Contact Count)              280
	0x25, 0x7f,                    //  Logical Maximum (127)              282
	0x95, 0x01,                    //  Report Count (1)                   284
	0x75, 0x08,                    //  Report Size (8)                    286
	0x81, 0x02,                    //  Input (Data,Var,Abs)               288
	0x75, 0x08,                    //  Report Size (8)                    290
	0x95, 0x08,                    //  Report Count (8)                   292
	0x81, 0x03,                    //  Input (Cnst,Var,Abs)               294
	0x85, 0x05,                    //  Report ID (5)                      296
	0x09, 0x55,                    //  Usage (Contact Max)                298
	0x25, 0x0a,                    //  Logical Maximum (10)               300
	0x75, 0x08,                    //  Report Size (8)                    302
	0x95, 0x01,                    //  Report Count (1)                   304
	0xb1, 0x02,                    //  Feature (Data,Var,Abs)             306
	0x06, 0x00, 0xff,              //  Usage Page (Vendor Defined Page 1) 308
	0x09, 0xc5,                    //  Usage (Vendor Usage 0xc5)          311
	0x85, 0x06,                    //  Report ID (6)                      313
	0x15, 0x00,                    //  Logical Minimum (0)                315
	0x26, 0xff, 0x00,              //  Logical Maximum (255)              317
	0x75, 0x08,                    //  Report Size (8)                    320
	0x96, 0x00, 0x01,              //  Report Count (256)                 322
	0xb1, 0x02,                    //  Feature (Data,Var,Abs)             325
	0xc0,                          // End Collection                      327
};

SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_fix_rdesc_huion_kamvas_pro_19, struct hid_bpf_ctx *hctx)
{
	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);

	if (!data)
		return 0; /* EPERM check */

	__builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));

	return sizeof(fixed_rdesc);
}

/*
 * This tablet reports the 3rd button through invert, but this conflict
 * with the normal eraser mode.
 * Fortunately, before entering eraser mode, (so Invert = 1),
 * the tablet always sends an out-of-proximity event.
 * So we can detect that single event and:
 * - if there was none but the invert bit was toggled: this is the
 *   third button
 * - if there was this out-of-proximity event, we are entering
 *   eraser mode, and we will until the next out-of-proximity.
 */
SEC(HID_BPF_DEVICE_EVENT)
int BPF_PROG(kamvas_pro_19_fix_3rd_button, struct hid_bpf_ctx *hctx)
{
	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);

	if (!data)
		return 0; /* EPERM check */

	if (data[0] != 0x0a) /* not the pen report ID */
		return 0;

	/* stylus is out of range */
	if (!(data[1] & 0x40)) {
		prev_was_out_of_range = true;
		in_eraser_mode = false;
		return 0;
	}

	/* going into eraser mode (Invert = 1) only happens after an
	 * out of range event
	 */
	if (prev_was_out_of_range && (data[1] & 0x18))
		in_eraser_mode = true;

	/* eraser mode works fine */
	if (in_eraser_mode)
		return 0;

	/* copy the Invert bit reported for the 3rd button in bit 7 */
	if (data[1] & 0x08)
		data[1] |= 0x20;

	/* clear Invert bit now that it was copied */
	data[1] &= 0xf7;

	prev_was_out_of_range = false;

	return 0;
}

HID_BPF_OPS(huion_Kamvas_pro_19) = {
	.hid_rdesc_fixup = (void *)hid_fix_rdesc_huion_kamvas_pro_19,
	.hid_device_event = (void *)kamvas_pro_19_fix_3rd_button,
};

SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
	ctx->retval = ctx->rdesc_size != 328;
	if (ctx->retval)
		ctx->retval = -EINVAL;

	/* ensure the kernel isn't fixed already */
	if (ctx->rdesc[17] != 0x43) /* Secondary Tip Switch */
		ctx->retval = -EINVAL;

	struct hid_bpf_ctx *hctx = hid_bpf_allocate_context(ctx->hid);

	if (!hctx) {
		return ctx->retval = -EINVAL;
		return 0;
	}

	const char *name = hctx->hid->name;

	/* strip out TEST_PREFIX */
	if (!__builtin_memcmp(name, TEST_PREFIX, sizeof(TEST_PREFIX) - 1))
		name += sizeof(TEST_PREFIX) - 1;

	if (__builtin_memcmp(name, NAME_KAMVAS_PRO_19, sizeof(NAME_KAMVAS_PRO_19)))
		ctx->retval = -EINVAL;

	hid_bpf_release_context(hctx);

	return 0;
}

char _license[] SEC("license") = "GPL";