From 69481059c507b3b5a99aaab0d544e6516034defc Mon Sep 17 00:00:00 2001 From: Hongye Yuan Date: Wed, 9 Jan 2019 09:18:08 +0800 Subject: HID: sony: Transform one-time SHANWAN check into quirk Transform the one-time check for a SHANWAN PS3 clone gamepad into a quirk, to avoid doing the same string comparison in multiple places. Signed-off-by: Hongye Yuan Reviewed-by: Bastien Nocera Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-sony.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 9671a4bad643..fc82f02bae75 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -58,6 +58,7 @@ #define FUTUREMAX_DANCE_MAT BIT(13) #define NSG_MR5U_REMOTE_BT BIT(14) #define NSG_MR7U_REMOTE_BT BIT(15) +#define SHANWAN_GAMEPAD BIT(16) #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT) #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT) @@ -1490,6 +1491,7 @@ static int sony_register_sensors(struct sony_sc *sc) */ static int sixaxis_set_operational_usb(struct hid_device *hdev) { + struct sony_sc *sc = hid_get_drvdata(hdev); const int buf_size = max(SIXAXIS_REPORT_0xF2_SIZE, SIXAXIS_REPORT_0xF5_SIZE); u8 *buf; @@ -1519,14 +1521,15 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) /* * But the USB interrupt would cause SHANWAN controllers to - * start rumbling non-stop. + * start rumbling non-stop, so skip step 3 for these controllers. */ - if (strcmp(hdev->name, "SHANWAN PS3 GamePad")) { - ret = hid_hw_output_report(hdev, buf, 1); - if (ret < 0) { - hid_info(hdev, "can't set operational mode: step 3, ignoring\n"); - ret = 0; - } + if (sc->quirks & SHANWAN_GAMEPAD) + goto out; + + ret = hid_hw_output_report(hdev, buf, 1); + if (ret < 0) { + hid_info(hdev, "can't set operational mode: step 3, ignoring\n"); + ret = 0; } out: @@ -2811,6 +2814,9 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) if (!strcmp(hdev->name, "FutureMax Dance Mat")) quirks |= FUTUREMAX_DANCE_MAT; + if (!strcmp(hdev->name, "SHANWAN PS3 GamePad")) + quirks |= SHANWAN_GAMEPAD; + sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL); if (sc == NULL) { hid_err(hdev, "can't alloc sony descriptor\n"); -- cgit v1.2.3 From d03213f1287bcf3ad0102837694f021847737a0d Mon Sep 17 00:00:00 2001 From: Hongye Yuan Date: Wed, 9 Jan 2019 09:18:09 +0800 Subject: HID: sony: Fix SHANWAN PS3 GamePad rumble and led settings support Rumble support on SHANWAN PS3 gamepad is not working when a user program has asked it to. If a HID Output Reports is sent via Control Channel then it will be discard by gamepad, thus rumble motor and led settings in Output Report are ignored. This patch therefore sends HID Output Report via Interrupt Channel to SHANWAN gamepad instead of Control Channel, fixing rumble motor and led settings. Signed-off-by: Hongye Yuan Reviewed-by: Bastien Nocera Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-sony.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index fc82f02bae75..26fae90b931a 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -2100,9 +2100,14 @@ static void sixaxis_send_output_report(struct sony_sc *sc) } } - hid_hw_raw_request(sc->hdev, report->report_id, (u8 *)report, - sizeof(struct sixaxis_output_report), - HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); + /* SHANWAN controllers require output reports via intr channel */ + if (sc->quirks & SHANWAN_GAMEPAD) + hid_hw_output_report(sc->hdev, (u8 *)report, + sizeof(struct sixaxis_output_report)); + else + hid_hw_raw_request(sc->hdev, report->report_id, (u8 *)report, + sizeof(struct sixaxis_output_report), + HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); } static void dualshock4_send_output_report(struct sony_sc *sc) -- cgit v1.2.3 From af8cd70a99300a7c3451c76efe1afa8eca37cfce Mon Sep 17 00:00:00 2001 From: William Whistler Date: Mon, 14 Jan 2019 17:50:07 +0000 Subject: Support for Maltron L90 keyboard media keys The USB report descriptor sent by the Maltron L90 keyboard is invalid, causing the media key reports not to be accepted. This patch adds a driver which uses a report fixup to replace the descriptor. Signed-off-by: William Whistler Signed-off-by: Benjamin Tissoires --- drivers/hid/Kconfig | 7 ++ drivers/hid/Makefile | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-maltron.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+) create mode 100644 drivers/hid/hid-maltron.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 41e9935fc584..661fe610ee5b 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -590,6 +590,13 @@ config HID_MAGICMOUSE Say Y here if you want support for the multi-touch features of the Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad. +config HID_MALTRON + tristate "Maltron L90 keyboard" + depends on HID + ---help--- + Adds support for the volume up, volume down, mute, and play/pause buttons + of the Maltron L90 keyboard. + config HID_MAYFLASH tristate "Mayflash game controller adapter force feedback" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 896a51ce7ce0..cf2752003253 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o +obj-$(CONFIG_HID_MALTRON) += hid-maltron.o obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 518fa76414f5..01d565357dbe 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -72,6 +72,7 @@ #define USB_VENDOR_ID_ALCOR 0x058f #define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 +#define USB_DEVICE_ID_ALCOR_MALTRON_KB 0x9410 #define USB_VENDOR_ID_ALPS 0x0433 #define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 diff --git a/drivers/hid/hid-maltron.c b/drivers/hid/hid-maltron.c new file mode 100644 index 000000000000..dcd6db6a646e --- /dev/null +++ b/drivers/hid/hid-maltron.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * HID driver for Maltron L90 + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2008 Jiri Slaby + * Copyright (c) 2012 David Dillow + * Copyright (c) 2006-2013 Jiri Kosina + * Copyright (c) 2013 Colin Leitner + * Copyright (c) 2014-2016 Frank Praznik + * Copyright (c) 2010 Richard Nauber + * Copyright (c) 2016 Yuxuan Shui + * Copyright (c) 2018 William Whistler + */ + +#include +#include +#include + +#include "hid-ids.h" + +/* The original buggy USB descriptor */ +static u8 maltron_rdesc_o[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ + 0x09, 0x80, /* Usage (Sys Control) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x85, 0x02, /* Report ID (2) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x01, /* Report Count (1) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x09, 0x82, /* Usage (Sys Sleep) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0x09, 0x82, /* Usage (Sys Sleep) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0x09, 0x83, /* Usage (Sys Wake Up) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0x75, 0x05, /* Report Size (5) */ + 0x81, 0x01, /* Input (Const,Array,Abs) */ + 0xC0, /* End Collection */ + 0x05, 0x0C, /* Usage Page (Consumer) */ + 0x09, 0x01, /* Usage (Consumer Control) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x85, 0x03, /* Report ID (3) */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x10, /* Report Size (16) */ + 0x19, 0x00, /* Usage Minimum (Unassigned) */ + 0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */ + 0x81, 0x00, /* Input (Data,Array,Abs) */ + 0xC0, /* End Collection */ + 0x06, 0x7F, 0xFF, /* Usage Page (Vendor Defined 0xFF7F) */ + 0x09, 0x01, /* Usage (0x01) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x85, 0x04, /* Report ID (4) */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x10, /* Report Size (16) */ + 0x19, 0x00, /* Usage Minimum (0x00) */ + 0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */ + 0x81, 0x00, /* Input (Data,Array,Abs) */ + 0x75, 0x02, /* Report Size (2) */ + 0x25, 0x02, /* Logical Maximum (2) */ + 0x09, 0x90, /* Usage (0x90) */ + 0xB1, 0x02, /* Feature (Data,Var,Abs) */ + 0x75, 0x06, /* Report Size (6) */ + 0xB1, 0x01, /* Feature (Const,Array,Abs) */ + 0x75, 0x01, /* Report Size (1) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x05, 0x08, /* Usage Page (LEDs) */ + 0x09, 0x2A, /* Usage (On-Line) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0x09, 0x4B, /* Usage (Generic Indicator) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0x75, 0x06, /* Report Size (6) */ + 0x95, 0x01, /* Report Count (1) */ + 0x91, 0x01, /* Output (Const,Array,Abs) */ + 0xC0 /* End Collection */ +}; + +/* The patched descriptor, allowing media key events to be accepted as valid */ +static u8 maltron_rdesc[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ + 0x09, 0x80, /* Usage (Sys Control) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x85, 0x02, /* Report ID (2) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x01, /* Report Count (1) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x09, 0x82, /* Usage (Sys Sleep) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0x09, 0x82, /* Usage (Sys Sleep) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0x09, 0x83, /* Usage (Sys Wake Up) */ + 0x81, 0x06, /* Input (Data,Var,Rel) */ + 0x75, 0x05, /* Report Size (5) */ + 0x81, 0x01, /* Input (Const,Array,Abs) */ + 0xC0, /* End Collection */ + 0x05, 0x0C, /* Usage Page (Consumer) */ + 0x09, 0x01, /* Usage (Consumer Control) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x85, 0x03, /* Report ID (3) */ + 0x15, 0x00, /* Logical Minimum (0) - changed */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767) - changed */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x10, /* Report Size (16) */ + 0x19, 0x00, /* Usage Minimum (Unassigned) */ + 0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */ + 0x81, 0x00, /* Input (Data,Array,Abs) */ + 0xC0, /* End Collection */ + 0x06, 0x7F, 0xFF, /* Usage Page (Vendor Defined 0xFF7F) */ + 0x09, 0x01, /* Usage (0x01) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x85, 0x04, /* Report ID (4) */ + 0x95, 0x01, /* Report Count (1) */ + 0x75, 0x10, /* Report Size (16) */ + 0x19, 0x00, /* Usage Minimum (0x00) */ + 0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */ + 0x81, 0x00, /* Input (Data,Array,Abs) */ + 0x75, 0x02, /* Report Size (2) */ + 0x25, 0x02, /* Logical Maximum (2) */ + 0x09, 0x90, /* Usage (0x90) */ + 0xB1, 0x02, /* Feature (Data,Var,Abs) */ + 0x75, 0x06, /* Report Size (6) */ + 0xB1, 0x01, /* Feature (Const,Array,Abs) */ + 0x75, 0x01, /* Report Size (1) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x05, 0x08, /* Usage Page (LEDs) */ + 0x09, 0x2A, /* Usage (On-Line) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0x09, 0x4B, /* Usage (Generic Indicator) */ + 0x91, 0x02, /* Output (Data,Var,Abs) */ + 0x75, 0x06, /* Report Size (6) */ + 0x95, 0x01, /* Report Count (1) */ + 0x91, 0x01, /* Output (Const,Array,Abs) */ + 0xC0 /* End Collection */ +}; + +static __u8 *maltron_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + if (*rsize == sizeof(maltron_rdesc_o) && + !memcmp(maltron_rdesc_o, rdesc, sizeof(maltron_rdesc_o))) { + hid_info(hdev, "Replacing Maltron L90 keyboard report descriptor\n"); + *rsize = sizeof(maltron_rdesc); + return maltron_rdesc; + } + return rdesc; +} + +static const struct hid_device_id maltron_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_MALTRON_KB)}, + { } +}; +MODULE_DEVICE_TABLE(hid, maltron_devices); + +static struct hid_driver maltron_driver = { + .name = "maltron", + .id_table = maltron_devices, + .report_fixup = maltron_report_fixup +}; +module_hid_driver(maltron_driver); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 1475af255e18f35dc46f8a7acc18354c73d45149 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Mon, 7 Jan 2019 15:24:29 +0800 Subject: HID: i2c-hid: Ignore input report if there's no data present on Elan touchpanels While using Elan touchpads, the message floods: [ 136.138487] i2c_hid i2c-DELL08D6:00: i2c_hid_get_input: incomplete report (14/65535) Though the message flood is annoying, the device it self works without any issue. I suspect that the device in question takes too much time to pull the IRQ back to high after I2C host has done reading its data. Since the host receives all useful data, let's ignore the input report when there's no data. Signed-off-by: Kai-Heng Feng Signed-off-by: Benjamin Tissoires --- drivers/hid/i2c-hid/i2c-hid-core.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index 8555ce7e737b..2f940c1de616 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -50,6 +50,7 @@ #define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1) #define I2C_HID_QUIRK_NO_RUNTIME_PM BIT(2) #define I2C_HID_QUIRK_DELAY_AFTER_SLEEP BIT(3) +#define I2C_HID_QUIRK_BOGUS_IRQ BIT(4) /* flags */ #define I2C_HID_STARTED 0 @@ -179,6 +180,8 @@ static const struct i2c_hid_quirks { I2C_HID_QUIRK_DELAY_AFTER_SLEEP }, { USB_VENDOR_ID_LG, I2C_DEVICE_ID_LG_8001, I2C_HID_QUIRK_NO_RUNTIME_PM }, + { USB_VENDOR_ID_ELAN, HID_ANY_ID, + I2C_HID_QUIRK_BOGUS_IRQ }, { 0, 0 } }; @@ -503,6 +506,12 @@ static void i2c_hid_get_input(struct i2c_hid *ihid) return; } + if (ihid->quirks & I2C_HID_QUIRK_BOGUS_IRQ && ret_size == 0xffff) { + dev_warn_once(&ihid->client->dev, "%s: IRQ triggered but " + "there's no data\n", __func__); + return; + } + if ((ret_size > size) || (ret_size < 2)) { dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n", __func__, size, ret_size); -- cgit v1.2.3 From 2edefc056e4f0e6ec9508dd1aca2c18fa320efef Mon Sep 17 00:00:00 2001 From: Song Hongyan Date: Tue, 22 Jan 2019 09:06:26 +0800 Subject: HID: intel-ish: ipc: handle PIMR before ish_wakeup also clear PISR busy_clear bit Host driver should handle interrupt mask register earlier than wake up ish FW else there will be conditions when FW interrupt comes, host PIMR register still not set ready, so move the interrupt mask setting before ish_wakeup. Clear PISR busy_clear bit in ish_irq_handler. If not clear, there will be conditions host driver received a busy_clear interrupt (before the busy_clear mask bit is ready), it will return IRQ_NONE after check_generated_interrupt, the interrupt will never be cleared, causing the DEVICE not sending following IRQ. Since PISR clear should not be called for the CHV device we do this change. After the change, both ISH2HOST interrupt and busy_clear interrupt will be considered as interrupt from ISH, busy_clear interrupt will return IRQ_HANDLED from IPC_IS_BUSY check. Signed-off-by: Song Hongyan Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/ipc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index 742191bb24c6..45e33c7ba9a6 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -91,7 +91,10 @@ static bool check_generated_interrupt(struct ishtp_device *dev) IPC_INT_FROM_ISH_TO_HOST_CHV_AB(pisr_val); } else { pisr_val = ish_reg_read(dev, IPC_REG_PISR_BXT); - interrupt_generated = IPC_INT_FROM_ISH_TO_HOST_BXT(pisr_val); + interrupt_generated = !!pisr_val; + /* only busy-clear bit is RW, others are RO */ + if (pisr_val) + ish_reg_write(dev, IPC_REG_PISR_BXT, pisr_val); } return interrupt_generated; @@ -839,11 +842,11 @@ int ish_hw_start(struct ishtp_device *dev) { ish_set_host_rdy(dev); + set_host_ready(dev); + /* After that we can enable ISH DMA operation and wakeup ISHFW */ ish_wakeup(dev); - set_host_ready(dev); - /* wait for FW-initiated reset flow */ if (!dev->recvd_hw_ready) wait_event_interruptible_timeout(dev->wait_hw_ready, -- cgit v1.2.3 From 141065012a8c89a48e5c584ef94f6a11b9523c9d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 24 Jan 2019 22:09:06 +0200 Subject: HID: intel-ish-hid: Switch to use new generic UUID API There are new types and helpers that are supposed to be used in new code. As a preparation to get rid of legacy types and API functions do the conversion here. Signed-off-by: Andy Shevchenko Reviewed-by: Christoph Hellwig Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ishtp-hid-client.c | 4 ++-- drivers/hid/intel-ish-hid/ishtp-hid.h | 6 +++--- drivers/hid/intel-ish-hid/ishtp/bus.c | 21 +++++++++------------ drivers/hid/intel-ish-hid/ishtp/bus.h | 4 ++-- drivers/hid/intel-ish-hid/ishtp/client.h | 2 +- drivers/hid/intel-ish-hid/ishtp/hbm.h | 2 +- 6 files changed, 18 insertions(+), 21 deletions(-) diff --git a/drivers/hid/intel-ish-hid/ishtp-hid-client.c b/drivers/hid/intel-ish-hid/ishtp-hid-client.c index e64243bc9c96..30fe0c5e6fad 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid-client.c +++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c @@ -788,8 +788,8 @@ static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device) if (!cl_device) return -ENODEV; - if (uuid_le_cmp(hid_ishtp_guid, - cl_device->fw_client->props.protocol_name) != 0) + if (!guid_equal(&hid_ishtp_guid, + &cl_device->fw_client->props.protocol_name)) return -ENODEV; client_data = devm_kzalloc(&cl_device->dev, sizeof(*client_data), diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.h b/drivers/hid/intel-ish-hid/ishtp-hid.h index f5c7eb79b7b5..1cd07a441cd4 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid.h +++ b/drivers/hid/intel-ish-hid/ishtp-hid.h @@ -29,9 +29,9 @@ client->cl_device->ishtp_dev, __VA_ARGS__) /* ISH Transport protocol (ISHTP in short) GUID */ -static const uuid_le hid_ishtp_guid = UUID_LE(0x33AECD58, 0xB679, 0x4E54, - 0x9B, 0xD9, 0xA0, 0x4D, 0x34, - 0xF0, 0xC2, 0x26); +static const guid_t hid_ishtp_guid = + GUID_INIT(0x33AECD58, 0xB679, 0x4E54, + 0x9B, 0xD9, 0xA0, 0x4D, 0x34, 0xF0, 0xC2, 0x26); /* ISH HID message structure */ struct hostif_msg_hdr { diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.c b/drivers/hid/intel-ish-hid/ishtp/bus.c index 728dc6d4561a..efa21d33ad60 100644 --- a/drivers/hid/intel-ish-hid/ishtp/bus.c +++ b/drivers/hid/intel-ish-hid/ishtp/bus.c @@ -133,18 +133,15 @@ int ishtp_write_message(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr, * * Return: fw client index or -ENOENT if not found */ -int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *uuid) +int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const guid_t *uuid) { - int i, res = -ENOENT; + unsigned int i; for (i = 0; i < dev->fw_clients_num; ++i) { - if (uuid_le_cmp(*uuid, dev->fw_clients[i].props.protocol_name) - == 0) { - res = i; - break; - } + if (guid_equal(uuid, &dev->fw_clients[i].props.protocol_name)) + return i; } - return res; + return -ENOENT; } EXPORT_SYMBOL(ishtp_fw_cl_by_uuid); @@ -158,7 +155,7 @@ EXPORT_SYMBOL(ishtp_fw_cl_by_uuid); * Return: pointer of client information on success, NULL on failure. */ struct ishtp_fw_client *ishtp_fw_cl_get_client(struct ishtp_device *dev, - const uuid_le *uuid) + const guid_t *uuid) { int i; unsigned long flags; @@ -401,7 +398,7 @@ static const struct device_type ishtp_cl_device_type = { * Return: ishtp_cl_device pointer or NULL on failure */ static struct ishtp_cl_device *ishtp_bus_add_device(struct ishtp_device *dev, - uuid_le uuid, char *name) + guid_t uuid, char *name) { struct ishtp_cl_device *device; int status; @@ -629,7 +626,7 @@ int ishtp_bus_new_client(struct ishtp_device *dev) int i; char *dev_name; struct ishtp_cl_device *cl_device; - uuid_le device_uuid; + guid_t device_uuid; /* * For all reported clients, create an unconnected client and add its @@ -639,7 +636,7 @@ int ishtp_bus_new_client(struct ishtp_device *dev) */ i = dev->fw_client_presentation_num - 1; device_uuid = dev->fw_clients[i].props.protocol_name; - dev_name = kasprintf(GFP_KERNEL, "{%pUL}", device_uuid.b); + dev_name = kasprintf(GFP_KERNEL, "{%pUL}", &device_uuid); if (!dev_name) return -ENOMEM; diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.h b/drivers/hid/intel-ish-hid/ishtp/bus.h index b8a5bcc82536..babf19ba3ff6 100644 --- a/drivers/hid/intel-ish-hid/ishtp/bus.h +++ b/drivers/hid/intel-ish-hid/ishtp/bus.h @@ -112,8 +112,8 @@ void ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver); int ishtp_register_event_cb(struct ishtp_cl_device *device, void (*read_cb)(struct ishtp_cl_device *)); -int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *cuuid); +int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const guid_t *cuuid); struct ishtp_fw_client *ishtp_fw_cl_get_client(struct ishtp_device *dev, - const uuid_le *uuid); + const guid_t *uuid); #endif /* _LINUX_ISHTP_CL_BUS_H */ diff --git a/drivers/hid/intel-ish-hid/ishtp/client.h b/drivers/hid/intel-ish-hid/ishtp/client.h index 042f4c4853b1..e0df3eb611e6 100644 --- a/drivers/hid/intel-ish-hid/ishtp/client.h +++ b/drivers/hid/intel-ish-hid/ishtp/client.h @@ -126,7 +126,7 @@ struct ishtp_cl { }; /* Client connection managenment internal functions */ -int ishtp_can_client_connect(struct ishtp_device *ishtp_dev, uuid_le *uuid); +int ishtp_can_client_connect(struct ishtp_device *ishtp_dev, guid_t *uuid); int ishtp_fw_cl_by_id(struct ishtp_device *dev, uint8_t client_id); void ishtp_cl_send_msg(struct ishtp_device *dev, struct ishtp_cl *cl); void recv_ishtp_cl_msg(struct ishtp_device *dev, diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.h b/drivers/hid/intel-ish-hid/ishtp/hbm.h index d96111cef7f8..7286e3600140 100644 --- a/drivers/hid/intel-ish-hid/ishtp/hbm.h +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.h @@ -149,7 +149,7 @@ struct hbm_host_enum_response { } __packed; struct ishtp_client_properties { - uuid_le protocol_name; + guid_t protocol_name; uint8_t protocol_version; uint8_t max_number_of_connections; uint8_t fixed_address; -- cgit v1.2.3 From 8471300fce4cf2593267d07d6db50709c89049b4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 25 Jan 2019 16:05:46 +0000 Subject: HID: elan: Make array buf static, shrinks object size Don't populate the array buf on the stack but instead make it static. Makes the object code smaller by 43 bytes: Before: text data bss dec hex filename 7769 1520 0 9289 2449 drivers/hid/hid-elan.o After: text data bss dec hex filename 7662 1584 0 9246 241e drivers/hid/hid-elan.o (gcc version 8.2.0 x86_64) Signed-off-by: Colin Ian King Signed-off-by: Jiri Kosina --- drivers/hid/hid-elan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index 0bfd6d1b44c1..1c62095cee99 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -393,7 +393,7 @@ static int elan_start_multitouch(struct hid_device *hdev) * This byte sequence will enable multitouch mode and disable * mouse emulation */ - const unsigned char buf[] = { 0x0D, 0x00, 0x03, 0x21, 0x00 }; + static const unsigned char buf[] = { 0x0D, 0x00, 0x03, 0x21, 0x00 }; unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL); if (!dmabuf) -- cgit v1.2.3 From e9fe0d4921ee07d934d839d8e418f83dded48aa7 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Thu, 24 Jan 2019 11:09:44 -0800 Subject: HID: wacom: Move HID fix for AES serial number into wacom_hid_usage_quirk The 'wacom_hid_usage_quirk' function is the intended home for fixing up descriptors that are buggy or that don't quite fit the mold. Commit 578325120e was supposed to move all of these quirks but it missed the code to handle fixup the serial number usages for AES pens. Lets move this code out of 'wacom_wac_pen_usage_mapping' where it was previously lurking and put it into the same place as the others. Signed-off-by: Jason Gerecke Signed-off-by: Benjamin Tissoires --- drivers/hid/wacom_sys.c | 32 ++++++++++++++++++++++++++++++++ drivers/hid/wacom_wac.c | 21 --------------------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 0bdd85d486fe..a8633b1437b2 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -252,6 +252,38 @@ static void wacom_hid_usage_quirk(struct hid_device *hdev, } } + /* + * Wacom's AES devices use different vendor-defined usages to + * report serial number information compared to their branded + * hardware. The usages are also sometimes ill-defined and do + * not have the correct logical min/max values set. Lets patch + * the descriptor to use the branded usage convention and fix + * the errors. + */ + if (usage->hid == WACOM_HID_WT_SERIALNUMBER && + field->report_size == 16 && + field->index + 2 < field->report->maxfield) { + struct hid_field *a = field->report->field[field->index + 1]; + struct hid_field *b = field->report->field[field->index + 2]; + + if (a->maxusage > 0 && + a->usage[0].hid == HID_DG_TOOLSERIALNUMBER && + a->report_size == 32 && + b->maxusage > 0 && + b->usage[0].hid == 0xFF000000 && + b->report_size == 8) { + features->quirks |= WACOM_QUIRK_AESPEN; + usage->hid = WACOM_HID_WD_TOOLTYPE; + field->logical_minimum = S16_MIN; + field->logical_maximum = S16_MAX; + a->logical_minimum = S32_MIN; + a->logical_maximum = S32_MAX; + b->usage[0].hid = WACOM_HID_WD_SERIALHI; + b->logical_minimum = 0; + b->logical_maximum = U8_MAX; + } + } + /* 2nd-generation Intuos Pro Large has incorrect Y maximum */ if (hdev->vendor == USB_VENDOR_ID_WACOM && hdev->product == 0x0358 && diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 5dd3a8245f0f..72477e872324 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2159,27 +2159,6 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, case HID_DG_TOOLSERIALNUMBER: features->quirks |= WACOM_QUIRK_TOOLSERIAL; wacom_map_usage(input, usage, field, EV_MSC, MSC_SERIAL, 0); - - /* Adjust AES usages to match modern convention */ - if (usage->hid == WACOM_HID_WT_SERIALNUMBER && field->report_size == 16) { - if (field->index + 2 < field->report->maxfield) { - struct hid_field *a = field->report->field[field->index + 1]; - struct hid_field *b = field->report->field[field->index + 2]; - - if (a->maxusage > 0 && a->usage[0].hid == HID_DG_TOOLSERIALNUMBER && a->report_size == 32 && - b->maxusage > 0 && b->usage[0].hid == 0xFF000000 && b->report_size == 8) { - features->quirks |= WACOM_QUIRK_AESPEN; - usage->hid = WACOM_HID_WD_TOOLTYPE; - field->logical_minimum = S16_MIN; - field->logical_maximum = S16_MAX; - a->logical_minimum = S32_MIN; - a->logical_maximum = S32_MAX; - b->usage[0].hid = WACOM_HID_WD_SERIALHI; - b->logical_minimum = 0; - b->logical_maximum = U8_MAX; - } - } - } break; case WACOM_HID_WD_SENSE: features->quirks |= WACOM_QUIRK_SENSE; -- cgit v1.2.3 From 1578461a18596cf33093b392d1b7853ba4276154 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 30 Jan 2019 13:32:17 -0800 Subject: HID: intel-ish-hid: Use VID/PID from ISH The Intel ISH HID can now have other devices attached to it, not just sensors. Hence ISH HID transport is no longer just used for sensors. Currently the vendor and product id is hardcoded for sensors, but they can be obtained from ISH firmware for the real device. The driver already extract them and store as part of device_info structure in client_data. So use vendor id/product id obtained from the ISH firmware. Signed-off-by: Srinivas Pandruvada Signed-off-by: Benjamin Tissoires --- drivers/hid/intel-ish-hid/ishtp-hid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.c b/drivers/hid/intel-ish-hid/ishtp-hid.c index e918d78e541c..bc4c536f3c0d 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid.c +++ b/drivers/hid/intel-ish-hid/ishtp-hid.c @@ -206,8 +206,8 @@ int ishtp_hid_probe(unsigned int cur_hid_dev, hid->bus = BUS_INTEL_ISHTP; hid->dev.parent = &client_data->cl_device->dev; hid->version = le16_to_cpu(ISH_HID_VERSION); - hid->vendor = le16_to_cpu(ISH_HID_VENDOR); - hid->product = le16_to_cpu(ISH_HID_PRODUCT); + hid->vendor = le16_to_cpu(client_data->hid_devices[cur_hid_dev].vid); + hid->product = le16_to_cpu(client_data->hid_devices[cur_hid_dev].pid); snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-ishtp", hid->vendor, hid->product); -- cgit v1.2.3 From aa2d495f0cac219d5be1d64f6bf16e7c24cfb148 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 1 Feb 2019 16:18:07 +0100 Subject: HID: Add support for Toshiba WT10A tablet bluetooth keyboard The bluetooth-keyboard which comes with the Toshiba WT10A tablet uses a couple of usage codes in the vendor specific ffbc page. The keyboard has a vendor-id of 04f2 which maps to Chicony. Other then adding a few keymappings for the ffbc usages, no special handling is necessary. So rather then adding a new hid-toshiba driver, this commit adds the ID and 2 extra key-mappings to the hid-topseed driver. The hid-topseed driver already contains mapping for some ffbc usages (and does nothing else) and it already binds to another Chicony manufactured keyboard. Signed-off-by: Hans de Goede Signed-off-by: Benjamin Tissoires --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-topseed.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 24f846d67478..644a9f2bf624 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -273,6 +273,7 @@ #define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE 0x1053 #define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123 #define USB_DEVICE_ID_ASUS_AK1D 0x1125 +#define USB_DEVICE_ID_CHICONY_TOSHIBA_WT10A 0x1408 #define USB_DEVICE_ID_CHICONY_ACER_SWITCH12 0x1421 #define USB_VENDOR_ID_CHUNGHWAT 0x2247 diff --git a/drivers/hid/hid-topseed.c b/drivers/hid/hid-topseed.c index 8a5b843e9dd6..e9cdde840362 100644 --- a/drivers/hid/hid-topseed.c +++ b/drivers/hid/hid-topseed.c @@ -34,7 +34,9 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; switch (usage->hid & HID_USAGE) { + case 0x00c: ts_map_key_clear(KEY_WLAN); break; case 0x00d: ts_map_key_clear(KEY_MEDIA); break; + case 0x010: ts_map_key_clear(KEY_ZOOM); break; case 0x024: ts_map_key_clear(KEY_MENU); break; case 0x025: ts_map_key_clear(KEY_TV); break; case 0x027: ts_map_key_clear(KEY_MODE); break; @@ -67,6 +69,7 @@ static const struct hid_device_id ts_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TOSHIBA_WT10A) }, { } }; MODULE_DEVICE_TABLE(hid, ts_devices); -- cgit v1.2.3 From 300c64d7f6b48b2ada4ed52e8303c815033ee801 Mon Sep 17 00:00:00 2001 From: NOGUCHI Hiroshi Date: Tue, 29 Jan 2019 13:23:20 +0900 Subject: HID: hid-asus: Add Transbook T90CHI support Add Transbook T90CHI support into hid-asus. T90CHI returns same BT vendor/product values as T100CHI, so identifies T90CHI by name. Signed-off-by: NOGUCHI Hiroshi Signed-off-by: Jiri Kosina --- drivers/hid/hid-asus.c | 54 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 951bb17ae8b2..c7b4638cdeb1 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -71,6 +71,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); #define QUIRK_T100CHI BIT(7) #define QUIRK_G752_KEYBOARD BIT(8) #define QUIRK_T101HA_DOCK BIT(9) +#define QUIRK_T90CHI BIT(10) #define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \ @@ -500,7 +501,7 @@ static int asus_input_mapping(struct hid_device *hdev, * This avoids a bunch of non-functional hid_input devices getting * created because of the T100CHI using HID_QUIRK_MULTI_INPUT. */ - if (drvdata->quirks & QUIRK_T100CHI) { + if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) { if (field->application == (HID_UP_GENDESK | 0x0080) || usage->hid == (HID_UP_GENDEVCTRLS | 0x0024) || usage->hid == (HID_UP_GENDEVCTRLS | 0x0025) || @@ -660,6 +661,11 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) drvdata->quirks = id->driver_data; + if (strstr(hdev->name, "T90CHI")) { + drvdata->quirks &= ~QUIRK_T100CHI; + drvdata->quirks |= QUIRK_T90CHI; + } + if (drvdata->quirks & QUIRK_IS_MULTITOUCH) drvdata->tp = &asus_i2c_tp; @@ -769,28 +775,44 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, hid_info(hdev, "Fixing up Asus T100 keyb report descriptor\n"); rdesc[74] &= ~HID_MAIN_ITEM_CONSTANT; } - /* For the T100CHI keyboard dock */ - if (drvdata->quirks & QUIRK_T100CHI && - *rsize == 403 && rdesc[388] == 0x09 && rdesc[389] == 0x76) { + /* For the T100CHI/T90CHI keyboard dock */ + if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) { + int rsize_orig; + int offs; + + if (drvdata->quirks & QUIRK_T100CHI) { + rsize_orig = 403; + offs = 388; + } else { + rsize_orig = 306; + offs = 291; + } + /* * Change Usage (76h) to Usage Minimum (00h), Usage Maximum * (FFh) and clear the flags in the Input() byte. * Note the descriptor has a bogus 0 byte at the end so we * only need 1 extra byte. */ - *rsize = 404; - rdesc = kmemdup(rdesc, *rsize, GFP_KERNEL); - if (!rdesc) - return NULL; - - hid_info(hdev, "Fixing up T100CHI keyb report descriptor\n"); - memmove(rdesc + 392, rdesc + 390, 12); - rdesc[388] = 0x19; - rdesc[389] = 0x00; - rdesc[390] = 0x29; - rdesc[391] = 0xff; - rdesc[402] = 0x00; + if (*rsize == rsize_orig && + rdesc[offs] == 0x09 && rdesc[offs + 1] == 0x76) { + *rsize = rsize_orig + 1; + rdesc = kmemdup(rdesc, *rsize, GFP_KERNEL); + if (!rdesc) + return NULL; + + hid_info(hdev, "Fixing up %s keyb report descriptor\n", + drvdata->quirks & QUIRK_T100CHI ? + "T100CHI" : "T90CHI"); + memmove(rdesc + offs + 4, rdesc + offs + 2, 12); + rdesc[offs] = 0x19; + rdesc[offs + 1] = 0x00; + rdesc[offs + 2] = 0x29; + rdesc[offs + 3] = 0xff; + rdesc[offs + 14] = 0x00; + } } + if (drvdata->quirks & QUIRK_G752_KEYBOARD && *rsize == 75 && rdesc[61] == 0x15 && rdesc[62] == 0x00) { /* report is missing usage mninum and maximum */ -- cgit v1.2.3 From a767ffea05d2737f6542cd78458a84a157fa216d Mon Sep 17 00:00:00 2001 From: NOGUCHI Hiroshi Date: Tue, 29 Jan 2019 13:31:05 +0900 Subject: HID: Add ASUS T100CHI keyboard dock battery quirks Add ASUS Transbook T100CHI/T90CHI keyboard dock into battery quirk list, in order to add specific implementation in hid-asus. Signed-off-by: NOGUCHI Hiroshi Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 59a5608b8dc0..b10b1922c5bd 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -328,6 +328,9 @@ static const struct hid_device_id hid_battery_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_3), HID_BATTERY_QUIRK_IGNORE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ASUSTEK, + USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD), + HID_BATTERY_QUIRK_IGNORE }, {} }; -- cgit v1.2.3 From 6311d329e12a358a9813d2e929d26fbc5a4c73c2 Mon Sep 17 00:00:00 2001 From: NOGUCHI Hiroshi Date: Tue, 29 Jan 2019 13:31:29 +0900 Subject: HID: hid-asus: Add BT keyboard dock battery monitoring support Add battery monitoring support for Transbook T100CHI/T90CHI's Bluetooth keyboard dock. They report rest battery level and charging status. Signed-off-by: NOGUCHI Hiroshi Signed-off-by: Jiri Kosina --- drivers/hid/hid-asus.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index c7b4638cdeb1..336aeaed1159 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -32,6 +32,7 @@ #include #include #include /* For to_usb_interface for T100 touchpad intf check */ +#include #include "hid-ids.h" @@ -61,6 +62,13 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); #define CONTACT_TOUCH_MAJOR_MASK 0x07 #define CONTACT_PRESSURE_MASK 0x7f +#define BATTERY_REPORT_ID (0x03) +#define BATTERY_REPORT_SIZE (1 + 8) +#define BATTERY_LEVEL_MAX ((u8)255) +#define BATTERY_STAT_DISCONNECT (0) +#define BATTERY_STAT_CHARGING (1) +#define BATTERY_STAT_FULL (2) + #define QUIRK_FIX_NOTEBOOK_REPORT BIT(0) #define QUIRK_NO_INIT_REPORTS BIT(1) #define QUIRK_SKIP_INPUT_MAPPING BIT(2) @@ -101,12 +109,21 @@ struct asus_touchpad_info { struct asus_drvdata { unsigned long quirks; + struct hid_device *hdev; struct input_dev *input; struct asus_kbd_leds *kbd_backlight; const struct asus_touchpad_info *tp; bool enable_backlight; + struct power_supply *battery; + struct power_supply_desc battery_desc; + int battery_capacity; + int battery_stat; + bool battery_in_query; + unsigned long battery_next_query; }; +static int asus_report_battery(struct asus_drvdata *, u8 *, int); + static const struct asus_touchpad_info asus_i2c_tp = { .max_x = 2794, .max_y = 1758, @@ -260,6 +277,9 @@ static int asus_raw_event(struct hid_device *hdev, { struct asus_drvdata *drvdata = hid_get_drvdata(hdev); + if (drvdata->battery && data[0] == BATTERY_REPORT_ID) + return asus_report_battery(drvdata, data, size); + if (drvdata->tp && data[0] == INPUT_REPORT_ID) return asus_report_input(drvdata, data, size); @@ -429,6 +449,164 @@ static int asus_kbd_register_leds(struct hid_device *hdev) return ret; } +/* + * [0] REPORT_ID (same value defined in report descriptor) + * [1] rest battery level. range [0..255] + * [2]..[7] Bluetooth hardware address (MAC address) + * [8] charging status + * = 0 : AC offline / discharging + * = 1 : AC online / charging + * = 2 : AC online / fully charged + */ +static int asus_parse_battery(struct asus_drvdata *drvdata, u8 *data, int size) +{ + u8 sts; + u8 lvl; + int val; + + lvl = data[1]; + sts = data[8]; + + drvdata->battery_capacity = ((int)lvl * 100) / (int)BATTERY_LEVEL_MAX; + + switch (sts) { + case BATTERY_STAT_CHARGING: + val = POWER_SUPPLY_STATUS_CHARGING; + break; + case BATTERY_STAT_FULL: + val = POWER_SUPPLY_STATUS_FULL; + break; + case BATTERY_STAT_DISCONNECT: + default: + val = POWER_SUPPLY_STATUS_DISCHARGING; + break; + } + drvdata->battery_stat = val; + + return 0; +} + +static int asus_report_battery(struct asus_drvdata *drvdata, u8 *data, int size) +{ + /* notify only the autonomous event by device */ + if ((drvdata->battery_in_query == false) && + (size == BATTERY_REPORT_SIZE)) + power_supply_changed(drvdata->battery); + + return 0; +} + +static int asus_battery_query(struct asus_drvdata *drvdata) +{ + u8 *buf; + int ret = 0; + + buf = kmalloc(BATTERY_REPORT_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + drvdata->battery_in_query = true; + ret = hid_hw_raw_request(drvdata->hdev, BATTERY_REPORT_ID, + buf, BATTERY_REPORT_SIZE, + HID_INPUT_REPORT, HID_REQ_GET_REPORT); + drvdata->battery_in_query = false; + if (ret == BATTERY_REPORT_SIZE) + ret = asus_parse_battery(drvdata, buf, BATTERY_REPORT_SIZE); + else + ret = -ENODATA; + + kfree(buf); + + return ret; +} + +static enum power_supply_property asus_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_MODEL_NAME, +}; + +#define QUERY_MIN_INTERVAL (60 * HZ) /* 60[sec] */ + +static int asus_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct asus_drvdata *drvdata = power_supply_get_drvdata(psy); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_CAPACITY: + if (time_before(drvdata->battery_next_query, jiffies)) { + drvdata->battery_next_query = + jiffies + QUERY_MIN_INTERVAL; + ret = asus_battery_query(drvdata); + if (ret) + return ret; + } + if (psp == POWER_SUPPLY_PROP_STATUS) + val->intval = drvdata->battery_stat; + else + val->intval = drvdata->battery_capacity; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = drvdata->hdev->name; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int asus_battery_probe(struct hid_device *hdev) +{ + struct asus_drvdata *drvdata = hid_get_drvdata(hdev); + struct power_supply_config pscfg = { .drv_data = drvdata }; + int ret = 0; + + drvdata->battery_capacity = 0; + drvdata->battery_stat = POWER_SUPPLY_STATUS_UNKNOWN; + drvdata->battery_in_query = false; + + drvdata->battery_desc.properties = asus_battery_props; + drvdata->battery_desc.num_properties = ARRAY_SIZE(asus_battery_props); + drvdata->battery_desc.get_property = asus_battery_get_property; + drvdata->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; + drvdata->battery_desc.use_for_apm = 0; + drvdata->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "asus-keyboard-%s-battery", + strlen(hdev->uniq) ? + hdev->uniq : dev_name(&hdev->dev)); + if (!drvdata->battery_desc.name) + return -ENOMEM; + + drvdata->battery_next_query = jiffies; + + drvdata->battery = devm_power_supply_register(&hdev->dev, + &(drvdata->battery_desc), &pscfg); + if (IS_ERR(drvdata->battery)) { + ret = PTR_ERR(drvdata->battery); + drvdata->battery = NULL; + hid_err(hdev, "Unable to register battery device\n"); + return ret; + } + + power_supply_powers(drvdata->battery, &hdev->dev); + + return ret; +} + static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct input_dev *input = hi->input; @@ -661,6 +839,10 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) drvdata->quirks = id->driver_data; + /* + * T90CHI's keyboard dock returns same ID values as T100CHI's dock. + * Thus, identify T90CHI dock with product name string. + */ if (strstr(hdev->name, "T90CHI")) { drvdata->quirks &= ~QUIRK_T100CHI; drvdata->quirks |= QUIRK_T90CHI; @@ -700,6 +882,17 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) if (drvdata->quirks & QUIRK_NO_INIT_REPORTS) hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; + drvdata->hdev = hdev; + + if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) { + ret = asus_battery_probe(hdev); + if (ret) { + hid_err(hdev, + "Asus hid battery_probe failed: %d\n", ret); + return ret; + } + } + ret = hid_parse(hdev); if (ret) { hid_err(hdev, "Asus hid parse failed: %d\n", ret); -- cgit v1.2.3 From cf28aee292e102740e49f74385b4b89c00050763 Mon Sep 17 00:00:00 2001 From: Rodrigo Rivas Costa Date: Wed, 6 Feb 2019 22:27:54 +0100 Subject: HID: steam: fix boot loop with bluetooth firmware There is a new firmware for the Steam Controller with support for BLE connections. When using such a device with a wired connection, it reboots itself every 10 seconds unless an application has opened it. Doing hid_hw_open() unconditionally on probe fixes the issue, and the code becomes simpler. Signed-off-by: Rodrigo Rivas Costa Signed-off-by: Jiri Kosina --- drivers/hid/hid-steam.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c index dc4128bfe2ca..8141cadfca0e 100644 --- a/drivers/hid/hid-steam.c +++ b/drivers/hid/hid-steam.c @@ -283,11 +283,6 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable) static int steam_input_open(struct input_dev *dev) { struct steam_device *steam = input_get_drvdata(dev); - int ret; - - ret = hid_hw_open(steam->hdev); - if (ret) - return ret; mutex_lock(&steam->mutex); if (!steam->client_opened && lizard_mode) @@ -304,8 +299,6 @@ static void steam_input_close(struct input_dev *dev) if (!steam->client_opened && lizard_mode) steam_set_lizard_mode(steam, true); mutex_unlock(&steam->mutex); - - hid_hw_close(steam->hdev); } static enum power_supply_property steam_battery_props[] = { @@ -623,11 +616,6 @@ static void steam_client_ll_stop(struct hid_device *hdev) static int steam_client_ll_open(struct hid_device *hdev) { struct steam_device *steam = hdev->driver_data; - int ret; - - ret = hid_hw_open(steam->hdev); - if (ret) - return ret; mutex_lock(&steam->mutex); steam->client_opened = true; @@ -635,7 +623,7 @@ static int steam_client_ll_open(struct hid_device *hdev) steam_input_unregister(steam); - return ret; + return 0; } static void steam_client_ll_close(struct hid_device *hdev) @@ -646,7 +634,6 @@ static void steam_client_ll_close(struct hid_device *hdev) steam->client_opened = false; mutex_unlock(&steam->mutex); - hid_hw_close(steam->hdev); if (steam->connected) { steam_set_lizard_mode(steam, lizard_mode); steam_input_register(steam); @@ -759,14 +746,15 @@ static int steam_probe(struct hid_device *hdev, if (ret) goto client_hdev_add_fail; + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, + "%s:hid_hw_open\n", + __func__); + goto hid_hw_open_fail; + } + if (steam->quirks & STEAM_QUIRK_WIRELESS) { - ret = hid_hw_open(hdev); - if (ret) { - hid_err(hdev, - "%s:hid_hw_open for wireless\n", - __func__); - goto hid_hw_open_fail; - } hid_info(hdev, "Steam wireless receiver connected"); steam_request_conn_status(steam); } else { @@ -781,8 +769,8 @@ static int steam_probe(struct hid_device *hdev, return 0; -hid_hw_open_fail: input_register_fail: +hid_hw_open_fail: client_hdev_add_fail: hid_hw_stop(hdev); hid_hw_start_fail: @@ -809,8 +797,8 @@ static void steam_remove(struct hid_device *hdev) cancel_work_sync(&steam->work_connect); if (steam->quirks & STEAM_QUIRK_WIRELESS) { hid_info(hdev, "Steam wireless receiver disconnected"); - hid_hw_close(hdev); } + hid_hw_close(hdev); hid_hw_stop(hdev); steam_unregister(steam); } -- cgit v1.2.3 From 8d5037dca7c2089f27e5903c2aecfc5bb10d7806 Mon Sep 17 00:00:00 2001 From: Mikael Wikström Date: Sat, 9 Feb 2019 19:05:26 +0100 Subject: HID: multitouch: Lenovo X1 Tablet Gen3 trackpoint and buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the trackpoint and three mouse buttons on the type cover of the Lenovo X1 Tablet Gen3. This is the same as with the 2nd generation Lenovo X1 Tablet. Signed-off-by: Mikael Wikström Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-multitouch.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 24f846d67478..d76c033ba0fc 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -714,6 +714,7 @@ #define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067 #define USB_DEVICE_ID_LENOVO_X1_COVER 0x6085 #define USB_DEVICE_ID_LENOVO_X1_TAB 0x60a3 +#define USB_DEVICE_ID_LENOVO_X1_TAB3 0x60b5 #define USB_VENDOR_ID_LG 0x1fd2 #define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index dca0a3a90fb8..c02d4cad1893 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1780,6 +1780,12 @@ static const struct hid_device_id mt_devices[] = { USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_TAB) }, + /* Lenovo X1 TAB Gen 3 */ + { .driver_data = MT_CLS_WIN_8_DUAL, + HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, + USB_VENDOR_ID_LENOVO, + USB_DEVICE_ID_LENOVO_X1_TAB3) }, + /* Anton devices */ { .driver_data = MT_CLS_EXPORT_ALL_INPUTS, MT_USB_DEVICE(USB_VENDOR_ID_ANTON, -- cgit v1.2.3 From e41b3cdafed777f8e927db01c2fab5ae9c95055c Mon Sep 17 00:00:00 2001 From: Jarrad Whitaker Date: Thu, 24 Jan 2019 22:40:50 +1100 Subject: HID: fix Logitech WingMan Formula GP joined axes The Logitech WingMan Formula GP by default presents a combined accelerate/brake axis, so this patch assigns it to hid-logitech in order to benefit from the axis-splitting logic in lg4ff. There is also a fixed report descriptor for the resulting report. Signed-off-by: Jarrad Whitaker Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-lg.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-lg4ff.c | 6 +++++ drivers/hid/hid-quirks.c | 1 + 4 files changed, 68 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 24f846d67478..c43fe0a0fc36 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -744,6 +744,7 @@ #define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 #define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 #define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287 +#define USB_DEVICE_ID_LOGITECH_WINGMAN_FG 0xc20e #define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 596227ddb6e0..5d419a95b6c2 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -50,6 +50,7 @@ #define MOMO_RDESC_ORIG_SIZE 87 #define MOMO2_RDESC_ORIG_SIZE 87 #define FFG_RDESC_ORIG_SIZE 85 +#define FG_RDESC_ORIG_SIZE 82 /* Fixed report descriptors for Logitech Driving Force (and Pro) * wheel controllers @@ -381,6 +382,49 @@ static __u8 ffg_rdesc_fixed[] = { 0xC0 /* End Collection */ }; +static __u8 fg_rdesc_fixed[] = { +0x05, 0x01, /* Usage Page (Desktop), */ +0x09, 0x04, /* Usage (Joystik), */ +0xA1, 0x01, /* Collection (Application), */ +0xA1, 0x02, /* Collection (Logical), */ +0x15, 0x00, /* Logical Minimum (0), */ +0x26, 0xFF, 0x00, /* Logical Maximum (255), */ +0x35, 0x00, /* Physical Minimum (0), */ +0x46, 0xFF, 0x00, /* Physical Maximum (255), */ +0x75, 0x08, /* Report Size (8), */ +0x95, 0x01, /* Report Count (1), */ +0x09, 0x30, /* Usage (X), */ +0x81, 0x02, /* Input (Variable), */ +0xA4, /* Push, */ +0x25, 0x01, /* Logical Maximum (1), */ +0x45, 0x01, /* Physical Maximum (1), */ +0x75, 0x01, /* Report Size (1), */ +0x95, 0x02, /* Report Count (2), */ +0x81, 0x01, /* Input (Constant), */ +0x95, 0x06, /* Report Count (6), */ +0x05, 0x09, /* Usage Page (Button), */ +0x19, 0x01, /* Usage Minimum (01h), */ +0x29, 0x06, /* Usage Maximum (06h), */ +0x81, 0x02, /* Input (Variable), */ +0x05, 0x01, /* Usage Page (Desktop), */ +0xB4, /* Pop, */ +0x81, 0x02, /* Input (Constant), */ +0x09, 0x31, /* Usage (Y), */ +0x81, 0x02, /* Input (Variable), */ +0x09, 0x32, /* Usage (Z), */ +0x81, 0x02, /* Input (Variable), */ +0xC0, /* End Collection, */ +0xA1, 0x02, /* Collection (Logical), */ +0x26, 0xFF, 0x00, /* Logical Maximum (255), */ +0x46, 0xFF, 0x00, /* Physical Maximum (255), */ +0x75, 0x08, /* Report Size (8), */ +0x95, 0x04, /* Report Count (4), */ +0x09, 0x02, /* Usage (02h), */ +0xB1, 0x02, /* Feature (Variable), */ +0xC0, /* End Collection, */ +0xC0 /* End Collection, */ +}; + /* * Certain Logitech keyboards send in report #3 keys which are far * above the logical maximum described in descriptor. This extends @@ -408,6 +452,19 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, switch (hdev->product) { + case USB_DEVICE_ID_LOGITECH_WINGMAN_FG: + if (*rsize == FG_RDESC_ORIG_SIZE) { + hid_info(hdev, + "fixing up Logitech Wingman Formula GP report descriptor\n"); + rdesc = fg_rdesc_fixed; + *rsize = sizeof(fg_rdesc_fixed); + } else { + hid_info(hdev, + "rdesc size test failed for formula gp\n"); + } + break; + + case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG: if (*rsize == FFG_RDESC_ORIG_SIZE) { hid_info(hdev, @@ -664,6 +721,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, usage->code == ABS_RZ)) { switch (hdev->product) { case USB_DEVICE_ID_LOGITECH_G29_WHEEL: + case USB_DEVICE_ID_LOGITECH_WINGMAN_FG: case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG: case USB_DEVICE_ID_LOGITECH_WHEEL: case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: @@ -871,6 +929,8 @@ static const struct hid_device_id lg_devices[] = { .driver_data = LG_NOGET | LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), .driver_data = LG_FF4 }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FG), + .driver_data = LG_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG), .driver_data = LG_NOGET | LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 512d67e1aae3..a299c9d1605f 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -103,6 +103,10 @@ static const signed short lg4ff_wheel_effects[] = { -1 }; +static const signed short no_wheel_effects[] = { + -1 +}; + struct lg4ff_wheel { const u32 product_id; const signed short *ff_effects; @@ -137,6 +141,7 @@ struct lg4ff_alternate_mode { }; static const struct lg4ff_wheel lg4ff_devices[] = { + {USB_DEVICE_ID_LOGITECH_WINGMAN_FG, no_wheel_effects, 40, 180, NULL}, {USB_DEVICE_ID_LOGITECH_WINGMAN_FFG, lg4ff_wheel_effects, 40, 180, NULL}, {USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, @@ -346,6 +351,7 @@ int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report, rd[5] = rd[3]; rd[6] = 0x7F; return 1; + case USB_DEVICE_ID_LOGITECH_WINGMAN_FG: case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG: case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2: diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 94088c0ed68a..4c063d8b2e13 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -451,6 +451,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FG) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) }, -- cgit v1.2.3 From 1da92d436c93a6ffc049f60206b684db2d25882f Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 11 Feb 2019 16:04:22 -0600 Subject: HID: wacom: Mark expected switch fall-through MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. This patch fixes the following warning: drivers/hid/wacom_wac.c: In function ‘wacom_setup_pen_input_capabilities’: drivers/hid/wacom_wac.c:3506:3: warning: this statement may fall through [-Wimplicit-fallthrough=] __clear_bit(ABS_MISC, input_dev->absbit); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/hid/wacom_wac.c:3508:2: note: here case WACOM_MO: ^~~~ Warning level 3 was used: -Wimplicit-fallthrough=3 This patch is part of the ongoing efforts to enable -Wimplicit-fallthrough. Signed-off-by: Gustavo A. R. Silva Acked-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 72477e872324..5ecda99bf431 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -3504,6 +3504,7 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, switch (features->type) { case GRAPHIRE_BT: __clear_bit(ABS_MISC, input_dev->absbit); + /* fall through */ case WACOM_MO: case WACOM_G4: -- cgit v1.2.3 From 0d28f49412405d87d3aae83da255070a46e67627 Mon Sep 17 00:00:00 2001 From: Hong Liu Date: Tue, 12 Feb 2019 20:05:20 +0800 Subject: HID: intel-ish-hid: avoid binding wrong ishtp_cl_device When performing a warm reset in ishtp bus driver, the ishtp_cl_device will not be removed, its fw_client still points to the already freed ishtp_device.fw_clients array. Later after driver finishing ishtp client enumeration, this dangling pointer may cause driver to bind the wrong ishtp_cl_device to the new client, causing wrong callback to be called for messages intended for the new client. This helps in development of firmware where frequent switching of firmwares is required without Linux reboot. Signed-off-by: Hong Liu Tested-by: Hongyan Song Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ishtp/bus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.c b/drivers/hid/intel-ish-hid/ishtp/bus.c index 728dc6d4561a..a271d6d169b1 100644 --- a/drivers/hid/intel-ish-hid/ishtp/bus.c +++ b/drivers/hid/intel-ish-hid/ishtp/bus.c @@ -675,7 +675,8 @@ int ishtp_cl_device_bind(struct ishtp_cl *cl) spin_lock_irqsave(&cl->dev->device_list_lock, flags); list_for_each_entry(cl_device, &cl->dev->device_list, device_link) { - if (cl_device->fw_client->client_id == cl->fw_client_id) { + if (cl_device->fw_client && + cl_device->fw_client->client_id == cl->fw_client_id) { cl->device = cl_device; rv = 0; break; @@ -735,6 +736,7 @@ void ishtp_bus_remove_all_clients(struct ishtp_device *ishtp_dev, spin_lock_irqsave(&ishtp_dev->device_list_lock, flags); list_for_each_entry_safe(cl_device, n, &ishtp_dev->device_list, device_link) { + cl_device->fw_client = NULL; if (warm_reset && cl_device->reference_count) continue; -- cgit v1.2.3 From b22f805bbe090d42e2eed86aa075687e47f924b7 Mon Sep 17 00:00:00 2001 From: Hong Liu Date: Tue, 12 Feb 2019 20:05:21 +0800 Subject: HID: intel-ish-hid: Optimize writing ipc message from queue Currently we are using one additional static variable and a spinlock to prevent contention of writing IPC messages to ISH hardware, which is not necessary. Once ISH is ready to accept new data, we can push new data to hardware. This pushing of new data is already protected by wr_processing_spinlock for contention, which is enough. So use this spinlock to check both readiness for accepting new data and once ready allow writing of ipc message from queue to ISH hardware. While here, cleaned up some space after return. Signed-off-by: Hong Liu Tested-by: Hongyan Song Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/ipc.c | 19 +++---------------- drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h | 2 -- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index 742191bb24c6..ff8eca11ff73 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -256,33 +256,22 @@ static int write_ipc_from_queue(struct ishtp_device *dev) int i; void (*ipc_send_compl)(void *); void *ipc_send_compl_prm; - static int out_ipc_locked; - unsigned long out_ipc_flags; if (dev->dev_state == ISHTP_DEV_DISABLED) - return -EINVAL; + return -EINVAL; - spin_lock_irqsave(&dev->out_ipc_spinlock, out_ipc_flags); - if (out_ipc_locked) { - spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags); - return -EBUSY; - } - out_ipc_locked = 1; + spin_lock_irqsave(&dev->wr_processing_spinlock, flags); if (!ish_is_input_ready(dev)) { - out_ipc_locked = 0; - spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags); + spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); return -EBUSY; } - spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags); - spin_lock_irqsave(&dev->wr_processing_spinlock, flags); /* * if tx send list is empty - return 0; * may happen, as RX_COMPLETE handler doesn't check list emptiness. */ if (list_empty(&dev->wr_processing_list)) { spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags); - out_ipc_locked = 0; return 0; } @@ -333,7 +322,6 @@ static int write_ipc_from_queue(struct ishtp_device *dev) dev->ipc_tx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val); ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, doorbell_val); - out_ipc_locked = 0; ipc_send_compl = ipc_link->ipc_send_compl; ipc_send_compl_prm = ipc_link->ipc_send_compl_prm; @@ -914,7 +902,6 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev) init_waitqueue_head(&dev->wait_hw_ready); spin_lock_init(&dev->wr_processing_spinlock); - spin_lock_init(&dev->out_ipc_spinlock); /* Init IPC processing and free lists */ INIT_LIST_HEAD(&dev->wr_processing_list); diff --git a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h index e7c6bfefaf9e..e54ce1ef27dd 100644 --- a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h +++ b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h @@ -211,8 +211,6 @@ struct ishtp_device { /* For both processing list and free list */ spinlock_t wr_processing_spinlock; - spinlock_t out_ipc_spinlock; - struct ishtp_fw_client *fw_clients; /*Note:memory has to be allocated*/ DECLARE_BITMAP(fw_clients_map, ISHTP_CLIENTS_MAX); DECLARE_BITMAP(host_clients_map, ISHTP_CLIENTS_MAX); -- cgit v1.2.3 From 7e06e0d5493b3e88016b247fe92f67a071d2c655 Mon Sep 17 00:00:00 2001 From: Hong Liu Date: Tue, 12 Feb 2019 20:05:22 +0800 Subject: HID: intel-ish-hid: move doorbell writing before flush Reading of IPC_REG_ISH_HOST_FWSTS will flush both message register and doorbell. So move the doorbell write before reading of IPC_REG_ISH_HOST_FWSTS. Signed-off-by: Hong Liu Tested-by: Hongyan Song Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ipc/ipc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index ff8eca11ff73..30d8a639a5bb 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -314,6 +314,8 @@ static int write_ipc_from_queue(struct ishtp_device *dev) memcpy(®, &r_buf[length >> 2], rem); ish_reg_write(dev, reg_addr, reg); } + ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, doorbell_val); + /* Flush writes to msg registers and doorbell */ ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS); @@ -321,8 +323,6 @@ static int write_ipc_from_queue(struct ishtp_device *dev) ++dev->ipc_tx_cnt; dev->ipc_tx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val); - ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, doorbell_val); - ipc_send_compl = ipc_link->ipc_send_compl; ipc_send_compl_prm = ipc_link->ipc_send_compl_prm; list_del_init(&ipc_link->link); -- cgit v1.2.3 From 09cc8b361887787a3577aa0b6510af4b11b51b9e Mon Sep 17 00:00:00 2001 From: Hong Liu Date: Tue, 12 Feb 2019 20:05:23 +0800 Subject: HID: intel-ish-hid: remove data[128] usage on stack when sending HBM request Instead of using an 128-byte on-stack array to store the request, we can instantiate the request on stack directly. This can save the stack usage of these functions, since most of the requests are much smaller than 128 bytes. Signed-off-by: Hong Liu Tested-by: Hongyan Song Acked-by: Srinivas Pandruvada Signed-off-by: Jiri Kosina --- drivers/hid/intel-ish-hid/ishtp/bus.c | 2 +- drivers/hid/intel-ish-hid/ishtp/bus.h | 2 +- drivers/hid/intel-ish-hid/ishtp/hbm.c | 97 +++++++++++++---------------------- 3 files changed, 38 insertions(+), 63 deletions(-) diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.c b/drivers/hid/intel-ish-hid/ishtp/bus.c index a271d6d169b1..f358a02325da 100644 --- a/drivers/hid/intel-ish-hid/ishtp/bus.c +++ b/drivers/hid/intel-ish-hid/ishtp/bus.c @@ -119,7 +119,7 @@ int ishtp_send_msg(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr, * Return: This returns IPC send message status. */ int ishtp_write_message(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr, - unsigned char *buf) + void *buf) { return ishtp_send_msg(dev, hdr, buf, NULL, NULL); } diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.h b/drivers/hid/intel-ish-hid/ishtp/bus.h index b8a5bcc82536..5c4763d73f8b 100644 --- a/drivers/hid/intel-ish-hid/ishtp/bus.h +++ b/drivers/hid/intel-ish-hid/ishtp/bus.h @@ -85,7 +85,7 @@ int ishtp_send_msg(struct ishtp_device *dev, /* Write a single-fragment message */ int ishtp_write_message(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr, - unsigned char *buf); + void *buf); /* Use DMA to send/receive messages */ int ishtp_use_dma_transfer(void); diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.c b/drivers/hid/intel-ish-hid/ishtp/hbm.c index 8b5dd580ceec..d0b847c86935 100644 --- a/drivers/hid/intel-ish-hid/ishtp/hbm.c +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.c @@ -136,19 +136,14 @@ int ishtp_hbm_start_wait(struct ishtp_device *dev) int ishtp_hbm_start_req(struct ishtp_device *dev) { struct ishtp_msg_hdr hdr; - unsigned char data[128]; - struct ishtp_msg_hdr *ishtp_hdr = &hdr; - struct hbm_host_version_request *start_req; - const size_t len = sizeof(struct hbm_host_version_request); + struct hbm_host_version_request start_req = { 0 }; - ishtp_hbm_hdr(ishtp_hdr, len); + ishtp_hbm_hdr(&hdr, sizeof(start_req)); /* host start message */ - start_req = (struct hbm_host_version_request *)data; - memset(start_req, 0, len); - start_req->hbm_cmd = HOST_START_REQ_CMD; - start_req->host_version.major_version = HBM_MAJOR_VERSION; - start_req->host_version.minor_version = HBM_MINOR_VERSION; + start_req.hbm_cmd = HOST_START_REQ_CMD; + start_req.host_version.major_version = HBM_MAJOR_VERSION; + start_req.host_version.minor_version = HBM_MINOR_VERSION; /* * (!) Response to HBM start may be so quick that this thread would get @@ -156,7 +151,7 @@ int ishtp_hbm_start_req(struct ishtp_device *dev) * So set it at first, change back to ISHTP_HBM_IDLE upon failure */ dev->hbm_state = ISHTP_HBM_START; - if (ishtp_write_message(dev, ishtp_hdr, data)) { + if (ishtp_write_message(dev, &hdr, &start_req)) { dev_err(dev->devc, "version message send failed\n"); dev->dev_state = ISHTP_DEV_RESETTING; dev->hbm_state = ISHTP_HBM_IDLE; @@ -178,19 +173,13 @@ int ishtp_hbm_start_req(struct ishtp_device *dev) void ishtp_hbm_enum_clients_req(struct ishtp_device *dev) { struct ishtp_msg_hdr hdr; - unsigned char data[128]; - struct ishtp_msg_hdr *ishtp_hdr = &hdr; - struct hbm_host_enum_request *enum_req; - const size_t len = sizeof(struct hbm_host_enum_request); + struct hbm_host_enum_request enum_req = { 0 }; /* enumerate clients */ - ishtp_hbm_hdr(ishtp_hdr, len); + ishtp_hbm_hdr(&hdr, sizeof(enum_req)); + enum_req.hbm_cmd = HOST_ENUM_REQ_CMD; - enum_req = (struct hbm_host_enum_request *)data; - memset(enum_req, 0, len); - enum_req->hbm_cmd = HOST_ENUM_REQ_CMD; - - if (ishtp_write_message(dev, ishtp_hdr, data)) { + if (ishtp_write_message(dev, &hdr, &enum_req)) { dev->dev_state = ISHTP_DEV_RESETTING; dev_err(dev->devc, "enumeration request send failed\n"); ish_hw_reset(dev); @@ -208,12 +197,8 @@ void ishtp_hbm_enum_clients_req(struct ishtp_device *dev) */ static int ishtp_hbm_prop_req(struct ishtp_device *dev) { - struct ishtp_msg_hdr hdr; - unsigned char data[128]; - struct ishtp_msg_hdr *ishtp_hdr = &hdr; - struct hbm_props_request *prop_req; - const size_t len = sizeof(struct hbm_props_request); + struct hbm_props_request prop_req = { 0 }; unsigned long next_client_index; uint8_t client_num; @@ -237,15 +222,12 @@ static int ishtp_hbm_prop_req(struct ishtp_device *dev) dev->fw_clients[client_num].client_id = next_client_index; - ishtp_hbm_hdr(ishtp_hdr, len); - prop_req = (struct hbm_props_request *)data; + ishtp_hbm_hdr(&hdr, sizeof(prop_req)); - memset(prop_req, 0, sizeof(struct hbm_props_request)); + prop_req.hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD; + prop_req.address = next_client_index; - prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD; - prop_req->address = next_client_index; - - if (ishtp_write_message(dev, ishtp_hdr, data)) { + if (ishtp_write_message(dev, &hdr, &prop_req)) { dev->dev_state = ISHTP_DEV_RESETTING; dev_err(dev->devc, "properties request send failed\n"); ish_hw_reset(dev); @@ -266,19 +248,14 @@ static int ishtp_hbm_prop_req(struct ishtp_device *dev) static void ishtp_hbm_stop_req(struct ishtp_device *dev) { struct ishtp_msg_hdr hdr; - unsigned char data[128]; - struct ishtp_msg_hdr *ishtp_hdr = &hdr; - struct hbm_host_stop_request *req; - const size_t len = sizeof(struct hbm_host_stop_request); + struct hbm_host_stop_request stop_req = { 0 } ; - ishtp_hbm_hdr(ishtp_hdr, len); - req = (struct hbm_host_stop_request *)data; + ishtp_hbm_hdr(&hdr, sizeof(stop_req)); - memset(req, 0, sizeof(struct hbm_host_stop_request)); - req->hbm_cmd = HOST_STOP_REQ_CMD; - req->reason = DRIVER_STOP_REQUEST; + stop_req.hbm_cmd = HOST_STOP_REQ_CMD; + stop_req.reason = DRIVER_STOP_REQUEST; - ishtp_write_message(dev, ishtp_hdr, data); + ishtp_write_message(dev, &hdr, &stop_req); } /** @@ -294,15 +271,15 @@ int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev, struct ishtp_cl *cl) { struct ishtp_msg_hdr hdr; - unsigned char data[128]; - struct ishtp_msg_hdr *ishtp_hdr = &hdr; - const size_t len = sizeof(struct hbm_flow_control); + struct hbm_flow_control flow_ctrl; + const size_t len = sizeof(flow_ctrl); int rv; unsigned long flags; spin_lock_irqsave(&cl->fc_spinlock, flags); - ishtp_hbm_hdr(ishtp_hdr, len); - ishtp_hbm_cl_hdr(cl, ISHTP_FLOW_CONTROL_CMD, data, len); + + ishtp_hbm_hdr(&hdr, len); + ishtp_hbm_cl_hdr(cl, ISHTP_FLOW_CONTROL_CMD, &flow_ctrl, len); /* * Sync possible race when RB recycle and packet receive paths @@ -315,7 +292,7 @@ int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev, cl->recv_msg_num_frags = 0; - rv = ishtp_write_message(dev, ishtp_hdr, data); + rv = ishtp_write_message(dev, &hdr, &flow_ctrl); if (!rv) { ++cl->out_flow_ctrl_creds; ++cl->out_flow_ctrl_cnt; @@ -345,14 +322,13 @@ int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev, int ishtp_hbm_cl_disconnect_req(struct ishtp_device *dev, struct ishtp_cl *cl) { struct ishtp_msg_hdr hdr; - unsigned char data[128]; - struct ishtp_msg_hdr *ishtp_hdr = &hdr; - const size_t len = sizeof(struct hbm_client_connect_request); + struct hbm_client_connect_request disconn_req; + const size_t len = sizeof(disconn_req); - ishtp_hbm_hdr(ishtp_hdr, len); - ishtp_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, data, len); + ishtp_hbm_hdr(&hdr, len); + ishtp_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, &disconn_req, len); - return ishtp_write_message(dev, ishtp_hdr, data); + return ishtp_write_message(dev, &hdr, &disconn_req); } /** @@ -391,14 +367,13 @@ static void ishtp_hbm_cl_disconnect_res(struct ishtp_device *dev, int ishtp_hbm_cl_connect_req(struct ishtp_device *dev, struct ishtp_cl *cl) { struct ishtp_msg_hdr hdr; - unsigned char data[128]; - struct ishtp_msg_hdr *ishtp_hdr = &hdr; - const size_t len = sizeof(struct hbm_client_connect_request); + struct hbm_client_connect_request conn_req; + const size_t len = sizeof(conn_req); - ishtp_hbm_hdr(ishtp_hdr, len); - ishtp_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, data, len); + ishtp_hbm_hdr(&hdr, len); + ishtp_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, &conn_req, len); - return ishtp_write_message(dev, ishtp_hdr, data); + return ishtp_write_message(dev, &hdr, &conn_req); } /** -- cgit v1.2.3 From 9b6872a123862cecd19a958f08eb9848b6a4240d Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 11 Feb 2019 15:53:34 -0600 Subject: HID: roccat: Mark expected switch fall-through MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. This patch fixes the following warning: drivers/hid/hid-roccat-kone.c: In function ‘kone_keep_values_up_to_date’: drivers/hid/hid-roccat-kone.c:784:20: warning: this statement may fall through [-Wimplicit-fallthrough=] kone->actual_dpi = kone->profiles[event->value - 1]. ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ startup_dpi; ~~~~~~~~~~~ drivers/hid/hid-roccat-kone.c:786:2: note: here case kone_mouse_event_osd_profile: ^~~~ Warning level 3 was used: -Wimplicit-fallthrough=3 This patch is part of the ongoing efforts to enable -Wimplicit-fallthrough. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Jiri Kosina --- drivers/hid/hid-roccat-kone.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index bf4675a27396..c4dd6162c1d6 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -783,6 +783,7 @@ static void kone_keep_values_up_to_date(struct kone_device *kone, case kone_mouse_event_switch_profile: kone->actual_dpi = kone->profiles[event->value - 1]. startup_dpi; + /* fall through */ case kone_mouse_event_osd_profile: kone->actual_profile = event->value; break; -- cgit v1.2.3 From 4e6e7d7252745ff589a5b02834c1b228d2c9140f Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Tue, 19 Feb 2019 17:58:56 +0000 Subject: HID: wacom: Add support for Pro Pen slim Wacom has introduced a new pen compatible with its MobileStudio Pro and other tablets. Although adding it to the tool ID tablet is not strictly necessary unrecognized pens are reported as BTN_TOOL_PEN already, unless the tablet sends the "eraser" bit, when BTN_TOOL_RUBBER is used instead), we'll keep it updated anyway. Signed-off-by: Jason Gerecke Signed-off-by: Benjamin Tissoires --- drivers/hid/wacom_wac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 5ecda99bf431..747730d32ab6 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -626,6 +626,7 @@ static int wacom_intuos_get_tool_type(int tool_id) case 0x8e2: /* IntuosHT2 pen */ case 0x022: case 0x10804: /* Intuos4/5 13HD/24HD Art Pen */ + case 0x10842: /* MobileStudio Pro Pro Pen slim */ case 0x14802: /* Intuos4/5 13HD/24HD Classic Pen */ case 0x16802: /* Cintiq 13HD Pro Pen */ case 0x18802: /* DTH2242 Pen */ @@ -667,6 +668,7 @@ static int wacom_intuos_get_tool_type(int tool_id) case 0x1480a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */ case 0x1090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ case 0x1080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */ + case 0x1084a: /* MobileStudio Pro Pro Pen slim Eraser */ case 0x1680a: /* Cintiq 13HD Pro Pen Eraser */ case 0x1880a: /* DTH2242 Eraser */ case 0x1080a: /* Intuos4/5 13HD/24HD General Pen Eraser */ -- cgit v1.2.3 From 5e55e2aa802d429800b793080a48f310eaf82138 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:47 +0200 Subject: 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 }, -- cgit v1.2.3 From 7c2af0a16e1056e2c208c5a5295f53a0c96f4aca Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:48 +0200 Subject: 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"); -- cgit v1.2.3 From bf226cd3316a01d71eb74ea5736f6c8905ff5041 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:49 +0200 Subject: 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) }, -- cgit v1.2.3 From ff0c13d6d2edc9c4952c668f4503a51b5f101ab3 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:50 +0200 Subject: 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 ++++++++++++++++++ drivers/hid/hid-uclogic-rdesc.c | 683 ++++++++++++++++++++++++ drivers/hid/hid-uclogic-rdesc.h | 117 +++++ drivers/hid/hid-uclogic.c | 1090 --------------------------------------- 5 files changed, 1295 insertions(+), 1090 deletions(-) create mode 100644 drivers/hid/hid-uclogic-core.c create mode 100644 drivers/hid/hid-uclogic-rdesc.c create mode 100644 drivers/hid/hid-uclogic-rdesc.h delete mode 100644 drivers/hid/hid-uclogic.c 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-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c new file mode 100644 index 000000000000..e9053d28f4e0 --- /dev/null +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -0,0 +1,683 @@ +// 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-2017 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 "hid-uclogic-rdesc.h" +#include +#include + +/* Fixed WP4030U report descriptor */ +__u8 uclogic_rdesc_wp4030u_fixed_arr[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x09, /* Report ID (9), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x05, /* Report Count (5), */ + 0x81, 0x01, /* Input (Constant), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x14, /* Logical Minimum (0), */ + 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, 0xA0, 0x0F, /* Physical Maximum (4000), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0xB8, 0x0B, /* Physical Maximum (3000), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 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 */ +}; + +const size_t uclogic_rdesc_wp4030u_fixed_size = + sizeof(uclogic_rdesc_wp4030u_fixed_arr); + +/* Fixed WP5540U report descriptor */ +__u8 uclogic_rdesc_wp5540u_fixed_arr[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x09, /* Report ID (9), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x05, /* Report Count (5), */ + 0x81, 0x01, /* Input (Constant), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x14, /* Logical Minimum (0), */ + 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, 0x7C, 0x15, /* Physical Maximum (5500), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0xA0, 0x0F, /* Physical Maximum (4000), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 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, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x02, /* Usage (Mouse), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x08, /* Report ID (8), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x01, /* Report Size (1), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x03, /* Usage Maximum (03h), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x05, /* Report Count (5), */ + 0x81, 0x01, /* Input (Constant), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x75, 0x08, /* Report Size (8), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x15, 0x81, /* Logical Minimum (-127), */ + 0x25, 0x7F, /* Logical Maximum (127), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x09, 0x38, /* Usage (Wheel), */ + 0x15, 0xFF, /* Logical Minimum (-1), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x81, 0x01, /* Input (Constant), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +const size_t uclogic_rdesc_wp5540u_fixed_size = + sizeof(uclogic_rdesc_wp5540u_fixed_arr); + +/* Fixed WP8060U report descriptor */ +__u8 uclogic_rdesc_wp8060u_fixed_arr[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x09, /* Report ID (9), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x05, /* Report Count (5), */ + 0x81, 0x01, /* Input (Constant), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x14, /* Logical Minimum (0), */ + 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, 0x40, 0x1F, /* Physical Maximum (8000), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0x70, 0x17, /* Physical Maximum (6000), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 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, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x02, /* Usage (Mouse), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x08, /* Report ID (8), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x01, /* Report Size (1), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x03, /* Usage Maximum (03h), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x05, /* Report Count (5), */ + 0x81, 0x01, /* Input (Constant), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x75, 0x08, /* Report Size (8), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x15, 0x81, /* Logical Minimum (-127), */ + 0x25, 0x7F, /* Logical Maximum (127), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x09, 0x38, /* Usage (Wheel), */ + 0x15, 0xFF, /* Logical Minimum (-1), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x81, 0x01, /* Input (Constant), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +const size_t uclogic_rdesc_wp8060u_fixed_size = + sizeof(uclogic_rdesc_wp8060u_fixed_arr); + +/* Fixed WP1062 report descriptor */ +__u8 uclogic_rdesc_wp1062_fixed_arr[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x09, /* Report ID (9), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x04, /* Report Count (4), */ + 0x81, 0x01, /* Input (Constant), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x14, /* Logical Minimum (0), */ + 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, 0x10, 0x27, /* Physical Maximum (10000), */ + 0x26, 0x20, 0x4E, /* Logical Maximum (20000), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0xB7, 0x19, /* Physical Maximum (6583), */ + 0x26, 0x6E, 0x33, /* Logical Maximum (13166), */ + 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 */ +}; + +const size_t uclogic_rdesc_wp1062_fixed_size = + sizeof(uclogic_rdesc_wp1062_fixed_arr); + +/* Fixed PF1209 report descriptor */ +__u8 uclogic_rdesc_pf1209_fixed_arr[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x09, /* Report ID (9), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x05, /* Report Count (5), */ + 0x81, 0x01, /* Input (Constant), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x14, /* Logical Minimum (0), */ + 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, 0xE0, 0x2E, /* Physical Maximum (12000), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0x28, 0x23, /* Physical Maximum (9000), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 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, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x02, /* Usage (Mouse), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x08, /* Report ID (8), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x01, /* Report Size (1), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x03, /* Usage Maximum (03h), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x05, /* Report Count (5), */ + 0x81, 0x01, /* Input (Constant), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x75, 0x08, /* Report Size (8), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x15, 0x81, /* Logical Minimum (-127), */ + 0x25, 0x7F, /* Logical Maximum (127), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x09, 0x38, /* Usage (Wheel), */ + 0x15, 0xFF, /* Logical Minimum (-1), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x81, 0x01, /* Input (Constant), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +const size_t uclogic_rdesc_pf1209_fixed_size = + sizeof(uclogic_rdesc_pf1209_fixed_arr); + +/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */ +__u8 uclogic_rdesc_twhl850_fixed0_arr[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x09, /* Report ID (9), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x81, 0x02, /* Input (Variable), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x95, 0x01, /* Report Count (1), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x81, 0x02, /* Input (Variable), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 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, 0x40, 0x1F, /* Physical Maximum (8000), */ + 0x26, 0x00, 0x7D, /* Logical Maximum (32000), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0x88, 0x13, /* Physical Maximum (5000), */ + 0x26, 0x20, 0x4E, /* Logical Maximum (20000), */ + 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 */ +}; + +const size_t uclogic_rdesc_twhl850_fixed0_size = + sizeof(uclogic_rdesc_twhl850_fixed0_arr); + +/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */ +__u8 uclogic_rdesc_twhl850_fixed1_arr[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x02, /* Usage (Mouse), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x01, /* Report ID (1), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xA0, /* Collection (Physical), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x03, /* Usage Maximum (03h), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x05, /* Report Count (5), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x16, 0x00, 0x80, /* Logical Minimum (-32768), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x09, 0x38, /* Usage (Wheel), */ + 0x15, 0xFF, /* Logical Minimum (-1), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x01, /* Report Count (1), */ + 0x75, 0x08, /* Report Size (8), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0xC0, /* End Collection, */ + 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) */ +__u8 uclogic_rdesc_twhl850_fixed2_arr[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x06, /* Usage (Keyboard), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x03, /* Report ID (3), */ + 0x05, 0x07, /* Usage Page (Keyboard), */ + 0x14, /* Logical Minimum (0), */ + 0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */ + 0x29, 0xE7, /* Usage Maximum (KB Right GUI), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x02, /* Input (Variable), */ + 0x18, /* Usage Minimum (None), */ + 0x29, 0xFF, /* Usage Maximum (FFh), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x06, /* Report Count (6), */ + 0x80, /* Input, */ + 0xC0 /* End Collection */ +}; + +const size_t uclogic_rdesc_twhl850_fixed2_size = + sizeof(uclogic_rdesc_twhl850_fixed2_arr); + +/* Fixed TWHA60 report descriptor, interface 0 (stylus) */ +__u8 uclogic_rdesc_twha60_fixed0_arr[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x09, /* Report ID (9), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x04, /* Report Count (4), */ + 0x81, 0x01, /* Input (Constant), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x14, /* Logical Minimum (0), */ + 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, 0x10, 0x27, /* Physical Maximum (10000), */ + 0x27, 0x3F, 0x9C, + 0x00, 0x00, /* Logical Maximum (39999), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0x6A, 0x18, /* Physical Maximum (6250), */ + 0x26, 0xA7, 0x61, /* Logical Maximum (24999), */ + 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 */ +}; + +const size_t uclogic_rdesc_twha60_fixed0_size = + sizeof(uclogic_rdesc_twha60_fixed0_arr); + +/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */ +__u8 uclogic_rdesc_twha60_fixed1_arr[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x06, /* Usage (Keyboard), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x05, /* Report ID (5), */ + 0x05, 0x07, /* Usage Page (Keyboard), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x01, /* Input (Constant), */ + 0x95, 0x0C, /* Report Count (12), */ + 0x19, 0x3A, /* Usage Minimum (KB F1), */ + 0x29, 0x45, /* Usage Maximum (KB F12), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x0C, /* Report Count (12), */ + 0x19, 0x68, /* Usage Minimum (KB F13), */ + 0x29, 0x73, /* Usage Maximum (KB F24), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x01, /* Input (Constant), */ + 0xC0 /* End Collection */ +}; + +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[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x07, /* Report ID (7), */ + 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), */ + 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), */ + 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), */ + 0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +const size_t uclogic_rdesc_pen_template_size = + sizeof(uclogic_rdesc_pen_template_arr); + +/** + * Expand to the contents of a generic buttonpad report descriptor. + * + * @_padding: Padding from the end of button bits at bit 44, until + * the end of the report, in bits. + */ +#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) +{ + static const __u8 head[] = {UCLOGIC_RDESC_PH_HEAD}; + __u8 *rdesc_ptr; + __u8 *p; + s32 v; + + rdesc_ptr = kmemdup(template_ptr, template_size, GFP_KERNEL); + if (rdesc_ptr == NULL) + return NULL; + + 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 += sizeof(head) + 1; + } else { + p++; + } + } + + return rdesc_ptr; +} 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 */ diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c deleted file mode 100644 index fdc4d1ea7049..000000000000 --- a/drivers/hid/hid-uclogic.c +++ /dev/null @@ -1,1090 +0,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 -#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[] = { - 0x05, 0x0D, /* Usage Page (Digitizer), */ - 0x09, 0x02, /* Usage (Pen), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x09, /* Report ID (9), */ - 0x09, 0x20, /* Usage (Stylus), */ - 0xA0, /* Collection (Physical), */ - 0x75, 0x01, /* Report Size (1), */ - 0x09, 0x42, /* Usage (Tip Switch), */ - 0x09, 0x44, /* Usage (Barrel Switch), */ - 0x09, 0x46, /* Usage (Tablet Pick), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x05, /* Report Count (5), */ - 0x81, 0x01, /* Input (Constant), */ - 0x75, 0x10, /* Report Size (16), */ - 0x95, 0x01, /* Report Count (1), */ - 0x14, /* Logical Minimum (0), */ - 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, 0xA0, 0x0F, /* Physical Maximum (4000), */ - 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ - 0x81, 0x02, /* Input (Variable), */ - 0x09, 0x31, /* Usage (Y), */ - 0x46, 0xB8, 0x0B, /* Physical Maximum (3000), */ - 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ - 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 */ -}; - -/* Fixed WP5540U report descriptor */ -static __u8 wp5540u_rdesc_fixed[] = { - 0x05, 0x0D, /* Usage Page (Digitizer), */ - 0x09, 0x02, /* Usage (Pen), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x09, /* Report ID (9), */ - 0x09, 0x20, /* Usage (Stylus), */ - 0xA0, /* Collection (Physical), */ - 0x75, 0x01, /* Report Size (1), */ - 0x09, 0x42, /* Usage (Tip Switch), */ - 0x09, 0x44, /* Usage (Barrel Switch), */ - 0x09, 0x46, /* Usage (Tablet Pick), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x05, /* Report Count (5), */ - 0x81, 0x01, /* Input (Constant), */ - 0x75, 0x10, /* Report Size (16), */ - 0x95, 0x01, /* Report Count (1), */ - 0x14, /* Logical Minimum (0), */ - 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, 0x7C, 0x15, /* Physical Maximum (5500), */ - 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ - 0x81, 0x02, /* Input (Variable), */ - 0x09, 0x31, /* Usage (Y), */ - 0x46, 0xA0, 0x0F, /* Physical Maximum (4000), */ - 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ - 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, 0x01, /* Usage Page (Desktop), */ - 0x09, 0x02, /* Usage (Mouse), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x08, /* Report ID (8), */ - 0x09, 0x01, /* Usage (Pointer), */ - 0xA0, /* Collection (Physical), */ - 0x75, 0x01, /* Report Size (1), */ - 0x05, 0x09, /* Usage Page (Button), */ - 0x19, 0x01, /* Usage Minimum (01h), */ - 0x29, 0x03, /* Usage Maximum (03h), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x05, /* Report Count (5), */ - 0x81, 0x01, /* Input (Constant), */ - 0x05, 0x01, /* Usage Page (Desktop), */ - 0x75, 0x08, /* Report Size (8), */ - 0x09, 0x30, /* Usage (X), */ - 0x09, 0x31, /* Usage (Y), */ - 0x15, 0x81, /* Logical Minimum (-127), */ - 0x25, 0x7F, /* Logical Maximum (127), */ - 0x95, 0x02, /* Report Count (2), */ - 0x81, 0x06, /* Input (Variable, Relative), */ - 0x09, 0x38, /* Usage (Wheel), */ - 0x15, 0xFF, /* Logical Minimum (-1), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x01, /* Report Count (1), */ - 0x81, 0x06, /* Input (Variable, Relative), */ - 0x81, 0x01, /* Input (Constant), */ - 0xC0, /* End Collection, */ - 0xC0 /* End Collection */ -}; - -/* Fixed WP8060U report descriptor */ -static __u8 wp8060u_rdesc_fixed[] = { - 0x05, 0x0D, /* Usage Page (Digitizer), */ - 0x09, 0x02, /* Usage (Pen), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x09, /* Report ID (9), */ - 0x09, 0x20, /* Usage (Stylus), */ - 0xA0, /* Collection (Physical), */ - 0x75, 0x01, /* Report Size (1), */ - 0x09, 0x42, /* Usage (Tip Switch), */ - 0x09, 0x44, /* Usage (Barrel Switch), */ - 0x09, 0x46, /* Usage (Tablet Pick), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x05, /* Report Count (5), */ - 0x81, 0x01, /* Input (Constant), */ - 0x75, 0x10, /* Report Size (16), */ - 0x95, 0x01, /* Report Count (1), */ - 0x14, /* Logical Minimum (0), */ - 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, 0x40, 0x1F, /* Physical Maximum (8000), */ - 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ - 0x81, 0x02, /* Input (Variable), */ - 0x09, 0x31, /* Usage (Y), */ - 0x46, 0x70, 0x17, /* Physical Maximum (6000), */ - 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ - 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, 0x01, /* Usage Page (Desktop), */ - 0x09, 0x02, /* Usage (Mouse), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x08, /* Report ID (8), */ - 0x09, 0x01, /* Usage (Pointer), */ - 0xA0, /* Collection (Physical), */ - 0x75, 0x01, /* Report Size (1), */ - 0x05, 0x09, /* Usage Page (Button), */ - 0x19, 0x01, /* Usage Minimum (01h), */ - 0x29, 0x03, /* Usage Maximum (03h), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x05, /* Report Count (5), */ - 0x81, 0x01, /* Input (Constant), */ - 0x05, 0x01, /* Usage Page (Desktop), */ - 0x75, 0x08, /* Report Size (8), */ - 0x09, 0x30, /* Usage (X), */ - 0x09, 0x31, /* Usage (Y), */ - 0x15, 0x81, /* Logical Minimum (-127), */ - 0x25, 0x7F, /* Logical Maximum (127), */ - 0x95, 0x02, /* Report Count (2), */ - 0x81, 0x06, /* Input (Variable, Relative), */ - 0x09, 0x38, /* Usage (Wheel), */ - 0x15, 0xFF, /* Logical Minimum (-1), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x01, /* Report Count (1), */ - 0x81, 0x06, /* Input (Variable, Relative), */ - 0x81, 0x01, /* Input (Constant), */ - 0xC0, /* End Collection, */ - 0xC0 /* End Collection */ -}; - -/* Size of the original descriptor of WP1062 tablet */ -#define WP1062_RDESC_ORIG_SIZE 254 - -/* Fixed WP1062 report descriptor */ -static __u8 wp1062_rdesc_fixed[] = { - 0x05, 0x0D, /* Usage Page (Digitizer), */ - 0x09, 0x02, /* Usage (Pen), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x09, /* Report ID (9), */ - 0x09, 0x20, /* Usage (Stylus), */ - 0xA0, /* Collection (Physical), */ - 0x75, 0x01, /* Report Size (1), */ - 0x09, 0x42, /* Usage (Tip Switch), */ - 0x09, 0x44, /* Usage (Barrel Switch), */ - 0x09, 0x46, /* Usage (Tablet Pick), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x04, /* Report Count (4), */ - 0x81, 0x01, /* Input (Constant), */ - 0x09, 0x32, /* Usage (In Range), */ - 0x95, 0x01, /* Report Count (1), */ - 0x81, 0x02, /* Input (Variable), */ - 0x75, 0x10, /* Report Size (16), */ - 0x95, 0x01, /* Report Count (1), */ - 0x14, /* Logical Minimum (0), */ - 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, 0x10, 0x27, /* Physical Maximum (10000), */ - 0x26, 0x20, 0x4E, /* Logical Maximum (20000), */ - 0x81, 0x02, /* Input (Variable), */ - 0x09, 0x31, /* Usage (Y), */ - 0x46, 0xB7, 0x19, /* Physical Maximum (6583), */ - 0x26, 0x6E, 0x33, /* Logical Maximum (13166), */ - 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 */ -}; - -/* Size of the original descriptor of PF1209 tablet */ -#define PF1209_RDESC_ORIG_SIZE 234 - -/* Fixed PF1209 report descriptor */ -static __u8 pf1209_rdesc_fixed[] = { - 0x05, 0x0D, /* Usage Page (Digitizer), */ - 0x09, 0x02, /* Usage (Pen), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x09, /* Report ID (9), */ - 0x09, 0x20, /* Usage (Stylus), */ - 0xA0, /* Collection (Physical), */ - 0x75, 0x01, /* Report Size (1), */ - 0x09, 0x42, /* Usage (Tip Switch), */ - 0x09, 0x44, /* Usage (Barrel Switch), */ - 0x09, 0x46, /* Usage (Tablet Pick), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x05, /* Report Count (5), */ - 0x81, 0x01, /* Input (Constant), */ - 0x75, 0x10, /* Report Size (16), */ - 0x95, 0x01, /* Report Count (1), */ - 0x14, /* Logical Minimum (0), */ - 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, 0xE0, 0x2E, /* Physical Maximum (12000), */ - 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ - 0x81, 0x02, /* Input (Variable), */ - 0x09, 0x31, /* Usage (Y), */ - 0x46, 0x28, 0x23, /* Physical Maximum (9000), */ - 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ - 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, 0x01, /* Usage Page (Desktop), */ - 0x09, 0x02, /* Usage (Mouse), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x08, /* Report ID (8), */ - 0x09, 0x01, /* Usage (Pointer), */ - 0xA0, /* Collection (Physical), */ - 0x75, 0x01, /* Report Size (1), */ - 0x05, 0x09, /* Usage Page (Button), */ - 0x19, 0x01, /* Usage Minimum (01h), */ - 0x29, 0x03, /* Usage Maximum (03h), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x05, /* Report Count (5), */ - 0x81, 0x01, /* Input (Constant), */ - 0x05, 0x01, /* Usage Page (Desktop), */ - 0x75, 0x08, /* Report Size (8), */ - 0x09, 0x30, /* Usage (X), */ - 0x09, 0x31, /* Usage (Y), */ - 0x15, 0x81, /* Logical Minimum (-127), */ - 0x25, 0x7F, /* Logical Maximum (127), */ - 0x95, 0x02, /* Report Count (2), */ - 0x81, 0x06, /* Input (Variable, Relative), */ - 0x09, 0x38, /* Usage (Wheel), */ - 0x15, 0xFF, /* Logical Minimum (-1), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x01, /* Report Count (1), */ - 0x81, 0x06, /* Input (Variable, Relative), */ - 0x81, 0x01, /* Input (Constant), */ - 0xC0, /* End Collection, */ - 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 - -/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */ -static __u8 twhl850_rdesc_fixed0[] = { - 0x05, 0x0D, /* Usage Page (Digitizer), */ - 0x09, 0x02, /* Usage (Pen), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x09, /* Report ID (9), */ - 0x09, 0x20, /* Usage (Stylus), */ - 0xA0, /* Collection (Physical), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x75, 0x01, /* Report Size (1), */ - 0x95, 0x03, /* Report Count (3), */ - 0x09, 0x42, /* Usage (Tip Switch), */ - 0x09, 0x44, /* Usage (Barrel Switch), */ - 0x09, 0x46, /* Usage (Tablet Pick), */ - 0x81, 0x02, /* Input (Variable), */ - 0x81, 0x03, /* Input (Constant, Variable), */ - 0x95, 0x01, /* Report Count (1), */ - 0x09, 0x32, /* Usage (In Range), */ - 0x81, 0x02, /* Input (Variable), */ - 0x81, 0x03, /* Input (Constant, Variable), */ - 0x75, 0x10, /* Report Size (16), */ - 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, 0x40, 0x1F, /* Physical Maximum (8000), */ - 0x26, 0x00, 0x7D, /* Logical Maximum (32000), */ - 0x81, 0x02, /* Input (Variable), */ - 0x09, 0x31, /* Usage (Y), */ - 0x46, 0x88, 0x13, /* Physical Maximum (5000), */ - 0x26, 0x20, 0x4E, /* Logical Maximum (20000), */ - 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 */ -}; - -/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */ -static __u8 twhl850_rdesc_fixed1[] = { - 0x05, 0x01, /* Usage Page (Desktop), */ - 0x09, 0x02, /* Usage (Mouse), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x01, /* Report ID (1), */ - 0x09, 0x01, /* Usage (Pointer), */ - 0xA0, /* Collection (Physical), */ - 0x05, 0x09, /* Usage Page (Button), */ - 0x75, 0x01, /* Report Size (1), */ - 0x95, 0x03, /* Report Count (3), */ - 0x19, 0x01, /* Usage Minimum (01h), */ - 0x29, 0x03, /* Usage Maximum (03h), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x05, /* Report Count (5), */ - 0x81, 0x03, /* Input (Constant, Variable), */ - 0x05, 0x01, /* Usage Page (Desktop), */ - 0x09, 0x30, /* Usage (X), */ - 0x09, 0x31, /* Usage (Y), */ - 0x16, 0x00, 0x80, /* Logical Minimum (-32768), */ - 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ - 0x75, 0x10, /* Report Size (16), */ - 0x95, 0x02, /* Report Count (2), */ - 0x81, 0x06, /* Input (Variable, Relative), */ - 0x09, 0x38, /* Usage (Wheel), */ - 0x15, 0xFF, /* Logical Minimum (-1), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x01, /* Report Count (1), */ - 0x75, 0x08, /* Report Size (8), */ - 0x81, 0x06, /* Input (Variable, Relative), */ - 0x81, 0x03, /* Input (Constant, Variable), */ - 0xC0, /* End Collection, */ - 0xC0 /* End Collection */ -}; - -/* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */ -static __u8 twhl850_rdesc_fixed2[] = { - 0x05, 0x01, /* Usage Page (Desktop), */ - 0x09, 0x06, /* Usage (Keyboard), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x03, /* Report ID (3), */ - 0x05, 0x07, /* Usage Page (Keyboard), */ - 0x14, /* Logical Minimum (0), */ - 0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */ - 0x29, 0xE7, /* Usage Maximum (KB Right GUI), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x75, 0x01, /* Report Size (1), */ - 0x95, 0x08, /* Report Count (8), */ - 0x81, 0x02, /* Input (Variable), */ - 0x18, /* Usage Minimum (None), */ - 0x29, 0xFF, /* Usage Maximum (FFh), */ - 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ - 0x75, 0x08, /* Report Size (8), */ - 0x95, 0x06, /* Report Count (6), */ - 0x80, /* Input, */ - 0xC0 /* End Collection */ -}; - -/* Size of the original descriptors of TWHA60 tablet */ -#define TWHA60_RDESC_ORIG_SIZE0 254 -#define TWHA60_RDESC_ORIG_SIZE1 139 - -/* Fixed TWHA60 report descriptor, interface 0 (stylus) */ -static __u8 twha60_rdesc_fixed0[] = { - 0x05, 0x0D, /* Usage Page (Digitizer), */ - 0x09, 0x02, /* Usage (Pen), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x09, /* Report ID (9), */ - 0x09, 0x20, /* Usage (Stylus), */ - 0xA0, /* Collection (Physical), */ - 0x75, 0x01, /* Report Size (1), */ - 0x09, 0x42, /* Usage (Tip Switch), */ - 0x09, 0x44, /* Usage (Barrel Switch), */ - 0x09, 0x46, /* Usage (Tablet Pick), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x95, 0x03, /* Report Count (3), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x04, /* Report Count (4), */ - 0x81, 0x01, /* Input (Constant), */ - 0x09, 0x32, /* Usage (In Range), */ - 0x95, 0x01, /* Report Count (1), */ - 0x81, 0x02, /* Input (Variable), */ - 0x75, 0x10, /* Report Size (16), */ - 0x95, 0x01, /* Report Count (1), */ - 0x14, /* Logical Minimum (0), */ - 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, 0x10, 0x27, /* Physical Maximum (10000), */ - 0x27, 0x3F, 0x9C, - 0x00, 0x00, /* Logical Maximum (39999), */ - 0x81, 0x02, /* Input (Variable), */ - 0x09, 0x31, /* Usage (Y), */ - 0x46, 0x6A, 0x18, /* Physical Maximum (6250), */ - 0x26, 0xA7, 0x61, /* Logical Maximum (24999), */ - 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 */ -}; - -/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */ -static __u8 twha60_rdesc_fixed1[] = { - 0x05, 0x01, /* Usage Page (Desktop), */ - 0x09, 0x06, /* Usage (Keyboard), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x05, /* Report ID (5), */ - 0x05, 0x07, /* Usage Page (Keyboard), */ - 0x14, /* Logical Minimum (0), */ - 0x25, 0x01, /* Logical Maximum (1), */ - 0x75, 0x01, /* Report Size (1), */ - 0x95, 0x08, /* Report Count (8), */ - 0x81, 0x01, /* Input (Constant), */ - 0x95, 0x0C, /* Report Count (12), */ - 0x19, 0x3A, /* Usage Minimum (KB F1), */ - 0x29, 0x45, /* Usage Maximum (KB F12), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x0C, /* Report Count (12), */ - 0x19, 0x68, /* Usage Minimum (KB F13), */ - 0x29, 0x73, /* Usage Maximum (KB F24), */ - 0x81, 0x02, /* Input (Variable), */ - 0x95, 0x08, /* Report Count (8), */ - 0x81, 0x01, /* Input (Constant), */ - 0xC0 /* End Collection */ -}; - -/* Report descriptor template placeholder head */ -#define UCLOGIC_PH_HEAD 0xFE, 0xED, 0x1D - -/* 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[] = { - 0x05, 0x0D, /* Usage Page (Digitizer), */ - 0x09, 0x02, /* Usage (Pen), */ - 0xA1, 0x01, /* Collection (Application), */ - 0x85, 0x07, /* Report ID (7), */ - 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), */ - 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), */ - 0x27, UCLOGIC_PH(X_LM), /* Logical Maximum (PLACEHOLDER), */ - 0x47, UCLOGIC_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), */ - 0x81, 0x02, /* Input (Variable), */ - 0xB4, /* Pop, */ - 0x09, 0x30, /* Usage (Tip Pressure), */ - 0x27, - UCLOGIC_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; -} - -/** - * 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_PH_ID_NUM]; - s32 resolution; - __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; - } - - /* 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]]; - put_unaligned(cpu_to_le32(v), (s32 *)p); - p += 4; - } else { - p++; - } - } - - 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 + 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"); -- cgit v1.2.3 From 9614219e9310ef19e66719bf37f9f68919bac08e Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:51 +0200 Subject: 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 | 428 +++++---------------- drivers/hid/hid-uclogic-params.c | 806 +++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-uclogic-params.h | 180 +++++++++ 4 files changed, 1090 insertions(+), 327 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; - } - - 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; + if (drvdata->desc_ptr != NULL) { + rdesc = drvdata->desc_ptr; + *rsize = drvdata->desc_size; } - 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,104 +126,97 @@ 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; - - 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; + /* 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; + /* 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) +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; - if ((report->type == HID_INPUT_REPORT) && - (report->id == UCLOGIC_RDESC_PEN_ID) && + /* Tweak pen reports, if necessary */ + if (!params->pen_unused && + (report->type == HID_INPUT_REPORT) && + (report->id == params->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) + /* 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 void uclogic_remove(struct hid_device *hdev) +{ + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + + hid_hw_stop(hdev); + kfree(drvdata->desc_ptr); + uclogic_params_cleanup(&drvdata->params); +} + static const struct hid_device_id uclogic_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, @@ -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 */ -- cgit v1.2.3 From 251b427566e2cf6ec87dd479e2d3062fdbd41732 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:52 +0200 Subject: 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 @@ failure: 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); -- cgit v1.2.3 From eecb5b845b1a724422e139db9ade356cdf3955e5 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:53 +0200 Subject: 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 */ -- cgit v1.2.3 From 01309e29eb95c16bd48984f2589fad0cbf5e27d1 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:54 +0200 Subject: 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 */ -- cgit v1.2.3 From 59f2e0fca8ca84f02a718c0f3cd72990d03545be Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:55 +0200 Subject: 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, \ -- cgit v1.2.3 From 2c3a88c64cb62cc59010359dbfc97f734e98d683 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:56 +0200 Subject: 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 @@ cleanup: 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 @@ output: 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 */ -- cgit v1.2.3 From 0c15efe9ef7f2042234485ad3a7b09567b9821f6 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:57 +0200 Subject: 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; -- cgit v1.2.3 From 9c17f7353928ddb0ab5ee4cb8b29d7ddf8f8eab7 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:58 +0200 Subject: 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 -- cgit v1.2.3 From 1ee7c6852351fb0a025fe87e6a2570df4e4e24de Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:13:59 +0200 Subject: 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 */ -- cgit v1.2.3 From c3e5a67c46e560faf66a63d9c10514eb4d2a0432 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:00 +0200 Subject: 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 */ -- cgit v1.2.3 From 492a9e9a3c439a2ff486c60213fa5da3f465c2e9 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:01 +0200 Subject: 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 */ -- cgit v1.2.3 From 08367be171b0b7d6ff030a351a58d34f77803685 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:02 +0200 Subject: 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 */ -- cgit v1.2.3 From fde44ac556359b0fd56e11b889686377392b7407 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:03 +0200 Subject: 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. */ -- cgit v1.2.3 From 8a47670c35e2a8e70753eabd96d4f8d8b3c0eeba Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:04 +0200 Subject: 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 -- cgit v1.2.3 From e902ed9344873ba199093958ca7bdc3d125828f6 Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:05 +0200 Subject: 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 */ -- cgit v1.2.3 From 8547b7789c62f81350422d357fdf48bfe5d9932f Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:06 +0200 Subject: 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 @@ cleanup: * 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; } -- cgit v1.2.3 From c970f8453fe1cd33d1969b807a7217af391cfdba Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:07 +0200 Subject: 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 -- cgit v1.2.3 From 3c261a14f5204809418748bd315f7d21087a150d Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:08 +0200 Subject: 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) }, -- cgit v1.2.3 From 24b2f66628ac7e0de06666501f682d77c6c0485d Mon Sep 17 00:00:00 2001 From: Nikolai Kondrashov Date: Sun, 10 Feb 2019 12:14:09 +0200 Subject: 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) }, -- cgit v1.2.3