Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

José Expósito and committed by
Jiri Kosina
0cb1fc09 867c8925

+310
+1
drivers/hid/hid-ids.h
··· 1278 1278 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075 1279 1279 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 0x0094 1280 1280 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042 1281 + #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L 0x0935 1281 1282 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 0x0078 1282 1283 #define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074 1283 1284 #define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071
+2
drivers/hid/hid-uclogic-core.c
··· 522 522 { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, 523 523 USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) }, 524 524 { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, 525 + USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) }, 526 + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, 525 527 USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) }, 526 528 { } 527 529 };
+197
drivers/hid/hid-uclogic-params.c
··· 1003 1003 } 1004 1004 1005 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 + 1196 + /** 1006 1197 * uclogic_params_init() - initialize a tablet interface and discover its 1007 1198 * parameters. 1008 1199 * ··· 1427 1236 } else { 1428 1237 uclogic_params_init_invalid(&p); 1429 1238 } 1239 + break; 1240 + case VID_PID(USB_VENDOR_ID_UGEE, 1241 + USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L): 1242 + rc = uclogic_params_ugee_v2_init(&p, hdev); 1243 + if (rc != 0) 1244 + goto cleanup; 1430 1245 break; 1431 1246 case VID_PID(USB_VENDOR_ID_TRUST, 1432 1247 USB_DEVICE_ID_TRUST_PANORA_TABLET):
+102
drivers/hid/hid-uclogic-rdesc.c
··· 859 859 const size_t uclogic_rdesc_v2_frame_dial_size = 860 860 sizeof(uclogic_rdesc_v2_frame_dial_arr); 861 861 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 + 862 964 /* Fixed report descriptor for Ugee EX07 frame */ 863 965 const __u8 uclogic_rdesc_ugee_ex07_frame_arr[] = { 864 966 0x05, 0x01, /* Usage Page (Desktop), */
+8
drivers/hid/hid-uclogic-rdesc.h
··· 161 161 /* Device ID byte offset in v2 frame dial reports */ 162 162 #define UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE 0x4 163 163 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 + 164 172 /* Fixed report descriptor for Ugee EX07 frame */ 165 173 extern const __u8 uclogic_rdesc_ugee_ex07_frame_arr[]; 166 174 extern const size_t uclogic_rdesc_ugee_ex07_frame_size;