Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/bpf/progs/Huion__Kamvas16Gen3.bpf.c
38242 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/* Copyright (c) 2025 Nicholas LaPointe
3
* Copyright (c) 2025 Higgins Dragon
4
*/
5
6
#include "vmlinux.h"
7
#include "hid_bpf.h"
8
#include "hid_bpf_helpers.h"
9
#include "hid_report_helpers.h"
10
#include <bpf/bpf_tracing.h>
11
12
#define VID_HUION 0x256c
13
#define PID_KAMVAS16_GEN3 0x2009
14
15
#define VENDOR_DESCRIPTOR_LENGTH 36
16
#define TABLET_DESCRIPTOR_LENGTH 328
17
#define WHEEL_DESCRIPTOR_LENGTH 200
18
19
#define VENDOR_REPORT_ID 8
20
#define VENDOR_REPORT_LENGTH 14
21
22
#define VENDOR_REPORT_SUBTYPE_PEN 0x08
23
#define VENDOR_REPORT_SUBTYPE_PEN_OUT 0x00
24
#define VENDOR_REPORT_SUBTYPE_BUTTONS 0x0e
25
#define VENDOR_REPORT_SUBTYPE_WHEELS 0x0f
26
27
/* For the reports that we create ourselves */
28
#define CUSTOM_PAD_REPORT_ID 9
29
30
HID_BPF_CONFIG(
31
HID_DEVICE(BUS_USB, HID_GROUP_ANY, VID_HUION, PID_KAMVAS16_GEN3),
32
);
33
34
/*
35
* This tablet can send reports using one of two different data formats,
36
* depending on what "mode" the tablet is in.
37
*
38
* By default, the tablet will send reports that can be decoded using its
39
* included HID descriptors (descriptors 1 and 2, shown below).
40
* This mode will be called "firmware mode" throughout this file.
41
*
42
* The HID descriptor that describes pen events in firmware mode (descriptor 1)
43
* has multiple bugs:
44
* * "Secondary Tip Switch" instead of "Secondary Barrel Switch"
45
* * "Invert" instead of (or potentially shared with) third barrel button
46
* * Specified tablet area of 2048 in³ instead of 293.8 x 165.2mm
47
* * Specified tilt range of -90 to +90 instead of -60 to +60
48
*
49
* While these can be easily patched up by editing the descriptor, a larger
50
* problem with the firmware mode exists: it is impossible to tell which of the
51
* two wheels are being rotated (or having their central button pressed).
52
*
53
*
54
* By using a tool such as huion-switcher (https://github.com/whot/huion-switcher),
55
* the tablet can be made to send reports using a proprietary format that is not
56
* adequately described by its relevant descriptor (descriptor 0, shown below).
57
* This mode will be called "vendor mode" throughout this file.
58
*
59
* The reports sent while in vendor mode allow for proper decoding of the wheels.
60
*
61
* For simplicity and maximum functionality, this BPF focuses strictly on
62
* enabling one to make use of the vendor mode.
63
*/
64
65
/*
66
* DESCRIPTORS
67
* DESCRIPTOR 0
68
* # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 0
69
* # 0x09, 0x01, // Usage (Vendor Usage 1) 3
70
* # 0xa1, 0x01, // Collection (Application) 5
71
* # 0x85, 0x08, // Report ID (8) 7
72
* # 0x75, 0x68, // Report Size (104) 9
73
* # 0x95, 0x01, // Report Count (1) 11
74
* # 0x09, 0x01, // Usage (Vendor Usage 1) 13
75
* # 0x81, 0x02, // Input (Data,Var,Abs) 15
76
* # 0xc0, // End Collection 17
77
* # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 18
78
* # 0x09, 0x01, // Usage (Vendor Usage 1) 21
79
* # 0xa1, 0x01, // Collection (Application) 23
80
* # 0x85, 0x16, // Report ID (22) 25
81
* # 0x75, 0x08, // Report Size (8) 27
82
* # 0x95, 0x07, // Report Count (7) 29
83
* # 0x09, 0x01, // Usage (Vendor Usage 1) 31
84
* # 0xb1, 0x02, // Feature (Data,Var,Abs) 33
85
* # 0xc0, // End Collection 35
86
* #
87
* R: 36 06 00 ff 09 01 a1 01 85 08 75 68 95 01 09 01 81 02 c0 06 00 ff 09 01 a1 01 85 16 75 08 95 07 09 01 b1 02 c0
88
* N: HUION Huion Tablet_GS1563
89
* I: 3 256c 2009
90
*
91
*
92
* DESCRIPTOR 1
93
* # 0x05, 0x0d, // Usage Page (Digitizers) 0
94
* # 0x09, 0x02, // Usage (Pen) 2
95
* # 0xa1, 0x01, // Collection (Application) 4
96
* # 0x85, 0x0a, // Report ID (10) 6
97
* # 0x09, 0x20, // Usage (Stylus) 8
98
* # 0xa1, 0x01, // Collection (Application) 10
99
* # 0x09, 0x42, // Usage (Tip Switch) 12
100
* # 0x09, 0x44, // Usage (Barrel Switch) 14
101
* # 0x09, 0x43, // Usage (Secondary Tip Switch) 16
102
* # 0x09, 0x3c, // Usage (Invert) 18
103
* # 0x09, 0x45, // Usage (Eraser) 20
104
* # 0x15, 0x00, // Logical Minimum (0) 22
105
* # 0x25, 0x01, // Logical Maximum (1) 24
106
* # 0x75, 0x01, // Report Size (1) 26
107
* # 0x95, 0x06, // Report Count (6) 28
108
* # 0x81, 0x02, // Input (Data,Var,Abs) 30
109
* # 0x09, 0x32, // Usage (In Range) 32
110
* # 0x75, 0x01, // Report Size (1) 34
111
* # 0x95, 0x01, // Report Count (1) 36
112
* # 0x81, 0x02, // Input (Data,Var,Abs) 38
113
* # 0x81, 0x03, // Input (Cnst,Var,Abs) 40
114
* # 0x05, 0x01, // Usage Page (Generic Desktop) 42
115
* # 0x09, 0x30, // Usage (X) 44
116
* # 0x09, 0x31, // Usage (Y) 46
117
* # 0x55, 0x0d, // Unit Exponent (-3) 48
118
* # 0x65, 0x33, // Unit (EnglishLinear: in³) 50
119
* # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 52
120
* # 0x35, 0x00, // Physical Minimum (0) 55
121
* # 0x46, 0x00, 0x08, // Physical Maximum (2048) 57
122
* # 0x75, 0x10, // Report Size (16) 60
123
* # 0x95, 0x02, // Report Count (2) 62
124
* # 0x81, 0x02, // Input (Data,Var,Abs) 64
125
* # 0x05, 0x0d, // Usage Page (Digitizers) 66
126
* # 0x09, 0x30, // Usage (Tip Pressure) 68
127
* # 0x26, 0xff, 0x3f, // Logical Maximum (16383) 70
128
* # 0x75, 0x10, // Report Size (16) 73
129
* # 0x95, 0x01, // Report Count (1) 75
130
* # 0x81, 0x02, // Input (Data,Var,Abs) 77
131
* # 0x09, 0x3d, // Usage (X Tilt) 79
132
* # 0x09, 0x3e, // Usage (Y Tilt) 81
133
* # 0x15, 0xa6, // Logical Minimum (-90) 83
134
* # 0x25, 0x5a, // Logical Maximum (90) 85
135
* # 0x75, 0x08, // Report Size (8) 87
136
* # 0x95, 0x02, // Report Count (2) 89
137
* # 0x81, 0x02, // Input (Data,Var,Abs) 91
138
* # 0xc0, // End Collection 93
139
* # 0xc0, // End Collection 94
140
* # 0x05, 0x0d, // Usage Page (Digitizers) 95
141
* # 0x09, 0x04, // Usage (Touch Screen) 97
142
* # 0xa1, 0x01, // Collection (Application) 99
143
* # 0x85, 0x04, // Report ID (4) 101
144
* # 0x09, 0x22, // Usage (Finger) 103
145
* # 0xa1, 0x02, // Collection (Logical) 105
146
* # 0x05, 0x0d, // Usage Page (Digitizers) 107
147
* # 0x95, 0x01, // Report Count (1) 109
148
* # 0x75, 0x06, // Report Size (6) 111
149
* # 0x09, 0x51, // Usage (Contact Id) 113
150
* # 0x15, 0x00, // Logical Minimum (0) 115
151
* # 0x25, 0x3f, // Logical Maximum (63) 117
152
* # 0x81, 0x02, // Input (Data,Var,Abs) 119
153
* # 0x09, 0x42, // Usage (Tip Switch) 121
154
* # 0x25, 0x01, // Logical Maximum (1) 123
155
* # 0x75, 0x01, // Report Size (1) 125
156
* # 0x95, 0x01, // Report Count (1) 127
157
* # 0x81, 0x02, // Input (Data,Var,Abs) 129
158
* # 0x75, 0x01, // Report Size (1) 131
159
* # 0x95, 0x01, // Report Count (1) 133
160
* # 0x81, 0x03, // Input (Cnst,Var,Abs) 135
161
* # 0x05, 0x01, // Usage Page (Generic Desktop) 137
162
* # 0x75, 0x10, // Report Size (16) 139
163
* # 0x55, 0x0e, // Unit Exponent (-2) 141
164
* # 0x65, 0x11, // Unit (SILinear: cm) 143
165
* # 0x09, 0x30, // Usage (X) 145
166
* # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 147
167
* # 0x35, 0x00, // Physical Minimum (0) 150
168
* # 0x46, 0x15, 0x0c, // Physical Maximum (3093) 152
169
* # 0x81, 0x42, // Input (Data,Var,Abs,Null) 155
170
* # 0x09, 0x31, // Usage (Y) 157
171
* # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 159
172
* # 0x46, 0xcb, 0x06, // Physical Maximum (1739) 162
173
* # 0x81, 0x42, // Input (Data,Var,Abs,Null) 165
174
* # 0x05, 0x0d, // Usage Page (Digitizers) 167
175
* # 0x09, 0x30, // Usage (Tip Pressure) 169
176
* # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 171
177
* # 0x75, 0x10, // Report Size (16) 174
178
* # 0x95, 0x01, // Report Count (1) 176
179
* # 0x81, 0x02, // Input (Data,Var,Abs) 178
180
* # 0xc0, // End Collection 180
181
* # 0x05, 0x0d, // Usage Page (Digitizers) 181
182
* # 0x09, 0x22, // Usage (Finger) 183
183
* # 0xa1, 0x02, // Collection (Logical) 185
184
* # 0x05, 0x0d, // Usage Page (Digitizers) 187
185
* # 0x95, 0x01, // Report Count (1) 189
186
* # 0x75, 0x06, // Report Size (6) 191
187
* # 0x09, 0x51, // Usage (Contact Id) 193
188
* # 0x15, 0x00, // Logical Minimum (0) 195
189
* # 0x25, 0x3f, // Logical Maximum (63) 197
190
* # 0x81, 0x02, // Input (Data,Var,Abs) 199
191
* # 0x09, 0x42, // Usage (Tip Switch) 201
192
* # 0x25, 0x01, // Logical Maximum (1) 203
193
* # 0x75, 0x01, // Report Size (1) 205
194
* # 0x95, 0x01, // Report Count (1) 207
195
* # 0x81, 0x02, // Input (Data,Var,Abs) 209
196
* # 0x75, 0x01, // Report Size (1) 211
197
* # 0x95, 0x01, // Report Count (1) 213
198
* # 0x81, 0x03, // Input (Cnst,Var,Abs) 215
199
* # 0x05, 0x01, // Usage Page (Generic Desktop) 217
200
* # 0x75, 0x10, // Report Size (16) 219
201
* # 0x55, 0x0e, // Unit Exponent (-2) 221
202
* # 0x65, 0x11, // Unit (SILinear: cm) 223
203
* # 0x09, 0x30, // Usage (X) 225
204
* # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 227
205
* # 0x35, 0x00, // Physical Minimum (0) 230
206
* # 0x46, 0x15, 0x0c, // Physical Maximum (3093) 232
207
* # 0x81, 0x42, // Input (Data,Var,Abs,Null) 235
208
* # 0x09, 0x31, // Usage (Y) 237
209
* # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 239
210
* # 0x46, 0xcb, 0x06, // Physical Maximum (1739) 242
211
* # 0x81, 0x42, // Input (Data,Var,Abs,Null) 245
212
* # 0x05, 0x0d, // Usage Page (Digitizers) 247
213
* # 0x09, 0x30, // Usage (Tip Pressure) 249
214
* # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 251
215
* # 0x75, 0x10, // Report Size (16) 254
216
* # 0x95, 0x01, // Report Count (1) 256
217
* # 0x81, 0x02, // Input (Data,Var,Abs) 258
218
* # 0xc0, // End Collection 260
219
* # 0x05, 0x0d, // Usage Page (Digitizers) 261
220
* # 0x09, 0x56, // Usage (Scan Time) 263
221
* # 0x55, 0x00, // Unit Exponent (0) 265
222
* # 0x65, 0x00, // Unit (None) 267
223
* # 0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 269
224
* # 0x95, 0x01, // Report Count (1) 274
225
* # 0x75, 0x20, // Report Size (32) 276
226
* # 0x81, 0x02, // Input (Data,Var,Abs) 278
227
* # 0x09, 0x54, // Usage (Contact Count) 280
228
* # 0x25, 0x7f, // Logical Maximum (127) 282
229
* # 0x95, 0x01, // Report Count (1) 284
230
* # 0x75, 0x08, // Report Size (8) 286
231
* # 0x81, 0x02, // Input (Data,Var,Abs) 288
232
* # 0x75, 0x08, // Report Size (8) 290
233
* # 0x95, 0x08, // Report Count (8) 292
234
* # 0x81, 0x03, // Input (Cnst,Var,Abs) 294
235
* # 0x85, 0x05, // Report ID (5) 296
236
* # 0x09, 0x55, // Usage (Contact Max) 298
237
* # 0x25, 0x0a, // Logical Maximum (10) 300
238
* # 0x75, 0x08, // Report Size (8) 302
239
* # 0x95, 0x01, // Report Count (1) 304
240
* # 0xb1, 0x02, // Feature (Data,Var,Abs) 306
241
* # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 308
242
* # 0x09, 0xc5, // Usage (Vendor Usage 0xc5) 311
243
* # 0x85, 0x06, // Report ID (6) 313
244
* # 0x15, 0x00, // Logical Minimum (0) 315
245
* # 0x26, 0xff, 0x00, // Logical Maximum (255) 317
246
* # 0x75, 0x08, // Report Size (8) 320
247
* # 0x96, 0x00, 0x01, // Report Count (256) 322
248
* # 0xb1, 0x02, // Feature (Data,Var,Abs) 325
249
* # 0xc0, // End Collection 327
250
* #
251
* 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
252
* N: HUION Huion Tablet_GS1563
253
* I: 3 256c 2009
254
*
255
* DESCRIPTOR 2
256
* # 0x05, 0x01, // Usage Page (Generic Desktop) 0
257
* # 0x09, 0x0e, // Usage (System Multi-Axis Controller) 2
258
* # 0xa1, 0x01, // Collection (Application) 4
259
* # 0x85, 0x11, // Report ID (17) 6
260
* # 0x05, 0x0d, // Usage Page (Digitizers) 8
261
* # 0x09, 0x21, // Usage (Puck) 10
262
* # 0xa1, 0x02, // Collection (Logical) 12
263
* # 0x15, 0x00, // Logical Minimum (0) 14
264
* # 0x25, 0x01, // Logical Maximum (1) 16
265
* # 0x75, 0x01, // Report Size (1) 18
266
* # 0x95, 0x01, // Report Count (1) 20
267
* # 0xa1, 0x00, // Collection (Physical) 22
268
* # 0x05, 0x09, // Usage Page (Button) 24
269
* # 0x09, 0x01, // Usage (Vendor Usage 0x01) 26
270
* # 0x81, 0x02, // Input (Data,Var,Abs) 28
271
* # 0x05, 0x0d, // Usage Page (Digitizers) 30
272
* # 0x09, 0x33, // Usage (Touch) 32
273
* # 0x81, 0x02, // Input (Data,Var,Abs) 34
274
* # 0x95, 0x06, // Report Count (6) 36
275
* # 0x81, 0x03, // Input (Cnst,Var,Abs) 38
276
* # 0xa1, 0x02, // Collection (Logical) 40
277
* # 0x05, 0x01, // Usage Page (Generic Desktop) 42
278
* # 0x09, 0x37, // Usage (Dial) 44
279
* # 0x16, 0x00, 0x80, // Logical Minimum (-32768) 46
280
* # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 49
281
* # 0x75, 0x10, // Report Size (16) 52
282
* # 0x95, 0x01, // Report Count (1) 54
283
* # 0x81, 0x06, // Input (Data,Var,Rel) 56
284
* # 0x35, 0x00, // Physical Minimum (0) 58
285
* # 0x46, 0x10, 0x0e, // Physical Maximum (3600) 60
286
* # 0x15, 0x00, // Logical Minimum (0) 63
287
* # 0x26, 0x10, 0x0e, // Logical Maximum (3600) 65
288
* # 0x09, 0x48, // Usage (Resolution Multiplier) 68
289
* # 0xb1, 0x02, // Feature (Data,Var,Abs) 70
290
* # 0x45, 0x00, // Physical Maximum (0) 72
291
* # 0xc0, // End Collection 74
292
* # 0x75, 0x08, // Report Size (8) 75
293
* # 0x95, 0x01, // Report Count (1) 77
294
* # 0x81, 0x01, // Input (Cnst,Arr,Abs) 79
295
* # 0x75, 0x08, // Report Size (8) 81
296
* # 0x95, 0x01, // Report Count (1) 83
297
* # 0x81, 0x01, // Input (Cnst,Arr,Abs) 85
298
* # 0x75, 0x08, // Report Size (8) 87
299
* # 0x95, 0x01, // Report Count (1) 89
300
* # 0x81, 0x01, // Input (Cnst,Arr,Abs) 91
301
* # 0x75, 0x08, // Report Size (8) 93
302
* # 0x95, 0x01, // Report Count (1) 95
303
* # 0x81, 0x01, // Input (Cnst,Arr,Abs) 97
304
* # 0x75, 0x08, // Report Size (8) 99
305
* # 0x95, 0x01, // Report Count (1) 101
306
* # 0x81, 0x01, // Input (Cnst,Arr,Abs) 103
307
* # 0xc0, // End Collection 105
308
* # 0xc0, // End Collection 106
309
* # 0xc0, // End Collection 107
310
* # 0x05, 0x01, // Usage Page (Generic Desktop) 108
311
* # 0x09, 0x06, // Usage (Keyboard) 110
312
* # 0xa1, 0x01, // Collection (Application) 112
313
* # 0x85, 0x03, // Report ID (3) 114
314
* # 0x05, 0x07, // Usage Page (Keyboard) 116
315
* # 0x19, 0xe0, // Usage Minimum (224) 118
316
* # 0x29, 0xe7, // Usage Maximum (231) 120
317
* # 0x15, 0x00, // Logical Minimum (0) 122
318
* # 0x25, 0x01, // Logical Maximum (1) 124
319
* # 0x75, 0x01, // Report Size (1) 126
320
* # 0x95, 0x08, // Report Count (8) 128
321
* # 0x81, 0x02, // Input (Data,Var,Abs) 130
322
* # 0x05, 0x07, // Usage Page (Keyboard) 132
323
* # 0x19, 0x00, // Usage Minimum (0) 134
324
* # 0x29, 0xff, // Usage Maximum (255) 136
325
* # 0x26, 0xff, 0x00, // Logical Maximum (255) 138
326
* # 0x75, 0x08, // Report Size (8) 141
327
* # 0x95, 0x06, // Report Count (6) 143
328
* # 0x81, 0x00, // Input (Data,Arr,Abs) 145
329
* # 0xc0, // End Collection 147
330
* # 0x05, 0x0c, // Usage Page (Consumer Devices) 148
331
* # 0x09, 0x01, // Usage (Consumer Control) 150
332
* # 0xa1, 0x01, // Collection (Application) 152
333
* # 0x85, 0x04, // Report ID (4) 154
334
* # 0x19, 0x01, // Usage Minimum (1) 156
335
* # 0x2a, 0x9c, 0x02, // Usage Maximum (668) 158
336
* # 0x15, 0x01, // Logical Minimum (1) 161
337
* # 0x26, 0x9c, 0x02, // Logical Maximum (668) 163
338
* # 0x95, 0x01, // Report Count (1) 166
339
* # 0x75, 0x10, // Report Size (16) 168
340
* # 0x81, 0x00, // Input (Data,Arr,Abs) 170
341
* # 0xc0, // End Collection 172
342
* # 0x05, 0x01, // Usage Page (Generic Desktop) 173
343
* # 0x09, 0x80, // Usage (System Control) 175
344
* # 0xa1, 0x01, // Collection (Application) 177
345
* # 0x85, 0x05, // Report ID (5) 179
346
* # 0x19, 0x81, // Usage Minimum (129) 181
347
* # 0x29, 0x83, // Usage Maximum (131) 183
348
* # 0x15, 0x00, // Logical Minimum (0) 185
349
* # 0x25, 0x01, // Logical Maximum (1) 187
350
* # 0x75, 0x01, // Report Size (1) 189
351
* # 0x95, 0x03, // Report Count (3) 191
352
* # 0x81, 0x02, // Input (Data,Var,Abs) 193
353
* # 0x95, 0x05, // Report Count (5) 195
354
* # 0x81, 0x01, // Input (Cnst,Arr,Abs) 197
355
* # 0xc0, // End Collection 199
356
* #
357
* 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
358
* N: HUION Huion Tablet_GS1563
359
* I: 3 256c 2009
360
*
361
*
362
*
363
* VENDOR MODE
364
* HUION_FIRMWARE_ID="HUION_M22d_241101"
365
* HUION_MAGIC_BYTES="1403201101ac9900ff3fd81305080080083c4010"
366
*
367
* MAGIC BYTES
368
* [LogicalMaximum, X ] [LogicalMaximum, Y ] [LogicalMaximum, Pressure] [ LPI]
369
* 14 03 [ 20 11 01] [ ac 99 00] [ ff 3f] [d8 13] 05 08 00 80 08 3c 40 10
370
*
371
* See Huion__Kamvas13Gen3.bpf.c for more details on detailed button/dial reports and caveats. It's very
372
* similar to the Kamvas 16 Gen 3.
373
*/
374
375
376
/* Filled in by udev-hid-bpf */
377
char UDEV_PROP_HUION_FIRMWARE_ID[64];
378
379
char EXPECTED_FIRMWARE_ID[] = "HUION_M22d_";
380
381
__u8 last_button_state;
382
383
static const __u8 disabled_rdesc_tablet[] = {
384
FixedSizeVendorReport(28) /* Input report 4 */
385
};
386
387
static const __u8 disabled_rdesc_wheel[] = {
388
FixedSizeVendorReport(9) /* Input report 17 */
389
};
390
391
static const __u8 fixed_rdesc_vendor[] = {
392
UsagePage_Digitizers
393
Usage_Dig_Pen
394
CollectionApplication(
395
ReportId(VENDOR_REPORT_ID)
396
UsagePage_Digitizers
397
Usage_Dig_Pen
398
CollectionPhysical(
399
/*
400
* I have only examined the tablet's behavior while using
401
* the PW600L pen, which does not have an eraser.
402
* Because of this, I don't know where the Eraser and Invert
403
* bits will go, or if they work as one would expect.
404
*
405
* For the time being, there is no expectation that a pen
406
* with an eraser will work without modifications here.
407
*/
408
ReportSize(1)
409
LogicalMinimum_i8(0)
410
LogicalMaximum_i8(1)
411
ReportCount(3)
412
Usage_Dig_TipSwitch
413
Usage_Dig_BarrelSwitch
414
Usage_Dig_SecondaryBarrelSwitch
415
Input(Var|Abs)
416
PushPop(
417
ReportCount(1)
418
UsagePage_Button
419
Usage_i8(0x4a) /* (BTN_STYLUS3 + 1) & 0xff */
420
Input(Var|Abs)
421
)
422
ReportCount(3)
423
Input(Const)
424
ReportCount(1)
425
Usage_Dig_InRange
426
Input(Var|Abs)
427
ReportSize(16)
428
ReportCount(1)
429
PushPop(
430
UsagePage_GenericDesktop
431
Unit(cm)
432
UnitExponent(-2)
433
LogicalMinimum_i16(0)
434
PhysicalMinimum_i16(0)
435
/*
436
* The tablet has a logical maximum of 69920 x 39340
437
* and a claimed resolution of 5080 LPI (200 L/mm)
438
* This works out to a physical maximum of
439
* 349.6 x 196.7mm, which matches Huion's advertised
440
* (rounded) active area dimensions from
441
* https://www.huion.com/products/pen_display/Kamvas/kamvas-16-gen-3.html
442
*
443
* The Kamvas uses data[8] for the 3rd byte of the X-axis, and adding
444
* that after data[2] and data[3] makes a contiguous little-endian
445
* 24-bit value. (See BPF_PROG below)
446
*/
447
ReportSize(24)
448
LogicalMaximum_i32(69920)
449
PhysicalMaximum_i16(3496)
450
Usage_GD_X
451
Input(Var|Abs)
452
ReportSize(16)
453
LogicalMaximum_i16(39340)
454
PhysicalMaximum_i16(1967)
455
Usage_GD_Y
456
Input(Var|Abs)
457
)
458
ReportSize(16)
459
LogicalMinimum_i16(0)
460
LogicalMaximum_i16(16383)
461
Usage_Dig_TipPressure
462
Input(Var|Abs)
463
ReportSize(8)
464
ReportCount(1)
465
Input(Const)
466
ReportCount(2)
467
PushPop(
468
Unit(deg)
469
UnitExponent(0)
470
LogicalMinimum_i8(-60)
471
PhysicalMinimum_i8(-60)
472
LogicalMaximum_i8(60)
473
PhysicalMaximum_i8(60)
474
Usage_Dig_XTilt
475
Usage_Dig_YTilt
476
Input(Var|Abs)
477
)
478
)
479
)
480
UsagePage_GenericDesktop
481
Usage_GD_Keypad
482
CollectionApplication(
483
ReportId(CUSTOM_PAD_REPORT_ID)
484
LogicalMinimum_i8(0)
485
LogicalMaximum_i8(1)
486
UsagePage_Digitizers
487
Usage_Dig_TabletFunctionKeys
488
CollectionPhysical(
489
/*
490
* The first 3 bytes are somewhat vestigial and will
491
* always be set to zero. Their presence here is needed
492
* to ensure that this device will be detected as a
493
* tablet pad by software that otherwise wouldn't know
494
* any better.
495
*/
496
/* (data[1] & 0x01) barrel switch */
497
ReportSize(1)
498
ReportCount(1)
499
Usage_Dig_BarrelSwitch
500
Input(Var|Abs)
501
ReportCount(7)
502
Input(Const)
503
/* data[2] X */
504
/* data[3] Y */
505
ReportSize(8)
506
ReportCount(2)
507
UsagePage_GenericDesktop
508
Usage_GD_X
509
Usage_GD_Y
510
Input(Var|Abs)
511
/*
512
* (data[4] & 0x01) button 1
513
* (data[4] & 0x02) button 2
514
* (data[4] & 0x04) button 3
515
* (data[4] & 0x08) button 4
516
* (data[4] & 0x10) button 5
517
* (data[4] & 0x20) button 6
518
* (data[4] & 0x40) button 7 (top wheel button)
519
* (data[4] & 0x80) button 8 (bottom wheel button)
520
*/
521
ReportSize(1)
522
ReportCount(8)
523
UsagePage_Button
524
UsageMinimum_i8(1)
525
UsageMaximum_i8(8)
526
Input(Var|Abs)
527
/* data[5] top wheel (signed, positive clockwise) */
528
ReportSize(8)
529
ReportCount(1)
530
UsagePage_GenericDesktop
531
Usage_GD_Wheel
532
LogicalMinimum_i8(-1)
533
LogicalMaximum_i8(1)
534
Input(Var|Rel)
535
/* data[6] bottom wheel (signed, positive clockwise) */
536
UsagePage_Consumer
537
Usage_Con_ACPan
538
Input(Var|Rel)
539
)
540
/*
541
* The kernel will drop reports that are bigger than the
542
* largest report specified in the HID descriptor.
543
* Therefore, our modified descriptor needs to have at least one
544
* HID report that is as long as, or longer than, the largest
545
* report in the original descriptor.
546
*
547
* This macro expands to a no-op report that is padded to the
548
* provided length.
549
*/
550
FixedSizeVendorReport(VENDOR_REPORT_LENGTH)
551
)
552
};
553
554
SEC(HID_BPF_RDESC_FIXUP)
555
int BPF_PROG(hid_fix_rdesc_huion_kamvas16_gen3, struct hid_bpf_ctx *hid_ctx)
556
{
557
__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
558
__s32 rdesc_size = hid_ctx->size;
559
__u8 have_fw_id;
560
561
if (!data)
562
return 0; /* EPERM check */
563
564
have_fw_id = __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID,
565
EXPECTED_FIRMWARE_ID,
566
sizeof(EXPECTED_FIRMWARE_ID) - 1) == 0;
567
568
if (have_fw_id) {
569
/*
570
* Tablet should be in vendor mode.
571
* Disable the unused devices
572
*/
573
if (rdesc_size == TABLET_DESCRIPTOR_LENGTH) {
574
__builtin_memcpy(data, disabled_rdesc_tablet,
575
sizeof(disabled_rdesc_tablet));
576
return sizeof(disabled_rdesc_tablet);
577
}
578
579
if (rdesc_size == WHEEL_DESCRIPTOR_LENGTH) {
580
__builtin_memcpy(data, disabled_rdesc_wheel,
581
sizeof(disabled_rdesc_wheel));
582
return sizeof(disabled_rdesc_wheel);
583
}
584
}
585
586
/*
587
* Regardless of which mode the tablet is in, always fix the vendor
588
* descriptor in case the udev property just happened to not be set
589
*/
590
if (rdesc_size == VENDOR_DESCRIPTOR_LENGTH) {
591
__builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor));
592
return sizeof(fixed_rdesc_vendor);
593
}
594
595
return 0;
596
}
597
598
SEC(HID_BPF_DEVICE_EVENT)
599
int BPF_PROG(hid_fix_event_huion_kamvas16_gen3, struct hid_bpf_ctx *hid_ctx)
600
{
601
__u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, VENDOR_REPORT_LENGTH /* size */);
602
603
if (!data)
604
return 0; /* EPERM check */
605
606
/* Handle vendor reports only */
607
if (hid_ctx->size != VENDOR_REPORT_LENGTH)
608
return 0;
609
if (data[0] != VENDOR_REPORT_ID)
610
return 0;
611
612
__u8 report_subtype = (data[1] >> 4) & 0x0f;
613
614
if (report_subtype == VENDOR_REPORT_SUBTYPE_PEN ||
615
report_subtype == VENDOR_REPORT_SUBTYPE_PEN_OUT) {
616
/* Invert Y tilt */
617
data[11] = -data[11];
618
619
/*
620
* Rearrange the bytes of the report so that
621
* [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
622
* will be arranged as
623
* [0, 1, 2, 3, 8, 4, 5, 6, 7, 9, 10, 11, 12, 13]
624
*/
625
__u8 x_24 = data[8];
626
627
data[8] = data[7];
628
data[7] = data[6];
629
data[6] = data[5];
630
data[5] = data[4];
631
632
data[4] = x_24;
633
634
} else if (report_subtype == VENDOR_REPORT_SUBTYPE_BUTTONS ||
635
report_subtype == VENDOR_REPORT_SUBTYPE_WHEELS) {
636
struct pad_report {
637
__u8 report_id;
638
__u8 btn_stylus:1;
639
__u8 padding:7;
640
__u8 x;
641
__u8 y;
642
__u8 buttons;
643
__s8 top_wheel;
644
__s8 bottom_wheel;
645
} __attribute__((packed)) *pad_report;
646
647
__s8 top_wheel = 0;
648
__s8 bottom_wheel = 0;
649
650
switch (report_subtype) {
651
case VENDOR_REPORT_SUBTYPE_WHEELS:
652
/*
653
* The wheel direction byte is 1 for clockwise rotation
654
* and 2 for counter-clockwise.
655
* Change it to 1 and -1, respectively.
656
*/
657
switch (data[3]) {
658
case 1:
659
top_wheel = (data[5] == 1) ? 1 : -1;
660
break;
661
case 2:
662
bottom_wheel = (data[5] == 1) ? 1 : -1;
663
break;
664
}
665
break;
666
667
case VENDOR_REPORT_SUBTYPE_BUTTONS:
668
/*
669
* If a button is already being held, ignore any new
670
* button event unless it's a release.
671
*
672
* The tablet only cleanly handles one button being held
673
* at a time, and trying to hold multiple buttons
674
* (particularly wheel+pad buttons) can result in sequences
675
* of reports that look like imaginary presses and releases.
676
*
677
* This is an imperfect way to filter out some of these
678
* reports.
679
*/
680
if (last_button_state != 0x00 && data[4] != 0x00)
681
break;
682
683
last_button_state = data[4];
684
break;
685
}
686
687
pad_report = (struct pad_report *)data;
688
689
pad_report->report_id = CUSTOM_PAD_REPORT_ID;
690
pad_report->btn_stylus = 0;
691
pad_report->x = 0;
692
pad_report->y = 0;
693
pad_report->buttons = last_button_state;
694
pad_report->top_wheel = top_wheel;
695
pad_report->bottom_wheel = bottom_wheel;
696
697
return sizeof(struct pad_report);
698
}
699
700
return 0;
701
}
702
703
HID_BPF_OPS(huion_kamvas16_gen3) = {
704
.hid_device_event = (void *)hid_fix_event_huion_kamvas16_gen3,
705
.hid_rdesc_fixup = (void *)hid_fix_rdesc_huion_kamvas16_gen3,
706
};
707
708
SEC("syscall")
709
int probe(struct hid_bpf_probe_args *ctx)
710
{
711
switch (ctx->rdesc_size) {
712
case VENDOR_DESCRIPTOR_LENGTH:
713
case TABLET_DESCRIPTOR_LENGTH:
714
case WHEEL_DESCRIPTOR_LENGTH:
715
ctx->retval = 0;
716
break;
717
default:
718
ctx->retval = -EINVAL;
719
}
720
721
return 0;
722
}
723
724
char _license[] SEC("license") = "GPL";
725
726