Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Peter Hutterer and committed by
Benjamin Tissoires
2dc702c9 5a4abb36

+108 -3
+105 -3
drivers/hid/hid-input.c
··· 712 712 map_abs_clear(usage->hid & 0xf); 713 713 break; 714 714 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: 716 724 if (field->flags & HID_MAIN_ITEM_RELATIVE) 717 725 map_rel(usage->hid & 0xf); 718 726 else ··· 1020 1012 case 0x22f: map_key_clear(KEY_ZOOMRESET); break; 1021 1013 case 0x233: map_key_clear(KEY_SCROLLUP); break; 1022 1014 case 0x234: map_key_clear(KEY_SCROLLDOWN); break; 1023 - case 0x238: map_rel(REL_HWHEEL); break; 1015 + case 0x238: /* AC Pan */ 1016 + set_bit(REL_HWHEEL, input->relbit); 1017 + map_rel(REL_HWHEEL_HI_RES); 1018 + break; 1024 1019 case 0x23d: map_key_clear(KEY_EDIT); break; 1025 1020 case 0x25f: map_key_clear(KEY_CANCEL); break; 1026 1021 case 0x269: map_key_clear(KEY_INSERT); break; ··· 1211 1200 1212 1201 } 1213 1202 1203 + static void hidinput_handle_scroll(struct hid_usage *usage, 1204 + struct input_dev *input, 1205 + __s32 value) 1206 + { 1207 + int code; 1208 + int hi_res, lo_res; 1209 + 1210 + if (value == 0) 1211 + return; 1212 + 1213 + if (usage->code == REL_WHEEL_HI_RES) 1214 + code = REL_WHEEL; 1215 + else 1216 + code = REL_HWHEEL; 1217 + 1218 + /* 1219 + * Windows reports one wheel click as value 120. Where a high-res 1220 + * scroll wheel is present, a fraction of 120 is reported instead. 1221 + * Our REL_WHEEL_HI_RES axis does the same because all HW must 1222 + * adhere to the 120 expectation. 1223 + */ 1224 + hi_res = value * 120/usage->resolution_multiplier; 1225 + 1226 + usage->wheel_accumulated += hi_res; 1227 + lo_res = usage->wheel_accumulated/120; 1228 + if (lo_res) 1229 + usage->wheel_accumulated -= lo_res * 120; 1230 + 1231 + input_event(input, EV_REL, code, lo_res); 1232 + input_event(input, EV_REL, usage->code, hi_res); 1233 + } 1234 + 1214 1235 void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) 1215 1236 { 1216 1237 struct input_dev *input; ··· 1304 1261 1305 1262 if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ 1306 1263 return; 1264 + 1265 + if ((usage->type == EV_REL) && (usage->code == REL_WHEEL_HI_RES || 1266 + usage->code == REL_HWHEEL_HI_RES)) { 1267 + hidinput_handle_scroll(usage, input, value); 1268 + return; 1269 + } 1307 1270 1308 1271 if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) && 1309 1272 (usage->code == ABS_VOLUME)) { ··· 1536 1487 struct hid_device *hid = input_get_drvdata(dev); 1537 1488 1538 1489 hid_hw_close(hid); 1490 + } 1491 + 1492 + static void hidinput_change_resolution_multipliers(struct hid_device *hid) 1493 + { 1494 + struct hid_report_enum *rep_enum; 1495 + struct hid_report *rep; 1496 + struct hid_usage *usage; 1497 + int i, j; 1498 + 1499 + rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; 1500 + list_for_each_entry(rep, &rep_enum->report_list, list) { 1501 + bool update_needed = false; 1502 + 1503 + if (rep->maxfield == 0) 1504 + continue; 1505 + 1506 + /* 1507 + * If we have more than one feature within this report we 1508 + * need to fill in the bits from the others before we can 1509 + * overwrite the ones for the Resolution Multiplier. 1510 + */ 1511 + if (rep->maxfield > 1) { 1512 + hid_hw_request(hid, rep, HID_REQ_GET_REPORT); 1513 + hid_hw_wait(hid); 1514 + } 1515 + 1516 + for (i = 0; i < rep->maxfield; i++) { 1517 + __s32 logical_max = rep->field[i]->logical_maximum; 1518 + 1519 + /* There is no good reason for a Resolution 1520 + * Multiplier to have a count other than 1. 1521 + * Ignore that case. 1522 + */ 1523 + if (rep->field[i]->report_count != 1) 1524 + continue; 1525 + 1526 + for (j = 0; j < rep->field[i]->maxusage; j++) { 1527 + usage = &rep->field[i]->usage[j]; 1528 + 1529 + if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER) 1530 + continue; 1531 + 1532 + *rep->field[i]->value = logical_max; 1533 + update_needed = true; 1534 + } 1535 + } 1536 + if (update_needed) 1537 + hid_hw_request(hid, rep, HID_REQ_SET_REPORT); 1538 + } 1539 + 1540 + /* refresh our structs */ 1541 + hid_setup_resolution_multiplier(hid); 1539 1542 } 1540 1543 1541 1544 static void report_features(struct hid_device *hid) ··· 1883 1782 } 1884 1783 } 1885 1784 1785 + hidinput_change_resolution_multipliers(hid); 1786 + 1886 1787 list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { 1887 1788 if (drv->input_configured && 1888 1789 drv->input_configured(hid, hidinput)) ··· 1943 1840 cancel_work_sync(&hid->led_work); 1944 1841 } 1945 1842 EXPORT_SYMBOL_GPL(hidinput_disconnect); 1946 -
+3
include/linux/hid.h
··· 233 233 #define HID_DC_BATTERYSTRENGTH 0x00060020 234 234 235 235 #define HID_CP_CONSUMER_CONTROL 0x000c0001 236 + #define HID_CP_AC_PAN 0x000c0238 236 237 237 238 #define HID_DG_DIGITIZER 0x000d0001 238 239 #define HID_DG_PEN 0x000d0002 ··· 442 441 __s8 resolution_multiplier;/* Effective Resolution Multiplier 443 442 (HUT v1.12, 4.3.1), default: 1 */ 444 443 /* hidinput data */ 444 + __s8 wheel_factor; /* 120/resolution_multiplier */ 445 445 __u16 code; /* input driver code */ 446 446 __u8 type; /* input driver type */ 447 447 __s8 hat_min; /* hat switch fun */ 448 448 __s8 hat_max; /* ditto */ 449 449 __s8 hat_dir; /* ditto */ 450 + __s16 wheel_accumulated; /* hi-res wheel */ 450 451 }; 451 452 452 453 struct hid_input;