summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndres Salomon <dilinger@debian.org>2007-03-02 15:19:09 -0500
committerAndres Salomon <dilinger@debian.org>2007-03-02 15:19:09 -0500
commit2dbe59e365c2a5ca404f40be6e2b1171ea4b6a3a (patch)
tree4b6671e371da067d8e94fdf27ecfb2e159a48ada
parent6ba3c9b2c4e5330195724cb9f271fb788575c355 (diff)
downloadlwn-2dbe59e365c2a5ca404f40be6e2b1171ea4b6a3a.tar.gz
lwn-2dbe59e365c2a5ca404f40be6e2b1171ea4b6a3a.zip
psmouse: fix bug with touchpad getting stuck in PT mode
Original patch from Zephaniah; this readds code that somehow got lost that fixes a race in the touchpad mode switching. If we quickly switch from GS to PT, and then back to GS, we can get stuck in PT mode. In order to protect against that, we delay the mode switch by 50ms and allow a subsequent packet to kill the mode switch. Signed-off-by: Andres Salomon <dilinger@debian.org>
-rw-r--r--drivers/input/mouse/olpc.c28
-rw-r--r--drivers/input/mouse/olpc.h2
2 files changed, 26 insertions, 4 deletions
diff --git a/drivers/input/mouse/olpc.c b/drivers/input/mouse/olpc.c
index 3f981789f92b..1a046ee41d83 100644
--- a/drivers/input/mouse/olpc.c
+++ b/drivers/input/mouse/olpc.c
@@ -151,6 +151,12 @@ static void olpc_process_packet_gspt(struct psmouse *psmouse)
input_sync(dev);
input_sync(dev2);
+ if (priv->pending_mode == OLPC_GS &&
+ psmouse->packet[0] == OLPC_PKT_PT && pt_down) {
+ priv->pending_mode = 0;
+ cancel_delayed_work(&priv->mode_switch);
+ }
+
if (priv->i->flags & (OLPC_PT|OLPC_GS)) {
int pending = 0;
if (psmouse->packet[0] == OLPC_PKT_PT && !pt_down)
@@ -166,7 +172,7 @@ static void olpc_process_packet_gspt(struct psmouse *psmouse)
priv->pending_mode = pending;
printk(KERN_WARNING "Scheduling mode switch to %s.\n",
pending == OLPC_GS ? "GS" : "PT");
- queue_work(kpsmoused_wq, &priv->mode_switch);
+ queue_delayed_work(kpsmoused_wq, &priv->mode_switch, 0);
}
}
}
@@ -353,8 +359,9 @@ static void olpc_disconnect(struct psmouse *psmouse)
kfree(priv);
}
-static void olpc_mode_switch(struct work_struct *work)
+static void olpc_mode_switch(struct work_struct *w)
{
+ struct delayed_work *work = container_of(w, struct delayed_work, work);
struct olpc_data *priv = container_of(work, struct olpc_data, mode_switch);
struct psmouse *psmouse = priv->psmouse;
struct ps2dev *ps2dev = &psmouse->ps2dev;
@@ -386,8 +393,23 @@ static void olpc_mode_switch(struct work_struct *work)
* hardware sends back its ACK, it has stopped sending bytes).
*/
pending_mode = priv->pending_mode;
+
if (olpc_new_mode(psmouse, priv->pending_mode))
goto bad;
+
+ /*
+ * Deal with a potential race condition.
+ *
+ * If there is a brief tap of a stylus or a fingernail that
+ * triggers a mode switch to PT mode, and the stylus/fingernail is
+ * lifted after the DISABLE above, but before we reenable in the new mode,
+ * then we can get stuck in PT mode.
+ */
+ if (pending_mode == OLPC_PT) {
+ priv->pending_mode = OLPC_GS;
+ queue_delayed_work(kpsmoused_wq, &priv->mode_switch, msecs_to_jiffies(50));
+ }
+
return;
bad:
@@ -477,7 +499,7 @@ int olpc_init(struct psmouse *psmouse)
/* Reset after a lot of bad bytes. */
psmouse->resetafter = 1024;
- INIT_WORK(&priv->mode_switch, olpc_mode_switch);
+ INIT_DELAYED_WORK(&priv->mode_switch, olpc_mode_switch);
return 0;
diff --git a/drivers/input/mouse/olpc.h b/drivers/input/mouse/olpc.h
index 6c88de4b12cf..74c2e4111ba6 100644
--- a/drivers/input/mouse/olpc.h
+++ b/drivers/input/mouse/olpc.h
@@ -32,7 +32,7 @@ struct olpc_data {
int pending_mode;
int current_mode;
s64 late;
- struct work_struct mode_switch;
+ struct delayed_work mode_switch;
};