File Index
Files and directories
Sign in
FILE

Huion__Kamvas16Gen3.bpf.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hid/bpf/progs/Huion__Kamvas16Gen3.bpf.c C
  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
 30HID_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 */
377char UDEV_PROP_HUION_FIRMWARE_ID[64];
378
379char EXPECTED_FIRMWARE_ID[] = "HUION_M22d_";
380
381__u8 last_button_state;
382
383static const __u8 disabled_rdesc_tablet[] = {
384	FixedSizeVendorReport(28)	/* Input report 4 */
385};
386
387static const __u8 disabled_rdesc_wheel[] = {
388	FixedSizeVendorReport(9)	/* Input report 17 */
389};
390
391static 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
554SEC(HID_BPF_RDESC_FIXUP)
555int 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
598SEC(HID_BPF_DEVICE_EVENT)
599int 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
703HID_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
708SEC("syscall")
709int 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
724char _license[] SEC("license") = "GPL";
~/ Read-only file preview.