Merge branch 'next' into for-linus-3.0
[pandora-kernel.git] / drivers / input / evdev.c
index 7f42d3a..4cf2534 100644 (file)
@@ -39,13 +39,14 @@ struct evdev {
 };
 
 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;
+       unsigned int bufsize;
        struct input_event buffer[];
 };
 
@@ -55,20 +56,33 @@ 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);
-       spin_unlock(&client->buffer_lock);
 
-       if (event->type == EV_SYN)
+       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);
+       }
+
+       spin_unlock(&client->buffer_lock);
 }
 
 /*
@@ -97,7 +111,8 @@ static void evdev_event(struct input_handle *handle,
 
        rcu_read_unlock();
 
-       wake_up_interruptible(&evdev->wait);
+       if (type == EV_SYN && code == SYN_REPORT)
+               wake_up_interruptible(&evdev->wait);
 }
 
 static int evdev_fasync(int fd, struct file *file, int on)
@@ -150,7 +165,6 @@ static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
                return error;
 
        rcu_assign_pointer(evdev->grab, client);
-       synchronize_rcu();
 
        return 0;
 }
@@ -173,7 +187,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,
@@ -378,12 +391,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
        if (count < input_event_size())
                return -EINVAL;
 
-       if (client->head == client->tail && evdev->exist &&
+       if (client->packet_head == client->tail && evdev->exist &&
            (file->f_flags & O_NONBLOCK))
                return -EAGAIN;
 
        retval = wait_event_interruptible(evdev->wait,
-               client->head != client->tail || !evdev->exist);
+               client->packet_head != client->tail || !evdev->exist);
        if (retval)
                return retval;
 
@@ -412,7 +425,7 @@ 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 (client->packet_head != client->tail)
                mask |= POLLIN | POLLRDNORM;
 
        return mask;