aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/evdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/evdev.c')
-rw-r--r--drivers/input/evdev.c380
1 files changed, 258 insertions, 122 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 6c58bfff01a..fd325ec9f06 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -18,16 +18,18 @@
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input/mt.h>
#include <linux/major.h>
#include <linux/device.h>
+#include <linux/cdev.h>
#include "input-compat.h"
struct evdev {
int open;
- int minor;
struct input_handle handle;
wait_queue_head_t wait;
struct evdev_client __rcu *grab;
@@ -35,6 +37,7 @@ struct evdev {
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
+ struct cdev cdev;
bool exist;
};
@@ -47,23 +50,90 @@ struct evdev_client {
struct evdev *evdev;
struct list_head node;
int clkid;
+ bool revoked;
unsigned int bufsize;
struct input_event buffer[];
};
-static struct evdev *evdev_table[EVDEV_MINORS];
-static DEFINE_MUTEX(evdev_table_mutex);
+/* flush queued events of type @type, caller must hold client->buffer_lock */
+static void __evdev_flush_queue(struct evdev_client *client, unsigned int type)
+{
+ unsigned int i, head, num;
+ unsigned int mask = client->bufsize - 1;
+ bool is_report;
+ struct input_event *ev;
+
+ BUG_ON(type == EV_SYN);
+
+ head = client->tail;
+ client->packet_head = client->tail;
+
+ /* init to 1 so a leading SYN_REPORT will not be dropped */
+ num = 1;
+
+ for (i = client->tail; i != client->head; i = (i + 1) & mask) {
+ ev = &client->buffer[i];
+ is_report = ev->type == EV_SYN && ev->code == SYN_REPORT;
+
+ if (ev->type == type) {
+ /* drop matched entry */
+ continue;
+ } else if (is_report && !num) {
+ /* drop empty SYN_REPORT groups */
+ continue;
+ } else if (head != i) {
+ /* move entry to fill the gap */
+ client->buffer[head].time = ev->time;
+ client->buffer[head].type = ev->type;
+ client->buffer[head].code = ev->code;
+ client->buffer[head].value = ev->value;
+ }
+
+ num++;
+ head = (head + 1) & mask;
-static void evdev_pass_event(struct evdev_client *client,
- struct input_event *event,
- ktime_t mono, ktime_t real)
+ if (is_report) {
+ num = 0;
+ client->packet_head = head;
+ }
+ }
+
+ client->head = head;
+}
+
+/* queue SYN_DROPPED event */
+static void evdev_queue_syn_dropped(struct evdev_client *client)
{
- event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
- mono : real);
+ unsigned long flags;
+ struct input_event ev;
+ ktime_t time;
- /* Interrupts are disabled, just acquire the lock. */
- spin_lock(&client->buffer_lock);
+ time = ktime_get();
+ if (client->clkid != CLOCK_MONOTONIC)
+ time = ktime_sub(time, ktime_get_monotonic_offset());
+
+ ev.time = ktime_to_timeval(time);
+ ev.type = EV_SYN;
+ ev.code = SYN_DROPPED;
+ ev.value = 0;
+
+ spin_lock_irqsave(&client->buffer_lock, flags);
+
+ client->buffer[client->head++] = ev;
+ client->head &= client->bufsize - 1;
+ if (unlikely(client->head == client->tail)) {
+ /* drop queue but keep our SYN_DROPPED event */
+ client->tail = (client->head - 1) & (client->bufsize - 1);
+ client->packet_head = client->tail;
+ }
+
+ spin_unlock_irqrestore(&client->buffer_lock, flags);
+}
+
+static void __pass_event(struct evdev_client *client,
+ const struct input_event *event)
+{
client->buffer[client->head++] = *event;
client->head &= client->bufsize - 1;
@@ -86,42 +156,77 @@ static void evdev_pass_event(struct evdev_client *client,
client->packet_head = client->head;
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
+}
+
+static void evdev_pass_values(struct evdev_client *client,
+ const struct input_value *vals, unsigned int count,
+ ktime_t mono, ktime_t real)
+{
+ struct evdev *evdev = client->evdev;
+ const struct input_value *v;
+ struct input_event event;
+ bool wakeup = false;
+
+ if (client->revoked)
+ return;
+
+ event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
+ mono : real);
+
+ /* Interrupts are disabled, just acquire the lock. */
+ spin_lock(&client->buffer_lock);
+
+ for (v = vals; v != vals + count; v++) {
+ event.type = v->type;
+ event.code = v->code;
+ event.value = v->value;
+ __pass_event(client, &event);
+ if (v->type == EV_SYN && v->code == SYN_REPORT)
+ wakeup = true;
+ }
spin_unlock(&client->buffer_lock);
+
+ if (wakeup)
+ wake_up_interruptible(&evdev->wait);
}
/*
- * Pass incoming event to all connected clients.
+ * Pass incoming events to all connected clients.
*/
-static void evdev_event(struct input_handle *handle,
- unsigned int type, unsigned int code, int value)
+static void evdev_events(struct input_handle *handle,
+ const struct input_value *vals, unsigned int count)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
- struct input_event event;
ktime_t time_mono, time_real;
time_mono = ktime_get();
time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
- event.type = type;
- event.code = code;
- event.value = value;
-
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)
- evdev_pass_event(client, &event, time_mono, time_real);
+ evdev_pass_values(client, vals, count, time_mono, time_real);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
- evdev_pass_event(client, &event, time_mono, time_real);
+ evdev_pass_values(client, vals, count,
+ time_mono, time_real);
rcu_read_unlock();
+}
- if (type == EV_SYN && code == SYN_REPORT)
- wake_up_interruptible(&evdev->wait);
+/*
+ * Pass incoming event to all connected clients.
+ */
+static void evdev_event(struct input_handle *handle,
+ unsigned int type, unsigned int code, int value)
+{
+ struct input_value vals[] = { { type, code, value } };
+
+ evdev_events(handle, vals, 1);
}
static int evdev_fasync(int fd, struct file *file, int on)
@@ -141,7 +246,7 @@ static int evdev_flush(struct file *file, fl_owner_t id)
if (retval)
return retval;
- if (!evdev->exist)
+ if (!evdev->exist || client->revoked)
retval = -ENODEV;
else
retval = input_flush_device(&evdev->handle, file);
@@ -266,10 +371,13 @@ static int evdev_release(struct inode *inode, struct file *file)
mutex_unlock(&evdev->mutex);
evdev_detach_client(evdev, client);
- kfree(client);
+
+ if (is_vmalloc_addr(client))
+ vfree(client);
+ else
+ kfree(client);
evdev_close_device(evdev);
- put_device(&evdev->dev);
return 0;
}
@@ -285,35 +393,18 @@ static unsigned int evdev_compute_buffer_size(struct input_dev *dev)
static int evdev_open(struct inode *inode, struct file *file)
{
- struct evdev *evdev;
+ struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev);
+ unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev);
+ unsigned int size = sizeof(struct evdev_client) +
+ bufsize * sizeof(struct input_event);
struct evdev_client *client;
- int i = iminor(inode) - EVDEV_MINOR_BASE;
- unsigned int bufsize;
int error;
- if (i >= EVDEV_MINORS)
- return -ENODEV;
-
- error = mutex_lock_interruptible(&evdev_table_mutex);
- if (error)
- return error;
- evdev = evdev_table[i];
- if (evdev)
- get_device(&evdev->dev);
- mutex_unlock(&evdev_table_mutex);
-
- if (!evdev)
- return -ENODEV;
-
- bufsize = evdev_compute_buffer_size(evdev->handle.dev);
-
- client = kzalloc(sizeof(struct evdev_client) +
- bufsize * sizeof(struct input_event),
- GFP_KERNEL);
- if (!client) {
- error = -ENOMEM;
- goto err_put_evdev;
- }
+ client = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
+ if (!client)
+ client = vzalloc(size);
+ if (!client)
+ return -ENOMEM;
client->bufsize = bufsize;
spin_lock_init(&client->buffer_lock);
@@ -332,8 +423,6 @@ static int evdev_open(struct inode *inode, struct file *file)
err_free_client:
evdev_detach_client(evdev, client);
kfree(client);
- err_put_evdev:
- put_device(&evdev->dev);
return error;
}
@@ -352,7 +441,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
if (retval)
return retval;
- if (!evdev->exist) {
+ if (!evdev->exist || client->revoked) {
retval = -ENODEV;
goto out;
}
@@ -405,7 +494,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
return -EINVAL;
for (;;) {
- if (!evdev->exist)
+ if (!evdev->exist || client->revoked)
return -ENODEV;
if (client->packet_head == client->tail &&
@@ -434,7 +523,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
if (!(file->f_flags & O_NONBLOCK)) {
error = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail ||
- !evdev->exist);
+ !evdev->exist || client->revoked);
if (error)
return error;
}
@@ -452,7 +541,11 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait)
poll_wait(file, &evdev->wait, wait);
- mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;
+ if (evdev->exist && !client->revoked)
+ mask = POLLOUT | POLLWRNORM;
+ else
+ mask = POLLHUP | POLLERR;
+
if (client->packet_head != client->tail)
mask |= POLLIN | POLLRDNORM;
@@ -536,12 +629,10 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
return copy_to_user(p, str, len) ? -EFAULT : len;
}
-#define OLD_KEY_MAX 0x1ff
static int handle_eviocgbit(struct input_dev *dev,
unsigned int type, unsigned int size,
void __user *p, int compat_mode)
{
- static unsigned long keymax_warn_time;
unsigned long *bits;
int len;
@@ -559,24 +650,8 @@ static int handle_eviocgbit(struct input_dev *dev,
default: return -EINVAL;
}
- /*
- * Work around bugs in userspace programs that like to do
- * EVIOCGBIT(EV_KEY, KEY_MAX) and not realize that 'len'
- * should be in bytes, not in bits.
- */
- if (type == EV_KEY && size == OLD_KEY_MAX) {
- len = OLD_KEY_MAX;
- if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000))
- pr_warning("(EVIOCGBIT): Suspicious buffer size %u, "
- "limiting output to %zu bytes. See "
- "http://userweb.kernel.org/~dtor/eviocgbit-bug.html\n",
- OLD_KEY_MAX,
- BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
- }
-
return bits_to_user(bits, len, size, p, compat_mode);
}
-#undef OLD_KEY_MAX
static int evdev_handle_get_keycode(struct input_dev *dev, void __user *p)
{
@@ -649,24 +724,82 @@ static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p)
return input_set_keycode(dev, &ke);
}
+/*
+ * If we transfer state to the user, we should flush all pending events
+ * of the same type from the client's queue. Otherwise, they might end up
+ * with duplicate events, which can screw up client's state tracking.
+ * If bits_to_user fails after flushing the queue, we queue a SYN_DROPPED
+ * event so user-space will notice missing events.
+ *
+ * LOCKING:
+ * We need to take event_lock before buffer_lock to avoid dead-locks. But we
+ * need the even_lock only to guarantee consistent state. We can safely release
+ * it while flushing the queue. This allows input-core to handle filters while
+ * we flush the queue.
+ */
+static int evdev_handle_get_val(struct evdev_client *client,
+ struct input_dev *dev, unsigned int type,
+ unsigned long *bits, unsigned int max,
+ unsigned int size, void __user *p, int compat)
+{
+ int ret;
+ unsigned long *mem;
+
+ mem = kmalloc(sizeof(unsigned long) * max, GFP_KERNEL);
+ if (!mem)
+ return -ENOMEM;
+
+ spin_lock_irq(&dev->event_lock);
+ spin_lock(&client->buffer_lock);
+
+ memcpy(mem, bits, sizeof(unsigned long) * max);
+
+ spin_unlock(&dev->event_lock);
+
+ __evdev_flush_queue(client, type);
+
+ spin_unlock_irq(&client->buffer_lock);
+
+ ret = bits_to_user(mem, max, size, p, compat);
+ if (ret < 0)
+ evdev_queue_syn_dropped(client);
+
+ kfree(mem);
+
+ return ret;
+}
+
static int evdev_handle_mt_request(struct input_dev *dev,
unsigned int size,
int __user *ip)
{
- const struct input_mt_slot *mt = dev->mt;
+ const struct input_mt *mt = dev->mt;
unsigned int code;
int max_slots;
int i;
if (get_user(code, &ip[0]))
return -EFAULT;
- if (!input_is_mt_value(code))
+ if (!mt || !input_is_mt_value(code))
return -EINVAL;
max_slots = (size - sizeof(__u32)) / sizeof(__s32);
- for (i = 0; i < dev->mtsize && i < max_slots; i++)
- if (put_user(input_mt_get_value(&mt[i], code), &ip[1 + i]))
+ for (i = 0; i < mt->num_slots && i < max_slots; i++) {
+ int value = input_mt_get_value(&mt->slots[i], code);
+ if (put_user(value, &ip[1 + i]))
return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
+ struct file *file)
+{
+ client->revoked = true;
+ evdev_ungrab(evdev, client);
+ input_flush_device(&evdev->handle, file);
+ wake_up_interruptible(&evdev->wait);
return 0;
}
@@ -733,6 +866,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
else
return evdev_ungrab(evdev, client);
+ case EVIOCREVOKE:
+ if (p)
+ return -EINVAL;
+ else
+ return evdev_revoke(evdev, client, file);
+
case EVIOCSCLOCKID:
if (copy_from_user(&i, p, sizeof(unsigned int)))
return -EFAULT;
@@ -768,16 +907,20 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
return evdev_handle_mt_request(dev, size, ip);
case EVIOCGKEY(0):
- return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode);
+ return evdev_handle_get_val(client, dev, EV_KEY, dev->key,
+ KEY_MAX, size, p, compat_mode);
case EVIOCGLED(0):
- return bits_to_user(dev->led, LED_MAX, size, p, compat_mode);
+ return evdev_handle_get_val(client, dev, EV_LED, dev->led,
+ LED_MAX, size, p, compat_mode);
case EVIOCGSND(0):
- return bits_to_user(dev->snd, SND_MAX, size, p, compat_mode);
+ return evdev_handle_get_val(client, dev, EV_SND, dev->snd,
+ SND_MAX, size, p, compat_mode);
case EVIOCGSW(0):
- return bits_to_user(dev->sw, SW_MAX, size, p, compat_mode);
+ return evdev_handle_get_val(client, dev, EV_SW, dev->sw,
+ SW_MAX, size, p, compat_mode);
case EVIOCGNAME(0):
return str_to_user(dev->name, size, p);
@@ -793,11 +936,13 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
return -EFAULT;
error = input_ff_upload(dev, &effect, file);
+ if (error)
+ return error;
if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
return -EFAULT;
- return error;
+ return 0;
}
/* Multi-number variable-length handlers */
@@ -874,7 +1019,7 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
if (retval)
return retval;
- if (!evdev->exist) {
+ if (!evdev->exist || client->revoked) {
retval = -ENODEV;
goto out;
}
@@ -915,26 +1060,6 @@ static const struct file_operations evdev_fops = {
.llseek = no_llseek,
};
-static int evdev_install_chrdev(struct evdev *evdev)
-{
- /*
- * No need to do any locking here as calls to connect and
- * disconnect are serialized by the input core
- */
- evdev_table[evdev->minor] = evdev;
- return 0;
-}
-
-static void evdev_remove_chrdev(struct evdev *evdev)
-{
- /*
- * Lock evdev table to prevent race with evdev_open()
- */
- mutex_lock(&evdev_table_mutex);
- evdev_table[evdev->minor] = NULL;
- mutex_unlock(&evdev_table_mutex);
-}
-
/*
* Mark device non-existent. This disables writes, ioctls and
* prevents new users from opening the device. Already posted
@@ -953,7 +1078,8 @@ static void evdev_cleanup(struct evdev *evdev)
evdev_mark_dead(evdev);
evdev_hangup(evdev);
- evdev_remove_chrdev(evdev);
+
+ cdev_del(&evdev->cdev);
/* evdev is marked dead so no one else accesses evdev->open */
if (evdev->open) {
@@ -964,43 +1090,47 @@ static void evdev_cleanup(struct evdev *evdev)
/*
* Create new evdev device. Note that input core serializes calls
- * to connect and disconnect so we don't need to lock evdev_table here.
+ * to connect and disconnect.
*/
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
int minor;
+ int dev_no;
int error;
- for (minor = 0; minor < EVDEV_MINORS; minor++)
- if (!evdev_table[minor])
- break;
-
- if (minor == EVDEV_MINORS) {
- pr_err("no more free evdev devices\n");
- return -ENFILE;
+ minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);
+ if (minor < 0) {
+ error = minor;
+ pr_err("failed to reserve new minor: %d\n", error);
+ return error;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
- if (!evdev)
- return -ENOMEM;
+ if (!evdev) {
+ error = -ENOMEM;
+ goto err_free_minor;
+ }
INIT_LIST_HEAD(&evdev->client_list);
spin_lock_init(&evdev->client_lock);
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
-
- dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = true;
- evdev->minor = minor;
+
+ dev_no = minor;
+ /* Normalize device number if it falls into legacy range */
+ if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS)
+ dev_no -= EVDEV_MINOR_BASE;
+ dev_set_name(&evdev->dev, "event%d", dev_no);
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
- evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
+ evdev->dev.devt = MKDEV(INPUT_MAJOR, minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;
evdev->dev.release = evdev_free;
@@ -1010,7 +1140,9 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
if (error)
goto err_free_evdev;
- error = evdev_install_chrdev(evdev);
+ cdev_init(&evdev->cdev, &evdev_fops);
+ evdev->cdev.kobj.parent = &evdev->dev.kobj;
+ error = cdev_add(&evdev->cdev, evdev->dev.devt, 1);
if (error)
goto err_unregister_handle;
@@ -1026,6 +1158,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
input_unregister_handle(&evdev->handle);
err_free_evdev:
put_device(&evdev->dev);
+ err_free_minor:
+ input_free_minor(minor);
return error;
}
@@ -1035,6 +1169,7 @@ static void evdev_disconnect(struct input_handle *handle)
device_del(&evdev->dev);
evdev_cleanup(evdev);
+ input_free_minor(MINOR(evdev->dev.devt));
input_unregister_handle(handle);
put_device(&evdev->dev);
}
@@ -1048,9 +1183,10 @@ MODULE_DEVICE_TABLE(input, evdev_ids);
static struct input_handler evdev_handler = {
.event = evdev_event,
+ .events = evdev_events,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
- .fops = &evdev_fops,
+ .legacy_minors = true,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,