]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
input patches for .23
authorGreg Kroah-Hartman <gregkh@suse.de>
Tue, 15 Jan 2008 21:37:35 +0000 (13:37 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 15 Jan 2008 21:37:35 +0000 (13:37 -0800)
queue-2.6.23/input-evdev-implement-proper-locking.patch [new file with mode: 0644]
queue-2.6.23/input-fix-open-count-handling-in-input-interfaces.patch [new file with mode: 0644]
queue-2.6.23/input-implement-proper-locking-in-input-core.patch [new file with mode: 0644]
queue-2.6.23/input-joydev-implement-proper-locking.patch [new file with mode: 0644]
queue-2.6.23/input-mousedev-implement-proper-locking.patch [new file with mode: 0644]
queue-2.6.23/input-tsdev-implement-proper-locking.patch [new file with mode: 0644]
queue-2.6.23/input-use-full-rcu-api.patch [new file with mode: 0644]
queue-2.6.23/series

diff --git a/queue-2.6.23/input-evdev-implement-proper-locking.patch b/queue-2.6.23/input-evdev-implement-proper-locking.patch
new file mode 100644 (file)
index 0000000..b3d70d7
--- /dev/null
@@ -0,0 +1,1028 @@
+From 6addb1d6de1968b84852f54561cc9a999909b5a9 Mon Sep 17 00:00:00 2001
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Date: Thu, 30 Aug 2007 00:22:18 -0400
+Subject: Input: evdev - implement proper locking
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+patch 6addb1d6de1968b84852f54561cc9a999909b5a9 in mainline.
+
+Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
+Cc: Al Viro <viro@ZenIV.linux.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/input/evdev.c |  719 +++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 476 insertions(+), 243 deletions(-)
+
+--- a/drivers/input/evdev.c
++++ b/drivers/input/evdev.c
+@@ -30,6 +30,8 @@ struct evdev {
+       wait_queue_head_t wait;
+       struct evdev_client *grab;
+       struct list_head client_list;
++      spinlock_t client_lock; /* protects client_list */
++      struct mutex mutex;
+       struct device dev;
+ };
+@@ -37,39 +39,53 @@ struct evdev_client {
+       struct input_event buffer[EVDEV_BUFFER_SIZE];
+       int head;
+       int tail;
++      spinlock_t buffer_lock; /* protects access to buffer, head and tail */
+       struct fasync_struct *fasync;
+       struct evdev *evdev;
+       struct list_head node;
+ };
+ static struct evdev *evdev_table[EVDEV_MINORS];
++static DEFINE_MUTEX(evdev_table_mutex);
+-static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
++static void evdev_pass_event(struct evdev_client *client,
++                           struct input_event *event)
++{
++      /*
++       * Interrupts are disabled, just acquire the lock
++       */
++      spin_lock(&client->buffer_lock);
++      client->buffer[client->head++] = *event;
++      client->head &= EVDEV_BUFFER_SIZE - 1;
++      spin_unlock(&client->buffer_lock);
++
++      kill_fasync(&client->fasync, SIGIO, POLL_IN);
++}
++
++/*
++ * Pass incoming event to all connected clients. Note that we are
++ * caleld under a spinlock with interrupts off so we don't need
++ * to use rcu_read_lock() here. Writers will be using syncronize_sched()
++ * instead of synchrnoize_rcu().
++ */
++static void evdev_event(struct input_handle *handle,
++                      unsigned int type, unsigned int code, int value)
+ {
+       struct evdev *evdev = handle->private;
+       struct evdev_client *client;
++      struct input_event event;
+-      if (evdev->grab) {
+-              client = evdev->grab;
+-
+-              do_gettimeofday(&client->buffer[client->head].time);
+-              client->buffer[client->head].type = type;
+-              client->buffer[client->head].code = code;
+-              client->buffer[client->head].value = value;
+-              client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
+-
+-              kill_fasync(&client->fasync, SIGIO, POLL_IN);
+-      } else
+-              list_for_each_entry(client, &evdev->client_list, node) {
+-
+-                      do_gettimeofday(&client->buffer[client->head].time);
+-                      client->buffer[client->head].type = type;
+-                      client->buffer[client->head].code = code;
+-                      client->buffer[client->head].value = value;
+-                      client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
+-
+-                      kill_fasync(&client->fasync, SIGIO, POLL_IN);
+-              }
++      do_gettimeofday(&event.time);
++      event.type = type;
++      event.code = code;
++      event.value = value;
++
++      client = rcu_dereference(evdev->grab);
++      if (client)
++              evdev_pass_event(client, &event);
++      else
++              list_for_each_entry_rcu(client, &evdev->client_list, node)
++                      evdev_pass_event(client, &event);
+       wake_up_interruptible(&evdev->wait);
+ }
+@@ -88,38 +104,142 @@ static int evdev_flush(struct file *file
+ {
+       struct evdev_client *client = file->private_data;
+       struct evdev *evdev = client->evdev;
++      int retval;
++
++      retval = mutex_lock_interruptible(&evdev->mutex);
++      if (retval)
++              return retval;
+       if (!evdev->exist)
+-              return -ENODEV;
++              retval = -ENODEV;
++      else
++              retval = input_flush_device(&evdev->handle, file);
+-      return input_flush_device(&evdev->handle, file);
++      mutex_unlock(&evdev->mutex);
++      return retval;
+ }
+ static void evdev_free(struct device *dev)
+ {
+       struct evdev *evdev = container_of(dev, struct evdev, dev);
+-      evdev_table[evdev->minor] = NULL;
+       kfree(evdev);
+ }
++/*
++ * Grabs an event device (along with underlying input device).
++ * This function is called with evdev->mutex taken.
++ */
++static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
++{
++      int error;
++
++      if (evdev->grab)
++              return -EBUSY;
++
++      error = input_grab_device(&evdev->handle);
++      if (error)
++              return error;
++
++      rcu_assign_pointer(evdev->grab, client);
++      /*
++       * We don't use synchronize_rcu() here because read-side
++       * critical section is protected by a spinlock instead
++       * of rcu_read_lock().
++       */
++      synchronize_sched();
++
++      return 0;
++}
++
++static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
++{
++      if (evdev->grab != client)
++              return  -EINVAL;
++
++      rcu_assign_pointer(evdev->grab, NULL);
++      synchronize_sched();
++      input_release_device(&evdev->handle);
++
++      return 0;
++}
++
++static void evdev_attach_client(struct evdev *evdev,
++                              struct evdev_client *client)
++{
++      spin_lock(&evdev->client_lock);
++      list_add_tail_rcu(&client->node, &evdev->client_list);
++      spin_unlock(&evdev->client_lock);
++      synchronize_sched();
++}
++
++static void evdev_detach_client(struct evdev *evdev,
++                              struct evdev_client *client)
++{
++      spin_lock(&evdev->client_lock);
++      list_del_rcu(&client->node);
++      spin_unlock(&evdev->client_lock);
++      synchronize_sched();
++}
++
++static int evdev_open_device(struct evdev *evdev)
++{
++      int retval;
++
++      retval = mutex_lock_interruptible(&evdev->mutex);
++      if (retval)
++              return retval;
++
++      if (!evdev->exist)
++              retval = -ENODEV;
++      else if (!evdev->open++)
++              retval = input_open_device(&evdev->handle);
++
++      mutex_unlock(&evdev->mutex);
++      return retval;
++}
++
++static void evdev_close_device(struct evdev *evdev)
++{
++      mutex_lock(&evdev->mutex);
++
++      if (evdev->exist && !--evdev->open)
++              input_close_device(&evdev->handle);
++
++      mutex_unlock(&evdev->mutex);
++}
++
++/*
++ * Wake up users waiting for IO so they can disconnect from
++ * dead device.
++ */
++static void evdev_hangup(struct evdev *evdev)
++{
++      struct evdev_client *client;
++
++      spin_lock(&evdev->client_lock);
++      list_for_each_entry(client, &evdev->client_list, node)
++              kill_fasync(&client->fasync, SIGIO, POLL_HUP);
++      spin_unlock(&evdev->client_lock);
++
++      wake_up_interruptible(&evdev->wait);
++}
++
+ static int evdev_release(struct inode *inode, struct file *file)
+ {
+       struct evdev_client *client = file->private_data;
+       struct evdev *evdev = client->evdev;
+-      if (evdev->grab == client) {
+-              input_release_device(&evdev->handle);
+-              evdev->grab = NULL;
+-      }
++      mutex_lock(&evdev->mutex);
++      if (evdev->grab == client)
++              evdev_ungrab(evdev, client);
++      mutex_unlock(&evdev->mutex);
+       evdev_fasync(-1, file, 0);
+-      list_del(&client->node);
++      evdev_detach_client(evdev, client);
+       kfree(client);
+-      if (!--evdev->open && evdev->exist)
+-              input_close_device(&evdev->handle);
+-
++      evdev_close_device(evdev);
+       put_device(&evdev->dev);
+       return 0;
+@@ -127,41 +247,44 @@ static int evdev_release(struct inode *i
+ static int evdev_open(struct inode *inode, struct file *file)
+ {
+-      struct evdev_client *client;
+       struct evdev *evdev;
++      struct evdev_client *client;
+       int i = iminor(inode) - EVDEV_MINOR_BASE;
+       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 || !evdev->exist)
++      if (!evdev)
+               return -ENODEV;
+-      get_device(&evdev->dev);
+-
+       client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
+       if (!client) {
+               error = -ENOMEM;
+               goto err_put_evdev;
+       }
++      spin_lock_init(&client->buffer_lock);
+       client->evdev = evdev;
+-      list_add_tail(&client->node, &evdev->client_list);
++      evdev_attach_client(evdev, client);
+-      if (!evdev->open++ && evdev->exist) {
+-              error = input_open_device(&evdev->handle);
+-              if (error)
+-                      goto err_free_client;
+-      }
++      error = evdev_open_device(evdev);
++      if (error)
++              goto err_free_client;
+       file->private_data = client;
+       return 0;
+  err_free_client:
+-      list_del(&client->node);
++      evdev_detach_client(evdev, client);
+       kfree(client);
+  err_put_evdev:
+       put_device(&evdev->dev);
+@@ -197,12 +320,14 @@ static inline size_t evdev_event_size(vo
+               sizeof(struct input_event_compat) : sizeof(struct input_event);
+ }
+-static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
++static int evdev_event_from_user(const char __user *buffer,
++                               struct input_event *event)
+ {
+       if (COMPAT_TEST) {
+               struct input_event_compat compat_event;
+-              if (copy_from_user(&compat_event, buffer, sizeof(struct input_event_compat)))
++              if (copy_from_user(&compat_event, buffer,
++                                 sizeof(struct input_event_compat)))
+                       return -EFAULT;
+               event->time.tv_sec = compat_event.time.tv_sec;
+@@ -219,7 +344,8 @@ static int evdev_event_from_user(const c
+       return 0;
+ }
+-static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
++static int evdev_event_to_user(char __user *buffer,
++                              const struct input_event *event)
+ {
+       if (COMPAT_TEST) {
+               struct input_event_compat compat_event;
+@@ -230,7 +356,8 @@ static int evdev_event_to_user(char __us
+               compat_event.code = event->code;
+               compat_event.value = event->value;
+-              if (copy_to_user(buffer, &compat_event, sizeof(struct input_event_compat)))
++              if (copy_to_user(buffer, &compat_event,
++                               sizeof(struct input_event_compat)))
+                       return -EFAULT;
+       } else {
+@@ -248,7 +375,8 @@ static inline size_t evdev_event_size(vo
+       return sizeof(struct input_event);
+ }
+-static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
++static int evdev_event_from_user(const char __user *buffer,
++                               struct input_event *event)
+ {
+       if (copy_from_user(event, buffer, sizeof(struct input_event)))
+               return -EFAULT;
+@@ -256,7 +384,8 @@ static int evdev_event_from_user(const c
+       return 0;
+ }
+-static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
++static int evdev_event_to_user(char __user *buffer,
++                              const struct input_event *event)
+ {
+       if (copy_to_user(buffer, event, sizeof(struct input_event)))
+               return -EFAULT;
+@@ -266,37 +395,71 @@ static int evdev_event_to_user(char __us
+ #endif /* CONFIG_COMPAT */
+-static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
++static ssize_t evdev_write(struct file *file, const char __user *buffer,
++                         size_t count, loff_t *ppos)
+ {
+       struct evdev_client *client = file->private_data;
+       struct evdev *evdev = client->evdev;
+       struct input_event event;
+-      int retval = 0;
++      int retval;
+-      if (!evdev->exist)
+-              return -ENODEV;
++      retval = mutex_lock_interruptible(&evdev->mutex);
++      if (retval)
++              return retval;
++
++      if (!evdev->exist) {
++              retval = -ENODEV;
++              goto out;
++      }
+       while (retval < count) {
+-              if (evdev_event_from_user(buffer + retval, &event))
+-                      return -EFAULT;
+-              input_inject_event(&evdev->handle, event.type, event.code, event.value);
++              if (evdev_event_from_user(buffer + retval, &event)) {
++                      retval = -EFAULT;
++                      goto out;
++              }
++
++              input_inject_event(&evdev->handle,
++                                 event.type, event.code, event.value);
+               retval += evdev_event_size();
+       }
++ out:
++      mutex_unlock(&evdev->mutex);
+       return retval;
+ }
+-static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
++static int evdev_fetch_next_event(struct evdev_client *client,
++                                struct input_event *event)
++{
++      int have_event;
++
++      spin_lock_irq(&client->buffer_lock);
++
++      have_event = client->head != client->tail;
++      if (have_event) {
++              *event = client->buffer[client->tail++];
++              client->tail &= EVDEV_BUFFER_SIZE - 1;
++      }
++
++      spin_unlock_irq(&client->buffer_lock);
++
++      return have_event;
++}
++
++static ssize_t evdev_read(struct file *file, char __user *buffer,
++                        size_t count, loff_t *ppos)
+ {
+       struct evdev_client *client = file->private_data;
+       struct evdev *evdev = client->evdev;
++      struct input_event event;
+       int retval;
+       if (count < evdev_event_size())
+               return -EINVAL;
+-      if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
++      if (client->head == client->tail && evdev->exist &&
++          (file->f_flags & O_NONBLOCK))
+               return -EAGAIN;
+       retval = wait_event_interruptible(evdev->wait,
+@@ -307,14 +470,12 @@ static ssize_t evdev_read(struct file *f
+       if (!evdev->exist)
+               return -ENODEV;
+-      while (client->head != client->tail && retval + evdev_event_size() <= count) {
+-
+-              struct input_event *event = (struct input_event *) client->buffer + client->tail;
++      while (retval + evdev_event_size() <= count &&
++             evdev_fetch_next_event(client, &event)) {
+-              if (evdev_event_to_user(buffer + retval, event))
++              if (evdev_event_to_user(buffer + retval, &event))
+                       return -EFAULT;
+-              client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
+               retval += evdev_event_size();
+       }
+@@ -409,8 +570,8 @@ static int str_to_user(const char *str, 
+       return copy_to_user(p, str, len) ? -EFAULT : len;
+ }
+-static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
+-                              void __user *p, int compat_mode)
++static long evdev_do_ioctl(struct file *file, unsigned int cmd,
++                         void __user *p, int compat_mode)
+ {
+       struct evdev_client *client = file->private_data;
+       struct evdev *evdev = client->evdev;
+@@ -421,186 +582,208 @@ static long evdev_ioctl_handler(struct f
+       int i, t, u, v;
+       int error;
+-      if (!evdev->exist)
+-              return -ENODEV;
+-
+       switch (cmd) {
+-              case EVIOCGVERSION:
+-                      return put_user(EV_VERSION, ip);
++      case EVIOCGVERSION:
++              return put_user(EV_VERSION, ip);
+-              case EVIOCGID:
+-                      if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
+-                              return -EFAULT;
+-                      return 0;
++      case EVIOCGID:
++              if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
++                      return -EFAULT;
++              return 0;
+-              case EVIOCGREP:
+-                      if (!test_bit(EV_REP, dev->evbit))
+-                              return -ENOSYS;
+-                      if (put_user(dev->rep[REP_DELAY], ip))
+-                              return -EFAULT;
+-                      if (put_user(dev->rep[REP_PERIOD], ip + 1))
+-                              return -EFAULT;
+-                      return 0;
++      case EVIOCGREP:
++              if (!test_bit(EV_REP, dev->evbit))
++                      return -ENOSYS;
++              if (put_user(dev->rep[REP_DELAY], ip))
++                      return -EFAULT;
++              if (put_user(dev->rep[REP_PERIOD], ip + 1))
++                      return -EFAULT;
++              return 0;
+-              case EVIOCSREP:
+-                      if (!test_bit(EV_REP, dev->evbit))
+-                              return -ENOSYS;
+-                      if (get_user(u, ip))
+-                              return -EFAULT;
+-                      if (get_user(v, ip + 1))
+-                              return -EFAULT;
++      case EVIOCSREP:
++              if (!test_bit(EV_REP, dev->evbit))
++                      return -ENOSYS;
++              if (get_user(u, ip))
++                      return -EFAULT;
++              if (get_user(v, ip + 1))
++                      return -EFAULT;
+-                      input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
+-                      input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
++              input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
++              input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
+-                      return 0;
++              return 0;
+-              case EVIOCGKEYCODE:
+-                      if (get_user(t, ip))
+-                              return -EFAULT;
++      case EVIOCGKEYCODE:
++              if (get_user(t, ip))
++                      return -EFAULT;
+-                      error = dev->getkeycode(dev, t, &v);
+-                      if (error)
+-                              return error;
++              error = dev->getkeycode(dev, t, &v);
++              if (error)
++                      return error;
+-                      if (put_user(v, ip + 1))
+-                              return -EFAULT;
++              if (put_user(v, ip + 1))
++                      return -EFAULT;
+-                      return 0;
++              return 0;
+-              case EVIOCSKEYCODE:
+-                      if (get_user(t, ip) || get_user(v, ip + 1))
+-                              return -EFAULT;
++      case EVIOCSKEYCODE:
++              if (get_user(t, ip) || get_user(v, ip + 1))
++                      return -EFAULT;
+-                      return dev->setkeycode(dev, t, v);
++              return dev->setkeycode(dev, t, v);
+-              case EVIOCSFF:
+-                      if (copy_from_user(&effect, p, sizeof(effect)))
+-                              return -EFAULT;
++      case EVIOCSFF:
++              if (copy_from_user(&effect, p, sizeof(effect)))
++                      return -EFAULT;
+-                      error = input_ff_upload(dev, &effect, file);
++              error = input_ff_upload(dev, &effect, file);
+-                      if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
+-                              return -EFAULT;
++              if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
++                      return -EFAULT;
+-                      return error;
++              return error;
+-              case EVIOCRMFF:
+-                      return input_ff_erase(dev, (int)(unsigned long) p, file);
++      case EVIOCRMFF:
++              return input_ff_erase(dev, (int)(unsigned long) p, file);
+-              case EVIOCGEFFECTS:
+-                      i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0;
+-                      if (put_user(i, ip))
+-                              return -EFAULT;
+-                      return 0;
++      case EVIOCGEFFECTS:
++              i = test_bit(EV_FF, dev->evbit) ?
++                              dev->ff->max_effects : 0;
++              if (put_user(i, ip))
++                      return -EFAULT;
++              return 0;
+-              case EVIOCGRAB:
+-                      if (p) {
+-                              if (evdev->grab)
+-                                      return -EBUSY;
+-                              if (input_grab_device(&evdev->handle))
+-                                      return -EBUSY;
+-                              evdev->grab = client;
+-                              return 0;
+-                      } else {
+-                              if (evdev->grab != client)
+-                                      return -EINVAL;
+-                              input_release_device(&evdev->handle);
+-                              evdev->grab = NULL;
+-                              return 0;
++      case EVIOCGRAB:
++              if (p)
++                      return evdev_grab(evdev, client);
++              else
++                      return evdev_ungrab(evdev, client);
++
++      default:
++
++              if (_IOC_TYPE(cmd) != 'E')
++                      return -EINVAL;
++
++              if (_IOC_DIR(cmd) == _IOC_READ) {
++
++                      if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) {
++
++                              unsigned long *bits;
++                              int len;
++
++                              switch (_IOC_NR(cmd) & EV_MAX) {
++
++                              case      0: bits = dev->evbit;  len = EV_MAX;  break;
++                              case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
++                              case EV_REL: bits = dev->relbit; len = REL_MAX; break;
++                              case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
++                              case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
++                              case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
++                              case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
++                              case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  break;
++                              case EV_SW:  bits = dev->swbit;  len = SW_MAX;  break;
++                              default: return -EINVAL;
++                      }
++                              return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
+                       }
+-              default:
++                      if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
++                              return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
++                                                  p, compat_mode);
+-                      if (_IOC_TYPE(cmd) != 'E')
+-                              return -EINVAL;
++                      if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
++                              return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
++                                                  p, compat_mode);
+-                      if (_IOC_DIR(cmd) == _IOC_READ) {
++                      if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
++                              return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
++                                                  p, compat_mode);
+-                              if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
++                      if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
++                              return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
++                                                  p, compat_mode);
+-                                      unsigned long *bits;
+-                                      int len;
++                      if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
++                              return str_to_user(dev->name, _IOC_SIZE(cmd), p);
+-                                      switch (_IOC_NR(cmd) & EV_MAX) {
+-                                              case      0: bits = dev->evbit;  len = EV_MAX;  break;
+-                                              case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
+-                                              case EV_REL: bits = dev->relbit; len = REL_MAX; break;
+-                                              case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
+-                                              case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
+-                                              case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
+-                                              case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
+-                                              case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  break;
+-                                              case EV_SW:  bits = dev->swbit;  len = SW_MAX;  break;
+-                                              default: return -EINVAL;
+-                                      }
+-                                      return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
+-                              }
++                      if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
++                              return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
+-                              if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
+-                                      return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
+-                                                          p, compat_mode);
++                      if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
++                              return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
+-                              if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
+-                                      return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
+-                                                          p, compat_mode);
++                      if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
+-                              if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
+-                                      return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
+-                                                          p, compat_mode);
++                              t = _IOC_NR(cmd) & ABS_MAX;
+-                              if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
+-                                      return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
+-                                                          p, compat_mode);
++                              abs.value = dev->abs[t];
++                              abs.minimum = dev->absmin[t];
++                              abs.maximum = dev->absmax[t];
++                              abs.fuzz = dev->absfuzz[t];
++                              abs.flat = dev->absflat[t];
+-                              if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
+-                                      return str_to_user(dev->name, _IOC_SIZE(cmd), p);
++                              if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
++                                      return -EFAULT;
+-                              if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
+-                                      return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
++                              return 0;
++                      }
+-                              if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
+-                                      return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
++              }
+-                              if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
++              if (_IOC_DIR(cmd) == _IOC_WRITE) {
+-                                      t = _IOC_NR(cmd) & ABS_MAX;
++                      if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
+-                                      abs.value = dev->abs[t];
+-                                      abs.minimum = dev->absmin[t];
+-                                      abs.maximum = dev->absmax[t];
+-                                      abs.fuzz = dev->absfuzz[t];
+-                                      abs.flat = dev->absflat[t];
++                              t = _IOC_NR(cmd) & ABS_MAX;
+-                                      if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
+-                                              return -EFAULT;
++                              if (copy_from_user(&abs, p,
++                                              sizeof(struct input_absinfo)))
++                                      return -EFAULT;
++
++                              /*
++                               * Take event lock to ensure that we are not
++                               * changing device parameters in the middle
++                               * of event.
++                               */
++                              spin_lock_irq(&dev->event_lock);
++
++                              dev->abs[t] = abs.value;
++                              dev->absmin[t] = abs.minimum;
++                              dev->absmax[t] = abs.maximum;
++                              dev->absfuzz[t] = abs.fuzz;
++                              dev->absflat[t] = abs.flat;
+-                                      return 0;
+-                              }
++                              spin_unlock_irq(&dev->event_lock);
++                              return 0;
+                       }
++              }
++      }
++      return -EINVAL;
++}
+-                      if (_IOC_DIR(cmd) == _IOC_WRITE) {
+-
+-                              if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
++static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
++                              void __user *p, int compat_mode)
++{
++      struct evdev_client *client = file->private_data;
++      struct evdev *evdev = client->evdev;
++      int retval;
+-                                      t = _IOC_NR(cmd) & ABS_MAX;
++      retval = mutex_lock_interruptible(&evdev->mutex);
++      if (retval)
++              return retval;
+-                                      if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
+-                                              return -EFAULT;
++      if (!evdev->exist) {
++              retval = -ENODEV;
++              goto out;
++      }
+-                                      dev->abs[t] = abs.value;
+-                                      dev->absmin[t] = abs.minimum;
+-                                      dev->absmax[t] = abs.maximum;
+-                                      dev->absfuzz[t] = abs.fuzz;
+-                                      dev->absflat[t] = abs.flat;
++      retval = evdev_do_ioctl(file, cmd, p, compat_mode);
+-                                      return 0;
+-                              }
+-                      }
+-      }
+-      return -EINVAL;
++ out:
++      mutex_unlock(&evdev->mutex);
++      return retval;
+ }
+ static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+@@ -609,27 +792,79 @@ static long evdev_ioctl(struct file *fil
+ }
+ #ifdef CONFIG_COMPAT
+-static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
++static long evdev_ioctl_compat(struct file *file,
++                              unsigned int cmd, unsigned long arg)
+ {
+       return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
+ }
+ #endif
+ static const struct file_operations evdev_fops = {
+-      .owner =        THIS_MODULE,
+-      .read =         evdev_read,
+-      .write =        evdev_write,
+-      .poll =         evdev_poll,
+-      .open =         evdev_open,
+-      .release =      evdev_release,
+-      .unlocked_ioctl = evdev_ioctl,
++      .owner          = THIS_MODULE,
++      .read           = evdev_read,
++      .write          = evdev_write,
++      .poll           = evdev_poll,
++      .open           = evdev_open,
++      .release        = evdev_release,
++      .unlocked_ioctl = evdev_ioctl,
+ #ifdef CONFIG_COMPAT
+-      .compat_ioctl = evdev_ioctl_compat,
++      .compat_ioctl   = evdev_ioctl_compat,
+ #endif
+-      .fasync =       evdev_fasync,
+-      .flush =        evdev_flush
++      .fasync         = evdev_fasync,
++      .flush          = evdev_flush
+ };
++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
++ * blocking reads will stay, however new ones will fail.
++ */
++static void evdev_mark_dead(struct evdev *evdev)
++{
++      mutex_lock(&evdev->mutex);
++      evdev->exist = 0;
++      mutex_unlock(&evdev->mutex);
++}
++
++static void evdev_cleanup(struct evdev *evdev)
++{
++      struct input_handle *handle = &evdev->handle;
++
++      evdev_mark_dead(evdev);
++      evdev_hangup(evdev);
++      evdev_remove_chrdev(evdev);
++
++      /* evdev is marked dead so no one else accesses evdev->open */
++      if (evdev->open) {
++              input_flush_device(handle, NULL);
++              input_close_device(handle);
++      }
++}
++
++/*
++ * Create new evdev device. Note that input core serializes calls
++ * to connect and disconnect so we don't need to lock evdev_table here.
++ */
+ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
+                        const struct input_device_id *id)
+ {
+@@ -637,7 +872,10 @@ static int evdev_connect(struct input_ha
+       int minor;
+       int error;
+-      for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
++      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;
+@@ -648,38 +886,44 @@ static int evdev_connect(struct input_ha
+               return -ENOMEM;
+       INIT_LIST_HEAD(&evdev->client_list);
++      spin_lock_init(&evdev->client_lock);
++      mutex_init(&evdev->mutex);
+       init_waitqueue_head(&evdev->wait);
++      snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
+       evdev->exist = 1;
+       evdev->minor = minor;
++
+       evdev->handle.dev = dev;
+       evdev->handle.name = evdev->name;
+       evdev->handle.handler = handler;
+       evdev->handle.private = evdev;
+-      snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
+-      snprintf(evdev->dev.bus_id, sizeof(evdev->dev.bus_id),
+-               "event%d", minor);
++      strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));
++      evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
+       evdev->dev.class = &input_class;
+       evdev->dev.parent = &dev->dev;
+-      evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
+       evdev->dev.release = evdev_free;
+       device_initialize(&evdev->dev);
+-      evdev_table[minor] = evdev;
+-
+-      error = device_add(&evdev->dev);
++      error = input_register_handle(&evdev->handle);
+       if (error)
+               goto err_free_evdev;
+-      error = input_register_handle(&evdev->handle);
++      error = evdev_install_chrdev(evdev);
++      if (error)
++              goto err_unregister_handle;
++
++      error = device_add(&evdev->dev);
+       if (error)
+-              goto err_delete_evdev;
++              goto err_cleanup_evdev;
+       return 0;
+- err_delete_evdev:
+-      device_del(&evdev->dev);
++ err_cleanup_evdev:
++      evdev_cleanup(evdev);
++ err_unregister_handle:
++      input_unregister_handle(&evdev->handle);
+  err_free_evdev:
+       put_device(&evdev->dev);
+       return error;
+@@ -688,21 +932,10 @@ static int evdev_connect(struct input_ha
+ static void evdev_disconnect(struct input_handle *handle)
+ {
+       struct evdev *evdev = handle->private;
+-      struct evdev_client *client;
+-      input_unregister_handle(handle);
+       device_del(&evdev->dev);
+-
+-      evdev->exist = 0;
+-
+-      if (evdev->open) {
+-              input_flush_device(handle, NULL);
+-              input_close_device(handle);
+-              list_for_each_entry(client, &evdev->client_list, node)
+-                      kill_fasync(&client->fasync, SIGIO, POLL_HUP);
+-              wake_up_interruptible(&evdev->wait);
+-      }
+-
++      evdev_cleanup(evdev);
++      input_unregister_handle(handle);
+       put_device(&evdev->dev);
+ }
+@@ -714,13 +947,13 @@ static const struct input_device_id evde
+ MODULE_DEVICE_TABLE(input, evdev_ids);
+ static struct input_handler evdev_handler = {
+-      .event =        evdev_event,
+-      .connect =      evdev_connect,
+-      .disconnect =   evdev_disconnect,
+-      .fops =         &evdev_fops,
+-      .minor =        EVDEV_MINOR_BASE,
+-      .name =         "evdev",
+-      .id_table =     evdev_ids,
++      .event          = evdev_event,
++      .connect        = evdev_connect,
++      .disconnect     = evdev_disconnect,
++      .fops           = &evdev_fops,
++      .minor          = EVDEV_MINOR_BASE,
++      .name           = "evdev",
++      .id_table       = evdev_ids,
+ };
+ static int __init evdev_init(void)
diff --git a/queue-2.6.23/input-fix-open-count-handling-in-input-interfaces.patch b/queue-2.6.23/input-fix-open-count-handling-in-input-interfaces.patch
new file mode 100644 (file)
index 0000000..ac5d705
--- /dev/null
@@ -0,0 +1,84 @@
+From 064450140f1eab959bd0eca0245f449993216074 Mon Sep 17 00:00:00 2001
+From: Oliver Neukum <oliver@neukum.org>
+Date: Fri, 12 Oct 2007 14:18:40 -0400
+Subject: [PATCH] Input: fix open count handling in input interfaces
+
+From: Oliver Neukum <oliver@neukum.org>
+
+patch 064450140f1eab959bd0eca0245f449993216074 in mainline.
+
+If input_open_device() fails we should not leave interfaces marked
+as opened.
+
+Signed-off-by: Oliver Neukum <oneukum@suse.de>
+Cc: Al Viro <viro@ZenIV.linux.org.uk>
+Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/input/evdev.c    |    5 ++++-
+ drivers/input/joydev.c   |    5 ++++-
+ drivers/input/mousedev.c |    5 ++++-
+ drivers/input/tsdev.c    |    5 ++++-
+ 4 files changed, 16 insertions(+), 4 deletions(-)
+
+--- a/drivers/input/evdev.c
++++ b/drivers/input/evdev.c
+@@ -188,8 +188,11 @@ static int evdev_open_device(struct evde
+       if (!evdev->exist)
+               retval = -ENODEV;
+-      else if (!evdev->open++)
++      else if (!evdev->open++) {
+               retval = input_open_device(&evdev->handle);
++              if (retval)
++                      evdev->open--;
++      }
+       mutex_unlock(&evdev->mutex);
+       return retval;
+--- a/drivers/input/joydev.c
++++ b/drivers/input/joydev.c
+@@ -202,8 +202,11 @@ static int joydev_open_device(struct joy
+       if (!joydev->exist)
+               retval = -ENODEV;
+-      else if (!joydev->open++)
++      else if (!joydev->open++) {
+               retval = input_open_device(&joydev->handle);
++              if (retval)
++                      joydev->open--;
++      }
+       mutex_unlock(&joydev->mutex);
+       return retval;
+--- a/drivers/input/mousedev.c
++++ b/drivers/input/mousedev.c
+@@ -430,8 +430,11 @@ static int mousedev_open_device(struct m
+               mixdev_open_devices();
+       else if (!mousedev->exist)
+               retval = -ENODEV;
+-      else if (!mousedev->open++)
++      else if (!mousedev->open++) {
+               retval = input_open_device(&mousedev->handle);
++              if (retval)
++                      mousedev->open--;
++      }
+       mutex_unlock(&mousedev->mutex);
+       return retval;
+--- a/drivers/input/tsdev.c
++++ b/drivers/input/tsdev.c
+@@ -185,8 +185,11 @@ static int tsdev_open_device(struct tsde
+       if (!tsdev->exist)
+               retval = -ENODEV;
+-      else if (!tsdev->open++)
++      else if (!tsdev->open++) {
+               retval = input_open_device(&tsdev->handle);
++              if (retval)
++                      tsdev->open--;
++      }
+       mutex_unlock(&tsdev->mutex);
+       return retval;
diff --git a/queue-2.6.23/input-implement-proper-locking-in-input-core.patch b/queue-2.6.23/input-implement-proper-locking-in-input-core.patch
new file mode 100644 (file)
index 0000000..df2afa0
--- /dev/null
@@ -0,0 +1,1133 @@
+From 8006479c9b75fb6594a7b746af3d7f1fbb68f18f Mon Sep 17 00:00:00 2001
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Date: Thu, 30 Aug 2007 00:22:11 -0400
+Subject: Input: implement proper locking in input core
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+patch 8006479c9b75fb6594a7b746af3d7f1fbb68f18f in mainline.
+
+Also add some kerneldoc documentation to input.h
+
+Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
+Cc: Al Viro <viro@ZenIV.linux.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/input/input.c |  666 ++++++++++++++++++++++++++++++++++++--------------
+ include/linux/input.h |  112 +++++++-
+ 2 files changed, 595 insertions(+), 183 deletions(-)
+
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -17,10 +17,10 @@
+ #include <linux/major.h>
+ #include <linux/proc_fs.h>
+ #include <linux/seq_file.h>
+-#include <linux/interrupt.h>
+ #include <linux/poll.h>
+ #include <linux/device.h>
+ #include <linux/mutex.h>
++#include <linux/rcupdate.h>
+ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+ MODULE_DESCRIPTION("Input core");
+@@ -31,167 +31,244 @@ MODULE_LICENSE("GPL");
+ static LIST_HEAD(input_dev_list);
+ static LIST_HEAD(input_handler_list);
++/*
++ * input_mutex protects access to both input_dev_list and input_handler_list.
++ * This also causes input_[un]register_device and input_[un]register_handler
++ * be mutually exclusive which simplifies locking in drivers implementing
++ * input handlers.
++ */
++static DEFINE_MUTEX(input_mutex);
++
+ static struct input_handler *input_table[8];
+-/**
+- * input_event() - report new input event
+- * @dev: device that generated the event
+- * @type: type of the event
+- * @code: event code
+- * @value: value of the event
+- *
+- * This function should be used by drivers implementing various input devices
+- * See also input_inject_event()
+- */
+-void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
++static inline int is_event_supported(unsigned int code,
++                                   unsigned long *bm, unsigned int max)
+ {
+-      struct input_handle *handle;
++      return code <= max && test_bit(code, bm);
++}
+-      if (type > EV_MAX || !test_bit(type, dev->evbit))
+-              return;
++static int input_defuzz_abs_event(int value, int old_val, int fuzz)
++{
++      if (fuzz) {
++              if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2)
++                      return old_val;
+-      add_input_randomness(type, code, value);
++              if (value > old_val - fuzz && value < old_val + fuzz)
++                      return (old_val * 3 + value) / 4;
+-      switch (type) {
++              if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2)
++                      return (old_val + value) / 2;
++      }
+-              case EV_SYN:
+-                      switch (code) {
+-                              case SYN_CONFIG:
+-                                      if (dev->event)
+-                                              dev->event(dev, type, code, value);
+-                                      break;
+-
+-                              case SYN_REPORT:
+-                                      if (dev->sync)
+-                                              return;
+-                                      dev->sync = 1;
+-                                      break;
+-                      }
+-                      break;
++      return value;
++}
+-              case EV_KEY:
++/*
++ * Pass event through all open handles. This function is called with
++ * dev->event_lock held and interrupts disabled. Because of that we
++ * do not need to use rcu_read_lock() here although we are using RCU
++ * to access handle list. Note that because of that write-side uses
++ * synchronize_sched() instead of synchronize_ru().
++ */
++static void input_pass_event(struct input_dev *dev,
++                           unsigned int type, unsigned int code, int value)
++{
++      struct input_handle *handle = rcu_dereference(dev->grab);
+-                      if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
+-                              return;
++      if (handle)
++              handle->handler->event(handle, type, code, value);
++      else
++              list_for_each_entry_rcu(handle, &dev->h_list, d_node)
++                      if (handle->open)
++                              handle->handler->event(handle,
++                                                      type, code, value);
++}
+-                      if (value == 2)
+-                              break;
++/*
++ * Generate software autorepeat event. Note that we take
++ * dev->event_lock here to avoid racing with input_event
++ * which may cause keys get "stuck".
++ */
++static void input_repeat_key(unsigned long data)
++{
++      struct input_dev *dev = (void *) data;
++      unsigned long flags;
+-                      change_bit(code, dev->key);
++      spin_lock_irqsave(&dev->event_lock, flags);
+-                      if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
+-                              dev->repeat_key = code;
+-                              mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
+-                      }
++      if (test_bit(dev->repeat_key, dev->key) &&
++          is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
+-                      break;
++              input_pass_event(dev, EV_KEY, dev->repeat_key, 2);
+-              case EV_SW:
++              if (dev->sync) {
++                      /*
++                       * Only send SYN_REPORT if we are not in a middle
++                       * of driver parsing a new hardware packet.
++                       * Otherwise assume that the driver will send
++                       * SYN_REPORT once it's done.
++                       */
++                      input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
++              }
+-                      if (code > SW_MAX || !test_bit(code, dev->swbit) || !!test_bit(code, dev->sw) == value)
+-                              return;
++              if (dev->rep[REP_PERIOD])
++                      mod_timer(&dev->timer, jiffies +
++                                      msecs_to_jiffies(dev->rep[REP_PERIOD]));
++      }
+-                      change_bit(code, dev->sw);
++      spin_unlock_irqrestore(&dev->event_lock, flags);
++}
+-                      break;
++static void input_start_autorepeat(struct input_dev *dev, int code)
++{
++      if (test_bit(EV_REP, dev->evbit) &&
++          dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
++          dev->timer.data) {
++              dev->repeat_key = code;
++              mod_timer(&dev->timer,
++                        jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
++      }
++}
+-              case EV_ABS:
++#define INPUT_IGNORE_EVENT    0
++#define INPUT_PASS_TO_HANDLERS        1
++#define INPUT_PASS_TO_DEVICE  2
++#define INPUT_PASS_TO_ALL     (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
+-                      if (code > ABS_MAX || !test_bit(code, dev->absbit))
+-                              return;
++static void input_handle_event(struct input_dev *dev,
++                             unsigned int type, unsigned int code, int value)
++{
++      int disposition = INPUT_IGNORE_EVENT;
+-                      if (dev->absfuzz[code]) {
+-                              if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) &&
+-                                  (value < dev->abs[code] + (dev->absfuzz[code] >> 1)))
+-                                      return;
+-
+-                              if ((value > dev->abs[code] - dev->absfuzz[code]) &&
+-                                  (value < dev->abs[code] + dev->absfuzz[code]))
+-                                      value = (dev->abs[code] * 3 + value) >> 2;
+-
+-                              if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) &&
+-                                  (value < dev->abs[code] + (dev->absfuzz[code] << 1)))
+-                                      value = (dev->abs[code] + value) >> 1;
+-                      }
++      switch (type) {
+-                      if (dev->abs[code] == value)
+-                              return;
++      case EV_SYN:
++              switch (code) {
++              case SYN_CONFIG:
++                      disposition = INPUT_PASS_TO_ALL;
++                      break;
+-                      dev->abs[code] = value;
++              case SYN_REPORT:
++                      if (!dev->sync) {
++                              dev->sync = 1;
++                              disposition = INPUT_PASS_TO_HANDLERS;
++                      }
+                       break;
++              }
++              break;
+-              case EV_REL:
++      case EV_KEY:
++              if (is_event_supported(code, dev->keybit, KEY_MAX) &&
++                  !!test_bit(code, dev->key) != value) {
+-                      if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0))
+-                              return;
++                      if (value != 2) {
++                              __change_bit(code, dev->key);
++                              if (value)
++                                      input_start_autorepeat(dev, code);
++                      }
+-                      break;
++                      disposition = INPUT_PASS_TO_HANDLERS;
++              }
++              break;
+-              case EV_MSC:
++      case EV_SW:
++              if (is_event_supported(code, dev->swbit, SW_MAX) &&
++                  !!test_bit(code, dev->sw) != value) {
+-                      if (code > MSC_MAX || !test_bit(code, dev->mscbit))
+-                              return;
++                      __change_bit(code, dev->sw);
++                      disposition = INPUT_PASS_TO_HANDLERS;
++              }
++              break;
++
++      case EV_ABS:
++              if (is_event_supported(code, dev->absbit, ABS_MAX)) {
+-                      if (dev->event)
+-                              dev->event(dev, type, code, value);
++                      value = input_defuzz_abs_event(value,
++                                      dev->abs[code], dev->absfuzz[code]);
+-                      break;
++                      if (dev->abs[code] != value) {
++                              dev->abs[code] = value;
++                              disposition = INPUT_PASS_TO_HANDLERS;
++                      }
++              }
++              break;
+-              case EV_LED:
++      case EV_REL:
++              if (is_event_supported(code, dev->relbit, REL_MAX) && value)
++                      disposition = INPUT_PASS_TO_HANDLERS;
+-                      if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
+-                              return;
++              break;
+-                      change_bit(code, dev->led);
++      case EV_MSC:
++              if (is_event_supported(code, dev->mscbit, MSC_MAX))
++                      disposition = INPUT_PASS_TO_ALL;
+-                      if (dev->event)
+-                              dev->event(dev, type, code, value);
++              break;
+-                      break;
++      case EV_LED:
++              if (is_event_supported(code, dev->ledbit, LED_MAX) &&
++                  !!test_bit(code, dev->led) != value) {
+-              case EV_SND:
++                      __change_bit(code, dev->led);
++                      disposition = INPUT_PASS_TO_ALL;
++              }
++              break;
+-                      if (code > SND_MAX || !test_bit(code, dev->sndbit))
+-                              return;
++      case EV_SND:
++              if (is_event_supported(code, dev->sndbit, SND_MAX)) {
+                       if (!!test_bit(code, dev->snd) != !!value)
+-                              change_bit(code, dev->snd);
++                              __change_bit(code, dev->snd);
++                      disposition = INPUT_PASS_TO_ALL;
++              }
++              break;
+-                      if (dev->event)
+-                              dev->event(dev, type, code, value);
++      case EV_REP:
++              if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
++                      dev->rep[code] = value;
++                      disposition = INPUT_PASS_TO_ALL;
++              }
++              break;
+-                      break;
++      case EV_FF:
++              if (value >= 0)
++                      disposition = INPUT_PASS_TO_ALL;
++              break;
++      }
+-              case EV_REP:
++      if (type != EV_SYN)
++              dev->sync = 0;
+-                      if (code > REP_MAX || value < 0 || dev->rep[code] == value)
+-                              return;
++      if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
++              dev->event(dev, type, code, value);
+-                      dev->rep[code] = value;
+-                      if (dev->event)
+-                              dev->event(dev, type, code, value);
++      if (disposition & INPUT_PASS_TO_HANDLERS)
++              input_pass_event(dev, type, code, value);
++}
+-                      break;
++/**
++ * input_event() - report new input event
++ * @dev: device that generated the event
++ * @type: type of the event
++ * @code: event code
++ * @value: value of the event
++ *
++ * This function should be used by drivers implementing various input
++ * devices. See also input_inject_event().
++ */
+-              case EV_FF:
++void input_event(struct input_dev *dev,
++               unsigned int type, unsigned int code, int value)
++{
++      unsigned long flags;
+-                      if (value < 0)
+-                              return;
++      if (is_event_supported(type, dev->evbit, EV_MAX)) {
+-                      if (dev->event)
+-                              dev->event(dev, type, code, value);
+-                      break;
++              spin_lock_irqsave(&dev->event_lock, flags);
++              add_input_randomness(type, code, value);
++              input_handle_event(dev, type, code, value);
++              spin_unlock_irqrestore(&dev->event_lock, flags);
+       }
+-
+-      if (type != EV_SYN)
+-              dev->sync = 0;
+-
+-      if (dev->grab)
+-              dev->grab->handler->event(dev->grab, type, code, value);
+-      else
+-              list_for_each_entry(handle, &dev->h_list, d_node)
+-                      if (handle->open)
+-                              handle->handler->event(handle, type, code, value);
+ }
+ EXPORT_SYMBOL(input_event);
+@@ -202,102 +279,230 @@ EXPORT_SYMBOL(input_event);
+  * @code: event code
+  * @value: value of the event
+  *
+- * Similar to input_event() but will ignore event if device is "grabbed" and handle
+- * injecting event is not the one that owns the device.
++ * Similar to input_event() but will ignore event if device is
++ * "grabbed" and handle injecting event is not the one that owns
++ * the device.
+  */
+-void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
++void input_inject_event(struct input_handle *handle,
++                      unsigned int type, unsigned int code, int value)
+ {
+-      if (!handle->dev->grab || handle->dev->grab == handle)
+-              input_event(handle->dev, type, code, value);
+-}
+-EXPORT_SYMBOL(input_inject_event);
+-
+-static void input_repeat_key(unsigned long data)
+-{
+-      struct input_dev *dev = (void *) data;
++      struct input_dev *dev = handle->dev;
++      struct input_handle *grab;
++      unsigned long flags;
+-      if (!test_bit(dev->repeat_key, dev->key))
+-              return;
++      if (is_event_supported(type, dev->evbit, EV_MAX)) {
++              spin_lock_irqsave(&dev->event_lock, flags);
+-      input_event(dev, EV_KEY, dev->repeat_key, 2);
+-      input_sync(dev);
++              grab = rcu_dereference(dev->grab);
++              if (!grab || grab == handle)
++                      input_handle_event(dev, type, code, value);
+-      if (dev->rep[REP_PERIOD])
+-              mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD]));
++              spin_unlock_irqrestore(&dev->event_lock, flags);
++      }
+ }
++EXPORT_SYMBOL(input_inject_event);
++/**
++ * input_grab_device - grabs device for exclusive use
++ * @handle: input handle that wants to own the device
++ *
++ * When a device is grabbed by an input handle all events generated by
++ * the device are delivered only to this handle. Also events injected
++ * by other input handles are ignored while device is grabbed.
++ */
+ int input_grab_device(struct input_handle *handle)
+ {
+-      if (handle->dev->grab)
+-              return -EBUSY;
++      struct input_dev *dev = handle->dev;
++      int retval;
+-      handle->dev->grab = handle;
+-      return 0;
++      retval = mutex_lock_interruptible(&dev->mutex);
++      if (retval)
++              return retval;
++
++      if (dev->grab) {
++              retval = -EBUSY;
++              goto out;
++      }
++
++      rcu_assign_pointer(dev->grab, handle);
++      /*
++       * Not using synchronize_rcu() because read-side is protected
++       * by a spinlock with interrupts off instead of rcu_read_lock().
++       */
++      synchronize_sched();
++
++ out:
++      mutex_unlock(&dev->mutex);
++      return retval;
+ }
+ EXPORT_SYMBOL(input_grab_device);
+-void input_release_device(struct input_handle *handle)
++static void __input_release_device(struct input_handle *handle)
+ {
+       struct input_dev *dev = handle->dev;
+       if (dev->grab == handle) {
+-              dev->grab = NULL;
++              rcu_assign_pointer(dev->grab, NULL);
++              /* Make sure input_pass_event() notices that grab is gone */
++              synchronize_sched();
+               list_for_each_entry(handle, &dev->h_list, d_node)
+-                      if (handle->handler->start)
++                      if (handle->open && handle->handler->start)
+                               handle->handler->start(handle);
+       }
+ }
++
++/**
++ * input_release_device - release previously grabbed device
++ * @handle: input handle that owns the device
++ *
++ * Releases previously grabbed device so that other input handles can
++ * start receiving input events. Upon release all handlers attached
++ * to the device have their start() method called so they have a change
++ * to synchronize device state with the rest of the system.
++ */
++void input_release_device(struct input_handle *handle)
++{
++      struct input_dev *dev = handle->dev;
++
++      mutex_lock(&dev->mutex);
++      __input_release_device(handle);
++      mutex_unlock(&dev->mutex);
++}
+ EXPORT_SYMBOL(input_release_device);
++/**
++ * input_open_device - open input device
++ * @handle: handle through which device is being accessed
++ *
++ * This function should be called by input handlers when they
++ * want to start receive events from given input device.
++ */
+ int input_open_device(struct input_handle *handle)
+ {
+       struct input_dev *dev = handle->dev;
+-      int err;
++      int retval;
+-      err = mutex_lock_interruptible(&dev->mutex);
+-      if (err)
+-              return err;
++      retval = mutex_lock_interruptible(&dev->mutex);
++      if (retval)
++              return retval;
++
++      if (dev->going_away) {
++              retval = -ENODEV;
++              goto out;
++      }
+       handle->open++;
+       if (!dev->users++ && dev->open)
+-              err = dev->open(dev);
++              retval = dev->open(dev);
+-      if (err)
+-              handle->open--;
++      if (retval) {
++              dev->users--;
++              if (!--handle->open) {
++                      /*
++                       * Make sure we are not delivering any more events
++                       * through this handle
++                       */
++                      synchronize_sched();
++              }
++      }
++ out:
+       mutex_unlock(&dev->mutex);
+-
+-      return err;
++      return retval;
+ }
+ EXPORT_SYMBOL(input_open_device);
+-int input_flush_device(struct input_handle* handle, struct file* file)
++int input_flush_device(struct input_handle *handle, struct file *file)
+ {
+-      if (handle->dev->flush)
+-              return handle->dev->flush(handle->dev, file);
++      struct input_dev *dev = handle->dev;
++      int retval;
+-      return 0;
++      retval = mutex_lock_interruptible(&dev->mutex);
++      if (retval)
++              return retval;
++
++      if (dev->flush)
++              retval = dev->flush(dev, file);
++
++      mutex_unlock(&dev->mutex);
++      return retval;
+ }
+ EXPORT_SYMBOL(input_flush_device);
++/**
++ * input_close_device - close input device
++ * @handle: handle through which device is being accessed
++ *
++ * This function should be called by input handlers when they
++ * want to stop receive events from given input device.
++ */
+ void input_close_device(struct input_handle *handle)
+ {
+       struct input_dev *dev = handle->dev;
+-      input_release_device(handle);
+-
+       mutex_lock(&dev->mutex);
++      __input_release_device(handle);
++
+       if (!--dev->users && dev->close)
+               dev->close(dev);
+-      handle->open--;
++
++      if (!--handle->open) {
++              /*
++               * synchronize_sched() makes sure that input_pass_event()
++               * completed and that no more input events are delivered
++               * through this handle
++               */
++              synchronize_sched();
++      }
+       mutex_unlock(&dev->mutex);
+ }
+ EXPORT_SYMBOL(input_close_device);
++/*
++ * Prepare device for unregistering
++ */
++static void input_disconnect_device(struct input_dev *dev)
++{
++      struct input_handle *handle;
++      int code;
++
++      /*
++       * Mark device as going away. Note that we take dev->mutex here
++       * not to protect access to dev->going_away but rather to ensure
++       * that there are no threads in the middle of input_open_device()
++       */
++      mutex_lock(&dev->mutex);
++      dev->going_away = 1;
++      mutex_unlock(&dev->mutex);
++
++      spin_lock_irq(&dev->event_lock);
++
++      /*
++       * Simulate keyup events for all pressed keys so that handlers
++       * are not left with "stuck" keys. The driver may continue
++       * generate events even after we done here but they will not
++       * reach any handlers.
++       */
++      if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
++              for (code = 0; code <= KEY_MAX; code++) {
++                      if (is_event_supported(code, dev->keybit, KEY_MAX) &&
++                          test_bit(code, dev->key)) {
++                              input_pass_event(dev, EV_KEY, code, 0);
++                      }
++              }
++              input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
++      }
++
++      list_for_each_entry(handle, &dev->h_list, d_node)
++              handle->open = 0;
++
++      spin_unlock_irq(&dev->event_lock);
++}
++
+ static int input_fetch_keycode(struct input_dev *dev, int scancode)
+ {
+       switch (dev->keycodesize) {
+@@ -473,7 +678,8 @@ static unsigned int input_proc_devices_p
+ static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos)
+ {
+-      /* acquire lock here ... Yes, we do need locking, I knowi, I know... */
++      if (mutex_lock_interruptible(&input_mutex))
++              return NULL;
+       return seq_list_start(&input_dev_list, *pos);
+ }
+@@ -485,7 +691,7 @@ static void *input_devices_seq_next(stru
+ static void input_devices_seq_stop(struct seq_file *seq, void *v)
+ {
+-      /* release lock here */
++      mutex_unlock(&input_mutex);
+ }
+ static void input_seq_print_bitmap(struct seq_file *seq, const char *name,
+@@ -569,7 +775,9 @@ static const struct file_operations inpu
+ static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
+ {
+-      /* acquire lock here ... Yes, we do need locking, I knowi, I know... */
++      if (mutex_lock_interruptible(&input_mutex))
++              return NULL;
++
+       seq->private = (void *)(unsigned long)*pos;
+       return seq_list_start(&input_handler_list, *pos);
+ }
+@@ -582,7 +790,7 @@ static void *input_handlers_seq_next(str
+ static void input_handlers_seq_stop(struct seq_file *seq, void *v)
+ {
+-      /* release lock here */
++      mutex_unlock(&input_mutex);
+ }
+ static int input_handlers_seq_show(struct seq_file *seq, void *v)
+@@ -1005,6 +1213,7 @@ struct input_dev *input_allocate_device(
+               dev->dev.class = &input_class;
+               device_initialize(&dev->dev);
+               mutex_init(&dev->mutex);
++              spin_lock_init(&dev->event_lock);
+               INIT_LIST_HEAD(&dev->h_list);
+               INIT_LIST_HEAD(&dev->node);
+@@ -1022,7 +1231,7 @@ EXPORT_SYMBOL(input_allocate_device);
+  * This function should only be used if input_register_device()
+  * was not called yet or if it failed. Once device was registered
+  * use input_unregister_device() and memory will be freed once last
+- * refrence to the device is dropped.
++ * reference to the device is dropped.
+  *
+  * Device should be allocated by input_allocate_device().
+  *
+@@ -1092,6 +1301,18 @@ void input_set_capability(struct input_d
+ }
+ EXPORT_SYMBOL(input_set_capability);
++/**
++ * input_register_device - register device with input core
++ * @dev: device to be registered
++ *
++ * This function registers device with input core. The device must be
++ * allocated with input_allocate_device() and all it's capabilities
++ * set up before registering.
++ * If function fails the device must be freed with input_free_device().
++ * Once device has been successfully registered it can be unregistered
++ * with input_unregister_device(); input_free_device() should not be
++ * called in this case.
++ */
+ int input_register_device(struct input_dev *dev)
+ {
+       static atomic_t input_no = ATOMIC_INIT(0);
+@@ -1099,7 +1320,7 @@ int input_register_device(struct input_d
+       const char *path;
+       int error;
+-      set_bit(EV_SYN, dev->evbit);
++      __set_bit(EV_SYN, dev->evbit);
+       /*
+        * If delay and period are pre-set by the driver, then autorepeating
+@@ -1120,8 +1341,6 @@ int input_register_device(struct input_d
+       if (!dev->setkeycode)
+               dev->setkeycode = input_default_setkeycode;
+-      list_add_tail(&dev->node, &input_dev_list);
+-
+       snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
+                "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
+@@ -1137,49 +1356,79 @@ int input_register_device(struct input_d
+               dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
+       kfree(path);
++      error = mutex_lock_interruptible(&input_mutex);
++      if (error) {
++              device_del(&dev->dev);
++              return error;
++      }
++
++      list_add_tail(&dev->node, &input_dev_list);
++
+       list_for_each_entry(handler, &input_handler_list, node)
+               input_attach_handler(dev, handler);
+       input_wakeup_procfs_readers();
++      mutex_unlock(&input_mutex);
++
+       return 0;
+ }
+ EXPORT_SYMBOL(input_register_device);
++/**
++ * input_unregister_device - unregister previously registered device
++ * @dev: device to be unregistered
++ *
++ * This function unregisters an input device. Once device is unregistered
++ * the caller should not try to access it as it may get freed at any moment.
++ */
+ void input_unregister_device(struct input_dev *dev)
+ {
+       struct input_handle *handle, *next;
+-      int code;
+-      for (code = 0; code <= KEY_MAX; code++)
+-              if (test_bit(code, dev->key))
+-                      input_report_key(dev, code, 0);
+-      input_sync(dev);
++      input_disconnect_device(dev);
+-      del_timer_sync(&dev->timer);
++      mutex_lock(&input_mutex);
+       list_for_each_entry_safe(handle, next, &dev->h_list, d_node)
+               handle->handler->disconnect(handle);
+       WARN_ON(!list_empty(&dev->h_list));
++      del_timer_sync(&dev->timer);
+       list_del_init(&dev->node);
+-      device_unregister(&dev->dev);
+-
+       input_wakeup_procfs_readers();
++
++      mutex_unlock(&input_mutex);
++
++      device_unregister(&dev->dev);
+ }
+ EXPORT_SYMBOL(input_unregister_device);
++/**
++ * input_register_handler - register a new input handler
++ * @handler: handler to be registered
++ *
++ * This function registers a new input handler (interface) for input
++ * devices in the system and attaches it to all input devices that
++ * are compatible with the handler.
++ */
+ int input_register_handler(struct input_handler *handler)
+ {
+       struct input_dev *dev;
++      int retval;
++
++      retval = mutex_lock_interruptible(&input_mutex);
++      if (retval)
++              return retval;
+       INIT_LIST_HEAD(&handler->h_list);
+       if (handler->fops != NULL) {
+-              if (input_table[handler->minor >> 5])
+-                      return -EBUSY;
+-
++              if (input_table[handler->minor >> 5]) {
++                      retval = -EBUSY;
++                      goto out;
++              }
+               input_table[handler->minor >> 5] = handler;
+       }
+@@ -1189,14 +1438,26 @@ int input_register_handler(struct input_
+               input_attach_handler(dev, handler);
+       input_wakeup_procfs_readers();
+-      return 0;
++
++ out:
++      mutex_unlock(&input_mutex);
++      return retval;
+ }
+ EXPORT_SYMBOL(input_register_handler);
++/**
++ * input_unregister_handler - unregisters an input handler
++ * @handler: handler to be unregistered
++ *
++ * This function disconnects a handler from its input devices and
++ * removes it from lists of known handlers.
++ */
+ void input_unregister_handler(struct input_handler *handler)
+ {
+       struct input_handle *handle, *next;
++      mutex_lock(&input_mutex);
++
+       list_for_each_entry_safe(handle, next, &handler->h_list, h_node)
+               handler->disconnect(handle);
+       WARN_ON(!list_empty(&handler->h_list));
+@@ -1207,14 +1468,50 @@ void input_unregister_handler(struct inp
+               input_table[handler->minor >> 5] = NULL;
+       input_wakeup_procfs_readers();
++
++      mutex_unlock(&input_mutex);
+ }
+ EXPORT_SYMBOL(input_unregister_handler);
++/**
++ * input_register_handle - register a new input handle
++ * @handle: handle to register
++ *
++ * This function puts a new input handle onto device's
++ * and handler's lists so that events can flow through
++ * it once it is opened using input_open_device().
++ *
++ * This function is supposed to be called from handler's
++ * connect() method.
++ */
+ int input_register_handle(struct input_handle *handle)
+ {
+       struct input_handler *handler = handle->handler;
++      struct input_dev *dev = handle->dev;
++      int error;
++
++      /*
++       * We take dev->mutex here to prevent race with
++       * input_release_device().
++       */
++      error = mutex_lock_interruptible(&dev->mutex);
++      if (error)
++              return error;
++      list_add_tail_rcu(&handle->d_node, &dev->h_list);
++      mutex_unlock(&dev->mutex);
++      /*
++       * We don't use synchronize_rcu() here because we rely
++       * on dev->event_lock to protect read-side critical
++       * section in input_pass_event().
++       */
++      synchronize_sched();
+-      list_add_tail(&handle->d_node, &handle->dev->h_list);
++      /*
++       * Since we are supposed to be called from ->connect()
++       * which is mutually exclusive with ->disconnect()
++       * we can't be racing with input_unregister_handle()
++       * and so separate lock is not needed here.
++       */
+       list_add_tail(&handle->h_node, &handler->h_list);
+       if (handler->start)
+@@ -1224,10 +1521,29 @@ int input_register_handle(struct input_h
+ }
+ EXPORT_SYMBOL(input_register_handle);
++/**
++ * input_unregister_handle - unregister an input handle
++ * @handle: handle to unregister
++ *
++ * This function removes input handle from device's
++ * and handler's lists.
++ *
++ * This function is supposed to be called from handler's
++ * disconnect() method.
++ */
+ void input_unregister_handle(struct input_handle *handle)
+ {
++      struct input_dev *dev = handle->dev;
++
+       list_del_init(&handle->h_node);
+-      list_del_init(&handle->d_node);
++
++      /*
++       * Take dev->mutex to prevent race with input_release_device().
++       */
++      mutex_lock(&dev->mutex);
++      list_del_rcu(&handle->d_node);
++      mutex_unlock(&dev->mutex);
++      synchronize_sched();
+ }
+ EXPORT_SYMBOL(input_unregister_handle);
+--- a/include/linux/input.h
++++ b/include/linux/input.h
+@@ -853,7 +853,7 @@ struct ff_rumble_effect {
+  *    defining effect parameters
+  *
+  * This structure is sent through ioctl from the application to the driver.
+- * To create a new effect aplication should set its @id to -1; the kernel
++ * To create a new effect application should set its @id to -1; the kernel
+  * will return assigned @id which can later be used to update or delete
+  * this effect.
+  *
+@@ -933,9 +933,82 @@ struct ff_effect {
+ #define BIT(x)        (1UL<<((x)%BITS_PER_LONG))
+ #define LONG(x) ((x)/BITS_PER_LONG)
++/**
++ * struct input_dev - represents an input device
++ * @name: name of the device
++ * @phys: physical path to the device in the system hierarchy
++ * @uniq: unique identification code for the device (if device has it)
++ * @id: id of the device (struct input_id)
++ * @evbit: bitmap of types of events supported by the device (EV_KEY,
++ *    EV_REL, etc.)
++ * @keybit: bitmap of keys/buttons this device has
++ * @relbit: bitmap of relative axes for the device
++ * @absbit: bitmap of absolute axes for the device
++ * @mscbit: bitmap of miscellaneous events supported by the device
++ * @ledbit: bitmap of leds present on the device
++ * @sndbit: bitmap of sound effects supported by the device
++ * @ffbit: bitmap of force feedback effects supported by the device
++ * @swbit: bitmap of switches present on the device
++ * @keycodemax: size of keycode table
++ * @keycodesize: size of elements in keycode table
++ * @keycode: map of scancodes to keycodes for this device
++ * @setkeycode: optional method to alter current keymap, used to implement
++ *    sparse keymaps. If not supplied default mechanism will be used
++ * @getkeycode: optional method to retrieve current keymap. If not supplied
++ *    default mechanism will be used
++ * @ff: force feedback structure associated with the device if device
++ *    supports force feedback effects
++ * @repeat_key: stores key code of the last key pressed; used to implement
++ *    software autorepeat
++ * @timer: timer for software autorepeat
++ * @sync: set to 1 when there were no new events since last EV_SYNC
++ * @abs: current values for reports from absolute axes
++ * @rep: current values for autorepeat parameters (delay, rate)
++ * @key: reflects current state of device's keys/buttons
++ * @led: reflects current state of device's LEDs
++ * @snd: reflects current state of sound effects
++ * @sw: reflects current state of device's switches
++ * @absmax: maximum values for events coming from absolute axes
++ * @absmin: minimum values for events coming from absolute axes
++ * @absfuzz: describes noisiness for axes
++ * @absflat: size of the center flat position (used by joydev)
++ * @open: this method is called when the very first user calls
++ *    input_open_device(). The driver must prepare the device
++ *    to start generating events (start polling thread,
++ *    request an IRQ, submit URB, etc.)
++ * @close: this method is called when the very last user calls
++ *    input_close_device().
++ * @flush: purges the device. Most commonly used to get rid of force
++ *    feedback effects loaded into the device when disconnecting
++ *    from it
++ * @event: event handler for events sent _to_ the device, like EV_LED
++ *    or EV_SND. The device is expected to carry out the requested
++ *    action (turn on a LED, play sound, etc.) The call is protected
++ *    by @event_lock and must not sleep
++ * @grab: input handle that currently has the device grabbed (via
++ *    EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
++ *    recipient for all input events coming from the device
++ * @event_lock: this spinlock is is taken when input core receives
++ *    and processes a new event for the device (in input_event()).
++ *    Code that accesses and/or modifies parameters of a device
++ *    (such as keymap or absmin, absmax, absfuzz, etc.) after device
++ *    has been registered with input core must take this lock.
++ * @mutex: serializes calls to open(), close() and flush() methods
++ * @users: stores number of users (input handlers) that opened this
++ *    device. It is used by input_open_device() and input_close_device()
++ *    to make sure that dev->open() is only called when the first
++ *    user opens device and dev->close() is called when the very
++ *    last user closes the device
++ * @going_away: marks devices that are in a middle of unregistering and
++ *    causes input_open_device*() fail with -ENODEV.
++ * @dev: driver model's view of this device
++ * @h_list: list of input handles associated with the device. When
++ *    accessing the list dev->mutex must be held
++ * @node: used to place the device onto input_dev_list
++ */
+ struct input_dev {
+-      void *private;
++      void *private;  /* do not use */
+       const char *name;
+       const char *phys;
+@@ -963,8 +1036,6 @@ struct input_dev {
+       unsigned int repeat_key;
+       struct timer_list timer;
+-      int state;
+-
+       int sync;
+       int abs[ABS_MAX + 1];
+@@ -987,8 +1058,11 @@ struct input_dev {
+       struct input_handle *grab;
+-      struct mutex mutex;     /* serializes open and close operations */
++      spinlock_t event_lock;
++      struct mutex mutex;
++
+       unsigned int users;
++      int going_away;
+       struct device dev;
+       union {                 /* temporarily so while we switching to struct device */
+@@ -1054,7 +1128,9 @@ struct input_handle;
+ /**
+  * struct input_handler - implements one of interfaces for input devices
+  * @private: driver-specific data
+- * @event: event handler
++ * @event: event handler. This method is being called by input core with
++ *    interrupts disabled and dev->event_lock spinlock held and so
++ *    it may not sleep
+  * @connect: called when attaching a handler to an input device
+  * @disconnect: disconnects a handler from input device
+  * @start: starts handler for given handle. This function is called by
+@@ -1066,10 +1142,18 @@ struct input_handle;
+  * @name: name of the handler, to be shown in /proc/bus/input/handlers
+  * @id_table: pointer to a table of input_device_ids this driver can
+  *    handle
+- * @blacklist: prointer to a table of input_device_ids this driver should
++ * @blacklist: pointer to a table of input_device_ids this driver should
+  *    ignore even if they match @id_table
+  * @h_list: list of input handles associated with the handler
+  * @node: for placing the driver onto input_handler_list
++ *
++ * Input handlers attach to input devices and create input handles. There
++ * are likely several handlers attached to any given input device at the
++ * same time. All of them will get their copy of input event generated by
++ * the device.
++ *
++ * Note that input core serializes calls to connect() and disconnect()
++ * methods.
+  */
+ struct input_handler {
+@@ -1091,6 +1175,18 @@ struct input_handler {
+       struct list_head        node;
+ };
++/**
++ * struct input_handle - links input device with an input handler
++ * @private: handler-specific data
++ * @open: counter showing whether the handle is 'open', i.e. should deliver
++ *    events from its device
++ * @name: name given to the handle by handler that created it
++ * @dev: input device the handle is attached to
++ * @handler: handler that works with the device through this handle
++ * @d_node: used to put the handle on device's list of attached handles
++ * @h_node: used to put the handle on handler's list of handles from which
++ *    it gets events
++ */
+ struct input_handle {
+       void *private;
+@@ -1213,7 +1309,7 @@ extern struct class input_class;
+  * @max_effects: maximum number of effects supported by device
+  * @effects: pointer to an array of effects currently loaded into device
+  * @effect_owners: array of effect owners; when file handle owning
+- *    an effect gets closed the effcet is automatically erased
++ *    an effect gets closed the effect is automatically erased
+  *
+  * Every force-feedback device must implement upload() and playback()
+  * methods; erase() is optional. set_gain() and set_autocenter() need
diff --git a/queue-2.6.23/input-joydev-implement-proper-locking.patch b/queue-2.6.23/input-joydev-implement-proper-locking.patch
new file mode 100644 (file)
index 0000000..13bbea0
--- /dev/null
@@ -0,0 +1,1042 @@
+From b126207ccdfe492fbc339c18d4898b1b5353fc6b Mon Sep 17 00:00:00 2001
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Date: Thu, 30 Aug 2007 00:22:32 -0400
+Subject: [PATCH] Input: joydev - implement proper locking
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+patch b126207ccdfe492fbc339c18d4898b1b5353fc6b in mainline.
+
+Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
+Cc: Al Viro <viro@ZenIV.linux.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/input/joydev.c |  745 ++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 493 insertions(+), 252 deletions(-)
+
+--- a/drivers/input/joydev.c
++++ b/drivers/input/joydev.c
+@@ -43,6 +43,8 @@ struct joydev {
+       struct input_handle handle;
+       wait_queue_head_t wait;
+       struct list_head client_list;
++      spinlock_t client_lock; /* protects client_list */
++      struct mutex mutex;
+       struct device dev;
+       struct js_corr corr[ABS_MAX + 1];
+@@ -61,31 +63,61 @@ struct joydev_client {
+       int head;
+       int tail;
+       int startup;
++      spinlock_t buffer_lock; /* protects access to buffer, head and tail */
+       struct fasync_struct *fasync;
+       struct joydev *joydev;
+       struct list_head node;
+ };
+ static struct joydev *joydev_table[JOYDEV_MINORS];
++static DEFINE_MUTEX(joydev_table_mutex);
+ static int joydev_correct(int value, struct js_corr *corr)
+ {
+       switch (corr->type) {
+-              case JS_CORR_NONE:
+-                      break;
+-              case JS_CORR_BROKEN:
+-                      value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
+-                              ((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
+-                              ((corr->coef[2] * (value - corr->coef[0])) >> 14);
+-                      break;
+-              default:
+-                      return 0;
++
++      case JS_CORR_NONE:
++              break;
++
++      case JS_CORR_BROKEN:
++              value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 :
++                      ((corr->coef[3] * (value - corr->coef[1])) >> 14)) :
++                      ((corr->coef[2] * (value - corr->coef[0])) >> 14);
++              break;
++
++      default:
++              return 0;
+       }
+       return value < -32767 ? -32767 : (value > 32767 ? 32767 : value);
+ }
+-static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
++static void joydev_pass_event(struct joydev_client *client,
++                            struct js_event *event)
++{
++      struct joydev *joydev = client->joydev;
++
++      /*
++       * IRQs already disabled, just acquire the lock
++       */
++      spin_lock(&client->buffer_lock);
++
++      client->buffer[client->head] = *event;
++
++      if (client->startup == joydev->nabs + joydev->nkey) {
++              client->head++;
++              client->head &= JOYDEV_BUFFER_SIZE - 1;
++              if (client->tail == client->head)
++                      client->startup = 0;
++      }
++
++      spin_unlock(&client->buffer_lock);
++
++      kill_fasync(&client->fasync, SIGIO, POLL_IN);
++}
++
++static void joydev_event(struct input_handle *handle,
++                       unsigned int type, unsigned int code, int value)
+ {
+       struct joydev *joydev = handle->private;
+       struct joydev_client *client;
+@@ -93,39 +125,32 @@ static void joydev_event(struct input_ha
+       switch (type) {
+-              case EV_KEY:
+-                      if (code < BTN_MISC || value == 2)
+-                              return;
+-                      event.type = JS_EVENT_BUTTON;
+-                      event.number = joydev->keymap[code - BTN_MISC];
+-                      event.value = value;
+-                      break;
+-
+-              case EV_ABS:
+-                      event.type = JS_EVENT_AXIS;
+-                      event.number = joydev->absmap[code];
+-                      event.value = joydev_correct(value, joydev->corr + event.number);
+-                      if (event.value == joydev->abs[event.number])
+-                              return;
+-                      joydev->abs[event.number] = event.value;
+-                      break;
++      case EV_KEY:
++              if (code < BTN_MISC || value == 2)
++                      return;
++              event.type = JS_EVENT_BUTTON;
++              event.number = joydev->keymap[code - BTN_MISC];
++              event.value = value;
++              break;
+-              default:
++      case EV_ABS:
++              event.type = JS_EVENT_AXIS;
++              event.number = joydev->absmap[code];
++              event.value = joydev_correct(value,
++                                      &joydev->corr[event.number]);
++              if (event.value == joydev->abs[event.number])
+                       return;
++              joydev->abs[event.number] = event.value;
++              break;
++
++      default:
++              return;
+       }
+       event.time = jiffies_to_msecs(jiffies);
+-      list_for_each_entry(client, &joydev->client_list, node) {
+-
+-              memcpy(client->buffer + client->head, &event, sizeof(struct js_event));
+-
+-              if (client->startup == joydev->nabs + joydev->nkey)
+-                      if (client->tail == (client->head = (client->head + 1) & (JOYDEV_BUFFER_SIZE - 1)))
+-                              client->startup = 0;
+-
+-              kill_fasync(&client->fasync, SIGIO, POLL_IN);
+-      }
++      list_for_each_entry_rcu(client, &joydev->client_list, node)
++              joydev_pass_event(client, &event);
+       wake_up_interruptible(&joydev->wait);
+ }
+@@ -144,23 +169,85 @@ static void joydev_free(struct device *d
+ {
+       struct joydev *joydev = container_of(dev, struct joydev, dev);
+-      joydev_table[joydev->minor] = NULL;
+       kfree(joydev);
+ }
++static void joydev_attach_client(struct joydev *joydev,
++                               struct joydev_client *client)
++{
++      spin_lock(&joydev->client_lock);
++      list_add_tail_rcu(&client->node, &joydev->client_list);
++      spin_unlock(&joydev->client_lock);
++      /*
++       * We don't use synchronize_rcu() here because read-side
++       * critical section is protected by a spinlock (dev->event_lock)
++       * instead of rcu_read_lock().
++       */
++      synchronize_sched();
++}
++
++static void joydev_detach_client(struct joydev *joydev,
++                               struct joydev_client *client)
++{
++      spin_lock(&joydev->client_lock);
++      list_del_rcu(&client->node);
++      spin_unlock(&joydev->client_lock);
++      synchronize_sched();
++}
++
++static int joydev_open_device(struct joydev *joydev)
++{
++      int retval;
++
++      retval = mutex_lock_interruptible(&joydev->mutex);
++      if (retval)
++              return retval;
++
++      if (!joydev->exist)
++              retval = -ENODEV;
++      else if (!joydev->open++)
++              retval = input_open_device(&joydev->handle);
++
++      mutex_unlock(&joydev->mutex);
++      return retval;
++}
++
++static void joydev_close_device(struct joydev *joydev)
++{
++      mutex_lock(&joydev->mutex);
++
++      if (joydev->exist && !--joydev->open)
++              input_close_device(&joydev->handle);
++
++      mutex_unlock(&joydev->mutex);
++}
++
++/*
++ * Wake up users waiting for IO so they can disconnect from
++ * dead device.
++ */
++static void joydev_hangup(struct joydev *joydev)
++{
++      struct joydev_client *client;
++
++      spin_lock(&joydev->client_lock);
++      list_for_each_entry(client, &joydev->client_list, node)
++              kill_fasync(&client->fasync, SIGIO, POLL_HUP);
++      spin_unlock(&joydev->client_lock);
++
++      wake_up_interruptible(&joydev->wait);
++}
++
+ static int joydev_release(struct inode *inode, struct file *file)
+ {
+       struct joydev_client *client = file->private_data;
+       struct joydev *joydev = client->joydev;
+       joydev_fasync(-1, file, 0);
+-
+-      list_del(&client->node);
++      joydev_detach_client(joydev, client);
+       kfree(client);
+-      if (!--joydev->open && joydev->exist)
+-              input_close_device(&joydev->handle);
+-
++      joydev_close_device(joydev);
+       put_device(&joydev->dev);
+       return 0;
+@@ -176,11 +263,16 @@ static int joydev_open(struct inode *ino
+       if (i >= JOYDEV_MINORS)
+               return -ENODEV;
++      error = mutex_lock_interruptible(&joydev_table_mutex);
++      if (error)
++              return error;
+       joydev = joydev_table[i];
+-      if (!joydev || !joydev->exist)
+-              return -ENODEV;
++      if (joydev)
++              get_device(&joydev->dev);
++      mutex_unlock(&joydev_table_mutex);
+-      get_device(&joydev->dev);
++      if (!joydev)
++              return -ENODEV;
+       client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL);
+       if (!client) {
+@@ -188,37 +280,129 @@ static int joydev_open(struct inode *ino
+               goto err_put_joydev;
+       }
++      spin_lock_init(&client->buffer_lock);
+       client->joydev = joydev;
+-      list_add_tail(&client->node, &joydev->client_list);
++      joydev_attach_client(joydev, client);
+-      if (!joydev->open++ && joydev->exist) {
+-              error = input_open_device(&joydev->handle);
+-              if (error)
+-                      goto err_free_client;
+-      }
++      error = joydev_open_device(joydev);
++      if (error)
++              goto err_free_client;
+       file->private_data = client;
+       return 0;
+  err_free_client:
+-      list_del(&client->node);
++      joydev_detach_client(joydev, client);
+       kfree(client);
+  err_put_joydev:
+       put_device(&joydev->dev);
+       return error;
+ }
+-static ssize_t joydev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
++static int joydev_generate_startup_event(struct joydev_client *client,
++                                       struct input_dev *input,
++                                       struct js_event *event)
+ {
+-      return -EINVAL;
++      struct joydev *joydev = client->joydev;
++      int have_event;
++
++      spin_lock_irq(&client->buffer_lock);
++
++      have_event = client->startup < joydev->nabs + joydev->nkey;
++
++      if (have_event) {
++
++              event->time = jiffies_to_msecs(jiffies);
++              if (client->startup < joydev->nkey) {
++                      event->type = JS_EVENT_BUTTON | JS_EVENT_INIT;
++                      event->number = client->startup;
++                      event->value = !!test_bit(joydev->keypam[event->number],
++                                                input->key);
++              } else {
++                      event->type = JS_EVENT_AXIS | JS_EVENT_INIT;
++                      event->number = client->startup - joydev->nkey;
++                      event->value = joydev->abs[event->number];
++              }
++              client->startup++;
++      }
++
++      spin_unlock_irq(&client->buffer_lock);
++
++      return have_event;
++}
++
++static int joydev_fetch_next_event(struct joydev_client *client,
++                                 struct js_event *event)
++{
++      int have_event;
++
++      spin_lock_irq(&client->buffer_lock);
++
++      have_event = client->head != client->tail;
++      if (have_event) {
++              *event = client->buffer[client->tail++];
++              client->tail &= JOYDEV_BUFFER_SIZE - 1;
++      }
++
++      spin_unlock_irq(&client->buffer_lock);
++
++      return have_event;
+ }
+-static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
++/*
++ * Old joystick interface
++ */
++static ssize_t joydev_0x_read(struct joydev_client *client,
++                            struct input_dev *input,
++                            char __user *buf)
++{
++      struct joydev *joydev = client->joydev;
++      struct JS_DATA_TYPE data;
++      int i;
++
++      spin_lock_irq(&input->event_lock);
++
++      /*
++       * Get device state
++       */
++      for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++)
++              data.buttons |=
++                      test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0;
++      data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x;
++      data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y;
++
++      /*
++       * Reset reader's event queue
++       */
++      spin_lock(&client->buffer_lock);
++      client->startup = 0;
++      client->tail = client->head;
++      spin_unlock(&client->buffer_lock);
++
++      spin_unlock_irq(&input->event_lock);
++
++      if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
++              return -EFAULT;
++
++      return sizeof(struct JS_DATA_TYPE);
++}
++
++static inline int joydev_data_pending(struct joydev_client *client)
++{
++      struct joydev *joydev = client->joydev;
++
++      return client->startup < joydev->nabs + joydev->nkey ||
++              client->head != client->tail;
++}
++
++static ssize_t joydev_read(struct file *file, char __user *buf,
++                         size_t count, loff_t *ppos)
+ {
+       struct joydev_client *client = file->private_data;
+       struct joydev *joydev = client->joydev;
+       struct input_dev *input = joydev->handle.dev;
+-      int retval = 0;
++      struct js_event event;
++      int retval;
+       if (!joydev->exist)
+               return -ENODEV;
+@@ -226,68 +410,35 @@ static ssize_t joydev_read(struct file *
+       if (count < sizeof(struct js_event))
+               return -EINVAL;
+-      if (count == sizeof(struct JS_DATA_TYPE)) {
+-
+-              struct JS_DATA_TYPE data;
+-              int i;
+-
+-              for (data.buttons = i = 0; i < 32 && i < joydev->nkey; i++)
+-                      data.buttons |= test_bit(joydev->keypam[i], input->key) ? (1 << i) : 0;
+-              data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x;
+-              data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y;
+-
+-              if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)))
+-                      return -EFAULT;
+-
+-              client->startup = 0;
+-              client->tail = client->head;
+-
+-              return sizeof(struct JS_DATA_TYPE);
+-      }
++      if (count == sizeof(struct JS_DATA_TYPE))
++              return joydev_0x_read(client, input, buf);
+-      if (client->startup == joydev->nabs + joydev->nkey &&
+-          client->head == client->tail && (file->f_flags & O_NONBLOCK))
++      if (!joydev_data_pending(client) && (file->f_flags & O_NONBLOCK))
+               return -EAGAIN;
+       retval = wait_event_interruptible(joydev->wait,
+-                                        !joydev->exist ||
+-                                        client->startup < joydev->nabs + joydev->nkey ||
+-                                        client->head != client->tail);
++                      !joydev->exist || joydev_data_pending(client));
+       if (retval)
+               return retval;
+       if (!joydev->exist)
+               return -ENODEV;
+-      while (client->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) {
+-
+-              struct js_event event;
+-
+-              event.time = jiffies_to_msecs(jiffies);
+-
+-              if (client->startup < joydev->nkey) {
+-                      event.type = JS_EVENT_BUTTON | JS_EVENT_INIT;
+-                      event.number = client->startup;
+-                      event.value = !!test_bit(joydev->keypam[event.number], input->key);
+-              } else {
+-                      event.type = JS_EVENT_AXIS | JS_EVENT_INIT;
+-                      event.number = client->startup - joydev->nkey;
+-                      event.value = joydev->abs[event.number];
+-              }
++      while (retval + sizeof(struct js_event) <= count &&
++             joydev_generate_startup_event(client, input, &event)) {
+               if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
+                       return -EFAULT;
+-              client->startup++;
+               retval += sizeof(struct js_event);
+       }
+-      while (client->head != client->tail && retval + sizeof(struct js_event) <= count) {
++      while (retval + sizeof(struct js_event) <= count &&
++             joydev_fetch_next_event(client, &event)) {
+-              if (copy_to_user(buf + retval, client->buffer + client->tail, sizeof(struct js_event)))
++              if (copy_to_user(buf + retval, &event, sizeof(struct js_event)))
+                       return -EFAULT;
+-              client->tail = (client->tail + 1) & (JOYDEV_BUFFER_SIZE - 1);
+               retval += sizeof(struct js_event);
+       }
+@@ -301,126 +452,144 @@ static unsigned int joydev_poll(struct f
+       struct joydev *joydev = client->joydev;
+       poll_wait(file, &joydev->wait, wait);
+-      return ((client->head != client->tail || client->startup < joydev->nabs + joydev->nkey) ?
+-              (POLLIN | POLLRDNORM) : 0) | (joydev->exist ? 0 : (POLLHUP | POLLERR));
++      return (joydev_data_pending(client) ? (POLLIN | POLLRDNORM) : 0) |
++              (joydev->exist ?  0 : (POLLHUP | POLLERR));
+ }
+-static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp)
++static int joydev_ioctl_common(struct joydev *joydev,
++                              unsigned int cmd, void __user *argp)
+ {
+       struct input_dev *dev = joydev->handle.dev;
+       int i, j;
+       switch (cmd) {
+-              case JS_SET_CAL:
+-                      return copy_from_user(&joydev->glue.JS_CORR, argp,
++      case JS_SET_CAL:
++              return copy_from_user(&joydev->glue.JS_CORR, argp,
+                               sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0;
+-              case JS_GET_CAL:
+-                      return copy_to_user(argp, &joydev->glue.JS_CORR,
++      case JS_GET_CAL:
++              return copy_to_user(argp, &joydev->glue.JS_CORR,
+                               sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0;
+-              case JS_SET_TIMEOUT:
+-                      return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
++      case JS_SET_TIMEOUT:
++              return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
+-              case JS_GET_TIMEOUT:
+-                      return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
++      case JS_GET_TIMEOUT:
++              return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
+-              case JSIOCGVERSION:
+-                      return put_user(JS_VERSION, (__u32 __user *) argp);
++      case JSIOCGVERSION:
++              return put_user(JS_VERSION, (__u32 __user *) argp);
+-              case JSIOCGAXES:
+-                      return put_user(joydev->nabs, (__u8 __user *) argp);
+-
+-              case JSIOCGBUTTONS:
+-                      return put_user(joydev->nkey, (__u8 __user *) argp);
+-
+-              case JSIOCSCORR:
+-                      if (copy_from_user(joydev->corr, argp,
+-                                    sizeof(joydev->corr[0]) * joydev->nabs))
+-                          return -EFAULT;
+-                      for (i = 0; i < joydev->nabs; i++) {
+-                              j = joydev->abspam[i];
+-                              joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
+-                      }
+-                      return 0;
+-
+-              case JSIOCGCORR:
+-                      return copy_to_user(argp, joydev->corr,
+-                                              sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0;
++      case JSIOCGAXES:
++              return put_user(joydev->nabs, (__u8 __user *) argp);
++
++      case JSIOCGBUTTONS:
++              return put_user(joydev->nkey, (__u8 __user *) argp);
++
++      case JSIOCSCORR:
++              if (copy_from_user(joydev->corr, argp,
++                            sizeof(joydev->corr[0]) * joydev->nabs))
++                  return -EFAULT;
++
++              for (i = 0; i < joydev->nabs; i++) {
++                      j = joydev->abspam[i];
++                      joydev->abs[i] = joydev_correct(dev->abs[j],
++                                                      &joydev->corr[i]);
++              }
++              return 0;
+-              case JSIOCSAXMAP:
+-                      if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * (ABS_MAX + 1)))
+-                              return -EFAULT;
+-                      for (i = 0; i < joydev->nabs; i++) {
+-                              if (joydev->abspam[i] > ABS_MAX)
+-                                      return -EINVAL;
+-                              joydev->absmap[joydev->abspam[i]] = i;
+-                      }
+-                      return 0;
+-
+-              case JSIOCGAXMAP:
+-                      return copy_to_user(argp, joydev->abspam,
+-                                              sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0;
++      case JSIOCGCORR:
++              return copy_to_user(argp, joydev->corr,
++                      sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0;
++
++      case JSIOCSAXMAP:
++              if (copy_from_user(joydev->abspam, argp,
++                                 sizeof(__u8) * (ABS_MAX + 1)))
++                      return -EFAULT;
++
++              for (i = 0; i < joydev->nabs; i++) {
++                      if (joydev->abspam[i] > ABS_MAX)
++                              return -EINVAL;
++                      joydev->absmap[joydev->abspam[i]] = i;
++              }
++              return 0;
++
++      case JSIOCGAXMAP:
++              return copy_to_user(argp, joydev->abspam,
++                      sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0;
++
++      case JSIOCSBTNMAP:
++              if (copy_from_user(joydev->keypam, argp,
++                                 sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)))
++                      return -EFAULT;
++
++              for (i = 0; i < joydev->nkey; i++) {
++                      if (joydev->keypam[i] > KEY_MAX ||
++                          joydev->keypam[i] < BTN_MISC)
++                              return -EINVAL;
++                      joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
++              }
+-              case JSIOCSBTNMAP:
+-                      if (copy_from_user(joydev->keypam, argp, sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)))
++              return 0;
++
++      case JSIOCGBTNMAP:
++              return copy_to_user(argp, joydev->keypam,
++                      sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0;
++
++      default:
++              if ((cmd & ~IOCSIZE_MASK) == JSIOCGNAME(0)) {
++                      int len;
++                      if (!dev->name)
++                              return 0;
++                      len = strlen(dev->name) + 1;
++                      if (len > _IOC_SIZE(cmd))
++                              len = _IOC_SIZE(cmd);
++                      if (copy_to_user(argp, dev->name, len))
+                               return -EFAULT;
+-                      for (i = 0; i < joydev->nkey; i++) {
+-                              if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC)
+-                                      return -EINVAL;
+-                              joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
+-                      }
+-                      return 0;
+-
+-              case JSIOCGBTNMAP:
+-                      return copy_to_user(argp, joydev->keypam,
+-                                              sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0;
+-
+-              default:
+-                      if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
+-                              int len;
+-                              if (!dev->name)
+-                                      return 0;
+-                              len = strlen(dev->name) + 1;
+-                              if (len > _IOC_SIZE(cmd))
+-                                      len = _IOC_SIZE(cmd);
+-                              if (copy_to_user(argp, dev->name, len))
+-                                      return -EFAULT;
+-                              return len;
+-                      }
++                      return len;
++              }
+       }
+       return -EINVAL;
+ }
+ #ifdef CONFIG_COMPAT
+-static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++static long joydev_compat_ioctl(struct file *file,
++                              unsigned int cmd, unsigned long arg)
+ {
+       struct joydev_client *client = file->private_data;
+       struct joydev *joydev = client->joydev;
+       void __user *argp = (void __user *)arg;
+       s32 tmp32;
+       struct JS_DATA_SAVE_TYPE_32 ds32;
+-      int err;
++      int retval;
+-      if (!joydev->exist)
+-              return -ENODEV;
++      retval = mutex_lock_interruptible(&joydev->mutex);
++      if (retval)
++              return retval;
++
++      if (!joydev->exist) {
++              retval = -ENODEV;
++              goto out;
++      }
++
++      switch (cmd) {
+-      switch(cmd) {
+       case JS_SET_TIMELIMIT:
+-              err = get_user(tmp32, (s32 __user *) arg);
+-              if (err == 0)
++              retval = get_user(tmp32, (s32 __user *) arg);
++              if (retval == 0)
+                       joydev->glue.JS_TIMELIMIT = tmp32;
+               break;
++
+       case JS_GET_TIMELIMIT:
+               tmp32 = joydev->glue.JS_TIMELIMIT;
+-              err = put_user(tmp32, (s32 __user *) arg);
++              retval = put_user(tmp32, (s32 __user *) arg);
+               break;
+       case JS_SET_ALL:
+-              err = copy_from_user(&ds32, argp,
+-                                   sizeof(ds32)) ? -EFAULT : 0;
+-              if (err == 0) {
++              retval = copy_from_user(&ds32, argp,
++                                      sizeof(ds32)) ? -EFAULT : 0;
++              if (retval == 0) {
+                       joydev->glue.JS_TIMEOUT    = ds32.JS_TIMEOUT;
+                       joydev->glue.BUSY          = ds32.BUSY;
+                       joydev->glue.JS_EXPIRETIME = ds32.JS_EXPIRETIME;
+@@ -438,55 +607,119 @@ static long joydev_compat_ioctl(struct f
+               ds32.JS_SAVE       = joydev->glue.JS_SAVE;
+               ds32.JS_CORR       = joydev->glue.JS_CORR;
+-              err = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0;
++              retval = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0;
+               break;
+       default:
+-              err = joydev_ioctl_common(joydev, cmd, argp);
++              retval = joydev_ioctl_common(joydev, cmd, argp);
++              break;
+       }
+-      return err;
++
++ out:
++      mutex_unlock(&joydev->mutex);
++      return retval;
+ }
+ #endif /* CONFIG_COMPAT */
+-static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
++static long joydev_ioctl(struct file *file,
++                       unsigned int cmd, unsigned long arg)
+ {
+       struct joydev_client *client = file->private_data;
+       struct joydev *joydev = client->joydev;
+       void __user *argp = (void __user *)arg;
++      int retval;
+-      if (!joydev->exist)
+-              return -ENODEV;
++      retval = mutex_lock_interruptible(&joydev->mutex);
++      if (retval)
++              return retval;
++
++      if (!joydev->exist) {
++              retval = -ENODEV;
++              goto out;
++      }
++
++      switch (cmd) {
++
++      case JS_SET_TIMELIMIT:
++              retval = get_user(joydev->glue.JS_TIMELIMIT,
++                                (long __user *) arg);
++              break;
++
++      case JS_GET_TIMELIMIT:
++              retval = put_user(joydev->glue.JS_TIMELIMIT,
++                                (long __user *) arg);
++              break;
++
++      case JS_SET_ALL:
++              retval = copy_from_user(&joydev->glue, argp,
++                                      sizeof(joydev->glue)) ? -EFAULT: 0;
++              break;
+-      switch(cmd) {
+-              case JS_SET_TIMELIMIT:
+-                      return get_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg);
+-              case JS_GET_TIMELIMIT:
+-                      return put_user(joydev->glue.JS_TIMELIMIT, (long __user *) arg);
+-              case JS_SET_ALL:
+-                      return copy_from_user(&joydev->glue, argp,
+-                                              sizeof(joydev->glue)) ? -EFAULT : 0;
+-              case JS_GET_ALL:
+-                      return copy_to_user(argp, &joydev->glue,
+-                                              sizeof(joydev->glue)) ? -EFAULT : 0;
+-              default:
+-                      return joydev_ioctl_common(joydev, cmd, argp);
++      case JS_GET_ALL:
++              retval = copy_to_user(argp, &joydev->glue,
++                                    sizeof(joydev->glue)) ? -EFAULT : 0;
++              break;
++
++      default:
++              retval = joydev_ioctl_common(joydev, cmd, argp);
++              break;
+       }
++ out:
++      mutex_unlock(&joydev->mutex);
++      return retval;
+ }
+ static const struct file_operations joydev_fops = {
+-      .owner =        THIS_MODULE,
+-      .read =         joydev_read,
+-      .write =        joydev_write,
+-      .poll =         joydev_poll,
+-      .open =         joydev_open,
+-      .release =      joydev_release,
+-      .ioctl =        joydev_ioctl,
++      .owner          = THIS_MODULE,
++      .read           = joydev_read,
++      .poll           = joydev_poll,
++      .open           = joydev_open,
++      .release        = joydev_release,
++      .unlocked_ioctl = joydev_ioctl,
+ #ifdef CONFIG_COMPAT
+-      .compat_ioctl = joydev_compat_ioctl,
++      .compat_ioctl   = joydev_compat_ioctl,
+ #endif
+-      .fasync =       joydev_fasync,
++      .fasync         = joydev_fasync,
+ };
++static int joydev_install_chrdev(struct joydev *joydev)
++{
++      joydev_table[joydev->minor] = joydev;
++      return 0;
++}
++
++static void joydev_remove_chrdev(struct joydev *joydev)
++{
++      mutex_lock(&joydev_table_mutex);
++      joydev_table[joydev->minor] = NULL;
++      mutex_unlock(&joydev_table_mutex);
++}
++
++/*
++ * Mark device non-existant. This disables writes, ioctls and
++ * prevents new users from opening the device. Already posted
++ * blocking reads will stay, however new ones will fail.
++ */
++static void joydev_mark_dead(struct joydev *joydev)
++{
++      mutex_lock(&joydev->mutex);
++      joydev->exist = 0;
++      mutex_unlock(&joydev->mutex);
++}
++
++static void joydev_cleanup(struct joydev *joydev)
++{
++      struct input_handle *handle = &joydev->handle;
++
++      joydev_mark_dead(joydev);
++      joydev_hangup(joydev);
++      joydev_remove_chrdev(joydev);
++
++      /* joydev is marked dead so noone else accesses joydev->open */
++      if (joydev->open)
++              input_close_device(handle);
++}
++
+ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
+                         const struct input_device_id *id)
+ {
+@@ -494,7 +727,10 @@ static int joydev_connect(struct input_h
+       int i, j, t, minor;
+       int error;
+-      for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++);
++      for (minor = 0; minor < JOYDEV_MINORS; minor++)
++              if (!joydev_table[minor])
++                      break;
++
+       if (minor == JOYDEV_MINORS) {
+               printk(KERN_ERR "joydev: no more free joydev devices\n");
+               return -ENFILE;
+@@ -505,15 +741,19 @@ static int joydev_connect(struct input_h
+               return -ENOMEM;
+       INIT_LIST_HEAD(&joydev->client_list);
++      spin_lock_init(&joydev->client_lock);
++      mutex_init(&joydev->mutex);
+       init_waitqueue_head(&joydev->wait);
++      snprintf(joydev->name, sizeof(joydev->name), "js%d", minor);
++      joydev->exist = 1;
+       joydev->minor = minor;
++
+       joydev->exist = 1;
+       joydev->handle.dev = dev;
+       joydev->handle.name = joydev->name;
+       joydev->handle.handler = handler;
+       joydev->handle.private = joydev;
+-      snprintf(joydev->name, sizeof(joydev->name), "js%d", minor);
+       for (i = 0; i < ABS_MAX + 1; i++)
+               if (test_bit(i, dev->absbit)) {
+@@ -545,67 +785,65 @@ static int joydev_connect(struct input_h
+               }
+               joydev->corr[i].type = JS_CORR_BROKEN;
+               joydev->corr[i].prec = dev->absfuzz[j];
+-              joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
+-              joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
+-              if (!(t = ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j])))
+-                      continue;
+-              joydev->corr[i].coef[2] = (1 << 29) / t;
+-              joydev->corr[i].coef[3] = (1 << 29) / t;
++              joydev->corr[i].coef[0] =
++                      (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j];
++              joydev->corr[i].coef[1] =
++                      (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j];
++
++              t = (dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j];
++              if (t) {
++                      joydev->corr[i].coef[2] = (1 << 29) / t;
++                      joydev->corr[i].coef[3] = (1 << 29) / t;
+-              joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
++                      joydev->abs[i] = joydev_correct(dev->abs[j],
++                                                      joydev->corr + i);
++              }
+       }
+-      snprintf(joydev->dev.bus_id, sizeof(joydev->dev.bus_id),
+-               "js%d", minor);
++      strlcpy(joydev->dev.bus_id, joydev->name, sizeof(joydev->dev.bus_id));
++      joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor);
+       joydev->dev.class = &input_class;
+       joydev->dev.parent = &dev->dev;
+-      joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor);
+       joydev->dev.release = joydev_free;
+       device_initialize(&joydev->dev);
+-      joydev_table[minor] = joydev;
+-
+-      error = device_add(&joydev->dev);
++      error = input_register_handle(&joydev->handle);
+       if (error)
+               goto err_free_joydev;
+-      error = input_register_handle(&joydev->handle);
++      error = joydev_install_chrdev(joydev);
+       if (error)
+-              goto err_delete_joydev;
++              goto err_unregister_handle;
++
++      error = device_add(&joydev->dev);
++      if (error)
++              goto err_cleanup_joydev;
+       return 0;
+- err_delete_joydev:
+-      device_del(&joydev->dev);
++ err_cleanup_joydev:
++      joydev_cleanup(joydev);
++ err_unregister_handle:
++      input_unregister_handle(&joydev->handle);
+  err_free_joydev:
+       put_device(&joydev->dev);
+       return error;
+ }
+-
+ static void joydev_disconnect(struct input_handle *handle)
+ {
+       struct joydev *joydev = handle->private;
+-      struct joydev_client *client;
+-      input_unregister_handle(handle);
+       device_del(&joydev->dev);
+-
+-      joydev->exist = 0;
+-
+-      if (joydev->open) {
+-              input_close_device(handle);
+-              list_for_each_entry(client, &joydev->client_list, node)
+-                      kill_fasync(&client->fasync, SIGIO, POLL_HUP);
+-              wake_up_interruptible(&joydev->wait);
+-      }
+-
++      joydev_cleanup(joydev);
++      input_unregister_handle(handle);
+       put_device(&joydev->dev);
+ }
+ static const struct input_device_id joydev_blacklist[] = {
+       {
+-              .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
++              .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
++                              INPUT_DEVICE_ID_MATCH_KEYBIT,
+               .evbit = { BIT(EV_KEY) },
+               .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
+       },      /* Avoid itouchpads, touchscreens and tablets */
+@@ -614,17 +852,20 @@ static const struct input_device_id joyd
+ static const struct input_device_id joydev_ids[] = {
+       {
+-              .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
++              .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
++                              INPUT_DEVICE_ID_MATCH_ABSBIT,
+               .evbit = { BIT(EV_ABS) },
+               .absbit = { BIT(ABS_X) },
+       },
+       {
+-              .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
++              .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
++                              INPUT_DEVICE_ID_MATCH_ABSBIT,
+               .evbit = { BIT(EV_ABS) },
+               .absbit = { BIT(ABS_WHEEL) },
+       },
+       {
+-              .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
++              .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
++                              INPUT_DEVICE_ID_MATCH_ABSBIT,
+               .evbit = { BIT(EV_ABS) },
+               .absbit = { BIT(ABS_THROTTLE) },
+       },
+@@ -634,14 +875,14 @@ static const struct input_device_id joyd
+ MODULE_DEVICE_TABLE(input, joydev_ids);
+ static struct input_handler joydev_handler = {
+-      .event =        joydev_event,
+-      .connect =      joydev_connect,
+-      .disconnect =   joydev_disconnect,
+-      .fops =         &joydev_fops,
+-      .minor =        JOYDEV_MINOR_BASE,
+-      .name =         "joydev",
+-      .id_table =     joydev_ids,
+-      .blacklist =    joydev_blacklist,
++      .event          = joydev_event,
++      .connect        = joydev_connect,
++      .disconnect     = joydev_disconnect,
++      .fops           = &joydev_fops,
++      .minor          = JOYDEV_MINOR_BASE,
++      .name           = "joydev",
++      .id_table       = joydev_ids,
++      .blacklist      = joydev_blacklist,
+ };
+ static int __init joydev_init(void)
diff --git a/queue-2.6.23/input-mousedev-implement-proper-locking.patch b/queue-2.6.23/input-mousedev-implement-proper-locking.patch
new file mode 100644 (file)
index 0000000..8c0ad43
--- /dev/null
@@ -0,0 +1,1103 @@
+From 464b241575f3700e14492e34f26bcd1794280f55 Mon Sep 17 00:00:00 2001
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Date: Thu, 30 Aug 2007 00:22:24 -0400
+Subject: [PATCH] Input: mousedev - implement proper locking
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+patch 464b241575f3700e14492e34f26bcd1794280f55 in mainline.
+
+Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
+Cc: Al Viro <viro@ZenIV.linux.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/input/mousedev.c |  742 +++++++++++++++++++++++++++++------------------
+ 1 file changed, 470 insertions(+), 272 deletions(-)
+
+--- a/drivers/input/mousedev.c
++++ b/drivers/input/mousedev.c
+@@ -61,9 +61,11 @@ struct mousedev {
+       int open;
+       int minor;
+       char name[16];
++      struct input_handle handle;
+       wait_queue_head_t wait;
+       struct list_head client_list;
+-      struct input_handle handle;
++      spinlock_t client_lock; /* protects client_list */
++      struct mutex mutex;
+       struct device dev;
+       struct list_head mixdev_node;
+@@ -113,108 +115,137 @@ static unsigned char mousedev_imex_seq[]
+ static struct input_handler mousedev_handler;
+ static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
++static DEFINE_MUTEX(mousedev_table_mutex);
+ static struct mousedev *mousedev_mix;
+ static LIST_HEAD(mousedev_mix_list);
++static void mixdev_open_devices(void);
++static void mixdev_close_devices(void);
++
+ #define fx(i)  (mousedev->old_x[(mousedev->pkt_count - (i)) & 03])
+ #define fy(i)  (mousedev->old_y[(mousedev->pkt_count - (i)) & 03])
+-static void mousedev_touchpad_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value)
++static void mousedev_touchpad_event(struct input_dev *dev,
++                                  struct mousedev *mousedev,
++                                  unsigned int code, int value)
+ {
+       int size, tmp;
+       enum { FRACTION_DENOM = 128 };
+       switch (code) {
+-              case ABS_X:
+-                      fx(0) = value;
+-                      if (mousedev->touch && mousedev->pkt_count >= 2) {
+-                              size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+-                              if (size == 0)
+-                                      size = 256 * 2;
+-                              tmp = ((value - fx(2)) * (256 * FRACTION_DENOM)) / size;
+-                              tmp += mousedev->frac_dx;
+-                              mousedev->packet.dx = tmp / FRACTION_DENOM;
+-                              mousedev->frac_dx = tmp - mousedev->packet.dx * FRACTION_DENOM;
+-                      }
+-                      break;
+-              case ABS_Y:
+-                      fy(0) = value;
+-                      if (mousedev->touch && mousedev->pkt_count >= 2) {
+-                              /* use X size to keep the same scale */
+-                              size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+-                              if (size == 0)
+-                                      size = 256 * 2;
+-                              tmp = -((value - fy(2)) * (256 * FRACTION_DENOM)) / size;
+-                              tmp += mousedev->frac_dy;
+-                              mousedev->packet.dy = tmp / FRACTION_DENOM;
+-                              mousedev->frac_dy = tmp - mousedev->packet.dy * FRACTION_DENOM;
+-                      }
+-                      break;
++      case ABS_X:
++              fx(0) = value;
++              if (mousedev->touch && mousedev->pkt_count >= 2) {
++                      size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
++                      if (size == 0)
++                              size = 256 * 2;
++                      tmp = ((value - fx(2)) * 256 * FRACTION_DENOM) / size;
++                      tmp += mousedev->frac_dx;
++                      mousedev->packet.dx = tmp / FRACTION_DENOM;
++                      mousedev->frac_dx =
++                              tmp - mousedev->packet.dx * FRACTION_DENOM;
++              }
++              break;
++
++      case ABS_Y:
++              fy(0) = value;
++              if (mousedev->touch && mousedev->pkt_count >= 2) {
++                      /* use X size to keep the same scale */
++                      size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
++                      if (size == 0)
++                              size = 256 * 2;
++                      tmp = -((value - fy(2)) * 256 * FRACTION_DENOM) / size;
++                      tmp += mousedev->frac_dy;
++                      mousedev->packet.dy = tmp / FRACTION_DENOM;
++                      mousedev->frac_dy = tmp -
++                              mousedev->packet.dy * FRACTION_DENOM;
++              }
++              break;
+       }
+ }
+-static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev, unsigned int code, int value)
++static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
++                              unsigned int code, int value)
+ {
+       int size;
+       switch (code) {
+-              case ABS_X:
+-                      size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
+-                      if (size == 0)
+-                              size = xres ? : 1;
+-                      if (value > dev->absmax[ABS_X])
+-                              value = dev->absmax[ABS_X];
+-                      if (value < dev->absmin[ABS_X])
+-                              value = dev->absmin[ABS_X];
+-                      mousedev->packet.x = ((value - dev->absmin[ABS_X]) * xres) / size;
+-                      mousedev->packet.abs_event = 1;
+-                      break;
+-              case ABS_Y:
+-                      size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y];
+-                      if (size == 0)
+-                              size = yres ? : 1;
+-                      if (value > dev->absmax[ABS_Y])
+-                              value = dev->absmax[ABS_Y];
+-                      if (value < dev->absmin[ABS_Y])
+-                              value = dev->absmin[ABS_Y];
+-                      mousedev->packet.y = yres - ((value - dev->absmin[ABS_Y]) * yres) / size;
+-                      mousedev->packet.abs_event = 1;
+-                      break;
++      case ABS_X:
++              size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
++              if (size == 0)
++                      size = xres ? : 1;
++              if (value > dev->absmax[ABS_X])
++                      value = dev->absmax[ABS_X];
++              if (value < dev->absmin[ABS_X])
++                      value = dev->absmin[ABS_X];
++              mousedev->packet.x =
++                      ((value - dev->absmin[ABS_X]) * xres) / size;
++              mousedev->packet.abs_event = 1;
++              break;
++
++      case ABS_Y:
++              size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y];
++              if (size == 0)
++                      size = yres ? : 1;
++              if (value > dev->absmax[ABS_Y])
++                      value = dev->absmax[ABS_Y];
++              if (value < dev->absmin[ABS_Y])
++                      value = dev->absmin[ABS_Y];
++              mousedev->packet.y = yres -
++                      ((value - dev->absmin[ABS_Y]) * yres) / size;
++              mousedev->packet.abs_event = 1;
++              break;
+       }
+ }
+-static void mousedev_rel_event(struct mousedev *mousedev, unsigned int code, int value)
++static void mousedev_rel_event(struct mousedev *mousedev,
++                              unsigned int code, int value)
+ {
+       switch (code) {
+-              case REL_X:     mousedev->packet.dx += value; break;
+-              case REL_Y:     mousedev->packet.dy -= value; break;
+-              case REL_WHEEL: mousedev->packet.dz -= value; break;
++      case REL_X:
++              mousedev->packet.dx += value;
++              break;
++
++      case REL_Y:
++              mousedev->packet.dy -= value;
++              break;
++
++      case REL_WHEEL:
++              mousedev->packet.dz -= value;
++              break;
+       }
+ }
+-static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int value)
++static void mousedev_key_event(struct mousedev *mousedev,
++                              unsigned int code, int value)
+ {
+       int index;
+       switch (code) {
+-              case BTN_TOUCH:
+-              case BTN_0:
+-              case BTN_LEFT:          index = 0; break;
+-              case BTN_STYLUS:
+-              case BTN_1:
+-              case BTN_RIGHT:         index = 1; break;
+-              case BTN_2:
+-              case BTN_FORWARD:
+-              case BTN_STYLUS2:
+-              case BTN_MIDDLE:        index = 2; break;
+-              case BTN_3:
+-              case BTN_BACK:
+-              case BTN_SIDE:          index = 3; break;
+-              case BTN_4:
+-              case BTN_EXTRA:         index = 4; break;
+-              default:                return;
++
++      case BTN_TOUCH:
++      case BTN_0:
++      case BTN_LEFT:          index = 0; break;
++
++      case BTN_STYLUS:
++      case BTN_1:
++      case BTN_RIGHT:         index = 1; break;
++
++      case BTN_2:
++      case BTN_FORWARD:
++      case BTN_STYLUS2:
++      case BTN_MIDDLE:        index = 2; break;
++
++      case BTN_3:
++      case BTN_BACK:
++      case BTN_SIDE:          index = 3; break;
++
++      case BTN_4:
++      case BTN_EXTRA:         index = 4; break;
++
++      default:                return;
+       }
+       if (value) {
+@@ -226,19 +257,22 @@ static void mousedev_key_event(struct mo
+       }
+ }
+-static void mousedev_notify_readers(struct mousedev *mousedev, struct mousedev_hw_data *packet)
++static void mousedev_notify_readers(struct mousedev *mousedev,
++                                  struct mousedev_hw_data *packet)
+ {
+       struct mousedev_client *client;
+       struct mousedev_motion *p;
+-      unsigned long flags;
++      unsigned int new_head;
+       int wake_readers = 0;
+-      list_for_each_entry(client, &mousedev->client_list, node) {
+-              spin_lock_irqsave(&client->packet_lock, flags);
++      list_for_each_entry_rcu(client, &mousedev->client_list, node) {
++
++              /* Just acquire the lock, interrupts already disabled */
++              spin_lock(&client->packet_lock);
+               p = &client->packets[client->head];
+               if (client->ready && p->buttons != mousedev->packet.buttons) {
+-                      unsigned int new_head = (client->head + 1) % PACKET_QUEUE_LEN;
++                      new_head = (client->head + 1) % PACKET_QUEUE_LEN;
+                       if (new_head != client->tail) {
+                               p = &client->packets[client->head = new_head];
+                               memset(p, 0, sizeof(struct mousedev_motion));
+@@ -253,19 +287,22 @@ static void mousedev_notify_readers(stru
+               }
+               client->pos_x += packet->dx;
+-              client->pos_x = client->pos_x < 0 ? 0 : (client->pos_x >= xres ? xres : client->pos_x);
++              client->pos_x = client->pos_x < 0 ?
++                      0 : (client->pos_x >= xres ? xres : client->pos_x);
+               client->pos_y += packet->dy;
+-              client->pos_y = client->pos_y < 0 ? 0 : (client->pos_y >= yres ? yres : client->pos_y);
++              client->pos_y = client->pos_y < 0 ?
++                      0 : (client->pos_y >= yres ? yres : client->pos_y);
+               p->dx += packet->dx;
+               p->dy += packet->dy;
+               p->dz += packet->dz;
+               p->buttons = mousedev->packet.buttons;
+-              if (p->dx || p->dy || p->dz || p->buttons != client->last_buttons)
++              if (p->dx || p->dy || p->dz ||
++                  p->buttons != client->last_buttons)
+                       client->ready = 1;
+-              spin_unlock_irqrestore(&client->packet_lock, flags);
++              spin_unlock(&client->packet_lock);
+               if (client->ready) {
+                       kill_fasync(&client->fasync, SIGIO, POLL_IN);
+@@ -281,7 +318,8 @@ static void mousedev_touchpad_touch(stru
+ {
+       if (!value) {
+               if (mousedev->touch &&
+-                  time_before(jiffies, mousedev->touch + msecs_to_jiffies(tap_time))) {
++                  time_before(jiffies,
++                              mousedev->touch + msecs_to_jiffies(tap_time))) {
+                       /*
+                        * Toggle left button to emulate tap.
+                        * We rely on the fact that mousedev_mix always has 0
+@@ -290,7 +328,8 @@ static void mousedev_touchpad_touch(stru
+                       set_bit(0, &mousedev->packet.buttons);
+                       set_bit(0, &mousedev_mix->packet.buttons);
+                       mousedev_notify_readers(mousedev, &mousedev_mix->packet);
+-                      mousedev_notify_readers(mousedev_mix, &mousedev_mix->packet);
++                      mousedev_notify_readers(mousedev_mix,
++                                              &mousedev_mix->packet);
+                       clear_bit(0, &mousedev->packet.buttons);
+                       clear_bit(0, &mousedev_mix->packet.buttons);
+               }
+@@ -302,54 +341,61 @@ static void mousedev_touchpad_touch(stru
+               mousedev->touch = jiffies;
+ }
+-static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
++static void mousedev_event(struct input_handle *handle,
++                         unsigned int type, unsigned int code, int value)
+ {
+       struct mousedev *mousedev = handle->private;
+       switch (type) {
+-              case EV_ABS:
+-                      /* Ignore joysticks */
+-                      if (test_bit(BTN_TRIGGER, handle->dev->keybit))
+-                              return;
+-                      if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
+-                              mousedev_touchpad_event(handle->dev, mousedev, code, value);
++      case EV_ABS:
++              /* Ignore joysticks */
++              if (test_bit(BTN_TRIGGER, handle->dev->keybit))
++                      return;
++
++              if (test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
++                      mousedev_touchpad_event(handle->dev,
++                                              mousedev, code, value);
++              else
++                      mousedev_abs_event(handle->dev, mousedev, code, value);
++
++              break;
++
++      case EV_REL:
++              mousedev_rel_event(mousedev, code, value);
++              break;
++
++      case EV_KEY:
++              if (value != 2) {
++                      if (code == BTN_TOUCH &&
++                          test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
++                              mousedev_touchpad_touch(mousedev, value);
+                       else
+-                              mousedev_abs_event(handle->dev, mousedev, code, value);
+-
+-                      break;
+-
+-              case EV_REL:
+-                      mousedev_rel_event(mousedev, code, value);
+-                      break;
++                              mousedev_key_event(mousedev, code, value);
++              }
++              break;
+-              case EV_KEY:
+-                      if (value != 2) {
+-                              if (code == BTN_TOUCH && test_bit(BTN_TOOL_FINGER, handle->dev->keybit))
+-                                      mousedev_touchpad_touch(mousedev, value);
+-                              else
+-                                      mousedev_key_event(mousedev, code, value);
++      case EV_SYN:
++              if (code == SYN_REPORT) {
++                      if (mousedev->touch) {
++                              mousedev->pkt_count++;
++                              /*
++                               * Input system eats duplicate events,
++                               * but we need all of them to do correct
++                               * averaging so apply present one forward
++                               */
++                              fx(0) = fx(1);
++                              fy(0) = fy(1);
+                       }
+-                      break;
+-
+-              case EV_SYN:
+-                      if (code == SYN_REPORT) {
+-                              if (mousedev->touch) {
+-                                      mousedev->pkt_count++;
+-                                      /* Input system eats duplicate events, but we need all of them
+-                                       * to do correct averaging so apply present one forward
+-                                       */
+-                                      fx(0) = fx(1);
+-                                      fy(0) = fy(1);
+-                              }
+-                              mousedev_notify_readers(mousedev, &mousedev->packet);
+-                              mousedev_notify_readers(mousedev_mix, &mousedev->packet);
++                      mousedev_notify_readers(mousedev, &mousedev->packet);
++                      mousedev_notify_readers(mousedev_mix, &mousedev->packet);
+-                              mousedev->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0;
+-                              mousedev->packet.abs_event = 0;
+-                      }
+-                      break;
++                      mousedev->packet.dx = mousedev->packet.dy =
++                              mousedev->packet.dz = 0;
++                      mousedev->packet.abs_event = 0;
++              }
++              break;
+       }
+ }
+@@ -367,41 +413,45 @@ static void mousedev_free(struct device 
+ {
+       struct mousedev *mousedev = container_of(dev, struct mousedev, dev);
+-      mousedev_table[mousedev->minor] = NULL;
+       kfree(mousedev);
+ }
+-static int mixdev_add_device(struct mousedev *mousedev)
++static int mousedev_open_device(struct mousedev *mousedev)
+ {
+-      int error;
+-
+-      if (mousedev_mix->open) {
+-              error = input_open_device(&mousedev->handle);
+-              if (error)
+-                      return error;
++      int retval;
+-              mousedev->open++;
+-              mousedev->mixdev_open = 1;
+-      }
++      retval = mutex_lock_interruptible(&mousedev->mutex);
++      if (retval)
++              return retval;
+-      get_device(&mousedev->dev);
+-      list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list);
++      if (mousedev->minor == MOUSEDEV_MIX)
++              mixdev_open_devices();
++      else if (!mousedev->exist)
++              retval = -ENODEV;
++      else if (!mousedev->open++)
++              retval = input_open_device(&mousedev->handle);
+-      return 0;
++      mutex_unlock(&mousedev->mutex);
++      return retval;
+ }
+-static void mixdev_remove_device(struct mousedev *mousedev)
++static void mousedev_close_device(struct mousedev *mousedev)
+ {
+-      if (mousedev->mixdev_open) {
+-              mousedev->mixdev_open = 0;
+-              if (!--mousedev->open && mousedev->exist)
+-                      input_close_device(&mousedev->handle);
+-      }
++      mutex_lock(&mousedev->mutex);
+-      list_del_init(&mousedev->mixdev_node);
+-      put_device(&mousedev->dev);
++      if (mousedev->minor == MOUSEDEV_MIX)
++              mixdev_close_devices();
++      else if (mousedev->exist && !--mousedev->open)
++              input_close_device(&mousedev->handle);
++
++      mutex_unlock(&mousedev->mutex);
+ }
++/*
++ * Open all available devices so they can all be multiplexed in one.
++ * stream. Note that this function is called with mousedev_mix->mutex
++ * held.
++ */
+ static void mixdev_open_devices(void)
+ {
+       struct mousedev *mousedev;
+@@ -411,16 +461,19 @@ static void mixdev_open_devices(void)
+       list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
+               if (!mousedev->mixdev_open) {
+-                      if (!mousedev->open && mousedev->exist)
+-                              if (input_open_device(&mousedev->handle))
+-                                      continue;
++                      if (mousedev_open_device(mousedev))
++                              continue;
+-                      mousedev->open++;
+                       mousedev->mixdev_open = 1;
+               }
+       }
+ }
++/*
++ * Close all devices that were opened as part of multiplexed
++ * device. Note that this function is called with mousedev_mix->mutex
++ * held.
++ */
+ static void mixdev_close_devices(void)
+ {
+       struct mousedev *mousedev;
+@@ -431,33 +484,50 @@ static void mixdev_close_devices(void)
+       list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) {
+               if (mousedev->mixdev_open) {
+                       mousedev->mixdev_open = 0;
+-                      if (!--mousedev->open && mousedev->exist)
+-                              input_close_device(&mousedev->handle);
++                      mousedev_close_device(mousedev);
+               }
+       }
+ }
++
++static void mousedev_attach_client(struct mousedev *mousedev,
++                                 struct mousedev_client *client)
++{
++      spin_lock(&mousedev->client_lock);
++      list_add_tail_rcu(&client->node, &mousedev->client_list);
++      spin_unlock(&mousedev->client_lock);
++      /*
++       * We don't use synchronize_rcu() here because read-side
++       * critical section is protected by a spinlock (dev->event_lock)
++       * instead of rcu_read_lock().
++       */
++      synchronize_sched();
++}
++
++static void mousedev_detach_client(struct mousedev *mousedev,
++                                 struct mousedev_client *client)
++{
++      spin_lock(&mousedev->client_lock);
++      list_del_rcu(&client->node);
++      spin_unlock(&mousedev->client_lock);
++      synchronize_sched();
++}
++
+ static int mousedev_release(struct inode *inode, struct file *file)
+ {
+       struct mousedev_client *client = file->private_data;
+       struct mousedev *mousedev = client->mousedev;
+       mousedev_fasync(-1, file, 0);
+-
+-      list_del(&client->node);
++      mousedev_detach_client(mousedev, client);
+       kfree(client);
+-      if (mousedev->minor == MOUSEDEV_MIX)
+-              mixdev_close_devices();
+-      else if (!--mousedev->open && mousedev->exist)
+-              input_close_device(&mousedev->handle);
+-
++      mousedev_close_device(mousedev);
+       put_device(&mousedev->dev);
+       return 0;
+ }
+-
+ static int mousedev_open(struct inode *inode, struct file *file)
+ {
+       struct mousedev_client *client;
+@@ -475,12 +545,17 @@ static int mousedev_open(struct inode *i
+       if (i >= MOUSEDEV_MINORS)
+               return -ENODEV;
++      error = mutex_lock_interruptible(&mousedev_table_mutex);
++      if (error)
++              return error;
+       mousedev = mousedev_table[i];
++      if (mousedev)
++              get_device(&mousedev->dev);
++      mutex_unlock(&mousedev_table_mutex);
++
+       if (!mousedev)
+               return -ENODEV;
+-      get_device(&mousedev->dev);
+-
+       client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL);
+       if (!client) {
+               error = -ENOMEM;
+@@ -491,21 +566,17 @@ static int mousedev_open(struct inode *i
+       client->pos_x = xres / 2;
+       client->pos_y = yres / 2;
+       client->mousedev = mousedev;
+-      list_add_tail(&client->node, &mousedev->client_list);
++      mousedev_attach_client(mousedev, client);
+-      if (mousedev->minor == MOUSEDEV_MIX)
+-              mixdev_open_devices();
+-      else if (!mousedev->open++ && mousedev->exist) {
+-              error = input_open_device(&mousedev->handle);
+-              if (error)
+-                      goto err_free_client;
+-      }
++      error = mousedev_open_device(mousedev);
++      if (error)
++              goto err_free_client;
+       file->private_data = client;
+       return 0;
+  err_free_client:
+-      list_del(&client->node);
++      mousedev_detach_client(mousedev, client);
+       kfree(client);
+  err_put_mousedev:
+       put_device(&mousedev->dev);
+@@ -517,41 +588,41 @@ static inline int mousedev_limit_delta(i
+       return delta > limit ? limit : (delta < -limit ? -limit : delta);
+ }
+-static void mousedev_packet(struct mousedev_client *client, signed char *ps2_data)
++static void mousedev_packet(struct mousedev_client *client,
++                          signed char *ps2_data)
+ {
+-      struct mousedev_motion *p;
+-      unsigned long flags;
+-
+-      spin_lock_irqsave(&client->packet_lock, flags);
+-      p = &client->packets[client->tail];
++      struct mousedev_motion *p = &client->packets[client->tail];
+-      ps2_data[0] = 0x08 | ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
++      ps2_data[0] = 0x08 |
++              ((p->dx < 0) << 4) | ((p->dy < 0) << 5) | (p->buttons & 0x07);
+       ps2_data[1] = mousedev_limit_delta(p->dx, 127);
+       ps2_data[2] = mousedev_limit_delta(p->dy, 127);
+       p->dx -= ps2_data[1];
+       p->dy -= ps2_data[2];
+       switch (client->mode) {
+-              case MOUSEDEV_EMUL_EXPS:
+-                      ps2_data[3] = mousedev_limit_delta(p->dz, 7);
+-                      p->dz -= ps2_data[3];
+-                      ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
+-                      client->bufsiz = 4;
+-                      break;
+-
+-              case MOUSEDEV_EMUL_IMPS:
+-                      ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
+-                      ps2_data[3] = mousedev_limit_delta(p->dz, 127);
+-                      p->dz -= ps2_data[3];
+-                      client->bufsiz = 4;
+-                      break;
+-
+-              case MOUSEDEV_EMUL_PS2:
+-              default:
+-                      ps2_data[0] |= ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
+-                      p->dz = 0;
+-                      client->bufsiz = 3;
+-                      break;
++      case MOUSEDEV_EMUL_EXPS:
++              ps2_data[3] = mousedev_limit_delta(p->dz, 7);
++              p->dz -= ps2_data[3];
++              ps2_data[3] = (ps2_data[3] & 0x0f) | ((p->buttons & 0x18) << 1);
++              client->bufsiz = 4;
++              break;
++
++      case MOUSEDEV_EMUL_IMPS:
++              ps2_data[0] |=
++                      ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
++              ps2_data[3] = mousedev_limit_delta(p->dz, 127);
++              p->dz -= ps2_data[3];
++              client->bufsiz = 4;
++              break;
++
++      case MOUSEDEV_EMUL_PS2:
++      default:
++              ps2_data[0] |=
++                      ((p->buttons & 0x10) >> 3) | ((p->buttons & 0x08) >> 1);
++              p->dz = 0;
++              client->bufsiz = 3;
++              break;
+       }
+       if (!p->dx && !p->dy && !p->dz) {
+@@ -561,12 +632,56 @@ static void mousedev_packet(struct mouse
+               } else
+                       client->tail = (client->tail + 1) % PACKET_QUEUE_LEN;
+       }
+-
+-      spin_unlock_irqrestore(&client->packet_lock, flags);
+ }
++static void mousedev_generate_response(struct mousedev_client *client,
++                                      int command)
++{
++      client->ps2[0] = 0xfa; /* ACK */
++
++      switch (command) {
+-static ssize_t mousedev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
++      case 0xeb: /* Poll */
++              mousedev_packet(client, &client->ps2[1]);
++              client->bufsiz++; /* account for leading ACK */
++              break;
++
++      case 0xf2: /* Get ID */
++              switch (client->mode) {
++              case MOUSEDEV_EMUL_PS2:
++                      client->ps2[1] = 0;
++                      break;
++              case MOUSEDEV_EMUL_IMPS:
++                      client->ps2[1] = 3;
++                      break;
++              case MOUSEDEV_EMUL_EXPS:
++                      client->ps2[1] = 4;
++                      break;
++              }
++              client->bufsiz = 2;
++              break;
++
++      case 0xe9: /* Get info */
++              client->ps2[1] = 0x60; client->ps2[2] = 3; client->ps2[3] = 200;
++              client->bufsiz = 4;
++              break;
++
++      case 0xff: /* Reset */
++              client->impsseq = client->imexseq = 0;
++              client->mode = MOUSEDEV_EMUL_PS2;
++              client->ps2[1] = 0xaa; client->ps2[2] = 0x00;
++              client->bufsiz = 3;
++              break;
++
++      default:
++              client->bufsiz = 1;
++              break;
++      }
++      client->buffer = client->bufsiz;
++}
++
++static ssize_t mousedev_write(struct file *file, const char __user *buffer,
++                              size_t count, loff_t *ppos)
+ {
+       struct mousedev_client *client = file->private_data;
+       unsigned char c;
+@@ -577,6 +692,8 @@ static ssize_t mousedev_write(struct fil
+               if (get_user(c, buffer + i))
+                       return -EFAULT;
++              spin_lock_irq(&client->packet_lock);
++
+               if (c == mousedev_imex_seq[client->imexseq]) {
+                       if (++client->imexseq == MOUSEDEV_SEQ_LEN) {
+                               client->imexseq = 0;
+@@ -593,68 +710,39 @@ static ssize_t mousedev_write(struct fil
+               } else
+                       client->impsseq = 0;
+-              client->ps2[0] = 0xfa;
+-
+-              switch (c) {
+-
+-                      case 0xeb: /* Poll */
+-                              mousedev_packet(client, &client->ps2[1]);
+-                              client->bufsiz++; /* account for leading ACK */
+-                              break;
+-
+-                      case 0xf2: /* Get ID */
+-                              switch (client->mode) {
+-                                      case MOUSEDEV_EMUL_PS2:  client->ps2[1] = 0; break;
+-                                      case MOUSEDEV_EMUL_IMPS: client->ps2[1] = 3; break;
+-                                      case MOUSEDEV_EMUL_EXPS: client->ps2[1] = 4; break;
+-                              }
+-                              client->bufsiz = 2;
+-                              break;
+-
+-                      case 0xe9: /* Get info */
+-                              client->ps2[1] = 0x60; client->ps2[2] = 3; client->ps2[3] = 200;
+-                              client->bufsiz = 4;
+-                              break;
+-
+-                      case 0xff: /* Reset */
+-                              client->impsseq = client->imexseq = 0;
+-                              client->mode = MOUSEDEV_EMUL_PS2;
+-                              client->ps2[1] = 0xaa; client->ps2[2] = 0x00;
+-                              client->bufsiz = 3;
+-                              break;
+-
+-                      default:
+-                              client->bufsiz = 1;
+-                              break;
+-              }
++              mousedev_generate_response(client, c);
+-              client->buffer = client->bufsiz;
++              spin_unlock_irq(&client->packet_lock);
+       }
+       kill_fasync(&client->fasync, SIGIO, POLL_IN);
+-
+       wake_up_interruptible(&client->mousedev->wait);
+       return count;
+ }
+-static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
++static ssize_t mousedev_read(struct file *file, char __user *buffer,
++                           size_t count, loff_t *ppos)
+ {
+       struct mousedev_client *client = file->private_data;
++      struct mousedev *mousedev = client->mousedev;
++      signed char data[sizeof(client->ps2)];
+       int retval = 0;
+-      if (!client->ready && !client->buffer && (file->f_flags & O_NONBLOCK))
++      if (!client->ready && !client->buffer && mousedev->exist &&
++          (file->f_flags & O_NONBLOCK))
+               return -EAGAIN;
+-      retval = wait_event_interruptible(client->mousedev->wait,
+-                                        !client->mousedev->exist || client->ready || client->buffer);
+-
++      retval = wait_event_interruptible(mousedev->wait,
++                      !mousedev->exist || client->ready || client->buffer);
+       if (retval)
+               return retval;
+-      if (!client->mousedev->exist)
++      if (!mousedev->exist)
+               return -ENODEV;
++      spin_lock_irq(&client->packet_lock);
++
+       if (!client->buffer && client->ready) {
+               mousedev_packet(client, client->ps2);
+               client->buffer = client->bufsiz;
+@@ -663,9 +751,12 @@ static ssize_t mousedev_read(struct file
+       if (count > client->buffer)
+               count = client->buffer;
++      memcpy(data, client->ps2 + client->bufsiz - client->buffer, count);
+       client->buffer -= count;
+-      if (copy_to_user(buffer, client->ps2 + client->bufsiz - client->buffer - count, count))
++      spin_unlock_irq(&client->packet_lock);
++
++      if (copy_to_user(buffer, data, count))
+               return -EFAULT;
+       return count;
+@@ -692,6 +783,60 @@ static const struct file_operations mous
+       .fasync =       mousedev_fasync,
+ };
++static int mousedev_install_chrdev(struct mousedev *mousedev)
++{
++      mousedev_table[mousedev->minor] = mousedev;
++      return 0;
++}
++
++static void mousedev_remove_chrdev(struct mousedev *mousedev)
++{
++      mutex_lock(&mousedev_table_mutex);
++      mousedev_table[mousedev->minor] = NULL;
++      mutex_unlock(&mousedev_table_mutex);
++}
++
++/*
++ * Mark device non-existent. This disables writes, ioctls and
++ * prevents new users from opening the device. Already posted
++ * blocking reads will stay, however new ones will fail.
++ */
++static void mousedev_mark_dead(struct mousedev *mousedev)
++{
++      mutex_lock(&mousedev->mutex);
++      mousedev->exist = 0;
++      mutex_unlock(&mousedev->mutex);
++}
++
++/*
++ * Wake up users waiting for IO so they can disconnect from
++ * dead device.
++ */
++static void mousedev_hangup(struct mousedev *mousedev)
++{
++      struct mousedev_client *client;
++
++      spin_lock(&mousedev->client_lock);
++      list_for_each_entry(client, &mousedev->client_list, node)
++              kill_fasync(&client->fasync, SIGIO, POLL_HUP);
++      spin_unlock(&mousedev->client_lock);
++
++      wake_up_interruptible(&mousedev->wait);
++}
++
++static void mousedev_cleanup(struct mousedev *mousedev)
++{
++      struct input_handle *handle = &mousedev->handle;
++
++      mousedev_mark_dead(mousedev);
++      mousedev_hangup(mousedev);
++      mousedev_remove_chrdev(mousedev);
++
++      /* mousedev is marked dead so no one else accesses mousedev->open */
++      if (mousedev->open)
++              input_close_device(handle);
++}
++
+ static struct mousedev *mousedev_create(struct input_dev *dev,
+                                       struct input_handler *handler,
+                                       int minor)
+@@ -707,6 +852,10 @@ static struct mousedev *mousedev_create(
+       INIT_LIST_HEAD(&mousedev->client_list);
+       INIT_LIST_HEAD(&mousedev->mixdev_node);
++      spin_lock_init(&mousedev->client_lock);
++      mutex_init(&mousedev->mutex);
++      lockdep_set_subclass(&mousedev->mutex,
++                           minor == MOUSEDEV_MIX ? MOUSEDEV_MIX : 0);
+       init_waitqueue_head(&mousedev->wait);
+       if (minor == MOUSEDEV_MIX)
+@@ -731,14 +880,27 @@ static struct mousedev *mousedev_create(
+       mousedev->dev.release = mousedev_free;
+       device_initialize(&mousedev->dev);
+-      mousedev_table[minor] = mousedev;
++      if (minor != MOUSEDEV_MIX) {
++              error = input_register_handle(&mousedev->handle);
++              if (error)
++                      goto err_free_mousedev;
++      }
++
++      error = mousedev_install_chrdev(mousedev);
++      if (error)
++              goto err_unregister_handle;
+       error = device_add(&mousedev->dev);
+       if (error)
+-              goto err_free_mousedev;
++              goto err_cleanup_mousedev;
+       return mousedev;
++ err_cleanup_mousedev:
++      mousedev_cleanup(mousedev);
++ err_unregister_handle:
++      if (minor != MOUSEDEV_MIX)
++              input_unregister_handle(&mousedev->handle);
+  err_free_mousedev:
+       put_device(&mousedev->dev);
+  err_out:
+@@ -747,29 +909,64 @@ static struct mousedev *mousedev_create(
+ static void mousedev_destroy(struct mousedev *mousedev)
+ {
+-      struct mousedev_client *client;
+-
+       device_del(&mousedev->dev);
+-      mousedev->exist = 0;
++      mousedev_cleanup(mousedev);
++      if (mousedev->minor != MOUSEDEV_MIX)
++              input_unregister_handle(&mousedev->handle);
++      put_device(&mousedev->dev);
++}
+-      if (mousedev->open) {
+-              input_close_device(&mousedev->handle);
+-              list_for_each_entry(client, &mousedev->client_list, node)
+-                      kill_fasync(&client->fasync, SIGIO, POLL_HUP);
+-              wake_up_interruptible(&mousedev->wait);
++static int mixdev_add_device(struct mousedev *mousedev)
++{
++      int retval;
++
++      retval = mutex_lock_interruptible(&mousedev_mix->mutex);
++      if (retval)
++              return retval;
++
++      if (mousedev_mix->open) {
++              retval = mousedev_open_device(mousedev);
++              if (retval)
++                      goto out;
++
++              mousedev->mixdev_open = 1;
+       }
++      get_device(&mousedev->dev);
++      list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list);
++
++ out:
++      mutex_unlock(&mousedev_mix->mutex);
++      return retval;
++}
++
++static void mixdev_remove_device(struct mousedev *mousedev)
++{
++      mutex_lock(&mousedev_mix->mutex);
++
++      if (mousedev->mixdev_open) {
++              mousedev->mixdev_open = 0;
++              mousedev_close_device(mousedev);
++      }
++
++      list_del_init(&mousedev->mixdev_node);
++      mutex_unlock(&mousedev_mix->mutex);
++
+       put_device(&mousedev->dev);
+ }
+-static int mousedev_connect(struct input_handler *handler, struct input_dev *dev,
++static int mousedev_connect(struct input_handler *handler,
++                          struct input_dev *dev,
+                           const struct input_device_id *id)
+ {
+       struct mousedev *mousedev;
+       int minor;
+       int error;
+-      for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++);
++      for (minor = 0; minor < MOUSEDEV_MINORS; minor++)
++              if (!mousedev_table[minor])
++                      break;
++
+       if (minor == MOUSEDEV_MINORS) {
+               printk(KERN_ERR "mousedev: no more free mousedev devices\n");
+               return -ENFILE;
+@@ -779,21 +976,13 @@ static int mousedev_connect(struct input
+       if (IS_ERR(mousedev))
+               return PTR_ERR(mousedev);
+-      error = input_register_handle(&mousedev->handle);
+-      if (error)
+-              goto err_delete_mousedev;
+-
+       error = mixdev_add_device(mousedev);
+-      if (error)
+-              goto err_unregister_handle;
++      if (error) {
++              mousedev_destroy(mousedev);
++              return error;
++      }
+       return 0;
+-
+- err_unregister_handle:
+-      input_unregister_handle(&mousedev->handle);
+- err_delete_mousedev:
+-      device_unregister(&mousedev->dev);
+-      return error;
+ }
+ static void mousedev_disconnect(struct input_handle *handle)
+@@ -801,33 +990,42 @@ static void mousedev_disconnect(struct i
+       struct mousedev *mousedev = handle->private;
+       mixdev_remove_device(mousedev);
+-      input_unregister_handle(handle);
+       mousedev_destroy(mousedev);
+ }
+ static const struct input_device_id mousedev_ids[] = {
+       {
+-              .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
++              .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
++                              INPUT_DEVICE_ID_MATCH_KEYBIT |
++                              INPUT_DEVICE_ID_MATCH_RELBIT,
+               .evbit = { BIT(EV_KEY) | BIT(EV_REL) },
+               .keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
+               .relbit = { BIT(REL_X) | BIT(REL_Y) },
+-      },      /* A mouse like device, at least one button, two relative axes */
++      },      /* A mouse like device, at least one button,
++                 two relative axes */
+       {
+-              .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
++              .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
++                              INPUT_DEVICE_ID_MATCH_RELBIT,
+               .evbit = { BIT(EV_KEY) | BIT(EV_REL) },
+               .relbit = { BIT(REL_WHEEL) },
+       },      /* A separate scrollwheel */
+       {
+-              .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
++              .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
++                              INPUT_DEVICE_ID_MATCH_KEYBIT |
++                              INPUT_DEVICE_ID_MATCH_ABSBIT,
+               .evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
+               .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
+               .absbit = { BIT(ABS_X) | BIT(ABS_Y) },
+-      },      /* A tablet like device, at least touch detection, two absolute axes */
++      },      /* A tablet like device, at least touch detection,
++                 two absolute axes */
+       {
+-              .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
++              .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
++                              INPUT_DEVICE_ID_MATCH_KEYBIT |
++                              INPUT_DEVICE_ID_MATCH_ABSBIT,
+               .evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
+               .keybit = { [LONG(BTN_TOOL_FINGER)] = BIT(BTN_TOOL_FINGER) },
+-              .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) },
++              .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) |
++                              BIT(ABS_TOOL_WIDTH) },
+       },      /* A touchpad */
+       { },    /* Terminating entry */
diff --git a/queue-2.6.23/input-tsdev-implement-proper-locking.patch b/queue-2.6.23/input-tsdev-implement-proper-locking.patch
new file mode 100644 (file)
index 0000000..3a332ae
--- /dev/null
@@ -0,0 +1,625 @@
+From b9d2d110b10f7b4788d0fdd328cf57e34b767817 Mon Sep 17 00:00:00 2001
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Date: Thu, 30 Aug 2007 00:22:39 -0400
+Subject: [PATCH] Input: tsdev - implement proper locking
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+patch b9d2d110b10f7b4788d0fdd328cf57e34b767817 in mainline.
+
+Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
+Cc: Al Viro <viro@ZenIV.linux.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/input/tsdev.c |  392 +++++++++++++++++++++++++++++++++++---------------
+ 1 file changed, 278 insertions(+), 114 deletions(-)
+
+--- a/drivers/input/tsdev.c
++++ b/drivers/input/tsdev.c
+@@ -112,6 +112,8 @@ struct tsdev {
+       struct input_handle handle;
+       wait_queue_head_t wait;
+       struct list_head client_list;
++      spinlock_t client_lock; /* protects client_list */
++      struct mutex mutex;
+       struct device dev;
+       int x, y, pressure;
+@@ -122,8 +124,9 @@ struct tsdev_client {
+       struct fasync_struct *fasync;
+       struct list_head node;
+       struct tsdev *tsdev;
++      struct ts_event buffer[TSDEV_BUFFER_SIZE];
+       int head, tail;
+-      struct ts_event event[TSDEV_BUFFER_SIZE];
++      spinlock_t buffer_lock; /* protects access to buffer, head and tail */
+       int raw;
+ };
+@@ -137,6 +140,7 @@ struct tsdev_client {
+ #define TS_SET_CAL    _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration)
+ static struct tsdev *tsdev_table[TSDEV_MINORS/2];
++static DEFINE_MUTEX(tsdev_table_mutex);
+ static int tsdev_fasync(int fd, struct file *file, int on)
+ {
+@@ -144,9 +148,91 @@ static int tsdev_fasync(int fd, struct f
+       int retval;
+       retval = fasync_helper(fd, file, on, &client->fasync);
++
+       return retval < 0 ? retval : 0;
+ }
++static void tsdev_free(struct device *dev)
++{
++      struct tsdev *tsdev = container_of(dev, struct tsdev, dev);
++
++      kfree(tsdev);
++}
++
++static void tsdev_attach_client(struct tsdev *tsdev, struct tsdev_client *client)
++{
++      spin_lock(&tsdev->client_lock);
++      list_add_tail_rcu(&client->node, &tsdev->client_list);
++      spin_unlock(&tsdev->client_lock);
++      synchronize_sched();
++}
++
++static void tsdev_detach_client(struct tsdev *tsdev, struct tsdev_client *client)
++{
++      spin_lock(&tsdev->client_lock);
++      list_del_rcu(&client->node);
++      spin_unlock(&tsdev->client_lock);
++      synchronize_sched();
++}
++
++static int tsdev_open_device(struct tsdev *tsdev)
++{
++      int retval;
++
++      retval = mutex_lock_interruptible(&tsdev->mutex);
++      if (retval)
++              return retval;
++
++      if (!tsdev->exist)
++              retval = -ENODEV;
++      else if (!tsdev->open++)
++              retval = input_open_device(&tsdev->handle);
++
++      mutex_unlock(&tsdev->mutex);
++      return retval;
++}
++
++static void tsdev_close_device(struct tsdev *tsdev)
++{
++      mutex_lock(&tsdev->mutex);
++
++      if (tsdev->exist && !--tsdev->open)
++              input_close_device(&tsdev->handle);
++
++      mutex_unlock(&tsdev->mutex);
++}
++
++/*
++ * Wake up users waiting for IO so they can disconnect from
++ * dead device.
++ */
++static void tsdev_hangup(struct tsdev *tsdev)
++{
++      struct tsdev_client *client;
++
++      spin_lock(&tsdev->client_lock);
++      list_for_each_entry(client, &tsdev->client_list, node)
++              kill_fasync(&client->fasync, SIGIO, POLL_HUP);
++      spin_unlock(&tsdev->client_lock);
++
++      wake_up_interruptible(&tsdev->wait);
++}
++
++static int tsdev_release(struct inode *inode, struct file *file)
++{
++      struct tsdev_client *client = file->private_data;
++      struct tsdev *tsdev = client->tsdev;
++
++      tsdev_fasync(-1, file, 0);
++      tsdev_detach_client(tsdev, client);
++      kfree(client);
++
++      tsdev_close_device(tsdev);
++      put_device(&tsdev->dev);
++
++      return 0;
++}
++
+ static int tsdev_open(struct inode *inode, struct file *file)
+ {
+       int i = iminor(inode) - TSDEV_MINOR_BASE;
+@@ -161,11 +247,16 @@ static int tsdev_open(struct inode *inod
+       if (i >= TSDEV_MINORS)
+               return -ENODEV;
++      error = mutex_lock_interruptible(&tsdev_table_mutex);
++      if (error)
++              return error;
+       tsdev = tsdev_table[i & TSDEV_MINOR_MASK];
+-      if (!tsdev || !tsdev->exist)
+-              return -ENODEV;
++      if (tsdev)
++              get_device(&tsdev->dev);
++      mutex_unlock(&tsdev_table_mutex);
+-      get_device(&tsdev->dev);
++      if (!tsdev)
++              return -ENODEV;
+       client = kzalloc(sizeof(struct tsdev_client), GFP_KERNEL);
+       if (!client) {
+@@ -173,51 +264,42 @@ static int tsdev_open(struct inode *inod
+               goto err_put_tsdev;
+       }
++      spin_lock_init(&client->buffer_lock);
+       client->tsdev = tsdev;
+-      client->raw = (i >= TSDEV_MINORS / 2) ? 1 : 0;
+-      list_add_tail(&client->node, &tsdev->client_list);
++      client->raw = i >= TSDEV_MINORS / 2;
++      tsdev_attach_client(tsdev, client);
+-      if (!tsdev->open++ && tsdev->exist) {
+-              error = input_open_device(&tsdev->handle);
+-              if (error)
+-                      goto err_free_client;
+-      }
++      error = tsdev_open_device(tsdev);
++      if (error)
++              goto err_free_client;
+       file->private_data = client;
+       return 0;
+  err_free_client:
+-      list_del(&client->node);
++      tsdev_detach_client(tsdev, client);
+       kfree(client);
+  err_put_tsdev:
+       put_device(&tsdev->dev);
+       return error;
+ }
+-static void tsdev_free(struct device *dev)
++static int tsdev_fetch_next_event(struct tsdev_client *client,
++                                struct ts_event *event)
+ {
+-      struct tsdev *tsdev = container_of(dev, struct tsdev, dev);
++      int have_event;
+-      tsdev_table[tsdev->minor] = NULL;
+-      kfree(tsdev);
+-}
+-
+-static int tsdev_release(struct inode *inode, struct file *file)
+-{
+-      struct tsdev_client *client = file->private_data;
+-      struct tsdev *tsdev = client->tsdev;
+-
+-      tsdev_fasync(-1, file, 0);
++      spin_lock_irq(&client->buffer_lock);
+-      list_del(&client->node);
+-      kfree(client);
+-
+-      if (!--tsdev->open && tsdev->exist)
+-              input_close_device(&tsdev->handle);
++      have_event = client->head != client->tail;
++      if (have_event) {
++              *event = client->buffer[client->tail++];
++              client->tail &= TSDEV_BUFFER_SIZE - 1;
++      }
+-      put_device(&tsdev->dev);
++      spin_unlock_irq(&client->buffer_lock);
+-      return 0;
++      return have_event;
+ }
+ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count,
+@@ -225,9 +307,11 @@ static ssize_t tsdev_read(struct file *f
+ {
+       struct tsdev_client *client = file->private_data;
+       struct tsdev *tsdev = client->tsdev;
+-      int retval = 0;
++      struct ts_event event;
++      int retval;
+-      if (client->head == client->tail && tsdev->exist && (file->f_flags & O_NONBLOCK))
++      if (client->head == client->tail && tsdev->exist &&
++          (file->f_flags & O_NONBLOCK))
+               return -EAGAIN;
+       retval = wait_event_interruptible(tsdev->wait,
+@@ -238,13 +322,14 @@ static ssize_t tsdev_read(struct file *f
+       if (!tsdev->exist)
+               return -ENODEV;
+-      while (client->head != client->tail &&
+-             retval + sizeof (struct ts_event) <= count) {
+-              if (copy_to_user (buffer + retval, client->event + client->tail,
+-                                sizeof (struct ts_event)))
++      while (retval + sizeof(struct ts_event) <= count &&
++             tsdev_fetch_next_event(client, &event)) {
++
++              if (copy_to_user(buffer + retval, &event,
++                               sizeof(struct ts_event)))
+                       return -EFAULT;
+-              client->tail = (client->tail + 1) & (TSDEV_BUFFER_SIZE - 1);
+-              retval += sizeof (struct ts_event);
++
++              retval += sizeof(struct ts_event);
+       }
+       return retval;
+@@ -261,14 +346,23 @@ static unsigned int tsdev_poll(struct fi
+               (tsdev->exist ? 0 : (POLLHUP | POLLERR));
+ }
+-static int tsdev_ioctl(struct inode *inode, struct file *file,
+-                     unsigned int cmd, unsigned long arg)
++static long tsdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ {
+       struct tsdev_client *client = file->private_data;
+       struct tsdev *tsdev = client->tsdev;
+       int retval = 0;
++      retval = mutex_lock_interruptible(&tsdev->mutex);
++      if (retval)
++              return retval;
++
++      if (!tsdev->exist) {
++              retval = -ENODEV;
++              goto out;
++      }
++
+       switch (cmd) {
++
+       case TS_GET_CAL:
+               if (copy_to_user((void __user *)arg, &tsdev->cal,
+                                sizeof (struct ts_calibration)))
+@@ -277,7 +371,7 @@ static int tsdev_ioctl(struct inode *ino
+       case TS_SET_CAL:
+               if (copy_from_user(&tsdev->cal, (void __user *)arg,
+-                                 sizeof (struct ts_calibration)))
++                                 sizeof(struct ts_calibration)))
+                       retval = -EFAULT;
+               break;
+@@ -286,29 +380,79 @@ static int tsdev_ioctl(struct inode *ino
+               break;
+       }
++ out:
++      mutex_unlock(&tsdev->mutex);
+       return retval;
+ }
+ static const struct file_operations tsdev_fops = {
+-      .owner =        THIS_MODULE,
+-      .open =         tsdev_open,
+-      .release =      tsdev_release,
+-      .read =         tsdev_read,
+-      .poll =         tsdev_poll,
+-      .fasync =       tsdev_fasync,
+-      .ioctl =        tsdev_ioctl,
++      .owner          = THIS_MODULE,
++      .open           = tsdev_open,
++      .release        = tsdev_release,
++      .read           = tsdev_read,
++      .poll           = tsdev_poll,
++      .fasync         = tsdev_fasync,
++      .unlocked_ioctl = tsdev_ioctl,
+ };
++static void tsdev_pass_event(struct tsdev *tsdev, struct tsdev_client *client,
++                           int x, int y, int pressure, int millisecs)
++{
++      struct ts_event *event;
++      int tmp;
++
++      /* Interrupts are already disabled, just acquire the lock */
++      spin_lock(&client->buffer_lock);
++
++      event = &client->buffer[client->head++];
++      client->head &= TSDEV_BUFFER_SIZE - 1;
++
++      /* Calibration */
++      if (!client->raw) {
++              x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans;
++              y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans;
++              if (tsdev->cal.xyswap) {
++                      tmp = x; x = y; y = tmp;
++              }
++      }
++
++      event->millisecs = millisecs;
++      event->x = x;
++      event->y = y;
++      event->pressure = pressure;
++
++      spin_unlock(&client->buffer_lock);
++
++      kill_fasync(&client->fasync, SIGIO, POLL_IN);
++}
++
++static void tsdev_distribute_event(struct tsdev *tsdev)
++{
++      struct tsdev_client *client;
++      struct timeval time;
++      int millisecs;
++
++      do_gettimeofday(&time);
++      millisecs = time.tv_usec / 1000;
++
++      list_for_each_entry_rcu(client, &tsdev->client_list, node)
++              tsdev_pass_event(tsdev, client,
++                               tsdev->x, tsdev->y,
++                               tsdev->pressure, millisecs);
++}
++
+ static void tsdev_event(struct input_handle *handle, unsigned int type,
+                       unsigned int code, int value)
+ {
+       struct tsdev *tsdev = handle->private;
+-      struct tsdev_client *client;
+-      struct timeval time;
++      struct input_dev *dev = handle->dev;
++      int wake_up_readers = 0;
+       switch (type) {
++
+       case EV_ABS:
+               switch (code) {
++
+               case ABS_X:
+                       tsdev->x = value;
+                       break;
+@@ -318,9 +462,9 @@ static void tsdev_event(struct input_han
+                       break;
+               case ABS_PRESSURE:
+-                      if (value > handle->dev->absmax[ABS_PRESSURE])
+-                              value = handle->dev->absmax[ABS_PRESSURE];
+-                      value -= handle->dev->absmin[ABS_PRESSURE];
++                      if (value > dev->absmax[ABS_PRESSURE])
++                              value = dev->absmax[ABS_PRESSURE];
++                      value -= dev->absmin[ABS_PRESSURE];
+                       if (value < 0)
+                               value = 0;
+                       tsdev->pressure = value;
+@@ -330,6 +474,7 @@ static void tsdev_event(struct input_han
+       case EV_REL:
+               switch (code) {
++
+               case REL_X:
+                       tsdev->x += value;
+                       if (tsdev->x < 0)
+@@ -351,6 +496,7 @@ static void tsdev_event(struct input_han
+       case EV_KEY:
+               if (code == BTN_TOUCH || code == BTN_MOUSE) {
+                       switch (value) {
++
+                       case 0:
+                               tsdev->pressure = 0;
+                               break;
+@@ -362,49 +508,71 @@ static void tsdev_event(struct input_han
+                       }
+               }
+               break;
++
++      case EV_SYN:
++              if (code == SYN_REPORT) {
++                      tsdev_distribute_event(tsdev);
++                      wake_up_readers = 1;
++              }
++              break;
+       }
+-      if (type != EV_SYN || code != SYN_REPORT)
+-              return;
++      if (wake_up_readers)
++              wake_up_interruptible(&tsdev->wait);
++}
+-      list_for_each_entry(client, &tsdev->client_list, node) {
+-              int x, y, tmp;
++static int tsdev_install_chrdev(struct tsdev *tsdev)
++{
++      tsdev_table[tsdev->minor] = tsdev;
++      return 0;
++}
+-              do_gettimeofday(&time);
+-              client->event[client->head].millisecs = time.tv_usec / 1000;
+-              client->event[client->head].pressure = tsdev->pressure;
+-
+-              x = tsdev->x;
+-              y = tsdev->y;
+-
+-              /* Calibration */
+-              if (!client->raw) {
+-                      x = ((x * tsdev->cal.xscale) >> 8) + tsdev->cal.xtrans;
+-                      y = ((y * tsdev->cal.yscale) >> 8) + tsdev->cal.ytrans;
+-                      if (tsdev->cal.xyswap) {
+-                              tmp = x; x = y; y = tmp;
+-                      }
+-              }
++static void tsdev_remove_chrdev(struct tsdev *tsdev)
++{
++      mutex_lock(&tsdev_table_mutex);
++      tsdev_table[tsdev->minor] = NULL;
++      mutex_unlock(&tsdev_table_mutex);
++}
+-              client->event[client->head].x = x;
+-              client->event[client->head].y = y;
+-              client->head = (client->head + 1) & (TSDEV_BUFFER_SIZE - 1);
+-              kill_fasync(&client->fasync, SIGIO, POLL_IN);
+-      }
+-      wake_up_interruptible(&tsdev->wait);
++/*
++ * Mark device non-existant. This disables writes, ioctls and
++ * prevents new users from opening the device. Already posted
++ * blocking reads will stay, however new ones will fail.
++ */
++static void tsdev_mark_dead(struct tsdev *tsdev)
++{
++      mutex_lock(&tsdev->mutex);
++      tsdev->exist = 0;
++      mutex_unlock(&tsdev->mutex);
++}
++
++static void tsdev_cleanup(struct tsdev *tsdev)
++{
++      struct input_handle *handle = &tsdev->handle;
++
++      tsdev_mark_dead(tsdev);
++      tsdev_hangup(tsdev);
++      tsdev_remove_chrdev(tsdev);
++
++      /* tsdev is marked dead so noone else accesses tsdev->open */
++      if (tsdev->open)
++              input_close_device(handle);
+ }
+ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev,
+                        const struct input_device_id *id)
+ {
+       struct tsdev *tsdev;
+-      int minor, delta;
++      int delta;
++      int minor;
+       int error;
+-      for (minor = 0; minor < TSDEV_MINORS / 2 && tsdev_table[minor]; minor++);
+-      if (minor >= TSDEV_MINORS / 2) {
+-              printk(KERN_ERR
+-                     "tsdev: You have way too many touchscreens\n");
++      for (minor = 0; minor < TSDEV_MINORS / 2; minor++)
++              if (!tsdev_table[minor])
++                      break;
++
++      if (minor == TSDEV_MINORS) {
++              printk(KERN_ERR "tsdev: no more free tsdev devices\n");
+               return -ENFILE;
+       }
+@@ -413,15 +581,18 @@ static int tsdev_connect(struct input_ha
+               return -ENOMEM;
+       INIT_LIST_HEAD(&tsdev->client_list);
++      spin_lock_init(&tsdev->client_lock);
++      mutex_init(&tsdev->mutex);
+       init_waitqueue_head(&tsdev->wait);
++      snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor);
+       tsdev->exist = 1;
+       tsdev->minor = minor;
++
+       tsdev->handle.dev = dev;
+       tsdev->handle.name = tsdev->name;
+       tsdev->handle.handler = handler;
+       tsdev->handle.private = tsdev;
+-      snprintf(tsdev->name, sizeof(tsdev->name), "ts%d", minor);
+       /* Precompute the rough calibration matrix */
+       delta = dev->absmax [ABS_X] - dev->absmin [ABS_X] + 1;
+@@ -436,28 +607,31 @@ static int tsdev_connect(struct input_ha
+       tsdev->cal.yscale = (yres << 8) / delta;
+       tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8);
+-      snprintf(tsdev->dev.bus_id, sizeof(tsdev->dev.bus_id),
+-               "ts%d", minor);
++      strlcpy(tsdev->dev.bus_id, tsdev->name, sizeof(tsdev->dev.bus_id));
++      tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor);
+       tsdev->dev.class = &input_class;
+       tsdev->dev.parent = &dev->dev;
+-      tsdev->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor);
+       tsdev->dev.release = tsdev_free;
+       device_initialize(&tsdev->dev);
+-      tsdev_table[minor] = tsdev;
+-
+-      error = device_add(&tsdev->dev);
++      error = input_register_handle(&tsdev->handle);
+       if (error)
+               goto err_free_tsdev;
+-      error = input_register_handle(&tsdev->handle);
++      error = tsdev_install_chrdev(tsdev);
+       if (error)
+-              goto err_delete_tsdev;
++              goto err_unregister_handle;
++
++      error = device_add(&tsdev->dev);
++      if (error)
++              goto err_cleanup_tsdev;
+       return 0;
+- err_delete_tsdev:
+-      device_del(&tsdev->dev);
++ err_cleanup_tsdev:
++      tsdev_cleanup(tsdev);
++ err_unregister_handle:
++      input_unregister_handle(&tsdev->handle);
+  err_free_tsdev:
+       put_device(&tsdev->dev);
+       return error;
+@@ -466,20 +640,10 @@ static int tsdev_connect(struct input_ha
+ static void tsdev_disconnect(struct input_handle *handle)
+ {
+       struct tsdev *tsdev = handle->private;
+-      struct tsdev_client *client;
+-      input_unregister_handle(handle);
+       device_del(&tsdev->dev);
+-
+-      tsdev->exist = 0;
+-
+-      if (tsdev->open) {
+-              input_close_device(handle);
+-              list_for_each_entry(client, &tsdev->client_list, node)
+-                      kill_fasync(&client->fasync, SIGIO, POLL_HUP);
+-              wake_up_interruptible(&tsdev->wait);
+-      }
+-
++      tsdev_cleanup(tsdev);
++      input_unregister_handle(handle);
+       put_device(&tsdev->dev);
+ }
+@@ -510,13 +674,13 @@ static const struct input_device_id tsde
+ MODULE_DEVICE_TABLE(input, tsdev_ids);
+ static struct input_handler tsdev_handler = {
+-      .event =        tsdev_event,
+-      .connect =      tsdev_connect,
+-      .disconnect =   tsdev_disconnect,
+-      .fops =         &tsdev_fops,
+-      .minor =        TSDEV_MINOR_BASE,
+-      .name =         "tsdev",
+-      .id_table =     tsdev_ids,
++      .event          = tsdev_event,
++      .connect        = tsdev_connect,
++      .disconnect     = tsdev_disconnect,
++      .fops           = &tsdev_fops,
++      .minor          = TSDEV_MINOR_BASE,
++      .name           = "tsdev",
++      .id_table       = tsdev_ids,
+ };
+ static int __init tsdev_init(void)
diff --git a/queue-2.6.23/input-use-full-rcu-api.patch b/queue-2.6.23/input-use-full-rcu-api.patch
new file mode 100644 (file)
index 0000000..3ef70a1
--- /dev/null
@@ -0,0 +1,286 @@
+From 82ba56c273911f7eda79849cfa0fc2d2e5a3b75b Mon Sep 17 00:00:00 2001
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Date: Sat, 13 Oct 2007 15:46:55 -0400
+Subject: [PATCH] Input: use full RCU API
+
+From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+
+patch 82ba56c273911f7eda79849cfa0fc2d2e5a3b75b in mainline.
+
+RT guys alerted me to the fact that in their tree spinlocks
+are preemptible and it is better to use full RCU API
+(rcu_read_lock()/rcu_read_unlock()) to be safe.
+
+Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
+Cc: Al Viro <viro@ZenIV.linux.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/input/evdev.c    |   22 +++++++++-------------
+ drivers/input/input.c    |   36 +++++++++++++++---------------------
+ drivers/input/joydev.c   |   11 ++++-------
+ drivers/input/mousedev.c |   11 ++++-------
+ 4 files changed, 32 insertions(+), 48 deletions(-)
+
+--- a/drivers/input/evdev.c
++++ b/drivers/input/evdev.c
+@@ -63,10 +63,7 @@ static void evdev_pass_event(struct evde
+ }
+ /*
+- * Pass incoming event to all connected clients. Note that we are
+- * caleld under a spinlock with interrupts off so we don't need
+- * to use rcu_read_lock() here. Writers will be using syncronize_sched()
+- * instead of synchrnoize_rcu().
++ * Pass incoming event to all connected clients.
+  */
+ static void evdev_event(struct input_handle *handle,
+                       unsigned int type, unsigned int code, int value)
+@@ -80,6 +77,8 @@ static void evdev_event(struct input_han
+       event.code = code;
+       event.value = value;
++      rcu_read_lock();
++
+       client = rcu_dereference(evdev->grab);
+       if (client)
+               evdev_pass_event(client, &event);
+@@ -87,6 +86,8 @@ static void evdev_event(struct input_han
+               list_for_each_entry_rcu(client, &evdev->client_list, node)
+                       evdev_pass_event(client, &event);
++      rcu_read_unlock();
++
+       wake_up_interruptible(&evdev->wait);
+ }
+@@ -142,12 +143,7 @@ static int evdev_grab(struct evdev *evde
+               return error;
+       rcu_assign_pointer(evdev->grab, client);
+-      /*
+-       * We don't use synchronize_rcu() here because read-side
+-       * critical section is protected by a spinlock instead
+-       * of rcu_read_lock().
+-       */
+-      synchronize_sched();
++      synchronize_rcu();
+       return 0;
+ }
+@@ -158,7 +154,7 @@ static int evdev_ungrab(struct evdev *ev
+               return  -EINVAL;
+       rcu_assign_pointer(evdev->grab, NULL);
+-      synchronize_sched();
++      synchronize_rcu();
+       input_release_device(&evdev->handle);
+       return 0;
+@@ -170,7 +166,7 @@ static void evdev_attach_client(struct e
+       spin_lock(&evdev->client_lock);
+       list_add_tail_rcu(&client->node, &evdev->client_list);
+       spin_unlock(&evdev->client_lock);
+-      synchronize_sched();
++      synchronize_rcu();
+ }
+ static void evdev_detach_client(struct evdev *evdev,
+@@ -179,7 +175,7 @@ static void evdev_detach_client(struct e
+       spin_lock(&evdev->client_lock);
+       list_del_rcu(&client->node);
+       spin_unlock(&evdev->client_lock);
+-      synchronize_sched();
++      synchronize_rcu();
+ }
+ static int evdev_open_device(struct evdev *evdev)
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -65,16 +65,16 @@ static int input_defuzz_abs_event(int va
+ /*
+  * Pass event through all open handles. This function is called with
+- * dev->event_lock held and interrupts disabled. Because of that we
+- * do not need to use rcu_read_lock() here although we are using RCU
+- * to access handle list. Note that because of that write-side uses
+- * synchronize_sched() instead of synchronize_ru().
++ * dev->event_lock held and interrupts disabled.
+  */
+ static void input_pass_event(struct input_dev *dev,
+                            unsigned int type, unsigned int code, int value)
+ {
+-      struct input_handle *handle = rcu_dereference(dev->grab);
++      struct input_handle *handle;
++
++      rcu_read_lock();
++      handle = rcu_dereference(dev->grab);
+       if (handle)
+               handle->handler->event(handle, type, code, value);
+       else
+@@ -82,6 +82,7 @@ static void input_pass_event(struct inpu
+                       if (handle->open)
+                               handle->handler->event(handle,
+                                                       type, code, value);
++      rcu_read_unlock();
+ }
+ /*
+@@ -293,9 +294,11 @@ void input_inject_event(struct input_han
+       if (is_event_supported(type, dev->evbit, EV_MAX)) {
+               spin_lock_irqsave(&dev->event_lock, flags);
++              rcu_read_lock();
+               grab = rcu_dereference(dev->grab);
+               if (!grab || grab == handle)
+                       input_handle_event(dev, type, code, value);
++              rcu_read_unlock();
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+       }
+@@ -325,11 +328,7 @@ int input_grab_device(struct input_handl
+       }
+       rcu_assign_pointer(dev->grab, handle);
+-      /*
+-       * Not using synchronize_rcu() because read-side is protected
+-       * by a spinlock with interrupts off instead of rcu_read_lock().
+-       */
+-      synchronize_sched();
++      synchronize_rcu();
+  out:
+       mutex_unlock(&dev->mutex);
+@@ -344,7 +343,7 @@ static void __input_release_device(struc
+       if (dev->grab == handle) {
+               rcu_assign_pointer(dev->grab, NULL);
+               /* Make sure input_pass_event() notices that grab is gone */
+-              synchronize_sched();
++              synchronize_rcu();
+               list_for_each_entry(handle, &dev->h_list, d_node)
+                       if (handle->open && handle->handler->start)
+@@ -404,7 +403,7 @@ int input_open_device(struct input_handl
+                        * Make sure we are not delivering any more events
+                        * through this handle
+                        */
+-                      synchronize_sched();
++                      synchronize_rcu();
+               }
+       }
+@@ -451,11 +450,11 @@ void input_close_device(struct input_han
+       if (!--handle->open) {
+               /*
+-               * synchronize_sched() makes sure that input_pass_event()
++               * synchronize_rcu() makes sure that input_pass_event()
+                * completed and that no more input events are delivered
+                * through this handle
+                */
+-              synchronize_sched();
++              synchronize_rcu();
+       }
+       mutex_unlock(&dev->mutex);
+@@ -1499,12 +1498,7 @@ int input_register_handle(struct input_h
+               return error;
+       list_add_tail_rcu(&handle->d_node, &dev->h_list);
+       mutex_unlock(&dev->mutex);
+-      /*
+-       * We don't use synchronize_rcu() here because we rely
+-       * on dev->event_lock to protect read-side critical
+-       * section in input_pass_event().
+-       */
+-      synchronize_sched();
++      synchronize_rcu();
+       /*
+        * Since we are supposed to be called from ->connect()
+@@ -1543,7 +1537,7 @@ void input_unregister_handle(struct inpu
+       mutex_lock(&dev->mutex);
+       list_del_rcu(&handle->d_node);
+       mutex_unlock(&dev->mutex);
+-      synchronize_sched();
++      synchronize_rcu();
+ }
+ EXPORT_SYMBOL(input_unregister_handle);
+--- a/drivers/input/joydev.c
++++ b/drivers/input/joydev.c
+@@ -149,8 +149,10 @@ static void joydev_event(struct input_ha
+       event.time = jiffies_to_msecs(jiffies);
++      rcu_read_lock();
+       list_for_each_entry_rcu(client, &joydev->client_list, node)
+               joydev_pass_event(client, &event);
++      rcu_read_unlock();
+       wake_up_interruptible(&joydev->wait);
+ }
+@@ -178,12 +180,7 @@ static void joydev_attach_client(struct 
+       spin_lock(&joydev->client_lock);
+       list_add_tail_rcu(&client->node, &joydev->client_list);
+       spin_unlock(&joydev->client_lock);
+-      /*
+-       * We don't use synchronize_rcu() here because read-side
+-       * critical section is protected by a spinlock (dev->event_lock)
+-       * instead of rcu_read_lock().
+-       */
+-      synchronize_sched();
++      synchronize_rcu();
+ }
+ static void joydev_detach_client(struct joydev *joydev,
+@@ -192,7 +189,7 @@ static void joydev_detach_client(struct 
+       spin_lock(&joydev->client_lock);
+       list_del_rcu(&client->node);
+       spin_unlock(&joydev->client_lock);
+-      synchronize_sched();
++      synchronize_rcu();
+ }
+ static int joydev_open_device(struct joydev *joydev)
+--- a/drivers/input/mousedev.c
++++ b/drivers/input/mousedev.c
+@@ -265,6 +265,7 @@ static void mousedev_notify_readers(stru
+       unsigned int new_head;
+       int wake_readers = 0;
++      rcu_read_lock();
+       list_for_each_entry_rcu(client, &mousedev->client_list, node) {
+               /* Just acquire the lock, interrupts already disabled */
+@@ -309,6 +310,7 @@ static void mousedev_notify_readers(stru
+                       wake_readers = 1;
+               }
+       }
++      rcu_read_unlock();
+       if (wake_readers)
+               wake_up_interruptible(&mousedev->wait);
+@@ -496,12 +498,7 @@ static void mousedev_attach_client(struc
+       spin_lock(&mousedev->client_lock);
+       list_add_tail_rcu(&client->node, &mousedev->client_list);
+       spin_unlock(&mousedev->client_lock);
+-      /*
+-       * We don't use synchronize_rcu() here because read-side
+-       * critical section is protected by a spinlock (dev->event_lock)
+-       * instead of rcu_read_lock().
+-       */
+-      synchronize_sched();
++      synchronize_rcu();
+ }
+ static void mousedev_detach_client(struct mousedev *mousedev,
+@@ -510,7 +507,7 @@ static void mousedev_detach_client(struc
+       spin_lock(&mousedev->client_lock);
+       list_del_rcu(&client->node);
+       spin_unlock(&mousedev->client_lock);
+-      synchronize_sched();
++      synchronize_rcu();
+ }
+ static int mousedev_release(struct inode *inode, struct file *file)
index be04d5c1574774f8e7e367a96d9347326d4060d9..81f8e5700d0f4e15192480d9643dc5d70e0044de 100644 (file)
@@ -46,3 +46,10 @@ quicklists-only-consider-memory-that-can-be-used-with-gfp_kernel.patch
 chelsio-fix-skb-dev-setting.patch
 cxgb-fix-t2-gso.patch
 cxgb-fix-stats.patch
+input-implement-proper-locking-in-input-core.patch
+input-evdev-implement-proper-locking.patch
+input-mousedev-implement-proper-locking.patch
+input-joydev-implement-proper-locking.patch
+input-tsdev-implement-proper-locking.patch
+input-use-full-rcu-api.patch
+input-fix-open-count-handling-in-input-interfaces.patch