diff options
author | Benjamin Tissoires <benjamin.tissoires@redhat.com> | 2019-04-03 10:20:20 -0400 |
---|---|---|
committer | Benjamin Tissoires <benjamin.tissoires@redhat.com> | 2019-04-24 09:25:29 -0400 |
commit | d43c17ead879ba7c076dc2f5fd80cd76047c9ff4 (patch) | |
tree | a7914ab47df0cd026d7ffc3b1e32519c80352a43 /drivers/hid/hid-input.c | |
parent | a50e8e2ecc1428df28c748c6af6255eb65faf9f3 (diff) |
HID: input: make sure the wheel high resolution multiplier is set
Some old mice have a tendency to not accept the high resolution multiplier.
They reply with a -EPIPE which was previously ignored.
Force the call to resolution multiplier to be synchronous and actually
check for the answer. If this fails, consider the mouse like a normal one.
Fixes: 2dc702c991e377 ("HID: input: use the Resolution Multiplier for
high-resolution scrolling")
Link: https://bugzilla.redhat.com/show_bug.cgi?id=1700071
Reported-and-tested-by: James Feeney <james@nurealm.net>
Cc: stable@vger.kernel.org # v5.0+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Diffstat (limited to 'drivers/hid/hid-input.c')
-rw-r--r-- | drivers/hid/hid-input.c | 81 |
1 files changed, 50 insertions, 31 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 1fce0076e7dc..fadf76f0a386 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c | |||
@@ -1542,52 +1542,71 @@ static void hidinput_close(struct input_dev *dev) | |||
1542 | hid_hw_close(hid); | 1542 | hid_hw_close(hid); |
1543 | } | 1543 | } |
1544 | 1544 | ||
1545 | static void hidinput_change_resolution_multipliers(struct hid_device *hid) | 1545 | static bool __hidinput_change_resolution_multipliers(struct hid_device *hid, |
1546 | struct hid_report *report, bool use_logical_max) | ||
1546 | { | 1547 | { |
1547 | struct hid_report_enum *rep_enum; | ||
1548 | struct hid_report *rep; | ||
1549 | struct hid_usage *usage; | 1548 | struct hid_usage *usage; |
1549 | bool update_needed = false; | ||
1550 | int i, j; | 1550 | int i, j; |
1551 | 1551 | ||
1552 | rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; | 1552 | if (report->maxfield == 0) |
1553 | list_for_each_entry(rep, &rep_enum->report_list, list) { | 1553 | return false; |
1554 | bool update_needed = false; | ||
1555 | 1554 | ||
1556 | if (rep->maxfield == 0) | 1555 | /* |
1557 | continue; | 1556 | * If we have more than one feature within this report we |
1557 | * need to fill in the bits from the others before we can | ||
1558 | * overwrite the ones for the Resolution Multiplier. | ||
1559 | */ | ||
1560 | if (report->maxfield > 1) { | ||
1561 | hid_hw_request(hid, report, HID_REQ_GET_REPORT); | ||
1562 | hid_hw_wait(hid); | ||
1563 | } | ||
1558 | 1564 | ||
1559 | /* | 1565 | for (i = 0; i < report->maxfield; i++) { |
1560 | * If we have more than one feature within this report we | 1566 | __s32 value = use_logical_max ? |
1561 | * need to fill in the bits from the others before we can | 1567 | report->field[i]->logical_maximum : |
1562 | * overwrite the ones for the Resolution Multiplier. | 1568 | report->field[i]->logical_minimum; |
1569 | |||
1570 | /* There is no good reason for a Resolution | ||
1571 | * Multiplier to have a count other than 1. | ||
1572 | * Ignore that case. | ||
1563 | */ | 1573 | */ |
1564 | if (rep->maxfield > 1) { | 1574 | if (report->field[i]->report_count != 1) |
1565 | hid_hw_request(hid, rep, HID_REQ_GET_REPORT); | 1575 | continue; |
1566 | hid_hw_wait(hid); | ||
1567 | } | ||
1568 | 1576 | ||
1569 | for (i = 0; i < rep->maxfield; i++) { | 1577 | for (j = 0; j < report->field[i]->maxusage; j++) { |
1570 | __s32 logical_max = rep->field[i]->logical_maximum; | 1578 | usage = &report->field[i]->usage[j]; |
1571 | 1579 | ||
1572 | /* There is no good reason for a Resolution | 1580 | if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER) |
1573 | * Multiplier to have a count other than 1. | ||
1574 | * Ignore that case. | ||
1575 | */ | ||
1576 | if (rep->field[i]->report_count != 1) | ||
1577 | continue; | 1581 | continue; |
1578 | 1582 | ||
1579 | for (j = 0; j < rep->field[i]->maxusage; j++) { | 1583 | *report->field[i]->value = value; |
1580 | usage = &rep->field[i]->usage[j]; | 1584 | update_needed = true; |
1585 | } | ||
1586 | } | ||
1587 | |||
1588 | return update_needed; | ||
1589 | } | ||
1581 | 1590 | ||
1582 | if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER) | 1591 | static void hidinput_change_resolution_multipliers(struct hid_device *hid) |
1583 | continue; | 1592 | { |
1593 | struct hid_report_enum *rep_enum; | ||
1594 | struct hid_report *rep; | ||
1595 | int ret; | ||
1584 | 1596 | ||
1585 | *rep->field[i]->value = logical_max; | 1597 | rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; |
1586 | update_needed = true; | 1598 | list_for_each_entry(rep, &rep_enum->report_list, list) { |
1599 | bool update_needed = __hidinput_change_resolution_multipliers(hid, | ||
1600 | rep, true); | ||
1601 | |||
1602 | if (update_needed) { | ||
1603 | ret = __hid_request(hid, rep, HID_REQ_SET_REPORT); | ||
1604 | if (ret) { | ||
1605 | __hidinput_change_resolution_multipliers(hid, | ||
1606 | rep, false); | ||
1607 | return; | ||
1587 | } | 1608 | } |
1588 | } | 1609 | } |
1589 | if (update_needed) | ||
1590 | hid_hw_request(hid, rep, HID_REQ_SET_REPORT); | ||
1591 | } | 1610 | } |
1592 | 1611 | ||
1593 | /* refresh our structs */ | 1612 | /* refresh our structs */ |