From patchwork Fri Dec 14 23:24:37 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Machek X-Patchwork-Id: 10731813 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BE05E6C5 for ; Fri, 14 Dec 2018 23:24:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6329729A91 for ; Fri, 14 Dec 2018 23:24:44 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 53C5E2A193; Fri, 14 Dec 2018 23:24:44 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4B1AC29A91 for ; Fri, 14 Dec 2018 23:24:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728548AbeLNXYm (ORCPT ); Fri, 14 Dec 2018 18:24:42 -0500 Received: from atrey.karlin.mff.cuni.cz ([195.113.26.193]:58433 "EHLO atrey.karlin.mff.cuni.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726423AbeLNXYm (ORCPT ); Fri, 14 Dec 2018 18:24:42 -0500 Received: by atrey.karlin.mff.cuni.cz (Postfix, from userid 512) id 40B3B80935; Sat, 15 Dec 2018 00:24:34 +0100 (CET) Date: Sat, 15 Dec 2018 00:24:37 +0100 From: Pavel Machek To: kernel list , jikos@suse.cz, vojtech@suse.cz, linux-input@vger.kernel.org, dmitry.torokhov@gmail.com Subject: [rfd] saving old mice -- button glitching/debouncing Message-ID: <20181214232437.GA8310@amd> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.23 (2014-03-12) Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP I believe I have hardware problem, but I'm kind of hoping software could help me...? Mouse wheel on my machine started glitching on my machine, generating double-clicks when I click it once. Which unfortunately is quite annoying: texts are pasted twice, two tabs are closed instead of one, .... Event: time 1544733054.903129, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90003 Event: time 1544733054.903129, type 1 (EV_KEY), code 274 (BTN_MIDDLE), value 1 Event: time 1544733054.903129, -------------- EV_SYN ------------ 1544733054.967251, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90003 Event: time 1544733054.967251, type 1 (EV_KEY), code 274 (BTN_MIDDLE), value 0 Event: time 1544733054.967251, -------------- EV_SYN ------------ Event: time 1544733054.975144, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90003 Event: time 1544733054.975144, type 1 (EV_KEY), code 274 (BTN_MIDDLE), value 1 Event: time 1544733054.975144, -------------- EV_SYN ------------ : time 1544733065.127190, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90003 Event: time 1544733065.127190, type 1 (EV_KEY), code 274 (BTN_MIDDLE), value 0 Event: time 1544733065.127190, -------------- EV_SYN ------------ Now, I could just buy a new mouse, but it seems that most optical mice die like this... so maybe it would be nice to have an option to debounce the buttons, so that the useful life of mice is extended a bit? (So... I have two mice with that fault -- cheap to replace, but button in thinkpad X220 started doing that, too. That one will not be so cheap to fix :-( ). It is possible that some X versions already do something like this. Patch is obviously not ready; but: a) would it be useful to people b) would it be acceptable if done properly? (cmd line option to enable, avoiding duplicate/wrong events?) Thanks, Pavel Signed-off-by: Pavel Machek diff --git a/drivers/input/input.c b/drivers/input/input.c index 3304aaa..ce0d081 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -40,6 +40,11 @@ static DEFINE_IDA(input_ida); static LIST_HEAD(input_dev_list); static LIST_HEAD(input_handler_list); +static void input_repeat_key(struct timer_list *t); +static void input_debounce_key(struct timer_list *t); + +static int debounce = 20; + /* * input_mutex protects access to both input_dev_list and input_handler_list. * This also causes input_[un]register_device and input_[un]register_handler @@ -77,6 +82,7 @@ static void input_start_autorepeat(struct input_dev *dev, int code) if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.function) { + dev->timer.function = input_repeat_key; dev->repeat_key = code; mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); @@ -88,18 +94,42 @@ static void input_stop_autorepeat(struct input_dev *dev) del_timer(&dev->timer); } +static void input_start_debounce(struct input_dev *dev, int code) +{ + dev->timer.function = input_debounce_key; + dev->debounce_key = code; + mod_timer(&dev->timer, + jiffies + msecs_to_jiffies(debounce)); +} + +static void input_stop_debounce(struct input_dev *dev) +{ + del_timer(&dev->timer); + dev->debounce_key = -1; +} + /* * Pass event first through all filters and then, if event has not been * filtered out, through all open handles. This function is called with * dev->event_lock held and interrupts disabled. */ -static unsigned int input_to_handler(struct input_handle *handle, +static unsigned int input_to_handler(struct input_dev *dev, struct input_handle *handle, struct input_value *vals, unsigned int count) { struct input_handler *handler = handle->handler; struct input_value *end = vals; struct input_value *v; + if (!test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit) && debounce) + for (v = vals; v != vals + count; v++) { + if (v->type == EV_KEY && v->value == 0 && dev->debounce_key == -1) { + input_start_debounce(dev, v->code); + v->code = -2; + } + if (v->type == EV_KEY && v->value == 1 && dev->debounce_key == v->code) + input_stop_debounce(dev); + } + if (handler->filter) { for (v = vals; v != vals + count; v++) { if (handler->filter(handle, v->type, v->code, v->value)) @@ -117,8 +147,9 @@ static unsigned int input_to_handler(struct input_handle *handle, if (handler->events) handler->events(handle, vals, count); else if (handler->event) - for (v = vals; v != vals + count; v++) + for (v = vals; v != vals + count; v++) { handler->event(handle, v->type, v->code, v->value); + } return count; } @@ -141,11 +172,11 @@ static void input_pass_values(struct input_dev *dev, handle = rcu_dereference(dev->grab); if (handle) { - count = input_to_handler(handle, vals, count); + count = input_to_handler(dev, handle, vals, count); } else { list_for_each_entry_rcu(handle, &dev->h_list, d_node) if (handle->open) { - count = input_to_handler(handle, vals, count); + count = input_to_handler(dev, handle, vals, count); if (!count) break; } @@ -203,6 +234,27 @@ static void input_repeat_key(struct timer_list *t) spin_unlock_irqrestore(&dev->event_lock, flags); } +/* + * Generate software autorepeat event. Note that we take + * dev->event_lock here to avoid racing with input_event + * which may cause keys get "stuck". + */ +static void input_debounce_key(struct timer_list *t) +{ + struct input_dev *dev = from_timer(dev, t, timer); + unsigned long flags; + + struct input_value vals[] = { + { EV_KEY, dev->debounce_key, 0 }, + input_value_sync + }; + + spin_lock_irqsave(&dev->event_lock, flags); + input_pass_values(dev, vals, ARRAY_SIZE(vals)); + input_stop_debounce(dev); + spin_unlock_irqrestore(&dev->event_lock, flags); +} + #define INPUT_IGNORE_EVENT 0 #define INPUT_PASS_TO_HANDLERS 1 #define INPUT_PASS_TO_DEVICE 2 @@ -2109,6 +2161,8 @@ int input_register_device(struct input_dev *dev) /* Every input device generates EV_SYN/SYN_REPORT events. */ __set_bit(EV_SYN, dev->evbit); + dev->debounce_key = -1; + /* KEY_RESERVED is not supposed to be transmitted to userspace. */ __clear_bit(KEY_RESERVED, dev->keybit); diff --git a/include/linux/input.h b/include/linux/input.h index 7c7516e..b2458b2 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -150,6 +150,7 @@ struct input_dev { struct ff_device *ff; + int debounce_key; unsigned int repeat_key; struct timer_list timer;