diff --git a/drivers/input/input.c b/drivers/input/input.c index ccaeb2426..30d8da5fb 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -139,6 +139,13 @@ static void input_pass_values(struct input_dev *dev, handle = rcu_dereference(dev->grab); if (handle) { count = input_to_handler(handle, vals, count); + list_for_each_entry_rcu(handle, &dev->h_list, d_node) + if (handle->open) { + if(!strncmp(handle->name, "mouse", 5)) { + count = input_to_handler(handle, vals, count); + if (!count) break; + } + } } else { list_for_each_entry_rcu(handle, &dev->h_list, d_node) if (handle->open) { diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 19a8e1533..97d4f9c7a 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -59,6 +59,7 @@ struct mousedev { int open; struct input_handle handle; wait_queue_head_t wait; + struct mousedev_client __rcu *grab; struct list_head client_list; spinlock_t client_lock; /* protects client_list */ struct mutex mutex; @@ -260,64 +261,142 @@ static void mousedev_key_event(struct mousedev *mousedev, } } -static void mousedev_notify_readers(struct mousedev *mousedev, +static int mousedev_notify_reader(struct mousedev_client *client, struct mousedev *mousedev, struct mousedev_hw_data *packet) { - struct mousedev_client *client; struct mousedev_motion *p; 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 */ + spin_lock(&client->packet_lock); - /* 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) { - 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)); - } - } - - if (packet->abs_event) { - p->dx += packet->x - client->pos_x; - p->dy += packet->y - client->pos_y; - client->pos_x = packet->x; - client->pos_y = packet->y; - } - - client->pos_x += packet->dx; - client->pos_x = clamp_val(client->pos_x, 0, xres); - - client->pos_y += packet->dy; - client->pos_y = clamp_val(client->pos_y, 0, yres); - - 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) - client->ready = 1; - - spin_unlock(&client->packet_lock); - - if (client->ready) { - kill_fasync(&client->fasync, SIGIO, POLL_IN); - wake_readers = 1; + p = &client->packets[client->head]; + if (client->ready && p->buttons != mousedev->packet.buttons) { + 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)); } } + + if (packet->abs_event) { + p->dx += packet->x - client->pos_x; + p->dy += packet->y - client->pos_y; + client->pos_x = packet->x; + client->pos_y = packet->y; + } + + client->pos_x += packet->dx; + client->pos_x = clamp_val(client->pos_x, 0, xres); + + client->pos_y += packet->dy; + client->pos_y = clamp_val(client->pos_y, 0, yres); + + 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) + client->ready = 1; + + spin_unlock(&client->packet_lock); + + if (client->ready) { + kill_fasync(&client->fasync, SIGIO, POLL_IN); + wake_readers = 1; + } + + return wake_readers; +} + +static void mousedev_notify_readers(struct mousedev *mousedev, + struct mousedev_hw_data *packet) +{ + struct mousedev_client *client; + int wake_readers = 0; + + rcu_read_lock(); + + client = rcu_dereference(mousedev->grab); + if(client) + { + wake_readers = mousedev_notify_reader(client, mousedev, packet); + } + else + { + list_for_each_entry_rcu(client, &mousedev->client_list, node) + { + if(mousedev_notify_reader(client, mousedev, packet)) wake_readers = 1; + } + } + rcu_read_unlock(); if (wake_readers) wake_up_interruptible(&mousedev->wait); } +static int mousedev_grab(struct mousedev *mousedev, struct mousedev_client *client) +{ + if (mousedev->grab) return -EBUSY; + rcu_assign_pointer(mousedev->grab, client); + return 0; +} + +static int mousedev_ungrab(struct mousedev *mousedev, struct mousedev_client *client) +{ + struct mousedev_client *grab = rcu_dereference_protected(mousedev->grab, + lockdep_is_held(&mousedev->mutex)); + + if (grab != client) return -EINVAL; + rcu_assign_pointer(mousedev->grab, NULL); + synchronize_rcu(); + return 0; +} + +static long mousedev_do_ioctl(struct file *file, unsigned int cmd, + void __user *p, int compat_mode) +{ + struct mousedev_client *client = file->private_data; + struct mousedev *mousedev = client->mousedev; + + if(cmd == EVIOCGRAB) + { + if (p) + return mousedev_grab(mousedev, client); + else + return mousedev_ungrab(mousedev, client); + } + + return -EINVAL; +} + +static long mousedev_ioctl_handler(struct file *file, unsigned int cmd, + void __user *p, int compat_mode) +{ + struct mousedev_client *client = file->private_data; + struct mousedev *mousedev = client->mousedev; + int retval; + + retval = mutex_lock_interruptible(&mousedev->mutex); + if (retval) + return retval; + + if (!mousedev->exist) { + retval = -ENODEV; + goto out; + } + + retval = mousedev_do_ioctl(file, cmd, p, compat_mode); + + out: + mutex_unlock(&mousedev->mutex); + return retval; +} + static void mousedev_touchpad_touch(struct mousedev *mousedev, int value) { if (!value) { @@ -530,6 +609,10 @@ static int mousedev_release(struct inode *inode, struct file *file) struct mousedev_client *client = file->private_data; struct mousedev *mousedev = client->mousedev; + mutex_lock(&mousedev->mutex); + mousedev_ungrab(mousedev, client); + mutex_unlock(&mousedev->mutex); + mousedev_detach_client(mousedev, client); kfree(client); @@ -778,6 +861,19 @@ static __poll_t mousedev_poll(struct file *file, poll_table *wait) return mask; } +static long mousedev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + return mousedev_ioctl_handler(file, cmd, (void __user *)arg, 0); +} + +#ifdef CONFIG_COMPAT +static long mousedev_ioctl_compat(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return mousedev_ioctl_handler(file, cmd, compat_ptr(arg), 1); +} +#endif + static const struct file_operations mousedev_fops = { .owner = THIS_MODULE, .read = mousedev_read, @@ -785,6 +881,10 @@ static const struct file_operations mousedev_fops = { .poll = mousedev_poll, .open = mousedev_open, .release = mousedev_release, + .unlocked_ioctl = mousedev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = mousedev_ioctl_compat, +#endif .fasync = mousedev_fasync, .llseek = noop_llseek, };