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