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
---
drivers/hid/hid-input.c | 137 ++--
include/linux/hid.h | 3 +
2 files changed, 136 insertions(+), 4 deletions(-)
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index ad823a01bd65..cf0f2ae2dbf5 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -562,6 +562,17 @@ static void hidinput_update_battery(struct hid_device
*dev, int value)
}
#endif /* CONFIG_HID_BATTERY_STRENGTH */
+static void hidinput_set_wheel_factor(struct hid_usage *usage)
+{
+ /*
+* Windows reports one wheels click as value 120. Where a high-res
+* scroll wheel is present, a fraction of 120 is reported instead.
+* Our REL_WHEEL_HI_RES axis does the same because all HW must
+* adhere to the 120 expectation.
+*/
+ usage->wheel_factor = 120/usage->resolution_multiplier;
+}
+
static void hidinput_configure_usage(struct hid_input *hidinput, struct
hid_field *field,
struct hid_usage *usage)
{
@@ -709,7 +720,16 @@ static void hidinput_configure_usage(struct hid_input
*hidinput, struct hid_fiel
map_abs_clear(usage->hid & 0xf);
break;
- case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
+ case HID_GD_WHEEL:
+ if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+ hidinput_set_wheel_factor(usage);
+ set_bit(REL_WHEEL, input->relbit);
+ map_rel(REL_WHEEL_HI_RES);
+ } else {
+ map_abs(usage->hid & 0xf);
+ }
+ break;
+ case HID_GD_SLIDER: case HID_GD_DIAL:
if (field->flags & HID_MAIN_ITEM_RELATIVE)
map_rel(usage->hid & 0xf);
else
@@ -1009,7 +1029,11 @@ static void hidinput_configure_usage(struct hid_input
*hidinput, struct hid_fiel
case 0x22f: map_key_clear(KEY_ZOOMRESET); break;
case 0x233: map_key_clear(KEY_SCROLLUP);break;
case 0x234: map_key_clear(KEY_SCROLLDOWN); break;
- case 0x238: map_rel(REL_HWHEEL);break;
+ case 0x238: /* AC Pan */
+ hidinput_set_wheel_factor(usage);
+ set_bit(REL_HWHEEL, input->relbit);
+ map_rel(REL_HWHEEL_HI_RES);
+ break;
case 0x23d: map_key_clear(KEY_EDIT);break;
case 0x25f: map_key_clear(KEY_CANCEL); break;
case 0x269: map_key_clear(KEY_INSERT); break;
@@ -1026,7 +1050,6 @@ static void hidinput_configure_usage(struct hid_input
*hidinput, struct hid_fiel
case 0x2ca: map_key_clear(KEY_KBDINPUTASSIST_NEXTGROUP);
break;
case 0x2cb: map_key_clear(KEY_KBDINPUTASSIST_ACCEPT); break;
case 0x2cc: map_key_clear(KEY_KBDINPUTASSIST_CANCEL); break;
-
default: map_key_clear(KEY_UNKNOWN);
}
break;
@@ -1197,6 +1220,39 @@ static void hidinput_configure_usage(struct hid_input
*hidinput, struct hid_fiel
}
+static void hidinput_handle_scroll(struct hid_device *hid,
+ struct hid_field *field,
+ struct hid_usage *usage,
+ struct input_dev *input,
+ __s32 value)
+{
+ int code;
+ struct hid_input *hidinput;
+ int hi_res, lo_res;
+
+ if (value == 0)
+ return;
+
+ hidinput = field->hidinpu