File Index
Files and directories
Sign in
FILE

XPPen__Artist24.bpf.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hid/bpf/progs/XPPen__Artist24.bpf.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/* Copyright (c) 2023 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_UGEE 0x28BD /* VID is shared with SinoWealth and Glorious and prob others */
 11#define PID_ARTIST_24 0x093A
 12#define PID_ARTIST_24_PRO 0x092D
 13
 14HID_BPF_CONFIG(
 15	HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_24),
 16	HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_24_PRO)
 17);
 18
 19/*
 20 * We need to amend the report descriptor for the following:
 21 * - the device reports Eraser instead of using Secondary Barrel Switch
 22 * - the pen doesn't have a rubber tail, so basically we are removing any
 23 *   eraser/invert bits
 24 */
 25static const __u8 fixed_rdesc[] = {
 26	0x05, 0x0d,                    // Usage Page (Digitizers)             0
 27	0x09, 0x02,                    // Usage (Pen)                         2
 28	0xa1, 0x01,                    // Collection (Application)            4
 29	0x85, 0x07,                    //  Report ID (7)                      6
 30	0x09, 0x20,                    //  Usage (Stylus)                     8
 31	0xa1, 0x00,                    //  Collection (Physical)              10
 32	0x09, 0x42,                    //   Usage (Tip Switch)                12
 33	0x09, 0x44,                    //   Usage (Barrel Switch)             14
 34	0x09, 0x5a,                    //   Usage (Secondary Barrel Switch)   16  /* changed from 0x45 (Eraser) to 0x5a (Secondary Barrel Switch) */
 35	0x15, 0x00,                    //   Logical Minimum (0)               18
 36	0x25, 0x01,                    //   Logical Maximum (1)               20
 37	0x75, 0x01,                    //   Report Size (1)                   22
 38	0x95, 0x03,                    //   Report Count (3)                  24
 39	0x81, 0x02,                    //   Input (Data,Var,Abs)              26
 40	0x95, 0x02,                    //   Report Count (2)                  28
 41	0x81, 0x03,                    //   Input (Cnst,Var,Abs)              30
 42	0x09, 0x32,                    //   Usage (In Range)                  32
 43	0x95, 0x01,                    //   Report Count (1)                  34
 44	0x81, 0x02,                    //   Input (Data,Var,Abs)              36
 45	0x95, 0x02,                    //   Report Count (2)                  38
 46	0x81, 0x03,                    //   Input (Cnst,Var,Abs)              40
 47	0x75, 0x10,                    //   Report Size (16)                  42
 48	0x95, 0x01,                    //   Report Count (1)                  44
 49	0x35, 0x00,                    //   Physical Minimum (0)              46
 50	0xa4,                          //   Push                              48
 51	0x05, 0x01,                    //   Usage Page (Generic Desktop)      49
 52	0x09, 0x30,                    //   Usage (X)                         51
 53	0x65, 0x13,                    //   Unit (EnglishLinear: in)          53
 54	0x55, 0x0d,                    //   Unit Exponent (-3)                55
 55	0x46, 0xf0, 0x50,              //   Physical Maximum (20720)          57
 56	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           60
 57	0x81, 0x02,                    //   Input (Data,Var,Abs)              63
 58	0x09, 0x31,                    //   Usage (Y)                         65
 59	0x46, 0x91, 0x2d,              //   Physical Maximum (11665)          67
 60	0x26, 0xff, 0x7f,              //   Logical Maximum (32767)           70
 61	0x81, 0x02,                    //   Input (Data,Var,Abs)              73
 62	0xb4,                          //   Pop                               75
 63	0x09, 0x30,                    //   Usage (Tip Pressure)              76
 64	0x45, 0x00,                    //   Physical Maximum (0)              78
 65	0x26, 0xff, 0x1f,              //   Logical Maximum (8191)            80
 66	0x81, 0x42,                    //   Input (Data,Var,Abs,Null)         83
 67	0x09, 0x3d,                    //   Usage (X Tilt)                    85
 68	0x15, 0x81,                    //   Logical Minimum (-127)            87
 69	0x25, 0x7f,                    //   Logical Maximum (127)             89
 70	0x75, 0x08,                    //   Report Size (8)                   91
 71	0x95, 0x01,                    //   Report Count (1)                  93
 72	0x81, 0x02,                    //   Input (Data,Var,Abs)              95
 73	0x09, 0x3e,                    //   Usage (Y Tilt)                    97
 74	0x15, 0x81,                    //   Logical Minimum (-127)            99
 75	0x25, 0x7f,                    //   Logical Maximum (127)             101
 76	0x81, 0x02,                    //   Input (Data,Var,Abs)              103
 77	0xc0,                          //  End Collection                     105
 78	0xc0,                          // End Collection                      106
 79};
 80
 81#define TIP_SWITCH		BIT(0)
 82#define BARREL_SWITCH		BIT(1)
 83#define ERASER			BIT(2)
 84/* padding			BIT(3) */
 85/* padding			BIT(4) */
 86#define IN_RANGE		BIT(5)
 87/* padding			BIT(6) */
 88/* padding			BIT(7) */
 89
 90#define U16(index) (data[index] | (data[index + 1] << 8))
 91
 92SEC(HID_BPF_RDESC_FIXUP)
 93int BPF_PROG(hid_fix_rdesc_xppen_artist24, struct hid_bpf_ctx *hctx)
 94{
 95	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
 96
 97	if (!data)
 98		return 0; /* EPERM check */
 99
100	__builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));
101
102	return sizeof(fixed_rdesc);
103}
104
105static __u8 prev_state = 0;
106
107/*
108 * There are a few cases where the device is sending wrong event
109 * sequences, all related to the second button (the pen doesn't
110 * have an eraser switch on the tail end):
111 *
112 *   whenever the second button gets pressed or released, an
113 *   out-of-proximity event is generated and then the firmware
114 *   compensate for the missing state (and the firmware uses
115 *   eraser for that button):
116 *
117 *   - if the pen is in range, an extra out-of-range is sent
118 *     when the second button is pressed/released:
119 *     // Pen is in range
120 *     E:                               InRange
121 *
122 *     // Second button is pressed
123 *     E:
124 *     E:                        Eraser InRange
125 *
126 *     // Second button is released
127 *     E:
128 *     E:                               InRange
129 *
130 *     This case is ignored by this filter, it's "valid"
131 *     and userspace knows how to deal with it, there are just
132 *     a few out-of-prox events generated, but the user doesn´t
133 *     see them.
134 *
135 *   - if the pen is in contact, 2 extra events are added when
136 *     the second button is pressed/released: an out of range
137 *     and an in range:
138 *
139 *     // Pen is in contact
140 *     E: TipSwitch                     InRange
141 *
142 *     // Second button is pressed
143 *     E:                                         <- false release, needs to be filtered out
144 *     E:                        Eraser InRange   <- false release, needs to be filtered out
145 *     E: TipSwitch              Eraser InRange
146 *
147 *     // Second button is released
148 *     E:                                         <- false release, needs to be filtered out
149 *     E:                               InRange   <- false release, needs to be filtered out
150 *     E: TipSwitch                     InRange
151 *
152 */
153SEC(HID_BPF_DEVICE_EVENT)
154int BPF_PROG(xppen_24_fix_eraser, struct hid_bpf_ctx *hctx)
155{
156	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
157	__u8 current_state, changed_state;
158	bool prev_tip;
159
160	if (!data)
161		return 0; /* EPERM check */
162
163	current_state = data[1];
164
165	/* if the state is identical to previously, early return */
166	if (current_state == prev_state)
167		return 0;
168
169	prev_tip = !!(prev_state & TIP_SWITCH);
170
171	/*
172	 * Illegal transition: pen is in range with the tip pressed, and
173	 * it goes into out of proximity.
174	 *
175	 * Ideally we should hold the event, start a timer and deliver it
176	 * only if the timer ends, but we are not capable of that now.
177	 *
178	 * And it doesn't matter because when we are in such cases, this
179	 * means we are detecting a false release.
180	 */
181	if ((current_state & IN_RANGE) == 0) {
182		if (prev_tip)
183			return HID_IGNORE_EVENT;
184		return 0;
185	}
186
187	/*
188	 * XOR to only set the bits that have changed between
189	 * previous and current state
190	 */
191	changed_state = prev_state ^ current_state;
192
193	/* Store the new state for future processing */
194	prev_state = current_state;
195
196	/*
197	 * We get both a tipswitch and eraser change in the same HID report:
198	 * this is not an authorized transition and is unlikely to happen
199	 * in real life.
200	 * This is likely to be added by the firmware to emulate the
201	 * eraser mode so we can skip the event.
202	 */
203	if ((changed_state & (TIP_SWITCH | ERASER)) == (TIP_SWITCH | ERASER)) /* we get both a tipswitch and eraser change at the same time */
204		return HID_IGNORE_EVENT;
205
206	return 0;
207}
208
209HID_BPF_OPS(xppen_artist_24) = {
210	.hid_rdesc_fixup = (void *)hid_fix_rdesc_xppen_artist24,
211	.hid_device_event = (void *)xppen_24_fix_eraser,
212};
213
214SEC("syscall")
215int probe(struct hid_bpf_probe_args *ctx)
216{
217	/*
218	 * The device exports 3 interfaces.
219	 */
220	ctx->retval = ctx->rdesc_size != 107;
221	if (ctx->retval)
222		ctx->retval = -EINVAL;
223
224	/* ensure the kernel isn't fixed already */
225	if (ctx->rdesc[17] != 0x45) /* Eraser */
226		ctx->retval = -EINVAL;
227
228	return 0;
229}
230
231char _license[] SEC("license") = "GPL";
~/ Read-only file preview.