Skip to content

Commit d43c17e

Browse files
committed
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: 2dc702c ("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 <[email protected]> Cc: [email protected] # v5.0+ Signed-off-by: Benjamin Tissoires <[email protected]>
1 parent a50e8e2 commit d43c17e

File tree

3 files changed

+56
-34
lines changed

3 files changed

+56
-34
lines changed

drivers/hid/hid-core.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,7 +1624,7 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
16241624
* Implement a generic .request() callback, using .raw_request()
16251625
* DO NOT USE in hid drivers directly, but through hid_hw_request instead.
16261626
*/
1627-
void __hid_request(struct hid_device *hid, struct hid_report *report,
1627+
int __hid_request(struct hid_device *hid, struct hid_report *report,
16281628
int reqtype)
16291629
{
16301630
char *buf;
@@ -1633,7 +1633,7 @@ void __hid_request(struct hid_device *hid, struct hid_report *report,
16331633

16341634
buf = hid_alloc_report_buf(report, GFP_KERNEL);
16351635
if (!buf)
1636-
return;
1636+
return -ENOMEM;
16371637

16381638
len = hid_report_len(report);
16391639

@@ -1650,8 +1650,11 @@ void __hid_request(struct hid_device *hid, struct hid_report *report,
16501650
if (reqtype == HID_REQ_GET_REPORT)
16511651
hid_input_report(hid, report->type, buf, ret, 0);
16521652

1653+
ret = 0;
1654+
16531655
out:
16541656
kfree(buf);
1657+
return ret;
16551658
}
16561659
EXPORT_SYMBOL_GPL(__hid_request);
16571660

drivers/hid/hid-input.c

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,52 +1542,71 @@ static void hidinput_close(struct input_dev *dev)
15421542
hid_hw_close(hid);
15431543
}
15441544

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)
15461547
{
1547-
struct hid_report_enum *rep_enum;
1548-
struct hid_report *rep;
15491548
struct hid_usage *usage;
1549+
bool update_needed = false;
15501550
int i, j;
15511551

1552-
rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
1553-
list_for_each_entry(rep, &rep_enum->report_list, list) {
1554-
bool update_needed = false;
1552+
if (report->maxfield == 0)
1553+
return false;
15551554

1556-
if (rep->maxfield == 0)
1557-
continue;
1555+
/*
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+
}
15581564

1559-
/*
1560-
* If we have more than one feature within this report we
1561-
* need to fill in the bits from the others before we can
1562-
* overwrite the ones for the Resolution Multiplier.
1565+
for (i = 0; i < report->maxfield; i++) {
1566+
__s32 value = use_logical_max ?
1567+
report->field[i]->logical_maximum :
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.
15631573
*/
1564-
if (rep->maxfield > 1) {
1565-
hid_hw_request(hid, rep, HID_REQ_GET_REPORT);
1566-
hid_hw_wait(hid);
1567-
}
1574+
if (report->field[i]->report_count != 1)
1575+
continue;
15681576

1569-
for (i = 0; i < rep->maxfield; i++) {
1570-
__s32 logical_max = rep->field[i]->logical_maximum;
1577+
for (j = 0; j < report->field[i]->maxusage; j++) {
1578+
usage = &report->field[i]->usage[j];
15711579

1572-
/* There is no good reason for a Resolution
1573-
* Multiplier to have a count other than 1.
1574-
* Ignore that case.
1575-
*/
1576-
if (rep->field[i]->report_count != 1)
1580+
if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER)
15771581
continue;
15781582

1579-
for (j = 0; j < rep->field[i]->maxusage; j++) {
1580-
usage = &rep->field[i]->usage[j];
1583+
*report->field[i]->value = value;
1584+
update_needed = true;
1585+
}
1586+
}
1587+
1588+
return update_needed;
1589+
}
15811590

1582-
if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER)
1583-
continue;
1591+
static void hidinput_change_resolution_multipliers(struct hid_device *hid)
1592+
{
1593+
struct hid_report_enum *rep_enum;
1594+
struct hid_report *rep;
1595+
int ret;
15841596

1585-
*rep->field[i]->value = logical_max;
1586-
update_needed = true;
1597+
rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
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;
15871608
}
15881609
}
1589-
if (update_needed)
1590-
hid_hw_request(hid, rep, HID_REQ_SET_REPORT);
15911610
}
15921611

15931612
/* refresh our structs */

include/linux/hid.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ struct hid_field *hidinput_get_led_field(struct hid_device *hid);
893893
unsigned int hidinput_count_leds(struct hid_device *hid);
894894
__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code);
895895
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);
897897
u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags);
898898
struct hid_device *hid_allocate_device(void);
899899
struct hid_report *hid_register_report(struct hid_device *device,

0 commit comments

Comments
 (0)