OSDN Git Service

atkbd: fix keyevents on resuming
authorYi Sun <beyounn@gmail.com>
Tue, 5 Jan 2010 22:30:37 +0000 (14:30 -0800)
committerChih-Wei Huang <cwhuang@linux.org.tw>
Sat, 27 Aug 2011 10:43:12 +0000 (18:43 +0800)
Change keyboard driver to push up all the keyevents even the ones before
resume so that Android PM can work correctly. This is a Android-x86 only
change and should not be in the upstream.

drivers/input/keyboard/atkbd.c

index 11478eb..ceeb96b 100644 (file)
@@ -67,6 +67,12 @@ 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");
 
+struct pending_key {
+       struct list_head list;
+       unsigned int     flags;
+       unsigned char    data;
+};
+
 /*
  * Scancode to keycode tables. These are just the default setting, and
  * are loadable via a userland utility.
@@ -234,6 +240,9 @@ struct atkbd {
 
        /* Serializes reconnect(), attr->set() and event work */
        struct mutex mutex;
+
+       unsigned int driver_init_done;
+       struct list_head pending_key_list;
 };
 
 /*
@@ -395,8 +404,20 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
                if  (ps2_handle_response(&atkbd->ps2dev, data))
                        goto out;
 
-       if (!atkbd->enabled)
+       if (!atkbd->enabled) {
+               if (atkbd->driver_init_done) {
+                       struct pending_key *key = kmalloc(sizeof(struct pending_key),
+                                       GFP_ATOMIC);
+                       if (key) {
+                               INIT_LIST_HEAD(&key->list);
+                               key->flags = flags;
+                               key->data = data;
+                               list_add_tail(&key->list, &atkbd->pending_key_list);
+                       }
+
+               }
                goto out;
+       }
 
        input_event(dev, EV_MSC, MSC_RAW, code);
 
@@ -661,6 +682,16 @@ static inline void atkbd_enable(struct atkbd *atkbd)
 {
        serio_pause_rx(atkbd->ps2dev.serio);
        atkbd->enabled = true;
+
+       if (atkbd->driver_init_done) {
+               struct pending_key *pos,*n;
+               list_for_each_entry_safe(pos, n, &atkbd->pending_key_list, list) {
+                       atkbd_interrupt(atkbd->ps2dev.serio, pos->data, pos->flags);
+                       list_del(&pos->list);
+                       kfree(pos);
+               }
+       }
+
        serio_continue_rx(atkbd->ps2dev.serio);
 }
 
@@ -864,6 +895,7 @@ static void atkbd_cleanup(struct serio *serio)
 static void atkbd_disconnect(struct serio *serio)
 {
        struct atkbd *atkbd = serio_get_drvdata(serio);
+       struct pending_key *pos,*n;
 
        sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
 
@@ -881,6 +913,10 @@ static void atkbd_disconnect(struct serio *serio)
 
        serio_close(serio);
        serio_set_drvdata(serio, NULL);
+       list_for_each_entry_safe(pos, n, &atkbd->pending_key_list, list) {
+               list_del(&pos->list);
+               kfree(pos);
+       }
        kfree(atkbd);
 }
 
@@ -1163,13 +1199,12 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
        err = sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group);
        if (err)
                goto fail3;
-
+       INIT_LIST_HEAD(&atkbd->pending_key_list);
        atkbd_enable(atkbd);
-
        err = input_register_device(atkbd->dev);
        if (err)
                goto fail4;
-
+       atkbd->driver_init_done = 1;
        return 0;
 
  fail4: sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);