From 5e55e2aa802d429800b793080a48f310eaf82138 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:47 +0200 Subject: [PATCH 01/23] HID: kye: Add support for EasyPen M406XE Originally contributed by Andrey Alekseenko . Signed-off-by: Andrey Alekseenko Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-kye.c | 83 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-quirks.c | 1 + 3 files changed, 85 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 24f846d67478..2bef2fd07360 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -661,6 +661,7 @@ #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2 0x501a #define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013 #define USB_DEVICE_ID_KYE_PENSKETCH_M912 0x5015 +#define USB_DEVICE_ID_KYE_EASYPEN_M406XE 0x5019 #define USB_VENDOR_ID_LABTEC 0x1020 #define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006 diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index 9c113f62472d..679d422b885a 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -483,6 +483,80 @@ static __u8 pensketch_m912_rdesc_fixed[] = { 0xC0 /* End Collection */ }; +/* Original EasyPen M406XE report descriptor size */ +#define EASYPEN_M406XE_RDESC_ORIG_SIZE 476 + +/* Fixed EasyPen M406XE report descriptor */ +static __u8 easypen_m406xe_rdesc_fixed[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x01, /* Usage (01h), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x05, /* Report ID (5), */ + 0x09, 0x01, /* Usage (01h), */ + 0x15, 0x80, /* Logical Minimum (-128), */ + 0x25, 0x7F, /* Logical Maximum (127), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x07, /* Report Count (7), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0, /* End Collection, */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x10, /* Report ID (16), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x04, /* Report Count (4), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x34, /* Physical Minimum (0), */ + 0x09, 0x30, /* Usage (X), */ + 0x46, 0x70, 0x17, /* Physical Maximum (6000), */ + 0x26, 0x00, 0x3C, /* Logical Maximum (15360), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0xA0, 0x0F, /* Physical Maximum (4000), */ + 0x26, 0x00, 0x28, /* Logical Maximum (10240), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0, /* End Collection */ + 0x05, 0x0C, /* Usage Page (Consumer), */ + 0x09, 0x01, /* Usage (Consumer Control), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x12, /* Report ID (18), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x04, /* Report Count (4), */ + 0x0A, 0x79, 0x02, /* Usage (AC Redo Or Repeat), */ + 0x0A, 0x1A, 0x02, /* Usage (AC Undo), */ + 0x0A, 0x2D, 0x02, /* Usage (AC Zoom In), */ + 0x0A, 0x2E, 0x02, /* Usage (AC Zoom Out), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x34, /* Report Count (52), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0xC0 /* End Collection */ +}; + static __u8 *kye_consumer_control_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize, int offset, const char *device_name) { /* @@ -555,6 +629,12 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, *rsize = sizeof(easypen_m610x_rdesc_fixed); } break; + case USB_DEVICE_ID_KYE_EASYPEN_M406XE: + if (*rsize == EASYPEN_M406XE_RDESC_ORIG_SIZE) { + rdesc = easypen_m406xe_rdesc_fixed; + *rsize = sizeof(easypen_m406xe_rdesc_fixed); + } + break; case USB_DEVICE_ID_KYE_PENSKETCH_M912: if (*rsize == PENSKETCH_M912_RDESC_ORIG_SIZE) { rdesc = pensketch_m912_rdesc_fixed; @@ -644,6 +724,7 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id) case USB_DEVICE_ID_KYE_MOUSEPEN_I608X: case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2: case USB_DEVICE_ID_KYE_EASYPEN_M610X: + case USB_DEVICE_ID_KYE_EASYPEN_M406XE: case USB_DEVICE_ID_KYE_PENSKETCH_M912: ret = kye_tablet_enable(hdev); if (ret) { @@ -678,6 +759,8 @@ static const struct hid_device_id kye_devices[] = { USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, + USB_DEVICE_ID_KYE_EASYPEN_M406XE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 94088c0ed68a..0f4a777b17a8 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -99,6 +99,7 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912), HID_QUIRK_MULTI_INPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M406XE), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C007), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077), HID_QUIRK_ALWAYS_POLL }, From 7c2af0a16e1056e2c208c5a5295f53a0c96f4aca Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:48 +0200 Subject: [PATCH 02/23] HID: viewsonic: Support PD1011 signature pad Add support for ViewSonic PD1011 signature (display) pad, which is also sold by Signotec under a different name. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/Kconfig | 6 +++ drivers/hid/Makefile | 1 + drivers/hid/hid-ids.h | 6 +++ drivers/hid/hid-viewsonic.c | 105 ++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 drivers/hid/hid-viewsonic.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 41e9935fc584..4e4cacf85cbd 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -412,6 +412,12 @@ config HID_WALTOP ---help--- Support for Waltop tablets. +config HID_VIEWSONIC + tristate "ViewSonic/Signotec" + depends on HID + help + Support for ViewSonic/Signotec PD1011 signature pad. + config HID_GYRATION tristate "Gyration remote control" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 896a51ce7ce0..a57e1088133a 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -114,6 +114,7 @@ obj-$(CONFIG_HID_LED) += hid-led.o obj-$(CONFIG_HID_XINMO) += hid-xinmo.o obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o +obj-$(CONFIG_HID_VIEWSONIC) += hid-viewsonic.o wacom-objs := wacom_wac.o wacom_sys.o obj-$(CONFIG_HID_WACOM) += wacom.o diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 2bef2fd07360..e23ec8fb2213 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1241,4 +1241,10 @@ #define USB_VENDOR_ID_UGTIZER 0x2179 #define USB_DEVICE_ID_UGTIZER_TABLET_GP0610 0x0053 +#define USB_VENDOR_ID_VIEWSONIC 0x0543 +#define USB_DEVICE_ID_VIEWSONIC_PD1011 0xe621 + +#define USB_VENDOR_ID_SIGNOTEC 0x2133 +#define USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011 0x0018 + #endif diff --git a/drivers/hid/hid-viewsonic.c b/drivers/hid/hid-viewsonic.c new file mode 100644 index 000000000000..df60c8fc2efd --- /dev/null +++ b/drivers/hid/hid-viewsonic.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HID driver for ViewSonic devices not fully compliant with HID standard + * + * Copyright (c) 2017 Nikolai Kondrashov + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include + +#include "hid-ids.h" + +/* Size of the original descriptor of PD1011 signature pad */ +#define PD1011_RDESC_ORIG_SIZE 408 + +/* Fixed report descriptor of PD1011 signature pad */ +static __u8 pd1011_rdesc_fixed[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x02, /* Report ID (2), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x34, /* Physical Minimum (0), */ + 0x09, 0x30, /* Usage (X), */ + 0x46, 0x5D, 0x21, /* Physical Maximum (8541), */ + 0x27, 0x80, 0xA9, + 0x00, 0x00, /* Logical Maximum (43392), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0xDA, 0x14, /* Physical Maximum (5338), */ + 0x26, 0xF0, 0x69, /* Logical Maximum (27120), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x05, /* Report Count (5), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x15, 0x05, /* Logical Minimum (5), */ + 0x26, 0xFF, 0x07, /* Logical Maximum (2047), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +static __u8 *viewsonic_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + switch (hdev->product) { + case USB_DEVICE_ID_VIEWSONIC_PD1011: + case USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011: + if (*rsize == PD1011_RDESC_ORIG_SIZE) { + rdesc = pd1011_rdesc_fixed; + *rsize = sizeof(pd1011_rdesc_fixed); + } + break; + } + + return rdesc; +} + +static const struct hid_device_id viewsonic_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_VIEWSONIC, + USB_DEVICE_ID_VIEWSONIC_PD1011) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SIGNOTEC, + USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011) }, + { } +}; +MODULE_DEVICE_TABLE(hid, viewsonic_devices); + +static struct hid_driver viewsonic_driver = { + .name = "viewsonic", + .id_table = viewsonic_devices, + .report_fixup = viewsonic_report_fixup, +}; +module_hid_driver(viewsonic_driver); + +MODULE_LICENSE("GPL"); From bf226cd3316a01d71eb74ea5736f6c8905ff5041 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:49 +0200 Subject: [PATCH 03/23] HID: Clarify vendor ID reuse by Ugee tablets Add "_UCLOGIC" to Ugee tablet device ID macros so it's clear they come with UC-Logic vendor ID. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-ids.h | 4 ++-- drivers/hid/hid-quirks.c | 4 ++-- drivers/hid/hid-uclogic.c | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e23ec8fb2213..f66553157be5 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1135,8 +1135,8 @@ #define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522 #define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781 #define USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3 0x3031 -#define USB_DEVICE_ID_UGEE_TABLET_81 0x0081 -#define USB_DEVICE_ID_UGEE_TABLET_45 0x0045 +#define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81 0x0081 +#define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45 0x0045 #define USB_DEVICE_ID_YIYNOVA_TABLET 0x004d #define USB_VENDOR_ID_UGEE 0x28bd diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 0f4a777b17a8..78c836e5ce51 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -685,8 +685,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c index 56b196d60041..fdc4d1ea7049 100644 --- a/drivers/hid/hid-uclogic.c +++ b/drivers/hid/hid-uclogic.c @@ -957,9 +957,9 @@ static int uclogic_probe(struct hid_device *hdev, switch (id->product) { case USB_DEVICE_ID_HUION_TABLET: case USB_DEVICE_ID_YIYNOVA_TABLET: - case USB_DEVICE_ID_UGEE_TABLET_81: + case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81: case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3: - case USB_DEVICE_ID_UGEE_TABLET_45: + case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45: /* If this is the pen interface */ if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { rc = uclogic_tablet_enable(hdev); @@ -1065,8 +1065,8 @@ static const struct hid_device_id uclogic_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, From ff0c13d6d2edc9c4952c668f4503a51b5f101ab3 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:50 +0200 Subject: [PATCH 04/23] HID: uclogic: Extract report descriptors to a module As hid-uclogic has a lot of report descriptors already and there's going to be more, move them out of the driver code and into a separate module. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/Makefile | 2 + drivers/hid/hid-uclogic-core.c | 493 +++++++++++++ .../{hid-uclogic.c => hid-uclogic-rdesc.c} | 677 ++++-------------- drivers/hid/hid-uclogic-rdesc.h | 117 +++ 4 files changed, 747 insertions(+), 542 deletions(-) create mode 100644 drivers/hid/hid-uclogic-core.c rename drivers/hid/{hid-uclogic.c => hid-uclogic-rdesc.c} (66%) create mode 100644 drivers/hid/hid-uclogic-rdesc.h diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index a57e1088133a..fb75366ea776 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -108,6 +108,8 @@ obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o obj-$(CONFIG_HID_TIVO) += hid-tivo.o obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o +hid-uclogic-objs := hid-uclogic-core.o \ + hid-uclogic-rdesc.o obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o obj-$(CONFIG_HID_LED) += hid-led.o diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c new file mode 100644 index 000000000000..4042183ee9a3 --- /dev/null +++ b/drivers/hid/hid-uclogic-core.c @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HID driver for UC-Logic devices not fully compliant with HID standard + * + * Copyright (c) 2010-2014 Nikolai Kondrashov + * Copyright (c) 2013 Martin Rusko + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include "usbhid/usbhid.h" +#include "hid-uclogic-rdesc.h" + +#include "hid-ids.h" + +/* Parameter indices */ +enum uclogic_prm { + UCLOGIC_PRM_X_LM = 1, + UCLOGIC_PRM_Y_LM = 2, + UCLOGIC_PRM_PRESSURE_LM = 4, + UCLOGIC_PRM_RESOLUTION = 5, + UCLOGIC_PRM_NUM +}; + +/* Driver data */ +struct uclogic_drvdata { + __u8 *rdesc; + unsigned int rsize; + bool invert_pen_inrange; + bool ignore_pen_usage; + bool has_virtual_pad_interface; +}; + +static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + struct usb_interface *iface = to_usb_interface(hdev->dev.parent); + __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + + if (drvdata->rdesc != NULL) { + rdesc = drvdata->rdesc; + *rsize = drvdata->rsize; + return rdesc; + } + + switch (hdev->product) { + case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: + if (*rsize == UCLOGIC_RDESC_PF1209_ORIG_SIZE) { + rdesc = uclogic_rdesc_pf1209_fixed_arr; + *rsize = uclogic_rdesc_pf1209_fixed_size; + } + break; + case USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U: + if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) { + rdesc = uclogic_rdesc_wp4030u_fixed_arr; + *rsize = uclogic_rdesc_wp4030u_fixed_size; + } + break; + case USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U: + if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) { + rdesc = uclogic_rdesc_wp5540u_fixed_arr; + *rsize = uclogic_rdesc_wp5540u_fixed_size; + } + break; + case USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U: + if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) { + rdesc = uclogic_rdesc_wp8060u_fixed_arr; + *rsize = uclogic_rdesc_wp8060u_fixed_size; + } + break; + case USB_DEVICE_ID_UCLOGIC_TABLET_WP1062: + if (*rsize == UCLOGIC_RDESC_WP1062_ORIG_SIZE) { + rdesc = uclogic_rdesc_wp1062_fixed_arr; + *rsize = uclogic_rdesc_wp1062_fixed_size; + } + break; + case USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850: + switch (iface_num) { + case 0: + if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG0_SIZE) { + rdesc = uclogic_rdesc_twhl850_fixed0_arr; + *rsize = uclogic_rdesc_twhl850_fixed0_size; + } + break; + case 1: + if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG1_SIZE) { + rdesc = uclogic_rdesc_twhl850_fixed1_arr; + *rsize = uclogic_rdesc_twhl850_fixed1_size; + } + break; + case 2: + if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG2_SIZE) { + rdesc = uclogic_rdesc_twhl850_fixed2_arr; + *rsize = uclogic_rdesc_twhl850_fixed2_size; + } + break; + } + break; + case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: + switch (iface_num) { + case 0: + if (*rsize == UCLOGIC_RDESC_TWHA60_ORIG0_SIZE) { + rdesc = uclogic_rdesc_twha60_fixed0_arr; + *rsize = uclogic_rdesc_twha60_fixed0_size; + } + break; + case 1: + if (*rsize == UCLOGIC_RDESC_TWHA60_ORIG1_SIZE) { + rdesc = uclogic_rdesc_twha60_fixed1_arr; + *rsize = uclogic_rdesc_twha60_fixed1_size; + } + break; + } + break; + } + + return rdesc; +} + +static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + + /* discard the unused pen interface */ + if ((drvdata->ignore_pen_usage) && + (field->application == HID_DG_PEN)) + return -1; + + /* let hid-core decide what to do */ + return 0; +} + +static int uclogic_input_configured(struct hid_device *hdev, + struct hid_input *hi) +{ + char *name; + const char *suffix = NULL; + struct hid_field *field; + size_t len; + + /* no report associated (HID_QUIRK_MULTI_INPUT not set) */ + if (!hi->report) + return 0; + + field = hi->report->field[0]; + + switch (field->application) { + case HID_GD_KEYBOARD: + suffix = "Keyboard"; + break; + case HID_GD_MOUSE: + suffix = "Mouse"; + break; + case HID_GD_KEYPAD: + suffix = "Pad"; + break; + case HID_DG_PEN: + suffix = "Pen"; + break; + case HID_CP_CONSUMER_CONTROL: + suffix = "Consumer Control"; + break; + case HID_GD_SYSTEM_CONTROL: + suffix = "System Control"; + break; + } + + if (suffix) { + len = strlen(hdev->name) + 2 + strlen(suffix); + name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL); + if (name) { + snprintf(name, len, "%s %s", hdev->name, suffix); + hi->input->name = name; + } + } + + return 0; +} + +/** + * Enable fully-functional tablet mode and determine device parameters. + * + * @hdev: HID device + */ +static int uclogic_tablet_enable(struct hid_device *hdev) +{ + int rc; + struct usb_device *usb_dev = hid_to_usb_dev(hdev); + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + __le16 *buf = NULL; + size_t len; + s32 params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; + s32 resolution; + + /* + * Read string descriptor containing tablet parameters. The specific + * string descriptor and data were discovered by sniffing the Windows + * driver traffic. + * NOTE: This enables fully-functional tablet mode. + */ + len = UCLOGIC_PRM_NUM * sizeof(*buf); + buf = kmalloc(len, GFP_KERNEL); + if (buf == NULL) { + rc = -ENOMEM; + goto cleanup; + } + rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (USB_DT_STRING << 8) + 0x64, + 0x0409, buf, len, + USB_CTRL_GET_TIMEOUT); + if (rc == -EPIPE) { + hid_err(hdev, "device parameters not found\n"); + rc = -ENODEV; + goto cleanup; + } else if (rc < 0) { + hid_err(hdev, "failed to get device parameters: %d\n", rc); + rc = -ENODEV; + goto cleanup; + } else if (rc != len) { + hid_err(hdev, "invalid device parameters\n"); + rc = -ENODEV; + goto cleanup; + } + + /* Extract device parameters */ + params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = + le16_to_cpu(buf[UCLOGIC_PRM_X_LM]); + params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = + le16_to_cpu(buf[UCLOGIC_PRM_Y_LM]); + params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = + le16_to_cpu(buf[UCLOGIC_PRM_PRESSURE_LM]); + resolution = le16_to_cpu(buf[UCLOGIC_PRM_RESOLUTION]); + if (resolution == 0) { + params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; + params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; + } else { + params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = + params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * + 1000 / resolution; + params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = + params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * + 1000 / resolution; + } + + /* Format fixed report descriptor */ + drvdata->rdesc = uclogic_rdesc_template_apply( + uclogic_rdesc_pen_template_arr, + uclogic_rdesc_pen_template_size, + params, ARRAY_SIZE(params)); + if (drvdata->rdesc == NULL) { + rc = -ENOMEM; + goto cleanup; + } + drvdata->rsize = uclogic_rdesc_pen_template_size; + + rc = 0; + +cleanup: + kfree(buf); + return rc; +} + +/** + * Enable actual button mode. + * + * @hdev: HID device + */ +static int uclogic_button_enable(struct hid_device *hdev) +{ + int rc; + struct usb_device *usb_dev = hid_to_usb_dev(hdev); + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + char *str_buf; + size_t str_len = 16; + unsigned char *rdesc; + size_t rdesc_len; + + str_buf = kzalloc(str_len, GFP_KERNEL); + if (str_buf == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + /* Enable abstract keyboard mode */ + rc = usb_string(usb_dev, 0x7b, str_buf, str_len); + if (rc == -EPIPE) { + hid_info(hdev, "button mode setting not found\n"); + rc = 0; + goto cleanup; + } else if (rc < 0) { + hid_err(hdev, "failed to enable abstract keyboard\n"); + goto cleanup; + } else if (strncmp(str_buf, "HK On", rc)) { + hid_info(hdev, "invalid answer when requesting buttons: '%s'\n", + str_buf); + rc = -EINVAL; + goto cleanup; + } + + /* Re-allocate fixed report descriptor */ + rdesc_len = drvdata->rsize + uclogic_rdesc_buttonpad_size; + rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL); + if (!rdesc) { + rc = -ENOMEM; + goto cleanup; + } + + memcpy(rdesc, drvdata->rdesc, drvdata->rsize); + + /* Append the buttonpad descriptor */ + memcpy(rdesc + drvdata->rsize, uclogic_rdesc_buttonpad_arr, + uclogic_rdesc_buttonpad_size); + + /* clean up old rdesc and use the new one */ + drvdata->rsize = rdesc_len; + devm_kfree(&hdev->dev, drvdata->rdesc); + drvdata->rdesc = rdesc; + + rc = 0; + +cleanup: + kfree(str_buf); + return rc; +} + +static int uclogic_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int rc; + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *udev = hid_to_usb_dev(hdev); + struct uclogic_drvdata *drvdata; + + /* + * libinput requires the pad interface to be on a different node + * than the pen, so use QUIRK_MULTI_INPUT for all tablets. + */ + hdev->quirks |= HID_QUIRK_MULTI_INPUT; + + /* Allocate and assign driver data */ + drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (drvdata == NULL) + return -ENOMEM; + + hid_set_drvdata(hdev, drvdata); + + switch (id->product) { + case USB_DEVICE_ID_HUION_TABLET: + case USB_DEVICE_ID_YIYNOVA_TABLET: + case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81: + case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3: + case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45: + /* If this is the pen interface */ + if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + rc = uclogic_tablet_enable(hdev); + if (rc) { + hid_err(hdev, "tablet enabling failed\n"); + return rc; + } + drvdata->invert_pen_inrange = true; + + rc = uclogic_button_enable(hdev); + drvdata->has_virtual_pad_interface = !rc; + } else { + drvdata->ignore_pen_usage = true; + } + break; + case USB_DEVICE_ID_UGTIZER_TABLET_GP0610: + case USB_DEVICE_ID_UGEE_TABLET_EX07S: + /* If this is the pen interface */ + if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { + rc = uclogic_tablet_enable(hdev); + if (rc) { + hid_err(hdev, "tablet enabling failed\n"); + return rc; + } + drvdata->invert_pen_inrange = true; + } else { + drvdata->ignore_pen_usage = true; + } + break; + case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: + /* + * If it is the three-interface version, which is known to + * respond to initialization. + */ + if (udev->config->desc.bNumInterfaces == 3) { + /* If it is the pen interface */ + if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + rc = uclogic_tablet_enable(hdev); + if (rc) { + hid_err(hdev, "tablet enabling failed\n"); + return rc; + } + drvdata->invert_pen_inrange = true; + + rc = uclogic_button_enable(hdev); + drvdata->has_virtual_pad_interface = !rc; + } else { + drvdata->ignore_pen_usage = true; + } + } + break; + } + + rc = hid_parse(hdev); + if (rc) { + hid_err(hdev, "parse failed\n"); + return rc; + } + + rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (rc) { + hid_err(hdev, "hw start failed\n"); + return rc; + } + + return 0; +} + +static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + + if ((report->type == HID_INPUT_REPORT) && + (report->id == UCLOGIC_RDESC_PEN_ID) && + (size >= 2)) { + if (drvdata->has_virtual_pad_interface && (data[1] & 0x20)) + /* Change to virtual frame button report ID */ + data[0] = 0xf7; + else if (drvdata->invert_pen_inrange) + /* Invert the in-range bit */ + data[1] ^= 0x40; + } + + return 0; +} + +static const struct hid_device_id uclogic_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, + { } +}; +MODULE_DEVICE_TABLE(hid, uclogic_devices); + +static struct hid_driver uclogic_driver = { + .name = "uclogic", + .id_table = uclogic_devices, + .probe = uclogic_probe, + .report_fixup = uclogic_report_fixup, + .raw_event = uclogic_raw_event, + .input_mapping = uclogic_input_mapping, + .input_configured = uclogic_input_configured, +}; +module_hid_driver(uclogic_driver); + +MODULE_AUTHOR("Martin Rusko"); +MODULE_AUTHOR("Nikolai Kondrashov"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic-rdesc.c similarity index 66% rename from drivers/hid/hid-uclogic.c rename to drivers/hid/hid-uclogic-rdesc.c index fdc4d1ea7049..e9053d28f4e0 100644 --- a/drivers/hid/hid-uclogic.c +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -1,7 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * HID driver for UC-Logic devices not fully compliant with HID standard + * - original and fixed report descriptors * - * Copyright (c) 2010-2014 Nikolai Kondrashov + * Copyright (c) 2010-2017 Nikolai Kondrashov * Copyright (c) 2013 Martin Rusko */ @@ -12,20 +14,12 @@ * any later version. */ -#include -#include -#include -#include +#include "hid-uclogic-rdesc.h" +#include #include -#include "usbhid/usbhid.h" - -#include "hid-ids.h" - -/* Size of the original descriptor of WPXXXXU tablets */ -#define WPXXXXU_RDESC_ORIG_SIZE 212 /* Fixed WP4030U report descriptor */ -static __u8 wp4030u_rdesc_fixed[] = { +__u8 uclogic_rdesc_wp4030u_fixed_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -66,8 +60,11 @@ static __u8 wp4030u_rdesc_fixed[] = { 0xC0 /* End Collection */ }; +const size_t uclogic_rdesc_wp4030u_fixed_size = + sizeof(uclogic_rdesc_wp4030u_fixed_arr); + /* Fixed WP5540U report descriptor */ -static __u8 wp5540u_rdesc_fixed[] = { +__u8 uclogic_rdesc_wp5540u_fixed_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -140,8 +137,11 @@ static __u8 wp5540u_rdesc_fixed[] = { 0xC0 /* End Collection */ }; +const size_t uclogic_rdesc_wp5540u_fixed_size = + sizeof(uclogic_rdesc_wp5540u_fixed_arr); + /* Fixed WP8060U report descriptor */ -static __u8 wp8060u_rdesc_fixed[] = { +__u8 uclogic_rdesc_wp8060u_fixed_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -214,11 +214,11 @@ static __u8 wp8060u_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* Size of the original descriptor of WP1062 tablet */ -#define WP1062_RDESC_ORIG_SIZE 254 +const size_t uclogic_rdesc_wp8060u_fixed_size = + sizeof(uclogic_rdesc_wp8060u_fixed_arr); /* Fixed WP1062 report descriptor */ -static __u8 wp1062_rdesc_fixed[] = { +__u8 uclogic_rdesc_wp1062_fixed_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -262,11 +262,11 @@ static __u8 wp1062_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* Size of the original descriptor of PF1209 tablet */ -#define PF1209_RDESC_ORIG_SIZE 234 +const size_t uclogic_rdesc_wp1062_fixed_size = + sizeof(uclogic_rdesc_wp1062_fixed_arr); /* Fixed PF1209 report descriptor */ -static __u8 pf1209_rdesc_fixed[] = { +__u8 uclogic_rdesc_pf1209_fixed_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -339,13 +339,11 @@ static __u8 pf1209_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* Size of the original descriptors of TWHL850 tablet */ -#define TWHL850_RDESC_ORIG_SIZE0 182 -#define TWHL850_RDESC_ORIG_SIZE1 161 -#define TWHL850_RDESC_ORIG_SIZE2 92 +const size_t uclogic_rdesc_pf1209_fixed_size = + sizeof(uclogic_rdesc_pf1209_fixed_arr); /* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */ -static __u8 twhl850_rdesc_fixed0[] = { +__u8 uclogic_rdesc_twhl850_fixed0_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -387,8 +385,11 @@ static __u8 twhl850_rdesc_fixed0[] = { 0xC0 /* End Collection */ }; +const size_t uclogic_rdesc_twhl850_fixed0_size = + sizeof(uclogic_rdesc_twhl850_fixed0_arr); + /* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */ -static __u8 twhl850_rdesc_fixed1[] = { +__u8 uclogic_rdesc_twhl850_fixed1_arr[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x02, /* Usage (Mouse), */ 0xA1, 0x01, /* Collection (Application), */ @@ -424,8 +425,11 @@ static __u8 twhl850_rdesc_fixed1[] = { 0xC0 /* End Collection */ }; +const size_t uclogic_rdesc_twhl850_fixed1_size = + sizeof(uclogic_rdesc_twhl850_fixed1_arr); + /* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */ -static __u8 twhl850_rdesc_fixed2[] = { +__u8 uclogic_rdesc_twhl850_fixed2_arr[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x06, /* Usage (Keyboard), */ 0xA1, 0x01, /* Collection (Application), */ @@ -447,12 +451,11 @@ static __u8 twhl850_rdesc_fixed2[] = { 0xC0 /* End Collection */ }; -/* Size of the original descriptors of TWHA60 tablet */ -#define TWHA60_RDESC_ORIG_SIZE0 254 -#define TWHA60_RDESC_ORIG_SIZE1 139 +const size_t uclogic_rdesc_twhl850_fixed2_size = + sizeof(uclogic_rdesc_twhl850_fixed2_arr); /* Fixed TWHA60 report descriptor, interface 0 (stylus) */ -static __u8 twha60_rdesc_fixed0[] = { +__u8 uclogic_rdesc_twha60_fixed0_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -497,8 +500,11 @@ static __u8 twha60_rdesc_fixed0[] = { 0xC0 /* End Collection */ }; +const size_t uclogic_rdesc_twha60_fixed0_size = + sizeof(uclogic_rdesc_twha60_fixed0_arr); + /* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */ -static __u8 twha60_rdesc_fixed1[] = { +__u8 uclogic_rdesc_twha60_fixed1_arr[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x06, /* Usage (Keyboard), */ 0xA1, 0x01, /* Collection (Application), */ @@ -522,25 +528,11 @@ static __u8 twha60_rdesc_fixed1[] = { 0xC0 /* End Collection */ }; -/* Report descriptor template placeholder head */ -#define UCLOGIC_PH_HEAD 0xFE, 0xED, 0x1D +const size_t uclogic_rdesc_twha60_fixed1_size = + sizeof(uclogic_rdesc_twha60_fixed1_arr); -/* Report descriptor template placeholder IDs */ -enum uclogic_ph_id { - UCLOGIC_PH_ID_X_LM, - UCLOGIC_PH_ID_X_PM, - UCLOGIC_PH_ID_Y_LM, - UCLOGIC_PH_ID_Y_PM, - UCLOGIC_PH_ID_PRESSURE_LM, - UCLOGIC_PH_ID_NUM -}; - -/* Report descriptor template placeholder */ -#define UCLOGIC_PH(_ID) UCLOGIC_PH_HEAD, UCLOGIC_PH_ID_##_ID -#define UCLOGIC_PEN_REPORT_ID 0x07 - -/* Fixed report descriptor template */ -static const __u8 uclogic_tablet_rdesc_template[] = { +/* Fixed report descriptor template for (tweaked) pen reports */ +const __u8 uclogic_rdesc_pen_template_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -570,521 +562,122 @@ static const __u8 uclogic_tablet_rdesc_template[] = { 0x55, 0xFD, /* Unit Exponent (-3), */ 0x34, /* Physical Minimum (0), */ 0x09, 0x30, /* Usage (X), */ - 0x27, UCLOGIC_PH(X_LM), /* Logical Maximum (PLACEHOLDER), */ - 0x47, UCLOGIC_PH(X_PM), /* Physical Maximum (PLACEHOLDER), */ + 0x27, UCLOGIC_RDESC_PEN_PH(X_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x47, UCLOGIC_RDESC_PEN_PH(X_PM), + /* Physical Maximum (PLACEHOLDER), */ 0x81, 0x02, /* Input (Variable), */ 0x09, 0x31, /* Usage (Y), */ - 0x27, UCLOGIC_PH(Y_LM), /* Logical Maximum (PLACEHOLDER), */ - 0x47, UCLOGIC_PH(Y_PM), /* Physical Maximum (PLACEHOLDER), */ + 0x27, UCLOGIC_RDESC_PEN_PH(Y_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x47, UCLOGIC_RDESC_PEN_PH(Y_PM), + /* Physical Maximum (PLACEHOLDER), */ 0x81, 0x02, /* Input (Variable), */ 0xB4, /* Pop, */ 0x09, 0x30, /* Usage (Tip Pressure), */ - 0x27, - UCLOGIC_PH(PRESSURE_LM),/* Logical Maximum (PLACEHOLDER), */ + 0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM), + /* Logical Maximum (PLACEHOLDER), */ 0x81, 0x02, /* Input (Variable), */ 0xC0, /* End Collection, */ 0xC0 /* End Collection */ }; -/* Fixed virtual pad report descriptor */ -static const __u8 uclogic_buttonpad_rdesc[] = { - 0x05, 0x01, /* Usage Page (Desktop), */ - 0x09, 0x07, /* Usage (Keypad), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0xF7, /* Report ID (247), */ - 0x05, 0x0D, /* Usage Page (Digitizers), */ - 0x09, 0x39, /* Usage (Tablet Function Keys), */ - 0xA0, /* Collection (Physical), */ - 0x05, 0x09, /* Usage Page (Button), */ - 0x75, 0x01, /* Report Size (1), */ - 0x95, 0x18, /* Report Count (24), */ - 0x81, 0x03, /* Input (Constant, Variable), */ - 0x19, 0x01, /* Usage Minimum (01h), */ - 0x29, 0x08, /* Usage Maximum (08h), */ - 0x95, 0x08, /* Report Count (8), */ - 0x81, 0x02, /* Input (Variable), */ - 0xC0, /* End Collection */ - 0xC0 /* End Collection */ -}; - -/* Parameter indices */ -enum uclogic_prm { - UCLOGIC_PRM_X_LM = 1, - UCLOGIC_PRM_Y_LM = 2, - UCLOGIC_PRM_PRESSURE_LM = 4, - UCLOGIC_PRM_RESOLUTION = 5, - UCLOGIC_PRM_NUM -}; - -/* Driver data */ -struct uclogic_drvdata { - __u8 *rdesc; - unsigned int rsize; - bool invert_pen_inrange; - bool ignore_pen_usage; - bool has_virtual_pad_interface; -}; - -static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) -{ - struct usb_interface *iface = to_usb_interface(hdev->dev.parent); - __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; - struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); - - if (drvdata->rdesc != NULL) { - rdesc = drvdata->rdesc; - *rsize = drvdata->rsize; - return rdesc; - } - - switch (hdev->product) { - case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: - if (*rsize == PF1209_RDESC_ORIG_SIZE) { - rdesc = pf1209_rdesc_fixed; - *rsize = sizeof(pf1209_rdesc_fixed); - } - break; - case USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U: - if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) { - rdesc = wp4030u_rdesc_fixed; - *rsize = sizeof(wp4030u_rdesc_fixed); - } - break; - case USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U: - if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) { - rdesc = wp5540u_rdesc_fixed; - *rsize = sizeof(wp5540u_rdesc_fixed); - } - break; - case USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U: - if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) { - rdesc = wp8060u_rdesc_fixed; - *rsize = sizeof(wp8060u_rdesc_fixed); - } - break; - case USB_DEVICE_ID_UCLOGIC_TABLET_WP1062: - if (*rsize == WP1062_RDESC_ORIG_SIZE) { - rdesc = wp1062_rdesc_fixed; - *rsize = sizeof(wp1062_rdesc_fixed); - } - break; - case USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850: - switch (iface_num) { - case 0: - if (*rsize == TWHL850_RDESC_ORIG_SIZE0) { - rdesc = twhl850_rdesc_fixed0; - *rsize = sizeof(twhl850_rdesc_fixed0); - } - break; - case 1: - if (*rsize == TWHL850_RDESC_ORIG_SIZE1) { - rdesc = twhl850_rdesc_fixed1; - *rsize = sizeof(twhl850_rdesc_fixed1); - } - break; - case 2: - if (*rsize == TWHL850_RDESC_ORIG_SIZE2) { - rdesc = twhl850_rdesc_fixed2; - *rsize = sizeof(twhl850_rdesc_fixed2); - } - break; - } - break; - case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: - switch (iface_num) { - case 0: - if (*rsize == TWHA60_RDESC_ORIG_SIZE0) { - rdesc = twha60_rdesc_fixed0; - *rsize = sizeof(twha60_rdesc_fixed0); - } - break; - case 1: - if (*rsize == TWHA60_RDESC_ORIG_SIZE1) { - rdesc = twha60_rdesc_fixed1; - *rsize = sizeof(twha60_rdesc_fixed1); - } - break; - } - break; - } - - return rdesc; -} - -static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); - - /* discard the unused pen interface */ - if ((drvdata->ignore_pen_usage) && - (field->application == HID_DG_PEN)) - return -1; - - /* let hid-core decide what to do */ - return 0; -} - -static int uclogic_input_configured(struct hid_device *hdev, - struct hid_input *hi) -{ - char *name; - const char *suffix = NULL; - struct hid_field *field; - size_t len; - - /* no report associated (HID_QUIRK_MULTI_INPUT not set) */ - if (!hi->report) - return 0; - - field = hi->report->field[0]; - - switch (field->application) { - case HID_GD_KEYBOARD: - suffix = "Keyboard"; - break; - case HID_GD_MOUSE: - suffix = "Mouse"; - break; - case HID_GD_KEYPAD: - suffix = "Pad"; - break; - case HID_DG_PEN: - suffix = "Pen"; - break; - case HID_CP_CONSUMER_CONTROL: - suffix = "Consumer Control"; - break; - case HID_GD_SYSTEM_CONTROL: - suffix = "System Control"; - break; - } - - if (suffix) { - len = strlen(hdev->name) + 2 + strlen(suffix); - name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL); - if (name) { - snprintf(name, len, "%s %s", hdev->name, suffix); - hi->input->name = name; - } - } - - return 0; -} +const size_t uclogic_rdesc_pen_template_size = + sizeof(uclogic_rdesc_pen_template_arr); /** - * Enable fully-functional tablet mode and determine device parameters. + * Expand to the contents of a generic buttonpad report descriptor. * - * @hdev: HID device + * @_padding: Padding from the end of button bits at bit 44, until + * the end of the report, in bits. */ -static int uclogic_tablet_enable(struct hid_device *hdev) +#define UCLOGIC_RDESC_BUTTONPAD_BYTES(_padding) \ + 0x05, 0x01, /* Usage Page (Desktop), */ \ + 0x09, 0x07, /* Usage (Keypad), */ \ + 0xA1, 0x01, /* Collection (Application), */ \ + 0x85, 0xF7, /* Report ID (247), */ \ + 0x14, /* Logical Minimum (0), */ \ + 0x25, 0x01, /* Logical Maximum (1), */ \ + 0x75, 0x01, /* Report Size (1), */ \ + 0x05, 0x0D, /* Usage Page (Digitizer), */ \ + 0x09, 0x39, /* Usage (Tablet Function Keys), */ \ + 0xA0, /* Collection (Physical), */ \ + 0x09, 0x44, /* Usage (Barrel Switch), */ \ + 0x95, 0x01, /* Report Count (1), */ \ + 0x81, 0x02, /* Input (Variable), */ \ + 0x05, 0x01, /* Usage Page (Desktop), */ \ + 0x09, 0x30, /* Usage (X), */ \ + 0x09, 0x31, /* Usage (Y), */ \ + 0x95, 0x02, /* Report Count (2), */ \ + 0x81, 0x02, /* Input (Variable), */ \ + 0x95, 0x15, /* Report Count (21), */ \ + 0x81, 0x01, /* Input (Constant), */ \ + 0x05, 0x09, /* Usage Page (Button), */ \ + 0x19, 0x01, /* Usage Minimum (01h), */ \ + 0x29, 0x0A, /* Usage Maximum (0Ah), */ \ + 0x95, 0x0A, /* Report Count (10), */ \ + 0x81, 0x02, /* Input (Variable), */ \ + 0xC0, /* End Collection, */ \ + 0x05, 0x01, /* Usage Page (Desktop), */ \ + 0x09, 0x05, /* Usage (Gamepad), */ \ + 0xA0, /* Collection (Physical), */ \ + 0x05, 0x09, /* Usage Page (Button), */ \ + 0x19, 0x01, /* Usage Minimum (01h), */ \ + 0x29, 0x02, /* Usage Maximum (02h), */ \ + 0x95, 0x02, /* Report Count (2), */ \ + 0x81, 0x02, /* Input (Variable), */ \ + 0x95, _padding, /* Report Count (_padding), */ \ + 0x81, 0x01, /* Input (Constant), */ \ + 0xC0, /* End Collection, */ \ + 0xC0 /* End Collection */ + +/* Fixed report descriptor for (tweaked) buttonpad reports */ +const __u8 uclogic_rdesc_buttonpad_arr[] = { + UCLOGIC_RDESC_BUTTONPAD_BYTES(20) +}; +const size_t uclogic_rdesc_buttonpad_size = + sizeof(uclogic_rdesc_buttonpad_arr); + +/** + * uclogic_rdesc_template_apply() - apply report descriptor parameters to a + * report descriptor template, creating a report descriptor. Copies the + * template over to the new report descriptor and replaces every occurrence of + * UCLOGIC_RDESC_PH_HEAD, followed by an index byte, with the value from the + * parameter list at that index. + * + * @template_ptr: Pointer to the template buffer. + * @template_size: Size of the template buffer. + * @param_list: List of template parameters. + * @param_num: Number of parameters in the list. + * + * Returns: + * Kmalloc-allocated pointer to the created report descriptor, + * or NULL if allocation failed. + */ +__u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr, + size_t template_size, + const s32 *param_list, + size_t param_num) { - int rc; - struct usb_device *usb_dev = hid_to_usb_dev(hdev); - struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); - __le16 *buf = NULL; - size_t len; - s32 params[UCLOGIC_PH_ID_NUM]; - s32 resolution; + static const __u8 head[] = {UCLOGIC_RDESC_PH_HEAD}; + __u8 *rdesc_ptr; __u8 *p; s32 v; - /* - * Read string descriptor containing tablet parameters. The specific - * string descriptor and data were discovered by sniffing the Windows - * driver traffic. - * NOTE: This enables fully-functional tablet mode. - */ - len = UCLOGIC_PRM_NUM * sizeof(*buf); - buf = kmalloc(len, GFP_KERNEL); - if (buf == NULL) { - rc = -ENOMEM; - goto cleanup; - } - rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, - (USB_DT_STRING << 8) + 0x64, - 0x0409, buf, len, - USB_CTRL_GET_TIMEOUT); - if (rc == -EPIPE) { - hid_err(hdev, "device parameters not found\n"); - rc = -ENODEV; - goto cleanup; - } else if (rc < 0) { - hid_err(hdev, "failed to get device parameters: %d\n", rc); - rc = -ENODEV; - goto cleanup; - } else if (rc != len) { - hid_err(hdev, "invalid device parameters\n"); - rc = -ENODEV; - goto cleanup; - } + rdesc_ptr = kmemdup(template_ptr, template_size, GFP_KERNEL); + if (rdesc_ptr == NULL) + return NULL; - /* Extract device parameters */ - params[UCLOGIC_PH_ID_X_LM] = le16_to_cpu(buf[UCLOGIC_PRM_X_LM]); - params[UCLOGIC_PH_ID_Y_LM] = le16_to_cpu(buf[UCLOGIC_PRM_Y_LM]); - params[UCLOGIC_PH_ID_PRESSURE_LM] = - le16_to_cpu(buf[UCLOGIC_PRM_PRESSURE_LM]); - resolution = le16_to_cpu(buf[UCLOGIC_PRM_RESOLUTION]); - if (resolution == 0) { - params[UCLOGIC_PH_ID_X_PM] = 0; - params[UCLOGIC_PH_ID_Y_PM] = 0; - } else { - params[UCLOGIC_PH_ID_X_PM] = params[UCLOGIC_PH_ID_X_LM] * - 1000 / resolution; - params[UCLOGIC_PH_ID_Y_PM] = params[UCLOGIC_PH_ID_Y_LM] * - 1000 / resolution; - } - - /* Allocate fixed report descriptor */ - drvdata->rdesc = devm_kzalloc(&hdev->dev, - sizeof(uclogic_tablet_rdesc_template), - GFP_KERNEL); - if (drvdata->rdesc == NULL) { - rc = -ENOMEM; - goto cleanup; - } - drvdata->rsize = sizeof(uclogic_tablet_rdesc_template); - - /* Format fixed report descriptor */ - memcpy(drvdata->rdesc, uclogic_tablet_rdesc_template, - drvdata->rsize); - for (p = drvdata->rdesc; - p <= drvdata->rdesc + drvdata->rsize - 4;) { - if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D && - p[3] < ARRAY_SIZE(params)) { - v = params[p[3]]; + for (p = rdesc_ptr; p + sizeof(head) < rdesc_ptr + template_size;) { + if (memcmp(p, head, sizeof(head)) == 0 && + p[sizeof(head)] < param_num) { + v = param_list[p[sizeof(head)]]; put_unaligned(cpu_to_le32(v), (s32 *)p); - p += 4; + p += sizeof(head) + 1; } else { p++; } } - rc = 0; - -cleanup: - kfree(buf); - return rc; + return rdesc_ptr; } - -/** - * Enable actual button mode. - * - * @hdev: HID device - */ -static int uclogic_button_enable(struct hid_device *hdev) -{ - int rc; - struct usb_device *usb_dev = hid_to_usb_dev(hdev); - struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); - char *str_buf; - size_t str_len = 16; - unsigned char *rdesc; - size_t rdesc_len; - - str_buf = kzalloc(str_len, GFP_KERNEL); - if (str_buf == NULL) { - rc = -ENOMEM; - goto cleanup; - } - - /* Enable abstract keyboard mode */ - rc = usb_string(usb_dev, 0x7b, str_buf, str_len); - if (rc == -EPIPE) { - hid_info(hdev, "button mode setting not found\n"); - rc = 0; - goto cleanup; - } else if (rc < 0) { - hid_err(hdev, "failed to enable abstract keyboard\n"); - goto cleanup; - } else if (strncmp(str_buf, "HK On", rc)) { - hid_info(hdev, "invalid answer when requesting buttons: '%s'\n", - str_buf); - rc = -EINVAL; - goto cleanup; - } - - /* Re-allocate fixed report descriptor */ - rdesc_len = drvdata->rsize + sizeof(uclogic_buttonpad_rdesc); - rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL); - if (!rdesc) { - rc = -ENOMEM; - goto cleanup; - } - - memcpy(rdesc, drvdata->rdesc, drvdata->rsize); - - /* Append the buttonpad descriptor */ - memcpy(rdesc + drvdata->rsize, uclogic_buttonpad_rdesc, - sizeof(uclogic_buttonpad_rdesc)); - - /* clean up old rdesc and use the new one */ - drvdata->rsize = rdesc_len; - devm_kfree(&hdev->dev, drvdata->rdesc); - drvdata->rdesc = rdesc; - - rc = 0; - -cleanup: - kfree(str_buf); - return rc; -} - -static int uclogic_probe(struct hid_device *hdev, - const struct hid_device_id *id) -{ - int rc; - struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - struct usb_device *udev = hid_to_usb_dev(hdev); - struct uclogic_drvdata *drvdata; - - /* - * libinput requires the pad interface to be on a different node - * than the pen, so use QUIRK_MULTI_INPUT for all tablets. - */ - hdev->quirks |= HID_QUIRK_MULTI_INPUT; - - /* Allocate and assign driver data */ - drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); - if (drvdata == NULL) - return -ENOMEM; - - hid_set_drvdata(hdev, drvdata); - - switch (id->product) { - case USB_DEVICE_ID_HUION_TABLET: - case USB_DEVICE_ID_YIYNOVA_TABLET: - case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81: - case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3: - case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45: - /* If this is the pen interface */ - if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { - rc = uclogic_tablet_enable(hdev); - if (rc) { - hid_err(hdev, "tablet enabling failed\n"); - return rc; - } - drvdata->invert_pen_inrange = true; - - rc = uclogic_button_enable(hdev); - drvdata->has_virtual_pad_interface = !rc; - } else { - drvdata->ignore_pen_usage = true; - } - break; - case USB_DEVICE_ID_UGTIZER_TABLET_GP0610: - case USB_DEVICE_ID_UGEE_TABLET_EX07S: - /* If this is the pen interface */ - if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { - rc = uclogic_tablet_enable(hdev); - if (rc) { - hid_err(hdev, "tablet enabling failed\n"); - return rc; - } - drvdata->invert_pen_inrange = true; - } else { - drvdata->ignore_pen_usage = true; - } - break; - case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: - /* - * If it is the three-interface version, which is known to - * respond to initialization. - */ - if (udev->config->desc.bNumInterfaces == 3) { - /* If it is the pen interface */ - if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { - rc = uclogic_tablet_enable(hdev); - if (rc) { - hid_err(hdev, "tablet enabling failed\n"); - return rc; - } - drvdata->invert_pen_inrange = true; - - rc = uclogic_button_enable(hdev); - drvdata->has_virtual_pad_interface = !rc; - } else { - drvdata->ignore_pen_usage = true; - } - } - break; - } - - rc = hid_parse(hdev); - if (rc) { - hid_err(hdev, "parse failed\n"); - return rc; - } - - rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (rc) { - hid_err(hdev, "hw start failed\n"); - return rc; - } - - return 0; -} - -static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, - u8 *data, int size) -{ - struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); - - if ((report->type == HID_INPUT_REPORT) && - (report->id == UCLOGIC_PEN_REPORT_ID) && - (size >= 2)) { - if (drvdata->has_virtual_pad_interface && (data[1] & 0x20)) - /* Change to virtual frame button report ID */ - data[0] = 0xf7; - else if (drvdata->invert_pen_inrange) - /* Invert the in-range bit */ - data[1] ^= 0x40; - } - - return 0; -} - -static const struct hid_device_id uclogic_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, - USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, - USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, - USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, - USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, - USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, - USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, - USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, - { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, - { } -}; -MODULE_DEVICE_TABLE(hid, uclogic_devices); - -static struct hid_driver uclogic_driver = { - .name = "uclogic", - .id_table = uclogic_devices, - .probe = uclogic_probe, - .report_fixup = uclogic_report_fixup, - .raw_event = uclogic_raw_event, - .input_mapping = uclogic_input_mapping, - .input_configured = uclogic_input_configured, -}; -module_hid_driver(uclogic_driver); - -MODULE_AUTHOR("Martin Rusko"); -MODULE_AUTHOR("Nikolai Kondrashov"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h new file mode 100644 index 000000000000..610575879522 --- /dev/null +++ b/drivers/hid/hid-uclogic-rdesc.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * HID driver for UC-Logic devices not fully compliant with HID standard + * - original and fixed report descriptors + * + * Copyright (c) 2010-2018 Nikolai Kondrashov + * Copyright (c) 2013 Martin Rusko + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#ifndef _HID_UCLOGIC_RDESC_H +#define _HID_UCLOGIC_RDESC_H + +#include + +/* Size of the original descriptor of WPXXXXU tablets */ +#define UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE 212 + +/* Fixed WP4030U report descriptor */ +extern __u8 uclogic_rdesc_wp4030u_fixed_arr[]; +extern const size_t uclogic_rdesc_wp4030u_fixed_size; + +/* Fixed WP5540U report descriptor */ +extern __u8 uclogic_rdesc_wp5540u_fixed_arr[]; +extern const size_t uclogic_rdesc_wp5540u_fixed_size; + +/* Fixed WP8060U report descriptor */ +extern __u8 uclogic_rdesc_wp8060u_fixed_arr[]; +extern const size_t uclogic_rdesc_wp8060u_fixed_size; + +/* Size of the original descriptor of WP1062 tablet */ +#define UCLOGIC_RDESC_WP1062_ORIG_SIZE 254 + +/* Fixed WP1062 report descriptor */ +extern __u8 uclogic_rdesc_wp1062_fixed_arr[]; +extern const size_t uclogic_rdesc_wp1062_fixed_size; + +/* Size of the original descriptor of PF1209 tablet */ +#define UCLOGIC_RDESC_PF1209_ORIG_SIZE 234 + +/* Fixed PF1209 report descriptor */ +extern __u8 uclogic_rdesc_pf1209_fixed_arr[]; +extern const size_t uclogic_rdesc_pf1209_fixed_size; + +/* Size of the original descriptors of TWHL850 tablet */ +#define UCLOGIC_RDESC_TWHL850_ORIG0_SIZE 182 +#define UCLOGIC_RDESC_TWHL850_ORIG1_SIZE 161 +#define UCLOGIC_RDESC_TWHL850_ORIG2_SIZE 92 + +/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */ +extern __u8 uclogic_rdesc_twhl850_fixed0_arr[]; +extern const size_t uclogic_rdesc_twhl850_fixed0_size; + +/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */ +extern __u8 uclogic_rdesc_twhl850_fixed1_arr[]; +extern const size_t uclogic_rdesc_twhl850_fixed1_size; + +/* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */ +extern __u8 uclogic_rdesc_twhl850_fixed2_arr[]; +extern const size_t uclogic_rdesc_twhl850_fixed2_size; + +/* Size of the original descriptors of TWHA60 tablet */ +#define UCLOGIC_RDESC_TWHA60_ORIG0_SIZE 254 +#define UCLOGIC_RDESC_TWHA60_ORIG1_SIZE 139 + +/* Fixed TWHA60 report descriptor, interface 0 (stylus) */ +extern __u8 uclogic_rdesc_twha60_fixed0_arr[]; +extern const size_t uclogic_rdesc_twha60_fixed0_size; + +/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */ +extern __u8 uclogic_rdesc_twha60_fixed1_arr[]; +extern const size_t uclogic_rdesc_twha60_fixed1_size; + +/* Report descriptor template placeholder head */ +#define UCLOGIC_RDESC_PH_HEAD 0xFE, 0xED, 0x1D + +/* Apply report descriptor parameters to a report descriptor template */ +extern __u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr, + size_t template_size, + const s32 *param_list, + size_t param_num); + +/* Pen report descriptor template placeholder IDs */ +enum uclogic_rdesc_pen_ph_id { + UCLOGIC_RDESC_PEN_PH_ID_X_LM, + UCLOGIC_RDESC_PEN_PH_ID_X_PM, + UCLOGIC_RDESC_PEN_PH_ID_Y_LM, + UCLOGIC_RDESC_PEN_PH_ID_Y_PM, + UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM, + UCLOGIC_RDESC_PEN_PH_ID_NUM +}; + +/* Report descriptor pen template placeholder */ +#define UCLOGIC_RDESC_PEN_PH(_ID) \ + UCLOGIC_RDESC_PH_HEAD, UCLOGIC_RDESC_PEN_PH_ID_##_ID + +/* Report ID for pen reports */ +#define UCLOGIC_RDESC_PEN_ID 0x07 + +/* Fixed report descriptor template for (tweaked) pen reports */ +extern const __u8 uclogic_rdesc_pen_template_arr[]; +extern const size_t uclogic_rdesc_pen_template_size; + +/* Fixed report descriptor for (tweaked) buttonpad reports */ +extern const __u8 uclogic_rdesc_buttonpad_arr[]; +extern const size_t uclogic_rdesc_buttonpad_size; + +/* Report ID for tweaked buttonpad reports */ +#define UCLOGIC_RDESC_BUTTONPAD_ID 0xf7 + +#endif /* _HID_UCLOGIC_RDESC_H */ From 9614219e9310ef19e66719bf37f9f68919bac08e Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:51 +0200 Subject: [PATCH 05/23] HID: uclogic: Extract tablet parameter discovery into a module Refactor and extract UC-Logic tablet initialization and parameter discovery into a module. For these tablets, the major part of parameter discovery cannot be separated from initialization so they have to be in the same module. Define explicitly and clearly what possible quirks the tablets may have to make the driver implementation easier and simpler. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/Makefile | 3 +- drivers/hid/hid-uclogic-core.c | 440 +++++------------ drivers/hid/hid-uclogic-params.c | 806 +++++++++++++++++++++++++++++++ drivers/hid/hid-uclogic-params.h | 180 +++++++ 4 files changed, 1096 insertions(+), 333 deletions(-) create mode 100644 drivers/hid/hid-uclogic-params.c create mode 100644 drivers/hid/hid-uclogic-params.h diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fb75366ea776..9b3a747af60d 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -109,7 +109,8 @@ obj-$(CONFIG_HID_TIVO) += hid-tivo.o obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o hid-uclogic-objs := hid-uclogic-core.o \ - hid-uclogic-rdesc.o + hid-uclogic-rdesc.o \ + hid-uclogic-params.o obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o obj-$(CONFIG_HID_LED) += hid-led.o diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index 4042183ee9a3..72a3a43766cc 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -16,126 +16,48 @@ #include #include #include -#include #include "usbhid/usbhid.h" -#include "hid-uclogic-rdesc.h" +#include "hid-uclogic-params.h" #include "hid-ids.h" -/* Parameter indices */ -enum uclogic_prm { - UCLOGIC_PRM_X_LM = 1, - UCLOGIC_PRM_Y_LM = 2, - UCLOGIC_PRM_PRESSURE_LM = 4, - UCLOGIC_PRM_RESOLUTION = 5, - UCLOGIC_PRM_NUM -}; - /* Driver data */ struct uclogic_drvdata { - __u8 *rdesc; - unsigned int rsize; - bool invert_pen_inrange; - bool ignore_pen_usage; - bool has_virtual_pad_interface; + /* Interface parameters */ + struct uclogic_params params; + /* Pointer to the replacement report descriptor. NULL if none. */ + __u8 *desc_ptr; + /* + * Size of the replacement report descriptor. + * Only valid if desc_ptr is not NULL + */ + unsigned int desc_size; }; static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { - struct usb_interface *iface = to_usb_interface(hdev->dev.parent); - __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); - if (drvdata->rdesc != NULL) { - rdesc = drvdata->rdesc; - *rsize = drvdata->rsize; - return rdesc; + if (drvdata->desc_ptr != NULL) { + rdesc = drvdata->desc_ptr; + *rsize = drvdata->desc_size; } - - switch (hdev->product) { - case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: - if (*rsize == UCLOGIC_RDESC_PF1209_ORIG_SIZE) { - rdesc = uclogic_rdesc_pf1209_fixed_arr; - *rsize = uclogic_rdesc_pf1209_fixed_size; - } - break; - case USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U: - if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) { - rdesc = uclogic_rdesc_wp4030u_fixed_arr; - *rsize = uclogic_rdesc_wp4030u_fixed_size; - } - break; - case USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U: - if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) { - rdesc = uclogic_rdesc_wp5540u_fixed_arr; - *rsize = uclogic_rdesc_wp5540u_fixed_size; - } - break; - case USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U: - if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) { - rdesc = uclogic_rdesc_wp8060u_fixed_arr; - *rsize = uclogic_rdesc_wp8060u_fixed_size; - } - break; - case USB_DEVICE_ID_UCLOGIC_TABLET_WP1062: - if (*rsize == UCLOGIC_RDESC_WP1062_ORIG_SIZE) { - rdesc = uclogic_rdesc_wp1062_fixed_arr; - *rsize = uclogic_rdesc_wp1062_fixed_size; - } - break; - case USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850: - switch (iface_num) { - case 0: - if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG0_SIZE) { - rdesc = uclogic_rdesc_twhl850_fixed0_arr; - *rsize = uclogic_rdesc_twhl850_fixed0_size; - } - break; - case 1: - if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG1_SIZE) { - rdesc = uclogic_rdesc_twhl850_fixed1_arr; - *rsize = uclogic_rdesc_twhl850_fixed1_size; - } - break; - case 2: - if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG2_SIZE) { - rdesc = uclogic_rdesc_twhl850_fixed2_arr; - *rsize = uclogic_rdesc_twhl850_fixed2_size; - } - break; - } - break; - case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: - switch (iface_num) { - case 0: - if (*rsize == UCLOGIC_RDESC_TWHA60_ORIG0_SIZE) { - rdesc = uclogic_rdesc_twha60_fixed0_arr; - *rsize = uclogic_rdesc_twha60_fixed0_size; - } - break; - case 1: - if (*rsize == UCLOGIC_RDESC_TWHA60_ORIG1_SIZE) { - rdesc = uclogic_rdesc_twha60_fixed1_arr; - *rsize = uclogic_rdesc_twha60_fixed1_size; - } - break; - } - break; - } - return rdesc; } -static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) +static int uclogic_input_mapping(struct hid_device *hdev, + struct hid_input *hi, + struct hid_field *field, + struct hid_usage *usage, + unsigned long **bit, + int *max) { struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + struct uclogic_params *params = &drvdata->params; /* discard the unused pen interface */ - if ((drvdata->ignore_pen_usage) && - (field->application == HID_DG_PEN)) + if (params->pen_unused && (field->application == HID_DG_PEN)) return -1; /* let hid-core decide what to do */ @@ -189,160 +111,12 @@ static int uclogic_input_configured(struct hid_device *hdev, return 0; } -/** - * Enable fully-functional tablet mode and determine device parameters. - * - * @hdev: HID device - */ -static int uclogic_tablet_enable(struct hid_device *hdev) -{ - int rc; - struct usb_device *usb_dev = hid_to_usb_dev(hdev); - struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); - __le16 *buf = NULL; - size_t len; - s32 params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; - s32 resolution; - - /* - * Read string descriptor containing tablet parameters. The specific - * string descriptor and data were discovered by sniffing the Windows - * driver traffic. - * NOTE: This enables fully-functional tablet mode. - */ - len = UCLOGIC_PRM_NUM * sizeof(*buf); - buf = kmalloc(len, GFP_KERNEL); - if (buf == NULL) { - rc = -ENOMEM; - goto cleanup; - } - rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), - USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, - (USB_DT_STRING << 8) + 0x64, - 0x0409, buf, len, - USB_CTRL_GET_TIMEOUT); - if (rc == -EPIPE) { - hid_err(hdev, "device parameters not found\n"); - rc = -ENODEV; - goto cleanup; - } else if (rc < 0) { - hid_err(hdev, "failed to get device parameters: %d\n", rc); - rc = -ENODEV; - goto cleanup; - } else if (rc != len) { - hid_err(hdev, "invalid device parameters\n"); - rc = -ENODEV; - goto cleanup; - } - - /* Extract device parameters */ - params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = - le16_to_cpu(buf[UCLOGIC_PRM_X_LM]); - params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = - le16_to_cpu(buf[UCLOGIC_PRM_Y_LM]); - params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = - le16_to_cpu(buf[UCLOGIC_PRM_PRESSURE_LM]); - resolution = le16_to_cpu(buf[UCLOGIC_PRM_RESOLUTION]); - if (resolution == 0) { - params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; - params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; - } else { - params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = - params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * - 1000 / resolution; - params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = - params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * - 1000 / resolution; - } - - /* Format fixed report descriptor */ - drvdata->rdesc = uclogic_rdesc_template_apply( - uclogic_rdesc_pen_template_arr, - uclogic_rdesc_pen_template_size, - params, ARRAY_SIZE(params)); - if (drvdata->rdesc == NULL) { - rc = -ENOMEM; - goto cleanup; - } - drvdata->rsize = uclogic_rdesc_pen_template_size; - - rc = 0; - -cleanup: - kfree(buf); - return rc; -} - -/** - * Enable actual button mode. - * - * @hdev: HID device - */ -static int uclogic_button_enable(struct hid_device *hdev) -{ - int rc; - struct usb_device *usb_dev = hid_to_usb_dev(hdev); - struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); - char *str_buf; - size_t str_len = 16; - unsigned char *rdesc; - size_t rdesc_len; - - str_buf = kzalloc(str_len, GFP_KERNEL); - if (str_buf == NULL) { - rc = -ENOMEM; - goto cleanup; - } - - /* Enable abstract keyboard mode */ - rc = usb_string(usb_dev, 0x7b, str_buf, str_len); - if (rc == -EPIPE) { - hid_info(hdev, "button mode setting not found\n"); - rc = 0; - goto cleanup; - } else if (rc < 0) { - hid_err(hdev, "failed to enable abstract keyboard\n"); - goto cleanup; - } else if (strncmp(str_buf, "HK On", rc)) { - hid_info(hdev, "invalid answer when requesting buttons: '%s'\n", - str_buf); - rc = -EINVAL; - goto cleanup; - } - - /* Re-allocate fixed report descriptor */ - rdesc_len = drvdata->rsize + uclogic_rdesc_buttonpad_size; - rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL); - if (!rdesc) { - rc = -ENOMEM; - goto cleanup; - } - - memcpy(rdesc, drvdata->rdesc, drvdata->rsize); - - /* Append the buttonpad descriptor */ - memcpy(rdesc + drvdata->rsize, uclogic_rdesc_buttonpad_arr, - uclogic_rdesc_buttonpad_size); - - /* clean up old rdesc and use the new one */ - drvdata->rsize = rdesc_len; - devm_kfree(&hdev->dev, drvdata->rdesc); - drvdata->rdesc = rdesc; - - rc = 0; - -cleanup: - kfree(str_buf); - return rc; -} - static int uclogic_probe(struct hid_device *hdev, const struct hid_device_id *id) { int rc; - struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - struct usb_device *udev = hid_to_usb_dev(hdev); - struct uclogic_drvdata *drvdata; + struct uclogic_drvdata *drvdata = NULL; + bool params_initialized = false; /* * libinput requires the pad interface to be on a different node @@ -352,102 +126,95 @@ static int uclogic_probe(struct hid_device *hdev, /* Allocate and assign driver data */ drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); - if (drvdata == NULL) - return -ENOMEM; - + if (drvdata == NULL) { + rc = -ENOMEM; + goto failure; + } hid_set_drvdata(hdev, drvdata); - switch (id->product) { - case USB_DEVICE_ID_HUION_TABLET: - case USB_DEVICE_ID_YIYNOVA_TABLET: - case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81: - case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3: - case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45: - /* If this is the pen interface */ - if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { - rc = uclogic_tablet_enable(hdev); - if (rc) { - hid_err(hdev, "tablet enabling failed\n"); - return rc; - } - drvdata->invert_pen_inrange = true; + /* Initialize the device and retrieve interface parameters */ + rc = uclogic_params_init(&drvdata->params, hdev); + if (rc != 0) { + hid_err(hdev, "failed probing parameters: %d\n", rc); + goto failure; + } + params_initialized = true; + hid_dbg(hdev, "parameters:\n" UCLOGIC_PARAMS_FMT_STR, + UCLOGIC_PARAMS_FMT_ARGS(&drvdata->params)); + if (drvdata->params.invalid) { + hid_info(hdev, "interface is invalid, ignoring\n"); + rc = -ENODEV; + goto failure; + } - rc = uclogic_button_enable(hdev); - drvdata->has_virtual_pad_interface = !rc; - } else { - drvdata->ignore_pen_usage = true; - } - break; - case USB_DEVICE_ID_UGTIZER_TABLET_GP0610: - case USB_DEVICE_ID_UGEE_TABLET_EX07S: - /* If this is the pen interface */ - if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { - rc = uclogic_tablet_enable(hdev); - if (rc) { - hid_err(hdev, "tablet enabling failed\n"); - return rc; - } - drvdata->invert_pen_inrange = true; - } else { - drvdata->ignore_pen_usage = true; - } - break; - case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: - /* - * If it is the three-interface version, which is known to - * respond to initialization. - */ - if (udev->config->desc.bNumInterfaces == 3) { - /* If it is the pen interface */ - if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { - rc = uclogic_tablet_enable(hdev); - if (rc) { - hid_err(hdev, "tablet enabling failed\n"); - return rc; - } - drvdata->invert_pen_inrange = true; - - rc = uclogic_button_enable(hdev); - drvdata->has_virtual_pad_interface = !rc; - } else { - drvdata->ignore_pen_usage = true; - } - } - break; + /* Generate replacement report descriptor */ + rc = uclogic_params_get_desc(&drvdata->params, + &drvdata->desc_ptr, + &drvdata->desc_size); + if (rc) { + hid_err(hdev, + "failed generating replacement report descriptor: %d\n", + rc); + goto failure; } rc = hid_parse(hdev); if (rc) { hid_err(hdev, "parse failed\n"); - return rc; + goto failure; } rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (rc) { hid_err(hdev, "hw start failed\n"); - return rc; + goto failure; + } + + return 0; +failure: + /* Assume "remove" might not be called if "probe" failed */ + if (params_initialized) + uclogic_params_cleanup(&drvdata->params); + return rc; +} + +static int uclogic_raw_event(struct hid_device *hdev, + struct hid_report *report, + u8 *data, int size) +{ + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + struct uclogic_params *params = &drvdata->params; + + /* Tweak pen reports, if necessary */ + if (!params->pen_unused && + (report->type == HID_INPUT_REPORT) && + (report->id == params->pen.id) && + (size >= 2)) { + /* If it's the "virtual" frame controls report */ + if (params->frame.id != 0 && + data[1] & params->pen_frame_flag) { + /* Change to virtual frame controls report ID */ + data[0] = params->frame.id; + return 0; + } + /* If in-range reports are inverted */ + if (params->pen.inrange == + UCLOGIC_PARAMS_PEN_INRANGE_INVERTED) { + /* Invert the in-range bit */ + data[1] ^= 0x40; + } } return 0; } -static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, - u8 *data, int size) +static void uclogic_remove(struct hid_device *hdev) { struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); - if ((report->type == HID_INPUT_REPORT) && - (report->id == UCLOGIC_RDESC_PEN_ID) && - (size >= 2)) { - if (drvdata->has_virtual_pad_interface && (data[1] & 0x20)) - /* Change to virtual frame button report ID */ - data[0] = 0xf7; - else if (drvdata->invert_pen_inrange) - /* Invert the in-range bit */ - data[1] ^= 0x40; - } - - return 0; + hid_hw_stop(hdev); + kfree(drvdata->desc_ptr); + uclogic_params_cleanup(&drvdata->params); } static const struct hid_device_id uclogic_devices[] = { @@ -465,14 +232,22 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, - { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, + USB_DEVICE_ID_HUION_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_HUION_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_YIYNOVA_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, + USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_TABLET_EX07S) }, { } }; MODULE_DEVICE_TABLE(hid, uclogic_devices); @@ -481,6 +256,7 @@ static struct hid_driver uclogic_driver = { .name = "uclogic", .id_table = uclogic_devices, .probe = uclogic_probe, + .remove = uclogic_remove, .report_fixup = uclogic_report_fixup, .raw_event = uclogic_raw_event, .input_mapping = uclogic_input_mapping, diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c new file mode 100644 index 000000000000..2f8870d58f9a --- /dev/null +++ b/drivers/hid/hid-uclogic-params.c @@ -0,0 +1,806 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HID driver for UC-Logic devices not fully compliant with HID standard + * - tablet initialization and parameter retrieval + * + * Copyright (c) 2018 Nikolai Kondrashov + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include "hid-uclogic-params.h" +#include "hid-uclogic-rdesc.h" +#include "usbhid/usbhid.h" +#include "hid-ids.h" +#include +#include + +/** + * Convert a pen in-range reporting type to a string. + * + * @inrange: The in-range reporting type to convert. + * + * Returns: + * The string representing the type, or NULL if the type is unknown. + */ +const char *uclogic_params_pen_inrange_to_str( + enum uclogic_params_pen_inrange inrange) +{ + switch (inrange) { + case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL: + return "normal"; + case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED: + return "inverted"; + default: + return NULL; + } +} + +/** + * uclogic_params_get_str_desc - retrieve a string descriptor from a HID + * device interface, putting it into a kmalloc-allocated buffer as is, without + * character encoding conversion. + * + * @pbuf: Location for the kmalloc-allocated buffer pointer containing + * the retrieved descriptor. Not modified in case of error. + * Can be NULL to have retrieved descriptor discarded. + * @hdev: The HID device of the tablet interface to retrieve the string + * descriptor from. Cannot be NULL. + * @idx: Index of the string descriptor to request from the device. + * @len: Length of the buffer to allocate and the data to retrieve. + * + * Returns: + * number of bytes retrieved (<= len), + * -EPIPE, if the descriptor was not found, or + * another negative errno code in case of other error. + */ +static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev, + __u8 idx, size_t len) +{ + int rc; + struct usb_device *udev = hid_to_usb_dev(hdev); + __u8 *buf = NULL; + + /* Check arguments */ + if (hdev == NULL) { + rc = -EINVAL; + goto cleanup; + } + + buf = kmalloc(len, GFP_KERNEL); + if (buf == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (USB_DT_STRING << 8) + idx, + 0x0409, buf, len, + USB_CTRL_GET_TIMEOUT); + if (rc == -EPIPE) { + hid_dbg(hdev, "string descriptor #%hhu not found\n", idx); + goto cleanup; + } else if (rc < 0) { + hid_err(hdev, + "failed retrieving string descriptor #%hhu: %d\n", + idx, rc); + goto cleanup; + } + + if (pbuf != NULL) { + *pbuf = buf; + buf = NULL; + } + +cleanup: + kfree(buf); + return rc; +} + +/** + * uclogic_params_pen_cleanup - free resources used by struct + * uclogic_params_pen (tablet interface's pen input parameters). + * Can be called repeatedly. + * + * @pen: Pen input parameters to cleanup. Cannot be NULL. + */ +static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen) +{ + kfree(pen->desc_ptr); + memset(pen, 0, sizeof(*pen)); +} + +/** + * uclogic_params_pen_init() - initialize tablet interface pen + * input and retrieve its parameters from the device. + * + * @pen: Pointer to the pen parameters to initialize (to be + * cleaned up with uclogic_params_pen_cleanup()). Not modified in + * case of error, or if parameters are not found. Cannot be NULL. + * @pfound: Location for a flag which is set to true if the parameters + * were found, and to false if not (e.g. device was + * incompatible). 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_pen_init(struct uclogic_params_pen *pen, + bool *pfound, + struct hid_device *hdev) +{ + int rc; + bool found = false; + /* Buffer for (part of) the string descriptor */ + __u8 *buf = NULL; + /* Minimum descriptor length required, maximum seen so far is 18 */ + const int len = 12; + s32 resolution; + /* Pen report descriptor template parameters */ + s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; + __u8 *desc_ptr = NULL; + + /* Check arguments */ + if (pen == NULL || pfound == NULL || hdev == NULL) { + rc = -EINVAL; + goto cleanup; + } + + /* + * Read string descriptor containing pen input parameters. + * The specific string descriptor and data were discovered by sniffing + * the Windows driver traffic. + * NOTE: This enables fully-functional tablet mode. + */ + rc = uclogic_params_get_str_desc(&buf, hdev, 100, len); + if (rc == -EPIPE) { + hid_dbg(hdev, + "string descriptor with pen parameters not found, assuming not compatible\n"); + goto finish; + } else if (rc < 0) { + hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); + goto cleanup; + } else if (rc != len) { + hid_dbg(hdev, + "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n", + rc, len); + goto finish; + } + + /* + * Fill report descriptor parameters from the string descriptor + */ + desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = + get_unaligned_le16(buf + 2); + desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = + get_unaligned_le16(buf + 4); + desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = + get_unaligned_le16(buf + 8); + resolution = get_unaligned_le16(buf + 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(buf); + buf = NULL; + + /* + * Generate pen report descriptor + */ + desc_ptr = uclogic_rdesc_template_apply( + uclogic_rdesc_pen_template_arr, + uclogic_rdesc_pen_template_size, + desc_params, ARRAY_SIZE(desc_params)); + if (desc_ptr == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + /* + * Fill-in the parameters + */ + memset(pen, 0, sizeof(*pen)); + pen->desc_ptr = desc_ptr; + desc_ptr = NULL; + pen->desc_size = uclogic_rdesc_pen_template_size; + pen->id = UCLOGIC_RDESC_PEN_ID; + pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED; + found = true; +finish: + *pfound = found; + rc = 0; +cleanup: + kfree(desc_ptr); + kfree(buf); + return rc; +} + +/** + * uclogic_params_frame_cleanup - free resources used by struct + * uclogic_params_frame (tablet interface's frame controls input parameters). + * Can be called repeatedly. + * + * @frame: Frame controls input parameters to cleanup. Cannot be NULL. + */ +static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame) +{ + kfree(frame->desc_ptr); + memset(frame, 0, sizeof(*frame)); +} + +/** + * uclogic_params_frame_init_with_desc() - initialize tablet's frame control + * parameters with a static report descriptor. + * + * @frame: Pointer to the frame parameters to initialize (to be cleaned + * up with uclogic_params_frame_cleanup()). Not modified in case + * of error. Cannot be NULL. + * @desc_ptr: Report descriptor pointer. Can be NULL, if desc_size is zero. + * @desc_size: Report descriptor size. + * @id: Report ID used for frame reports, if they should be tweaked, + * zero if not. + * + * Returns: + * Zero, if successful. A negative errno code on error. + */ +static int uclogic_params_frame_init_with_desc( + struct uclogic_params_frame *frame, + const __u8 *desc_ptr, + size_t desc_size, + unsigned int id) +{ + __u8 *copy_desc_ptr; + + if (frame == NULL || (desc_ptr == NULL && desc_size != 0)) + return -EINVAL; + + copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); + if (copy_desc_ptr == NULL) + return -ENOMEM; + + memset(frame, 0, sizeof(*frame)); + frame->desc_ptr = copy_desc_ptr; + frame->desc_size = desc_size; + frame->id = id; + return 0; +} + +/** + * uclogic_params_frame_init_buttonpad() - initialize abstract buttonpad + * on a tablet interface. + * + * @frame: Pointer to the frame parameters to initialize (to be cleaned + * up with uclogic_params_frame_cleanup()). Not modified in case + * of error, or if parameters are not found. Cannot be NULL. + * @pfound: Location for a flag which is set to true if the parameters + * were found, and to false if not (e.g. device was + * incompatible). 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_frame_init_buttonpad( + struct uclogic_params_frame *frame, + bool *pfound, + struct hid_device *hdev) +{ + int rc; + bool found = false; + struct usb_device *usb_dev = hid_to_usb_dev(hdev); + char *str_buf = NULL; + const size_t str_len = 16; + + /* Check arguments */ + if (frame == NULL || pfound == NULL || hdev == NULL) { + rc = -EINVAL; + goto cleanup; + } + + /* + * Enable generic button mode + */ + str_buf = kzalloc(str_len, GFP_KERNEL); + if (str_buf == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + rc = usb_string(usb_dev, 123, str_buf, str_len); + if (rc == -EPIPE) { + hid_dbg(hdev, + "generic button -enabling string descriptor not found\n"); + } else if (rc < 0) { + goto cleanup; + } else if (strncmp(str_buf, "HK On", rc) != 0) { + hid_dbg(hdev, + "invalid response to enabling generic buttons: \"%s\"\n", + str_buf); + } else { + hid_dbg(hdev, "generic buttons enabled\n"); + rc = uclogic_params_frame_init_with_desc( + frame, + uclogic_rdesc_buttonpad_arr, + uclogic_rdesc_buttonpad_size, + UCLOGIC_RDESC_BUTTONPAD_ID); + if (rc != 0) + goto cleanup; + found = true; + } + + *pfound = found; + rc = 0; +cleanup: + kfree(str_buf); + return rc; +} + +/** + * uclogic_params_cleanup - free resources used by struct uclogic_params + * (tablet interface's parameters). + * Can be called repeatedly. + * + * @params: Input parameters to cleanup. Cannot be NULL. + */ +void uclogic_params_cleanup(struct uclogic_params *params) +{ + if (!params->invalid) { + kfree(params->desc_ptr); + if (!params->pen_unused) + uclogic_params_pen_cleanup(¶ms->pen); + uclogic_params_frame_cleanup(¶ms->frame); + memset(params, 0, sizeof(*params)); + } +} + +/** + * Get a replacement report descriptor for a tablet's interface. + * + * @params: The parameters of a tablet interface to get report + * descriptor for. Cannot be NULL. + * @pdesc: Location for the resulting, kmalloc-allocated report + * descriptor pointer, or for NULL, if there's no replacement + * report descriptor. Not modified in case of error. Cannot be + * NULL. + * @psize: Location for the resulting report descriptor size, not set if + * there's no replacement report descriptor. Not modified in case + * of error. Cannot be NULL. + * + * Returns: + * Zero, if successful. + * -EINVAL, if invalid arguments are supplied. + * -ENOMEM, if failed to allocate memory. + */ +int uclogic_params_get_desc(const struct uclogic_params *params, + __u8 **pdesc, + unsigned int *psize) +{ + bool common_present; + bool pen_present; + bool frame_present; + unsigned int size; + __u8 *desc = NULL; + + /* Check arguments */ + if (params == NULL || pdesc == NULL || psize == NULL) + return -EINVAL; + + size = 0; + + common_present = (params->desc_ptr != NULL); + pen_present = (!params->pen_unused && params->pen.desc_ptr != NULL); + frame_present = (params->frame.desc_ptr != NULL); + + if (common_present) + size += params->desc_size; + if (pen_present) + size += params->pen.desc_size; + if (frame_present) + size += params->frame.desc_size; + + if (common_present || pen_present || frame_present) { + __u8 *p; + + desc = kmalloc(size, GFP_KERNEL); + if (desc == NULL) + return -ENOMEM; + p = desc; + + if (common_present) { + memcpy(p, params->desc_ptr, + params->desc_size); + p += params->desc_size; + } + if (pen_present) { + memcpy(p, params->pen.desc_ptr, + params->pen.desc_size); + p += params->pen.desc_size; + } + if (frame_present) { + memcpy(p, params->frame.desc_ptr, + params->frame.desc_size); + p += params->frame.desc_size; + } + + WARN_ON(p != desc + size); + + *psize = size; + } + + *pdesc = desc; + return 0; +} + +/** + * uclogic_params_init_invalid() - initialize tablet interface parameters, + * specifying the interface is invalid. + * + * @params: Parameters to initialize (to be cleaned with + * uclogic_params_cleanup()). Cannot be NULL. + */ +static void uclogic_params_init_invalid(struct uclogic_params *params) +{ + params->invalid = true; +} + +/** + * uclogic_params_init_with_opt_desc() - initialize tablet interface + * parameters with an optional replacement report descriptor. Only modify + * report descriptor, if the original report descriptor matches the expected + * size. + * + * @params: Parameters to initialize (to be cleaned with + * uclogic_params_cleanup()). Not modified in case of + * error. Cannot be NULL. + * @hdev: The HID device of the tablet interface create the + * parameters for. Cannot be NULL. + * @orig_desc_size: Expected size of the original report descriptor to + * be replaced. + * @desc_ptr: Pointer to the replacement report descriptor. + * Can be NULL, if desc_size is zero. + * @desc_size: Size of the replacement report descriptor. + * + * Returns: + * Zero, if successful. -EINVAL if an invalid argument was passed. + * -ENOMEM, if failed to allocate memory. + */ +static int uclogic_params_init_with_opt_desc(struct uclogic_params *params, + struct hid_device *hdev, + unsigned int orig_desc_size, + __u8 *desc_ptr, + unsigned int desc_size) +{ + __u8 *desc_copy_ptr = NULL; + unsigned int desc_copy_size; + int rc; + + /* Check arguments */ + if (params == NULL || hdev == NULL || + (desc_ptr == NULL && desc_size != 0)) { + rc = -EINVAL; + goto cleanup; + } + + /* Replace report descriptor, if it matches */ + if (hdev->dev_rsize == orig_desc_size) { + hid_dbg(hdev, + "device report descriptor matches the expected size, replacing\n"); + desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); + if (desc_copy_ptr == NULL) { + rc = -ENOMEM; + goto cleanup; + } + desc_copy_size = desc_size; + } else { + hid_dbg(hdev, + "device report descriptor doesn't match the expected size (%u != %u), preserving\n", + hdev->dev_rsize, orig_desc_size); + desc_copy_ptr = NULL; + desc_copy_size = 0; + } + + /* Output parameters */ + memset(params, 0, sizeof(*params)); + params->desc_ptr = desc_copy_ptr; + desc_copy_ptr = NULL; + params->desc_size = desc_copy_size; + + rc = 0; +cleanup: + kfree(desc_copy_ptr); + return rc; +} + +/** + * uclogic_params_init_with_pen_unused() - initialize tablet interface + * parameters preserving original reports and generic HID processing, but + * disabling pen usage. + * + * @params: Parameters to initialize (to be cleaned with + * uclogic_params_cleanup()). Not modified in case of + * error. Cannot be NULL. + */ +static void uclogic_params_init_with_pen_unused(struct uclogic_params *params) +{ + memset(params, 0, sizeof(*params)); + params->pen_unused = true; +} + +/** + * uclogic_params_init() - initialize a Huion tablet interface and discover + * its parameters. + * + * @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_huion_init(struct uclogic_params *params, + struct hid_device *hdev) +{ + int rc; + struct usb_interface *iface = to_usb_interface(hdev->dev.parent); + __u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; + bool found; + /* The resulting parameters (noop) */ + struct uclogic_params p = {0, }; + + /* Check arguments */ + if (params == NULL || hdev == NULL) { + rc = -EINVAL; + goto cleanup; + } + + /* If it's not a pen interface */ + if (bInterfaceNumber != 0) { + /* TODO: Consider marking the interface invalid */ + uclogic_params_init_with_pen_unused(&p); + goto output; + } + + /* Try to probe pen parameters */ + rc = uclogic_params_pen_init(&p.pen, &found, hdev); + if (rc != 0) { + hid_err(hdev, + "failed probing pen parameters: %d\n", rc); + goto cleanup; + } else if (found) { + hid_dbg(hdev, "pen parameters found\n"); + /* Try to probe buttonpad */ + rc = uclogic_params_frame_init_buttonpad( + &p.frame, + &found, hdev); + if (rc != 0) { + hid_err(hdev, "v1 buttonpad probing failed: %d\n", rc); + goto cleanup; + } + hid_dbg(hdev, "buttonpad parameters%s found\n", + (found ? "" : " not")); + if (found) { + /* Set bitmask marking frame reports */ + p.pen_frame_flag = 0x20; + } + goto output; + } + hid_dbg(hdev, "pen parameters not found\n"); + + uclogic_params_init_invalid(&p); + +output: + /* Output parameters */ + memcpy(params, &p, sizeof(*params)); + memset(&p, 0, sizeof(p)); + rc = 0; +cleanup: + uclogic_params_cleanup(&p); + return rc; +} + +/** + * uclogic_params_init() - initialize a tablet interface and discover its + * parameters. + * + * @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. + */ +int uclogic_params_init(struct uclogic_params *params, + struct hid_device *hdev) +{ + int rc; + struct usb_device *udev = hid_to_usb_dev(hdev); + __u8 bNumInterfaces = udev->config->desc.bNumInterfaces; + struct usb_interface *iface = to_usb_interface(hdev->dev.parent); + __u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; + bool found; + /* The resulting parameters (noop) */ + struct uclogic_params p = {0, }; + + /* Check arguments */ + if (params == NULL || hdev == NULL) { + rc = -EINVAL; + goto cleanup; + } + + /* + * Set replacement report descriptor if the original matches the + * specified size. Otherwise keep interface unchanged. + */ +#define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \ + uclogic_params_init_with_opt_desc( \ + &p, hdev, \ + UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \ + uclogic_rdesc_##_new_desc_token##_arr, \ + uclogic_rdesc_##_new_desc_token##_size) + +#define VID_PID(_vid, _pid) \ + (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX)) + + /* + * Handle specific interfaces for specific tablets. + * + * Observe the following logic: + * + * If the interface is recognized as producing certain useful input: + * Mark interface as valid. + * Output interface parameters. + * Else, if the interface is recognized as *not* producing any useful + * input: + * Mark interface as invalid. + * Else: + * Mark interface as valid. + * Output noop parameters. + * + * Rule of thumb: it is better to disable a broken interface than let + * it spew garbage input. + */ + + switch (VID_PID(hdev->vendor, hdev->product)) { + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_TABLET_PF1209): + rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed); + if (rc != 0) + goto cleanup; + break; + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U): + rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed); + if (rc != 0) + goto cleanup; + break; + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U): + rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed); + if (rc != 0) + goto cleanup; + break; + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U): + rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed); + if (rc != 0) + goto cleanup; + break; + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_TABLET_WP1062): + rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed); + if (rc != 0) + goto cleanup; + break; + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850): + switch (bInterfaceNumber) { + case 0: + rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0); + if (rc != 0) + goto cleanup; + break; + case 1: + rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1); + if (rc != 0) + goto cleanup; + break; + case 2: + rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2); + if (rc != 0) + goto cleanup; + break; + } + break; + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60): + /* + * If it is not a three-interface version, which is known to + * respond to initialization. + */ + if (bNumInterfaces != 3) { + switch (bInterfaceNumber) { + case 0: + rc = WITH_OPT_DESC(TWHA60_ORIG0, + twha60_fixed0); + if (rc != 0) + goto cleanup; + break; + case 1: + rc = WITH_OPT_DESC(TWHA60_ORIG1, + twha60_fixed1); + if (rc != 0) + goto cleanup; + break; + } + break; + } + /* FALL THROUGH */ + case VID_PID(USB_VENDOR_ID_HUION, + USB_DEVICE_ID_HUION_TABLET): + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_HUION_TABLET): + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_YIYNOVA_TABLET): + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81): + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3): + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45): + rc = uclogic_params_huion_init(&p, hdev); + if (rc != 0) + goto cleanup; + break; + case VID_PID(USB_VENDOR_ID_UGTIZER, + USB_DEVICE_ID_UGTIZER_TABLET_GP0610): + case VID_PID(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_TABLET_EX07S): + /* If this is the pen interface */ + if (bInterfaceNumber == 1) { + /* Probe pen parameters */ + rc = uclogic_params_pen_init(&p.pen, &found, hdev); + if (rc != 0) { + hid_err(hdev, "pen probing failed: %d\n", rc); + goto cleanup; + } + if (!found) { + hid_warn(hdev, "pen parameters not found"); + uclogic_params_init_invalid(&p); + } + } else { + /* TODO: Consider marking the interface invalid */ + uclogic_params_init_with_pen_unused(&p); + } + break; + } + +#undef VID_PID +#undef WITH_OPT_DESC + + /* Output parameters */ + memcpy(params, &p, sizeof(*params)); + memset(&p, 0, sizeof(p)); + rc = 0; +cleanup: + uclogic_params_cleanup(&p); + return rc; +} diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h new file mode 100644 index 000000000000..4c78d9dd0576 --- /dev/null +++ b/drivers/hid/hid-uclogic-params.h @@ -0,0 +1,180 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * HID driver for UC-Logic devices not fully compliant with HID standard + * - tablet initialization and parameter retrieval + * + * Copyright (c) 2018 Nikolai Kondrashov + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#ifndef _HID_UCLOGIC_PARAMS_H +#define _HID_UCLOGIC_PARAMS_H + +#include +#include + +/* Types of pen in-range reporting */ +enum uclogic_params_pen_inrange { + /* Normal reports: zero - out of proximity, one - in proximity */ + UCLOGIC_PARAMS_PEN_INRANGE_NORMAL = 0, + /* Inverted reports: zero - in proximity, one - out of proximity */ + UCLOGIC_PARAMS_PEN_INRANGE_INVERTED, +}; + +/* Convert a pen in-range reporting type to a string */ +extern const char *uclogic_params_pen_inrange_to_str( + enum uclogic_params_pen_inrange inrange); + +/* + * Tablet interface's pen input parameters. + * + * Must use declarative (descriptive) language, not imperative, to simplify + * understanding and maintain consistency. + * + * Noop (preserving functionality) when filled with zeroes. + */ +struct uclogic_params_pen { + /* + * Pointer to report descriptor describing the inputs. + * Allocated with kmalloc. + */ + __u8 *desc_ptr; + /* + * Size of the report descriptor. + * Only valid, if "desc_ptr" is not NULL. + */ + unsigned int desc_size; + /* Report ID, if reports should be tweaked, zero if not */ + unsigned int id; + /* Type of in-range reporting, only valid if "id" is not zero */ + enum uclogic_params_pen_inrange inrange; +}; + +/* + * Parameters of frame control inputs of a tablet interface. + * + * Must use declarative (descriptive) language, not imperative, to simplify + * understanding and maintain consistency. + * + * Noop (preserving functionality) when filled with zeroes. + */ +struct uclogic_params_frame { + /* + * Pointer to report descriptor describing the inputs. + * Allocated with kmalloc. + */ + __u8 *desc_ptr; + /* + * Size of the report descriptor. + * Only valid, if "desc_ptr" is not NULL. + */ + unsigned int desc_size; + /* + * Report ID, if reports should be tweaked, zero if not. + */ + unsigned int id; +}; + +/* + * Tablet interface report parameters. + * + * Must use declarative (descriptive) language, not imperative, to simplify + * understanding and maintain consistency. + * + * When filled with zeros represents a "noop" configuration - passes all + * reports unchanged and lets the generic HID driver handle everything. + * + * The resulting device report descriptor is assembled from all the report + * descriptor parts referenced by the structure. No order of assembly should + * be assumed. The structure represents original device report descriptor if + * all the parts are NULL. + */ +struct uclogic_params { + /* + * True if the whole interface is invalid, false otherwise. + */ + bool invalid; + /* + * Pointer to the common part of the replacement report descriptor, + * allocated with kmalloc. NULL if no common part is needed. + * Only valid, if "invalid" is false. + */ + __u8 *desc_ptr; + /* + * Size of the common part of the replacement report descriptor. + * Only valid, if "desc_ptr" is not NULL. + */ + unsigned int desc_size; + /* + * True, if pen usage in report descriptor is invalid, when present. + * Only valid, if "invalid" is false. + */ + bool pen_unused; + /* + * Pen parameters and optional report descriptor part. + * Only valid if "pen_unused" is valid and false. + */ + struct uclogic_params_pen pen; + /* + * Frame control parameters and optional report descriptor part. + * Only valid, if "invalid" is false. + */ + struct uclogic_params_frame frame; + /* + * Bitmask matching frame controls "sub-report" flag in the second + * byte of the pen report, or zero if it's not expected. + * Only valid if both "pen" and "frame" are valid, and "frame.id" is + * not zero. + */ + __u8 pen_frame_flag; +}; + +/* Initialize a tablet interface and discover its parameters */ +extern int uclogic_params_init(struct uclogic_params *params, + struct hid_device *hdev); + +/* Tablet interface parameters *printf format string */ +#define UCLOGIC_PARAMS_FMT_STR \ + ".invalid = %s\n" \ + ".desc_ptr = %p\n" \ + ".desc_size = %u\n" \ + ".pen_unused = %s\n" \ + ".pen.desc_ptr = %p\n" \ + ".pen.desc_size = %u\n" \ + ".pen.id = %u\n" \ + ".pen.inrange = %s\n" \ + ".frame.desc_ptr = %p\n" \ + ".frame.desc_size = %u\n" \ + ".frame.id = %u\n" \ + ".pen_frame_flag = 0x%02x\n" + +/* Tablet interface parameters *printf format arguments */ +#define UCLOGIC_PARAMS_FMT_ARGS(_params) \ + ((_params)->invalid ? "true" : "false"), \ + (_params)->desc_ptr, \ + (_params)->desc_size, \ + ((_params)->pen_unused ? "true" : "false"), \ + (_params)->pen.desc_ptr, \ + (_params)->pen.desc_size, \ + (_params)->pen.id, \ + uclogic_params_pen_inrange_to_str((_params)->pen.inrange), \ + (_params)->frame.desc_ptr, \ + (_params)->frame.desc_size, \ + (_params)->frame.id, \ + (_params)->pen_frame_flag + +/* Get a replacement report descriptor for a tablet's interface. */ +extern int uclogic_params_get_desc(const struct uclogic_params *params, + __u8 **pdesc, + unsigned int *psize); + +/* Free resources used by tablet interface's parameters */ +extern void uclogic_params_cleanup(struct uclogic_params *params); + +#endif /* _HID_UCLOGIC_PARAMS_H */ From 251b427566e2cf6ec87dd479e2d3062fdbd41732 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:52 +0200 Subject: [PATCH 06/23] HID: uclogic: Re-initialize tablets on resume Re-initialize UC-Logic tablets on resume. UC-Logic tablet initialization and parameter retrieval cannot be separated for the large part, so simply discard the retrieved parameters after initialization. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-uclogic-core.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index 72a3a43766cc..8f8e445d77aa 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -178,6 +178,23 @@ static int uclogic_probe(struct hid_device *hdev, return rc; } +#ifdef CONFIG_PM +static int uclogic_resume(struct hid_device *hdev) +{ + int rc; + struct uclogic_params params; + + /* Re-initialize the device, but discard parameters */ + rc = uclogic_params_init(¶ms, hdev); + if (rc != 0) + hid_err(hdev, "failed to re-initialize the device\n"); + else + uclogic_params_cleanup(¶ms); + + return rc; +} +#endif + static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) @@ -261,6 +278,10 @@ static struct hid_driver uclogic_driver = { .raw_event = uclogic_raw_event, .input_mapping = uclogic_input_mapping, .input_configured = uclogic_input_configured, +#ifdef CONFIG_PM + .resume = uclogic_resume, + .reset_resume = uclogic_resume, +#endif }; module_hid_driver(uclogic_driver); From eecb5b845b1a724422e139db9ade356cdf3955e5 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:53 +0200 Subject: [PATCH 07/23] HID: uclogic: Designate current protocol v1 Designate the current UC-Logic tablet initialization protocol v1, in preparation for adding support for v2 protocol. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-uclogic-params.c | 50 ++++++++++++++++---------------- drivers/hid/hid-uclogic-rdesc.c | 16 +++++----- drivers/hid/hid-uclogic-rdesc.h | 20 ++++++------- 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index 2f8870d58f9a..f555db120baa 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -117,8 +117,8 @@ static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen) } /** - * uclogic_params_pen_init() - initialize tablet interface pen - * input and retrieve its parameters from the device. + * uclogic_params_pen_init_v1() - initialize tablet interface pen + * input and retrieve its parameters from the device, using v1 protocol. * * @pen: Pointer to the pen parameters to initialize (to be * cleaned up with uclogic_params_pen_cleanup()). Not modified in @@ -132,9 +132,9 @@ static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen) * Returns: * Zero, if successful. A negative errno code on error. */ -static int uclogic_params_pen_init(struct uclogic_params_pen *pen, - bool *pfound, - struct hid_device *hdev) +static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, + bool *pfound, + struct hid_device *hdev) { int rc; bool found = false; @@ -202,8 +202,8 @@ static int uclogic_params_pen_init(struct uclogic_params_pen *pen, * Generate pen report descriptor */ desc_ptr = uclogic_rdesc_template_apply( - uclogic_rdesc_pen_template_arr, - uclogic_rdesc_pen_template_size, + uclogic_rdesc_pen_v1_template_arr, + uclogic_rdesc_pen_v1_template_size, desc_params, ARRAY_SIZE(desc_params)); if (desc_ptr == NULL) { rc = -ENOMEM; @@ -216,8 +216,8 @@ static int uclogic_params_pen_init(struct uclogic_params_pen *pen, memset(pen, 0, sizeof(*pen)); pen->desc_ptr = desc_ptr; desc_ptr = NULL; - pen->desc_size = uclogic_rdesc_pen_template_size; - pen->id = UCLOGIC_RDESC_PEN_ID; + pen->desc_size = uclogic_rdesc_pen_v1_template_size; + pen->id = UCLOGIC_RDESC_PEN_V1_ID; pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED; found = true; finish: @@ -280,8 +280,8 @@ static int uclogic_params_frame_init_with_desc( } /** - * uclogic_params_frame_init_buttonpad() - initialize abstract buttonpad - * on a tablet interface. + * uclogic_params_frame_init_v1_buttonpad() - initialize abstract buttonpad + * on a v1 tablet interface. * * @frame: Pointer to the frame parameters to initialize (to be cleaned * up with uclogic_params_frame_cleanup()). Not modified in case @@ -295,7 +295,7 @@ static int uclogic_params_frame_init_with_desc( * Returns: * Zero, if successful. A negative errno code on error. */ -static int uclogic_params_frame_init_buttonpad( +static int uclogic_params_frame_init_v1_buttonpad( struct uclogic_params_frame *frame, bool *pfound, struct hid_device *hdev) @@ -335,9 +335,9 @@ static int uclogic_params_frame_init_buttonpad( hid_dbg(hdev, "generic buttons enabled\n"); rc = uclogic_params_frame_init_with_desc( frame, - uclogic_rdesc_buttonpad_arr, - uclogic_rdesc_buttonpad_size, - UCLOGIC_RDESC_BUTTONPAD_ID); + uclogic_rdesc_buttonpad_v1_arr, + uclogic_rdesc_buttonpad_v1_size, + UCLOGIC_RDESC_BUTTONPAD_V1_ID); if (rc != 0) goto cleanup; found = true; @@ -577,23 +577,23 @@ static int uclogic_params_huion_init(struct uclogic_params *params, goto output; } - /* Try to probe pen parameters */ - rc = uclogic_params_pen_init(&p.pen, &found, hdev); + /* Try to probe v1 pen parameters */ + rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); if (rc != 0) { hid_err(hdev, - "failed probing pen parameters: %d\n", rc); + "failed probing pen v1 parameters: %d\n", rc); goto cleanup; } else if (found) { - hid_dbg(hdev, "pen parameters found\n"); - /* Try to probe buttonpad */ - rc = uclogic_params_frame_init_buttonpad( + hid_dbg(hdev, "pen v1 parameters found\n"); + /* Try to probe v1 buttonpad */ + rc = uclogic_params_frame_init_v1_buttonpad( &p.frame, &found, hdev); if (rc != 0) { hid_err(hdev, "v1 buttonpad probing failed: %d\n", rc); goto cleanup; } - hid_dbg(hdev, "buttonpad parameters%s found\n", + hid_dbg(hdev, "buttonpad v1 parameters%s found\n", (found ? "" : " not")); if (found) { /* Set bitmask marking frame reports */ @@ -601,7 +601,7 @@ static int uclogic_params_huion_init(struct uclogic_params *params, } goto output; } - hid_dbg(hdev, "pen parameters not found\n"); + hid_dbg(hdev, "pen v1 parameters not found\n"); uclogic_params_init_invalid(&p); @@ -776,8 +776,8 @@ int uclogic_params_init(struct uclogic_params *params, USB_DEVICE_ID_UGEE_TABLET_EX07S): /* If this is the pen interface */ if (bInterfaceNumber == 1) { - /* Probe pen parameters */ - rc = uclogic_params_pen_init(&p.pen, &found, hdev); + /* Probe v1 pen parameters */ + rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); if (rc != 0) { hid_err(hdev, "pen probing failed: %d\n", rc); goto cleanup; diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c index e9053d28f4e0..359e72394d83 100644 --- a/drivers/hid/hid-uclogic-rdesc.c +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -531,8 +531,8 @@ __u8 uclogic_rdesc_twha60_fixed1_arr[] = { const size_t uclogic_rdesc_twha60_fixed1_size = sizeof(uclogic_rdesc_twha60_fixed1_arr); -/* Fixed report descriptor template for (tweaked) pen reports */ -const __u8 uclogic_rdesc_pen_template_arr[] = { +/* Fixed report descriptor template for (tweaked) v1 pen reports */ +const __u8 uclogic_rdesc_pen_v1_template_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -582,8 +582,8 @@ const __u8 uclogic_rdesc_pen_template_arr[] = { 0xC0 /* End Collection */ }; -const size_t uclogic_rdesc_pen_template_size = - sizeof(uclogic_rdesc_pen_template_arr); +const size_t uclogic_rdesc_pen_v1_template_size = + sizeof(uclogic_rdesc_pen_v1_template_arr); /** * Expand to the contents of a generic buttonpad report descriptor. @@ -631,12 +631,12 @@ const size_t uclogic_rdesc_pen_template_size = 0xC0, /* End Collection, */ \ 0xC0 /* End Collection */ -/* Fixed report descriptor for (tweaked) buttonpad reports */ -const __u8 uclogic_rdesc_buttonpad_arr[] = { +/* Fixed report descriptor for (tweaked) v1 buttonpad reports */ +const __u8 uclogic_rdesc_buttonpad_v1_arr[] = { UCLOGIC_RDESC_BUTTONPAD_BYTES(20) }; -const size_t uclogic_rdesc_buttonpad_size = - sizeof(uclogic_rdesc_buttonpad_arr); +const size_t uclogic_rdesc_buttonpad_v1_size = + sizeof(uclogic_rdesc_buttonpad_v1_arr); /** * uclogic_rdesc_template_apply() - apply report descriptor parameters to a diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h index 610575879522..cf03b98ae7cb 100644 --- a/drivers/hid/hid-uclogic-rdesc.h +++ b/drivers/hid/hid-uclogic-rdesc.h @@ -100,18 +100,18 @@ enum uclogic_rdesc_pen_ph_id { #define UCLOGIC_RDESC_PEN_PH(_ID) \ UCLOGIC_RDESC_PH_HEAD, UCLOGIC_RDESC_PEN_PH_ID_##_ID -/* Report ID for pen reports */ -#define UCLOGIC_RDESC_PEN_ID 0x07 +/* Report ID for v1 pen reports */ +#define UCLOGIC_RDESC_PEN_V1_ID 0x07 -/* Fixed report descriptor template for (tweaked) pen reports */ -extern const __u8 uclogic_rdesc_pen_template_arr[]; -extern const size_t uclogic_rdesc_pen_template_size; +/* Fixed report descriptor template for (tweaked) v1 pen reports */ +extern const __u8 uclogic_rdesc_pen_v1_template_arr[]; +extern const size_t uclogic_rdesc_pen_v1_template_size; -/* Fixed report descriptor for (tweaked) buttonpad reports */ -extern const __u8 uclogic_rdesc_buttonpad_arr[]; -extern const size_t uclogic_rdesc_buttonpad_size; +/* Fixed report descriptor for (tweaked) v1 buttonpad reports */ +extern const __u8 uclogic_rdesc_buttonpad_v1_arr[]; +extern const size_t uclogic_rdesc_buttonpad_v1_size; -/* Report ID for tweaked buttonpad reports */ -#define UCLOGIC_RDESC_BUTTONPAD_ID 0xf7 +/* Report ID for tweaked v1 buttonpad reports */ +#define UCLOGIC_RDESC_BUTTONPAD_V1_ID 0xf7 #endif /* _HID_UCLOGIC_RDESC_H */ From 01309e29eb95c16bd48984f2589fad0cbf5e27d1 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:54 +0200 Subject: [PATCH 08/23] HID: uclogic: Support in-range reporting emulation Newer UC-Logic tablets, such as ones made by Huion have stopped reporting in-range state, but they're otherwise worthy tablets. The manufacturer was notified of the problem and promised to fix this in the future. Meanwhile, detect pen coming in range, and emulate the reports to the userspace, to make the tablets useable. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-uclogic-core.c | 54 ++++++++++++++++++++++++++++++++ drivers/hid/hid-uclogic-params.c | 2 ++ drivers/hid/hid-uclogic-params.h | 2 ++ 3 files changed, 58 insertions(+) diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index 8f8e445d77aa..206642802ca5 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "usbhid/usbhid.h" #include "hid-uclogic-params.h" @@ -32,8 +33,40 @@ struct uclogic_drvdata { * Only valid if desc_ptr is not NULL */ unsigned int desc_size; + /* Pen input device */ + struct input_dev *pen_input; + /* In-range timer */ + struct timer_list inrange_timer; }; +/** + * uclogic_inrange_timeout - handle pen in-range state timeout. + * Emulate input events normally generated when pen goes out of range for + * tablets which don't report that. + * + * @t: The timer the timeout handler is attached to, stored in a struct + * uclogic_drvdata. + */ +static void uclogic_inrange_timeout(struct timer_list *t) +{ + struct uclogic_drvdata *drvdata = from_timer(drvdata, t, + inrange_timer); + struct input_dev *input = drvdata->pen_input; + + if (input == NULL) + return; + input_report_abs(input, ABS_PRESSURE, 0); + /* If BTN_TOUCH state is changing */ + if (test_bit(BTN_TOUCH, input->key)) { + input_event(input, EV_MSC, MSC_SCAN, + /* Digitizer Tip Switch usage */ + 0xd0042); + input_report_key(input, BTN_TOUCH, 0); + } + input_report_key(input, BTN_TOOL_PEN, 0); + input_sync(input); +} + static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { @@ -67,6 +100,8 @@ static int uclogic_input_mapping(struct hid_device *hdev, static int uclogic_input_configured(struct hid_device *hdev, struct hid_input *hi) { + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + struct uclogic_params *params = &drvdata->params; char *name; const char *suffix = NULL; struct hid_field *field; @@ -76,6 +111,15 @@ static int uclogic_input_configured(struct hid_device *hdev, if (!hi->report) return 0; + /* + * If this is the input corresponding to the pen report + * in need of tweaking. + */ + if (hi->report->id == params->pen.id) { + /* Remember the input device so we can simulate events */ + drvdata->pen_input = hi->input; + } + field = hi->report->field[0]; switch (field->application) { @@ -130,6 +174,7 @@ static int uclogic_probe(struct hid_device *hdev, rc = -ENOMEM; goto failure; } + timer_setup(&drvdata->inrange_timer, uclogic_inrange_timeout, 0); hid_set_drvdata(hdev, drvdata); /* Initialize the device and retrieve interface parameters */ @@ -220,6 +265,14 @@ static int uclogic_raw_event(struct hid_device *hdev, /* Invert the in-range bit */ data[1] ^= 0x40; } + /* If we need to emulate in-range detection */ + if (params->pen.inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) { + /* Set in-range bit */ + data[1] |= 0x40; + /* (Re-)start in-range timeout */ + mod_timer(&drvdata->inrange_timer, + jiffies + msecs_to_jiffies(100)); + } } return 0; @@ -229,6 +282,7 @@ static void uclogic_remove(struct hid_device *hdev) { struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + del_timer_sync(&drvdata->inrange_timer); hid_hw_stop(hdev); kfree(drvdata->desc_ptr); uclogic_params_cleanup(&drvdata->params); diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index f555db120baa..b5e4d99c6771 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -36,6 +36,8 @@ const char *uclogic_params_pen_inrange_to_str( return "normal"; case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED: return "inverted"; + case UCLOGIC_PARAMS_PEN_INRANGE_NONE: + return "none"; default: return NULL; } diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h index 4c78d9dd0576..665954d6ba57 100644 --- a/drivers/hid/hid-uclogic-params.h +++ b/drivers/hid/hid-uclogic-params.h @@ -25,6 +25,8 @@ enum uclogic_params_pen_inrange { UCLOGIC_PARAMS_PEN_INRANGE_NORMAL = 0, /* Inverted reports: zero - in proximity, one - out of proximity */ UCLOGIC_PARAMS_PEN_INRANGE_INVERTED, + /* No reports */ + UCLOGIC_PARAMS_PEN_INRANGE_NONE, }; /* Convert a pen in-range reporting type to a string */ From 59f2e0fca8ca84f02a718c0f3cd72990d03545be Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:55 +0200 Subject: [PATCH 09/23] HID: uclogic: Support fragmented high-res reports Support parsing fragmented high-resolution reports in hid-uclogic to support v2 reporting protocol. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-uclogic-core.c | 25 +++++++++++++++++++++++++ drivers/hid/hid-uclogic-params.h | 8 ++++++++ 2 files changed, 33 insertions(+) diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index 206642802ca5..81693183d647 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -265,6 +265,31 @@ static int uclogic_raw_event(struct hid_device *hdev, /* Invert the in-range bit */ data[1] ^= 0x40; } + /* + * If report contains fragmented high-resolution pen + * coordinates + */ + if (size >= 10 && params->pen.fragmented_hires) { + u8 pressure_low_byte; + u8 pressure_high_byte; + + /* Lift pressure bytes */ + pressure_low_byte = data[6]; + pressure_high_byte = data[7]; + /* + * Move Y coord to make space for high-order X + * coord byte + */ + data[6] = data[5]; + data[5] = data[4]; + /* Move high-order X coord byte */ + data[4] = data[8]; + /* Move high-order Y coord byte */ + data[7] = data[9]; + /* Place pressure bytes */ + data[8] = pressure_low_byte; + data[9] = pressure_high_byte; + } /* If we need to emulate in-range detection */ if (params->pen.inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) { /* Set in-range bit */ diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h index 665954d6ba57..1060f70d647d 100644 --- a/drivers/hid/hid-uclogic-params.h +++ b/drivers/hid/hid-uclogic-params.h @@ -56,6 +56,12 @@ struct uclogic_params_pen { unsigned int id; /* Type of in-range reporting, only valid if "id" is not zero */ enum uclogic_params_pen_inrange inrange; + /* + * True, if reports include fragmented high resolution coords, with + * high-order X and then Y bytes following the pressure field. + * Only valid if "id" is not zero. + */ + bool fragmented_hires; }; /* @@ -151,6 +157,7 @@ extern int uclogic_params_init(struct uclogic_params *params, ".pen.desc_size = %u\n" \ ".pen.id = %u\n" \ ".pen.inrange = %s\n" \ + ".pen.fragmented_hires = %s\n" \ ".frame.desc_ptr = %p\n" \ ".frame.desc_size = %u\n" \ ".frame.id = %u\n" \ @@ -166,6 +173,7 @@ extern int uclogic_params_init(struct uclogic_params *params, (_params)->pen.desc_size, \ (_params)->pen.id, \ uclogic_params_pen_inrange_to_str((_params)->pen.inrange), \ + ((_params)->pen.fragmented_hires ? "true" : "false"), \ (_params)->frame.desc_ptr, \ (_params)->frame.desc_size, \ (_params)->frame.id, \ From 2c3a88c64cb62cc59010359dbfc97f734e98d683 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:56 +0200 Subject: [PATCH 10/23] HID: uclogic: Support v2 protocol Add support for UC-Logic v2 protocol to hid-uclogic. This adds support for a bunch of new Huion models. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-uclogic-params.c | 201 +++++++++++++++++++++++++++++++ drivers/hid/hid-uclogic-rdesc.c | 63 ++++++++++ drivers/hid/hid-uclogic-rdesc.h | 14 +++ 3 files changed, 278 insertions(+) diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index b5e4d99c6771..132663a87f38 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -231,6 +231,151 @@ static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, return rc; } +/** + * uclogic_params_get_le24() - get a 24-bit little-endian number from a + * buffer. + * + * @p: The pointer to the number buffer. + * + * Returns: + * The retrieved number + */ +static s32 uclogic_params_get_le24(const void *p) +{ + const __u8 *b = p; + return b[0] | (b[1] << 8UL) | (b[2] << 16UL); +} + +/** + * uclogic_params_pen_init_v2() - initialize tablet interface pen + * input and retrieve its parameters from the device, using v2 protocol. + * + * @pen: Pointer to the pen parameters to initialize (to be + * cleaned up with uclogic_params_pen_cleanup()). Not modified in + * case of error, or if parameters are not found. Cannot be NULL. + * @pfound: Location for a flag which is set to true if the parameters + * were found, and to false if not (e.g. device was + * incompatible). 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_pen_init_v2(struct uclogic_params_pen *pen, + bool *pfound, + struct hid_device *hdev) +{ + int rc; + bool found = false; + /* Buffer for (part of) the string descriptor */ + __u8 *buf = NULL; + /* Descriptor length required */ + const int len = 18; + s32 resolution; + /* Pen report descriptor template parameters */ + s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; + __u8 *desc_ptr = NULL; + + /* Check arguments */ + if (pen == NULL || pfound == NULL || hdev == NULL) { + rc = -EINVAL; + goto cleanup; + } + + /* + * Read string descriptor containing pen input parameters. + * The specific string descriptor and data were discovered by sniffing + * the Windows driver traffic. + * NOTE: This enables fully-functional tablet mode. + */ + rc = uclogic_params_get_str_desc(&buf, hdev, 200, len); + if (rc == -EPIPE) { + hid_dbg(hdev, + "string descriptor with pen parameters not found, assuming not compatible\n"); + goto finish; + } else if (rc < 0) { + hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); + goto cleanup; + } else if (rc != len) { + hid_dbg(hdev, + "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n", + rc, len); + goto finish; + } else { + size_t i; + /* + * Check it's not just a catch-all UTF-16LE-encoded ASCII + * string (such as the model name) some tablets put into all + * unknown string descriptors. + */ + for (i = 2; + i < len && + (buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0); + i += 2); + if (i >= len) { + hid_dbg(hdev, + "string descriptor with pen parameters seems to contain only text, assuming not compatible\n"); + goto finish; + } + } + + /* + * Fill report descriptor parameters from the string descriptor + */ + desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = + uclogic_params_get_le24(buf + 2); + desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = + uclogic_params_get_le24(buf + 5); + desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = + get_unaligned_le16(buf + 8); + resolution = get_unaligned_le16(buf + 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(buf); + buf = NULL; + + /* + * Generate pen report descriptor + */ + desc_ptr = uclogic_rdesc_template_apply( + uclogic_rdesc_pen_v2_template_arr, + uclogic_rdesc_pen_v2_template_size, + desc_params, ARRAY_SIZE(desc_params)); + if (desc_ptr == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + /* + * Fill-in the parameters + */ + memset(pen, 0, sizeof(*pen)); + pen->desc_ptr = desc_ptr; + desc_ptr = NULL; + pen->desc_size = uclogic_rdesc_pen_v2_template_size; + pen->id = UCLOGIC_RDESC_PEN_V2_ID; + pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE; + pen->fragmented_hires = true; + found = true; +finish: + *pfound = found; + rc = 0; +cleanup: + kfree(desc_ptr); + kfree(buf); + return rc; +} + /** * uclogic_params_frame_cleanup - free resources used by struct * uclogic_params_frame (tablet interface's frame controls input parameters). @@ -560,11 +705,15 @@ static int uclogic_params_huion_init(struct uclogic_params *params, struct hid_device *hdev) { int rc; + struct usb_device *udev = hid_to_usb_dev(hdev); struct usb_interface *iface = to_usb_interface(hdev->dev.parent); __u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; bool found; /* The resulting parameters (noop) */ struct uclogic_params p = {0, }; + static const char transition_ver[] = "HUION_T153_160607"; + char *ver_ptr = NULL; + const size_t ver_len = sizeof(transition_ver) + 1; /* Check arguments */ if (params == NULL || hdev == NULL) { @@ -579,6 +728,57 @@ static int uclogic_params_huion_init(struct uclogic_params *params, goto output; } + /* Try to get firmware version */ + ver_ptr = kzalloc(ver_len, GFP_KERNEL); + if (ver_ptr == NULL) { + rc = -ENOMEM; + goto cleanup; + } + rc = usb_string(udev, 201, ver_ptr, ver_len); + if (ver_ptr == NULL) { + rc = -ENOMEM; + goto cleanup; + } + if (rc == -EPIPE) { + *ver_ptr = '\0'; + } else if (rc < 0) { + hid_err(hdev, + "failed retrieving Huion firmware version: %d\n", rc); + goto cleanup; + } + + /* If this is a transition firmware */ + if (strcmp(ver_ptr, transition_ver) == 0) { + hid_dbg(hdev, + "transition firmware detected, not probing pen v2 parameters\n"); + } else { + /* Try to probe v2 pen parameters */ + rc = uclogic_params_pen_init_v2(&p.pen, &found, hdev); + if (rc != 0) { + hid_err(hdev, + "failed probing pen v2 parameters: %d\n", rc); + goto cleanup; + } else if (found) { + hid_dbg(hdev, "pen v2 parameters found\n"); + /* Create v2 buttonpad parameters */ + rc = uclogic_params_frame_init_with_desc( + &p.frame, + uclogic_rdesc_buttonpad_v2_arr, + uclogic_rdesc_buttonpad_v2_size, + UCLOGIC_RDESC_BUTTONPAD_V2_ID); + if (rc != 0) { + hid_err(hdev, + "failed creating v2 buttonpad parameters: %d\n", + rc); + goto cleanup; + } + /* Set bitmask marking frame reports in pen reports */ + p.pen_frame_flag = 0x20; + goto output; + } + hid_dbg(hdev, "pen v2 parameters not found\n"); + } + /* Try to probe v1 pen parameters */ rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); if (rc != 0) { @@ -613,6 +813,7 @@ static int uclogic_params_huion_init(struct uclogic_params *params, memset(&p, 0, sizeof(p)); rc = 0; cleanup: + kfree(ver_ptr); uclogic_params_cleanup(&p); return rc; } diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c index 359e72394d83..ef1d3cf918a4 100644 --- a/drivers/hid/hid-uclogic-rdesc.c +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -585,6 +585,62 @@ const __u8 uclogic_rdesc_pen_v1_template_arr[] = { const size_t uclogic_rdesc_pen_v1_template_size = sizeof(uclogic_rdesc_pen_v1_template_arr); +/* Fixed report descriptor template for (tweaked) v2 pen reports */ +const __u8 uclogic_rdesc_pen_v2_template_arr[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x08, /* Report ID (8), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x75, 0x18, /* Report Size (24), */ + 0x34, /* Physical Minimum (0), */ + 0x09, 0x30, /* Usage (X), */ + 0x27, UCLOGIC_RDESC_PEN_PH(X_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x47, UCLOGIC_RDESC_PEN_PH(X_PM), + /* Physical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x27, UCLOGIC_RDESC_PEN_PH(Y_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x47, UCLOGIC_RDESC_PEN_PH(Y_PM), + /* Physical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x75, 0x10, /* Report Size (16), */ + 0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +const size_t uclogic_rdesc_pen_v2_template_size = + sizeof(uclogic_rdesc_pen_v2_template_arr); + /** * Expand to the contents of a generic buttonpad report descriptor. * @@ -638,6 +694,13 @@ const __u8 uclogic_rdesc_buttonpad_v1_arr[] = { const size_t uclogic_rdesc_buttonpad_v1_size = sizeof(uclogic_rdesc_buttonpad_v1_arr); +/* Fixed report descriptor for (tweaked) v2 buttonpad reports */ +const __u8 uclogic_rdesc_buttonpad_v2_arr[] = { + UCLOGIC_RDESC_BUTTONPAD_BYTES(52) +}; +const size_t uclogic_rdesc_buttonpad_v2_size = + sizeof(uclogic_rdesc_buttonpad_v2_arr); + /** * uclogic_rdesc_template_apply() - apply report descriptor parameters to a * report descriptor template, creating a report descriptor. Copies the diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h index cf03b98ae7cb..f205254a733c 100644 --- a/drivers/hid/hid-uclogic-rdesc.h +++ b/drivers/hid/hid-uclogic-rdesc.h @@ -107,6 +107,13 @@ enum uclogic_rdesc_pen_ph_id { extern const __u8 uclogic_rdesc_pen_v1_template_arr[]; extern const size_t uclogic_rdesc_pen_v1_template_size; +/* Report ID for v2 pen reports */ +#define UCLOGIC_RDESC_PEN_V2_ID 0x08 + +/* Fixed report descriptor template for (tweaked) v2 pen reports */ +extern const __u8 uclogic_rdesc_pen_v2_template_arr[]; +extern const size_t uclogic_rdesc_pen_v2_template_size; + /* Fixed report descriptor for (tweaked) v1 buttonpad reports */ extern const __u8 uclogic_rdesc_buttonpad_v1_arr[]; extern const size_t uclogic_rdesc_buttonpad_v1_size; @@ -114,4 +121,11 @@ extern const size_t uclogic_rdesc_buttonpad_v1_size; /* Report ID for tweaked v1 buttonpad reports */ #define UCLOGIC_RDESC_BUTTONPAD_V1_ID 0xf7 +/* Fixed report descriptor for (tweaked) v2 buttonpad reports */ +extern const __u8 uclogic_rdesc_buttonpad_v2_arr[]; +extern const size_t uclogic_rdesc_buttonpad_v2_size; + +/* Report ID for tweaked v2 buttonpad reports */ +#define UCLOGIC_RDESC_BUTTONPAD_V2_ID 0xf7 + #endif /* _HID_UCLOGIC_RDESC_H */ From 0c15efe9ef7f2042234485ad3a7b09567b9821f6 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:57 +0200 Subject: [PATCH 11/23] HID: uclogic: Add support for Ugee 2150 Add support for Ugee 2150 to hid-uclogic. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-uclogic-core.c | 2 ++ drivers/hid/hid-uclogic-params.c | 2 ++ 3 files changed, 5 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index f66553157be5..304476200c28 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1137,6 +1137,7 @@ #define USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3 0x3031 #define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81 0x0081 #define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45 0x0045 +#define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47 0x0047 #define USB_DEVICE_ID_YIYNOVA_TABLET 0x004d #define USB_VENDOR_ID_UGEE 0x28bd diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index 81693183d647..33333a3e5ffe 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -338,6 +338,8 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index 132663a87f38..f57bf2698374 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -969,6 +969,8 @@ int uclogic_params_init(struct uclogic_params *params, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3): case VID_PID(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45): + case VID_PID(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47): rc = uclogic_params_huion_init(&p, hdev); if (rc != 0) goto cleanup; From 9c17f7353928ddb0ab5ee4cb8b29d7ddf8f8eab7 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:58 +0200 Subject: [PATCH 12/23] HID: uclogic: Add support for Ugee M540 Add support for Ugee M540 to hid-uclogic. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-uclogic-params.c | 26 +++++++++++++++++++++++--- drivers/hid/hid-uclogic-rdesc.h | 3 +++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index f57bf2698374..72a073ae454c 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -897,9 +897,29 @@ int uclogic_params_init(struct uclogic_params *params, break; case VID_PID(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U): - rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed); - if (rc != 0) - goto cleanup; + if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) { + if (bInterfaceNumber == 0) { + /* Try to probe v1 pen parameters */ + rc = uclogic_params_pen_init_v1(&p.pen, + &found, hdev); + if (rc != 0) { + hid_err(hdev, + "pen probing failed: %d\n", + rc); + goto cleanup; + } + if (!found) { + hid_warn(hdev, + "pen parameters not found"); + } + } else { + uclogic_params_init_invalid(&p); + } + } else { + rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed); + if (rc != 0) + goto cleanup; + } break; case VID_PID(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U): diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h index f205254a733c..cc5f3dd3804a 100644 --- a/drivers/hid/hid-uclogic-rdesc.h +++ b/drivers/hid/hid-uclogic-rdesc.h @@ -34,6 +34,9 @@ extern const size_t uclogic_rdesc_wp5540u_fixed_size; extern __u8 uclogic_rdesc_wp8060u_fixed_arr[]; extern const size_t uclogic_rdesc_wp8060u_fixed_size; +/* Size of the original descriptor of the new WP5540U tablet */ +#define UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE 232 + /* Size of the original descriptor of WP1062 tablet */ #define UCLOGIC_RDESC_WP1062_ORIG_SIZE 254 From 1ee7c6852351fb0a025fe87e6a2570df4e4e24de Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:59 +0200 Subject: [PATCH 13/23] HID: uclogic: Add support for Ugee EX07S frame controls Add proper support for Ugee EX07(S) frame controls to hid-uclogic. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-uclogic-params.c | 32 ++++++++++++++++++++++++++++++-- drivers/hid/hid-uclogic-rdesc.c | 27 +++++++++++++++++++++++++++ drivers/hid/hid-uclogic-rdesc.h | 4 ++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index 72a073ae454c..b22815cec06d 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -997,8 +997,6 @@ int uclogic_params_init(struct uclogic_params *params, break; case VID_PID(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610): - case VID_PID(USB_VENDOR_ID_UGEE, - USB_DEVICE_ID_UGEE_TABLET_EX07S): /* If this is the pen interface */ if (bInterfaceNumber == 1) { /* Probe v1 pen parameters */ @@ -1015,6 +1013,36 @@ int uclogic_params_init(struct uclogic_params *params, /* TODO: Consider marking the interface invalid */ uclogic_params_init_with_pen_unused(&p); } + break; + case VID_PID(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_TABLET_EX07S): + /* Ignore non-pen interfaces */ + if (bInterfaceNumber != 1) { + uclogic_params_init_invalid(&p); + break; + } + + rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); + if (rc != 0) { + hid_err(hdev, "pen probing failed: %d\n", rc); + goto cleanup; + } else if (found) { + rc = uclogic_params_frame_init_with_desc( + &p.frame, + uclogic_rdesc_ugee_ex07_buttonpad_arr, + uclogic_rdesc_ugee_ex07_buttonpad_size, + 0); + if (rc != 0) { + hid_err(hdev, + "failed creating buttonpad parameters: %d\n", + rc); + goto cleanup; + } + } else { + hid_warn(hdev, "pen parameters not found"); + uclogic_params_init_invalid(&p); + } + break; } diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c index ef1d3cf918a4..2ab8747e87b7 100644 --- a/drivers/hid/hid-uclogic-rdesc.c +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -701,6 +701,33 @@ const __u8 uclogic_rdesc_buttonpad_v2_arr[] = { const size_t uclogic_rdesc_buttonpad_v2_size = sizeof(uclogic_rdesc_buttonpad_v2_arr); +/* Fixed report descriptor for Ugee EX07 buttonpad */ +const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x07, /* Usage (Keypad), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x06, /* Report ID (6), */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x39, /* Usage (Tablet Function Keys), */ + 0xA0, /* Collection (Physical), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x75, 0x01, /* Report Size (1), */ + 0x19, 0x03, /* Usage Minimum (03h), */ + 0x29, 0x06, /* Usage Maximum (06h), */ + 0x95, 0x04, /* Report Count (4), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x1A, /* Report Count (26), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x02, /* Usage Maximum (02h), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; +const size_t uclogic_rdesc_ugee_ex07_buttonpad_size = + sizeof(uclogic_rdesc_ugee_ex07_buttonpad_arr); + /** * uclogic_rdesc_template_apply() - apply report descriptor parameters to a * report descriptor template, creating a report descriptor. Copies the diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h index cc5f3dd3804a..3cb6e1725f43 100644 --- a/drivers/hid/hid-uclogic-rdesc.h +++ b/drivers/hid/hid-uclogic-rdesc.h @@ -131,4 +131,8 @@ extern const size_t uclogic_rdesc_buttonpad_v2_size; /* Report ID for tweaked v2 buttonpad reports */ #define UCLOGIC_RDESC_BUTTONPAD_V2_ID 0xf7 +/* Fixed report descriptor for Ugee EX07 buttonpad */ +extern const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[]; +extern const size_t uclogic_rdesc_ugee_ex07_buttonpad_size; + #endif /* _HID_UCLOGIC_RDESC_H */ From c3e5a67c46e560faf66a63d9c10514eb4d2a0432 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:00 +0200 Subject: [PATCH 14/23] HID: uclogic: Add support for XP-Pen Star G540 Add support for XP-Pen Star G540 to hid-uclogic. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-uclogic-core.c | 2 ++ drivers/hid/hid-uclogic-params.c | 2 ++ 3 files changed, 5 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 304476200c28..4da33b38606c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1141,6 +1141,7 @@ #define USB_DEVICE_ID_YIYNOVA_TABLET 0x004d #define USB_VENDOR_ID_UGEE 0x28bd +#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075 #define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071 #define USB_VENDOR_ID_UNITEC 0x227d diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index 33333a3e5ffe..42c93e3a8f1b 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -346,6 +346,8 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540) }, { } }; MODULE_DEVICE_TABLE(hid, uclogic_devices); diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index b22815cec06d..ac42f9117431 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -997,6 +997,8 @@ int uclogic_params_init(struct uclogic_params *params, break; case VID_PID(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610): + case VID_PID(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540): /* If this is the pen interface */ if (bInterfaceNumber == 1) { /* Probe v1 pen parameters */ From 492a9e9a3c439a2ff486c60213fa5da3f465c2e9 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:01 +0200 Subject: [PATCH 15/23] HID: uclogic: Add support for XP-Pen Star G640 Add support for XP-Pen Star G640 to hid-uclogic. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-uclogic-core.c | 2 ++ drivers/hid/hid-uclogic-params.c | 2 ++ 3 files changed, 5 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4da33b38606c..cadc8d4d6a4d 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1142,6 +1142,7 @@ #define USB_VENDOR_ID_UGEE 0x28bd #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075 +#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 0x0094 #define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071 #define USB_VENDOR_ID_UNITEC 0x227d diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index 42c93e3a8f1b..5fdd8919902d 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -348,6 +348,8 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UGEE_TABLET_EX07S) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640) }, { } }; MODULE_DEVICE_TABLE(hid, uclogic_devices); diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index ac42f9117431..709eeb15e7de 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -999,6 +999,8 @@ int uclogic_params_init(struct uclogic_params *params, USB_DEVICE_ID_UGTIZER_TABLET_GP0610): case VID_PID(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540): + case VID_PID(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640): /* If this is the pen interface */ if (bInterfaceNumber == 1) { /* Probe v1 pen parameters */ From 08367be171b0b7d6ff030a351a58d34f77803685 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:02 +0200 Subject: [PATCH 16/23] HID: uclogic: Add support for XP-Pen Deco 01 Add support for XP-Pen Deco 01 to hid-uclogic. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-uclogic-core.c | 2 ++ drivers/hid/hid-uclogic-params.c | 23 +++++++++++++++++++++ drivers/hid/hid-uclogic-rdesc.c | 35 ++++++++++++++++++++++++++++++++ drivers/hid/hid-uclogic-rdesc.h | 4 ++++ 5 files changed, 65 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index cadc8d4d6a4d..b38be58d61b6 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1143,6 +1143,7 @@ #define USB_VENDOR_ID_UGEE 0x28bd #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 0x0094 +#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042 #define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071 #define USB_VENDOR_ID_UNITEC 0x227d diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index 5fdd8919902d..e4324ad86e58 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -350,6 +350,8 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) }, { } }; MODULE_DEVICE_TABLE(hid, uclogic_devices); diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index 709eeb15e7de..9b0fc4dd31af 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -1018,6 +1018,29 @@ int uclogic_params_init(struct uclogic_params *params, uclogic_params_init_with_pen_unused(&p); } break; + case VID_PID(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01): + /* If this is the pen and frame interface */ + if (bInterfaceNumber == 1) { + /* Probe v1 pen parameters */ + rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); + if (rc != 0) { + hid_err(hdev, "pen probing failed: %d\n", rc); + goto cleanup; + } + /* Initialize frame parameters */ + rc = uclogic_params_frame_init_with_desc( + &p.frame, + uclogic_rdesc_xppen_deco01_frame_arr, + uclogic_rdesc_xppen_deco01_frame_size, + 0); + if (rc != 0) + goto cleanup; + } else { + /* TODO: Consider marking the interface invalid */ + uclogic_params_init_with_pen_unused(&p); + } + break; case VID_PID(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S): /* Ignore non-pen interfaces */ diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c index 2ab8747e87b7..73996586993f 100644 --- a/drivers/hid/hid-uclogic-rdesc.c +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -728,6 +728,41 @@ const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[] = { const size_t uclogic_rdesc_ugee_ex07_buttonpad_size = sizeof(uclogic_rdesc_ugee_ex07_buttonpad_arr); +/* Fixed report descriptor for XP-Pen Deco 01 frame controls */ +const __u8 uclogic_rdesc_xppen_deco01_frame_arr[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x07, /* Usage (Keypad), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x06, /* Report ID (6), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x39, /* Usage (Tablet Function Keys), */ + 0xA0, /* Collection (Physical), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x08, /* Usage Maximum (08h), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x02, /* Input (Variable), */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x15, /* Report Count (21), */ + 0x81, 0x01, /* Input (Constant), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +const size_t uclogic_rdesc_xppen_deco01_frame_size = + sizeof(uclogic_rdesc_xppen_deco01_frame_arr); + /** * uclogic_rdesc_template_apply() - apply report descriptor parameters to a * report descriptor template, creating a report descriptor. Copies the diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h index 3cb6e1725f43..0c5bd027f155 100644 --- a/drivers/hid/hid-uclogic-rdesc.h +++ b/drivers/hid/hid-uclogic-rdesc.h @@ -135,4 +135,8 @@ extern const size_t uclogic_rdesc_buttonpad_v2_size; extern const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[]; extern const size_t uclogic_rdesc_ugee_ex07_buttonpad_size; +/* Fixed report descriptor for XP-Pen Deco 01 frame controls */ +extern const __u8 uclogic_rdesc_xppen_deco01_frame_arr[]; +extern const size_t uclogic_rdesc_xppen_deco01_frame_size; + #endif /* _HID_UCLOGIC_RDESC_H */ From fde44ac556359b0fd56e11b889686377392b7407 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:03 +0200 Subject: [PATCH 17/23] HID: uclogic: Support faking Wacom pad device ID Add support for inserting a Wacom pad device ID into hid-uclogic reports. This allows reporting dial inputs in a way compatible with the Wacom driver. Needed for Ugee G5 support in particular. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-uclogic-core.c | 10 ++++++++++ drivers/hid/hid-uclogic-params.h | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index e4324ad86e58..f5fb612daa1e 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -300,6 +300,16 @@ static int uclogic_raw_event(struct hid_device *hdev, } } + /* Tweak frame control reports, if necessary */ + if ((report->type == HID_INPUT_REPORT) && + (report->id == params->frame.id)) { + /* If need to, and can, set pad device ID for Wacom drivers */ + if (params->frame.dev_id_byte > 0 && + params->frame.dev_id_byte < size) { + data[params->frame.dev_id_byte] = 0xf; + } + } + return 0; } diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h index 1060f70d647d..4ba6ecc2b8b8 100644 --- a/drivers/hid/hid-uclogic-params.h +++ b/drivers/hid/hid-uclogic-params.h @@ -87,6 +87,13 @@ struct uclogic_params_frame { * Report ID, if reports should be tweaked, zero if not. */ unsigned int id; + /* + * Offset of the Wacom-style device ID byte in the report, to be set + * to pad device ID (0xf), for compatibility with Wacom drivers. Zero + * if no changes to the report should be made. Only valid if "id" is + * not zero. + */ + unsigned int dev_id_byte; }; /* @@ -161,6 +168,7 @@ extern int uclogic_params_init(struct uclogic_params *params, ".frame.desc_ptr = %p\n" \ ".frame.desc_size = %u\n" \ ".frame.id = %u\n" \ + ".frame.dev_id_byte = %u\n" \ ".pen_frame_flag = 0x%02x\n" /* Tablet interface parameters *printf format arguments */ @@ -177,6 +185,7 @@ extern int uclogic_params_init(struct uclogic_params *params, (_params)->frame.desc_ptr, \ (_params)->frame.desc_size, \ (_params)->frame.id, \ + (_params)->frame.dev_id_byte, \ (_params)->pen_frame_flag /* Get a replacement report descriptor for a tablet's interface. */ From 8a47670c35e2a8e70753eabd96d4f8d8b3c0eeba Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:04 +0200 Subject: [PATCH 18/23] HID: uclogic: Support Gray-coded rotary encoders Add support for converting Gray-coded rotary encoder input into dial input compatible with HID standard. Needed for Ugee G5 support. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-uclogic-core.c | 29 +++++++++++++++++++++++++++++ drivers/hid/hid-uclogic-params.h | 8 ++++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index f5fb612daa1e..dfacb04308b1 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -37,6 +37,8 @@ struct uclogic_drvdata { struct input_dev *pen_input; /* In-range timer */ struct timer_list inrange_timer; + /* Last rotary encoder state, or U8_MAX for none */ + u8 re_state; }; /** @@ -175,6 +177,7 @@ static int uclogic_probe(struct hid_device *hdev, goto failure; } timer_setup(&drvdata->inrange_timer, uclogic_inrange_timeout, 0); + drvdata->re_state = U8_MAX; hid_set_drvdata(hdev, drvdata); /* Initialize the device and retrieve interface parameters */ @@ -308,6 +311,32 @@ static int uclogic_raw_event(struct hid_device *hdev, params->frame.dev_id_byte < size) { data[params->frame.dev_id_byte] = 0xf; } + /* If need to, and can, read rotary encoder state change */ + if (params->frame.re_lsb > 0 && + params->frame.re_lsb / 8 < size) { + unsigned int byte = params->frame.re_lsb / 8; + unsigned int bit = params->frame.re_lsb % 8; + + u8 change; + u8 prev_state = drvdata->re_state; + /* Read Gray-coded state */ + u8 state = (data[byte] >> bit) & 0x3; + /* Encode state change into 2-bit signed integer */ + if ((prev_state == 1 && state == 0) || + (prev_state == 2 && state == 3)) { + change = 1; + } else if ((prev_state == 2 && state == 0) || + (prev_state == 1 && state == 3)) { + change = 3; + } else { + change = 0; + } + /* Write change */ + data[byte] = (data[byte] & ~((u8)3 << bit)) | + (change << bit); + /* Remember state */ + drvdata->re_state = state; + } } return 0; diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h index 4ba6ecc2b8b8..ba48b1c7a0e5 100644 --- a/drivers/hid/hid-uclogic-params.h +++ b/drivers/hid/hid-uclogic-params.h @@ -87,6 +87,12 @@ struct uclogic_params_frame { * Report ID, if reports should be tweaked, zero if not. */ unsigned int id; + /* + * Number of the least-significant bit of the 2-bit state of a rotary + * encoder, in the report. Cannot point to a 2-bit field crossing a + * byte boundary. Zero if not present. Only valid if "id" is not zero. + */ + unsigned int re_lsb; /* * Offset of the Wacom-style device ID byte in the report, to be set * to pad device ID (0xf), for compatibility with Wacom drivers. Zero @@ -168,6 +174,7 @@ extern int uclogic_params_init(struct uclogic_params *params, ".frame.desc_ptr = %p\n" \ ".frame.desc_size = %u\n" \ ".frame.id = %u\n" \ + ".frame.re_lsb = %u\n" \ ".frame.dev_id_byte = %u\n" \ ".pen_frame_flag = 0x%02x\n" @@ -185,6 +192,7 @@ extern int uclogic_params_init(struct uclogic_params *params, (_params)->frame.desc_ptr, \ (_params)->frame.desc_size, \ (_params)->frame.id, \ + (_params)->frame.re_lsb, \ (_params)->frame.dev_id_byte, \ (_params)->pen_frame_flag From e902ed9344873ba199093958ca7bdc3d125828f6 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:05 +0200 Subject: [PATCH 19/23] HID: uclogic: Add support for Ugee G5 Add support for Ugee G5 to hid-uclogic. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-uclogic-core.c | 2 ++ drivers/hid/hid-uclogic-params.c | 34 ++++++++++++++++++++ drivers/hid/hid-uclogic-rdesc.c | 54 ++++++++++++++++++++++++++++++++ drivers/hid/hid-uclogic-rdesc.h | 13 ++++++++ 5 files changed, 104 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index b38be58d61b6..895b79daaf7c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1144,6 +1144,7 @@ #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 0x0094 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042 +#define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074 #define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071 #define USB_VENDOR_ID_UNITEC 0x227d diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index dfacb04308b1..8fe02d81265d 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -383,6 +383,8 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_TABLET_G5) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index 9b0fc4dd31af..b582739a570a 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -1040,6 +1040,40 @@ int uclogic_params_init(struct uclogic_params *params, /* TODO: Consider marking the interface invalid */ uclogic_params_init_with_pen_unused(&p); } + break; + case VID_PID(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_TABLET_G5): + /* Ignore non-pen interfaces */ + if (bInterfaceNumber != 1) { + uclogic_params_init_invalid(&p); + break; + } + + rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); + if (rc != 0) { + hid_err(hdev, "pen probing failed: %d\n", rc); + goto cleanup; + } else if (found) { + rc = uclogic_params_frame_init_with_desc( + &p.frame, + uclogic_rdesc_ugee_g5_frame_arr, + uclogic_rdesc_ugee_g5_frame_size, + UCLOGIC_RDESC_UGEE_G5_FRAME_ID); + if (rc != 0) { + hid_err(hdev, + "failed creating buttonpad parameters: %d\n", + rc); + goto cleanup; + } + p.frame.re_lsb = + UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB; + p.frame.dev_id_byte = + UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE; + } else { + hid_warn(hdev, "pen parameters not found"); + uclogic_params_init_invalid(&p); + } + break; case VID_PID(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S): diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c index 73996586993f..bf5da6de7bba 100644 --- a/drivers/hid/hid-uclogic-rdesc.c +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -728,6 +728,60 @@ const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[] = { const size_t uclogic_rdesc_ugee_ex07_buttonpad_size = sizeof(uclogic_rdesc_ugee_ex07_buttonpad_arr); +/* Fixed report descriptor for Ugee G5 frame controls */ +const __u8 uclogic_rdesc_ugee_g5_frame_arr[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x07, /* Usage (Keypad), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x06, /* Report ID (6), */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x39, /* Usage (Tablet Function Keys), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x05, /* Usage Maximum (05h), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x05, /* Report Count (5), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x01, /* Input (Constant), */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x0A, 0xFF, 0xFF, /* Usage (FFFFh), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x0B, /* Report Count (11), */ + 0x81, 0x01, /* Input (Constant), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x38, /* Usage (Wheel), */ + 0x15, 0xFF, /* Logical Minimum (-1), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x02, /* Report Size (2), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; +const size_t uclogic_rdesc_ugee_g5_frame_size = + sizeof(uclogic_rdesc_ugee_g5_frame_arr); + /* Fixed report descriptor for XP-Pen Deco 01 frame controls */ const __u8 uclogic_rdesc_xppen_deco01_frame_arr[] = { 0x05, 0x01, /* Usage Page (Desktop), */ diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h index 0c5bd027f155..c5da51055af3 100644 --- a/drivers/hid/hid-uclogic-rdesc.h +++ b/drivers/hid/hid-uclogic-rdesc.h @@ -139,4 +139,17 @@ extern const size_t uclogic_rdesc_ugee_ex07_buttonpad_size; extern const __u8 uclogic_rdesc_xppen_deco01_frame_arr[]; extern const size_t uclogic_rdesc_xppen_deco01_frame_size; +/* Fixed report descriptor for Ugee G5 frame controls */ +extern const __u8 uclogic_rdesc_ugee_g5_frame_arr[]; +extern const size_t uclogic_rdesc_ugee_g5_frame_size; + +/* Report ID of Ugee G5 frame control reports */ +#define UCLOGIC_RDESC_UGEE_G5_FRAME_ID 0x06 + +/* Device ID byte offset in Ugee G5 frame report */ +#define UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE 0x2 + +/* Least-significant bit of Ugee G5 frame rotary encoder state */ +#define UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB 38 + #endif /* _HID_UCLOGIC_RDESC_H */ From 8547b7789c62f81350422d357fdf48bfe5d9932f Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:06 +0200 Subject: [PATCH 20/23] HID: uclogic: Do not initialize non-USB devices Do not try to initialize UC-Logic tablets if the underlying device is not a USB device, but e.g. a uhid device. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-uclogic-params.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index b582739a570a..7710d9f957da 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -826,7 +826,8 @@ static int uclogic_params_huion_init(struct uclogic_params *params, * 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. + * parameters from. Cannot be NULL. Must be using the USB low-level + * driver, i.e. be an actual USB tablet. * * Returns: * Zero, if successful. A negative errno code on error. @@ -844,7 +845,8 @@ int uclogic_params_init(struct uclogic_params *params, struct uclogic_params p = {0, }; /* Check arguments */ - if (params == NULL || hdev == NULL) { + if (params == NULL || hdev == NULL || + !hid_is_using_ll_driver(hdev, &usb_hid_driver)) { rc = -EINVAL; goto cleanup; } From c970f8453fe1cd33d1969b807a7217af391cfdba Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:07 +0200 Subject: [PATCH 21/23] HID: Remove hid-uclogic entries from hid_have_special_driver Tested with a couple UC-Logic tablets and it seems to work fine. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-quirks.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 78c836e5ce51..4a6c7df9fc37 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -674,23 +674,6 @@ static const struct hid_device_id hid_have_special_driver[] = { #if IS_ENABLED(CONFIG_HID_TWINHAN) { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) }, #endif -#if IS_ENABLED(CONFIG_HID_UCLOGIC) - { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, -#endif #if IS_ENABLED(CONFIG_HID_UDRAW_PS3) { HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) }, #endif From 3c261a14f5204809418748bd315f7d21087a150d Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:08 +0200 Subject: [PATCH 22/23] HID: Remove KYE tablets from hid_have_special_driver Tested with one KYE tablet and it seems to work fine. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-quirks.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 4a6c7df9fc37..38ad3e00d82f 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -412,11 +412,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_MANTICORE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GX_IMPERATOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, - { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) }, - { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, - { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, - { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912) }, #endif #if IS_ENABLED(CONFIG_HID_LCPOWER) { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000) }, From 24b2f66628ac7e0de06666501f682d77c6c0485d Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:09 +0200 Subject: [PATCH 23/23] HID: Remove Waltop tablets from hid_have_special_driver Tested with a Waltop tablet and it seems to work fine. Signed-off-by: Nikolai Kondrashov Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-quirks.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 38ad3e00d82f..9579ec9f1c9f 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -672,15 +672,6 @@ static const struct hid_device_id hid_have_special_driver[] = { #if IS_ENABLED(CONFIG_HID_UDRAW_PS3) { HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) }, #endif -#if IS_ENABLED(CONFIG_HID_WALTOP) - { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_Q_PAD) }, - { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_PID_0038) }, - { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET) }, -#endif #if IS_ENABLED(CONFIG_HID_XINMO) { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_THT_2P_ARCADE) },