Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 23 Sep 2017 03:23:41 +0000 (17:23 -1000)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 23 Sep 2017 03:23:41 +0000 (17:23 -1000)
Pull input fixes from Dmitry Torokhov:

 - fixes for two long standing issues (lock up and a crash) in force
   feedback handling in uinput driver

 - tweak to firmware update timing in Elan I2C touchpad driver.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
  Input: elan_i2c - extend Flash-Write delay
  Input: uinput - avoid crash when sending FF request to device going away
  Input: uinput - avoid FF flush when destroying device

drivers/input/ff-core.c
drivers/input/misc/uinput.c
drivers/input/mouse/elan_i2c_i2c.c
include/linux/input.h

index 8f2042432c85151b25fab1e885fe321e1563d641..66a46c84e28f543bc27f91479d5da669984e0d89 100644 (file)
@@ -237,9 +237,15 @@ int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file)
 EXPORT_SYMBOL_GPL(input_ff_erase);
 
 /*
- * flush_effects - erase all effects owned by a file handle
+ * input_ff_flush - erase all effects owned by a file handle
+ * @dev: input device to erase effect from
+ * @file: purported owner of the effects
+ *
+ * This function erases all force-feedback effects associated with
+ * the given owner from specified device. Note that @file may be %NULL,
+ * in which case all effects will be erased.
  */
-static int flush_effects(struct input_dev *dev, struct file *file)
+int input_ff_flush(struct input_dev *dev, struct file *file)
 {
        struct ff_device *ff = dev->ff;
        int i;
@@ -255,6 +261,7 @@ static int flush_effects(struct input_dev *dev, struct file *file)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(input_ff_flush);
 
 /**
  * input_ff_event() - generic handler for force-feedback events
@@ -343,7 +350,7 @@ int input_ff_create(struct input_dev *dev, unsigned int max_effects)
        mutex_init(&ff->mutex);
 
        dev->ff = ff;
-       dev->flush = flush_effects;
+       dev->flush = input_ff_flush;
        dev->event = input_ff_event;
        __set_bit(EV_FF, dev->evbit);
 
index 022be0e22eba97b10b95e653f48cee44f1d4ba36..443151de90c6b506f6847b656a68d84577576ad1 100644 (file)
@@ -98,14 +98,15 @@ static int uinput_request_reserve_slot(struct uinput_device *udev,
                                        uinput_request_alloc_id(udev, request));
 }
 
-static void uinput_request_done(struct uinput_device *udev,
-                               struct uinput_request *request)
+static void uinput_request_release_slot(struct uinput_device *udev,
+                                       unsigned int id)
 {
        /* Mark slot as available */
-       udev->requests[request->id] = NULL;
-       wake_up(&udev->requests_waitq);
+       spin_lock(&udev->requests_lock);
+       udev->requests[id] = NULL;
+       spin_unlock(&udev->requests_lock);
 
-       complete(&request->done);
+       wake_up(&udev->requests_waitq);
 }
 
 static int uinput_request_send(struct uinput_device *udev,
@@ -138,20 +139,22 @@ static int uinput_request_send(struct uinput_device *udev,
 static int uinput_request_submit(struct uinput_device *udev,
                                 struct uinput_request *request)
 {
-       int error;
+       int retval;
 
-       error = uinput_request_reserve_slot(udev, request);
-       if (error)
-               return error;
+       retval = uinput_request_reserve_slot(udev, request);
+       if (retval)
+               return retval;
 
-       error = uinput_request_send(udev, request);
-       if (error) {
-               uinput_request_done(udev, request);
-               return error;
-       }
+       retval = uinput_request_send(udev, request);
+       if (retval)
+               goto out;
 
        wait_for_completion(&request->done);
-       return request->retval;
+       retval = request->retval;
+
+ out:
+       uinput_request_release_slot(udev, request->id);
+       return retval;
 }
 
 /*
@@ -169,7 +172,7 @@ static void uinput_flush_requests(struct uinput_device *udev)
                request = udev->requests[i];
                if (request) {
                        request->retval = -ENODEV;
-                       uinput_request_done(udev, request);
+                       complete(&request->done);
                }
        }
 
@@ -230,6 +233,18 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
        return uinput_request_submit(udev, &request);
 }
 
+static int uinput_dev_flush(struct input_dev *dev, struct file *file)
+{
+       /*
+        * If we are called with file == NULL that means we are tearing
+        * down the device, and therefore we can not handle FF erase
+        * requests: either we are handling UI_DEV_DESTROY (and holding
+        * the udev->mutex), or the file descriptor is closed and there is
+        * nobody on the other side anymore.
+        */
+       return file ? input_ff_flush(dev, file) : 0;
+}
+
 static void uinput_destroy_device(struct uinput_device *udev)
 {
        const char *name, *phys;
@@ -297,6 +312,12 @@ static int uinput_create_device(struct uinput_device *udev)
                dev->ff->playback = uinput_dev_playback;
                dev->ff->set_gain = uinput_dev_set_gain;
                dev->ff->set_autocenter = uinput_dev_set_autocenter;
+               /*
+                * The standard input_ff_flush() implementation does
+                * not quite work for uinput as we can't reasonably
+                * handle FF requests during device teardown.
+                */
+               dev->flush = uinput_dev_flush;
        }
 
        error = input_register_device(udev->dev);
@@ -939,7 +960,7 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
                        }
 
                        req->retval = ff_up.retval;
-                       uinput_request_done(udev, req);
+                       complete(&req->done);
                        goto out;
 
                case UI_END_FF_ERASE:
@@ -955,7 +976,7 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
                        }
 
                        req->retval = ff_erase.retval;
-                       uinput_request_done(udev, req);
+                       complete(&req->done);
                        goto out;
        }
 
index 15b1330606c1c3fcfc28cf109f89987d0425f75a..e19eb60b3d2f5adcdf0182673680de25ddd15483 100644 (file)
@@ -598,7 +598,7 @@ static int elan_i2c_write_fw_block(struct i2c_client *client,
        }
 
        /* Wait for F/W to update one page ROM data. */
-       msleep(20);
+       msleep(35);
 
        error = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
        if (error) {
index a65e3b24fb1838e43cffc24e2ecdc8a8f92eba12..fb5e23c7ed98894e2833f2143e35cd5f703a9b09 100644 (file)
@@ -529,6 +529,7 @@ int input_ff_event(struct input_dev *dev, unsigned int type, unsigned int code,
 
 int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, struct file *file);
 int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file);
+int input_ff_flush(struct input_dev *dev, struct file *file);
 
 int input_ff_create_memless(struct input_dev *dev, void *data,
                int (*play_effect)(struct input_dev *, void *, struct ff_effect *));