Commit 2dc702c9 authored by Peter Hutterer's avatar Peter Hutterer Committed by Benjamin Tissoires

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: default avatarPeter Hutterer <peter.hutterer@who-t.net>
Verified-by: default avatarHarry Cutts <hcutts@chromium.org>
Signed-off-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
parent 5a4abb36
...@@ -712,7 +712,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -712,7 +712,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
map_abs_clear(usage->hid & 0xf); map_abs_clear(usage->hid & 0xf);
break; break;
case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL: case HID_GD_WHEEL:
if (field->flags & HID_MAIN_ITEM_RELATIVE) {
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) if (field->flags & HID_MAIN_ITEM_RELATIVE)
map_rel(usage->hid & 0xf); map_rel(usage->hid & 0xf);
else else
...@@ -1012,7 +1020,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -1012,7 +1020,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case 0x22f: map_key_clear(KEY_ZOOMRESET); break; case 0x22f: map_key_clear(KEY_ZOOMRESET); break;
case 0x233: map_key_clear(KEY_SCROLLUP); break; case 0x233: map_key_clear(KEY_SCROLLUP); break;
case 0x234: map_key_clear(KEY_SCROLLDOWN); break; case 0x234: map_key_clear(KEY_SCROLLDOWN); break;
case 0x238: map_rel(REL_HWHEEL); break; case 0x238: /* AC Pan */
set_bit(REL_HWHEEL, input->relbit);
map_rel(REL_HWHEEL_HI_RES);
break;
case 0x23d: map_key_clear(KEY_EDIT); break; case 0x23d: map_key_clear(KEY_EDIT); break;
case 0x25f: map_key_clear(KEY_CANCEL); break; case 0x25f: map_key_clear(KEY_CANCEL); break;
case 0x269: map_key_clear(KEY_INSERT); break; case 0x269: map_key_clear(KEY_INSERT); break;
...@@ -1200,6 +1211,38 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -1200,6 +1211,38 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
} }
static void hidinput_handle_scroll(struct hid_usage *usage,
struct input_dev *input,
__s32 value)
{
int code;
int hi_res, lo_res;
if (value == 0)
return;
if (usage->code == REL_WHEEL_HI_RES)
code = REL_WHEEL;
else
code = REL_HWHEEL;
/*
* Windows reports one wheel 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.
*/
hi_res = value * 120/usage->resolution_multiplier;
usage->wheel_accumulated += hi_res;
lo_res = usage->wheel_accumulated/120;
if (lo_res)
usage->wheel_accumulated -= lo_res * 120;
input_event(input, EV_REL, code, lo_res);
input_event(input, EV_REL, usage->code, hi_res);
}
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
{ {
struct input_dev *input; struct input_dev *input;
...@@ -1262,6 +1305,12 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct ...@@ -1262,6 +1305,12 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */
return; return;
if ((usage->type == EV_REL) && (usage->code == REL_WHEEL_HI_RES ||
usage->code == REL_HWHEEL_HI_RES)) {
hidinput_handle_scroll(usage, input, value);
return;
}
if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) && if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) &&
(usage->code == ABS_VOLUME)) { (usage->code == ABS_VOLUME)) {
int count = abs(value); int count = abs(value);
...@@ -1489,6 +1538,58 @@ static void hidinput_close(struct input_dev *dev) ...@@ -1489,6 +1538,58 @@ static void hidinput_close(struct input_dev *dev)
hid_hw_close(hid); hid_hw_close(hid);
} }
static void hidinput_change_resolution_multipliers(struct hid_device *hid)
{
struct hid_report_enum *rep_enum;
struct hid_report *rep;
struct hid_usage *usage;
int i, j;
rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
list_for_each_entry(rep, &rep_enum->report_list, list) {
bool update_needed = false;
if (rep->maxfield == 0)
continue;
/*
* If we have more than one feature within this report we
* need to fill in the bits from the others before we can
* overwrite the ones for the Resolution Multiplier.
*/
if (rep->maxfield > 1) {
hid_hw_request(hid, rep, HID_REQ_GET_REPORT);
hid_hw_wait(hid);
}
for (i = 0; i < rep->maxfield; i++) {
__s32 logical_max = rep->field[i]->logical_maximum;
/* There is no good reason for a Resolution
* Multiplier to have a count other than 1.
* Ignore that case.
*/
if (rep->field[i]->report_count != 1)
continue;
for (j = 0; j < rep->field[i]->maxusage; j++) {
usage = &rep->field[i]->usage[j];
if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER)
continue;
*rep->field[i]->value = logical_max;
update_needed = true;
}
}
if (update_needed)
hid_hw_request(hid, rep, HID_REQ_SET_REPORT);
}
/* refresh our structs */
hid_setup_resolution_multiplier(hid);
}
static void report_features(struct hid_device *hid) static void report_features(struct hid_device *hid)
{ {
struct hid_driver *drv = hid->driver; struct hid_driver *drv = hid->driver;
...@@ -1782,6 +1883,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) ...@@ -1782,6 +1883,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
} }
} }
hidinput_change_resolution_multipliers(hid);
list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
if (drv->input_configured && if (drv->input_configured &&
drv->input_configured(hid, hidinput)) drv->input_configured(hid, hidinput))
...@@ -1840,4 +1943,3 @@ void hidinput_disconnect(struct hid_device *hid) ...@@ -1840,4 +1943,3 @@ void hidinput_disconnect(struct hid_device *hid)
cancel_work_sync(&hid->led_work); cancel_work_sync(&hid->led_work);
} }
EXPORT_SYMBOL_GPL(hidinput_disconnect); EXPORT_SYMBOL_GPL(hidinput_disconnect);
...@@ -233,6 +233,7 @@ struct hid_item { ...@@ -233,6 +233,7 @@ struct hid_item {
#define HID_DC_BATTERYSTRENGTH 0x00060020 #define HID_DC_BATTERYSTRENGTH 0x00060020
#define HID_CP_CONSUMER_CONTROL 0x000c0001 #define HID_CP_CONSUMER_CONTROL 0x000c0001
#define HID_CP_AC_PAN 0x000c0238
#define HID_DG_DIGITIZER 0x000d0001 #define HID_DG_DIGITIZER 0x000d0001
#define HID_DG_PEN 0x000d0002 #define HID_DG_PEN 0x000d0002
...@@ -441,11 +442,13 @@ struct hid_usage { ...@@ -441,11 +442,13 @@ struct hid_usage {
__s8 resolution_multiplier;/* Effective Resolution Multiplier __s8 resolution_multiplier;/* Effective Resolution Multiplier
(HUT v1.12, 4.3.1), default: 1 */ (HUT v1.12, 4.3.1), default: 1 */
/* hidinput data */ /* hidinput data */
__s8 wheel_factor; /* 120/resolution_multiplier */
__u16 code; /* input driver code */ __u16 code; /* input driver code */
__u8 type; /* input driver type */ __u8 type; /* input driver type */
__s8 hat_min; /* hat switch fun */ __s8 hat_min; /* hat switch fun */
__s8 hat_max; /* ditto */ __s8 hat_max; /* ditto */
__s8 hat_dir; /* ditto */ __s8 hat_dir; /* ditto */
__s16 wheel_accumulated; /* hi-res wheel */
}; };
struct hid_input; struct hid_input;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment