ANDROID: input: Add "inhibited" property for input devices
authorCaesar Wang <wxt@rock-chips.com>
Tue, 25 Aug 2015 10:21:42 +0000 (18:21 +0800)
committerCaesar Wang <wxt@rock-chips.com>
Tue, 1 Sep 2015 03:11:46 +0000 (11:11 +0800)
Under certain circumstances, we want to disable some input
devices from userspace. In particular, when we detect that the lid of a
laptop is closed, we want to be able to disable touchpad and
touchscreen to avoid bogus input.

To facilitate this, we introduce the "inhibited" sysfs property for
input devices. Using this property, userspace can tell a driver that the
events it can provide are not currently of interest and should be
ignored. We provide hooks so that the driver can take additional
actions, such as powering down the device.

We deliberately keep this limited to input devices for now to keep the
implementation as straightforward as possible.

(cherry-pick from: https://chromium-review.googlesource.com/207989)

verify that touchpad works
echo 1 > /sys/bus/i2c/drivers/elan_i2c/4-0015/input/input0/inhibited
touchpad stops working
echo 0 >  /sys/bus/i2c/drivers/elan_i2c/4-0015/input/input0/inhibited
touchpad works again

Signed-off-by: Patrik Fimml <patrikf@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/207989
Reviewed-by: Dmitry Torokhov <dtor@chromium.org>
Reviewed-by: Benson Leung <bleung@chromium.org>
Change-Id: I889d37ef7ffc49f3c073b1c283d5c3327c263b7f
Signed-off-by: Caesar Wang <wxt@rock-chips.com>
drivers/input/input.c
include/linux/input.h

index a161021c452615e9d2fb39169e9bb170e529f317..80481b6fc22c274e2dd753cdd1883d49c6f0ade4 100644 (file)
@@ -367,6 +367,13 @@ static void input_handle_event(struct input_dev *dev,
 {
        int disposition;
 
+       /*
+        * When inhibited, skip all events. For devices that do not implement
+        * inhibit() themselves.
+        */
+       if (dev->inhibited)
+               return;
+
        disposition = input_get_disposition(dev, type, code, &value);
 
        if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
@@ -1380,12 +1387,50 @@ static ssize_t input_dev_show_properties(struct device *dev,
 }
 static DEVICE_ATTR(properties, S_IRUGO, input_dev_show_properties, NULL);
 
+static int input_inhibit(struct input_dev *dev);
+static int input_uninhibit(struct input_dev *dev);
+
+static ssize_t input_dev_show_inhibited(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct input_dev *input_dev = to_input_dev(dev);
+       return scnprintf(buf, PAGE_SIZE, "%d\n", input_dev->inhibited);
+}
+
+static ssize_t input_dev_store_inhibited(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf,
+                                        size_t len)
+{
+       struct input_dev *input_dev = to_input_dev(dev);
+       ssize_t rv;
+       bool inhibited;
+
+       if (strtobool(buf, &inhibited))
+               return -EINVAL;
+
+       if (inhibited)
+               rv = input_inhibit(input_dev);
+       else
+               rv = input_uninhibit(input_dev);
+
+       if (rv != 0)
+               return rv;
+
+       return len;
+}
+
+static DEVICE_ATTR(inhibited, S_IWUSR | S_IRUGO, input_dev_show_inhibited,
+                  input_dev_store_inhibited);
+
 static struct attribute *input_dev_attrs[] = {
        &dev_attr_name.attr,
        &dev_attr_phys.attr,
        &dev_attr_uniq.attr,
        &dev_attr_modalias.attr,
        &dev_attr_properties.attr,
+       &dev_attr_inhibited.attr,
        NULL
 };
 
@@ -1673,6 +1718,57 @@ void input_reset_device(struct input_dev *dev)
 }
 EXPORT_SYMBOL(input_reset_device);
 
+static int input_inhibit(struct input_dev *dev)
+{
+       int rv = 0;
+
+       mutex_lock(&dev->mutex);
+
+       if (dev->inhibited)
+               goto out;
+
+       if (dev->inhibit) {
+               rv = dev->inhibit(dev);
+               if (rv != 0)
+                       goto out;
+       }
+
+       input_dev_release_keys(dev);
+       input_dev_toggle(dev, false);
+
+       dev->inhibited = true;
+
+out:
+       mutex_unlock(&dev->mutex);
+       return rv;
+}
+
+static int input_uninhibit(struct input_dev *dev)
+{
+       int rv = 0;
+
+       mutex_lock(&dev->mutex);
+
+       if (!dev->inhibited)
+               goto out;
+
+       input_dev_toggle(dev, true);
+
+       if (dev->uninhibit) {
+               rv = dev->uninhibit(dev);
+               if (rv != 0) {
+                       input_dev_toggle(dev, false);
+                       goto out;
+               }
+       }
+
+       dev->inhibited = false;
+
+out:
+       mutex_unlock(&dev->mutex);
+       return rv;
+}
+
 #ifdef CONFIG_PM
 static int input_dev_suspend(struct device *dev)
 {
index 82ce323b998692b9c3d0b95b762605e51147cafd..4770ed61cef0f292fdb4c2af3c29b462c38122a5 100644 (file)
@@ -187,6 +187,11 @@ struct input_dev {
        struct input_value *vals;
 
        bool devres_managed;
+
+       int (*inhibit)(struct input_dev *dev);
+       int (*uninhibit)(struct input_dev *dev);
+
+       bool inhibited;
 };
 #define to_input_dev(d) container_of(d, struct input_dev, dev)