From: Gavin Li <[email protected]>

For physically quirky or otherwise poor keyboards.
---
 drivers/input/keyboard/atkbd.c | 69 +++++++++++++++++++++++++++++++++---------
 1 file changed, 54 insertions(+), 15 deletions(-)

diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 387c51f..b1ff62d 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -28,6 +28,7 @@
 #include <linux/libps2.h>
 #include <linux/mutex.h>
 #include <linux/dmi.h>
+#include <linux/timekeeping.h>
 
 #define DRIVER_DESC    "AT and PS/2 keyboard driver"
 
@@ -67,6 +68,10 @@ static bool atkbd_terminal;
 module_param_named(terminal, atkbd_terminal, bool, 0);
 MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard 
connected via AT/PS2");
 
+static int atkbd_debounce;
+module_param_named(debounce, atkbd_debounce, int, 0);
+MODULE_PARM_DESC(debounce, "Milliseconds to debounce successive keys");
+
 /*
  * Scancode to keycode tables. These are just the default setting, and
  * are loadable via a userland utility.
@@ -218,6 +223,7 @@ struct atkbd {
        bool softraw;
        bool scroll;
        bool enabled;
+       ktime_t debounce_ktime;
 
        /* Accessed only from interrupt */
        unsigned char emul;
@@ -227,6 +233,9 @@ struct atkbd {
        unsigned int last;
        unsigned long time;
        unsigned long err_count;
+       bool is_debouncing;
+       unsigned short last_debounce_code;
+       ktime_t last_debounce_expiration;
 
        struct delayed_work event_work;
        unsigned long event_jiffies;
@@ -375,7 +384,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, 
unsigned char data,
        struct atkbd *atkbd = serio_get_drvdata(serio);
        struct input_dev *dev = atkbd->dev;
        unsigned int code = data;
-       int scroll = 0, hscroll = 0, click = -1;
+       int scroll = 0, hscroll = 0, keypress = 0, click = -1;
        int value;
        unsigned short keycode;
 
@@ -456,23 +465,9 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, 
unsigned char data,
 
        keycode = atkbd->keycode[code];
 
-       if (!(atkbd->release && test_bit(code, atkbd->force_release_mask)))
-               if (keycode != ATKBD_KEY_NULL)
-                       input_event(dev, EV_MSC, MSC_SCAN, code);
-
        switch (keycode) {
        case ATKBD_KEY_NULL:
-               break;
        case ATKBD_KEY_UNKNOWN:
-               dev_warn(&serio->dev,
-                        "Unknown key %s (%s set %d, code %#x on %s).\n",
-                        atkbd->release ? "released" : "pressed",
-                        atkbd->translated ? "translated" : "raw",
-                        atkbd->set, code, serio->phys);
-               dev_warn(&serio->dev,
-                        "Use 'setkeycodes %s%02x <keycode>' to make it 
known.\n",
-                        code & 0x80 ? "e0" : "", code & 0x7f);
-               input_sync(dev);
                break;
        case ATKBD_SCR_1:
                scroll = 1;
@@ -496,6 +491,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, 
unsigned char data,
                hscroll = 1;
                break;
        default:
+               keypress = 1;
                if (atkbd->release) {
                        value = 0;
                        atkbd->last = 0;
@@ -507,7 +503,49 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, 
unsigned char data,
                        atkbd->last = code;
                        atkbd->time = jiffies + 
msecs_to_jiffies(dev->rep[REP_DELAY]) / 2;
                }
+       }
+
+       if (keypress) {
+               if (atkbd->is_debouncing && code == atkbd->last_debounce_code) {
+                       /* ignore debounced release/repeat event */
+                       if (value == 0) {
+                               atkbd->is_debouncing = false;
+                               atkbd->release = false;
+                       }
+                       goto out;
+               }
+               if (!atkbd->is_debouncing && value == 1) {
+                       ktime_t now = ktime_get();
+                       if (code == atkbd->last_debounce_code &&
+                                       ktime_before(now, 
atkbd->last_debounce_expiration)) {
+                               /* debounce the press event */
+                               dev_dbg(&serio->dev, "Debounced scan code 
%#x.\n", code);
+                               atkbd->is_debouncing = !test_bit(code, 
atkbd->force_release_mask);
+                               goto out;
+                       } else {
+                               atkbd->last_debounce_code = code;
+                               atkbd->last_debounce_expiration = 
ktime_add(now, atkbd->debounce_ktime);
+                       }
+               }
+       }
+
+       if (keycode != ATKBD_KEY_NULL)
+               if (!(atkbd->release && test_bit(code, 
atkbd->force_release_mask)))
+                       input_event(dev, EV_MSC, MSC_SCAN, code);
+
+       if (keycode == ATKBD_KEY_UNKNOWN) {
+               dev_warn(&serio->dev,
+                        "Unknown key %s (%s set %d, code %#x on %s).\n",
+                        atkbd->release ? "released" : "pressed",
+                        atkbd->translated ? "translated" : "raw",
+                        atkbd->set, code, serio->phys);
+               dev_warn(&serio->dev,
+                        "Use 'setkeycodes %s%02x <keycode>' to make it 
known.\n",
+                        code & 0x80 ? "e0" : "", code & 0x7f);
+               input_sync(dev);
+       }
 
+       if (keypress) {
                input_event(dev, EV_KEY, keycode, value);
                input_sync(dev);
 
@@ -1161,6 +1199,7 @@ static int atkbd_connect(struct serio *serio, struct 
serio_driver *drv)
        atkbd->softraw = atkbd_softraw;
        atkbd->softrepeat = atkbd_softrepeat;
        atkbd->scroll = atkbd_scroll;
+       atkbd->debounce_ktime = ms_to_ktime(max(atkbd_debounce, 0));
 
        if (atkbd->softrepeat)
                atkbd->softraw = true;
-- 
2.4.5

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to