Merge branch 'for-6.17/uclogic' into for-linus

- support for XP-PEN Artist 22R Pro (Joshua Goins)
This commit is contained in:
Jiri Kosina
2025-07-31 22:54:15 +02:00
6 changed files with 249 additions and 5 deletions

View File

@@ -1409,6 +1409,7 @@
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S 0x0909
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW 0x0933
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 0x0078
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO 0x091b
#define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074
#define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071
#define USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720 0x0055

View File

@@ -62,6 +62,30 @@ static const __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
return rdesc;
}
/* Buttons considered valid tablet pad inputs. */
static const unsigned int uclogic_extra_input_mapping[] = {
BTN_0,
BTN_1,
BTN_2,
BTN_3,
BTN_4,
BTN_5,
BTN_6,
BTN_7,
BTN_8,
BTN_RIGHT,
BTN_MIDDLE,
BTN_SIDE,
BTN_EXTRA,
BTN_FORWARD,
BTN_BACK,
BTN_B,
BTN_A,
BTN_BASE,
BTN_BASE2,
BTN_X
};
static int uclogic_input_mapping(struct hid_device *hdev,
struct hid_input *hi,
struct hid_field *field,
@@ -72,9 +96,27 @@ static int uclogic_input_mapping(struct hid_device *hdev,
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
struct uclogic_params *params = &drvdata->params;
/* Discard invalid pen usages */
if (params->pen.usage_invalid && (field->application == HID_DG_PEN))
return -1;
if (field->application == HID_GD_KEYPAD) {
/*
* Remap input buttons to sensible ones that are not invalid.
* This only affects previous behavior for devices with more than ten or so buttons.
*/
const int key = (usage->hid & HID_USAGE) - 1;
if (key < ARRAY_SIZE(uclogic_extra_input_mapping)) {
hid_map_usage(hi,
usage,
bit,
max,
EV_KEY,
uclogic_extra_input_mapping[key]);
return 1;
}
} else if (field->application == HID_DG_PEN) {
/* Discard invalid pen usages */
if (params->pen.usage_invalid)
return -1;
}
/* Let hid-core decide what to do */
return 0;
@@ -407,8 +449,22 @@ static int uclogic_raw_event_frame(
/* If need to, and can, transform the bitmap dial reports */
if (frame->bitmap_dial_byte > 0 && frame->bitmap_dial_byte < size) {
if (data[frame->bitmap_dial_byte] == 2)
switch (data[frame->bitmap_dial_byte]) {
case 2:
data[frame->bitmap_dial_byte] = -1;
break;
/* Everything below here is for tablets that shove multiple dials into 1 byte */
case 16:
data[frame->bitmap_dial_byte] = 0;
data[frame->bitmap_second_dial_destination_byte] = 1;
break;
case 32:
data[frame->bitmap_dial_byte] = 0;
data[frame->bitmap_second_dial_destination_byte] = -1;
break;
}
}
return 0;
@@ -546,6 +602,8 @@ static const struct hid_device_id uclogic_devices[] = {
.driver_data = UCLOGIC_MOUSE_FRAME_QUIRK | UCLOGIC_BATTERY_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO) },
{ }
};
MODULE_DEVICE_TABLE(hid, uclogic_devices);

View File

@@ -103,6 +103,8 @@ static void uclogic_params_frame_hid_dbg(
frame->touch_flip_at);
hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n",
frame->bitmap_dial_byte);
hid_dbg(hdev, "\t\t.bitmap_second_dial_destination_byte = %u\n",
frame->bitmap_second_dial_destination_byte);
}
/**
@@ -1341,7 +1343,7 @@ static int uclogic_params_ugee_v2_init_event_hooks(struct hid_device *hdev,
struct uclogic_params *p)
{
struct uclogic_raw_event_hook *event_hook;
__u8 reconnect_event[] = {
static const __u8 reconnect_event[] = {
/* Event received on wireless tablet reconnection */
0x02, 0xF8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
@@ -1529,6 +1531,126 @@ cleanup:
return rc;
}
/*
* uclogic_params_init_ugee_xppen_pro_22r() - Initializes a UGEE XP-Pen Pro 22R tablet device.
*
* @hdev: The HID device of the tablet interface to initialize and get
* parameters from. Cannot be NULL.
* @params: Parameters to fill in (to be cleaned with
* uclogic_params_cleanup()). Not modified in case of error.
* Cannot be NULL.
*
* Returns:
* Zero, if successful. A negative errno code on error.
*/
static int uclogic_params_init_ugee_xppen_pro_22r(struct uclogic_params *params,
struct hid_device *hdev,
const u8 rdesc_frame_arr[],
const size_t rdesc_frame_size)
{
int rc = 0;
struct usb_interface *iface;
__u8 bInterfaceNumber;
const int str_desc_len = 12;
u8 *str_desc = NULL;
__u8 *rdesc_pen = NULL;
s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
enum uclogic_params_frame_type frame_type;
/* The resulting parameters (noop) */
struct uclogic_params p = {0, };
if (!hdev || !params) {
rc = -EINVAL;
goto cleanup;
}
iface = to_usb_interface(hdev->dev.parent);
bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
/* Ignore non-pen interfaces */
if (bInterfaceNumber != 2) {
rc = -EINVAL;
uclogic_params_init_invalid(&p);
goto cleanup;
}
/*
* Initialize the interface by sending magic data.
* This magic data is the same as other UGEE v2 tablets.
*/
rc = uclogic_probe_interface(hdev,
uclogic_ugee_v2_probe_arr,
uclogic_ugee_v2_probe_size,
uclogic_ugee_v2_probe_endpoint);
if (rc) {
uclogic_params_init_invalid(&p);
goto cleanup;
}
/**
* Read the string descriptor containing pen and frame parameters.
* These are slightly different than typical UGEE v2 devices.
*/
rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len);
if (rc != str_desc_len) {
rc = (rc < 0) ? rc : -EINVAL;
hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc);
uclogic_params_init_invalid(&p);
goto cleanup;
}
rc = uclogic_params_parse_ugee_v2_desc(str_desc, str_desc_len,
desc_params,
ARRAY_SIZE(desc_params),
&frame_type);
if (rc)
goto cleanup;
// str_desc doesn't report the correct amount of buttons, so manually fix it
desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = 20;
kfree(str_desc);
str_desc = NULL;
/* Initialize the pen interface */
rdesc_pen = uclogic_rdesc_template_apply(
uclogic_rdesc_ugee_v2_pen_template_arr,
uclogic_rdesc_ugee_v2_pen_template_size,
desc_params, ARRAY_SIZE(desc_params));
if (!rdesc_pen) {
rc = -ENOMEM;
goto cleanup;
}
p.pen.desc_ptr = rdesc_pen;
p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size;
p.pen.id = 0x02;
p.pen.subreport_list[0].value = 0xf0;
p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID;
/* Initialize the frame interface */
rc = uclogic_params_frame_init_with_desc(
&p.frame_list[0],
rdesc_frame_arr,
rdesc_frame_size,
UCLOGIC_RDESC_V1_FRAME_ID);
if (rc < 0) {
hid_err(hdev, "initializing frame params failed: %d\n", rc);
goto cleanup;
}
p.frame_list[0].bitmap_dial_byte = 7;
p.frame_list[0].bitmap_second_dial_destination_byte = 8;
/* Output parameters */
memcpy(params, &p, sizeof(*params));
memset(&p, 0, sizeof(p));
cleanup:
kfree(str_desc);
uclogic_params_cleanup(&p);
return rc;
}
/**
* uclogic_params_init() - initialize a tablet interface and discover its
* parameters.
@@ -1845,6 +1967,16 @@ int uclogic_params_init(struct uclogic_params *params,
uclogic_params_init_invalid(&p);
}
break;
case VID_PID(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO):
rc = uclogic_params_init_ugee_xppen_pro_22r(&p,
hdev,
uclogic_rdesc_xppen_artist_22r_pro_frame_arr,
uclogic_rdesc_xppen_artist_22r_pro_frame_size);
if (rc != 0)
goto cleanup;
break;
}

View File

@@ -175,6 +175,11 @@ struct uclogic_params_frame {
* counterclockwise, as opposed to the normal 1 and -1.
*/
unsigned int bitmap_dial_byte;
/*
* Destination offset for the second bitmap dial byte, if the tablet
* supports a second dial at all.
*/
unsigned int bitmap_second_dial_destination_byte;
};
/*

View File

@@ -1193,6 +1193,50 @@ const __u8 uclogic_rdesc_xppen_deco01_frame_arr[] = {
const size_t uclogic_rdesc_xppen_deco01_frame_size =
sizeof(uclogic_rdesc_xppen_deco01_frame_arr);
/* Fixed report descriptor for XP-Pen Arist 22R Pro frame */
const __u8 uclogic_rdesc_xppen_artist_22r_pro_frame_arr[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x07, /* Usage (Keypad), */
0xA1, 0x01, /* Collection (Application), */
0x85, UCLOGIC_RDESC_V1_FRAME_ID,
/* Report ID (Virtual report), */
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x39, /* Usage (Tablet Function Keys), */
0xA0, /* Collection (Physical), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x08, /* Report Count (8), */
0x81, 0x01, /* Input (Constant), */
0x05, 0x09, /* Usage Page (Button), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x14, /* Usage Maximum (14h), */
0x95, 0x14, /* Report Count (20), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x14, /* Report Count (20), */
0x81, 0x01, /* Input (Constant), */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x38, /* Usage (Wheel), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x01, /* Report Count (1), */
0x15, 0xFF, /* Logical Minimum (-1), */
0x25, 0x08, /* Logical Maximum (8), */
0x81, 0x06, /* Input (Variable, Relative), */
0x05, 0x0C, /* Usage Page (Consumer Devices), */
0x0A, 0x38, 0x02, /* Usage (AC PAN), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x06, /* Input (Variable, Relative), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection */
0xC0, /* End Collection */
};
const size_t uclogic_rdesc_xppen_artist_22r_pro_frame_size =
sizeof(uclogic_rdesc_xppen_artist_22r_pro_frame_arr);
/**
* uclogic_rdesc_template_apply() - apply report descriptor parameters to a
* report descriptor template, creating a report descriptor. Copies the

View File

@@ -210,4 +210,8 @@ extern const size_t uclogic_rdesc_ugee_g5_frame_size;
/* Least-significant bit of Ugee G5 frame rotary encoder state */
#define UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB 38
/* Fixed report descriptor for XP-Pen Arist 22R Pro frame */
extern const __u8 uclogic_rdesc_xppen_artist_22r_pro_frame_arr[];
extern const size_t uclogic_rdesc_xppen_artist_22r_pro_frame_size;
#endif /* _HID_UCLOGIC_RDESC_H */