Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp1195352imu; Thu, 22 Nov 2018 11:54:10 -0800 (PST) X-Google-Smtp-Source: AJdET5f2cj1FRD2/P+PkysMdKxy2idRDHfGZDZ6I8lQm4fucn/c9EEBTyv8A5fLUCvU0w+zNggNW X-Received: by 2002:a63:c64f:: with SMTP id x15mr10944521pgg.16.1542916450268; Thu, 22 Nov 2018 11:54:10 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1542916450; cv=none; d=google.com; s=arc-20160816; b=oaxvEDe0i5PsgUduNc8LK8KdCzN45Crig2T53ISR2QfDJl2mKwLSDw1eSzY63igOi+ Jkd4q3IstHkYqvhxKong7fCGDivAb31GReIUltnYAmiSo/0JjkUaOVoiM8xZXlB6lMhG 50r7U96G84fsFn35SeTRRYICMz+oJ90Ls4YF0ZhMvx5hgoDucN7v4/5hc3SHlKSdQ5kM e5Z8pksfG/wc3n43lCJ0ufnnlcrtBxwEufkRAZeUda146TuVbW8+wsFFrtml1q6kVO3c oVBt1nHEi1Nof6YEaf8+PHTEEokstbu6fNkRXL+iJBJD8hlgi9okuN+gsP7B5NOoEGz6 paEQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature:dkim-signature; bh=2NRKPg6DDQvDyJAKCZF+166ySrYs9Y64eU7bWIrFnUI=; b=FRiTTAeL8I2yO1L7tm9+/kC5qraa5IYz5DAEAdqucHJd/+2I2CCEPGkLxVYDzkMRKZ xsv2zxvQqLk6tFKCz4FvfLTG4S8GbfsvQUyfg//gtgLLIceYq6sa0EPgalRgXTbKU9oR ULXOwIpqVIiQOwrGES2oUQ6cYi5voxCVl5QXVC5IIxEEosVqnSH7zMUR52SsM1AXaWrZ FBl7PDx52RRxPun+OCK9rpaQu9c7d4F0GoFTQH6/5VJHaQUZhqdep/fEhELrYpGd66i6 IKYaUyVESK0wRS0L24Ov56iaa/KaIQfOAVSG+3fhiBPVPsUab+0s4Yiu4UDcmZHnWf0w l8yw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@who-t.net header.s=fm1 header.b=BD5IwikI; dkim=pass header.i=@messagingengine.com header.s=fm1 header.b=Lghx9vih; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id q25si34738556pgv.541.2018.11.22.11.53.55; Thu, 22 Nov 2018 11:54:10 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@who-t.net header.s=fm1 header.b=BD5IwikI; dkim=pass header.i=@messagingengine.com header.s=fm1 header.b=Lghx9vih; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2404756AbeKVRND (ORCPT + 99 others); Thu, 22 Nov 2018 12:13:03 -0500 Received: from out4-smtp.messagingengine.com ([66.111.4.28]:48677 "EHLO out4-smtp.messagingengine.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1732955AbeKVRNC (ORCPT ); Thu, 22 Nov 2018 12:13:02 -0500 Received: from compute3.internal (compute3.nyi.internal [10.202.2.43]) by mailout.nyi.internal (Postfix) with ESMTP id 75BB021F47; Thu, 22 Nov 2018 01:35:02 -0500 (EST) Received: from mailfrontend2 ([10.202.2.163]) by compute3.internal (MEProxy); Thu, 22 Nov 2018 01:35:02 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=who-t.net; h= from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; s=fm1; bh=2NRKPg6DDQvDy JAKCZF+166ySrYs9Y64eU7bWIrFnUI=; b=BD5IwikIDAOfG1kww86ekIozSAa8n DUTW9VAr7xo/c6FsnKQtpsacde1BwjnCbLOPA+XOzJ+ZOOyAcB5F6ffLScoXNNwL 667Mwar2RKTf/U9JHfwir7KaRQN/CkEe4X3r8T2pN10I+tQiWlHNeMOlaTOgI0s8 wtBwH8O0TYh+Evhzpvojn1qCR+Z5RMf2pKMC1yASatYMHEhvrwwIONXXwp94z43D 9xIz1Ktxh4Vgqfl8SUjGU+nwgv6V23QC2+uH1KBTOeUB+oyn73P+qHs1Cx5x+Fhu 7snroaXR8Ul5KWr4yStOLJj4/54QEeSjjK57fG7iQdnB9uyPEu8W+Zzqg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:date:from :in-reply-to:message-id:mime-version:references:subject:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm1; bh=2NRKPg6DDQvDyJAKCZF+166ySrYs9Y64eU7bWIrFnUI=; b=Lghx9vih 18MGMm1yqddj4xfW1qXW+4rSZh+VcCM6pM/q5QjCS8MrNpQnhUcDAfDqJlamEe1/ LCAkYY5QG4CFCiDp39F6N9PpQN0e9GyABXNu+3C+MiQZ4Sg1ouo/0LKUOY8zk66V OYAWpWMxdP152pwik3jnGf2difpoUhsdKvuYI/8DPXAJSCo6YYh4HzO6lVdKoMyq w0By/lsWY+05ChjhqraA1dN5eQq2UwdUYqDITlWyqW186z2XdW9lktZIAEL5nhU+ oREQI72YgNH5Xcq6e7Cm/crR8yiQCRtkPSiS1W2YrgQA3k5bJn5LHwK/QeErBsoy /3juPRJdp7/NZA== X-ME-Sender: X-ME-Proxy: Received: from jelly.home.gateway (167-179-166-29.a7b3a6.bne.nbn.aussiebb.net [167.179.166.29]) by mail.messagingengine.com (Postfix) with ESMTPA id 8702F102DE; Thu, 22 Nov 2018 01:34:59 -0500 (EST) From: Peter Hutterer To: linux-input@vger.kernel.org Cc: Dmitry Torokhov , Jiri Kosina , Harry Cutts , torvalds@linux-foundation.org, Nestor Lopez Casado , linux-kernel@vger.kernel.org, Benjamin Tissoires Subject: [PATCH 4/8] HID: input: use the Resolution Multiplier for high-resolution scrolling Date: Thu, 22 Nov 2018 16:34:05 +1000 Message-Id: <20181122063409.15816-5-peter.hutterer@who-t.net> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181122063409.15816-1-peter.hutterer@who-t.net> References: <20181122063409.15816-1-peter.hutterer@who-t.net> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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->hidinput; + if (!hidinput) + return; + + if (usage->code == REL_WHEEL_HI_RES) + code = REL_WHEEL; + else + code = REL_HWHEEL; + + hi_res = usage->wheel_factor * value; + + 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) { struct input_dev *input; @@ -1259,6 +1315,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 */ return; + if ((usage->type == EV_REL) && (usage->code == REL_WHEEL_HI_RES || + usage->code == REL_HWHEEL_HI_RES)) { + hidinput_handle_scroll(hid, field, usage, input, value); + return; + } + if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->code == ABS_VOLUME)) { int count = abs(value); @@ -1486,6 +1548,72 @@ static void hidinput_close(struct input_dev *dev) 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); + + /* now refresh the wheel factor */ + rep_enum = &hid->report_enum[HID_INPUT_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) { + for (i = 0; i < rep->maxfield; i++) { + for (j = 0; j < rep->field[i]->maxusage; j++) { + usage = &rep->field[i]->usage[j]; + + if (usage->hid == HID_GD_WHEEL || + usage->hid == HID_CP_AC_PAN) + hidinput_set_wheel_factor(usage); + } + } + } +} + static void report_features(struct hid_device *hid) { struct hid_driver *drv = hid->driver; @@ -1779,6 +1907,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) { if (drv->input_configured && drv->input_configured(hid, hidinput)) @@ -1837,4 +1967,3 @@ void hidinput_disconnect(struct hid_device *hid) cancel_work_sync(&hid->led_work); } EXPORT_SYMBOL_GPL(hidinput_disconnect); - diff --git a/include/linux/hid.h b/include/linux/hid.h index fd8d860365a4..93db548f8761 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -233,6 +233,7 @@ struct hid_item { #define HID_DC_BATTERYSTRENGTH 0x00060020 #define HID_CP_CONSUMER_CONTROL 0x000c0001 +#define HID_CP_AC_PAN 0x000c0238 #define HID_DG_DIGITIZER 0x000d0001 #define HID_DG_PEN 0x000d0002 @@ -441,11 +442,13 @@ struct hid_usage { __s8 resolution_multiplier;/* Effective Resolution Multiplier (HUT v1.12, 4.3.1), default: 1 */ /* hidinput data */ + __s8 wheel_factor; /* 120/resolution_multiplier */ __u16 code; /* input driver code */ __u8 type; /* input driver type */ __s8 hat_min; /* hat switch fun */ __s8 hat_max; /* ditto */ __s8 hat_dir; /* ditto */ + __s16 wheel_accumulated; /* hi-res wheel */ }; struct hid_input; -- 2.19.1