]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
HID: plantronics: Workaround for double volume key presses
authorMaxim Mikityanskiy <maxtram95@gmail.com>
Sun, 7 Feb 2021 14:47:40 +0000 (16:47 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 22 May 2021 08:38:24 +0000 (10:38 +0200)
[ Upstream commit f567d6ef8606fb427636e824c867229ecb5aefab ]

Plantronics Blackwire 3220 Series (047f:c056) sends HID reports twice
for each volume key press. This patch adds a quirk to hid-plantronics
for this product ID, which will ignore the second volume key press if
it happens within 5 ms from the last one that was handled.

The patch was tested on the mentioned model only, it shouldn't affect
other models, however, this quirk might be needed for them too.
Auto-repeat (when a key is held pressed) is not affected, because the
rate is about 3 times per second, which is far less frequent than once
in 5 ms.

Fixes: 81bb773faed7 ("HID: plantronics: Update to map volume up/down controls")
Signed-off-by: Maxim Mikityanskiy <maxtram95@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/hid/hid-ids.h
drivers/hid/hid-plantronics.c
include/linux/hid.h

index 773452c6edfabc85ffbd5f12b822679011ce7948..cbf13e9939022be77bcc2e3753d7fa870412f866 100644 (file)
 #define USB_DEVICE_ID_ORTEK_WKB2000    0x2000
 
 #define USB_VENDOR_ID_PLANTRONICS      0x047f
+#define USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3220_SERIES        0xc056
 
 #define USB_VENDOR_ID_PANASONIC                0x04da
 #define USB_DEVICE_ID_PANABOARD_UBT780 0x1044
index 584b10d3fc3d84d026c56e4ae4b74fb7b2525d69..460711c1124acea0a95f53513772489c3193035d 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/jiffies.h>
 
 #define PLT_HID_1_0_PAGE       0xffa00000
 #define PLT_HID_2_0_PAGE       0xffa20000
 #define PLT_ALLOW_CONSUMER (field->application == HID_CP_CONSUMERCONTROL && \
                            (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER)
 
+#define PLT_QUIRK_DOUBLE_VOLUME_KEYS BIT(0)
+
+#define PLT_DOUBLE_KEY_TIMEOUT 5 /* ms */
+
+struct plt_drv_data {
+       unsigned long device_type;
+       unsigned long last_volume_key_ts;
+       u32 quirks;
+};
+
 static int plantronics_input_mapping(struct hid_device *hdev,
                                     struct hid_input *hi,
                                     struct hid_field *field,
@@ -46,7 +57,8 @@ static int plantronics_input_mapping(struct hid_device *hdev,
                                     unsigned long **bit, int *max)
 {
        unsigned short mapped_key;
-       unsigned long plt_type = (unsigned long)hid_get_drvdata(hdev);
+       struct plt_drv_data *drv_data = hid_get_drvdata(hdev);
+       unsigned long plt_type = drv_data->device_type;
 
        /* special case for PTT products */
        if (field->application == HID_GD_JOYSTICK)
@@ -108,6 +120,30 @@ mapped:
        return 1;
 }
 
+static int plantronics_event(struct hid_device *hdev, struct hid_field *field,
+                            struct hid_usage *usage, __s32 value)
+{
+       struct plt_drv_data *drv_data = hid_get_drvdata(hdev);
+
+       if (drv_data->quirks & PLT_QUIRK_DOUBLE_VOLUME_KEYS) {
+               unsigned long prev_ts, cur_ts;
+
+               /* Usages are filtered in plantronics_usages. */
+
+               if (!value) /* Handle key presses only. */
+                       return 0;
+
+               prev_ts = drv_data->last_volume_key_ts;
+               cur_ts = jiffies;
+               if (jiffies_to_msecs(cur_ts - prev_ts) <= PLT_DOUBLE_KEY_TIMEOUT)
+                       return 1; /* Ignore the repeated key. */
+
+               drv_data->last_volume_key_ts = cur_ts;
+       }
+
+       return 0;
+}
+
 static unsigned long plantronics_device_type(struct hid_device *hdev)
 {
        unsigned i, col_page;
@@ -136,15 +172,24 @@ exit:
 static int plantronics_probe(struct hid_device *hdev,
                             const struct hid_device_id *id)
 {
+       struct plt_drv_data *drv_data;
        int ret;
 
+       drv_data = devm_kzalloc(&hdev->dev, sizeof(*drv_data), GFP_KERNEL);
+       if (!drv_data)
+               return -ENOMEM;
+
        ret = hid_parse(hdev);
        if (ret) {
                hid_err(hdev, "parse failed\n");
                goto err;
        }
 
-       hid_set_drvdata(hdev, (void *)plantronics_device_type(hdev));
+       drv_data->device_type = plantronics_device_type(hdev);
+       drv_data->quirks = id->driver_data;
+       drv_data->last_volume_key_ts = jiffies - msecs_to_jiffies(PLT_DOUBLE_KEY_TIMEOUT);
+
+       hid_set_drvdata(hdev, drv_data);
 
        ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT |
                HID_CONNECT_HIDINPUT_FORCE | HID_CONNECT_HIDDEV_FORCE);
@@ -156,15 +201,26 @@ err:
 }
 
 static const struct hid_device_id plantronics_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS,
+                                        USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3220_SERIES),
+               .driver_data = PLT_QUIRK_DOUBLE_VOLUME_KEYS },
        { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) },
        { }
 };
 MODULE_DEVICE_TABLE(hid, plantronics_devices);
 
+static const struct hid_usage_id plantronics_usages[] = {
+       { HID_CP_VOLUMEUP, EV_KEY, HID_ANY_ID },
+       { HID_CP_VOLUMEDOWN, EV_KEY, HID_ANY_ID },
+       { HID_TERMINATOR, HID_TERMINATOR, HID_TERMINATOR }
+};
+
 static struct hid_driver plantronics_driver = {
        .name = "plantronics",
        .id_table = plantronics_devices,
+       .usage_table = plantronics_usages,
        .input_mapping = plantronics_input_mapping,
+       .event = plantronics_event,
        .probe = plantronics_probe,
 };
 module_hid_driver(plantronics_driver);
index d93ba6014e3c537c7e0f631c74b7ec8194e01714..19c53b64e07a119d03de47eba8f6aef7c6ddbd70 100644 (file)
@@ -246,6 +246,8 @@ struct hid_item {
 #define HID_CP_SELECTION       0x000c0080
 #define HID_CP_MEDIASELECTION  0x000c0087
 #define HID_CP_SELECTDISC      0x000c00ba
+#define HID_CP_VOLUMEUP                0x000c00e9
+#define HID_CP_VOLUMEDOWN      0x000c00ea
 #define HID_CP_PLAYBACKSPEED   0x000c00f1
 #define HID_CP_PROXIMITY       0x000c0109
 #define HID_CP_SPEAKERSYSTEM   0x000c0160