summaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-uclogic-params.c
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2022-08-02 21:25:14 +0200
committerJiri Kosina <jkosina@suse.cz>2022-08-02 21:25:14 +0200
commita60885b6a97b5dc9340dd9310a57b5682c2daf2d (patch)
tree23a9206c7ea96483200fc9ddf883424615276b58 /drivers/hid/hid-uclogic-params.c
parentdb244339fa60b6cda6a2a32adb85be7e08de167e (diff)
parent0cb1fc0988e32bda84c2b7218e0c761af1430baf (diff)
downloadlinux-a60885b6a97b5dc9340dd9310a57b5682c2daf2d.tar.gz
linux-a60885b6a97b5dc9340dd9310a57b5682c2daf2d.tar.bz2
linux-a60885b6a97b5dc9340dd9310a57b5682c2daf2d.zip
Merge branch 'for-5.20/uclogic' into for-linus
- XP-PEN Deco L support (José Expósito)
Diffstat (limited to 'drivers/hid/hid-uclogic-params.c')
-rw-r--r--drivers/hid/hid-uclogic-params.c201
1 files changed, 199 insertions, 2 deletions
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c
index e5e65d849faa..c11fa239e6a2 100644
--- a/drivers/hid/hid-uclogic-params.c
+++ b/drivers/hid/hid-uclogic-params.c
@@ -238,7 +238,7 @@ static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,
const int len = 12;
s32 resolution;
/* Pen report descriptor template parameters */
- s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
+ s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
__u8 *desc_ptr = NULL;
/* Check arguments */
@@ -383,7 +383,7 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
size_t i;
s32 resolution;
/* Pen report descriptor template parameters */
- s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
+ s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
__u8 *desc_ptr = NULL;
/* Check arguments */
@@ -1007,6 +1007,197 @@ cleanup:
}
/**
+ * uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or
+ * the XP-PEN Deco Mini 7, need to be initialized by sending them magic data.
+ *
+ * @hdev: The HID device of the tablet interface to initialize and get
+ * parameters from. Cannot be NULL.
+ * @magic_arr: The magic data that should be sent to probe the interface.
+ * Cannot be NULL.
+ * @magic_size: Size of the magic data.
+ * @endpoint: Endpoint where the magic data should be sent.
+ *
+ * Returns:
+ * Zero, if successful. A negative errno code on error.
+ */
+static int uclogic_probe_interface(struct hid_device *hdev, u8 *magic_arr,
+ int magic_size, int endpoint)
+{
+ struct usb_device *udev;
+ unsigned int pipe = 0;
+ int sent;
+ u8 *buf = NULL;
+ int rc = 0;
+
+ if (!hdev || !magic_arr) {
+ rc = -EINVAL;
+ goto cleanup;
+ }
+
+ buf = kmemdup(magic_arr, magic_size, GFP_KERNEL);
+ if (!buf) {
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+
+ udev = hid_to_usb_dev(hdev);
+ pipe = usb_sndintpipe(udev, endpoint);
+
+ rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000);
+ if (rc || sent != magic_size) {
+ hid_err(hdev, "Interface probing failed: %d\n", rc);
+ rc = -1;
+ goto cleanup;
+ }
+
+ rc = 0;
+cleanup:
+ kfree(buf);
+ return rc;
+}
+
+/**
+ * uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by
+ * discovering their parameters.
+ *
+ * These tables, internally designed as v2 to differentiate them from older
+ * models, expect a payload of magic data in orther to be switched to the fully
+ * functional mode and expose their parameters in a similar way to the
+ * information present in uclogic_params_pen_init_v1() but with some
+ * differences.
+ *
+ * @params: Parameters to fill in (to be cleaned with
+ * uclogic_params_cleanup()). Not modified in case of error.
+ * Cannot be NULL.
+ * @hdev: The HID device of the tablet interface to initialize and get
+ * parameters from. Cannot be NULL.
+ *
+ * Returns:
+ * Zero, if successful. A negative errno code on error.
+ */
+static int uclogic_params_ugee_v2_init(struct uclogic_params *params,
+ struct hid_device *hdev)
+{
+ int rc = 0;
+ struct usb_interface *iface;
+ __u8 bInterfaceNumber;
+ const int str_desc_len = 12;
+ __u8 *str_desc = NULL;
+ __u8 *rdesc_pen = NULL;
+ __u8 *rdesc_frame = NULL;
+ s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
+ s32 resolution;
+ __u8 magic_arr[] = {
+ 0x02, 0xb0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ /* The resulting parameters (noop) */
+ struct uclogic_params p = {0, };
+
+ if (!params || !hdev) {
+ rc = -EINVAL;
+ goto cleanup;
+ }
+
+ iface = to_usb_interface(hdev->dev.parent);
+ bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
+ if (bInterfaceNumber != 2) {
+ uclogic_params_init_invalid(&p);
+ goto output;
+ }
+
+ /*
+ * Initialize the interface by sending magic data.
+ * The specific data was discovered by sniffing the Windows driver
+ * traffic.
+ */
+ rc = uclogic_probe_interface(hdev, magic_arr, sizeof(magic_arr), 0x03);
+ if (rc) {
+ uclogic_params_init_invalid(&p);
+ goto output;
+ }
+
+ /*
+ * Read the string descriptor containing pen and frame parameters.
+ * The specific string descriptor and data were discovered by sniffing
+ * the Windows driver traffic.
+ */
+ rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len);
+ if (rc != str_desc_len) {
+ hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc);
+ uclogic_params_init_invalid(&p);
+ goto output;
+ }
+
+ desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
+ get_unaligned_le16(str_desc + 2);
+ desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
+ get_unaligned_le16(str_desc + 4);
+ desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = str_desc[6];
+ desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
+ get_unaligned_le16(str_desc + 8);
+ resolution = get_unaligned_le16(str_desc + 10);
+ if (resolution == 0) {
+ desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
+ desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
+ } else {
+ desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
+ desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
+ resolution;
+ desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
+ desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
+ resolution;
+ }
+ 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 */
+ rdesc_frame = uclogic_rdesc_template_apply(
+ uclogic_rdesc_ugee_v2_frame_btn_template_arr,
+ uclogic_rdesc_ugee_v2_frame_btn_template_size,
+ desc_params, ARRAY_SIZE(desc_params));
+ if (!rdesc_frame) {
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+
+ rc = uclogic_params_frame_init_with_desc(&p.frame_list[0],
+ rdesc_frame,
+ uclogic_rdesc_ugee_v2_frame_btn_template_size,
+ UCLOGIC_RDESC_V1_FRAME_ID);
+ kfree(rdesc_frame);
+ if (rc) {
+ uclogic_params_init_invalid(&p);
+ goto output;
+ }
+
+output:
+ /* Output parameters */
+ memcpy(params, &p, sizeof(*params));
+ memset(&p, 0, sizeof(p));
+ rc = 0;
+cleanup:
+ kfree(str_desc);
+ uclogic_params_cleanup(&p);
+ return rc;
+}
+
+/**
* uclogic_params_init() - initialize a tablet interface and discover its
* parameters.
*
@@ -1241,6 +1432,12 @@ 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_DECO_L):
+ rc = uclogic_params_ugee_v2_init(&p, hdev);
+ if (rc != 0)
+ goto cleanup;
+ break;
case VID_PID(USB_VENDOR_ID_TRUST,
USB_DEVICE_ID_TRUST_PANORA_TABLET):
case VID_PID(USB_VENDOR_ID_UGEE,