diff options
Diffstat (limited to 'drivers/input/evdev.c')
-rw-r--r-- | drivers/input/evdev.c | 43 |
1 files changed, 27 insertions, 16 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index c8471a2552e..88d8e4cb419 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -39,13 +39,13 @@ struct evdev { }; struct evdev_client { - int head; - int tail; + unsigned int head; + unsigned int tail; spinlock_t buffer_lock; /* protects access to buffer, head and tail */ struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; - int bufsize; + unsigned int bufsize; struct input_event buffer[]; }; @@ -55,16 +55,25 @@ static DEFINE_MUTEX(evdev_table_mutex); static void evdev_pass_event(struct evdev_client *client, struct input_event *event) { - /* - * Interrupts are disabled, just acquire the lock. - * Make sure we don't leave with the client buffer - * "empty" by having client->head == client->tail. - */ + /* 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); + + 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; + } + spin_unlock(&client->buffer_lock); if (event->type == EV_SYN) @@ -321,6 +330,9 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer, struct input_event event; int retval; + if (count < input_event_size()) + return -EINVAL; + retval = mutex_lock_interruptible(&evdev->mutex); if (retval) return retval; @@ -330,17 +342,16 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer, goto out; } - while (retval < count) { - + do { 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(); - } + } while (retval + input_event_size() <= count); out: mutex_unlock(&evdev->mutex); |