Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: plantronics: Workaround for double volume key presses

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>

authored by

Maxim Mikityanskiy and committed by
Jiri Kosina
f567d6ef 69aea9d2

+61 -2
+1
drivers/hid/hid-ids.h
··· 944 944 #define USB_DEVICE_ID_ORTEK_IHOME_IMAC_A210S 0x8003 945 945 946 946 #define USB_VENDOR_ID_PLANTRONICS 0x047f 947 + #define USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3220_SERIES 0xc056 947 948 948 949 #define USB_VENDOR_ID_PANASONIC 0x04da 949 950 #define USB_DEVICE_ID_PANABOARD_UBT780 0x1044
+58 -2
drivers/hid/hid-plantronics.c
··· 13 13 14 14 #include <linux/hid.h> 15 15 #include <linux/module.h> 16 + #include <linux/jiffies.h> 16 17 17 18 #define PLT_HID_1_0_PAGE 0xffa00000 18 19 #define PLT_HID_2_0_PAGE 0xffa20000 ··· 37 36 #define PLT_ALLOW_CONSUMER (field->application == HID_CP_CONSUMERCONTROL && \ 38 37 (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) 39 38 39 + #define PLT_QUIRK_DOUBLE_VOLUME_KEYS BIT(0) 40 + 41 + #define PLT_DOUBLE_KEY_TIMEOUT 5 /* ms */ 42 + 43 + struct plt_drv_data { 44 + unsigned long device_type; 45 + unsigned long last_volume_key_ts; 46 + u32 quirks; 47 + }; 48 + 40 49 static int plantronics_input_mapping(struct hid_device *hdev, 41 50 struct hid_input *hi, 42 51 struct hid_field *field, ··· 54 43 unsigned long **bit, int *max) 55 44 { 56 45 unsigned short mapped_key; 57 - unsigned long plt_type = (unsigned long)hid_get_drvdata(hdev); 46 + struct plt_drv_data *drv_data = hid_get_drvdata(hdev); 47 + unsigned long plt_type = drv_data->device_type; 58 48 59 49 /* special case for PTT products */ 60 50 if (field->application == HID_GD_JOYSTICK) ··· 117 105 return 1; 118 106 } 119 107 108 + static int plantronics_event(struct hid_device *hdev, struct hid_field *field, 109 + struct hid_usage *usage, __s32 value) 110 + { 111 + struct plt_drv_data *drv_data = hid_get_drvdata(hdev); 112 + 113 + if (drv_data->quirks & PLT_QUIRK_DOUBLE_VOLUME_KEYS) { 114 + unsigned long prev_ts, cur_ts; 115 + 116 + /* Usages are filtered in plantronics_usages. */ 117 + 118 + if (!value) /* Handle key presses only. */ 119 + return 0; 120 + 121 + prev_ts = drv_data->last_volume_key_ts; 122 + cur_ts = jiffies; 123 + if (jiffies_to_msecs(cur_ts - prev_ts) <= PLT_DOUBLE_KEY_TIMEOUT) 124 + return 1; /* Ignore the repeated key. */ 125 + 126 + drv_data->last_volume_key_ts = cur_ts; 127 + } 128 + 129 + return 0; 130 + } 131 + 120 132 static unsigned long plantronics_device_type(struct hid_device *hdev) 121 133 { 122 134 unsigned i, col_page; ··· 169 133 static int plantronics_probe(struct hid_device *hdev, 170 134 const struct hid_device_id *id) 171 135 { 136 + struct plt_drv_data *drv_data; 172 137 int ret; 138 + 139 + drv_data = devm_kzalloc(&hdev->dev, sizeof(*drv_data), GFP_KERNEL); 140 + if (!drv_data) 141 + return -ENOMEM; 173 142 174 143 ret = hid_parse(hdev); 175 144 if (ret) { ··· 182 141 goto err; 183 142 } 184 143 185 - hid_set_drvdata(hdev, (void *)plantronics_device_type(hdev)); 144 + drv_data->device_type = plantronics_device_type(hdev); 145 + drv_data->quirks = id->driver_data; 146 + drv_data->last_volume_key_ts = jiffies - msecs_to_jiffies(PLT_DOUBLE_KEY_TIMEOUT); 147 + 148 + hid_set_drvdata(hdev, drv_data); 186 149 187 150 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | 188 151 HID_CONNECT_HIDINPUT_FORCE | HID_CONNECT_HIDDEV_FORCE); ··· 198 153 } 199 154 200 155 static const struct hid_device_id plantronics_devices[] = { 156 + { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, 157 + USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3220_SERIES), 158 + .driver_data = PLT_QUIRK_DOUBLE_VOLUME_KEYS }, 201 159 { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) }, 202 160 { } 203 161 }; 204 162 MODULE_DEVICE_TABLE(hid, plantronics_devices); 205 163 164 + static const struct hid_usage_id plantronics_usages[] = { 165 + { HID_CP_VOLUMEUP, EV_KEY, HID_ANY_ID }, 166 + { HID_CP_VOLUMEDOWN, EV_KEY, HID_ANY_ID }, 167 + { HID_TERMINATOR, HID_TERMINATOR, HID_TERMINATOR } 168 + }; 169 + 206 170 static struct hid_driver plantronics_driver = { 207 171 .name = "plantronics", 208 172 .id_table = plantronics_devices, 173 + .usage_table = plantronics_usages, 209 174 .input_mapping = plantronics_input_mapping, 175 + .event = plantronics_event, 210 176 .probe = plantronics_probe, 211 177 }; 212 178 module_hid_driver(plantronics_driver);
+2
include/linux/hid.h
··· 262 262 #define HID_CP_SELECTION 0x000c0080 263 263 #define HID_CP_MEDIASELECTION 0x000c0087 264 264 #define HID_CP_SELECTDISC 0x000c00ba 265 + #define HID_CP_VOLUMEUP 0x000c00e9 266 + #define HID_CP_VOLUMEDOWN 0x000c00ea 265 267 #define HID_CP_PLAYBACKSPEED 0x000c00f1 266 268 #define HID_CP_PROXIMITY 0x000c0109 267 269 #define HID_CP_SPEAKERSYSTEM 0x000c0160