Skip to content

Commit 0cb1fc0

Browse files
JoseExpositoJiri Kosina
authored andcommitted
HID: uclogic: Add support for XP-PEN Deco L
The XP-PEN Deco L (UGEE) needs to be initialized by sending a buffer of magic data, discovered by sniffing the Windows driver traffic. In order to differentiate UGEE tablets that need this kind of initialization from the previous ones, name them v2 internally and create an entry point for them. After initialization, the template report descriptors can be discovered by parsing a string descriptor, similar to the one exposed by HUION v1 devices. Add all the required elements to support the device. Signed-off-by: José Expósito <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 867c892 commit 0cb1fc0

File tree

5 files changed

+310
-0
lines changed

5 files changed

+310
-0
lines changed

drivers/hid/hid-ids.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,7 @@
12781278
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075
12791279
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 0x0094
12801280
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042
1281+
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L 0x0935
12811282
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 0x0078
12821283
#define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074
12831284
#define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071

drivers/hid/hid-uclogic-core.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,8 @@ static const struct hid_device_id uclogic_devices[] = {
521521
USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640) },
522522
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
523523
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) },
524+
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
525+
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) },
524526
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
525527
USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) },
526528
{ }

drivers/hid/hid-uclogic-params.c

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,197 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
10021002
return rc;
10031003
}
10041004

1005+
/**
1006+
* uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or
1007+
* the XP-PEN Deco Mini 7, need to be initialized by sending them magic data.
1008+
*
1009+
* @hdev: The HID device of the tablet interface to initialize and get
1010+
* parameters from. Cannot be NULL.
1011+
* @magic_arr: The magic data that should be sent to probe the interface.
1012+
* Cannot be NULL.
1013+
* @magic_size: Size of the magic data.
1014+
* @endpoint: Endpoint where the magic data should be sent.
1015+
*
1016+
* Returns:
1017+
* Zero, if successful. A negative errno code on error.
1018+
*/
1019+
static int uclogic_probe_interface(struct hid_device *hdev, u8 *magic_arr,
1020+
int magic_size, int endpoint)
1021+
{
1022+
struct usb_device *udev;
1023+
unsigned int pipe = 0;
1024+
int sent;
1025+
u8 *buf = NULL;
1026+
int rc = 0;
1027+
1028+
if (!hdev || !magic_arr) {
1029+
rc = -EINVAL;
1030+
goto cleanup;
1031+
}
1032+
1033+
buf = kmemdup(magic_arr, magic_size, GFP_KERNEL);
1034+
if (!buf) {
1035+
rc = -ENOMEM;
1036+
goto cleanup;
1037+
}
1038+
1039+
udev = hid_to_usb_dev(hdev);
1040+
pipe = usb_sndintpipe(udev, endpoint);
1041+
1042+
rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000);
1043+
if (rc || sent != magic_size) {
1044+
hid_err(hdev, "Interface probing failed: %d\n", rc);
1045+
rc = -1;
1046+
goto cleanup;
1047+
}
1048+
1049+
rc = 0;
1050+
cleanup:
1051+
kfree(buf);
1052+
return rc;
1053+
}
1054+
1055+
/**
1056+
* uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by
1057+
* discovering their parameters.
1058+
*
1059+
* These tables, internally designed as v2 to differentiate them from older
1060+
* models, expect a payload of magic data in orther to be switched to the fully
1061+
* functional mode and expose their parameters in a similar way to the
1062+
* information present in uclogic_params_pen_init_v1() but with some
1063+
* differences.
1064+
*
1065+
* @params: Parameters to fill in (to be cleaned with
1066+
* uclogic_params_cleanup()). Not modified in case of error.
1067+
* Cannot be NULL.
1068+
* @hdev: The HID device of the tablet interface to initialize and get
1069+
* parameters from. Cannot be NULL.
1070+
*
1071+
* Returns:
1072+
* Zero, if successful. A negative errno code on error.
1073+
*/
1074+
static int uclogic_params_ugee_v2_init(struct uclogic_params *params,
1075+
struct hid_device *hdev)
1076+
{
1077+
int rc = 0;
1078+
struct usb_interface *iface;
1079+
__u8 bInterfaceNumber;
1080+
const int str_desc_len = 12;
1081+
__u8 *str_desc = NULL;
1082+
__u8 *rdesc_pen = NULL;
1083+
__u8 *rdesc_frame = NULL;
1084+
s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
1085+
s32 resolution;
1086+
__u8 magic_arr[] = {
1087+
0x02, 0xb0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1088+
};
1089+
/* The resulting parameters (noop) */
1090+
struct uclogic_params p = {0, };
1091+
1092+
if (!params || !hdev) {
1093+
rc = -EINVAL;
1094+
goto cleanup;
1095+
}
1096+
1097+
iface = to_usb_interface(hdev->dev.parent);
1098+
bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
1099+
if (bInterfaceNumber != 2) {
1100+
uclogic_params_init_invalid(&p);
1101+
goto output;
1102+
}
1103+
1104+
/*
1105+
* Initialize the interface by sending magic data.
1106+
* The specific data was discovered by sniffing the Windows driver
1107+
* traffic.
1108+
*/
1109+
rc = uclogic_probe_interface(hdev, magic_arr, sizeof(magic_arr), 0x03);
1110+
if (rc) {
1111+
uclogic_params_init_invalid(&p);
1112+
goto output;
1113+
}
1114+
1115+
/*
1116+
* Read the string descriptor containing pen and frame parameters.
1117+
* The specific string descriptor and data were discovered by sniffing
1118+
* the Windows driver traffic.
1119+
*/
1120+
rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len);
1121+
if (rc != str_desc_len) {
1122+
hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc);
1123+
uclogic_params_init_invalid(&p);
1124+
goto output;
1125+
}
1126+
1127+
desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
1128+
get_unaligned_le16(str_desc + 2);
1129+
desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
1130+
get_unaligned_le16(str_desc + 4);
1131+
desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = str_desc[6];
1132+
desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
1133+
get_unaligned_le16(str_desc + 8);
1134+
resolution = get_unaligned_le16(str_desc + 10);
1135+
if (resolution == 0) {
1136+
desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
1137+
desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
1138+
} else {
1139+
desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
1140+
desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
1141+
resolution;
1142+
desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
1143+
desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
1144+
resolution;
1145+
}
1146+
kfree(str_desc);
1147+
str_desc = NULL;
1148+
1149+
/* Initialize the pen interface */
1150+
rdesc_pen = uclogic_rdesc_template_apply(
1151+
uclogic_rdesc_ugee_v2_pen_template_arr,
1152+
uclogic_rdesc_ugee_v2_pen_template_size,
1153+
desc_params, ARRAY_SIZE(desc_params));
1154+
if (!rdesc_pen) {
1155+
rc = -ENOMEM;
1156+
goto cleanup;
1157+
}
1158+
1159+
p.pen.desc_ptr = rdesc_pen;
1160+
p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size;
1161+
p.pen.id = 0x02;
1162+
p.pen.subreport_list[0].value = 0xf0;
1163+
p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID;
1164+
1165+
/* Initialize the frame interface */
1166+
rdesc_frame = uclogic_rdesc_template_apply(
1167+
uclogic_rdesc_ugee_v2_frame_btn_template_arr,
1168+
uclogic_rdesc_ugee_v2_frame_btn_template_size,
1169+
desc_params, ARRAY_SIZE(desc_params));
1170+
if (!rdesc_frame) {
1171+
rc = -ENOMEM;
1172+
goto cleanup;
1173+
}
1174+
1175+
rc = uclogic_params_frame_init_with_desc(&p.frame_list[0],
1176+
rdesc_frame,
1177+
uclogic_rdesc_ugee_v2_frame_btn_template_size,
1178+
UCLOGIC_RDESC_V1_FRAME_ID);
1179+
kfree(rdesc_frame);
1180+
if (rc) {
1181+
uclogic_params_init_invalid(&p);
1182+
goto output;
1183+
}
1184+
1185+
output:
1186+
/* Output parameters */
1187+
memcpy(params, &p, sizeof(*params));
1188+
memset(&p, 0, sizeof(p));
1189+
rc = 0;
1190+
cleanup:
1191+
kfree(str_desc);
1192+
uclogic_params_cleanup(&p);
1193+
return rc;
1194+
}
1195+
10051196
/**
10061197
* uclogic_params_init() - initialize a tablet interface and discover its
10071198
* parameters.
@@ -1237,6 +1428,12 @@ int uclogic_params_init(struct uclogic_params *params,
12371428
uclogic_params_init_invalid(&p);
12381429
}
12391430
break;
1431+
case VID_PID(USB_VENDOR_ID_UGEE,
1432+
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L):
1433+
rc = uclogic_params_ugee_v2_init(&p, hdev);
1434+
if (rc != 0)
1435+
goto cleanup;
1436+
break;
12401437
case VID_PID(USB_VENDOR_ID_TRUST,
12411438
USB_DEVICE_ID_TRUST_PANORA_TABLET):
12421439
case VID_PID(USB_VENDOR_ID_UGEE,

drivers/hid/hid-uclogic-rdesc.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,108 @@ const __u8 uclogic_rdesc_v2_frame_dial_arr[] = {
859859
const size_t uclogic_rdesc_v2_frame_dial_size =
860860
sizeof(uclogic_rdesc_v2_frame_dial_arr);
861861

862+
/* Fixed report descriptor template for UGEE v2 pen reports */
863+
const __u8 uclogic_rdesc_ugee_v2_pen_template_arr[] = {
864+
0x05, 0x0d, /* Usage Page (Digitizers), */
865+
0x09, 0x01, /* Usage (Digitizer), */
866+
0xa1, 0x01, /* Collection (Application), */
867+
0x85, 0x02, /* Report ID (2), */
868+
0x09, 0x20, /* Usage (Stylus), */
869+
0xa1, 0x00, /* Collection (Physical), */
870+
0x09, 0x42, /* Usage (Tip Switch), */
871+
0x09, 0x44, /* Usage (Barrel Switch), */
872+
0x09, 0x46, /* Usage (Tablet Pick), */
873+
0x75, 0x01, /* Report Size (1), */
874+
0x95, 0x03, /* Report Count (3), */
875+
0x14, /* Logical Minimum (0), */
876+
0x25, 0x01, /* Logical Maximum (1), */
877+
0x81, 0x02, /* Input (Variable), */
878+
0x95, 0x02, /* Report Count (2), */
879+
0x81, 0x03, /* Input (Constant, Variable), */
880+
0x09, 0x32, /* Usage (In Range), */
881+
0x95, 0x01, /* Report Count (1), */
882+
0x81, 0x02, /* Input (Variable), */
883+
0x95, 0x02, /* Report Count (2), */
884+
0x81, 0x03, /* Input (Constant, Variable), */
885+
0x75, 0x10, /* Report Size (16), */
886+
0x95, 0x01, /* Report Count (1), */
887+
0x35, 0x00, /* Physical Minimum (0), */
888+
0xa4, /* Push, */
889+
0x05, 0x01, /* Usage Page (Desktop), */
890+
0x09, 0x30, /* Usage (X), */
891+
0x65, 0x13, /* Unit (Inch), */
892+
0x55, 0x0d, /* Unit Exponent (-3), */
893+
0x27, UCLOGIC_RDESC_PEN_PH(X_LM),
894+
/* Logical Maximum (PLACEHOLDER), */
895+
0x47, UCLOGIC_RDESC_PEN_PH(X_PM),
896+
/* Physical Maximum (PLACEHOLDER), */
897+
0x81, 0x02, /* Input (Variable), */
898+
0x09, 0x31, /* Usage (Y), */
899+
0x27, UCLOGIC_RDESC_PEN_PH(Y_LM),
900+
/* Logical Maximum (PLACEHOLDER), */
901+
0x47, UCLOGIC_RDESC_PEN_PH(Y_PM),
902+
/* Physical Maximum (PLACEHOLDER), */
903+
0x81, 0x02, /* Input (Variable), */
904+
0xb4, /* Pop, */
905+
0x09, 0x30, /* Usage (Tip Pressure), */
906+
0x45, 0x00, /* Physical Maximum (0), */
907+
0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM),
908+
/* Logical Maximum (PLACEHOLDER), */
909+
0x75, 0x0D, /* Report Size (13), */
910+
0x95, 0x01, /* Report Count (1), */
911+
0x81, 0x02, /* Input (Variable), */
912+
0x75, 0x01, /* Report Size (1), */
913+
0x95, 0x03, /* Report Count (3), */
914+
0x81, 0x01, /* Input (Constant), */
915+
0x09, 0x3d, /* Usage (X Tilt), */
916+
0x35, 0xC3, /* Physical Minimum (-61), */
917+
0x45, 0x3C, /* Physical Maximum (60), */
918+
0x15, 0xC3, /* Logical Minimum (-61), */
919+
0x25, 0x3C, /* Logical Maximum (60), */
920+
0x75, 0x08, /* Report Size (8), */
921+
0x95, 0x01, /* Report Count (1), */
922+
0x81, 0x02, /* Input (Variable), */
923+
0x09, 0x3e, /* Usage (Y Tilt), */
924+
0x35, 0xC3, /* Physical Minimum (-61), */
925+
0x45, 0x3C, /* Physical Maximum (60), */
926+
0x15, 0xC3, /* Logical Minimum (-61), */
927+
0x25, 0x3C, /* Logical Maximum (60), */
928+
0x81, 0x02, /* Input (Variable), */
929+
0xc0, /* End Collection, */
930+
0xc0, /* End Collection */
931+
};
932+
const size_t uclogic_rdesc_ugee_v2_pen_template_size =
933+
sizeof(uclogic_rdesc_ugee_v2_pen_template_arr);
934+
935+
/* Fixed report descriptor template for UGEE v2 frame reports (buttons only) */
936+
const __u8 uclogic_rdesc_ugee_v2_frame_btn_template_arr[] = {
937+
0x05, 0x01, /* Usage Page (Desktop), */
938+
0x09, 0x07, /* Usage (Keypad), */
939+
0xA1, 0x01, /* Collection (Application), */
940+
0x85, UCLOGIC_RDESC_V1_FRAME_ID,
941+
/* Report ID, */
942+
0x05, 0x0D, /* Usage Page (Digitizer), */
943+
0x09, 0x39, /* Usage (Tablet Function Keys), */
944+
0xA0, /* Collection (Physical), */
945+
0x75, 0x01, /* Report Size (1), */
946+
0x95, 0x08, /* Report Count (8), */
947+
0x81, 0x01, /* Input (Constant), */
948+
0x05, 0x09, /* Usage Page (Button), */
949+
0x19, 0x01, /* Usage Minimum (01h), */
950+
UCLOGIC_RDESC_FRAME_PH_BTN,
951+
/* Usage Maximum (PLACEHOLDER), */
952+
0x95, 0x0A, /* Report Count (10), */
953+
0x14, /* Logical Minimum (0), */
954+
0x25, 0x01, /* Logical Maximum (1), */
955+
0x81, 0x02, /* Input (Variable), */
956+
0x95, 0x46, /* Report Count (70), */
957+
0x81, 0x01, /* Input (Constant), */
958+
0xC0, /* End Collection, */
959+
0xC0 /* End Collection */
960+
};
961+
const size_t uclogic_rdesc_ugee_v2_frame_btn_template_size =
962+
sizeof(uclogic_rdesc_ugee_v2_frame_btn_template_arr);
963+
862964
/* Fixed report descriptor for Ugee EX07 frame */
863965
const __u8 uclogic_rdesc_ugee_ex07_frame_arr[] = {
864966
0x05, 0x01, /* Usage Page (Desktop), */

drivers/hid/hid-uclogic-rdesc.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,14 @@ extern const size_t uclogic_rdesc_v2_frame_dial_size;
161161
/* Device ID byte offset in v2 frame dial reports */
162162
#define UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE 0x4
163163

164+
/* Fixed report descriptor template for UGEE v2 pen reports */
165+
extern const __u8 uclogic_rdesc_ugee_v2_pen_template_arr[];
166+
extern const size_t uclogic_rdesc_ugee_v2_pen_template_size;
167+
168+
/* Fixed report descriptor template for UGEE v2 frame reports (buttons only) */
169+
extern const __u8 uclogic_rdesc_ugee_v2_frame_btn_template_arr[];
170+
extern const size_t uclogic_rdesc_ugee_v2_frame_btn_template_size;
171+
164172
/* Fixed report descriptor for Ugee EX07 frame */
165173
extern const __u8 uclogic_rdesc_ugee_ex07_frame_arr[];
166174
extern const size_t uclogic_rdesc_ugee_ex07_frame_size;

0 commit comments

Comments
 (0)