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 | |
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>
-rw-r--r-- | drivers/hid/hid-core.c | 7 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 81 | ||||
-rw-r--r-- | include/linux/hid.h | 2 |
3 files changed, 56 insertions, 34 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 860e21ec6a49..bff8186e1ea5 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -1624,7 +1624,7 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, | |||
1624 | * Implement a generic .request() callback, using .raw_request() | 1624 | * Implement a generic .request() callback, using .raw_request() |
1625 | * DO NOT USE in hid drivers directly, but through hid_hw_request instead. | 1625 | * DO NOT USE in hid drivers directly, but through hid_hw_request instead. |
1626 | */ | 1626 | */ |
1627 | void __hid_request(struct hid_device *hid, struct hid_report *report, | 1627 | int __hid_request(struct hid_device *hid, struct hid_report *report, |
1628 | int reqtype) | 1628 | int reqtype) |
1629 | { | 1629 | { |
1630 | char *buf; | 1630 | char *buf; |
@@ -1633,7 +1633,7 @@ void __hid_request(struct hid_device *hid, struct hid_report *report, | |||
1633 | 1633 | ||
1634 | buf = hid_alloc_report_buf(report, GFP_KERNEL); | 1634 | buf = hid_alloc_report_buf(report, GFP_KERNEL); |
1635 | if (!buf) | 1635 | if (!buf) |
1636 | return; | 1636 | return -ENOMEM; |
1637 | 1637 | ||
1638 | len = hid_report_len(report); | 1638 | len = hid_report_len(report); |
1639 | 1639 | ||
@@ -1650,8 +1650,11 @@ void __hid_request(struct hid_device *hid, struct hid_report *report, | |||
1650 | if (reqtype == HID_REQ_GET_REPORT) | 1650 | if (reqtype == HID_REQ_GET_REPORT) |
1651 | hid_input_report(hid, report->type, buf, ret, 0); | 1651 | hid_input_report(hid, report->type, buf, ret, 0); |
1652 | 1652 | ||
1653 | ret = 0; | ||
1654 | |||
1653 | out: | 1655 | out: |
1654 | kfree(buf); | 1656 | kfree(buf); |
1657 | return ret; | ||
1655 | } | 1658 | } |
1656 | EXPORT_SYMBOL_GPL(__hid_request); | 1659 | EXPORT_SYMBOL_GPL(__hid_request); |
1657 | 1660 | ||
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 */ |
diff --git a/include/linux/hid.h b/include/linux/hid.h index f9707d1dcb58..2c69340d372d 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h | |||
@@ -893,7 +893,7 @@ struct hid_field *hidinput_get_led_field(struct hid_device *hid); | |||
893 | unsigned int hidinput_count_leds(struct hid_device *hid); | 893 | unsigned int hidinput_count_leds(struct hid_device *hid); |
894 | __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code); | 894 | __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code); |
895 | void hid_output_report(struct hid_report *report, __u8 *data); | 895 | void hid_output_report(struct hid_report *report, __u8 *data); |
896 | void __hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype); | 896 | int __hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype); |
897 | u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags); | 897 | u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags); |
898 | struct hid_device *hid_allocate_device(void); | 898 | struct hid_device *hid_allocate_device(void); |
899 | struct hid_report *hid_register_report(struct hid_device *device, | 899 | struct hid_report *hid_register_report(struct hid_device *device, |