brintos

brintos / linux-shallow public Read only

0
0
Text · 18.0 KiB · b09b801 Raw
535 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* Copyright (c) 2024 Red Hat, Inc3 */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 0x256C12#define PID_INSPIROY_2_S 0x006613 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 firmware22 * string has a date suffix, e.g. HUION_T21j_22122123 */24char EXPECTED_FIRMWARE_ID[] = "HUION_T21j_";25 26/* How this BPF program works: the tablet has two modes, firmware mode and27 * tablet mode. In firmware mode (out of the box) the tablet sends button events28 * and the dial as keyboard combinations. In tablet mode it uses a vendor specific29 * hid report to report everything instead.30 * Depending on the mode some hid reports are never sent and the corresponding31 * devices are mute.32 *33 * To switch the tablet use e.g.  https://github.com/whot/huion-switcher34 * or one of the tools from the digimend project35 *36 * This BPF works for both modes. The huion-switcher tool sets the37 * HUION_FIRMWARE_ID udev property - if that is set then we disable the firmware38 * 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 in40 * either mode though the drawback is that the device will show up twice if41 * you bind it to all event nodes42 *43 * Default report descriptor for the first exposed hidraw node:44 *45 * # HUION Huion Tablet_H641P46 * # Report descriptor length: 18 bytes47 * # 0x06, 0x00, 0xff,              // Usage Page (Vendor Defined Page 0xFF00)   048 * # 0x09, 0x01,                    // Usage (Vendor Usage 0x01)                 349 * # 0xa1, 0x01,                    // Collection (Application)                  550 * # 0x85, 0x08,                    //   Report ID (8)                           751 * # 0x75, 0x58,                    //   Report Size (88)                        952 * # 0x95, 0x01,                    //   Report Count (1)                        1153 * # 0x09, 0x01,                    //   Usage (Vendor Usage 0x01)               1354 * # 0x81, 0x02,                    //   Input (Data,Var,Abs)                    1555 * # 0xc0,                          // End Collection                            1756 * R: 18 06 00 ff 09 01 a1 01 85 08 75 58 95 01 09 01 81 02 c057 *58 * This rdesc does nothing until the tablet is switched to raw mode, see59 * https://github.com/whot/huion-switcher60 *61 *62 * Second hidraw node is the Pen. This one sends events until the tablet is63 * switched to raw mode, then it's mute.64 *65 * # Report descriptor length: 93 bytes66 * # 0x05, 0x0d,          // Usage Page (Digitizers)                   067 * # 0x09, 0x02,          // Usage (Pen)                               268 * # 0xa1, 0x01,          // Collection (Application)                  469 * # 0x85, 0x0a,          //   Report ID (10)                          670 * # 0x09, 0x20,          //   Usage (Stylus)                          871 * # 0xa1, 0x01,          //   Collection (Application)                1072 * # 0x09, 0x42,          //     Usage (Tip Switch)                    1273 * # 0x09, 0x44,          //     Usage (Barrel Switch)                 1474 * # 0x09, 0x45,          //     Usage (Eraser)                        1675 * # 0x09, 0x3c,          //     Usage (Invert)                        18 <-- has no Invert eraser76 * # 0x15, 0x00,          //     Logical Minimum (0)                   2077 * # 0x25, 0x01,          //     Logical Maximum (1)                   2278 * # 0x75, 0x01,          //     Report Size (1)                       2479 * # 0x95, 0x06,          //     Report Count (6)                      2680 * # 0x81, 0x02,          //     Input (Data,Var,Abs)                  2881 * # 0x09, 0x32,          //     Usage (In Range)                      3082 * # 0x75, 0x01,          //     Report Size (1)                       3283 * # 0x95, 0x01,          //     Report Count (1)                      3484 * # 0x81, 0x02,          //     Input (Data,Var,Abs)                  3685 * # 0x81, 0x03,          //     Input (Cnst,Var,Abs)                  3886 * # 0x05, 0x01,          //     Usage Page (Generic Desktop)          4087 * # 0x09, 0x30,          //     Usage (X)                             4288 * # 0x09, 0x31,          //     Usage (Y)                             4489 * # 0x55, 0x0d,          //     Unit Exponent (-3)                    46 <-- change to -290 * # 0x65, 0x33,          //     Unit (EnglishLinear: in³)             48 <-- change in³ to in91 * # 0x26, 0xff, 0x7f,    //     Logical Maximum (32767)               5092 * # 0x35, 0x00,          //     Physical Minimum (0)                  5393 * # 0x46, 0x00, 0x08,    //     Physical Maximum (2048)               55 <-- invalid size94 * # 0x75, 0x10,          //     Report Size (16)                      5895 * # 0x95, 0x02,          //     Report Count (2)                      6096 * # 0x81, 0x02,          //     Input (Data,Var,Abs)                  6297 * # 0x05, 0x0d,          //     Usage Page (Digitizers)               6498 * # 0x09, 0x30,          //     Usage (Tip Pressure)                  6699 * # 0x26, 0xff, 0x1f,    //     Logical Maximum (8191)                68100 * # 0x75, 0x10,          //     Report Size (16)                      71101 * # 0x95, 0x01,          //     Report Count (1)                      73102 * # 0x81, 0x02,          //     Input (Data,Var,Abs)                  75103 * # 0x09, 0x3d,          //     Usage (X Tilt)                        77 <-- No tilt reported104 * # 0x09, 0x3e,          //     Usage (Y Tilt)                        79105 * # 0x15, 0x81,          //     Logical Minimum (-127)                81106 * # 0x25, 0x7f,          //     Logical Maximum (127)                 83107 * # 0x75, 0x08,          //     Report Size (8)                       85108 * # 0x95, 0x02,          //     Report Count (2)                      87109 * # 0x81, 0x02,          //     Input (Data,Var,Abs)                  89110 * # 0xc0,                //   End Collection                          91111 * # 0xc0,                // End Collection                            92112 * 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 c0113 *114 * Third hidraw node is the pad which sends a combination of keyboard shortcuts until115 * the tablet is switched to raw mode, then it's mute:116 *117 * # Report descriptor length: 65 bytes118 * # 0x05, 0x01,          // Usage Page (Generic Desktop)              0119 * # 0x09, 0x06,          // Usage (Keyboard)                          2120 * # 0xa1, 0x01,          // Collection (Application)                  4121 * # 0x85, 0x03,          //   Report ID (3)                           6122 * # 0x05, 0x07,          //   Usage Page (Keyboard/Keypad)            8123 * # 0x19, 0xe0,          //   UsageMinimum (224)                      10124 * # 0x29, 0xe7,          //   UsageMaximum (231)                      12125 * # 0x15, 0x00,          //   Logical Minimum (0)                     14126 * # 0x25, 0x01,          //   Logical Maximum (1)                     16127 * # 0x75, 0x01,          //   Report Size (1)                         18128 * # 0x95, 0x08,          //   Report Count (8)                        20129 * # 0x81, 0x02,          //   Input (Data,Var,Abs)                    22130 * # 0x05, 0x07,          //   Usage Page (Keyboard/Keypad)            24131 * # 0x19, 0x00,          //   UsageMinimum (0)                        26132 * # 0x29, 0xff,          //   UsageMaximum (255)                      28133 * # 0x26, 0xff, 0x00,    //   Logical Maximum (255)                   30134 * # 0x75, 0x08,          //   Report Size (8)                         33135 * # 0x95, 0x06,          //   Report Count (6)                        35136 * # 0x81, 0x00,          //   Input (Data,Arr,Abs)                    37137 * # 0xc0,                // End Collection                            39138 * # 0x05, 0x0c,          // Usage Page (Consumer)                     40139 * # 0x09, 0x01,          // Usage (Consumer Control)                  42140 * # 0xa1, 0x01,          // Collection (Application)                  44141 * # 0x85, 0x04,          //   Report ID (4)                           46142 * # 0x19, 0x00,          //   UsageMinimum (0)                        48143 * # 0x2a, 0x3c, 0x02,    //   UsageMaximum (572)                      50144 * # 0x15, 0x00,          //   Logical Minimum (0)                     53145 * # 0x26, 0x3c, 0x02,    //   Logical Maximum (572)                   55146 * # 0x95, 0x01,          //   Report Count (1)                        58147 * # 0x75, 0x10,          //   Report Size (16)                        60148 * # 0x81, 0x00,          //   Input (Data,Arr,Abs)                    62149 * # 0xc0,                // End Collection                            64150 * 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 c0151 * N: HUION Huion Tablet_H641P152 */153 154#define PAD_REPORT_DESCRIPTOR_LENGTH 65155#define PEN_REPORT_DESCRIPTOR_LENGTH 93156#define VENDOR_REPORT_DESCRIPTOR_LENGTH 18157#define PAD_REPORT_ID 3158#define PEN_REPORT_ID 10159#define VENDOR_REPORT_ID 8160#define PAD_REPORT_LENGTH 8161#define PEN_REPORT_LENGTH 10162#define VENDOR_REPORT_LENGTH 12163 164 165__u8 last_button_state;166 167static const __u8 fixed_rdesc_pad[] = {168	UsagePage_GenericDesktop169	Usage_GD_Keypad170	CollectionApplication(171		// -- Byte 0 in report172		ReportId(PAD_REPORT_ID)173		LogicalRange_i8(0, 1)174		UsagePage_Digitizers175		Usage_Dig_TabletFunctionKeys176		CollectionPhysical(177			// Byte 1 in report - just exists so we get to be a tablet pad178			Usage_Dig_BarrelSwitch // BTN_STYLUS179			ReportCount(1)180			ReportSize(1)181			Input(Var|Abs)182			ReportCount(7) // padding183			Input(Const)184			// Bytes 2/3 in report - just exists so we get to be a tablet pad185			UsagePage_GenericDesktop186			Usage_GD_X187			Usage_GD_Y188			ReportCount(2)189			ReportSize(8)190			Input(Var|Abs)191			// Byte 4 in report is the wheel192			Usage_GD_Wheel193			LogicalRange_i8(-1, 1)194			ReportCount(1)195			ReportSize(8)196			Input(Var|Rel)197			// Byte 5 is the button state198			UsagePage_Button199			UsageRange_i8(0x01, 0x6)200			LogicalRange_i8(0x01, 0x6)201			ReportCount(1)202			ReportSize(8)203			Input(Arr|Abs)204		)205		// Make sure we match our original report length206		FixedSizeVendorReport(PAD_REPORT_LENGTH)207	)208};209 210static const __u8 fixed_rdesc_pen[] = {211	UsagePage_Digitizers212	Usage_Dig_Pen213	CollectionApplication(214		// -- Byte 0 in report215		ReportId(PEN_REPORT_ID)216		Usage_Dig_Pen217		CollectionPhysical(218			// -- Byte 1 in report219			Usage_Dig_TipSwitch220			Usage_Dig_BarrelSwitch221			Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2222			LogicalRange_i8(0, 1)223			ReportSize(1)224			ReportCount(3)225			Input(Var|Abs)226			ReportCount(4)  // Padding227			Input(Const)228			Usage_Dig_InRange229			ReportCount(1)230			Input(Var|Abs)231			ReportSize(16)232			ReportCount(1)233			PushPop(234				UsagePage_GenericDesktop235				Unit(cm)236				UnitExponent(-1)237				PhysicalRange_i16(0, 160)238				LogicalRange_i16(0, 32767)239				Usage_GD_X240				Input(Var|Abs) // Bytes 2+3241				PhysicalRange_i16(0, 100)242				LogicalRange_i16(0, 32767)243				Usage_GD_Y244				Input(Var|Abs) // Bytes 4+5245			)246			UsagePage_Digitizers247			Usage_Dig_TipPressure248			LogicalRange_i16(0, 8191)249			Input(Var|Abs) // Byte 6+7250			// Two bytes padding so we don't need to change the report at all251			ReportSize(8)252			ReportCount(2)253			Input(Const) // Byte 6+7254		)255	)256};257 258static const __u8 fixed_rdesc_vendor[] = {259	UsagePage_Digitizers260	Usage_Dig_Pen261	CollectionApplication(262		// Byte 0263		// We leave the pen on the vendor report ID264		ReportId(VENDOR_REPORT_ID)265		Usage_Dig_Pen266		CollectionPhysical(267			// Byte 1 are the buttons268			LogicalRange_i8(0, 1)269			ReportSize(1)270			Usage_Dig_TipSwitch271			Usage_Dig_BarrelSwitch272			Usage_Dig_SecondaryBarrelSwitch273			ReportCount(3)274			Input(Var|Abs)275			ReportCount(4) // Padding276			Input(Const)277			Usage_Dig_InRange278			ReportCount(1)279			Input(Var|Abs)280			ReportSize(16)281			ReportCount(1)282			PushPop(283				UsagePage_GenericDesktop284				Unit(cm)285				UnitExponent(-1)286				// Note: reported logical range differs287				// from the pen report ID for x and y288				LogicalRange_i16(0, 32000)289				PhysicalRange_i16(0, 160)290				// Bytes 2/3 in report291				Usage_GD_X292				Input(Var|Abs)293				LogicalRange_i16(0, 20000)294				PhysicalRange_i16(0, 100)295				// Bytes 4/5 in report296				Usage_GD_Y297				Input(Var|Abs)298			)299			// Bytes 6/7 in report300			LogicalRange_i16(0, 8192)301			Usage_Dig_TipPressure302			Input(Var|Abs)303		)304	)305	UsagePage_GenericDesktop306	Usage_GD_Keypad307	CollectionApplication(308		// Byte 0309		ReportId(PAD_REPORT_ID)310		LogicalRange_i8(0, 1)311		UsagePage_Digitizers312		Usage_Dig_TabletFunctionKeys313		CollectionPhysical(314			// Byte 1 are the buttons315			Usage_Dig_BarrelSwitch	 // BTN_STYLUS, needed so we get to be a tablet pad316			ReportCount(1)317			ReportSize(1)318			Input(Var|Abs)319			ReportCount(7) // Padding320			Input(Const)321			// Bytes 2/3 - x/y just exist so we get to be a tablet pad322			UsagePage_GenericDesktop323			Usage_GD_X324			Usage_GD_Y325			ReportCount(2)326			ReportSize(8)327			Input(Var|Abs)328			// Byte 4 is the button state329			UsagePage_Button330			UsageRange_i8(0x01, 0x6)331			LogicalRange_i8(0x0, 0x1)332			ReportCount(6)333			ReportSize(1)334			Input(Var|Abs)335			ReportCount(2)336			Input(Const)337			// Byte 5 is the wheel338			UsagePage_GenericDesktop339			Usage_GD_Wheel340			LogicalRange_i8(-1, 1)341			ReportCount(1)342			ReportSize(8)343			Input(Var|Rel)344		)345		// Make sure we match our original report length346		FixedSizeVendorReport(VENDOR_REPORT_LENGTH)347	)348};349 350static const __u8 disabled_rdesc_pen[] = {351	FixedSizeVendorReport(PEN_REPORT_LENGTH)352};353 354static const __u8 disabled_rdesc_pad[] = {355	FixedSizeVendorReport(PAD_REPORT_LENGTH)356};357 358SEC(HID_BPF_RDESC_FIXUP)359int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)360{361	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);362	__s32 rdesc_size = hctx->size;363	__u8 have_fw_id;364 365	if (!data)366		return 0; /* EPERM check */367 368	/* If we have a firmware ID and it matches our expected prefix, we369	 * disable the default pad/pen nodes. They won't send events370	 * but cause duplicate devices.371	 */372	have_fw_id = __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID,373				      EXPECTED_FIRMWARE_ID,374				      sizeof(EXPECTED_FIRMWARE_ID) - 1) == 0;375	if (rdesc_size == PAD_REPORT_DESCRIPTOR_LENGTH) {376		if (have_fw_id) {377			__builtin_memcpy(data, disabled_rdesc_pad, sizeof(disabled_rdesc_pad));378			return sizeof(disabled_rdesc_pad);379		}380 381		__builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad));382		return sizeof(fixed_rdesc_pad);383	}384	if (rdesc_size == PEN_REPORT_DESCRIPTOR_LENGTH) {385		if (have_fw_id) {386			__builtin_memcpy(data, disabled_rdesc_pen, sizeof(disabled_rdesc_pen));387			return sizeof(disabled_rdesc_pen);388		}389 390		__builtin_memcpy(data, fixed_rdesc_pen, sizeof(fixed_rdesc_pen));391		return sizeof(fixed_rdesc_pen);392	}393	/* Always fix the vendor mode so the tablet will work even if nothing sets394	 * the udev property (e.g. huion-switcher run manually)395	 */396	if (rdesc_size == VENDOR_REPORT_DESCRIPTOR_LENGTH) {397		__builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor));398		return sizeof(fixed_rdesc_vendor);399 400	}401	return 0;402}403 404SEC(HID_BPF_DEVICE_EVENT)405int BPF_PROG(inspiroy_2_fix_events, struct hid_bpf_ctx *hctx)406{407	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);408 409	if (!data)410		return 0; /* EPERM check */411 412	/* Only sent if tablet is in default mode */413	if (data[0] == PAD_REPORT_ID) {414		/* Nicely enough, this device only supports one button down at a time so415		 * the reports are easy to match. Buttons numbered from the top416		 *   Button released: 03 00 00 00 00 00 00 00417		 *   Button 1: 03 00 05 00 00 00 00 00 -> b418		 *   Button 2: 03 00 0c 00 00 00 00 00 -> i419		 *   Button 3: 03 00 08 00 00 00 00 00 -> e420		 *   Button 4: 03 01 16 00 00 00 00 00 -> Ctrl S421		 *   Button 5: 03 00 2c 00 00 00 00 00 -> space422		 *   Button 6: 03 05 1d 00 00 00 00 00 -> Ctrl Alt Z423		 *424		 *   Wheel down: 03 01 2d 00 00 00 00 00 -> Ctrl -425		 *   Wheel up:   03 01 2e 00 00 00 00 00 -> Ctrl =426		 */427		__u8 button = 0;428		__u8 wheel = 0;429 430		switch (data[1] << 8 | data[2]) {431		case 0x0000:432			break;433		case 0x0005:434			button = 1;435			break;436		case 0x000c:437			button = 2;438			break;439		case 0x0008:440			button = 3;441			break;442		case 0x0116:443			button = 4;444			break;445		case 0x002c:446			button = 5;447			break;448		case 0x051d:449			button = 6;450			break;451		case 0x012d:452			wheel = -1;453			break;454		case 0x012e:455			wheel = 1;456			break;457 458		}459 460		__u8 report[6] = {PAD_REPORT_ID, 0x0, 0x0, 0x0, wheel, button};461 462		__builtin_memcpy(data, report, sizeof(report));463		return sizeof(report);464	}465 466	/* Nothing to do for the PEN_REPORT_ID, it's already mapped */467 468	/* Only sent if tablet is in raw mode */469	if (data[0] == VENDOR_REPORT_ID) {470		/* Pad reports */471		if (data[1] & 0x20) {472			/* See fixed_rdesc_pad */473			struct pad_report {474				__u8 report_id;475				__u8 btn_stylus;476				__u8 x;477				__u8 y;478				__u8 buttons;479				__u8 wheel;480			} __attribute__((packed)) *pad_report;481			__u8 wheel = 0;482 483			/* Wheel report */484			if (data[1] == 0xf1) {485				if (data[5] == 2)486					wheel = 0xff;487				else488					wheel = data[5];489			} else {490				/* data[4] are the buttons, mapped correctly */491				last_button_state = data[4];492				wheel = 0; // wheel493			}494 495			pad_report = (struct pad_report *)data;496 497			pad_report->report_id = PAD_REPORT_ID;498			pad_report->btn_stylus = 0;499			pad_report->x = 0;500			pad_report->y = 0;501			pad_report->buttons = last_button_state;502			pad_report->wheel = wheel;503 504			return sizeof(struct pad_report);505		}506 507		/* Pen reports need nothing done */508	}509 510	return 0;511}512 513HID_BPF_OPS(inspiroy_2) = {514	.hid_device_event = (void *)inspiroy_2_fix_events,515	.hid_rdesc_fixup = (void *)hid_fix_rdesc,516};517 518SEC("syscall")519int probe(struct hid_bpf_probe_args *ctx)520{521	switch (ctx->rdesc_size) {522	case PAD_REPORT_DESCRIPTOR_LENGTH:523	case PEN_REPORT_DESCRIPTOR_LENGTH:524	case VENDOR_REPORT_DESCRIPTOR_LENGTH:525		ctx->retval = 0;526		break;527	default:528		ctx->retval = -EINVAL;529	}530 531	return 0;532}533 534char _license[] SEC("license") = "GPL";535