]> git.karo-electronics.de Git - mv-sheeva.git/commitdiff
Input: hgpk - forced recalibration for the OLPC touchpad
authorPaul Fox <pgf@laptop.org>
Wed, 5 Aug 2009 07:30:31 +0000 (00:30 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 5 Aug 2009 07:34:32 +0000 (00:34 -0700)
The OLPC XO laptop incorporates a combination touchpad/tablet device
which unfortunately requires frequent recalibration.  The driver will
force this automatically when various suspicious behaviors are
observed, and the user can recalibrate manually (with a special
keyboard sequence). There's currently no way, however, for an external
program to cause recalibration. We can not use the reconnect
capability which is already available in /sys because full reset of
the touchpad takes 1.1 - 1.2 secons which is too long.

This patch creates a new node in /sys which, when written with '1',
will force a touchpad recalibration; no other writes (or reads)
of this node are supported.

Signed-off-by: Paul Fox <pgf@laptop.org>
Acked-by: Andres Salomon <dilinger@collabora.co.uk>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/input/mouse/hgpk.c

index a1ad2f1a7bb377c776368d55c2a05b5fcd7fd69b..f5aa035774d9e1747d59e6cf6be12e37af62d0f6 100644 (file)
@@ -369,12 +369,46 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,
 __PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL,
                      hgpk_show_powered, hgpk_set_powered, 0);
 
+static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse,
+               void *data, char *buf)
+{
+       return -EINVAL;
+}
+
+static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data,
+                               const char *buf, size_t count)
+{
+       struct hgpk_data *priv = psmouse->private;
+       unsigned long value;
+       int err;
+
+       err = strict_strtoul(buf, 10, &value);
+       if (err || value != 1)
+               return -EINVAL;
+
+       /*
+        * We queue work instead of doing recalibration right here
+        * to avoid adding locking to to hgpk_force_recalibrate()
+        * since workqueue provides serialization.
+        */
+       psmouse_queue_work(psmouse, &priv->recalib_wq, 0);
+       return count;
+}
+
+__PSMOUSE_DEFINE_ATTR(recalibrate, S_IWUSR | S_IRUGO, NULL,
+                     hgpk_trigger_recal_show, hgpk_trigger_recal, 0);
+
 static void hgpk_disconnect(struct psmouse *psmouse)
 {
        struct hgpk_data *priv = psmouse->private;
 
        device_remove_file(&psmouse->ps2dev.serio->dev,
                           &psmouse_attr_powered.dattr);
+
+       if (psmouse->model >= HGPK_MODEL_C)
+               device_remove_file(&psmouse->ps2dev.serio->dev,
+                                  &psmouse_attr_recalibrate.dattr);
+
        psmouse_reset(psmouse);
        kfree(priv);
 }
@@ -423,10 +457,25 @@ static int hgpk_register(struct psmouse *psmouse)
 
        err = device_create_file(&psmouse->ps2dev.serio->dev,
                                 &psmouse_attr_powered.dattr);
-       if (err)
-               hgpk_err(psmouse, "Failed to create sysfs attribute\n");
+       if (err) {
+               hgpk_err(psmouse, "Failed creating 'powered' sysfs node\n");
+               return err;
+       }
 
-       return err;
+       /* C-series touchpads added the recalibrate command */
+       if (psmouse->model >= HGPK_MODEL_C) {
+               err = device_create_file(&psmouse->ps2dev.serio->dev,
+                                        &psmouse_attr_recalibrate.dattr);
+               if (err) {
+                       hgpk_err(psmouse,
+                               "Failed creating 'recalibrate' sysfs node\n");
+                       device_remove_file(&psmouse->ps2dev.serio->dev,
+                                       &psmouse_attr_powered.dattr);
+                       return err;
+               }
+       }
+
+       return 0;
 }
 
 int hgpk_init(struct psmouse *psmouse)