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