From 76e645be7ebecbf39ab2edd949ea7f1757f58900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Exp=C3=B3sito?= Date: Sat, 11 Jun 2022 13:39:11 +0200 Subject: HID: uclogic: Make template placeholder IDs generic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up until now, the report descriptor template parameter IDs were only used with pen report descriptors and they were named accordingly. Rename the enum and the total number of IDs to make them interface agnostic. Refactor, no functional changes. Signed-off-by: José Expósito Signed-off-by: Jiri Kosina --- drivers/hid/hid-uclogic-params.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hid/hid-uclogic-params.c') diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index db838f16282d..b43142f98a8b 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -234,7 +234,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 */ @@ -379,7 +379,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 */ -- cgit v1.2.3 From 0cb1fc0988e32bda84c2b7218e0c761af1430baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Exp=C3=B3sito?= Date: Sat, 11 Jun 2022 13:39:14 +0200 Subject: HID: uclogic: Add support for XP-PEN Deco L MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Jiri Kosina --- drivers/hid/hid-uclogic-params.c | 197 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) (limited to 'drivers/hid/hid-uclogic-params.c') diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index b43142f98a8b..f24a4aca7920 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -1002,6 +1002,197 @@ cleanup: return rc; } +/** + * 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. @@ -1237,6 +1428,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, -- cgit v1.2.3