summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
};