Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 Sep 2021 21:30:46 +0000 (14:30 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 2 Sep 2021 21:30:46 +0000 (14:30 -0700)
Pull HID updates from Jiri Kosina:

 - Fix in i2c-hid driver for Elan touchpad quirk regression (Jim
   Broadus)

 - Quirk preventing ASUS Claymore from accidentally suspending whole
   system (Luke D. Jones)

 - Updates to the existing FW reporting mechanism, MP2 FW status checks,
   adding proper power management support for amd-sfh (Basavaraj
   Natikar)

 - Regression fix for an issue in HID core that got uncovered by recent
   USB core cleanup leading to issues when transfer_buffer_length is not
   in line with wLength (Alan Stern)

 - Memory leak fix in USB HID core (Anirudh Rayabharam)

 - Improvement of stylus battery reporting (Dmitry Torokhov)

 - Power management improvement for Goodix driver (Douglas Anderson)

 - High-resolution scroll support for Magicmouse devices (José Expósito)

 - Support for GHLive PS4 dongles (Daniel Nguyen)

 - Support proper EV_MSC emissions to hid-apple (Vincent Lefevre)

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (31 commits)
  HID: usbhid: Simplify code in hid_submit_ctrl()
  HID: usbhid: Fix warning caused by 0-length input reports
  HID: usbhid: Fix flood of "control queue full" messages
  HID: sony: Fix more ShanWan clone gamepads to not rumble when plugged in.
  HID: sony: support for the ghlive ps4 dongles
  HID: thrustmaster: clean up Makefile and adapt quirks
  HID: i2c-hid: Fix Elan touchpad regression
  HID: asus: Prevent Claymore sending suspend event
  HID: amd_sfh: Add dyndbg prints for debugging
  HID: amd_sfh: Add support for PM suspend and resume
  HID: amd_sfh: Move hid probe after sensor is enabled
  HID: amd_sfh: Add command response to check command status
  HID: amd_sfh: Fix period data field to enable sensor
  HID: logitech-hidpp: battery: provide CAPACITY property for newer devices
  HID: thrustmaster: Fix memory leak in thrustmaster_interrupts()
  HID: thrustmaster: Fix memory leak in remove
  HID: thrustmaster: Fix memory leaks in probe
  HID: elo: update the reference count of the usb device structure
  HID: logitech-hidpp: Use 'atomic_inc_return' instead of hand-writing it
  HID: apple: Add missing scan code event for keys handled by hid-apple
  ...

22 files changed:
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/amd-sfh-hid/amd_sfh_client.c
drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
drivers/hid/hid-apple.c
drivers/hid/hid-asus.c
drivers/hid/hid-cmedia.c
drivers/hid/hid-elo.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-logitech-hidpp.c
drivers/hid/hid-magicmouse.c
drivers/hid/hid-quirks.c
drivers/hid/hid-sony.c
drivers/hid/hid-thrustmaster.c
drivers/hid/i2c-hid/i2c-hid-core.c
drivers/hid/i2c-hid/i2c-hid-of-goodix.c
drivers/hid/usbhid/hid-core.c
drivers/hid/wacom_sys.c
drivers/hid/wacom_wac.c
drivers/hid/wacom_wac.h

index 76937f7..3c33bf5 100644 (file)
@@ -259,10 +259,11 @@ config HID_PRODIKEYS
          and some additional multimedia keys.
 
 config HID_CMEDIA
-       tristate "CMedia CM6533 HID audio jack controls"
+       tristate "CMedia audio chips"
        depends on HID
        help
-       Support for CMedia CM6533 HID audio jack controls.
+       Support for CMedia CM6533 HID audio jack controls
+        and HS100B mute buttons.
 
 config HID_CP2112
        tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support"
@@ -952,7 +953,7 @@ config HID_SONY
          * Buzz controllers
          * Sony PS3 Blue-ray Disk Remote Control (Bluetooth)
          * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth)
-         * Guitar Hero Live PS3 and Wii U guitar dongles
+         * Guitar Hero Live PS3, Wii U and PS4 guitar dongles
          * Guitar Hero PS3 and PC guitar dongles
 
 config SONY_FF
index 1ea1a7c..e29efcb 100644 (file)
@@ -115,7 +115,6 @@ obj-$(CONFIG_HID_STEELSERIES)       += hid-steelseries.o
 obj-$(CONFIG_HID_SUNPLUS)      += hid-sunplus.o
 obj-$(CONFIG_HID_GREENASIA)    += hid-gaff.o
 obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o hid-thrustmaster.o
-obj-$(CONFIG_HID_TMINIT)       += hid-tminit.o
 obj-$(CONFIG_HID_TIVO)         += hid-tivo.o
 obj-$(CONFIG_HID_TOPSEED)      += hid-topseed.o
 obj-$(CONFIG_HID_TWINHAN)      += hid-twinhan.o
index efb8494..840fd07 100644 (file)
@@ -17,7 +17,6 @@
 #include "amd_sfh_pcie.h"
 #include "amd_sfh_hid.h"
 
-#define AMD_SFH_IDLE_LOOP      200
 
 struct request_list {
        struct hid_device *hid;
@@ -123,14 +122,24 @@ static void amd_sfh_work_buffer(struct work_struct *work)
        int i;
 
        for (i = 0; i < cli_data->num_hid_devices; i++) {
-               report_size = get_input_report(i, cli_data->sensor_idx[i], cli_data->report_id[i],
-                                              in_data);
-               hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,
-                                in_data->input_report[i], report_size, 0);
+               if (cli_data->sensor_sts[i] == SENSOR_ENABLED) {
+                       report_size = get_input_report
+                               (i, cli_data->sensor_idx[i], cli_data->report_id[i], in_data);
+                       hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT,
+                                        in_data->input_report[i], report_size, 0);
+               }
        }
        schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
 }
 
+u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
+{
+       if (mp2->mp2_ops->response)
+               sensor_sts = mp2->mp2_ops->response(mp2, sid, sensor_sts);
+
+       return sensor_sts;
+}
+
 int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
 {
        struct amd_input_data *in_data = &privdata->in_data;
@@ -139,8 +148,8 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
        struct device *dev;
        u32 feature_report_size;
        u32 input_report_size;
+       int rc, i, status;
        u8 cl_idx;
-       int rc, i;
 
        dev = &privdata->pdev->dev;
 
@@ -155,7 +164,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
                in_data->sensor_virt_addr[i] = dma_alloc_coherent(dev, sizeof(int) * 8,
                                                                  &cl_data->sensor_dma_addr[i],
                                                                  GFP_KERNEL);
-               cl_data->sensor_sts[i] = 0;
+               cl_data->sensor_sts[i] = SENSOR_DISABLED;
                cl_data->sensor_requested_cnt[i] = 0;
                cl_data->cur_hid_dev = i;
                cl_idx = cl_data->sensor_idx[i];
@@ -184,7 +193,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
                        rc = -ENOMEM;
                        goto cleanup;
                }
-               info.period = msecs_to_jiffies(AMD_SFH_IDLE_LOOP);
+               info.period = AMD_SFH_IDLE_LOOP;
                info.sensor_idx = cl_idx;
                info.dma_address = cl_data->sensor_dma_addr[i];
 
@@ -195,13 +204,27 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
                        goto cleanup;
                }
                rc = get_report_descriptor(cl_idx, cl_data->report_descr[i]);
-               if (rc)
-                       return rc;
-               rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data);
                if (rc)
                        return rc;
                privdata->mp2_ops->start(privdata, info);
-               cl_data->sensor_sts[i] = 1;
+               status = amd_sfh_wait_for_response
+                               (privdata, cl_data->sensor_idx[i], SENSOR_ENABLED);
+               if (status == SENSOR_ENABLED) {
+                       cl_data->sensor_sts[i] = SENSOR_ENABLED;
+                       rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data);
+                       if (rc) {
+                               privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
+                               status = amd_sfh_wait_for_response
+                                       (privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);
+                               if (status != SENSOR_ENABLED)
+                                       cl_data->sensor_sts[i] = SENSOR_DISABLED;
+                               dev_dbg(dev, "sid 0x%x status 0x%x\n",
+                                       cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
+                               goto cleanup;
+                       }
+               }
+               dev_dbg(dev, "sid 0x%x status 0x%x\n",
+                       cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
        }
        schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
        return 0;
@@ -224,10 +247,19 @@ int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)
 {
        struct amdtp_cl_data *cl_data = privdata->cl_data;
        struct amd_input_data *in_data = cl_data->in_data;
-       int i;
+       int i, status;
 
-       for (i = 0; i < cl_data->num_hid_devices; i++)
-               privdata->mp2_ops->stop(privdata, i);
+       for (i = 0; i < cl_data->num_hid_devices; i++) {
+               if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
+                       privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
+                       status = amd_sfh_wait_for_response
+                                       (privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);
+                       if (status != SENSOR_ENABLED)
+                               cl_data->sensor_sts[i] = SENSOR_DISABLED;
+                       dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x status 0x%x\n",
+                               cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
+               }
+       }
 
        cancel_delayed_work_sync(&cl_data->work);
        cancel_delayed_work_sync(&cl_data->work_buffer);
index 8d68796..79b138f 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/dmi.h>
 #include <linux/interrupt.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 
@@ -31,6 +32,20 @@ static int sensor_mask_override = -1;
 module_param_named(sensor_mask, sensor_mask_override, int, 0444);
 MODULE_PARM_DESC(sensor_mask, "override the detected sensors mask");
 
+static int amd_sfh_wait_response_v2(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts)
+{
+       union cmd_response cmd_resp;
+
+       /* Get response with status within a max of 800 ms timeout */
+       if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp,
+                               (cmd_resp.response_v2.response == sensor_sts &&
+                               cmd_resp.response_v2.status == 0 && (sid == 0xff ||
+                               cmd_resp.response_v2.sensor_id == sid)), 500, 800000))
+               return cmd_resp.response_v2.response;
+
+       return SENSOR_DISABLED;
+}
+
 static void amd_start_sensor_v2(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info)
 {
        union sfh_cmd_base cmd_base;
@@ -183,6 +198,7 @@ static const struct amd_mp2_ops amd_sfh_ops_v2 = {
        .start = amd_start_sensor_v2,
        .stop = amd_stop_sensor_v2,
        .stop_all = amd_stop_all_sensor_v2,
+       .response = amd_sfh_wait_response_v2,
 };
 
 static const struct amd_mp2_ops amd_sfh_ops = {
@@ -248,6 +264,58 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i
        return amd_sfh_hid_client_init(privdata);
 }
 
+static int __maybe_unused amd_mp2_pci_resume(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev);
+       struct amdtp_cl_data *cl_data = mp2->cl_data;
+       struct amd_mp2_sensor_info info;
+       int i, status;
+
+       for (i = 0; i < cl_data->num_hid_devices; i++) {
+               if (cl_data->sensor_sts[i] == SENSOR_DISABLED) {
+                       info.period = AMD_SFH_IDLE_LOOP;
+                       info.sensor_idx = cl_data->sensor_idx[i];
+                       info.dma_address = cl_data->sensor_dma_addr[i];
+                       mp2->mp2_ops->start(mp2, info);
+                       status = amd_sfh_wait_for_response
+                                       (mp2, cl_data->sensor_idx[i], SENSOR_ENABLED);
+                       if (status == SENSOR_ENABLED)
+                               cl_data->sensor_sts[i] = SENSOR_ENABLED;
+                       dev_dbg(dev, "resume sid 0x%x status 0x%x\n",
+                               cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
+               }
+       }
+
+       return 0;
+}
+
+static int __maybe_unused amd_mp2_pci_suspend(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct amd_mp2_dev *mp2 = pci_get_drvdata(pdev);
+       struct amdtp_cl_data *cl_data = mp2->cl_data;
+       int i, status;
+
+       for (i = 0; i < cl_data->num_hid_devices; i++) {
+               if (cl_data->sensor_idx[i] != HPD_IDX &&
+                   cl_data->sensor_sts[i] == SENSOR_ENABLED) {
+                       mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]);
+                       status = amd_sfh_wait_for_response
+                                       (mp2, cl_data->sensor_idx[i], SENSOR_DISABLED);
+                       if (status != SENSOR_ENABLED)
+                               cl_data->sensor_sts[i] = SENSOR_DISABLED;
+                       dev_dbg(dev, "suspend sid 0x%x status 0x%x\n",
+                               cl_data->sensor_idx[i], cl_data->sensor_sts[i]);
+               }
+       }
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(amd_mp2_pm_ops, amd_mp2_pci_suspend,
+               amd_mp2_pci_resume);
+
 static const struct pci_device_id amd_mp2_pci_tbl[] = {
        { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) },
        { }
@@ -258,6 +326,7 @@ static struct pci_driver amd_mp2_pci_driver = {
        .name           = DRIVER_NAME,
        .id_table       = amd_mp2_pci_tbl,
        .probe          = amd_mp2_pci_probe,
+       .driver.pm      = &amd_mp2_pm_ops,
 };
 module_pci_driver(amd_mp2_pci_driver);
 
index 2d5c57e..1ff6f83 100644 (file)
 #define AMD_C2P_MSG2   0x10508
 
 #define AMD_C2P_MSG(regno) (0x10500 + ((regno) * 4))
+#define AMD_P2C_MSG(regno) (0x10680 + ((regno) * 4))
 
 /* MP2 P2C Message Registers */
 #define AMD_P2C_MSG3   0x1068C /* Supported Sensors info */
 
 #define V2_STATUS      0x2
 
+#define SENSOR_ENABLED     4
+#define SENSOR_DISABLED    5
+
 #define HPD_IDX                16
 
+#define AMD_SFH_IDLE_LOOP      200
+
 /* SFH Command register */
 union sfh_cmd_base {
        u32 ul;
@@ -51,6 +57,19 @@ union sfh_cmd_base {
        } cmd_v2;
 };
 
+union cmd_response {
+       u32 resp;
+       struct {
+               u32 status      : 2;
+               u32 out_in_c2p  : 1;
+               u32 rsvd1       : 1;
+               u32 response    : 4;
+               u32 sub_cmd     : 8;
+               u32 sensor_id   : 6;
+               u32 rsvd2       : 10;
+       } response_v2;
+};
+
 union sfh_cmd_param {
        u32 ul;
        struct {
@@ -112,10 +131,14 @@ void amd_stop_all_sensors(struct amd_mp2_dev *privdata);
 int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id);
 int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata);
 int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata);
+u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts);
+void amd_mp2_suspend(struct amd_mp2_dev *mp2);
+void amd_mp2_resume(struct amd_mp2_dev *mp2);
 
 struct amd_mp2_ops {
         void (*start)(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info);
         void (*stop)(struct amd_mp2_dev *privdata, u16 sensor_idx);
         void (*stop_all)(struct amd_mp2_dev *privdata);
+        int (*response)(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts);
 };
 #endif
index dc6bd42..833fcf0 100644 (file)
@@ -187,6 +187,15 @@ static const struct apple_key_translation *apple_find_translation(
        return NULL;
 }
 
+static void input_event_with_scancode(struct input_dev *input,
+               __u8 type, __u16 code, unsigned int hid, __s32 value)
+{
+       if (type == EV_KEY &&
+           (!test_bit(code, input->key)) == value)
+               input_event(input, EV_MSC, MSC_SCAN, hid);
+       input_event(input, type, code, value);
+}
+
 static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
                struct hid_usage *usage, __s32 value)
 {
@@ -199,7 +208,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
 
        if (usage->code == fn_keycode) {
                asc->fn_on = !!value;
-               input_event(input, usage->type, KEY_FN, value);
+               input_event_with_scancode(input, usage->type, KEY_FN,
+                               usage->hid, value);
                return 1;
        }
 
@@ -240,7 +250,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
                                code = do_translate ? trans->to : trans->from;
                        }
 
-                       input_event(input, usage->type, code, value);
+                       input_event_with_scancode(input, usage->type, code,
+                                       usage->hid, value);
                        return 1;
                }
 
@@ -258,8 +269,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
                                        clear_bit(usage->code,
                                                        asc->pressed_numlock);
 
-                               input_event(input, usage->type, trans->to,
-                                               value);
+                               input_event_with_scancode(input, usage->type,
+                                               trans->to, usage->hid, value);
                        }
 
                        return 1;
@@ -270,7 +281,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
                if (hid->country == HID_COUNTRY_INTERNATIONAL_ISO) {
                        trans = apple_find_translation(apple_iso_keyboard, usage->code);
                        if (trans) {
-                               input_event(input, usage->type, trans->to, value);
+                               input_event_with_scancode(input, usage->type,
+                                               trans->to, usage->hid, value);
                                return 1;
                        }
                }
@@ -279,7 +291,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
        if (swap_opt_cmd) {
                trans = apple_find_translation(swapped_option_cmd_keys, usage->code);
                if (trans) {
-                       input_event(input, usage->type, trans->to, value);
+                       input_event_with_scancode(input, usage->type,
+                                       trans->to, usage->hid, value);
                        return 1;
                }
        }
@@ -287,7 +300,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
        if (swap_fn_leftctrl) {
                trans = apple_find_translation(swapped_fn_leftctrl_keys, usage->code);
                if (trans) {
-                       input_event(input, usage->type, trans->to, value);
+                       input_event_with_scancode(input, usage->type,
+                                       trans->to, usage->hid, value);
                        return 1;
                }
        }
@@ -306,8 +320,8 @@ static int apple_event(struct hid_device *hdev, struct hid_field *field,
 
        if ((asc->quirks & APPLE_INVERT_HWHEEL) &&
                        usage->code == REL_HWHEEL) {
-               input_event(field->hidinput->input, usage->type, usage->code,
-                               -value);
+               input_event_with_scancode(field->hidinput->input, usage->type,
+                               usage->code, usage->hid, -value);
                return 1;
        }
 
index fb807c8..f3ecddc 100644 (file)
@@ -82,6 +82,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
 #define QUIRK_T90CHI                   BIT(9)
 #define QUIRK_MEDION_E1239T            BIT(10)
 #define QUIRK_ROG_NKEY_KEYBOARD                BIT(11)
+#define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12)
 
 #define I2C_KEYBOARD_QUIRKS                    (QUIRK_FIX_NOTEBOOK_REPORT | \
                                                 QUIRK_NO_INIT_REPORTS | \
@@ -366,6 +367,17 @@ static int asus_raw_event(struct hid_device *hdev,
 
        }
 
+       if (drvdata->quirks & QUIRK_ROG_CLAYMORE_II_KEYBOARD) {
+               /*
+                * CLAYMORE II keyboard sends this packet when it goes to sleep
+                * this causes the whole system to go into suspend.
+               */
+
+               if(size == 2 && data[0] == 0x02 && data[1] == 0x00) {
+                       return -1;
+               }
+       }
+
        return 0;
 }
 
@@ -1225,6 +1237,9 @@ static const struct hid_device_id asus_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
            USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2),
          QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
+           USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD),
+         QUIRK_ROG_CLAYMORE_II_KEYBOARD },
        { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
                USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD),
          QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
index 3296c50..cab4204 100644 (file)
@@ -1,8 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * HID driver for CMedia CM6533 audio jack controls
+ * and HS100B mute buttons
  *
  * Copyright (C) 2015 Ben Chen <ben_chen@bizlinktech.com>
+ * Copyright (C) 2021 Thomas Weißschuh <linux@weissschuh.net>
  */
 
 #include <linux/device.h>
 #include "hid-ids.h"
 
 MODULE_AUTHOR("Ben Chen");
-MODULE_DESCRIPTION("CM6533 HID jack controls");
+MODULE_AUTHOR("Thomas Weißschuh");
+MODULE_DESCRIPTION("CM6533 HID jack controls and HS100B mute button");
 MODULE_LICENSE("GPL");
 
 #define CM6533_JD_TYPE_COUNT      1
 #define CM6533_JD_RAWEV_LEN     16
 #define CM6533_JD_SFX_OFFSET     8
 
+#define HS100B_RDESC_ORIG_SIZE   60
+
+/* Fixed report descriptor of HS-100B audio chip
+ * Bit 4 is an abolute Microphone mute usage instead of being unassigned.
+ */
+static __u8 hs100b_rdesc_fixed[] = {
+       0x05, 0x0C,         /*  Usage Page (Consumer),          */
+       0x09, 0x01,         /*  Usage (Consumer Control),       */
+       0xA1, 0x01,         /*  Collection (Application),       */
+       0x15, 0x00,         /*      Logical Minimum (0),        */
+       0x25, 0x01,         /*      Logical Maximum (1),        */
+       0x09, 0xE9,         /*      Usage (Volume Inc),         */
+       0x09, 0xEA,         /*      Usage (Volume Dec),         */
+       0x75, 0x01,         /*      Report Size (1),            */
+       0x95, 0x02,         /*      Report Count (2),           */
+       0x81, 0x02,         /*      Input (Variable),           */
+       0x09, 0xE2,         /*      Usage (Mute),               */
+       0x95, 0x01,         /*      Report Count (1),           */
+       0x81, 0x06,         /*      Input (Variable, Relative), */
+       0x05, 0x0B,         /*      Usage Page (Telephony),     */
+       0x09, 0x2F,         /*      Usage (2Fh),                */
+       0x81, 0x02,         /*      Input (Variable),           */
+       0x09, 0x20,         /*      Usage (20h),                */
+       0x81, 0x06,         /*      Input (Variable, Relative), */
+       0x05, 0x0C,         /*      Usage Page (Consumer),      */
+       0x09, 0x00,         /*      Usage (00h),                */
+       0x95, 0x03,         /*      Report Count (3),           */
+       0x81, 0x02,         /*      Input (Variable),           */
+       0x26, 0xFF, 0x00,   /*      Logical Maximum (255),      */
+       0x09, 0x00,         /*      Usage (00h),                */
+       0x75, 0x08,         /*      Report Size (8),            */
+       0x95, 0x03,         /*      Report Count (3),           */
+       0x81, 0x02,         /*      Input (Variable),           */
+       0x09, 0x00,         /*      Usage (00h),                */
+       0x95, 0x04,         /*      Report Count (4),           */
+       0x91, 0x02,         /*      Output (Variable),          */
+       0xC0                /*  End Collection                  */
+};
+
 /*
 *
 *CM6533 audio jack HID raw events:
@@ -156,5 +198,49 @@ static struct hid_driver cmhid_driver = {
        .remove = cmhid_remove,
        .input_mapping = cmhid_input_mapping,
 };
-module_hid_driver(cmhid_driver);
 
+static __u8 *cmhid_hs100b_report_fixup(struct hid_device *hid, __u8 *rdesc,
+                                      unsigned int *rsize)
+{
+       if (*rsize == HS100B_RDESC_ORIG_SIZE) {
+               hid_info(hid, "Fixing CMedia HS-100B report descriptor\n");
+               rdesc = hs100b_rdesc_fixed;
+               *rsize = sizeof(hs100b_rdesc_fixed);
+       }
+       return rdesc;
+}
+
+static const struct hid_device_id cmhid_hs100b_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CMEDIA_HS100B) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, cmhid_hs100b_devices);
+
+static struct hid_driver cmhid_hs100b_driver = {
+       .name = "cmedia_hs100b",
+       .id_table = cmhid_hs100b_devices,
+       .report_fixup = cmhid_hs100b_report_fixup,
+};
+
+static int cmedia_init(void)
+{
+       int ret;
+
+       ret = hid_register_driver(&cmhid_driver);
+       if (ret)
+               return ret;
+
+       ret = hid_register_driver(&cmhid_hs100b_driver);
+       if (ret)
+               hid_unregister_driver(&cmhid_driver);
+
+       return ret;
+}
+module_init(cmedia_init);
+
+static void cmedia_exit(void)
+{
+               hid_unregister_driver(&cmhid_driver);
+               hid_unregister_driver(&cmhid_hs100b_driver);
+}
+module_exit(cmedia_exit);
index 0d22713..383dfda 100644 (file)
@@ -228,13 +228,15 @@ static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        struct elo_priv *priv;
        int ret;
+       struct usb_device *udev;
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
        INIT_DELAYED_WORK(&priv->work, elo_work);
-       priv->usbdev = interface_to_usbdev(to_usb_interface(hdev->dev.parent));
+       udev = interface_to_usbdev(to_usb_interface(hdev->dev.parent));
+       priv->usbdev = usb_get_dev(udev);
 
        hid_set_drvdata(hdev, priv);
 
@@ -265,6 +267,8 @@ static void elo_remove(struct hid_device *hdev)
 {
        struct elo_priv *priv = hid_get_drvdata(hdev);
 
+       usb_put_dev(priv->usbdev);
+
        hid_hw_stop(hdev);
        cancel_delayed_work_sync(&priv->work);
        kfree(priv);
index 8f1893e..29564b3 100644 (file)
@@ -41,9 +41,6 @@
 #define USB_VENDOR_ID_ACTIONSTAR       0x2101
 #define USB_DEVICE_ID_ACTIONSTAR_1011  0x1011
 
-#define USB_VENDOR_ID_ACTIVISION       0x1430
-#define USB_DEVICE_ID_ACTIVISION_GUITAR_DONGLE 0x474c
-
 #define USB_VENDOR_ID_ADS_TECH         0x06e1
 #define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X    0xa155
 
 #define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822
 #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD        0x1866
 #define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2       0x19b6
+#define USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD 0x196b
 #define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
 
 #define USB_VENDOR_ID_ATEN             0x0557
 
 #define USB_VENDOR_ID_CMEDIA           0x0d8c
 #define USB_DEVICE_ID_CM109            0x000e
+#define USB_DEVICE_ID_CMEDIA_HS100B    0x0014
 #define USB_DEVICE_ID_CM6533           0x0022
 
 #define USB_VENDOR_ID_CODEMERCS                0x07c0
 #define USB_VENDOR_ID_REALTEK          0x0bda
 #define USB_DEVICE_ID_REALTEK_READER   0x0152
 
+#define USB_VENDOR_ID_REDOCTANE                0x1430
+#define USB_DEVICE_ID_REDOCTANE_GUITAR_DONGLE  0x474c
+#define USB_DEVICE_ID_REDOCTANE_PS4_GHLIVE_DONGLE      0x07bb
+
 #define USB_VENDOR_ID_RETROUSB         0xf000
 #define USB_DEVICE_ID_RETROUSB_SNES_RETROPAD   0x0003
 #define USB_DEVICE_ID_RETROUSB_SNES_RETROPORT  0x00f1
index 4286a51..4b5ebea 100644 (file)
@@ -419,8 +419,6 @@ static int hidinput_get_battery_property(struct power_supply *psy,
 
                if (dev->battery_status == HID_BATTERY_UNKNOWN)
                        val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
-               else if (dev->battery_capacity == 100)
-                       val->intval = POWER_SUPPLY_STATUS_FULL;
                else
                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
                break;
index 61635e6..81de88a 100644 (file)
@@ -1331,6 +1331,43 @@ static int hidpp20_battery_get_battery_voltage(struct hidpp_device *hidpp,
        return 0;
 }
 
+static int hidpp20_map_battery_capacity(struct hid_device *hid_dev, int voltage)
+{
+       /* NB: This voltage curve doesn't necessarily map perfectly to all
+        * devices that implement the BATTERY_VOLTAGE feature. This is because
+        * there are a few devices that use different battery technology.
+        */
+
+       static const int voltages[] = {
+               4186, 4156, 4143, 4133, 4122, 4113, 4103, 4094, 4086, 4075,
+               4067, 4059, 4051, 4043, 4035, 4027, 4019, 4011, 4003, 3997,
+               3989, 3983, 3976, 3969, 3961, 3955, 3949, 3942, 3935, 3929,
+               3922, 3916, 3909, 3902, 3896, 3890, 3883, 3877, 3870, 3865,
+               3859, 3853, 3848, 3842, 3837, 3833, 3828, 3824, 3819, 3815,
+               3811, 3808, 3804, 3800, 3797, 3793, 3790, 3787, 3784, 3781,
+               3778, 3775, 3772, 3770, 3767, 3764, 3762, 3759, 3757, 3754,
+               3751, 3748, 3744, 3741, 3737, 3734, 3730, 3726, 3724, 3720,
+               3717, 3714, 3710, 3706, 3702, 3697, 3693, 3688, 3683, 3677,
+               3671, 3666, 3662, 3658, 3654, 3646, 3633, 3612, 3579, 3537
+       };
+
+       int i;
+
+       BUILD_BUG_ON(ARRAY_SIZE(voltages) != 100);
+
+       if (unlikely(voltage < 3500 || voltage >= 5000))
+               hid_warn_once(hid_dev,
+                             "%s: possibly using the wrong voltage curve\n",
+                             __func__);
+
+       for (i = 0; i < ARRAY_SIZE(voltages); i++) {
+               if (voltage >= voltages[i])
+                       return ARRAY_SIZE(voltages) - i;
+       }
+
+       return 0;
+}
+
 static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp)
 {
        u8 feature_type;
@@ -1354,6 +1391,8 @@ static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp)
 
        hidpp->battery.status = status;
        hidpp->battery.voltage = voltage;
+       hidpp->battery.capacity = hidpp20_map_battery_capacity(hidpp->hid_dev,
+                                                              voltage);
        hidpp->battery.level = level;
        hidpp->battery.charge_type = charge_type;
        hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
@@ -1378,6 +1417,8 @@ static int hidpp20_battery_voltage_event(struct hidpp_device *hidpp,
 
        if (voltage != hidpp->battery.voltage || status != hidpp->battery.status) {
                hidpp->battery.voltage = voltage;
+               hidpp->battery.capacity = hidpp20_map_battery_capacity(hidpp->hid_dev,
+                                                                      voltage);
                hidpp->battery.status = status;
                hidpp->battery.level = level;
                hidpp->battery.charge_type = charge_type;
@@ -2240,11 +2281,10 @@ static int hidpp_ff_queue_work(struct hidpp_ff_private_data *data, int effect_id
        wd->size = size;
        memcpy(wd->params, params, size);
 
-       atomic_inc(&data->workqueue_size);
+       s = atomic_inc_return(&data->workqueue_size);
        queue_work(data->wq, &wd->work);
 
        /* warn about excessive queue size */
-       s = atomic_read(&data->workqueue_size);
        if (s >= 20 && s % 20 == 0)
                hid_warn(data->hidpp->hid_dev, "Force feedback command queue contains %d commands, causing substantial delays!", s);
 
@@ -3717,7 +3757,8 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
        num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 3;
 
        if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE ||
-           hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_PERCENTAGE)
+           hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_PERCENTAGE ||
+           hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
                battery_props[num_battery_props++] =
                                POWER_SUPPLY_PROP_CAPACITY;
 
index 8bcaee4..686788e 100644 (file)
@@ -68,6 +68,10 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
 #define TOUCH_STATE_START 0x30
 #define TOUCH_STATE_DRAG  0x40
 
+/* Number of high-resolution events for each low-resolution detent. */
+#define SCROLL_HR_STEPS 10
+#define SCROLL_HR_MULT (120 / SCROLL_HR_STEPS)
+#define SCROLL_HR_THRESHOLD 90 /* units */
 #define SCROLL_ACCEL_DEFAULT 7
 
 /* Touch surface information. Dimension is in hundredths of a mm, min and max
@@ -126,7 +130,11 @@ struct magicmouse_sc {
                short y;
                short scroll_x;
                short scroll_y;
+               short scroll_x_hr;
+               short scroll_y_hr;
                u8 size;
+               bool scroll_x_active;
+               bool scroll_y_active;
        } touches[16];
        int tracking_ids[16];
 
@@ -248,12 +256,20 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
                unsigned long now = jiffies;
                int step_x = msc->touches[id].scroll_x - x;
                int step_y = msc->touches[id].scroll_y - y;
+               int step_hr = ((64 - (int)scroll_speed) * msc->scroll_accel) /
+                             SCROLL_HR_STEPS;
+               int step_x_hr = msc->touches[id].scroll_x_hr - x;
+               int step_y_hr = msc->touches[id].scroll_y_hr - y;
 
                /* Calculate and apply the scroll motion. */
                switch (state) {
                case TOUCH_STATE_START:
                        msc->touches[id].scroll_x = x;
                        msc->touches[id].scroll_y = y;
+                       msc->touches[id].scroll_x_hr = x;
+                       msc->touches[id].scroll_y_hr = y;
+                       msc->touches[id].scroll_x_active = false;
+                       msc->touches[id].scroll_y_active = false;
 
                        /* Reset acceleration after half a second. */
                        if (scroll_acceleration && time_before(now,
@@ -280,6 +296,40 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
                                msc->scroll_jiffies = now;
                                input_report_rel(input, REL_WHEEL, step_y);
                        }
+
+                       if (!msc->touches[id].scroll_x_active &&
+                           abs(step_x_hr) > SCROLL_HR_THRESHOLD) {
+                               msc->touches[id].scroll_x_active = true;
+                               msc->touches[id].scroll_x_hr = x;
+                               step_x_hr = 0;
+                       }
+
+                       step_x_hr /= step_hr;
+                       if (step_x_hr != 0 &&
+                           msc->touches[id].scroll_x_active) {
+                               msc->touches[id].scroll_x_hr -= step_x_hr *
+                                       step_hr;
+                               input_report_rel(input,
+                                                REL_HWHEEL_HI_RES,
+                                                -step_x_hr * SCROLL_HR_MULT);
+                       }
+
+                       if (!msc->touches[id].scroll_y_active &&
+                           abs(step_y_hr) > SCROLL_HR_THRESHOLD) {
+                               msc->touches[id].scroll_y_active = true;
+                               msc->touches[id].scroll_y_hr = y;
+                               step_y_hr = 0;
+                       }
+
+                       step_y_hr /= step_hr;
+                       if (step_y_hr != 0 &&
+                           msc->touches[id].scroll_y_active) {
+                               msc->touches[id].scroll_y_hr -= step_y_hr *
+                                       step_hr;
+                               input_report_rel(input,
+                                                REL_WHEEL_HI_RES,
+                                                step_y_hr * SCROLL_HR_MULT);
+                       }
                        break;
                }
        }
@@ -481,6 +531,8 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
                if (emulate_scroll_wheel) {
                        __set_bit(REL_WHEEL, input->relbit);
                        __set_bit(REL_HWHEEL, input->relbit);
+                       __set_bit(REL_WHEEL_HI_RES, input->relbit);
+                       __set_bit(REL_HWHEEL_HI_RES, input->relbit);
                }
        } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
                /* setting the device name to ensure the same driver settings
index 51b39bd..2e10468 100644 (file)
@@ -662,8 +662,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb653) },
        { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
        { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65a) },
-#endif
-#if IS_ENABLED(CONFIG_HID_TMINIT)
        { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65d) },
 #endif
 #if IS_ENABLED(CONFIG_HID_TIVO)
index b3722c5..d1b107d 100644 (file)
@@ -11,8 +11,9 @@
  *  Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
  *  Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com>
  *  Copyright (c) 2018 Todd Kelner
- *  Copyright (c) 2020 Pascal Giard <pascal.giard@etsmtl.ca>
+ *  Copyright (c) 2020-2021 Pascal Giard <pascal.giard@etsmtl.ca>
  *  Copyright (c) 2020 Sanjay Govind <sanjay.govind9@gmail.com>
+ *  Copyright (c) 2021 Daniel Nguyen <daniel.nguyen.1@ens.etsmtl.ca>
  */
 
 /*
@@ -62,6 +63,7 @@
 #define SHANWAN_GAMEPAD           BIT(16)
 #define GH_GUITAR_CONTROLLER      BIT(17)
 #define GHL_GUITAR_PS3WIIU        BIT(18)
+#define GHL_GUITAR_PS4            BIT(19)
 
 #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
 #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
 #define NSG_MRXU_MAX_X 1667
 #define NSG_MRXU_MAX_Y 1868
 
-#define GHL_GUITAR_POKE_INTERVAL 10 /* In seconds */
+/* The PS3/Wii U dongles require a poke every 10 seconds, but the PS4
+ * requires one every 8 seconds. Using 8 seconds for all for simplicity.
+ */
+#define GHL_GUITAR_POKE_INTERVAL 8 /* In seconds */
 #define GUITAR_TILT_USAGE 44
 
-/* Magic value and data taken from GHLtarUtility:
+/* Magic data taken from GHLtarUtility:
  * https://github.com/ghlre/GHLtarUtility/blob/master/PS3Guitar.cs
  * Note: The Wii U and PS3 dongles happen to share the same!
  */
-static const u16 ghl_ps3wiiu_magic_value = 0x201;
 static const char ghl_ps3wiiu_magic_data[] = {
        0x02, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 
+/* Magic data for the PS4 dongles sniffed with a USB protocol
+ * analyzer.
+ */
+static const char ghl_ps4_magic_data[] = {
+       0x30, 0x02, 0x08, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
 /* PS/3 Motion controller */
 static u8 motion_rdesc[] = {
        0x05, 0x01,         /*  Usage Page (Desktop),               */
@@ -642,14 +653,14 @@ static void ghl_magic_poke(struct timer_list *t)
                hid_err(sc->hdev, "usb_submit_urb failed: %d", ret);
 }
 
-static int ghl_init_urb(struct sony_sc *sc, struct usb_device *usbdev)
+static int ghl_init_urb(struct sony_sc *sc, struct usb_device *usbdev,
+                                          const char ghl_magic_data[], u16 poke_size)
 {
        struct usb_ctrlrequest *cr;
-       u16 poke_size;
        u8 *databuf;
        unsigned int pipe;
+       u16 ghl_magic_value = (((HID_OUTPUT_REPORT + 1) << 8) | ghl_magic_data[0]);
 
-       poke_size = ARRAY_SIZE(ghl_ps3wiiu_magic_data);
        pipe = usb_sndctrlpipe(usbdev, 0);
 
        cr = devm_kzalloc(&sc->hdev->dev, sizeof(*cr), GFP_ATOMIC);
@@ -663,10 +674,10 @@ static int ghl_init_urb(struct sony_sc *sc, struct usb_device *usbdev)
        cr->bRequestType =
                USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT;
        cr->bRequest = USB_REQ_SET_CONFIGURATION;
-       cr->wValue = cpu_to_le16(ghl_ps3wiiu_magic_value);
+       cr->wValue = cpu_to_le16(ghl_magic_value);
        cr->wIndex = 0;
        cr->wLength = cpu_to_le16(poke_size);
-       memcpy(databuf, ghl_ps3wiiu_magic_data, poke_size);
+       memcpy(databuf, ghl_magic_data, poke_size);
        usb_fill_control_urb(
                sc->ghl_urb, usbdev, pipe,
                (unsigned char *) cr, databuf, poke_size,
@@ -2974,7 +2985,8 @@ 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"))
+       if (!strcmp(hdev->name, "SHANWAN PS3 GamePad") ||
+           !strcmp(hdev->name, "ShanWan PS(R) Ga`epad"))
                quirks |= SHANWAN_GAMEPAD;
 
        sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL);
@@ -3030,11 +3042,17 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
                return -ENODEV;
        }
 
-       if (sc->quirks & GHL_GUITAR_PS3WIIU) {
+       if (sc->quirks & (GHL_GUITAR_PS3WIIU | GHL_GUITAR_PS4)) {
                sc->ghl_urb = usb_alloc_urb(0, GFP_ATOMIC);
                if (!sc->ghl_urb)
                        return -ENOMEM;
-               ret = ghl_init_urb(sc, usbdev);
+
+               if (sc->quirks & GHL_GUITAR_PS3WIIU)
+                       ret = ghl_init_urb(sc, usbdev, ghl_ps3wiiu_magic_data,
+                                                          ARRAY_SIZE(ghl_ps3wiiu_magic_data));
+               else if (sc->quirks & GHL_GUITAR_PS4)
+                       ret = ghl_init_urb(sc, usbdev, ghl_ps4_magic_data,
+                                                          ARRAY_SIZE(ghl_ps4_magic_data));
                if (ret) {
                        hid_err(hdev, "error preparing URB\n");
                        return ret;
@@ -3052,7 +3070,7 @@ static void sony_remove(struct hid_device *hdev)
 {
        struct sony_sc *sc = hid_get_drvdata(hdev);
 
-       if (sc->quirks & GHL_GUITAR_PS3WIIU) {
+       if (sc->quirks & (GHL_GUITAR_PS3WIIU | GHL_GUITAR_PS4)) {
                del_timer_sync(&sc->ghl_poke_timer);
                usb_free_urb(sc->ghl_urb);
        }
@@ -3172,11 +3190,14 @@ static const struct hid_device_id sony_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3WIIU_GHLIVE_DONGLE),
                .driver_data = GHL_GUITAR_PS3WIIU | GH_GUITAR_CONTROLLER },
        /* Guitar Hero PC Guitar Dongle */
-       { HID_USB_DEVICE(USB_VENDOR_ID_ACTIVISION, USB_DEVICE_ID_ACTIVISION_GUITAR_DONGLE),
+       { HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_GUITAR_DONGLE),
                .driver_data = GH_GUITAR_CONTROLLER },
        /* Guitar Hero PS3 World Tour Guitar Dongle */
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY_RHYTHM, USB_DEVICE_ID_SONY_PS3_GUITAR_DONGLE),
                .driver_data = GH_GUITAR_CONTROLLER },
+       /* Guitar Hero Live PS4 guitar dongles */
+       { HID_USB_DEVICE(USB_VENDOR_ID_REDOCTANE, USB_DEVICE_ID_REDOCTANE_PS4_GHLIVE_DONGLE),
+               .driver_data = GHL_GUITAR_PS4 | GH_GUITAR_CONTROLLER },
        { }
 };
 MODULE_DEVICE_TABLE(hid, sony_devices);
index cdc7d82..d44550a 100644 (file)
@@ -173,6 +173,7 @@ static void thrustmaster_interrupts(struct hid_device *hdev)
 
                if (ret) {
                        hid_err(hdev, "setup data couldn't be sent\n");
+                       kfree(send_buf);
                        return;
                }
        }
@@ -253,6 +254,7 @@ static void thrustmaster_remove(struct hid_device *hdev)
 
        usb_kill_urb(tm_wheel->urb);
 
+       kfree(tm_wheel->change_request);
        kfree(tm_wheel->response);
        kfree(tm_wheel->model_request);
        usb_free_urb(tm_wheel->urb);
@@ -336,11 +338,14 @@ static int thrustmaster_probe(struct hid_device *hdev, const struct hid_device_i
        );
 
        ret = usb_submit_urb(tm_wheel->urb, GFP_ATOMIC);
-       if (ret)
+       if (ret) {
                hid_err(hdev, "Error %d while submitting the URB. I am unable to initialize this wheel...\n", ret);
+               goto error6;
+       }
 
        return ret;
 
+error6: kfree(tm_wheel->change_request);
 error5: kfree(tm_wheel->response);
 error4: kfree(tm_wheel->model_request);
 error3: usb_free_urb(tm_wheel->urb);
index 4647461..5171411 100644 (file)
@@ -171,8 +171,6 @@ static const struct i2c_hid_quirks {
                I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
        { I2C_VENDOR_ID_RAYDIUM, I2C_PRODUCT_ID_RAYDIUM_3118,
                I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
-       { USB_VENDOR_ID_ELAN, HID_ANY_ID,
-                I2C_HID_QUIRK_BOGUS_IRQ },
        { USB_VENDOR_ID_ALPS_JP, HID_ANY_ID,
                 I2C_HID_QUIRK_RESET_ON_RESUME },
        { I2C_VENDOR_ID_SYNAPTICS, I2C_PRODUCT_ID_SYNAPTICS_SYNA2393,
@@ -183,7 +181,8 @@ static const struct i2c_hid_quirks {
         * Sending the wakeup after reset actually break ELAN touchscreen controller
         */
        { USB_VENDOR_ID_ELAN, HID_ANY_ID,
-                I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET },
+                I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET |
+                I2C_HID_QUIRK_BOGUS_IRQ },
        { 0, 0 }
 };
 
index ee02259..5267414 100644 (file)
@@ -26,28 +26,29 @@ struct i2c_hid_of_goodix {
        struct i2chid_ops ops;
 
        struct regulator *vdd;
+       struct notifier_block nb;
+       struct mutex regulator_mutex;
        struct gpio_desc *reset_gpio;
        const struct goodix_i2c_hid_timing_data *timings;
 };
 
-static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
+static void goodix_i2c_hid_deassert_reset(struct i2c_hid_of_goodix *ihid_goodix,
+                                         bool regulator_just_turned_on)
 {
-       struct i2c_hid_of_goodix *ihid_goodix =
-               container_of(ops, struct i2c_hid_of_goodix, ops);
-       int ret;
-
-       ret = regulator_enable(ihid_goodix->vdd);
-       if (ret)
-               return ret;
-
-       if (ihid_goodix->timings->post_power_delay_ms)
+       if (regulator_just_turned_on && ihid_goodix->timings->post_power_delay_ms)
                msleep(ihid_goodix->timings->post_power_delay_ms);
 
        gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 0);
        if (ihid_goodix->timings->post_gpio_reset_delay_ms)
                msleep(ihid_goodix->timings->post_gpio_reset_delay_ms);
+}
 
-       return 0;
+static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
+{
+       struct i2c_hid_of_goodix *ihid_goodix =
+               container_of(ops, struct i2c_hid_of_goodix, ops);
+
+       return regulator_enable(ihid_goodix->vdd);
 }
 
 static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
@@ -55,20 +56,54 @@ static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
        struct i2c_hid_of_goodix *ihid_goodix =
                container_of(ops, struct i2c_hid_of_goodix, ops);
 
-       gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
        regulator_disable(ihid_goodix->vdd);
 }
 
+static int ihid_goodix_vdd_notify(struct notifier_block *nb,
+                                   unsigned long event,
+                                   void *ignored)
+{
+       struct i2c_hid_of_goodix *ihid_goodix =
+               container_of(nb, struct i2c_hid_of_goodix, nb);
+       int ret = NOTIFY_OK;
+
+       mutex_lock(&ihid_goodix->regulator_mutex);
+
+       switch (event) {
+       case REGULATOR_EVENT_PRE_DISABLE:
+               gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
+               break;
+
+       case REGULATOR_EVENT_ENABLE:
+               goodix_i2c_hid_deassert_reset(ihid_goodix, true);
+               break;
+
+       case REGULATOR_EVENT_ABORT_DISABLE:
+               goodix_i2c_hid_deassert_reset(ihid_goodix, false);
+               break;
+
+       default:
+               ret = NOTIFY_DONE;
+               break;
+       }
+
+       mutex_unlock(&ihid_goodix->regulator_mutex);
+
+       return ret;
+}
+
 static int i2c_hid_of_goodix_probe(struct i2c_client *client,
                                   const struct i2c_device_id *id)
 {
        struct i2c_hid_of_goodix *ihid_goodix;
-
+       int ret;
        ihid_goodix = devm_kzalloc(&client->dev, sizeof(*ihid_goodix),
                                   GFP_KERNEL);
        if (!ihid_goodix)
                return -ENOMEM;
 
+       mutex_init(&ihid_goodix->regulator_mutex);
+
        ihid_goodix->ops.power_up = goodix_i2c_hid_power_up;
        ihid_goodix->ops.power_down = goodix_i2c_hid_power_down;
 
@@ -84,6 +119,37 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client,
 
        ihid_goodix->timings = device_get_match_data(&client->dev);
 
+       /*
+        * We need to control the "reset" line in lockstep with the regulator
+        * actually turning on an off instead of just when we make the request.
+        * This matters if the regulator is shared with another consumer.
+        * - If the regulator is off then we must assert reset. The reset
+        *   line is active low and on some boards it could cause a current
+        *   leak if left high.
+        * - If the regulator is on then we don't want reset asserted for very
+        *   long. Holding the controller in reset apparently draws extra
+        *   power.
+        */
+       mutex_lock(&ihid_goodix->regulator_mutex);
+       ihid_goodix->nb.notifier_call = ihid_goodix_vdd_notify;
+       ret = devm_regulator_register_notifier(ihid_goodix->vdd, &ihid_goodix->nb);
+       if (ret) {
+               mutex_unlock(&ihid_goodix->regulator_mutex);
+               return dev_err_probe(&client->dev, ret,
+                       "regulator notifier request failed\n");
+       }
+
+       /*
+        * If someone else is holding the regulator on (or the regulator is
+        * an always-on one) we might never be told to deassert reset. Do it
+        * now. Here we'll assume that someone else might have _just
+        * barely_ turned the regulator on so we'll do the full
+        * "post_power_delay" just in case.
+        */
+       if (ihid_goodix->reset_gpio && regulator_is_enabled(ihid_goodix->vdd))
+               goodix_i2c_hid_deassert_reset(ihid_goodix, true);
+       mutex_unlock(&ihid_goodix->regulator_mutex);
+
        return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001);
 }
 
index 06130dc..2dcaf31 100644 (file)
@@ -377,27 +377,23 @@ static int hid_submit_ctrl(struct hid_device *hid)
        len = hid_report_len(report);
        if (dir == USB_DIR_OUT) {
                usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
-               usbhid->urbctrl->transfer_buffer_length = len;
                if (raw_report) {
                        memcpy(usbhid->ctrlbuf, raw_report, len);
                        kfree(raw_report);
                        usbhid->ctrl[usbhid->ctrltail].raw_report = NULL;
                }
        } else {
-               int maxpacket, padlen;
+               int maxpacket;
 
                usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
                maxpacket = usb_maxpacket(hid_to_usb_dev(hid),
                                          usbhid->urbctrl->pipe, 0);
-               if (maxpacket > 0) {
-                       padlen = DIV_ROUND_UP(len, maxpacket);
-                       padlen *= maxpacket;
-                       if (padlen > usbhid->bufsize)
-                               padlen = usbhid->bufsize;
-               } else
-                       padlen = 0;
-               usbhid->urbctrl->transfer_buffer_length = padlen;
+               len += (len == 0);      /* Don't allow 0-length reports */
+               len = round_up(len, maxpacket);
+               if (len > usbhid->bufsize)
+                       len = usbhid->bufsize;
        }
+       usbhid->urbctrl->transfer_buffer_length = len;
        usbhid->urbctrl->dev = hid_to_usb_dev(hid);
 
        usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
@@ -505,7 +501,7 @@ static void hid_ctrl(struct urb *urb)
 
        if (unplug) {
                usbhid->ctrltail = usbhid->ctrlhead;
-       } else {
+       } else if (usbhid->ctrlhead != usbhid->ctrltail) {
                usbhid->ctrltail = (usbhid->ctrltail + 1) & (HID_CONTROL_FIFO_SIZE - 1);
 
                if (usbhid->ctrlhead != usbhid->ctrltail &&
@@ -1223,9 +1219,20 @@ static void usbhid_stop(struct hid_device *hid)
        mutex_lock(&usbhid->mutex);
 
        clear_bit(HID_STARTED, &usbhid->iofl);
+
        spin_lock_irq(&usbhid->lock);   /* Sync with error and led handlers */
        set_bit(HID_DISCONNECTED, &usbhid->iofl);
+       while (usbhid->ctrltail != usbhid->ctrlhead) {
+               if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_OUT) {
+                       kfree(usbhid->ctrl[usbhid->ctrltail].raw_report);
+                       usbhid->ctrl[usbhid->ctrltail].raw_report = NULL;
+               }
+
+               usbhid->ctrltail = (usbhid->ctrltail + 1) &
+                       (HID_CONTROL_FIFO_SIZE - 1);
+       }
        spin_unlock_irq(&usbhid->lock);
+
        usb_kill_urb(usbhid->urbin);
        usb_kill_urb(usbhid->urbout);
        usb_kill_urb(usbhid->urbctrl);
index 57bfa0a..93f49b7 100644 (file)
@@ -2287,7 +2287,13 @@ static void wacom_set_shared_values(struct wacom_wac *wacom_wac)
 
        if (wacom_wac->has_mute_touch_switch) {
                wacom_wac->shared->has_mute_touch_switch = true;
-               wacom_wac->shared->is_touch_on = true;
+               /* Hardware touch switch may be off. Wait until
+                * we know the switch state to decide is_touch_on.
+                * Softkey state should be initialized to "on" to
+                * match historic default.
+                */
+               if (wacom_wac->is_soft_touch_switch)
+                       wacom_wac->shared->is_touch_on = true;
        }
 
        if (wacom_wac->shared->has_mute_touch_switch &&
@@ -2791,6 +2797,7 @@ static int wacom_probe(struct hid_device *hdev,
                                 error);
        }
 
+       wacom_wac->probe_complete = true;
        return 0;
 }
 
index 81ba642..fd51769 100644 (file)
@@ -824,6 +824,13 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
        return 0;
 }
 
+static inline bool touch_is_muted(struct wacom_wac *wacom_wac)
+{
+       return wacom_wac->probe_complete &&
+              wacom_wac->shared->has_mute_touch_switch &&
+              !wacom_wac->shared->is_touch_on;
+}
+
 static inline bool report_touch_events(struct wacom_wac *wacom)
 {
        return (touch_arbitration ? !wacom->shared->stylus_in_proximity : 1);
@@ -1525,11 +1532,8 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom)
        int byte_per_packet = WACOM_BYTES_PER_24HDT_PACKET;
        int y_offset = 2;
 
-       if (wacom->shared->has_mute_touch_switch &&
-           !wacom->shared->is_touch_on) {
-               if (!wacom->shared->touch_down)
-                       return 0;
-       }
+       if (touch_is_muted(wacom) && !wacom->shared->touch_down)
+               return 0;
 
        if (wacom->features.type == WACOM_27QHDT) {
                current_num_contacts = data[63];
@@ -1987,14 +1991,17 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev,
                features->numbered_buttons++;
                features->device_type |= WACOM_DEVICETYPE_PAD;
                break;
-       case WACOM_HID_WD_TOUCHONOFF:
        case WACOM_HID_WD_MUTE_DEVICE:
+               /* softkey touch switch */
+               wacom_wac->is_soft_touch_switch = true;
+               fallthrough;
+       case WACOM_HID_WD_TOUCHONOFF:
                /*
-                * This usage, which is used to mute touch events, comes
-                * from the pad packet, but is reported on the touch
+                * These two usages, which are used to mute touch events, come
+                * from the pad packet, but are reported on the touch
                 * interface. Because the touch interface may not have
                 * been created yet, we cannot call wacom_map_usage(). In
-                * order to process this usage when we receive it, we set
+                * order to process the usages when we receive them, we set
                 * the usage type and code directly.
                 */
                wacom_wac->has_mute_touch_switch = true;
@@ -2533,8 +2540,7 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
        bool prox = hid_data->tipswitch &&
                    report_touch_events(wacom_wac);
 
-       if (wacom_wac->shared->has_mute_touch_switch &&
-           !wacom_wac->shared->is_touch_on) {
+       if (touch_is_muted(wacom_wac)) {
                if (!wacom_wac->shared->touch_down)
                        return;
                prox = false;
@@ -2548,8 +2554,17 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac,
                int slot;
 
                slot = input_mt_get_slot_by_key(input, hid_data->id);
-               if (slot < 0)
+               if (slot < 0) {
                        return;
+               } else {
+                       struct input_mt_slot *ps = &input->mt->slots[slot];
+                       int mt_id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
+
+                       if (!prox && mt_id < 0) {
+                               // No data to send for this slot; short-circuit
+                               return;
+                       }
+               }
 
                input_mt_slot(input, slot);
                input_mt_report_slot_state(input, MT_TOOL_FINGER, prox);
@@ -2581,6 +2596,9 @@ static void wacom_wac_finger_event(struct hid_device *hdev,
        unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
        struct wacom_features *features = &wacom->wacom_wac.features;
 
+       if (touch_is_muted(wacom_wac) && !wacom_wac->shared->touch_down)
+               return;
+
        if (wacom_wac->is_invalid_bt_frame)
                return;
 
@@ -2630,6 +2648,9 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev,
        struct hid_data* hid_data = &wacom_wac->hid_data;
        int i;
 
+       if (touch_is_muted(wacom_wac) && !wacom_wac->shared->touch_down)
+               return;
+
        wacom_wac->is_invalid_bt_frame = false;
 
        for (i = 0; i < report->maxfield; i++) {
@@ -2681,6 +2702,10 @@ static void wacom_wac_finger_report(struct hid_device *hdev,
        struct input_dev *input = wacom_wac->touch_input;
        unsigned touch_max = wacom_wac->features.touch_max;
 
+       /* if there was nothing to process, don't send an empty sync */
+       if (wacom_wac->hid_data.num_expected == 0)
+               return;
+
        /* If more packets of data are expected, give us a chance to
         * process them rather than immediately syncing a partial
         * update.
@@ -3835,6 +3860,7 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
                        input_dev->evbit[0] |= BIT_MASK(EV_SW);
                        __set_bit(SW_MUTE_DEVICE, input_dev->swbit);
                        wacom_wac->has_mute_touch_switch = true;
+                       wacom_wac->is_soft_touch_switch = true;
                }
                fallthrough;
 
index 8f16654..8b2d4e5 100644 (file)
@@ -337,6 +337,7 @@ struct wacom_wac {
        int tool[2];
        int id[2];
        __u64 serial[2];
+       bool probe_complete;
        bool reporting_data;
        struct wacom_features features;
        struct wacom_shared *shared;
@@ -352,6 +353,7 @@ struct wacom_wac {
        int mode_value;
        struct hid_data hid_data;
        bool has_mute_touch_switch;
+       bool is_soft_touch_switch;
        bool has_mode_change;
        bool is_direct_mode;
        bool is_invalid_bt_frame;