#include "vmlinux.h"
#include "hid_bpf.h"
#include "hid_bpf_helpers.h"
#include <bpf/bpf_tracing.h>
#define VID_UGEE 0x28BD
#define PID_ARTIST_PRO14_GEN2 0x095A
#define PID_ARTIST_PRO16_GEN2 0x095B
#define PID_ARTIST_PRO19_GEN2 0x096A
HID_BPF_CONFIG(
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_PRO14_GEN2),
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_PRO16_GEN2),
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_PRO19_GEN2)
);
static const __u8 fixed_rdesc[] = {
0x05, 0x0d,
0x09, 0x02,
0xa1, 0x01,
0x85, 0x07,
0x09, 0x20,
0xa1, 0x00,
0x09, 0x42,
0x09, 0x44,
0x09, 0x5a,
0x09, 0x3c,
0x09, 0x45,
0x15, 0x00,
0x25, 0x01,
0x75, 0x01,
0x95, 0x05,
0x81, 0x02,
0x09, 0x32,
0x15, 0x00,
0x25, 0x01,
0x95, 0x01,
0x81, 0x02,
0x95, 0x02,
0x81, 0x03,
0x75, 0x10,
0x95, 0x01,
0x35, 0x00,
0xa4,
0x05, 0x01,
0x09, 0x30,
0x65, 0x13,
0x55, 0x0d,
0x46, 0xff, 0x34,
0x26, 0xff, 0x7f,
0x81, 0x02,
0x09, 0x31,
0x46, 0x20, 0x21,
0x26, 0xff, 0x7f,
0x81, 0x02,
0xb4,
0x09, 0x30,
0x45, 0x00,
0x26, 0xff, 0x3f,
0x81, 0x42,
0x09, 0x3d,
0x15, 0x81,
0x25, 0x7f,
0x75, 0x08,
0x95, 0x01,
0x81, 0x02,
0x09, 0x3e,
0x15, 0x81,
0x25, 0x7f,
0x81, 0x02,
0xc0,
0xc0,
};
SEC(HID_BPF_RDESC_FIXUP)
int BPF_PROG(hid_fix_rdesc_xppen_artistpro16gen2, struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 , 4096 );
if (!data)
return 0;
__builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));
if (hctx->hid->product == PID_ARTIST_PRO14_GEN2) {
data[63] = 0x2e;
data[62] = 0x62;
data[73] = 0x1c;
data[72] = 0xfd;
} else if (hctx->hid->product == PID_ARTIST_PRO19_GEN2) {
data[63] = 0x3e;
data[62] = 0xe5;
data[73] = 0x23;
data[72] = 0x61;
}
return sizeof(fixed_rdesc);
}
static int xppen_16_fix_eraser(struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 , 10 );
if (!data)
return 0;
if ((data[1] & 0x29) != 0x29)
return 0;
data[1] ^= 0x19;
return 0;
}
static const __u16 angle_offsets_horizontal_14[128] = {
0, 3, 5, 8, 11, 13, 16, 19, 21, 24, 27, 29, 32, 35, 37, 40, 42, 45, 47, 50, 53,
55, 58, 60, 62, 65, 67, 70, 72, 74, 77, 79, 81, 84, 86, 88, 90, 92, 95, 97, 99,
101, 103, 105, 107, 109, 111, 112, 114, 116, 118, 119, 121, 123, 124, 126, 127,
129, 130, 132, 133, 134, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
147, 148, 148, 149, 150, 150, 151, 151, 152, 152, 153, 153, 153, 153, 153, 154,
154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 151, 151, 150, 150, 149,
148, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 134, 133,
132, 130, 129, 127, 126, 124, 123
};
static const __u16 angle_offsets_vertical_14[128] = {
0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 59, 64, 68, 72, 76, 80, 84,
88, 92, 96, 100, 104, 108, 112, 115, 119, 123, 127, 130, 134, 137, 141, 145, 148,
151, 155, 158, 161, 165, 168, 171, 174, 177, 180, 183, 186, 188, 191, 194, 196,
199, 201, 204, 206, 208, 211, 213, 215, 217, 219, 221, 223, 225, 226, 228, 230,
231, 232, 234, 235, 236, 237, 239, 240, 240, 241, 242, 243, 243, 244, 244, 245,
245, 246, 246, 246, 246, 246, 246, 246, 245, 245, 244, 244, 243, 243, 242, 241,
240, 240, 239, 237, 236, 235, 234, 232, 231, 230, 228, 226, 225, 223, 221, 219,
217, 215, 213, 211, 208, 206, 204, 201, 199, 196
};
static const __u16 angle_offsets_horizontal_16[128] = {
0, 2, 5, 7, 9, 12, 14, 16, 19, 21, 23, 26, 28, 30, 33, 35, 37, 39, 42, 44, 46, 48,
50, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 86, 88, 90,
92, 93, 95, 97, 98, 100, 101, 103, 105, 106, 107, 109, 110, 111, 113, 114, 115,
116, 118, 119, 120, 121, 122, 123, 124, 125, 126, 126, 127, 128, 129, 129, 130,
130, 131, 132, 132, 132, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 134,
134, 134, 134, 134, 134, 133, 133, 133, 132, 132, 132, 131, 130, 130, 129, 129,
128, 127, 126, 126, 125, 124, 123, 122, 121, 120, 119, 118, 116, 115, 114, 113,
111, 110, 109, 107
};
static const __u16 angle_offsets_vertical_16[128] = {
0, 4, 8, 11, 15, 19, 22, 26, 30, 34, 37, 41, 45, 48, 52, 56, 59, 63, 66, 70, 74,
77, 81, 84, 88, 91, 94, 98, 101, 104, 108, 111, 114, 117, 120, 123, 126, 129, 132,
135, 138, 141, 144, 147, 149, 152, 155, 157, 160, 162, 165, 167, 170, 172, 174,
176, 178, 180, 182, 184, 186, 188, 190, 192, 193, 195, 197, 198, 199, 201, 202,
203, 205, 206, 207, 208, 209, 210, 210, 211, 212, 212, 213, 214, 214, 214, 215,
215, 215, 215, 215, 215, 215, 215, 215, 214, 214, 214, 213, 212, 212, 211, 210,
210, 209, 208, 207, 206, 205, 203, 202, 201, 199, 198, 197, 195, 193, 192, 190,
188, 186, 184, 182, 180, 178, 176, 174, 172
};
static const __u16 angle_offsets_horizontal_19[128] = {
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 25, 27, 29, 31, 33, 35, 37, 39, 41,
42, 44, 46, 48, 50, 51, 53, 55, 57, 58, 60, 62, 63, 65, 67, 68, 70, 71, 73, 74, 76,
77, 79, 80, 82, 83, 84, 86, 87, 88, 89, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100,
101, 102, 103, 104, 104, 105, 106, 106, 107, 108, 108, 109, 109, 110, 110, 111,
111, 112, 112, 112, 112, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
113, 113, 112, 112, 112, 112, 111, 111, 110, 110, 109, 109, 108, 108, 107, 106,
106, 105, 104, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 90
};
static const __u16 angle_offsets_vertical_19[128] = {
0, 4, 7, 11, 14, 18, 21, 25, 28, 32, 35, 38, 42, 45, 49, 52, 56, 59, 62, 66, 69, 72,
75, 79, 82, 85, 88, 91, 95, 98, 101, 104, 107, 110, 113, 116, 118, 121, 124, 127,
129, 132, 135, 137, 140, 142, 145, 147, 150, 152, 154, 157, 159, 161, 163, 165, 167,
169, 171, 173, 174, 176, 178, 179, 181, 183, 184, 185, 187, 188, 189, 190, 192, 193,
194, 195, 195, 196, 197, 198, 198, 199, 199, 200, 200, 201, 201, 201, 201, 201, 201,
201, 201, 201, 201, 201, 200, 200, 199, 199, 198, 198, 197, 196, 195, 195, 194, 193,
192, 190, 189, 188, 187, 185, 184, 183, 181, 179, 178, 176, 174, 173, 171, 169, 167,
165, 163, 161
};
static void compensate_coordinates_by_tilt(__u8 *data, const __u8 idx,
const __s8 tilt, const __u16 (*compensation_table)[128])
{
__u16 coords = data[idx+1];
coords <<= 8;
coords += data[idx];
__u8 direction = tilt > 0 ? 0 : 1;
__u8 angle = tilt > 0 ? tilt : -tilt;
if (angle > 127)
return;
__u16 compensation = (*compensation_table)[angle];
if (direction == 0) {
coords = (coords > compensation) ? coords - compensation : 0;
} else {
const __u16 logical_maximum = 32767;
__u16 max = logical_maximum - compensation;
coords = (coords < max) ? coords + compensation : logical_maximum;
}
data[idx] = coords & 0xff;
data[idx+1] = coords >> 8;
}
static int xppen_16_fix_angle_offset(struct hid_bpf_ctx *hctx)
{
__u8 *data = hid_bpf_get_data(hctx, 0 , 10 );
if (!data)
return 0;
__s8 tilt_x = (__s8) data[8];
__s8 tilt_y = (__s8) data[9];
switch (hctx->hid->product) {
case PID_ARTIST_PRO14_GEN2:
compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_14);
compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_14);
break;
case PID_ARTIST_PRO16_GEN2:
compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_16);
compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_16);
break;
case PID_ARTIST_PRO19_GEN2:
compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_19);
compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_19);
break;
}
return 0;
}
SEC(HID_BPF_DEVICE_EVENT)
int BPF_PROG(xppen_artist_pro_16_device_event, struct hid_bpf_ctx *hctx)
{
int ret = xppen_16_fix_angle_offset(hctx);
if (ret)
return ret;
return xppen_16_fix_eraser(hctx);
}
HID_BPF_OPS(xppen_artist_pro_16) = {
.hid_rdesc_fixup = (void *)hid_fix_rdesc_xppen_artistpro16gen2,
.hid_device_event = (void *)xppen_artist_pro_16_device_event,
};
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
ctx->retval = ctx->rdesc_size != 113;
if (ctx->retval)
ctx->retval = -EINVAL;
if (ctx->rdesc[17] != 0x45)
ctx->retval = -EINVAL;
return 0;
}
char _license[] SEC("license") = "GPL";