1 // SPDX-License-Identifier: GPL-2.0-only 2 /* Copyright (c) 2024 Benjamin Tissoires 3 */ 4 5 #include "vmlinux.h" 6 #include "hid_bpf.h" 7 #include "hid_bpf_helpers.h" 8 #include <bpf/bpf_tracing.h> 9 10 #define VID_WACOM 0x056a 11 #define ART_PEN_ID 0x0804 12 #define PID_INTUOS_PRO_2_M 0x0357 13 14 HID_BPF_CONFIG( 15 HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_WACOM, PID_INTUOS_PRO_2_M) 16 ); 17 18 /* 19 * This filter is here for the Art Pen stylus only: 20 * - when used on some Wacom devices (see the list of attached PIDs), this pen 21 * reports pressure every other events. 22 * - to solve that, given that we know that the next event will be the same as 23 * the current one, we can emulate a smoother pressure reporting by reporting 24 * the mean of the previous value and the current one. 25 * 26 * We are effectively delaying the pressure by one event every other event, but 27 * that's less of an annoyance compared to the chunkiness of the reported data. 28 * 29 * For example, let's assume the following set of events: 30 * <Tip switch 0> <X 0> <Y 0> <Pressure 0 > <Tooltype 0x0804> 31 * <Tip switch 1> <X 1> <Y 1> <Pressure 100 > <Tooltype 0x0804> 32 * <Tip switch 1> <X 2> <Y 2> <Pressure 100 > <Tooltype 0x0804> 33 * <Tip switch 1> <X 3> <Y 3> <Pressure 200 > <Tooltype 0x0804> 34 * <Tip switch 1> <X 4> <Y 4> <Pressure 200 > <Tooltype 0x0804> 35 * <Tip switch 0> <X 5> <Y 5> <Pressure 0 > <Tooltype 0x0804> 36 * 37 * The filter will report: 38 * <Tip switch 0> <X 0> <Y 0> <Pressure 0 > <Tooltype 0x0804> 39 * <Tip switch 1> <X 1> <Y 1> <Pressure * 50*> <Tooltype 0x0804> 40 * <Tip switch 1> <X 2> <Y 2> <Pressure 100 > <Tooltype 0x0804> 41 * <Tip switch 1> <X 3> <Y 3> <Pressure *150*> <Tooltype 0x0804> 42 * <Tip switch 1> <X 4> <Y 4> <Pressure 200 > <Tooltype 0x0804> 43 * <Tip switch 0> <X 5> <Y 5> <Pressure 0 > <Tooltype 0x0804> 44 * 45 */ 46 47 struct wacom_params { 48 __u16 pid; 49 __u16 rdesc_len; 50 __u8 report_id; 51 __u8 report_len; 52 struct { 53 __u8 tip_switch; 54 __u8 pressure; 55 __u8 tool_type; 56 } offsets; 57 }; 58 59 /* 60 * Multiple device can support the same stylus, so 61 * we need to know which device has which offsets 62 */ 63 static const struct wacom_params devices[] = { 64 { 65 .pid = PID_INTUOS_PRO_2_M, 66 .rdesc_len = 949, 67 .report_id = 16, 68 .report_len = 27, 69 .offsets = { 70 .tip_switch = 1, 71 .pressure = 8, 72 .tool_type = 25, 73 }, 74 }, 75 }; 76 77 static struct wacom_params params = { 0 }; 78 79 /* HID-BPF reports a 64 bytes chunk anyway, so this ensures 80 * the verifier to know we are addressing the memory correctly 81 */ 82 #define PEN_REPORT_LEN 64 83 84 /* only odd frames are modified */ 85 static bool odd; 86 87 static __u16 prev_pressure; 88 89 static inline void *get_bits(__u8 *data, unsigned int byte_offset) 90 { 91 return data + byte_offset; 92 } 93 94 static inline __u16 *get_u16(__u8 *data, unsigned int offset) 95 { 96 return (__u16 *)get_bits(data, offset); 97 } 98 99 static inline __u8 *get_u8(__u8 *data, unsigned int offset) 100 { 101 return (__u8 *)get_bits(data, offset); 102 } 103 104 SEC(HID_BPF_DEVICE_EVENT) 105 int BPF_PROG(artpen_pressure_interpolate, struct hid_bpf_ctx *hctx) 106 { 107 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, PEN_REPORT_LEN /* size */); 108 __u16 *pressure, *tool_type; 109 __u8 *tip_switch; 110 111 if (!data) 112 return 0; /* EPERM check */ 113 114 if (data[0] != params.report_id || 115 params.offsets.tip_switch >= PEN_REPORT_LEN || 116 params.offsets.pressure >= PEN_REPORT_LEN - 1 || 117 params.offsets.tool_type >= PEN_REPORT_LEN - 1) 118 return 0; /* invalid report or parameters */ 119 120 tool_type = get_u16(data, params.offsets.tool_type); 121 if (*tool_type != ART_PEN_ID) 122 return 0; 123 124 tip_switch = get_u8(data, params.offsets.tip_switch); 125 if ((*tip_switch & 0x01) == 0) { 126 prev_pressure = 0; 127 odd = true; 128 return 0; 129 } 130 131 pressure = get_u16(data, params.offsets.pressure); 132 133 if (odd) 134 *pressure = (*pressure + prev_pressure) / 2; 135 136 prev_pressure = *pressure; 137 odd = !odd; 138 139 return 0; 140 } 141 142 HID_BPF_OPS(wacom_artpen) = { 143 .hid_device_event = (void *)artpen_pressure_interpolate, 144 }; 145 146 SEC("syscall") 147 int probe(struct hid_bpf_probe_args *ctx) 148 { 149 struct hid_bpf_ctx *hid_ctx; 150 __u16 pid; 151 int i; 152 153 /* get a struct hid_device to access the actual pid of the device */ 154 hid_ctx = hid_bpf_allocate_context(ctx->hid); 155 if (!hid_ctx) { 156 ctx->retval = -ENODEV; 157 return -1; /* EPERM check */ 158 } 159 pid = hid_ctx->hid->product; 160 161 ctx->retval = -EINVAL; 162 163 /* Match the given device with the list of known devices */ 164 for (i = 0; i < ARRAY_SIZE(devices); i++) { 165 const struct wacom_params *device = &devices[i]; 166 167 if (device->pid == pid && device->rdesc_len == ctx->rdesc_size) { 168 params = *device; 169 ctx->retval = 0; 170 } 171 } 172 173 hid_bpf_release_context(hid_ctx); 174 return 0; 175 } 176 177 char _license[] SEC("license") = "GPL"; 178