1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (c) 2024 Red Hat, Inc 3 */ 4 5 #include "vmlinux.h" 6 #include "hid_bpf.h" 7 #include "hid_bpf_helpers.h" 8 #include "hid_report_helpers.h" 9 #include <bpf/bpf_tracing.h> 10 11 #define VID_HUION 0x256C 12 #define PID_DIAL_2 0x0060 13 14 15 HID_BPF_CONFIG( 16 HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HUION, PID_DIAL_2), 17 ); 18 19 /* Filled in by udev-hid-bpf */ 20 char UDEV_PROP_HUION_FIRMWARE_ID[64]; 21 22 /* The prefix of the firmware ID we expect for this device. The full firmware 23 * string has a date suffix, e.g. HUION_T21j_221221 24 */ 25 char EXPECTED_FIRMWARE_ID[] = "HUION_T216_"; 26 27 /* How this BPF program works: the tablet has two modes, firmware mode and 28 * tablet mode. In firmware mode (out of the box) the tablet sends button events 29 * and the dial as keyboard combinations. In tablet mode it uses a vendor specific 30 * hid report to report everything instead. 31 * Depending on the mode some hid reports are never sent and the corresponding 32 * devices are mute. 33 * 34 * To switch the tablet use e.g. https://github.com/whot/huion-switcher 35 * or one of the tools from the digimend project 36 * 37 * This BPF works for both modes. The huion-switcher tool sets the 38 * HUION_FIRMWARE_ID udev property - if that is set then we disable the firmware 39 * pad and pen reports (by making them vendor collections that are ignored). 40 * If that property is not set we fix all hidraw nodes so the tablet works in 41 * either mode though the drawback is that the device will show up twice if 42 * you bind it to all event nodes 43 * 44 * Default report descriptor for the first exposed hidraw node: 45 * 46 * # HUION Huion Tablet_Q630M 47 * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 0 48 * # 0x09, 0x01, // Usage (Vendor Usage 1) 3 49 * # 0xa1, 0x01, // Collection (Application) 5 50 * # 0x85, 0x08, // Report ID (8) 7 51 * # 0x75, 0x58, // Report Size (88) 9 52 * # 0x95, 0x01, // Report Count (1) 11 53 * # 0x09, 0x01, // Usage (Vendor Usage 1) 13 54 * # 0x81, 0x02, // Input (Data,Var,Abs) 15 55 * # 0xc0, // End Collection 17 56 * R: 18 06 00 ff 09 01 a1 01 85 08 75 58 95 01 09 01 81 02 c0 57 * 58 * This rdesc does nothing until the tablet is switched to raw mode, see 59 * https://github.com/whot/huion-switcher 60 * 61 * 62 * Second hidraw node is the Pen. This one sends events until the tablet is 63 * switched to raw mode, then it's mute. 64 * 65 * # Report descriptor length: 93 bytes 66 * # HUION Huion Tablet_Q630M 67 * # 0x05, 0x0d, // Usage Page (Digitizers) 0 68 * # 0x09, 0x02, // Usage (Pen) 2 69 * # 0xa1, 0x01, // Collection (Application) 4 70 * # 0x85, 0x0a, // Report ID (10) 6 71 * # 0x09, 0x20, // Usage (Stylus) 8 72 * # 0xa1, 0x01, // Collection (Application) 10 73 * # 0x09, 0x42, // Usage (Tip Switch) 12 74 * # 0x09, 0x44, // Usage (Barrel Switch) 14 75 * # 0x09, 0x45, // Usage (Eraser) 16 76 * # 0x09, 0x3c, // Usage (Invert) 18 77 * # 0x15, 0x00, // Logical Minimum (0) 20 78 * # 0x25, 0x01, // Logical Maximum (1) 22 79 * # 0x75, 0x01, // Report Size (1) 24 80 * # 0x95, 0x06, // Report Count (6) 26 81 * # 0x81, 0x02, // Input (Data,Var,Abs) 28 82 * # 0x09, 0x32, // Usage (In Range) 30 83 * # 0x75, 0x01, // Report Size (1) 32 84 * # 0x95, 0x01, // Report Count (1) 34 85 * # 0x81, 0x02, // Input (Data,Var,Abs) 36 86 * # 0x81, 0x03, // Input (Cnst,Var,Abs) 38 87 * # 0x05, 0x01, // Usage Page (Generic Desktop) 40 88 * # 0x09, 0x30, // Usage (X) 42 89 * # 0x09, 0x31, // Usage (Y) 44 90 * # 0x55, 0x0d, // Unit Exponent (-3) 46 91 * # 0x65, 0x33, // Unit (EnglishLinear: in³) 48 92 * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 50 93 * # 0x35, 0x00, // Physical Minimum (0) 53 94 * # 0x46, 0x00, 0x08, // Physical Maximum (2048) 55 95 * # 0x75, 0x10, // Report Size (16) 58 96 * # 0x95, 0x02, // Report Count (2) 60 97 * # 0x81, 0x02, // Input (Data,Var,Abs) 62 98 * # 0x05, 0x0d, // Usage Page (Digitizers) 64 99 * # 0x09, 0x30, // Usage (Tip Pressure) 66 100 * # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 68 101 * # 0x75, 0x10, // Report Size (16) 71 102 * # 0x95, 0x01, // Report Count (1) 73 103 * # 0x81, 0x02, // Input (Data,Var,Abs) 75 104 * # 0x09, 0x3d, // Usage (X Tilt) 77 105 * # 0x09, 0x3e, // Usage (Y Tilt) 79 106 * # 0x15, 0x81, // Logical Minimum (-127) 81 107 * # 0x25, 0x7f, // Logical Maximum (127) 83 108 * # 0x75, 0x08, // Report Size (8) 85 109 * # 0x95, 0x02, // Report Count (2) 87 110 * # 0x81, 0x02, // Input (Data,Var,Abs) 89 111 * # 0xc0, // End Collection 91 112 * # 0xc0, // End Collection 92 113 * R: 93 05 0d 09 02 a1 01 85 0a 09 20 a1 01 09 42 09 44 09 45 09 3c 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 1f 75 10 95 01 81 02 09 3d 09 3e 15 81 25 7f 75 08 95 02 81 02 c0 c0 114 * 115 * Third hidraw node is the pad which sends a combination of keyboard shortcuts until 116 * the tablet is switched to raw mode, then it's mute: 117 * 118 * # Report descriptor length: 148 bytes 119 * # HUION Huion Tablet_Q630M 120 * # 0x05, 0x01, // Usage Page (Generic Desktop) 0 121 * # 0x09, 0x0e, // Usage (System Multi-Axis Controller) 2 122 * # 0xa1, 0x01, // Collection (Application) 4 123 * # 0x85, 0x11, // Report ID (17) 6 124 * # 0x05, 0x0d, // Usage Page (Digitizers) 8 125 * # 0x09, 0x21, // Usage (Puck) 10 126 * # 0xa1, 0x02, // Collection (Logical) 12 127 * # 0x15, 0x00, // Logical Minimum (0) 14 128 * # 0x25, 0x01, // Logical Maximum (1) 16 129 * # 0x75, 0x01, // Report Size (1) 18 130 * # 0x95, 0x01, // Report Count (1) 20 131 * # 0xa1, 0x00, // Collection (Physical) 22 132 * # 0x05, 0x09, // Usage Page (Button) 24 133 * # 0x09, 0x01, // Usage (Vendor Usage 0x01) 26 134 * # 0x81, 0x02, // Input (Data,Var,Abs) 28 135 * # 0x05, 0x0d, // Usage Page (Digitizers) 30 136 * # 0x09, 0x33, // Usage (Touch) 32 137 * # 0x81, 0x02, // Input (Data,Var,Abs) 34 138 * # 0x95, 0x06, // Report Count (6) 36 139 * # 0x81, 0x03, // Input (Cnst,Var,Abs) 38 140 * # 0xa1, 0x02, // Collection (Logical) 40 141 * # 0x05, 0x01, // Usage Page (Generic Desktop) 42 142 * # 0x09, 0x37, // Usage (Dial) 44 143 * # 0x16, 0x00, 0x80, // Logical Minimum (-32768) 46 144 * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 49 145 * # 0x75, 0x10, // Report Size (16) 52 146 * # 0x95, 0x01, // Report Count (1) 54 147 * # 0x81, 0x06, // Input (Data,Var,Rel) 56 148 * # 0x35, 0x00, // Physical Minimum (0) 58 149 * # 0x46, 0x10, 0x0e, // Physical Maximum (3600) 60 150 * # 0x15, 0x00, // Logical Minimum (0) 63 151 * # 0x26, 0x10, 0x0e, // Logical Maximum (3600) 65 152 * # 0x09, 0x48, // Usage (Resolution Multiplier) 68 153 * # 0xb1, 0x02, // Feature (Data,Var,Abs) 70 154 * # 0x45, 0x00, // Physical Maximum (0) 72 155 * # 0xc0, // End Collection 74 156 * # 0x75, 0x08, // Report Size (8) 75 157 * # 0x95, 0x01, // Report Count (1) 77 158 * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 79 159 * # 0x75, 0x08, // Report Size (8) 81 160 * # 0x95, 0x01, // Report Count (1) 83 161 * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 85 162 * # 0x75, 0x08, // Report Size (8) 87 163 * # 0x95, 0x01, // Report Count (1) 89 164 * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 91 165 * # 0x75, 0x08, // Report Size (8) 93 166 * # 0x95, 0x01, // Report Count (1) 95 167 * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 97 168 * # 0x75, 0x08, // Report Size (8) 99 169 * # 0x95, 0x01, // Report Count (1) 101 170 * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 103 171 * # 0xc0, // End Collection 105 172 * # 0xc0, // End Collection 106 173 * # 0xc0, // End Collection 107 174 * # 0x05, 0x01, // Usage Page (Generic Desktop) 108 175 * # 0x09, 0x06, // Usage (Keyboard) 110 176 * # 0xa1, 0x01, // Collection (Application) 112 177 * # 0x85, 0x03, // Report ID (3) 114 178 * # 0x05, 0x07, // Usage Page (Keyboard) 116 179 * # 0x19, 0xe0, // Usage Minimum (224) 118 180 * # 0x29, 0xe7, // Usage Maximum (231) 120 181 * # 0x15, 0x00, // Logical Minimum (0) 122 182 * # 0x25, 0x01, // Logical Maximum (1) 124 183 * # 0x75, 0x01, // Report Size (1) 126 184 * # 0x95, 0x08, // Report Count (8) 128 185 * # 0x81, 0x02, // Input (Data,Var,Abs) 130 186 * # 0x05, 0x07, // Usage Page (Keyboard) 132 187 * # 0x19, 0x00, // Usage Minimum (0) 134 188 * # 0x29, 0xff, // Usage Maximum (255) 136 189 * # 0x26, 0xff, 0x00, // Logical Maximum (255) 138 190 * # 0x75, 0x08, // Report Size (8) 141 191 * # 0x95, 0x06, // Report Count (6) 143 192 * # 0x81, 0x00, // Input (Data,Arr,Abs) 145 193 * # 0xc0, // End Collection 147 194 * R: 148 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 195 */ 196 197 #define PAD_REPORT_DESCRIPTOR_LENGTH 148 198 #define PEN_REPORT_DESCRIPTOR_LENGTH 93 199 #define VENDOR_REPORT_DESCRIPTOR_LENGTH 18 200 #define PAD_REPORT_ID 3 201 #define DIAL_REPORT_ID 17 202 #define PEN_REPORT_ID 10 203 #define VENDOR_REPORT_ID 8 204 #define PAD_REPORT_LENGTH 9 205 #define PEN_REPORT_LENGTH 10 206 #define VENDOR_REPORT_LENGTH 12 207 208 209 __u8 last_button_state; 210 211 static const __u8 fixed_rdesc_pad[] = { 212 UsagePage_GenericDesktop 213 Usage_GD_Keypad 214 CollectionApplication( 215 // -- Byte 0 in report 216 ReportId(PAD_REPORT_ID) 217 LogicalMaximum_i8(0) 218 LogicalMaximum_i8(1) 219 UsagePage_Digitizers 220 Usage_Dig_TabletFunctionKeys 221 CollectionPhysical( 222 // Byte 1 in report - just exists so we get to be a tablet pad 223 Usage_Dig_BarrelSwitch // BTN_STYLUS 224 ReportCount(1) 225 ReportSize(1) 226 Input(Var|Abs) 227 ReportCount(7) // padding 228 Input(Const) 229 // Bytes 2/3 in report - just exists so we get to be a tablet pad 230 UsagePage_GenericDesktop 231 Usage_GD_X 232 Usage_GD_Y 233 ReportCount(2) 234 ReportSize(8) 235 Input(Var|Abs) 236 // Byte 4 in report is the dial 237 Usage_GD_Wheel 238 LogicalMinimum_i8(-1) 239 LogicalMaximum_i8(1) 240 ReportCount(1) 241 ReportSize(8) 242 Input(Var|Rel) 243 // Byte 5 is the button state 244 UsagePage_Button 245 UsageMinimum_i8(0x01) 246 UsageMaximum_i8(0x08) 247 LogicalMinimum_i8(0x0) 248 LogicalMaximum_i8(0x1) 249 ReportCount(7) 250 ReportSize(1) 251 Input(Var|Abs) 252 ReportCount(1) // padding 253 Input(Const) 254 ) 255 // Make sure we match our original report length 256 FixedSizeVendorReport(PAD_REPORT_LENGTH) 257 ) 258 }; 259 260 static const __u8 fixed_rdesc_pen[] = { 261 UsagePage_Digitizers 262 Usage_Dig_Pen 263 CollectionApplication( 264 // -- Byte 0 in report 265 ReportId(PEN_REPORT_ID) 266 Usage_Dig_Pen 267 CollectionPhysical( 268 // -- Byte 1 in report 269 Usage_Dig_TipSwitch 270 Usage_Dig_BarrelSwitch 271 Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2 272 LogicalMinimum_i8(0) 273 LogicalMaximum_i8(1) 274 ReportSize(1) 275 ReportCount(3) 276 Input(Var|Abs) 277 ReportCount(4) // Padding 278 Input(Const) 279 Usage_Dig_InRange 280 ReportCount(1) 281 Input(Var|Abs) 282 ReportSize(16) 283 ReportCount(1) 284 PushPop( 285 UsagePage_GenericDesktop 286 Unit(cm) 287 UnitExponent(-1) 288 PhysicalMinimum_i16(0) 289 PhysicalMaximum_i16(266) 290 LogicalMinimum_i16(0) 291 LogicalMaximum_i16(32767) 292 Usage_GD_X 293 Input(Var|Abs) // Bytes 2+3 294 PhysicalMinimum_i16(0) 295 PhysicalMaximum_i16(166) 296 LogicalMinimum_i16(0) 297 LogicalMaximum_i16(32767) 298 Usage_GD_Y 299 Input(Var|Abs) // Bytes 4+5 300 ) 301 UsagePage_Digitizers 302 Usage_Dig_TipPressure 303 LogicalMinimum_i16(0) 304 LogicalMaximum_i16(8191) 305 Input(Var|Abs) // Byte 6+7 306 ReportSize(8) 307 ReportCount(2) 308 LogicalMinimum_i8(-60) 309 LogicalMaximum_i8(60) 310 Usage_Dig_XTilt 311 Usage_Dig_YTilt 312 Input(Var|Abs) // Byte 8+9 313 ) 314 ) 315 }; 316 317 static const __u8 fixed_rdesc_vendor[] = { 318 UsagePage_Digitizers 319 Usage_Dig_Pen 320 CollectionApplication( 321 // Byte 0 322 // We leave the pen on the vendor report ID 323 ReportId(VENDOR_REPORT_ID) 324 Usage_Dig_Pen 325 CollectionPhysical( 326 // Byte 1 are the buttons 327 LogicalMinimum_i8(0) 328 LogicalMaximum_i8(1) 329 ReportSize(1) 330 Usage_Dig_TipSwitch 331 Usage_Dig_BarrelSwitch 332 Usage_Dig_SecondaryBarrelSwitch 333 ReportCount(3) 334 Input(Var|Abs) 335 ReportCount(4) // Padding 336 Input(Const) 337 Usage_Dig_InRange 338 ReportCount(1) 339 Input(Var|Abs) 340 ReportSize(16) 341 ReportCount(1) 342 PushPop( 343 UsagePage_GenericDesktop 344 Unit(cm) 345 UnitExponent(-1) 346 // Note: reported logical range differs 347 // from the pen report ID for x and y 348 LogicalMinimum_i16(0) 349 LogicalMaximum_i16(53340) 350 PhysicalMinimum_i16(0) 351 PhysicalMaximum_i16(266) 352 // Bytes 2/3 in report 353 Usage_GD_X 354 Input(Var|Abs) 355 LogicalMinimum_i16(0) 356 LogicalMaximum_i16(33340) 357 PhysicalMinimum_i16(0) 358 PhysicalMaximum_i16(166) 359 // Bytes 4/5 in report 360 Usage_GD_Y 361 Input(Var|Abs) 362 ) 363 // Bytes 6/7 in report 364 LogicalMinimum_i16(0) 365 LogicalMaximum_i16(8191) 366 Usage_Dig_TipPressure 367 Input(Var|Abs) 368 // Bytes 8/9 in report 369 ReportCount(1) // Padding 370 Input(Const) 371 LogicalMinimum_i8(-60) 372 LogicalMaximum_i8(60) 373 // Byte 10 in report 374 Usage_Dig_XTilt 375 // Byte 11 in report 376 Usage_Dig_YTilt 377 ReportSize(8) 378 ReportCount(2) 379 Input(Var|Abs) 380 ) 381 ) 382 UsagePage_GenericDesktop 383 Usage_GD_Keypad 384 CollectionApplication( 385 // Byte 0 386 ReportId(PAD_REPORT_ID) 387 LogicalMinimum_i8(0) 388 LogicalMaximum_i8(1) 389 UsagePage_Digitizers 390 Usage_Dig_TabletFunctionKeys 391 CollectionPhysical( 392 // Byte 1 are the buttons 393 Usage_Dig_BarrelSwitch // BTN_STYLUS, needed so we get to be a tablet pad 394 ReportCount(1) 395 ReportSize(1) 396 Input(Var|Abs) 397 ReportCount(7) // Padding 398 Input(Const) 399 // Bytes 2/3 - x/y just exist so we get to be a tablet pad 400 UsagePage_GenericDesktop 401 Usage_GD_X 402 Usage_GD_Y 403 ReportCount(2) 404 ReportSize(8) 405 Input(Var|Abs) 406 // Byte 4 is the button state 407 UsagePage_Button 408 UsageMinimum_i8(0x1) 409 UsageMaximum_i8(0x8) 410 LogicalMinimum_i8(0x0) 411 LogicalMaximum_i8(0x1) 412 ReportCount(8) 413 ReportSize(1) 414 Input(Var|Abs) 415 // Byte 5 is the top dial 416 UsagePage_GenericDesktop 417 Usage_GD_Wheel 418 LogicalMinimum_i8(-1) 419 LogicalMaximum_i8(1) 420 ReportCount(1) 421 ReportSize(8) 422 Input(Var|Rel) 423 // Byte 6 is the bottom dial 424 UsagePage_Consumer 425 Usage_Con_ACPan 426 Input(Var|Rel) 427 ) 428 // Make sure we match our original report length 429 FixedSizeVendorReport(VENDOR_REPORT_LENGTH) 430 ) 431 }; 432 433 static const __u8 disabled_rdesc_pen[] = { 434 FixedSizeVendorReport(PEN_REPORT_LENGTH) 435 }; 436 437 static const __u8 disabled_rdesc_pad[] = { 438 FixedSizeVendorReport(PAD_REPORT_LENGTH) 439 }; 440 441 SEC(HID_BPF_RDESC_FIXUP) 442 int BPF_PROG(dial_2_fix_rdesc, struct hid_bpf_ctx *hctx) 443 { 444 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */); 445 __s32 rdesc_size = hctx->size; 446 __u8 have_fw_id; 447 448 if (!data) 449 return 0; /* EPERM check */ 450 451 /* If we have a firmware ID and it matches our expected prefix, we 452 * disable the default pad/pen nodes. They won't send events 453 * but cause duplicate devices. 454 */ 455 have_fw_id = __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID, 456 EXPECTED_FIRMWARE_ID, 457 sizeof(EXPECTED_FIRMWARE_ID) - 1) == 0; 458 if (rdesc_size == PAD_REPORT_DESCRIPTOR_LENGTH) { 459 if (have_fw_id) { 460 __builtin_memcpy(data, disabled_rdesc_pad, sizeof(disabled_rdesc_pad)); 461 return sizeof(disabled_rdesc_pad); 462 } 463 464 __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad)); 465 return sizeof(fixed_rdesc_pad); 466 } 467 if (rdesc_size == PEN_REPORT_DESCRIPTOR_LENGTH) { 468 if (have_fw_id) { 469 __builtin_memcpy(data, disabled_rdesc_pen, sizeof(disabled_rdesc_pen)); 470 return sizeof(disabled_rdesc_pen); 471 } 472 473 __builtin_memcpy(data, fixed_rdesc_pen, sizeof(fixed_rdesc_pen)); 474 return sizeof(fixed_rdesc_pen); 475 } 476 /* Always fix the vendor mode so the tablet will work even if nothing sets 477 * the udev property (e.g. huion-switcher run manually) 478 */ 479 if (rdesc_size == VENDOR_REPORT_DESCRIPTOR_LENGTH) { 480 __builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor)); 481 return sizeof(fixed_rdesc_vendor); 482 483 } 484 return 0; 485 } 486 487 SEC(HID_BPF_DEVICE_EVENT) 488 int BPF_PROG(dial_2_fix_events, struct hid_bpf_ctx *hctx) 489 { 490 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 16 /* size */); 491 static __u8 button; 492 493 if (!data) 494 return 0; /* EPERM check */ 495 496 /* Only sent if tablet is in default mode */ 497 if (data[0] == PAD_REPORT_ID) { 498 /* Nicely enough, this device only supports one button down at a time so 499 * the reports are easy to match. Buttons numbered from the top 500 * Button released: 03 00 00 00 00 00 00 00 501 * Button 1: 03 00 05 00 00 00 00 00 -> b 502 * Button 2: 03 00 08 00 00 00 00 00 -> e 503 * Button 3: 03 00 0c 00 00 00 00 00 -> i 504 * Button 4: 03 00 e0 16 00 00 00 00 -> Ctrl S 505 * Button 5: 03 00 2c 00 00 00 00 00 -> space 506 * Button 6: 03 00 e0 e2 1d 00 00 00 -> Ctrl Alt Z 507 */ 508 button &= 0xc0; 509 510 switch ((data[2] << 16) | (data[3] << 8) | data[4]) { 511 case 0x000000: 512 break; 513 case 0x050000: 514 button |= BIT(0); 515 break; 516 case 0x080000: 517 button |= BIT(1); 518 break; 519 case 0x0c0000: 520 button |= BIT(2); 521 break; 522 case 0xe01600: 523 button |= BIT(3); 524 break; 525 case 0x2c0000: 526 button |= BIT(4); 527 break; 528 case 0xe0e21d: 529 button |= BIT(5); 530 break; 531 } 532 533 __u8 report[8] = {PAD_REPORT_ID, 0x0, 0x0, 0x0, 0x00, button}; 534 535 __builtin_memcpy(data, report, sizeof(report)); 536 return sizeof(report); 537 } 538 539 /* Only sent if tablet is in default mode */ 540 if (data[0] == DIAL_REPORT_ID) { 541 /* 542 * In default mode, both dials are merged together: 543 * 544 * Dial down: 11 00 ff ff 00 00 00 00 00 -> Dial -1 545 * Dial up: 11 00 01 00 00 00 00 00 00 -> Dial 1 546 */ 547 __u16 dial = data[3] << 8 | data[2]; 548 549 button &= 0x3f; 550 button |= !!data[1] << 6; 551 552 __u8 report[] = {PAD_REPORT_ID, 0x0, 0x0, 0x0, dial, button}; 553 554 __builtin_memcpy(data, report, sizeof(report)); 555 return sizeof(report); 556 } 557 558 /* Nothing to do for the PEN_REPORT_ID, it's already mapped */ 559 560 /* Only sent if tablet is in raw mode */ 561 if (data[0] == VENDOR_REPORT_ID) { 562 /* Pad reports */ 563 if (data[1] & 0x20) { 564 /* See fixed_rdesc_pad */ 565 struct pad_report { 566 __u8 report_id; 567 __u8 btn_stylus; 568 __u8 x; 569 __u8 y; 570 __u8 buttons; 571 __u8 dial_1; 572 __u8 dial_2; 573 } __attribute__((packed)) *pad_report; 574 __u8 dial_1 = 0, dial_2 = 0; 575 576 /* Dial report */ 577 if (data[1] == 0xf1) { 578 __u8 d = 0; 579 580 if (data[5] == 2) 581 d = 0xff; 582 else 583 d = data[5]; 584 585 if (data[3] == 1) 586 dial_1 = d; 587 else 588 dial_2 = d; 589 } else { 590 /* data[4] are the buttons, mapped correctly */ 591 last_button_state = data[4]; 592 dial_1 = 0; // dial 593 dial_2 = 0; 594 } 595 596 pad_report = (struct pad_report *)data; 597 598 pad_report->report_id = PAD_REPORT_ID; 599 pad_report->btn_stylus = 0; 600 pad_report->x = 0; 601 pad_report->y = 0; 602 pad_report->buttons = last_button_state; 603 pad_report->dial_1 = dial_1; 604 pad_report->dial_2 = dial_2; 605 606 return sizeof(struct pad_report); 607 } 608 609 /* Pen reports need nothing done */ 610 } 611 612 return 0; 613 } 614 615 HID_BPF_OPS(inspiroy_dial2) = { 616 .hid_device_event = (void *)dial_2_fix_events, 617 .hid_rdesc_fixup = (void *)dial_2_fix_rdesc, 618 }; 619 620 SEC("syscall") 621 int probe(struct hid_bpf_probe_args *ctx) 622 { 623 switch (ctx->rdesc_size) { 624 case PAD_REPORT_DESCRIPTOR_LENGTH: 625 case PEN_REPORT_DESCRIPTOR_LENGTH: 626 case VENDOR_REPORT_DESCRIPTOR_LENGTH: 627 ctx->retval = 0; 628 break; 629 default: 630 ctx->retval = -EINVAL; 631 } 632 633 return 0; 634 } 635 636 char _license[] SEC("license") = "GPL"; 637