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.c630
1 files changed, 425 insertions, 205 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index e3f7fc6f956..fd325ec9f06 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -8,6 +8,8 @@
* the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#define EVDEV_MINOR_BASE 64
#define EVDEV_MINORS 32
#define EVDEV_MIN_BUFFER_SIZE 64U
@@ -16,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.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;
@@ -33,69 +37,196 @@ struct evdev {
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
+ struct cdev cdev;
bool exist;
};
struct evdev_client {
- int head;
- int tail;
+ unsigned int head;
+ unsigned int tail;
+ unsigned int packet_head; /* [future] position of the first element of next packet */
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
- int bufsize;
+ 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)
+ 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)
{
- /*
- * Interrupts are disabled, just acquire the lock.
- * Make sure we don't leave with the client buffer
- * "empty" by having client->head == client->tail.
- */
+ unsigned long flags;
+ struct input_event ev;
+ ktime_t time;
+
+ 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;
+
+ if (unlikely(client->head == client->tail)) {
+ /*
+ * This effectively "drops" all unconsumed events, leaving
+ * EV_SYN/SYN_DROPPED plus the newest event in the queue.
+ */
+ client->tail = (client->head - 2) & (client->bufsize - 1);
+
+ client->buffer[client->tail].time = event->time;
+ client->buffer[client->tail].type = EV_SYN;
+ client->buffer[client->tail].code = SYN_DROPPED;
+ client->buffer[client->tail].value = 0;
+
+ client->packet_head = client->tail;
+ }
+
+ if (event->type == EV_SYN && event->code == SYN_REPORT) {
+ 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);
- do {
- client->buffer[client->head++] = *event;
- client->head &= client->bufsize - 1;
- } while (client->head == client->tail);
+
+ 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 (event->type == EV_SYN)
- kill_fasync(&client->fasync, SIGIO, POLL_IN);
+ 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;
- do_gettimeofday(&event.time);
- event.type = type;
- event.code = code;
- event.value = value;
+ time_mono = ktime_get();
+ time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
rcu_read_lock();
client = rcu_dereference(evdev->grab);
+
if (client)
- evdev_pass_event(client, &event);
+ 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);
+ evdev_pass_values(client, vals, count,
+ time_mono, time_real);
rcu_read_unlock();
+}
- 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)
@@ -115,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);
@@ -148,14 +279,16 @@ static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
return error;
rcu_assign_pointer(evdev->grab, client);
- synchronize_rcu();
return 0;
}
static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
{
- if (evdev->grab != client)
+ struct evdev_client *grab = rcu_dereference_protected(evdev->grab,
+ lockdep_is_held(&evdev->mutex));
+
+ if (grab != client)
return -EINVAL;
rcu_assign_pointer(evdev->grab, NULL);
@@ -171,7 +304,6 @@ static void evdev_attach_client(struct evdev *evdev,
spin_lock(&evdev->client_lock);
list_add_tail_rcu(&client->node, &evdev->client_list);
spin_unlock(&evdev->client_lock);
- synchronize_rcu();
}
static void evdev_detach_client(struct evdev *evdev,
@@ -235,15 +367,17 @@ static int evdev_release(struct inode *inode, struct file *file)
struct evdev *evdev = client->evdev;
mutex_lock(&evdev->mutex);
- if (evdev->grab == client)
- evdev_ungrab(evdev, client);
+ evdev_ungrab(evdev, client);
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;
}
@@ -259,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);
@@ -306,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;
}
@@ -317,27 +432,30 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
- int retval;
+ int retval = 0;
+
+ if (count != 0 && count < input_event_size())
+ return -EINVAL;
retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;
- if (!evdev->exist) {
+ if (!evdev->exist || client->revoked) {
retval = -ENODEV;
goto out;
}
- while (retval < count) {
+ while (retval + input_event_size() <= count) {
if (input_event_from_user(buffer + retval, &event)) {
retval = -EFAULT;
goto out;
}
+ retval += input_event_size();
input_inject_event(&evdev->handle,
event.type, event.code, event.value);
- retval += input_event_size();
}
out:
@@ -352,7 +470,7 @@ static int evdev_fetch_next_event(struct evdev_client *client,
spin_lock_irq(&client->buffer_lock);
- have_event = client->head != client->tail;
+ have_event = client->packet_head != client->tail;
if (have_event) {
*event = client->buffer[client->tail++];
client->tail &= client->bufsize - 1;
@@ -369,33 +487,49 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
- int retval;
+ size_t read = 0;
+ int error;
- if (count < input_event_size())
+ if (count != 0 && count < input_event_size())
return -EINVAL;
- if (client->head == client->tail && evdev->exist &&
- (file->f_flags & O_NONBLOCK))
- return -EAGAIN;
+ for (;;) {
+ if (!evdev->exist || client->revoked)
+ return -ENODEV;
- retval = wait_event_interruptible(evdev->wait,
- client->head != client->tail || !evdev->exist);
- if (retval)
- return retval;
+ if (client->packet_head == client->tail &&
+ (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
- if (!evdev->exist)
- return -ENODEV;
+ /*
+ * count == 0 is special - no IO is done but we check
+ * for error conditions (see above).
+ */
+ if (count == 0)
+ break;
- while (retval + input_event_size() <= count &&
- evdev_fetch_next_event(client, &event)) {
+ while (read + input_event_size() <= count &&
+ evdev_fetch_next_event(client, &event)) {
- if (input_event_to_user(buffer + retval, &event))
- return -EFAULT;
+ if (input_event_to_user(buffer + read, &event))
+ return -EFAULT;
- retval += input_event_size();
+ read += input_event_size();
+ }
+
+ if (read)
+ break;
+
+ if (!(file->f_flags & O_NONBLOCK)) {
+ error = wait_event_interruptible(evdev->wait,
+ client->packet_head != client->tail ||
+ !evdev->exist || client->revoked);
+ if (error)
+ return error;
+ }
}
- return retval;
+ return read;
}
/* No kernel lock - fine */
@@ -407,8 +541,12 @@ 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 (client->head != client->tail)
+ if (evdev->exist && !client->revoked)
+ mask = POLLOUT | POLLWRNORM;
+ else
+ mask = POLLHUP | POLLERR;
+
+ if (client->packet_head != client->tail)
mask |= POLLIN | POLLRDNORM;
return mask;
@@ -491,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;
@@ -514,98 +650,158 @@ 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))
- printk(KERN_WARNING
- "evdev.c(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, size_t size)
+static int evdev_handle_get_keycode(struct input_dev *dev, void __user *p)
{
- struct input_keymap_entry ke;
+ struct input_keymap_entry ke = {
+ .len = sizeof(unsigned int),
+ .flags = 0,
+ };
+ int __user *ip = (int __user *)p;
int error;
- memset(&ke, 0, sizeof(ke));
-
- if (size == sizeof(unsigned int[2])) {
- /* legacy case */
- int __user *ip = (int __user *)p;
+ /* legacy case */
+ if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
+ return -EFAULT;
- if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
- return -EFAULT;
+ error = input_get_keycode(dev, &ke);
+ if (error)
+ return error;
- ke.len = sizeof(unsigned int);
- ke.flags = 0;
+ if (put_user(ke.keycode, ip + 1))
+ return -EFAULT;
- error = input_get_keycode(dev, &ke);
- if (error)
- return error;
+ return 0;
+}
- if (put_user(ke.keycode, ip + 1))
- return -EFAULT;
+static int evdev_handle_get_keycode_v2(struct input_dev *dev, void __user *p)
+{
+ struct input_keymap_entry ke;
+ int error;
- } else {
- size = min(size, sizeof(ke));
+ if (copy_from_user(&ke, p, sizeof(ke)))
+ return -EFAULT;
- if (copy_from_user(&ke, p, size))
- return -EFAULT;
+ error = input_get_keycode(dev, &ke);
+ if (error)
+ return error;
- error = input_get_keycode(dev, &ke);
- if (error)
- return error;
+ if (copy_to_user(p, &ke, sizeof(ke)))
+ return -EFAULT;
- if (copy_to_user(p, &ke, size))
- return -EFAULT;
- }
return 0;
}
-static int evdev_handle_set_keycode(struct input_dev *dev,
- void __user *p, size_t size)
+static int evdev_handle_set_keycode(struct input_dev *dev, void __user *p)
+{
+ struct input_keymap_entry ke = {
+ .len = sizeof(unsigned int),
+ .flags = 0,
+ };
+ int __user *ip = (int __user *)p;
+
+ if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
+ return -EFAULT;
+
+ if (get_user(ke.keycode, ip + 1))
+ return -EFAULT;
+
+ return input_set_keycode(dev, &ke);
+}
+
+static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p)
{
struct input_keymap_entry ke;
- memset(&ke, 0, sizeof(ke));
+ if (copy_from_user(&ke, p, sizeof(ke)))
+ return -EFAULT;
- if (size == sizeof(unsigned int[2])) {
- /* legacy case */
- int __user *ip = (int __user *)p;
+ if (ke.len > sizeof(ke.scancode))
+ return -EINVAL;
- if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
- return -EFAULT;
+ return input_set_keycode(dev, &ke);
+}
- if (get_user(ke.keycode, ip + 1))
- return -EFAULT;
+/*
+ * 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;
- ke.len = sizeof(unsigned int);
- ke.flags = 0;
+ spin_lock_irq(&dev->event_lock);
+ spin_lock(&client->buffer_lock);
- } else {
- size = min(size, sizeof(ke));
+ memcpy(mem, bits, sizeof(unsigned long) * max);
- if (copy_from_user(&ke, p, size))
- return -EFAULT;
+ spin_unlock(&dev->event_lock);
- if (ke.len > sizeof(ke.scancode))
- return -EINVAL;
+ __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 *mt = dev->mt;
+ unsigned int code;
+ int max_slots;
+ int i;
+
+ if (get_user(code, &ip[0]))
+ return -EFAULT;
+ if (!mt || !input_is_mt_value(code))
+ return -EINVAL;
+
+ max_slots = (size - sizeof(__u32)) / sizeof(__s32);
+ 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 input_set_keycode(dev, &ke);
+ 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;
}
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
@@ -669,6 +865,32 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
return evdev_grab(evdev, client);
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;
+ if (i != CLOCK_MONOTONIC && i != CLOCK_REALTIME)
+ return -EINVAL;
+ client->clkid = i;
+ return 0;
+
+ case EVIOCGKEYCODE:
+ return evdev_handle_get_keycode(dev, p);
+
+ case EVIOCSKEYCODE:
+ return evdev_handle_set_keycode(dev, p);
+
+ case EVIOCGKEYCODE_V2:
+ return evdev_handle_get_keycode_v2(dev, p);
+
+ case EVIOCSKEYCODE_V2:
+ return evdev_handle_set_keycode_v2(dev, p);
}
size = _IOC_SIZE(cmd);
@@ -677,17 +899,28 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
switch (EVIOC_MASK_SIZE(cmd)) {
+ case EVIOCGPROP(0):
+ return bits_to_user(dev->propbit, INPUT_PROP_MAX,
+ size, p, compat_mode);
+
+ case EVIOCGMTSLOTS(0):
+ 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);
@@ -703,17 +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;
-
- case EVIOC_MASK_SIZE(EVIOCGKEYCODE):
- return evdev_handle_get_keycode(dev, p, size);
-
- case EVIOC_MASK_SIZE(EVIOCSKEYCODE):
- return evdev_handle_set_keycode(dev, p, size);
+ return 0;
}
/* Multi-number variable-length handlers */
@@ -790,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;
}
@@ -831,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
@@ -869,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) {
@@ -880,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) {
- printk(KERN_ERR "evdev: 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;
@@ -926,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;
@@ -942,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;
}
@@ -951,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);
}
@@ -964,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,