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