1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include "vmlinux.h" 4 #include "hid_bpf.h" 5 #include "hid_bpf_helpers.h" 6 #include "hid_report_helpers.h" 7 #include <bpf/bpf_tracing.h> 8 9 #define VID_UGEE 0x28BD 10 #define PID_DECO_02 0x0803 11 12 HID_BPF_CONFIG( 13 HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_DECO_02), 14 ); 15 16 /* 17 * Devices are: 18 * - Pad input, including pen (This is the only one we are interested in) 19 * - Pen input as mouse 20 * - Vendor 21 * 22 * Descriptors on main device are: 23 * - 7: Pen 24 * - 6: Vendor settings? Unclear 25 * - 3: Keyboard (This is what we want to modify) 26 * - 5: Feature report 27 * 28 * This creates three event nodes: 29 * - XP-PEN DECO 02 Stylus 30 * - XP-PEN DECO 02 31 * - XP-PEN DECO 02 Keyboard (Again, what we want to modify) 32 * 33 * # Report descriptor length: 188 bytes 34 * # 0x05, 0x0d, // Usage Page (Digitizers) 0 35 * # 0x09, 0x02, // Usage (Pen) 2 36 * # 0xa1, 0x01, // Collection (Application) 4 37 * # 0x85, 0x07, // Report ID (7) 6 38 * # 0x09, 0x20, // Usage (Stylus) 8 39 * # 0xa1, 0x00, // Collection (Physical) 10 40 * # 0x09, 0x42, // Usage (Tip Switch) 12 41 * # 0x09, 0x44, // Usage (Barrel Switch) 14 42 * # 0x09, 0x45, // Usage (Eraser) 16 43 * # 0x09, 0x3c, // Usage (Invert) 18 44 * # 0x09, 0x32, // Usage (In Range) 20 45 * # 0x15, 0x00, // Logical Minimum (0) 22 46 * # 0x25, 0x01, // Logical Maximum (1) 24 47 * # 0x75, 0x01, // Report Size (1) 26 48 * # 0x95, 0x05, // Report Count (5) 28 49 * # 0x81, 0x02, // Input (Data,Var,Abs) 30 50 * # 0x95, 0x03, // Report Count (3) 32 51 * # 0x81, 0x03, // Input (Cnst,Var,Abs) 34 52 * # 0x05, 0x01, // Usage Page (Generic Desktop) 36 53 * # 0x09, 0x30, // Usage (X) 38 54 * # 0x15, 0x00, // Logical Minimum (0) 40 55 * # 0x26, 0x50, 0x57, // Logical Maximum (22352) 42 56 * # 0x55, 0x0d, // Unit Exponent (-3) 45 57 * # 0x65, 0x13, // Unit (EnglishLinear: in) 47 58 * # 0x35, 0x00, // Physical Minimum (0) 49 59 * # 0x46, 0x50, 0x57, // Physical Maximum (22352) 51 60 * # 0x75, 0x10, // Report Size (16) 54 61 * # 0x95, 0x01, // Report Count (1) 56 62 * # 0x81, 0x02, // Input (Data,Var,Abs) 58 63 * # 0x09, 0x31, // Usage (Y) 60 64 * # 0x15, 0x00, // Logical Minimum (0) 62 65 * # 0x26, 0x92, 0x36, // Logical Maximum (13970) 64 66 * # 0x55, 0x0d, // Unit Exponent (-3) 67 67 * # 0x65, 0x13, // Unit (EnglishLinear: in) 69 68 * # 0x35, 0x00, // Physical Minimum (0) 71 69 * # 0x46, 0x92, 0x36, // Physical Maximum (13970) 73 70 * # 0x75, 0x10, // Report Size (16) 76 71 * # 0x95, 0x01, // Report Count (1) 78 72 * # 0x81, 0x02, // Input (Data,Var,Abs) 80 73 * # 0x05, 0x0d, // Usage Page (Digitizers) 82 74 * # 0x09, 0x30, // Usage (Tip Pressure) 84 75 * # 0x15, 0x00, // Logical Minimum (0) 86 76 * # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 88 77 * # 0x75, 0x10, // Report Size (16) 91 78 * # 0x95, 0x01, // Report Count (1) 93 79 * # 0x81, 0x02, // Input (Data,Var,Abs) 95 80 * # 0xc0, // End Collection 97 81 * # 0xc0, // End Collection 98 82 * # 0x09, 0x0e, // Usage (Device Configuration) 99 83 * # 0xa1, 0x01, // Collection (Application) 101 84 * # 0x85, 0x05, // Report ID (5) 103 85 * # 0x09, 0x23, // Usage (Device Settings) 105 86 * # 0xa1, 0x02, // Collection (Logical) 107 87 * # 0x09, 0x52, // Usage (Inputmode) 109 88 * # 0x09, 0x53, // Usage (Device Index) 111 89 * # 0x25, 0x0a, // Logical Maximum (10) 113 90 * # 0x75, 0x08, // Report Size (8) 115 91 * # 0x95, 0x02, // Report Count (2) 117 92 * # 0xb1, 0x02, // Feature (Data,Var,Abs) 119 93 * # 0xc0, // End Collection 121 94 * # 0xc0, // End Collection 122 95 * # 0x05, 0x0c, // Usage Page (Consumer Devices) 123 96 * # 0x09, 0x36, // Usage (Function Buttons) 125 97 * # 0xa1, 0x00, // Collection (Physical) 127 98 * # 0x85, 0x06, // Report ID (6) 129 99 * # 0x05, 0x09, // Usage Page (Button) 131 100 * # 0x19, 0x01, // Usage Minimum (1) 133 101 * # 0x29, 0x20, // Usage Maximum (32) 135 102 * # 0x15, 0x00, // Logical Minimum (0) 137 103 * # 0x25, 0x01, // Logical Maximum (1) 139 104 * # 0x95, 0x20, // Report Count (32) 141 105 * # 0x75, 0x01, // Report Size (1) 143 106 * # 0x81, 0x02, // Input (Data,Var,Abs) 145 107 * # 0xc0, // End Collection 147 108 * # 0x05, 0x01, // Usage Page (Generic Desktop) 148 109 * # 0x09, 0x06, // Usage (Keyboard) 150 110 * # 0xa1, 0x01, // Collection (Application) 152 111 * # 0x85, 0x03, // Report ID (3) 154 112 * # 0x05, 0x07, // Usage Page (Keyboard) 156 113 * # 0x19, 0xe0, // Usage Minimum (224) 158 114 * # 0x29, 0xe7, // Usage Maximum (231) 160 115 * # 0x15, 0x00, // Logical Minimum (0) 162 116 * # 0x25, 0x01, // Logical Maximum (1) 164 117 * # 0x75, 0x01, // Report Size (1) 166 118 * # 0x95, 0x08, // Report Count (8) 168 119 * # 0x81, 0x02, // Input (Data,Var,Abs) 170 120 * # 0x05, 0x07, // Usage Page (Keyboard) 172 121 * # 0x19, 0x00, // Usage Minimum (0) 174 122 * # 0x29, 0xff, // Usage Maximum (255) 176 123 * # 0x26, 0xff, 0x00, // Logical Maximum (255) 178 124 * # 0x75, 0x08, // Report Size (8) 181 125 * # 0x95, 0x06, // Report Count (6) 183 126 * # 0x81, 0x00, // Input (Data,Arr,Abs) 185 127 * # 0xc0, // End Collection 187 128 * 129 * Key events; top to bottom: 130 * Buttons released: 03 00 00 00 00 00 00 00 131 * Button1: 03 00 05 00 00 00 00 00 -> 'b and B' 132 * Button2: 03 00 2c 00 00 00 00 00 -> 'Spacebar' 133 * Button3: 03 00 08 00 00 00 00 00 -> 'e and E' 134 * Button4: 03 00 0c 00 00 00 00 00 -> 'i and I' 135 * Button5: 03 05 1d 00 00 00 00 00 -> LeftControl + LeftAlt + 'z and Z' 136 * Button6: 03 01 16 00 00 00 00 00 -> LeftControl + 's and S' 137 * 138 * Dial Events: 139 * Clockwise: 03 01 2e 00 00 00 00 00 -> LeftControl + '= and +' 140 * Anticlockwise: 03 01 2d 00 00 00 00 00 -> LeftControl + '- and (underscore)' 141 * 142 * NOTE: Input event descriptions begin at byte 2, and progressively build 143 * towards byte 7 as each new key is pressed maintaining the press order. 144 * For example: 145 * BTN1 followed by BTN2 is 03 00 05 2c 00 00 00 00 146 * BTN2 followed by BTN1 is 03 00 2c 05 00 00 00 00 147 * 148 * Releasing a button causes its byte to be freed, and the next item in the list 149 * is pushed forwards. Dial events are released immediately after an event is 150 * registered (i.e. after each "click"), so will continually appear pushed 151 * backwards in the report. 152 * 153 * When a button with a modifier key is pressed, the button identifier stacks in 154 * an abnormal way, where the highest modifier byte always supersedes others. 155 * In these cases, the button with the higher modifier is always last. 156 * For example: 157 * BTN6 followed by BTN5 is 03 05 1d 16 00 00 00 00 158 * BTN5 followed by BTN6 is 03 05 1d 16 00 00 00 00 159 * BTN5 followed by BTN1 is 03 05 05 1d 00 00 00 00 160 * 161 * For three button presses in order, demonstrating strictly above rules: 162 * BTN6, BTN1, BTN5 is 03 05 05 1d 16 00 00 00 163 * BTN5, BTN1, BTN6 is 03 05 05 1d 16 00 00 00 164 * 165 * In short, when BTN5/6 are pressed, the order of operations is lost, as they 166 * will always float to the end when pressed in combination with others. 167 * 168 * Fortunately, all states are recorded in the same way, with no overlaps. 169 * Byte 1 can be used as a spare for the wheel, since this is for mod keys. 170 */ 171 172 #define RDESC_SIZE_PAD 188 173 #define REPORT_SIZE_PAD 8 174 #define REPORT_ID_BUTTONS 3 175 #define PAD_BUTTON_COUNT 6 176 #define RDESC_KEYBOARD_OFFSET 148 177 178 static const __u8 fixed_rdesc_pad[] = { 179 /* Copy of pen descriptor to avoid losing functionality */ 180 UsagePage_Digitizers 181 Usage_Dig_Pen 182 CollectionApplication( 183 ReportId(7) 184 Usage_Dig_Stylus 185 CollectionPhysical( 186 Usage_Dig_TipSwitch 187 Usage_Dig_BarrelSwitch 188 Usage_Dig_Eraser 189 Usage_Dig_Invert 190 Usage_Dig_InRange 191 LogicalMinimum_i8(0) 192 LogicalMaximum_i8(1) 193 ReportSize(1) 194 ReportCount(5) 195 Input(Var|Abs) 196 ReportCount(3) 197 Input(Const) /* Input (Const, Var, Abs) */ 198 UsagePage_GenericDesktop 199 Usage_GD_X 200 LogicalMinimum_i16(0) 201 LogicalMaximum_i16(22352) 202 UnitExponent(-3) 203 Unit(in) /* (EnglishLinear: in) */ 204 PhysicalMinimum_i16(0) 205 PhysicalMaximum_i16(22352) 206 ReportSize(16) 207 ReportCount(1) 208 Input(Var|Abs) 209 Usage_GD_Y 210 LogicalMinimum_i16(0) 211 LogicalMaximum_i16(13970) 212 UnitExponent(-3) 213 Unit(in) /* (EnglishLinear: in) */ 214 PhysicalMinimum_i16(0) 215 PhysicalMaximum_i16(13970) 216 ReportSize(16) 217 ReportCount(1) 218 Input(Var|Abs) 219 UsagePage_Digitizers 220 Usage_Dig_TipPressure 221 LogicalMinimum_i16(0) 222 LogicalMaximum_i16(8191) 223 ReportSize(16) 224 ReportCount(1) 225 Input(Var|Abs) 226 ) 227 ) 228 229 /* FIXES BEGIN */ 230 UsagePage_GenericDesktop 231 Usage_GD_Keypad 232 CollectionApplication( 233 ReportId(REPORT_ID_BUTTONS) /* Retain original ID on byte 0 */ 234 ReportCount(1) 235 ReportSize(REPORT_SIZE_PAD) 236 UsagePage_Digitizers 237 Usage_Dig_TabletFunctionKeys 238 CollectionPhysical( 239 /* Byte 1: Dial state */ 240 UsagePage_GenericDesktop 241 Usage_GD_Dial 242 LogicalMinimum_i8(-1) 243 LogicalMaximum_i8(1) 244 ReportCount(1) 245 ReportSize(REPORT_SIZE_PAD) 246 Input(Var|Rel) 247 /* Byte 2: Button state */ 248 UsagePage_Button 249 ReportSize(1) 250 ReportCount(PAD_BUTTON_COUNT) 251 UsageMinimum_i8(0x01) 252 UsageMaximum_i8(PAD_BUTTON_COUNT) /* Number of buttons */ 253 LogicalMinimum_i8(0x0) 254 LogicalMaximum_i8(0x1) 255 Input(Var|Abs) 256 /* Byte 3: Exists to be tablet pad */ 257 UsagePage_Digitizers 258 Usage_Dig_BarrelSwitch 259 ReportCount(1) 260 ReportSize(1) 261 Input(Var|Abs) 262 ReportCount(7) /* Padding, to fill full report space */ 263 Input(Const) 264 /* Byte 4/5: Exists to be a tablet pad */ 265 UsagePage_GenericDesktop 266 Usage_GD_X 267 Usage_GD_Y 268 ReportCount(2) 269 ReportSize(8) 270 Input(Var|Abs) 271 /* Bytes 6/7: Padding, to match original length */ 272 ReportCount(2) 273 ReportSize(8) 274 Input(Const) 275 ) 276 FixedSizeVendorReport(RDESC_SIZE_PAD) 277 ) 278 }; 279 280 SEC(HID_BPF_RDESC_FIXUP) 281 int BPF_PROG(xppen_deco02_rdesc_fixup, struct hid_bpf_ctx *hctx) 282 { 283 __u8 *data = hid_bpf_get_data(hctx, 0, HID_MAX_DESCRIPTOR_SIZE); 284 285 if (!data) 286 return 0; /* EPERM Check */ 287 288 if (hctx->size == RDESC_SIZE_PAD) { 289 __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad)); 290 return sizeof(fixed_rdesc_pad); 291 } 292 293 return 0; 294 } 295 296 SEC(HID_BPF_DEVICE_EVENT) 297 int BPF_PROG(xppen_deco02_device_event, struct hid_bpf_ctx *hctx) 298 { 299 __u8 *data = hid_bpf_get_data(hctx, 0, REPORT_SIZE_PAD); 300 301 if (!data || data[0] != REPORT_ID_BUTTONS) 302 return 0; /* EPERM or wrong report */ 303 304 __u8 dial_code = 0; 305 __u8 button_mask = 0; 306 size_t d; 307 308 /* Start from 2; 0 is report ID, 1 is modifier keys, replaced by dial */ 309 for (d = 2; d < 8; d++) { 310 switch (data[d]) { 311 case 0x2e: 312 dial_code = 1; 313 break; 314 case 0x2d: 315 dial_code = -1; 316 break; 317 /* below are buttons, top to bottom */ 318 case 0x05: 319 button_mask |= BIT(0); 320 break; 321 case 0x2c: 322 button_mask |= BIT(1); 323 break; 324 case 0x08: 325 button_mask |= BIT(2); 326 break; 327 case 0x0c: 328 button_mask |= BIT(3); 329 break; 330 case 0x1d: 331 button_mask |= BIT(4); 332 break; 333 case 0x16: 334 button_mask |= BIT(05); 335 break; 336 default: 337 break; 338 } 339 } 340 341 __u8 report[8] = { REPORT_ID_BUTTONS, dial_code, button_mask, 0x00 }; 342 343 __builtin_memcpy(data, report, sizeof(report)); 344 return 0; 345 } 346 347 HID_BPF_OPS(xppen_deco02) = { 348 .hid_rdesc_fixup = (void *)xppen_deco02_rdesc_fixup, 349 .hid_device_event = (void *)xppen_deco02_device_event, 350 }; 351 352 SEC("syscall") 353 int probe(struct hid_bpf_probe_args *ctx) 354 { 355 ctx->retval = ctx->rdesc_size != RDESC_SIZE_PAD ? -EINVAL : 0; 356 return 0; 357 } 358 359 char _license[] SEC("license") = "GPL"; 360