File Index
Files and directories
Sign in
FILE

XPPen__ACK05.bpf.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hid/bpf/progs/XPPen__ACK05.bpf.c 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 HID_BPF_ASYNC_MAX_CTX 1
 12#include "hid_bpf_async.h"
 13
 14#define VID_UGEE		0x28BD
 15/* same PID whether connected directly or through the provided dongle: */
 16#define PID_ACK05_REMOTE	0x0202
 17
 18
 19HID_BPF_CONFIG(
 20	HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ACK05_REMOTE),
 21);
 22
 23/*
 24 * By default, the pad reports the buttons through a set of key sequences.
 25 *
 26 * The pad reports a classic keyboard report descriptor:
 27 * # HANVON UGEE Shortcut Remote
 28 * Report descriptor length: 102 bytes
 29 *  0x05, 0x01,                    // Usage Page (Generic Desktop)              0
 30 *  0x09, 0x02,                    // Usage (Mouse)                             2
 31 *  0xa1, 0x01,                    // Collection (Application)                  4
 32 *  0x85, 0x09,                    //   Report ID (9)                           6
 33 *  0x09, 0x01,                    //   Usage (Pointer)                         8
 34 *  0xa1, 0x00,                    //   Collection (Physical)                   10
 35 *  0x05, 0x09,                    //     Usage Page (Button)                   12
 36 *  0x19, 0x01,                    //     UsageMinimum (1)                      14
 37 *  0x29, 0x03,                    //     UsageMaximum (3)                      16
 38 *  0x15, 0x00,                    //     Logical Minimum (0)                   18
 39 *  0x25, 0x01,                    //     Logical Maximum (1)                   20
 40 *  0x95, 0x03,                    //     Report Count (3)                      22
 41 *  0x75, 0x01,                    //     Report Size (1)                       24
 42 *  0x81, 0x02,                    //     Input (Data,Var,Abs)                  26
 43 *  0x95, 0x05,                    //     Report Count (5)                      28
 44 *  0x81, 0x01,                    //     Input (Cnst,Arr,Abs)                  30
 45 *  0x05, 0x01,                    //     Usage Page (Generic Desktop)          32
 46 *  0x09, 0x30,                    //     Usage (X)                             34
 47 *  0x09, 0x31,                    //     Usage (Y)                             36
 48 *  0x26, 0xff, 0x7f,              //     Logical Maximum (32767)               38
 49 *  0x95, 0x02,                    //     Report Count (2)                      41
 50 *  0x75, 0x10,                    //     Report Size (16)                      43
 51 *  0x81, 0x02,                    //     Input (Data,Var,Abs)                  45
 52 *  0x05, 0x0d,                    //     Usage Page (Digitizers)               47
 53 *  0x09, 0x30,                    //     Usage (Tip Pressure)                  49
 54 *  0x26, 0xff, 0x07,              //     Logical Maximum (2047)                51
 55 *  0x95, 0x01,                    //     Report Count (1)                      54
 56 *  0x75, 0x10,                    //     Report Size (16)                      56
 57 *  0x81, 0x02,                    //     Input (Data,Var,Abs)                  58
 58 *  0xc0,                          //   End Collection                          60
 59 *  0xc0,                          // End Collection                            61
 60 *  0x05, 0x01,                    // Usage Page (Generic Desktop)              62
 61 *  0x09, 0x06,                    // Usage (Keyboard)                          64
 62 *  0xa1, 0x01,                    // Collection (Application)                  66
 63 *  0x85, 0x06,                    //   Report ID (6)                           68
 64 *  0x05, 0x07,                    //   Usage Page (Keyboard/Keypad)            70
 65 *  0x19, 0xe0,                    //   UsageMinimum (224)                      72
 66 *  0x29, 0xe7,                    //   UsageMaximum (231)                      74
 67 *  0x15, 0x00,                    //   Logical Minimum (0)                     76
 68 *  0x25, 0x01,                    //   Logical Maximum (1)                     78
 69 *  0x75, 0x01,                    //   Report Size (1)                         80
 70 *  0x95, 0x08,                    //   Report Count (8)                        82
 71 *  0x81, 0x02,                    //   Input (Data,Var,Abs)                    84
 72 *  0x05, 0x07,                    //   Usage Page (Keyboard/Keypad)            86
 73 *  0x19, 0x00,                    //   UsageMinimum (0)                        88
 74 *  0x29, 0xff,                    //   UsageMaximum (255)                      90
 75 *  0x26, 0xff, 0x00,              //   Logical Maximum (255)                   92
 76 *  0x75, 0x08,                    //   Report Size (8)                         95
 77 *  0x95, 0x06,                    //   Report Count (6)                        97
 78 *  0x81, 0x00,                    //   Input (Data,Arr,Abs)                    99
 79 *  0xc0,                          // End Collection                            101
 80 *
 81 * Each button gets assigned the following events:
 82 *
 83 *   Buttons released: 06 00 00 00 00 00 00 00
 84 *   Button 1:         06 01 12 00 00 00 00 00 -> LControl + o
 85 *   Button 2:         06 01 11 00 00 00 00 00 -> LControl + n
 86 *   Button 3:         06 00 3e 00 00 00 00 00 -> F5
 87 *   Button 4:         06 02 00 00 00 00 00 00 -> LShift
 88 *   Button 5:         06 01 00 00 00 00 00 00 -> LControl
 89 *   Button 6:         06 04 00 00 00 00 00 00 -> LAlt
 90 *   Button 7:         06 01 16 00 00 00 00 00 -> LControl + s
 91 *   Button 8:         06 01 1d 00 00 00 00 00 -> LControl + z
 92 *   Button 9:         06 00 2c 00 00 00 00 00 -> Space
 93 *   Button 10:        06 03 1d 00 00 00 00 00 -> LControl + LShift + z
 94 *   Wheel:            06 01 57 00 00 00 00 00 -> clockwise rotation (LControl + Keypad Plus)
 95 *   Wheel:            06 01 56 00 00 00 00 00 -> counter-clockwise rotation
 96 *						  (LControl + Keypad Minus)
 97 *
 98 * However, multiple buttons can be pressed at the same time, and when this happens,
 99 * each button gets assigned a new slot in the Input (Data,Arr,Abs):
100 *
101 *   Button 1 + 3:     06 01 12 3e 00 00 00 00 -> LControl + o + F5
102 *
103 * When a modifier is pressed (Button 4, 5, or 6), the assigned key is set to 00:
104 *
105 *   Button 5 + 7:     06 01 00 16 00 00 00 00 -> LControl + s
106 *
107 * This is mostly fine, but with Button 8 and Button 10 sharing the same
108 * key value ("z"), there are cases where we can not know which is which.
109 *
110 */
111
112#define PAD_WIRED_DESCRIPTOR_LENGTH 102
113#define PAD_DONGLE_DESCRIPTOR_LENGTH 177
114#define STYLUS_DESCRIPTOR_LENGTH 109
115#define VENDOR_DESCRIPTOR_LENGTH 36
116#define PAD_REPORT_ID 6
117#define RAW_PAD_REPORT_ID 0xf0
118#define RAW_BATTERY_REPORT_ID 0xf2
119#define VENDOR_REPORT_ID 2
120#define PAD_REPORT_LENGTH 8
121#define VENDOR_REPORT_LENGTH 12
122
123__u16 last_button_state;
124
125static const __u8 disabled_rdesc[] = {
126	// Make sure we match our original report length
127	FixedSizeVendorReport(VENDOR_REPORT_LENGTH)
128};
129
130static const __u8 fixed_rdesc_vendor[] = {
131	UsagePage_GenericDesktop
132	Usage_GD_Keypad
133	CollectionApplication(
134		// -- Byte 0 in report
135		ReportId(RAW_PAD_REPORT_ID)
136		// Byte 1 in report - same than report ID
137		ReportCount(1)
138		ReportSize(8)
139		Input(Const) // padding (internal report ID)
140		LogicalMaximum_i8(0)
141		LogicalMaximum_i8(1)
142		UsagePage_Digitizers
143		Usage_Dig_TabletFunctionKeys
144		CollectionPhysical(
145			// Byte 2-3 is the button state
146			UsagePage_Button
147			UsageMinimum_i8(0x01)
148			UsageMaximum_i8(0x0a)
149			LogicalMinimum_i8(0x0)
150			LogicalMaximum_i8(0x1)
151			ReportCount(10)
152			ReportSize(1)
153			Input(Var|Abs)
154			Usage_i8(0x31) // will be mapped as BTN_A / BTN_SOUTH
155			ReportCount(1)
156			Input(Var|Abs)
157			ReportCount(5) // padding
158			Input(Const)
159			// Byte 4 in report - just exists so we get to be a tablet pad
160			UsagePage_Digitizers
161			Usage_Dig_BarrelSwitch // BTN_STYLUS
162			ReportCount(1)
163			ReportSize(1)
164			Input(Var|Abs)
165			ReportCount(7) // padding
166			Input(Const)
167			// Bytes 5/6 in report - just exists so we get to be a tablet pad
168			UsagePage_GenericDesktop
169			Usage_GD_X
170			Usage_GD_Y
171			ReportCount(2)
172			ReportSize(8)
173			Input(Var|Abs)
174			// Byte 7 in report is the dial
175			Usage_GD_Wheel
176			LogicalMinimum_i8(-1)
177			LogicalMaximum_i8(1)
178			ReportCount(1)
179			ReportSize(8)
180			Input(Var|Rel)
181		)
182		// -- Byte 0 in report
183		ReportId(RAW_BATTERY_REPORT_ID)
184		// Byte 1 in report - same than report ID
185		ReportCount(1)
186		ReportSize(8)
187		Input(Const) // padding (internal report ID)
188		// Byte 2 in report - always 0x01
189		Input(Const) // padding (internal report ID)
190		UsagePage_Digitizers
191		/*
192		 * We represent the device as a stylus to force the kernel to not
193		 * directly query its battery state. Instead the kernel will rely
194		 * only on the provided events.
195		 */
196		Usage_Dig_Stylus
197		CollectionPhysical(
198			// Byte 3 in report - battery value
199			UsagePage_BatterySystem
200			Usage_BS_AbsoluteStateOfCharge
201			LogicalMinimum_i8(0)
202			LogicalMaximum_i8(100)
203			ReportCount(1)
204			ReportSize(8)
205			Input(Var|Abs)
206			// Byte 4 in report - charging state
207			Usage_BS_Charging
208			LogicalMinimum_i8(0)
209			LogicalMaximum_i8(1)
210			ReportCount(1)
211			ReportSize(8)
212			Input(Var|Abs)
213		)
214	)
215};
216
217SEC(HID_BPF_RDESC_FIXUP)
218int BPF_PROG(ack05_fix_rdesc, struct hid_bpf_ctx *hctx)
219{
220	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
221	__s32 rdesc_size = hctx->size;
222
223	if (!data)
224		return 0; /* EPERM check */
225
226	if (rdesc_size == VENDOR_DESCRIPTOR_LENGTH) {
227		/*
228		 * The vendor fixed rdesc is appended after the current one,
229		 * to keep the output reports working.
230		 */
231		__builtin_memcpy(data + rdesc_size, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor));
232		return sizeof(fixed_rdesc_vendor) + rdesc_size;
233	}
234
235	hid_set_name(hctx->hid, "Disabled by HID-BPF Hanvon Ugee Shortcut Remote");
236
237	__builtin_memcpy(data, disabled_rdesc, sizeof(disabled_rdesc));
238	return sizeof(disabled_rdesc);
239}
240
241static int HID_BPF_ASYNC_FUN(switch_to_raw_mode)(struct hid_bpf_ctx *hid)
242{
243	static __u8 magic_0[32] = {0x02, 0xb0, 0x04, 0x00, 0x00};
244	int err;
245
246	/*
247	 * The proprietary driver sends the 3 following packets after the
248	 * above one.
249	 * These don't seem to have any effect, so we don't send them to save
250	 * some processing time.
251	 *
252	 * static __u8 magic_1[32] = {0x02, 0xb4, 0x01, 0x00, 0x01};
253	 * static __u8 magic_2[32] = {0x02, 0xb4, 0x01, 0x00, 0xff};
254	 * static __u8 magic_3[32] = {0x02, 0xb8, 0x04, 0x00, 0x00};
255	 */
256
257	err = hid_bpf_hw_output_report(hid, magic_0, sizeof(magic_0));
258	if (err < 0)
259		return err;
260
261	return 0;
262}
263
264SEC(HID_BPF_DEVICE_EVENT)
265int BPF_PROG(ack05_fix_events, struct hid_bpf_ctx *hctx)
266{
267	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, PAD_REPORT_LENGTH);
268	int ret = 0;
269
270	if (!data)
271		return 0; /* EPERM check */
272
273	if (data[0] != VENDOR_REPORT_ID)
274		return 0;
275
276	/* reconnect event */
277	if (data[1] == 0xf8 && data[2] == 02 && data[3] == 0x01)
278		HID_BPF_ASYNC_DELAYED_CALL(switch_to_raw_mode, hctx, 10);
279
280	/* button event */
281	if (data[1] == RAW_PAD_REPORT_ID) {
282		data[0] = data[1];
283		if (data[7] == 0x02)
284			data[7] = 0xff;
285		ret = 8;
286	} else if (data[1] == RAW_BATTERY_REPORT_ID) {
287		data[0] = data[1];
288		ret = 5;
289	}
290
291	return ret;
292}
293
294HID_BPF_OPS(xppen_ack05_remote) = {
295	.hid_device_event = (void *)ack05_fix_events,
296	.hid_rdesc_fixup = (void *)ack05_fix_rdesc,
297};
298
299SEC("syscall")
300int probe(struct hid_bpf_probe_args *ctx)
301{
302	switch (ctx->rdesc_size) {
303	case PAD_WIRED_DESCRIPTOR_LENGTH:
304	case PAD_DONGLE_DESCRIPTOR_LENGTH:
305	case STYLUS_DESCRIPTOR_LENGTH:
306	case VENDOR_DESCRIPTOR_LENGTH:
307		ctx->retval = 0;
308		break;
309	default:
310		ctx->retval = -EINVAL;
311		break;
312	}
313
314	if (ctx->rdesc_size == VENDOR_DESCRIPTOR_LENGTH) {
315		struct hid_bpf_ctx *hctx = hid_bpf_allocate_context(ctx->hid);
316
317		if (!hctx) {
318			ctx->retval = -EINVAL;
319			return 0;
320		}
321
322		ctx->retval = HID_BPF_ASYNC_INIT(switch_to_raw_mode) ||
323			      switch_to_raw_mode(hctx);
324
325		hid_bpf_release_context(hctx);
326	}
327
328	return 0;
329}
330
331char _license[] SEC("license") = "GPL";
~/ Read-only file preview.