xref: /linux/drivers/hid/bpf/progs/Huion__Inspiroy-Frego-M.bpf.c (revision 637315cb400eed1dbe721954fea437a61a2d65b2)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include "vmlinux.h"
3 #include "hid_bpf.h"
4 #include "hid_bpf_helpers.h"
5 #include <bpf/bpf_tracing.h>
6 
7 /*
8  * Huion Inspiroy Frego M Pen Tablet
9  * Model L610
10  * 256c:8251 (Bluetooth)
11  * 256c:2012 (USB)
12  */
13 #define VID_HUION			0x256C
14 #define PID_INSPIROY_FREGO_M		0x8251
15 #define PID_L610			0x2012
16 
17 #define PEN_RDESC_SIZE			125
18 #define SECONDARY_SWITCH_OFFSET		17
19 
20 HID_BPF_CONFIG(
21 	HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC, VID_HUION, PID_INSPIROY_FREGO_M),
22 	HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HUION, PID_L610)
23 );
24 
25 /*
26  * The pen descriptor reports the second side button as Secondary Tip Switch
27  * instead of Secondary Barrel Switch.
28  *
29  * Relevant part of the original pen report descriptor:
30  *
31  * 0x09, 0x42,       // Usage (Tip Switch)                  12
32  * 0x09, 0x44,       // Usage (Barrel Switch)               14
33  * 0x09, 0x43,       // Usage (Secondary Tip Switch)        16 <- change to 0x5a
34  * 0x09, 0x3c,       // Usage (Invert)                      18
35  * 0x09, 0x45,       // Usage (Eraser)                      20
36  * 0x15, 0x00,       // Logical Minimum (0)                 22
37  * 0x25, 0x01,       // Logical Maximum (1)                 24
38  */
39 SEC(HID_BPF_RDESC_FIXUP)
40 int BPF_PROG(fix_secondary_barrel_rdesc, struct hid_bpf_ctx *hctx)
41 {
42 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
43 
44 	if (!data)
45 		return 0; /* EPERM check */
46 
47 	if (hctx->size != PEN_RDESC_SIZE)
48 		return 0;
49 
50 	if (data[0] != 0x05 || data[1] != 0x0d || /* Usage Page (Digitizers) */
51 	    data[2] != 0x09 || data[3] != 0x02 || /* Usage (Pen) */
52 	    data[16] != 0x09 ||
53 	    data[SECONDARY_SWITCH_OFFSET] != 0x43) /* Secondary Tip Switch */
54 		return 0;
55 
56 	data[SECONDARY_SWITCH_OFFSET] = 0x5a;
57 
58 	return 0;
59 }
60 
61 HID_BPF_OPS(fix_secondary_barrel) = {
62 	.hid_rdesc_fixup = (void *)fix_secondary_barrel_rdesc,
63 };
64 
65 SEC("syscall")
66 int probe(struct hid_bpf_probe_args *ctx)
67 {
68 	ctx->retval = ctx->rdesc_size != PEN_RDESC_SIZE;
69 	if (ctx->retval) {
70 		ctx->retval = -EINVAL;
71 		return 0;
72 	}
73 
74 	if (ctx->rdesc[0] != 0x05 || ctx->rdesc[1] != 0x0d || /* Usage Page (Digitizers) */
75 	    ctx->rdesc[2] != 0x09 || ctx->rdesc[3] != 0x02 || /* Usage (Pen) */
76 	    ctx->rdesc[16] != 0x09 ||
77 	    ctx->rdesc[SECONDARY_SWITCH_OFFSET] != 0x43) { /* Secondary Tip Switch */
78 		ctx->retval = -EINVAL;
79 		return 0;
80 	}
81 
82 	ctx->retval = 0;
83 
84 	return 0;
85 }
86 
87 char _license[] SEC("license") = "GPL";
88