]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
HID: input: Fix devices that return multiple bytes in battery report
authorGrant Likely <grant.likely@secretlab.ca>
Fri, 10 Jul 2020 15:19:39 +0000 (16:19 +0100)
committerJiri Kosina <jkosina@suse.cz>
Tue, 14 Jul 2020 13:16:54 +0000 (15:16 +0200)
Some devices, particularly the 3DConnexion Spacemouse wireless 3D
controllers, return more than just the battery capacity in the battery
report. The Spacemouse devices return an additional byte with a device
specific field. However, hidinput_query_battery_capacity() only
requests a 2 byte transfer.

When a spacemouse is connected via USB (direct wire, no wireless dongle)
and it returns a 3 byte report instead of the assumed 2 byte battery
report the larger transfer confuses and frightens the USB subsystem
which chooses to ignore the transfer. Then after 2 seconds assume the
device has stopped responding and reset it. This can be reproduced
easily by using a wired connection with a wireless spacemouse. The
Spacemouse will enter a loop of resetting every 2 seconds which can be
observed in dmesg.

This patch solves the problem by increasing the transfer request to 4
bytes instead of 2. The fix isn't particularly elegant, but it is simple
and safe to backport to stable kernels. A further patch will follow to
more elegantly handle battery reports that contain additional data.

Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Cc: Darren Hart <darren@dvhart.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: stable@vger.kernel.org
Tested-by: Darren Hart <dvhart@infradead.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/hid-input.c

index c8633beae260ad87dee0f1be7b11c5fa90b1a9a6..b8eabf206e7439908830e4c6d7218127ec5a73e8 100644 (file)
@@ -350,13 +350,13 @@ static int hidinput_query_battery_capacity(struct hid_device *dev)
        u8 *buf;
        int ret;
 
-       buf = kmalloc(2, GFP_KERNEL);
+       buf = kmalloc(4, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
-       ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2,
+       ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 4,
                                 dev->battery_report_type, HID_REQ_GET_REPORT);
-       if (ret != 2) {
+       if (ret < 2) {
                kfree(buf);
                return -ENODATA;
        }