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 
19 HID_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 
125 static const __u8 disabled_rdesc[] = {
126 	// Make sure we match our original report length
127 	FixedSizeVendorReport(VENDOR_REPORT_LENGTH)
128 };
129 
130 static 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 
217 SEC(HID_BPF_RDESC_FIXUP)
218 int 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 
241 static 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 
264 SEC(HID_BPF_DEVICE_EVENT)
265 int 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 
294 HID_BPF_OPS(xppen_ack05_remote) = {
295 	.hid_device_event = (void *)ack05_fix_events,
296 	.hid_rdesc_fixup = (void *)ack05_fix_rdesc,
297 };
298 
299 SEC("syscall")
300 int 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 
331 char _license[] SEC("license") = "GPL";
332