xref: /linux/drivers/hid/bpf/progs/Huion__Kamvas16Gen3.bpf.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
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