Skip to content

Commit a62702f

Browse files
JoseExpositospbnick
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 <jose.exposito89@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
1 parent 49e249d commit a62702f

5 files changed

Lines changed: 310 additions & 0 deletions

File tree

hid-ids.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075
5858
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 0x0094
5959
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042
60+
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L 0x0935
6061
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 0x0078
6162
#define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074
6263
#define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071

hid-uclogic-core.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,8 @@ static const struct hid_device_id uclogic_devices[] = {
536536
USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640) },
537537
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
538538
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) },
539+
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
540+
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) },
539541
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
540542
USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) },
541543
{ }

hid-uclogic-params.c

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,197 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
10081008
return rc;
10091009
}
10101010

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

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), */

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)