Skip to content

Commit 2dc702c

Browse files
whotbentiss
authored andcommitted
HID: input: use the Resolution Multiplier for high-resolution scrolling
Windows uses a magic number of 120 for a wheel click. High-resolution scroll wheels are supposed to use a fraction of 120 to signal smaller scroll steps. This is implemented by the Resolution Multiplier in the device itself. If the multiplier is present in the report descriptor, set it to the logical max and then use the resolution multiplier to calculate the high-resolution events. This is the recommendation by Microsoft, see http://msdn.microsoft.com/en-us/windows/hardware/gg487477.aspx Note that all mice encountered so far have a logical min/max of 0/1, so it's a binary "yes or no" to high-res scrolling anyway. To make userspace simpler, always enable the REL_WHEEL_HI_RES bit. Where the device doesn't support high-resolution scrolling, the value for the high-res data will simply be a multiple of 120 every time. For userspace, if REL_WHEEL_HI_RES is available that is the one to be used. Potential side-effect: a device with a Resolution Multiplier applying to other Input items will have those items set to the logical max as well. This cannot easily be worked around but it is doubtful such devices exist. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Verified-by: Harry Cutts <hcutts@chromium.org> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
1 parent 5a4abb3 commit 2dc702c

File tree

2 files changed

+108
-3
lines changed

2 files changed

+108
-3
lines changed

drivers/hid/hid-input.c

Lines changed: 105 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
712712
map_abs_clear(usage->hid & 0xf);
713713
break;
714714

715-
case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
715+
case HID_GD_WHEEL:
716+
if (field->flags & HID_MAIN_ITEM_RELATIVE) {
717+
set_bit(REL_WHEEL, input->relbit);
718+
map_rel(REL_WHEEL_HI_RES);
719+
} else {
720+
map_abs(usage->hid & 0xf);
721+
}
722+
break;
723+
case HID_GD_SLIDER: case HID_GD_DIAL:
716724
if (field->flags & HID_MAIN_ITEM_RELATIVE)
717725
map_rel(usage->hid & 0xf);
718726
else
@@ -1012,7 +1020,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
10121020
case 0x22f: map_key_clear(KEY_ZOOMRESET); break;
10131021
case 0x233: map_key_clear(KEY_SCROLLUP); break;
10141022
case 0x234: map_key_clear(KEY_SCROLLDOWN); break;
1015-
case 0x238: map_rel(REL_HWHEEL); break;
1023+
case 0x238: /* AC Pan */
1024+
set_bit(REL_HWHEEL, input->relbit);
1025+
map_rel(REL_HWHEEL_HI_RES);
1026+
break;
10161027
case 0x23d: map_key_clear(KEY_EDIT); break;
10171028
case 0x25f: map_key_clear(KEY_CANCEL); break;
10181029
case 0x269: map_key_clear(KEY_INSERT); break;
@@ -1200,6 +1211,38 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
12001211

12011212
}
12021213

1214+
static void hidinput_handle_scroll(struct hid_usage *usage,
1215+
struct input_dev *input,
1216+
__s32 value)
1217+
{
1218+
int code;
1219+
int hi_res, lo_res;
1220+
1221+
if (value == 0)
1222+
return;
1223+
1224+
if (usage->code == REL_WHEEL_HI_RES)
1225+
code = REL_WHEEL;
1226+
else
1227+
code = REL_HWHEEL;
1228+
1229+
/*
1230+
* Windows reports one wheel click as value 120. Where a high-res
1231+
* scroll wheel is present, a fraction of 120 is reported instead.
1232+
* Our REL_WHEEL_HI_RES axis does the same because all HW must
1233+
* adhere to the 120 expectation.
1234+
*/
1235+
hi_res = value * 120/usage->resolution_multiplier;
1236+
1237+
usage->wheel_accumulated += hi_res;
1238+
lo_res = usage->wheel_accumulated/120;
1239+
if (lo_res)
1240+
usage->wheel_accumulated -= lo_res * 120;
1241+
1242+
input_event(input, EV_REL, code, lo_res);
1243+
input_event(input, EV_REL, usage->code, hi_res);
1244+
}
1245+
12031246
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
12041247
{
12051248
struct input_dev *input;
@@ -1262,6 +1305,12 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
12621305
if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */
12631306
return;
12641307

1308+
if ((usage->type == EV_REL) && (usage->code == REL_WHEEL_HI_RES ||
1309+
usage->code == REL_HWHEEL_HI_RES)) {
1310+
hidinput_handle_scroll(usage, input, value);
1311+
return;
1312+
}
1313+
12651314
if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) &&
12661315
(usage->code == ABS_VOLUME)) {
12671316
int count = abs(value);
@@ -1489,6 +1538,58 @@ static void hidinput_close(struct input_dev *dev)
14891538
hid_hw_close(hid);
14901539
}
14911540

1541+
static void hidinput_change_resolution_multipliers(struct hid_device *hid)
1542+
{
1543+
struct hid_report_enum *rep_enum;
1544+
struct hid_report *rep;
1545+
struct hid_usage *usage;
1546+
int i, j;
1547+
1548+
rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
1549+
list_for_each_entry(rep, &rep_enum->report_list, list) {
1550+
bool update_needed = false;
1551+
1552+
if (rep->maxfield == 0)
1553+
continue;
1554+
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 (rep->maxfield > 1) {
1561+
hid_hw_request(hid, rep, HID_REQ_GET_REPORT);
1562+
hid_hw_wait(hid);
1563+
}
1564+
1565+
for (i = 0; i < rep->maxfield; i++) {
1566+
__s32 logical_max = rep->field[i]->logical_maximum;
1567+
1568+
/* There is no good reason for a Resolution
1569+
* Multiplier to have a count other than 1.
1570+
* Ignore that case.
1571+
*/
1572+
if (rep->field[i]->report_count != 1)
1573+
continue;
1574+
1575+
for (j = 0; j < rep->field[i]->maxusage; j++) {
1576+
usage = &rep->field[i]->usage[j];
1577+
1578+
if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER)
1579+
continue;
1580+
1581+
*rep->field[i]->value = logical_max;
1582+
update_needed = true;
1583+
}
1584+
}
1585+
if (update_needed)
1586+
hid_hw_request(hid, rep, HID_REQ_SET_REPORT);
1587+
}
1588+
1589+
/* refresh our structs */
1590+
hid_setup_resolution_multiplier(hid);
1591+
}
1592+
14921593
static void report_features(struct hid_device *hid)
14931594
{
14941595
struct hid_driver *drv = hid->driver;
@@ -1782,6 +1883,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
17821883
}
17831884
}
17841885

1886+
hidinput_change_resolution_multipliers(hid);
1887+
17851888
list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
17861889
if (drv->input_configured &&
17871890
drv->input_configured(hid, hidinput))
@@ -1840,4 +1943,3 @@ void hidinput_disconnect(struct hid_device *hid)
18401943
cancel_work_sync(&hid->led_work);
18411944
}
18421945
EXPORT_SYMBOL_GPL(hidinput_disconnect);
1843-

include/linux/hid.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ struct hid_item {
233233
#define HID_DC_BATTERYSTRENGTH 0x00060020
234234

235235
#define HID_CP_CONSUMER_CONTROL 0x000c0001
236+
#define HID_CP_AC_PAN 0x000c0238
236237

237238
#define HID_DG_DIGITIZER 0x000d0001
238239
#define HID_DG_PEN 0x000d0002
@@ -441,11 +442,13 @@ struct hid_usage {
441442
__s8 resolution_multiplier;/* Effective Resolution Multiplier
442443
(HUT v1.12, 4.3.1), default: 1 */
443444
/* hidinput data */
445+
__s8 wheel_factor; /* 120/resolution_multiplier */
444446
__u16 code; /* input driver code */
445447
__u8 type; /* input driver type */
446448
__s8 hat_min; /* hat switch fun */
447449
__s8 hat_max; /* ditto */
448450
__s8 hat_dir; /* ditto */
451+
__s16 wheel_accumulated; /* hi-res wheel */
449452
};
450453

451454
struct hid_input;

0 commit comments

Comments
 (0)