fix-EVIOCGRAB-semantics.patch
[kernel.git] / drivers / input / evdev.c
index e5b4e9b..2f3d825 100644 (file)
@@ -28,7 +28,7 @@ struct evdev {
        char name[16];
        struct input_handle handle;
        wait_queue_head_t wait;
-       struct evdev_client *grab;
+       int *grab;
        struct list_head client_list;
        spinlock_t client_lock; /* protects client_list */
        struct mutex mutex;
@@ -39,6 +39,7 @@ struct evdev_client {
        struct input_event buffer[EVDEV_BUFFER_SIZE];
        int head;
        int tail;
+       int grab;
        spinlock_t buffer_lock; /* protects access to buffer, head and tail */
        struct fasync_struct *fasync;
        struct evdev *evdev;
@@ -79,12 +80,8 @@ static void evdev_event(struct input_handle *handle,
 
        rcu_read_lock();
 
-       client = rcu_dereference(evdev->grab);
-       if (client)
+       list_for_each_entry_rcu(client, &evdev->client_list, node)
                evdev_pass_event(client, &event);
-       else
-               list_for_each_entry_rcu(client, &evdev->client_list, node)
-                       evdev_pass_event(client, &event);
 
        rcu_read_unlock();
 
@@ -135,14 +132,15 @@ static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
 {
        int error;
 
-       if (evdev->grab)
+       if (client->grab)
                return -EBUSY;
 
-       error = input_grab_device(&evdev->handle);
-       if (error)
-               return error;
-
-       rcu_assign_pointer(evdev->grab, client);
+       if (!evdev->grab++) {
+           error = input_grab_device(&evdev->handle);
+           if (error)
+                   return error;
+       }
+       client->grab = 1;
        synchronize_rcu();
 
        return 0;
@@ -150,12 +148,12 @@ static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
 
 static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
 {
-       if (evdev->grab != client)
+       if (!client->grab)
                return  -EINVAL;
 
-       rcu_assign_pointer(evdev->grab, NULL);
-       synchronize_rcu();
-       input_release_device(&evdev->handle);
+       if (!--evdev->grab && evdev->exist)
+               input_release_device(&evdev->handle);
+       client->grab = 0;
 
        return 0;
 }
@@ -230,7 +228,7 @@ static int evdev_release(struct inode *inode, struct file *file)
        struct evdev *evdev = client->evdev;
 
        mutex_lock(&evdev->mutex);
-       if (evdev->grab == client)
+       if (client->grab)
                evdev_ungrab(evdev, client);
        mutex_unlock(&evdev->mutex);