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