mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
The scroll resolution multipliers are set in the context of hidinput_connect(), which is only called at probe time: when the host changes the value on the device with a SET_REPORT(FEATURE), and the device accepts it, these multipliers are stored on the host side, and used to calculate the final scroll event values sent to userspace. After a USB suspend, the resume operation on many hubs and chipsets involve a USB reset signal as well. A reset on the device side clears all previous state information, including the value of the multiplier report. This reset is not handled by the multiplier handling logic, so what ends up happening is the host is still expecting high-resolution scroll events, but the device is reset to default resolution, making the effective, user-perceived scroll speed incredibly slow. The solution is to renegotiate the multiplier selection after each reset. This is not the only bug related to the high-resolution scrolling implementation in the kernel (the other one is https://bugzilla.kernel.org/show_bug.cgi?id=220144), but for this one, there is no device side workaround for, leading to poor user experience with our product: https://github.com/UltimateHackingKeyboard/firmware/issues/1155 https://github.com/UltimateHackingKeyboard/firmware/issues/1261 https://github.com/UltimateHackingKeyboard/firmware/pull/1355 This patch was tested by an affected user and has been reported to fix the issue (see discussion in 1355). Signed-off-by: Benedek Kupper <kupper.benedek@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.com>
99 lines
2.1 KiB
C
99 lines
2.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* HID support for Linux
|
|
*
|
|
* Copyright (c) 1999 Andreas Gal
|
|
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
|
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
|
|
* Copyright (c) 2007-2008 Oliver Neukum
|
|
* Copyright (c) 2006-2012 Jiri Kosina
|
|
* Copyright (c) 2012 Henrik Rydberg
|
|
*/
|
|
|
|
/*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/unaligned.h>
|
|
#include <asm/byteorder.h>
|
|
|
|
#include <linux/hid.h>
|
|
|
|
static struct hid_driver hid_generic;
|
|
|
|
static int __check_hid_generic(struct device_driver *drv, void *data)
|
|
{
|
|
struct hid_driver *hdrv = to_hid_driver(drv);
|
|
struct hid_device *hdev = data;
|
|
|
|
if (hdrv == &hid_generic)
|
|
return 0;
|
|
|
|
return hid_match_device(hdev, hdrv) != NULL;
|
|
}
|
|
|
|
static bool hid_generic_match(struct hid_device *hdev,
|
|
bool ignore_special_driver)
|
|
{
|
|
if (ignore_special_driver)
|
|
return true;
|
|
|
|
if (hdev->quirks & HID_QUIRK_IGNORE_SPECIAL_DRIVER)
|
|
return true;
|
|
|
|
if (hdev->quirks & HID_QUIRK_HAVE_SPECIAL_DRIVER)
|
|
return false;
|
|
|
|
/*
|
|
* If any other driver wants the device, leave the device to this other
|
|
* driver.
|
|
*/
|
|
if (bus_for_each_drv(&hid_bus_type, NULL, hdev, __check_hid_generic))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int hid_generic_probe(struct hid_device *hdev,
|
|
const struct hid_device_id *id)
|
|
{
|
|
int ret;
|
|
|
|
hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
|
|
|
|
ret = hid_parse(hdev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
|
}
|
|
|
|
static int hid_generic_reset_resume(struct hid_device *hdev)
|
|
{
|
|
if (hdev->claimed & HID_CLAIMED_INPUT)
|
|
hidinput_reset_resume(hdev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct hid_device_id hid_table[] = {
|
|
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, HID_ANY_ID, HID_ANY_ID) },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(hid, hid_table);
|
|
|
|
static struct hid_driver hid_generic = {
|
|
.name = "hid-generic",
|
|
.id_table = hid_table,
|
|
.match = hid_generic_match,
|
|
.probe = hid_generic_probe,
|
|
.reset_resume = hid_generic_reset_resume,
|
|
};
|
|
module_hid_driver(hid_generic);
|
|
|
|
MODULE_AUTHOR("Henrik Rydberg");
|
|
MODULE_DESCRIPTION("HID generic driver");
|
|
MODULE_LICENSE("GPL");
|