diff options
Diffstat (limited to 'drivers/input')
23 files changed, 3399 insertions, 1097 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index f1c3d6cebd5..27026f7d3c0 100644 --- 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); + do_gettimeofday(&event.time); + event.type = type; + event.code = code; + event.value = value; - kill_fasync(&client->fasync, SIGIO, POLL_IN); - } + 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,145 @@ static int evdev_flush(struct file *file, fl_owner_t id) { 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); + if (retval) + evdev->open--; + } + + 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 +250,44 @@ static int evdev_release(struct inode *inode, struct file *file) 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 +323,14 @@ static inline size_t evdev_event_size(void) 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 +347,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event * 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 +359,8 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev 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 +378,8 @@ static inline size_t evdev_event_size(void) 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 +387,8 @@ static int evdev_event_from_user(const char __user *buffer, struct input_event * 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 +398,71 @@ static int evdev_event_to_user(char __user *buffer, const struct input_event *ev #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 +473,12 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, if (!evdev->exist) return -ENODEV; - while (client->head != client->tail && retval + evdev_event_size() <= count) { + while (retval + evdev_event_size() <= count && + evdev_fetch_next_event(client, &event)) { - struct input_event *event = (struct input_event *) client->buffer + client->tail; - - 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 +573,8 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p) 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,215 +585,289 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, 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 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 EVIOCGEFFECTS: + i = test_bit(EV_FF, dev->evbit) ? + dev->ff->max_effects : 0; + if (put_user(i, ip)) + return -EFAULT; + return 0; - default: + case EVIOCGRAB: + if (p) + return evdev_grab(evdev, client); + else + return evdev_ungrab(evdev, client); - if (_IOC_TYPE(cmd) != 'E') - return -EINVAL; + default: - if (_IOC_DIR(cmd) == _IOC_READ) { + if (_IOC_TYPE(cmd) != 'E') + return -EINVAL; - if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { + if (_IOC_DIR(cmd) == _IOC_READ) { - unsigned long *bits; - int len; + if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) { - 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); - } + unsigned long *bits; + int len; - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0))) - return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd), - p, compat_mode); + switch (_IOC_NR(cmd) & EV_MAX) { - if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0))) - return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd), - p, compat_mode); + 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(EVIOCGSND(0))) - return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd), - p, compat_mode); + 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(EVIOCGSW(0))) - return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd), - p, compat_mode); + 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) == _IOC_NR(EVIOCGNAME(0))) - return str_to_user(dev->name, _IOC_SIZE(cmd), p); + 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) == _IOC_NR(EVIOCGPHYS(0))) - return str_to_user(dev->phys, _IOC_SIZE(cmd), p); + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0))) + return bits_to_user(dev->sw, SW_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(EVIOCGNAME(0))) + return str_to_user(dev->name, _IOC_SIZE(cmd), p); - if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0))) + return str_to_user(dev->phys, _IOC_SIZE(cmd), p); - t = _IOC_NR(cmd) & ABS_MAX; + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0))) + return str_to_user(dev->uniq, _IOC_SIZE(cmd), p); - 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) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { - if (copy_to_user(p, &abs, sizeof(struct input_absinfo))) - return -EFAULT; + t = _IOC_NR(cmd) & ABS_MAX; - return 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]; + if (copy_to_user(p, &abs, sizeof(struct input_absinfo))) + return -EFAULT; + + return 0; } - if (_IOC_DIR(cmd) == _IOC_WRITE) { + } - if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { + if (_IOC_DIR(cmd) == _IOC_WRITE) { - t = _IOC_NR(cmd) & ABS_MAX; + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { - if (copy_from_user(&abs, p, sizeof(struct input_absinfo))) - return -EFAULT; + t = _IOC_NR(cmd) & ABS_MAX; - 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; + if (copy_from_user(&abs, p, + sizeof(struct input_absinfo))) + return -EFAULT; - return 0; - } + /* + * 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; + + spin_unlock_irq(&dev->event_lock); + + return 0; } + } } return -EINVAL; } +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; + + retval = mutex_lock_interruptible(&evdev->mutex); + if (retval) + return retval; + + if (!evdev->exist) { + retval = -ENODEV; + goto out; + } + + retval = evdev_do_ioctl(file, cmd, p, compat_mode); + + out: + mutex_unlock(&evdev->mutex); + return retval; +} + static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0); } #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 +875,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, 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 +889,44 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, 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 +935,10 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, 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 +950,13 @@ static const struct input_device_id evdev_ids[] = { 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/drivers/input/input-polldev.c b/drivers/input/input-polldev.c index b773d4c756a..92b359894e8 100644 --- a/drivers/input/input-polldev.c +++ b/drivers/input/input-polldev.c @@ -70,6 +70,7 @@ static int input_open_polled_device(struct input_dev *input) { struct input_polled_dev *dev = input->private; int error; + unsigned long ticks; error = input_polldev_start_workqueue(); if (error) @@ -78,8 +79,10 @@ static int input_open_polled_device(struct input_dev *input) if (dev->flush) dev->flush(dev); - queue_delayed_work(polldev_wq, &dev->work, - msecs_to_jiffies(dev->poll_interval)); + ticks = msecs_to_jiffies(dev->poll_interval); + if (ticks >= HZ) + ticks = round_jiffies(ticks); + queue_delayed_work(polldev_wq, &dev->work, ticks); return 0; } diff --git a/drivers/input/input.c b/drivers/input/input.c index 5dc361c954e..3070c7aa123 100644 --- 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; + switch (type) { - if ((value > dev->abs[code] - dev->absfuzz[code]) && - (value < dev->abs[code] + dev->absfuzz[code])) - value = (dev->abs[code] * 3 + value) >> 2; + case EV_SYN: + switch (code) { + case SYN_CONFIG: + disposition = INPUT_PASS_TO_ALL; + break; - if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) && - (value < dev->abs[code] + (dev->absfuzz[code] << 1))) - value = (dev->abs[code] + value) >> 1; + case SYN_REPORT: + if (!dev->sync) { + dev->sync = 1; + disposition = INPUT_PASS_TO_HANDLERS; } - - if (dev->abs[code] == value) - return; - - dev->abs[code] = value; 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; - if (dev->event) - dev->event(dev, type, code, value); + case EV_ABS: + if (is_event_supported(code, dev->absbit, ABS_MAX)) { - break; + value = input_defuzz_abs_event(value, + dev->abs[code], dev->absfuzz[code]); - case EV_LED: + if (dev->abs[code] != value) { + dev->abs[code] = value; + disposition = INPUT_PASS_TO_HANDLERS; + } + } + break; - if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value) - return; + case EV_REL: + if (is_event_supported(code, dev->relbit, REL_MAX) && value) + disposition = INPUT_PASS_TO_HANDLERS; - change_bit(code, dev->led); + break; - if (dev->event) - dev->event(dev, type, code, value); + case EV_MSC: + if (is_event_supported(code, dev->mscbit, MSC_MAX)) + disposition = INPUT_PASS_TO_ALL; - 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); - - if (err) - handle->open--; + retval = dev->open(dev); + + 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_poll(struct file *file, poll_table *wait) 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(struct seq_file *seq, void *v, loff_t *pos) 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 input_devices_fileops = { 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(struct seq_file *seq, void *v, loff_t *pos) 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) @@ -983,6 +1191,7 @@ struct input_dev *input_allocate_device(void) 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); @@ -1000,7 +1209,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(). * @@ -1070,6 +1279,18 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int } 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); @@ -1077,7 +1298,7 @@ int input_register_device(struct input_dev *dev) 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 @@ -1098,8 +1319,6 @@ int input_register_device(struct input_dev *dev) 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); @@ -1115,49 +1334,79 @@ int input_register_device(struct input_dev *dev) 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; } @@ -1167,14 +1416,26 @@ int input_register_handler(struct input_handler *handler) 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)); @@ -1185,14 +1446,50 @@ void input_unregister_handler(struct input_handler *handler) 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) @@ -1202,10 +1499,29 @@ int input_register_handle(struct input_handle *handle) } 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); diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index a9a0180bfd4..f306c97f556 100644 --- 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_handle *handle, unsigned int type, unsigne 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,88 @@ static void joydev_free(struct device *dev) { 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); + if (retval) + joydev->open--; + } + + 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 +266,16 @@ static int joydev_open(struct inode *inode, struct file *file) 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 +283,129 @@ static int joydev_open(struct inode *inode, struct file *file) 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; +} + +/* + * 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) +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 +413,35 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo 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; + if (count == sizeof(struct JS_DATA_TYPE)) + return joydev_0x_read(client, input, buf); - return sizeof(struct JS_DATA_TYPE); - } - - 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 +455,144 @@ static unsigned int joydev_poll(struct file *file, poll_table *wait) 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 JSIOCGAXES: + return put_user(joydev->nabs, (__u8 __user *) argp); - case JSIOCGBUTTONS: - return put_user(joydev->nkey, (__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 JSIOCSCORR: + if (copy_from_user(joydev->corr, argp, + sizeof(joydev->corr[0]) * joydev->nabs)) + return -EFAULT; - case JSIOCGCORR: - return copy_to_user(argp, joydev->corr, - sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0; + 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 JSIOCSBTNMAP: - if (copy_from_user(joydev->keypam, argp, sizeof(__u16) * (KEY_MAX - BTN_MISC + 1))) + 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; + } + + 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 +610,119 @@ static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo 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; + + case JS_GET_ALL: + retval = copy_to_user(argp, &joydev->glue, + 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); + 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 +730,10 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, 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 +744,19 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, 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 +788,65 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, } 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->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); + 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); + } } - 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 +855,20 @@ static const struct input_device_id joydev_blacklist[] = { 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 +878,14 @@ static const struct input_device_id joydev_ids[] = { 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/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 28080395899..623629a69b0 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -223,12 +223,16 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d struct input_dev *dev = xpad->dev; /* left stick */ - input_report_abs(dev, ABS_X, (__s16) (((__s16)data[13] << 8) | data[12])); - input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[15] << 8) | data[14])); + input_report_abs(dev, ABS_X, + (__s16) le16_to_cpup((__le16 *)(data + 12))); + input_report_abs(dev, ABS_Y, + (__s16) le16_to_cpup((__le16 *)(data + 14))); /* right stick */ - input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[17] << 8) | data[16])); - input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[19] << 8) | data[18])); + input_report_abs(dev, ABS_RX, + (__s16) le16_to_cpup((__le16 *)(data + 16))); + input_report_abs(dev, ABS_RY, + (__s16) le16_to_cpup((__le16 *)(data + 18))); /* triggers left/right */ input_report_abs(dev, ABS_Z, data[10]); @@ -236,8 +240,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d /* digital pad */ if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) { - input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); - input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01)); + input_report_abs(dev, ABS_HAT0X, + !!(data[2] & 0x08) - !!(data[2] & 0x04)); + input_report_abs(dev, ABS_HAT0Y, + !!(data[2] & 0x02) - !!(data[2] & 0x01)); } else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ { input_report_key(dev, BTN_LEFT, data[2] & 0x04); input_report_key(dev, BTN_RIGHT, data[2] & 0x08); @@ -274,14 +280,17 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d * http://www.free60.org/wiki/Gamepad */ -static void xpad360_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) +static void xpad360_process_packet(struct usb_xpad *xpad, + u16 cmd, unsigned char *data) { struct input_dev *dev = xpad->dev; /* digital pad */ if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) { - input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); - input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01)); + input_report_abs(dev, ABS_HAT0X, + !!(data[2] & 0x08) - !!(data[2] & 0x04)); + input_report_abs(dev, ABS_HAT0Y, + !!(data[2] & 0x02) - !!(data[2] & 0x01)); } else if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS) { /* dpad as buttons (right, left, down, up) */ input_report_key(dev, BTN_LEFT, data[2] & 0x04); @@ -308,12 +317,16 @@ static void xpad360_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char input_report_key(dev, BTN_MODE, data[3] & 0x04); /* left stick */ - input_report_abs(dev, ABS_X, (__s16) (((__s16)data[7] << 8) | (__s16)data[6])); - input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[9] << 8) | (__s16)data[8])); + input_report_abs(dev, ABS_X, + (__s16) le16_to_cpup((__le16 *)(data + 6))); + input_report_abs(dev, ABS_Y, + (__s16) le16_to_cpup((__le16 *)(data + 8))); /* right stick */ - input_report_abs(dev, ABS_RX, (__s16) (((__s16)data[11] << 8) | (__s16)data[10])); - input_report_abs(dev, ABS_RY, (__s16) (((__s16)data[13] << 8) | (__s16)data[12])); + input_report_abs(dev, ABS_RX, + (__s16) le16_to_cpup((__le16 *)(data + 10))); + input_report_abs(dev, ABS_RY, + (__s16) le16_to_cpup((__le16 *)(data + 12))); /* triggers left/right */ input_report_abs(dev, ABS_Z, data[4]); @@ -335,10 +348,12 @@ static void xpad_irq_in(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); return; default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); goto exit; } @@ -367,10 +382,12 @@ static void xpad_irq_out(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", + __FUNCTION__, urb->status); return; default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + dbg("%s - nonzero urb status received: %d", + __FUNCTION__, urb->status); goto exit; } @@ -378,7 +395,7 @@ exit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) err("%s - usb_submit_urb failed with result %d", - __FUNCTION__, retval); + __FUNCTION__, retval); } static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) @@ -595,7 +612,7 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs) static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_device *udev = interface_to_usbdev (intf); + struct usb_device *udev = interface_to_usbdev(intf); struct usb_xpad *xpad; struct input_dev *input_dev; struct usb_endpoint_descriptor *ep_irq_in; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index c97d5eb0075..f6f79e880b2 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -208,6 +208,27 @@ config KEYBOARD_HIL This driver implements support for HIL-keyboards attached to your machine, so normally you should say Y here. +config KEYBOARD_HP6XX + tristate "HP Jornada 6XX Keyboard support" + depends on SH_HP6XX + select INPUT_POLLDEV + help + This adds support for the onboard keyboard found on + HP Jornada 620/660/680/690. + + To compile this driver as a module, choose M here: the + module will be called jornada680_kbd. + +config KEYBOARD_HP7XX + tristate "HP Jornada 7XX Keyboard Driver" + depends on SA1100_JORNADA720_SSP && SA1100_SSP + help + Say Y here to add support for the HP Jornada 7xx (710/720/728) + onboard keyboard. + + To compile this driver as a module, choose M here: the + module will be called jornada720_kbd. + config KEYBOARD_OMAP tristate "TI OMAP keypad support" depends on (ARCH_OMAP1 || ARCH_OMAP2) @@ -253,4 +274,14 @@ config KEYBOARD_GPIO To compile this driver as a module, choose M here: the module will be called gpio-keys. +config KEYBOARD_MAPLE + tristate "Maple bus keyboard" + depends on SH_DREAMCAST && MAPLE + help + Say Y here if you have a Dreamcast console running Linux and have + a keyboard attached to its Maple bus. + + To compile this driver as a module, choose M here: the + module will be called maple_keyb. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 28d211b87b1..2776aa4a3a9 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -21,4 +21,7 @@ obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o +obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o +obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o +obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index f0b22b8b276..e2a3293bc67 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -54,6 +54,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct input_dev *input; int i, error; + int wakeup = 0; input = input_allocate_device(); if (!input) @@ -77,31 +78,51 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) int irq = gpio_to_irq(button->gpio); unsigned int type = button->type ?: EV_KEY; - set_irq_type(irq, IRQ_TYPE_EDGE_BOTH); - error = request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM, - button->desc ? button->desc : "gpio_keys", - pdev); + if (irq < 0) { + error = irq; + printk(KERN_ERR + "gpio-keys: " + "Unable to get irq number for GPIO %d," + "error %d\n", + button->gpio, error); + goto fail; + } + + error = request_irq(irq, gpio_keys_isr, + IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + button->desc ? button->desc : "gpio_keys", + pdev); if (error) { - printk(KERN_ERR "gpio-keys: unable to claim irq %d; error %d\n", + printk(KERN_ERR + "gpio-keys: Unable to claim irq %d; error %d\n", irq, error); goto fail; } + if (button->wakeup) + wakeup = 1; + input_set_capability(input, type, button->code); } error = input_register_device(input); if (error) { - printk(KERN_ERR "Unable to register gpio-keys input device\n"); + printk(KERN_ERR + "gpio-keys: Unable to register input device, " + "error: %d\n", error); goto fail; } + device_init_wakeup(&pdev->dev, wakeup); + return 0; fail: - for (i = i - 1; i >= 0; i--) + while (--i >= 0) free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev); + platform_set_drvdata(pdev, NULL); input_free_device(input); return error; @@ -113,6 +134,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) struct input_dev *input = platform_get_drvdata(pdev); int i; + device_init_wakeup(&pdev->dev, 0); + for (i = 0; i < pdata->nbuttons; i++) { int irq = gpio_to_irq(pdata->buttons[i].gpio); free_irq(irq, pdev); @@ -123,9 +146,53 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) return 0; } + +#ifdef CONFIG_PM +static int gpio_keys_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + int i; + + if (device_may_wakeup(&pdev->dev)) { + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + if (button->wakeup) { + int irq = gpio_to_irq(button->gpio); + enable_irq_wake(irq); + } + } + } + + return 0; +} + +static int gpio_keys_resume(struct platform_device *pdev) +{ + struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + int i; + + if (device_may_wakeup(&pdev->dev)) { + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + if (button->wakeup) { + int irq = gpio_to_irq(button->gpio); + disable_irq_wake(irq); + } + } + } + + return 0; +} +#else +#define gpio_keys_suspend NULL +#define gpio_keys_resume NULL +#endif + struct platform_driver gpio_keys_device_driver = { .probe = gpio_keys_probe, .remove = __devexit_p(gpio_keys_remove), + .suspend = gpio_keys_suspend, + .resume = gpio_keys_resume, .driver = { .name = "gpio-keys", } diff --git a/drivers/input/keyboard/jornada680_kbd.c b/drivers/input/keyboard/jornada680_kbd.c new file mode 100644 index 00000000000..bec1cf48372 --- /dev/null +++ b/drivers/input/keyboard/jornada680_kbd.c @@ -0,0 +1,277 @@ +/* + * drivers/input/keyboard/jornada680_kbd.c + * + * HP Jornada 620/660/680/690 scan keyboard platform driver + * Copyright (C) 2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com> + * + * Based on hp680_keyb.c + * Copyright (C) 2006 Paul Mundt + * Copyright (C) 2005 Andriy Skulysh + * Split from drivers/input/keyboard/hp600_keyb.c + * Copyright (C) 2000 Yaegashi Takeshi (hp6xx kbd scan routine and translation table) + * Copyright (C) 2000 Niibe Yutaka (HP620 Keyb translation table) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input-polldev.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> + +#include <asm/delay.h> +#include <asm/io.h> + +#define PCCR 0xa4000104 +#define PDCR 0xa4000106 +#define PECR 0xa4000108 +#define PFCR 0xa400010a +#define PCDR 0xa4000124 +#define PDDR 0xa4000126 +#define PEDR 0xa4000128 +#define PFDR 0xa400012a +#define PGDR 0xa400012c +#define PHDR 0xa400012e +#define PJDR 0xa4000130 +#define PKDR 0xa4000132 +#define PLDR 0xa4000134 + +static const unsigned short jornada_scancodes[] = { +/* PTD1 */ KEY_CAPSLOCK, KEY_MACRO, KEY_LEFTCTRL, 0, KEY_ESC, 0, 0, 0, /* 1 -> 8 */ + KEY_F1, KEY_F2, KEY_F3, KEY_F8, KEY_F7, KEY_F2, KEY_F4, KEY_F5, /* 9 -> 16 */ +/* PTD5 */ KEY_SLASH, KEY_APOSTROPHE, KEY_ENTER, 0, KEY_Z, 0, 0, 0, /* 17 -> 24 */ + KEY_X, KEY_C, KEY_V, KEY_DOT, KEY_COMMA, KEY_M, KEY_B, KEY_N, /* 25 -> 32 */ +/* PTD7 */ KEY_KP2, KEY_KP6, 0, 0, 0, 0, 0, 0, /* 33 -> 40 */ + 0, 0, 0, KEY_KP4, 0, 0, KEY_LEFTALT, KEY_HANJA, /* 41 -> 48 */ +/* PTE0 */ 0, 0, 0, 0, KEY_FINANCE, 0, 0, 0, /* 49 -> 56 */ + KEY_LEFTCTRL, 0, KEY_SPACE, KEY_KPDOT, KEY_VOLUMEUP, 249, 0, 0, /* 57 -> 64 */ +/* PTE1 */ KEY_SEMICOLON, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, KEY_A, 0, 0, 0,/* 65 -> 72 */ + KEY_S, KEY_D, KEY_F, KEY_L, KEY_K, KEY_J, KEY_G, KEY_H, /* 73 -> 80 */ +/* PTE3 */ KEY_KP8, KEY_LEFTMETA, KEY_RIGHTSHIFT, 0, KEY_TAB, 0, 0,0, /* 81 -> 88 */ + 0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, /* 89 -> 96 */ +/* PTE6 */ KEY_P, KEY_LEFTBRACE, KEY_BACKSPACE, 0, KEY_Q, 0, 0, 0, /* 97 -> 104 */ + KEY_W, KEY_E, KEY_R, KEY_O, KEY_I, KEY_U, KEY_T, KEY_R, /* 105 -> 112 */ +/* PTE7 */ KEY_0, KEY_MINUS, KEY_EQUAL, 0, KEY_1, 0, 0, 0, /* 113 -> 120 */ + KEY_2, KEY_3, KEY_4, KEY_9, KEY_8, KEY_7, KEY_5, KEY_6, /* 121 -> 128 */ +/* **** */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 +}; + +#define JORNADA_SCAN_SIZE 18 + +struct jornadakbd { + struct input_polled_dev *poll_dev; + unsigned short keymap[ARRAY_SIZE(jornada_scancodes)]; + unsigned char length; + unsigned char old_scan[JORNADA_SCAN_SIZE]; + unsigned char new_scan[JORNADA_SCAN_SIZE]; +}; + +static void jornada_parse_kbd(struct jornadakbd *jornadakbd) +{ + struct input_dev *input_dev = jornadakbd->poll_dev->input; + unsigned short *keymap = jornadakbd->keymap; + unsigned int sync_me = 0; + unsigned int i, j; + + for (i = 0; i < JORNADA_SCAN_SIZE; i++) { + unsigned char new = jornadakbd->new_scan[i]; + unsigned char old = jornadakbd->old_scan[i]; + unsigned int xor = new ^ old; + + if (xor == 0) + continue; + + for (j = 0; j < 8; j++) { + unsigned int bit = 1 << j; + if (xor & bit) { + unsigned int scancode = (i << 3) + j; + input_event(input_dev, + EV_MSC, MSC_SCAN, scancode); + input_report_key(input_dev, + keymap[scancode], + !(new & bit)); + sync_me = 1; + } + } + } + + if (sync_me) + input_sync(input_dev); +} + +static void jornada_scan_keyb(unsigned char *s) +{ + int i; + unsigned short ec_static, dc_static; /* = UINT16_t */ + unsigned char matrix_switch[] = { + 0xfd, 0xff, /* PTD1 PD(1) */ + 0xdf, 0xff, /* PTD5 PD(5) */ + 0x7f, 0xff, /* PTD7 PD(7) */ + 0xff, 0xfe, /* PTE0 PE(0) */ + 0xff, 0xfd, /* PTE1 PE(1) */ + 0xff, 0xf7, /* PTE3 PE(3) */ + 0xff, 0xbf, /* PTE6 PE(6) */ + 0xff, 0x7f, /* PTE7 PE(7) */ + }, *t = matrix_switch; + /* PD(x) : + 1. 0xcc0c & (1~(1 << (2*(x)+1))))) + 2. (0xf0cf & 0xfffff) */ + /* PE(x) : + 1. 0xcc0c & 0xffff + 2. 0xf0cf & (1~(1 << (2*(x)+1))))) */ + unsigned short matrix_PDE[] = { + 0xcc04, 0xf0cf, /* PD(1) */ + 0xc40c, 0xf0cf, /* PD(5) */ + 0x4c0c, 0xf0cf, /* PD(7) */ + 0xcc0c, 0xf0cd, /* PE(0) */ + 0xcc0c, 0xf0c7, /* PE(1) */ + 0xcc0c, 0xf04f, /* PE(3) */ + 0xcc0c, 0xd0cf, /* PE(6) */ + 0xcc0c, 0x70cf, /* PE(7) */ + }, *y = matrix_PDE; + + /* Save these control reg bits */ + dc_static = (ctrl_inw(PDCR) & (~0xcc0c)); + ec_static = (ctrl_inw(PECR) & (~0xf0cf)); + + for (i = 0; i < 8; i++) { + /* disable output for all but the one we want to scan */ + ctrl_outw((dc_static | *y++), PDCR); + ctrl_outw((ec_static | *y++), PECR); + udelay(5); + + /* Get scanline row */ + ctrl_outb(*t++, PDDR); + ctrl_outb(*t++, PEDR); + udelay(50); + + /* Read data */ + *s++ = ctrl_inb(PCDR); + *s++ = ctrl_inb(PFDR); + } + /* Scan no lines */ + ctrl_outb(0xff, PDDR); + ctrl_outb(0xff, PEDR); + + /* Enable all scanlines */ + ctrl_outw((dc_static | (0x5555 & 0xcc0c)),PDCR); + ctrl_outw((ec_static | (0x5555 & 0xf0cf)),PECR); + + /* Ignore extra keys and events */ + *s++ = ctrl_inb(PGDR); + *s++ = ctrl_inb(PHDR); +} + +static void jornadakbd680_poll(struct input_polled_dev *dev) +{ + struct jornadakbd *jornadakbd = dev->private; + + jornada_scan_keyb(jornadakbd->new_scan); + jornada_parse_kbd(jornadakbd); + memcpy(jornadakbd->old_scan, jornadakbd->new_scan, JORNADA_SCAN_SIZE); +} + +static int __devinit jornada680kbd_probe(struct platform_device *pdev) +{ + struct jornadakbd *jornadakbd; + struct input_polled_dev *poll_dev; + struct input_dev *input_dev; + int i, error; + + jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL); + if (!jornadakbd) + return -ENOMEM; + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) { + error = -ENOMEM; + goto failed; + } + + platform_set_drvdata(pdev, jornadakbd); + + jornadakbd->poll_dev = poll_dev; + + memcpy(jornadakbd->keymap, jornada_scancodes, + sizeof(jornadakbd->keymap)); + + poll_dev->private = jornadakbd; + poll_dev->poll = jornadakbd680_poll; + poll_dev->poll_interval = 50; /* msec */ + + input_dev = poll_dev->input; + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + input_dev->name = "HP Jornada 680 keyboard"; + input_dev->phys = "jornadakbd/input0"; + input_dev->keycode = jornadakbd->keymap; + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = ARRAY_SIZE(jornada_scancodes); + input_dev->dev.parent = &pdev->dev; + input_dev->id.bustype = BUS_HOST; + + for (i = 0; i < 128; i++) + if (jornadakbd->keymap[i]) + __set_bit(jornadakbd->keymap[i], input_dev->keybit); + __clear_bit(KEY_RESERVED, input_dev->keybit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + error = input_register_polled_device(jornadakbd->poll_dev); + if (error) + goto failed; + + return 0; + + failed: + printk(KERN_ERR "Jornadakbd: failed to register driver, error: %d\n", + error); + platform_set_drvdata(pdev, NULL); + input_free_polled_device(poll_dev); + kfree(jornadakbd); + return error; + +} + +static int __devexit jornada680kbd_remove(struct platform_device *pdev) +{ + struct jornadakbd *jornadakbd = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + input_unregister_polled_device(jornadakbd->poll_dev); + input_free_polled_device(jornadakbd->poll_dev); + kfree(jornadakbd); + + return 0; +} + +static struct platform_driver jornada680kbd_driver = { + .driver = { + .name = "jornada680_kbd", + }, + .probe = jornada680kbd_probe, + .remove = __devexit_p(jornada680kbd_remove), +}; + +static int __init jornada680kbd_init(void) +{ + return platform_driver_register(&jornada680kbd_driver); +} + +static void __exit jornada680kbd_exit(void) +{ + platform_driver_unregister(&jornada680kbd_driver); +} + +module_init(jornada680kbd_init); +module_exit(jornada680kbd_exit); + +MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>"); +MODULE_DESCRIPTION("HP Jornada 620/660/680/690 Keyboard Driver"); +MODULE_LICENSE("GPLv2"); diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c new file mode 100644 index 00000000000..e6696b3c941 --- /dev/null +++ b/drivers/input/keyboard/jornada720_kbd.c @@ -0,0 +1,185 @@ +/* + * drivers/input/keyboard/jornada720_kbd.c + * + * HP Jornada 720 keyboard platform driver + * + * Copyright (C) 2006/2007 Kristoffer Ericson <Kristoffer.Ericson@Gmail.com> + * + * Copyright (C) 2006 jornada 720 kbd driver by + Filip Zyzniewsk <Filip.Zyzniewski@tefnet.plX + * based on (C) 2004 jornada 720 kbd driver by + Alex Lange <chicken@handhelds.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/arch/jornada720.h> +#include <asm/hardware.h> + +MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>"); +MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver"); +MODULE_LICENSE("GPLv2"); + +static unsigned short jornada_std_keymap[128] = { /* ROW */ + 0, KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, /* #1 */ + KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, /* -> */ + 0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, /* #2 */ + KEY_0, KEY_MINUS, KEY_EQUAL,0, 0, 0, /* -> */ + 0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, /* #3 */ + KEY_P, KEY_BACKSLASH, KEY_BACKSPACE, 0, 0, 0, /* -> */ + 0, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, /* #4 */ + KEY_SEMICOLON, KEY_LEFTBRACE, KEY_RIGHTBRACE, 0, 0, 0, /* -> */ + 0, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA, /* #5 */ + KEY_DOT, KEY_KPMINUS, KEY_APOSTROPHE, KEY_ENTER, 0, 0,0, /* -> */ + 0, KEY_TAB, 0, KEY_LEFTSHIFT, 0, KEY_APOSTROPHE, 0, 0, 0, 0, /* #6 */ + KEY_UP, 0, KEY_RIGHTSHIFT, 0, 0, 0,0, 0, 0, 0, 0, KEY_LEFTALT, KEY_GRAVE, /* -> */ + 0, 0, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0,0, KEY_KPASTERISK, /* -> */ + KEY_LEFTCTRL, 0, KEY_SPACE, 0, 0, 0, KEY_SLASH, KEY_DELETE, 0, 0, /* -> */ + 0, 0, 0, KEY_POWER, /* -> */ +}; + +struct jornadakbd { + unsigned short keymap[ARRAY_SIZE(jornada_std_keymap)]; + struct input_dev *input; +}; + +static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct jornadakbd *jornadakbd = platform_get_drvdata(pdev); + struct input_dev *input = jornadakbd->input; + u8 count, kbd_data, scan_code; + + /* startup ssp with spinlock */ + jornada_ssp_start(); + + if (jornada_ssp_inout(GETSCANKEYCODE) != TXDUMMY) { + printk(KERN_DEBUG + "jornada720_kbd: " + "GetKeycode command failed with ETIMEDOUT, " + "flushed bus\n"); + } else { + /* How many keycodes are waiting for us? */ + count = jornada_ssp_byte(TXDUMMY); + + /* Lets drag them out one at a time */ + while (count--) { + /* Exchange TxDummy for location (keymap[kbddata]) */ + kbd_data = jornada_ssp_byte(TXDUMMY); + scan_code = kbd_data & 0x7f; + + input_event(input, EV_MSC, MSC_SCAN, scan_code); + input_report_key(input, jornadakbd->keymap[scan_code], + !(kbd_data & 0x80)); + input_sync(input); + } + } + + /* release spinlock and turn off ssp */ + jornada_ssp_end(); + + return IRQ_HANDLED; +}; + +static int __devinit jornada720_kbd_probe(struct platform_device *pdev) +{ + struct jornadakbd *jornadakbd; + struct input_dev *input_dev; + int i, err; + + jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!jornadakbd || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + platform_set_drvdata(pdev, jornadakbd); + + memcpy(jornadakbd->keymap, jornada_std_keymap, + sizeof(jornada_std_keymap)); + jornadakbd->input = input_dev; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + input_dev->name = "HP Jornada 720 keyboard"; + input_dev->phys = "jornadakbd/input0"; + input_dev->keycode = jornadakbd->keymap; + input_dev->keycodesize = sizeof(unsigned short); + input_dev->keycodemax = ARRAY_SIZE(jornada_std_keymap); + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + + for (i = 0; i < ARRAY_SIZE(jornadakbd->keymap); i++) + __set_bit(jornadakbd->keymap[i], input_dev->keybit); + __clear_bit(KEY_RESERVED, input_dev->keybit); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + err = request_irq(IRQ_GPIO0, + jornada720_kbd_interrupt, + IRQF_DISABLED | IRQF_TRIGGER_FALLING, + "jornadakbd", pdev); + if (err) { + printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n"); + goto fail1; + } + + err = input_register_device(jornadakbd->input); + if (err) + goto fail2; + + return 0; + + fail2: /* IRQ, DEVICE, MEMORY */ + free_irq(IRQ_GPIO0, pdev); + fail1: /* DEVICE, MEMORY */ + platform_set_drvdata(pdev, NULL); + input_free_device(input_dev); + kfree(jornadakbd); + return err; +}; + +static int __devexit jornada720_kbd_remove(struct platform_device *pdev) +{ + struct jornadakbd *jornadakbd = platform_get_drvdata(pdev); + + free_irq(IRQ_GPIO0, pdev); + platform_set_drvdata(pdev, NULL); + input_unregister_device(jornadakbd->input); + kfree(jornadakbd); + + return 0; +} + +static struct platform_driver jornada720_kbd_driver = { + .driver = { + .name = "jornada720_kbd", + }, + .probe = jornada720_kbd_probe, + .remove = __devexit_p(jornada720_kbd_remove), +}; + +static int __init jornada720_kbd_init(void) +{ + return platform_driver_register(&jornada720_kbd_driver); +} + +static void __exit jornada720_kbd_exit(void) +{ + platform_driver_unregister(&jornada720_kbd_driver); +} + +module_init(jornada720_kbd_init); +module_exit(jornada720_kbd_exit); diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c new file mode 100644 index 00000000000..2b404284c28 --- /dev/null +++ b/drivers/input/keyboard/maple_keyb.c @@ -0,0 +1,252 @@ +/* + * SEGA Dreamcast keyboard driver + * Based on drivers/usb/usbkbd.c + * Copyright YAEGASHI Takeshi, 2001 + * Porting to 2.6 Copyright Adrian McMenamin, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/maple.h> +#include <asm/mach/maple.h> + +/* Very simple mutex to ensure proper cleanup */ +static DEFINE_MUTEX(maple_keyb_mutex); + +#define NR_SCANCODES 256 + +MODULE_AUTHOR("YAEGASHI Takeshi, Adrian McMenamin"); +MODULE_DESCRIPTION("SEGA Dreamcast keyboard driver"); +MODULE_LICENSE("GPL"); + +struct dc_kbd { + struct input_dev *dev; + unsigned short keycode[NR_SCANCODES]; + unsigned char new[8]; + unsigned char old[8]; +}; + +static const unsigned short dc_kbd_keycode[NR_SCANCODES] = { + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_A, KEY_B, KEY_C, KEY_D, + KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, + KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, + KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2, + KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, + KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE, + KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_BACKSLASH, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, + KEY_DOT, KEY_SLASH, KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, + KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_SYSRQ, + KEY_SCROLLLOCK, KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP, KEY_DELETE, + KEY_END, KEY_PAGEDOWN, KEY_RIGHT, KEY_LEFT, KEY_DOWN, KEY_UP, + KEY_NUMLOCK, KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS, KEY_KPPLUS, KEY_KPENTER, KEY_KP1, KEY_KP2, + KEY_KP3, KEY_KP4, KEY_KP5, KEY_KP6, KEY_KP7, KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT, + KEY_102ND, KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, KEY_F13, KEY_F14, KEY_F15, + KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20, + KEY_F21, KEY_F22, KEY_F23, KEY_F24, KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT, + KEY_STOP, KEY_AGAIN, KEY_UNDO, KEY_CUT, KEY_COPY, KEY_PASTE, KEY_FIND, KEY_MUTE, + KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_KPCOMMA, KEY_RESERVED, KEY_RO, KEY_KATAKANAHIRAGANA , KEY_YEN, + KEY_HENKAN, KEY_MUHENKAN, KEY_KPJPCOMMA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_HANGEUL, KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA, KEY_ZENKAKUHANKAKU, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, + KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_LEFTALT, KEY_LEFTMETA, KEY_RIGHTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT, KEY_RIGHTMETA, + KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG, KEY_NEXTSONG, KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, + KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, KEY_FIND, KEY_SCROLLUP, KEY_SCROLLDOWN, KEY_EDIT, KEY_SLEEP, + KEY_SCREENLOCK, KEY_REFRESH, KEY_CALC, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED +}; + +static void dc_scan_kbd(struct dc_kbd *kbd) +{ + struct input_dev *dev = kbd->dev; + void *ptr; + int code, keycode; + int i; + + for (i = 0; i < 8; i++) { + code = i + 224; + keycode = kbd->keycode[code]; + input_event(dev, EV_MSC, MSC_SCAN, code); + input_report_key(dev, keycode, (kbd->new[0] >> i) & 1); + } + + for (i = 2; i < 8; i++) { + ptr = memchr(kbd->new + 2, kbd->old[i], 6); + code = kbd->old[i]; + if (code > 3 && ptr == NULL) { + keycode = kbd->keycode[code]; + if (keycode) { + input_event(dev, EV_MSC, MSC_SCAN, code); + input_report_key(dev, keycode, 0); + } else + printk(KERN_DEBUG "maple_keyb: " + "Unknown key (scancode %#x) released.", + code); + } + ptr = memchr(kbd->old + 2, kbd->new[i], 6); + code = kbd->new[i]; + if (code > 3 && ptr) { + keycode = kbd->keycode[code]; + if (keycode) { + input_event(dev, EV_MSC, MSC_SCAN, code); + input_report_key(dev, keycode, 1); + } else + printk(KERN_DEBUG "maple_keyb: " + "Unknown key (scancode %#x) pressed.", + code); + } + } + input_sync(dev); + memcpy(kbd->old, kbd->new, 8); +} + +static void dc_kbd_callback(struct mapleq *mq) +{ + struct maple_device *mapledev = mq->dev; + struct dc_kbd *kbd = mapledev->private_data; + unsigned long *buf = mq->recvbuf; + + /* + * We should always be getting the lock because the only + * time it may be locked if driver is in cleanup phase. + */ + if (likely(mutex_trylock(&maple_keyb_mutex))) { + + if (buf[1] == mapledev->function) { + memcpy(kbd->new, buf + 2, 8); + dc_scan_kbd(kbd); + } + + mutex_unlock(&maple_keyb_mutex); + } +} + +static int dc_kbd_connect(struct maple_device *mdev) +{ + int i, error; + struct dc_kbd *kbd; + struct input_dev *dev; + + if (!(mdev->function & MAPLE_FUNC_KEYBOARD)) + return -EINVAL; + + kbd = kzalloc(sizeof(struct dc_kbd), GFP_KERNEL); + dev = input_allocate_device(); + if (!kbd || !dev) { + error = -ENOMEM; + goto fail; + } + + mdev->private_data = kbd; + + kbd->dev = dev; + memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode)); + + dev->name = mdev->product_name; + dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + dev->keycode = kbd->keycode; + dev->keycodesize = sizeof (unsigned short); + dev->keycodemax = ARRAY_SIZE(kbd->keycode); + dev->id.bustype = BUS_HOST; + dev->dev.parent = &mdev->dev; + + for (i = 0; i < NR_SCANCODES; i++) + __set_bit(dc_kbd_keycode[i], dev->keybit); + __clear_bit(KEY_RESERVED, dev->keybit); + + input_set_capability(dev, EV_MSC, MSC_SCAN); + input_set_drvdata(dev, kbd); + + error = input_register_device(dev); + if (error) + goto fail; + + /* Maple polling is locked to VBLANK - which may be just 50/s */ + maple_getcond_callback(mdev, dc_kbd_callback, HZ/50, MAPLE_FUNC_KEYBOARD); + return 0; + + fail: + input_free_device(dev); + kfree(kbd); + mdev->private_data = NULL; + return error; +} + +static void dc_kbd_disconnect(struct maple_device *mdev) +{ + struct dc_kbd *kbd; + + mutex_lock(&maple_keyb_mutex); + + kbd = mdev->private_data; + mdev->private_data = NULL; + input_unregister_device(kbd->dev); + kfree(kbd); + + mutex_unlock(&maple_keyb_mutex); +} + +/* allow the keyboard to be used */ +static int probe_maple_kbd(struct device *dev) +{ + struct maple_device *mdev = to_maple_dev(dev); + struct maple_driver *mdrv = to_maple_driver(dev->driver); + int error; + + error = dc_kbd_connect(mdev); + if (error) + return error; + + mdev->driver = mdrv; + mdev->registered = 1; + + return 0; +} + +static struct maple_driver dc_kbd_driver = { + .function = MAPLE_FUNC_KEYBOARD, + .connect = dc_kbd_connect, + .disconnect = dc_kbd_disconnect, + .drv = { + .name = "Dreamcast_keyboard", + .probe = probe_maple_kbd, + }, +}; + +static int __init dc_kbd_init(void) +{ + return maple_driver_register(&dc_kbd_driver.drv); +} + +static void __exit dc_kbd_exit(void) +{ + driver_unregister(&dc_kbd_driver.drv); +} + +module_init(dc_kbd_init); +module_exit(dc_kbd_exit); diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 3a228634f10..76f1969552c 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -233,7 +233,7 @@ static void omap_kp_tasklet(unsigned long data) omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); kp_cur_group = -1; } - } + } } static ssize_t omap_kp_enable_show(struct device *dev, @@ -318,7 +318,7 @@ static int __init omap_kp_probe(struct platform_device *pdev) keymap = pdata->keymap; if (pdata->rep) - set_bit(EV_REP, input_dev->evbit); + __set_bit(EV_REP, input_dev->evbit); if (pdata->delay) omap_kp->delay = pdata->delay; @@ -365,9 +365,9 @@ static int __init omap_kp_probe(struct platform_device *pdev) goto err2; /* setup input device */ - set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); for (i = 0; keymap[i] != 0; i++) - set_bit(keymap[i] & KEY_MAX, input_dev->keybit); + __set_bit(keymap[i] & KEY_MAX, input_dev->keybit); input_dev->name = "omap-keypad"; input_dev->phys = "omap-keypad/input0"; input_dev->dev.parent = &pdev->dev; @@ -377,10 +377,6 @@ static int __init omap_kp_probe(struct platform_device *pdev) input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; - input_dev->keycode = keymap; - input_dev->keycodesize = sizeof(unsigned int); - input_dev->keycodemax = pdata->keymapsize; - ret = input_register_device(omap_kp->input); if (ret < 0) { printk(KERN_ERR "Unable to register omap-keypad input device\n"); @@ -403,15 +399,15 @@ static int __init omap_kp_probe(struct platform_device *pdev) } else { for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) { if (request_irq(OMAP_GPIO_IRQ(row_gpios[irq_idx]), - omap_kp_interrupt, + omap_kp_interrupt, IRQF_TRIGGER_FALLING, - "omap-keypad", omap_kp) < 0) + "omap-keypad", omap_kp) < 0) goto err5; } } return 0; err5: - for (i = irq_idx-1; i >=0; i--) + for (i = irq_idx - 1; i >=0; i--) free_irq(row_gpios[i], 0); err4: input_unregister_device(omap_kp->input); @@ -440,9 +436,9 @@ static int omap_kp_remove(struct platform_device *pdev) if (cpu_is_omap24xx()) { int i; for (i = 0; i < omap_kp->cols; i++) - omap_free_gpio(col_gpios[i]); + omap_free_gpio(col_gpios[i]); for (i = 0; i < omap_kp->rows; i++) { - omap_free_gpio(row_gpios[i]); + omap_free_gpio(row_gpios[i]); free_irq(OMAP_GPIO_IRQ(row_gpios[i]), 0); } } else { diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 2c5f11a4f6b..64d70a9b714 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -48,11 +48,13 @@ static const struct alps_model_info alps_model_data[] = { { { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */ { { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 }, { { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS }, /* Dell Latitude D800 */ + { { 0x73, 0x00, 0x0a }, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */ { { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 }, { { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */ { { 0x20, 0x02, 0x0e }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* XXX */ { { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, { { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */ + { { 0x73, 0x02, 0x50 }, 0xcf, 0xff, ALPS_FW_BK_1 } /* Dell Vostro 1400 */ }; /* diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index 608674d0be8..d7de4c53b3d 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -97,6 +97,14 @@ static const struct dmi_system_id lifebook_dmi_table[] = { .callback = lifebook_set_6byte_proto, }, { + .ident = "CF-72", + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "CF-72"), + }, + .callback = lifebook_set_serio_phys, + .driver_data = "isa0060/serio3", + }, + { .ident = "Lifebook B142", .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook B142"), @@ -282,7 +290,7 @@ static int lifebook_create_relative_device(struct psmouse *psmouse) int lifebook_init(struct psmouse *psmouse) { struct input_dev *dev1 = psmouse->dev; - int max_coord = lifebook_use_6byte_proto ? 1024 : 4096; + int max_coord = lifebook_use_6byte_proto ? 4096 : 1024; if (lifebook_absolute_mode(psmouse)) return -1; diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index b9f0fb2530e..07352575653 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -648,9 +648,10 @@ static int psmouse_extensions(struct psmouse *psmouse, /* * Reset to defaults in case the device got confused by extended - * protocol probes. Note that we do full reset becuase some mice - * put themselves to sleep when see PSMOUSE_RESET_DIS. + * protocol probes. Note that we follow up with full reset because + * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS. */ + ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); psmouse_reset(psmouse); if (max_proto >= PSMOUSE_IMEX && im_explorer_detect(psmouse, set_properties) == 0) diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 9173916b8be..cc36edbb912 100644 --- 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[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 }; 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 mousedev *mousedev, unsigned int code, int } } -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(struct mousedev *mousedev, struct mousedev_h } 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(struct mousedev *mousedev, int value) { 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(struct mousedev *mousedev, int value) 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(struct mousedev *mousedev, int value) 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); - else - mousedev_abs_event(handle->dev, mousedev, code, value); + case EV_ABS: + /* Ignore joysticks */ + if (test_bit(BTN_TRIGGER, handle->dev->keybit)) + return; - break; + 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); - case EV_REL: - mousedev_rel_event(mousedev, code, value); - break; + 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); - } - break; + case EV_REL: + mousedev_rel_event(mousedev, code, value); + 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->packet.dx = mousedev->packet.dy = mousedev->packet.dz = 0; - mousedev->packet.abs_event = 0; + 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); + } + 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); } - break; + + 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; } } @@ -367,41 +413,48 @@ static void mousedev_free(struct device *dev) { 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; + int retval; - if (mousedev_mix->open) { - error = input_open_device(&mousedev->handle); - if (error) - return error; + retval = mutex_lock_interruptible(&mousedev->mutex); + if (retval) + return retval; - mousedev->open++; - mousedev->mixdev_open = 1; + if (mousedev->minor == MOUSEDEV_MIX) + mixdev_open_devices(); + else if (!mousedev->exist) + retval = -ENODEV; + else if (!mousedev->open++) { + retval = input_open_device(&mousedev->handle); + if (retval) + mousedev->open--; } - get_device(&mousedev->dev); - list_add_tail(&mousedev->mixdev_node, &mousedev_mix_list); - - 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 +464,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 +487,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 +548,17 @@ static int mousedev_open(struct inode *inode, struct file *file) 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 +569,17 @@ static int mousedev_open(struct inode *inode, struct file *file) 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 +591,41 @@ static inline int mousedev_limit_delta(int delta, int limit) 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 +635,56 @@ static void mousedev_packet(struct mousedev_client *client, signed char *ps2_dat } 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 +695,8 @@ static ssize_t mousedev_write(struct file *file, const char __user *buffer, size 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 +713,39 @@ static ssize_t mousedev_write(struct file *file, const char __user *buffer, size } 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 +754,12 @@ static ssize_t mousedev_read(struct file *file, char __user *buffer, size_t coun 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 +786,60 @@ static const struct file_operations mousedev_fops = { .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 +855,10 @@ static struct mousedev *mousedev_create(struct input_dev *dev, 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 +883,27 @@ static struct mousedev *mousedev_create(struct input_dev *dev, 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 +912,64 @@ static struct mousedev *mousedev_create(struct input_dev *dev, 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 +979,13 @@ static int mousedev_connect(struct input_handler *handler, struct input_dev *dev 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 +993,42 @@ static void mousedev_disconnect(struct input_handle *handle) 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/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index c2eea2767e1..11dafc0ee99 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -385,6 +385,8 @@ static int i8042_enable_kbd_port(void) i8042_ctr |= I8042_CTR_KBDINT; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + i8042_ctr &= ~I8042_CTR_KBDINT; + i8042_ctr |= I8042_CTR_KBDDIS; printk(KERN_ERR "i8042.c: Failed to enable KBD port.\n"); return -EIO; } @@ -402,6 +404,8 @@ static int i8042_enable_aux_port(void) i8042_ctr |= I8042_CTR_AUXINT; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { + i8042_ctr &= ~I8042_CTR_AUXINT; + i8042_ctr |= I8042_CTR_AUXDIS; printk(KERN_ERR "i8042.c: Failed to enable AUX port.\n"); return -EIO; } diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index f929fcdbae2..e3e0baa1a15 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -126,6 +126,16 @@ config TOUCHSCREEN_HP600 To compile this driver as a module, choose M here: the module will be called hp680_ts_input. +config TOUCHSCREEN_HP7XX + tristate "HP Jornada 710/720/728 touchscreen" + depends on SA1100_JORNADA720_SSP + help + Say Y here if you have a HP Jornada 710/720/728 and want + to support the built-in touchscreen. + + To compile this driver as a module, choose M here: the + module will be called jornada720_ts. + config TOUCHSCREEN_PENMOUNT tristate "Penmount serial touchscreen" select SERIO @@ -191,6 +201,7 @@ config TOUCHSCREEN_USB_COMPOSITE - Gunze AHL61 - DMC TSC-10/25 - IRTOUCHSYSTEMS/UNITOP + - IdealTEK URTC1000 Have a look at <http://linux.chapter7.ch/touchkit/> for a usage description and the required user-space stuff. @@ -238,4 +249,14 @@ config TOUCHSCREEN_USB_IRTOUCH bool "IRTOUCHSYSTEMS/UNITOP device support" if EMBEDDED depends on TOUCHSCREEN_USB_COMPOSITE +config TOUCHSCREEN_USB_IDEALTEK + default y + bool "IdealTEK URTC1000 device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + +config TOUCHSCREEN_USB_GENERAL_TOUCH + default y + bool "GeneralTouch Touchscreen device support" if EMBEDDED + depends on TOUCHSCREEN_USB_COMPOSITE + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 5de8933c499..35d4097df35 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c new file mode 100644 index 00000000000..42a1c9a1940 --- /dev/null +++ b/drivers/input/touchscreen/jornada720_ts.c @@ -0,0 +1,182 @@ +/* + * drivers/input/touchscreen/jornada720_ts.c + * + * Copyright (C) 2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com> + * + * Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl> + * based on HP Jornada 56x touchscreen driver by Alex Lange <chicken@handhelds.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * HP Jornada 710/720/729 Touchscreen Driver + */ + +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/module.h> + +#include <asm/hardware.h> +#include <asm/arch/jornada720.h> + +MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>"); +MODULE_DESCRIPTION("HP Jornada 710/720/728 touchscreen driver"); +MODULE_LICENSE("GPLv2"); + +struct jornada_ts { + struct input_dev *dev; + int x_data[4]; /* X sample values */ + int y_data[4]; /* Y sample values */ +}; + +static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts) +{ + + /* 3 low word X samples */ + jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY); + jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY); + jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY); + + /* 3 low word Y samples */ + jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY); + jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY); + jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY); + + /* combined x samples bits */ + jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY); + + /* combined y samples bits */ + jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY); +} + +static int jornada720_ts_average(int coords[4]) +{ + int coord, high_bits = coords[3]; + + coord = coords[0] | ((high_bits & 0x03) << 8); + coord += coords[1] | ((high_bits & 0x0c) << 6); + coord += coords[2] | ((high_bits & 0x30) << 4); + + return coord / 3; +} + +static irqreturn_t jornada720_ts_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + struct jornada_ts *jornada_ts = platform_get_drvdata(pdev); + struct input_dev *input = jornada_ts->dev; + int x, y; + + /* If GPIO_GPIO9 is set to high then report pen up */ + if (GPLR & GPIO_GPIO(9)) { + input_report_key(input, BTN_TOUCH, 0); + input_sync(input); + } else { + jornada_ssp_start(); + + /* proper reply to request is always TXDUMMY */ + if (jornada_ssp_inout(GETTOUCHSAMPLES) == TXDUMMY) { + jornada720_ts_collect_data(jornada_ts); + + x = jornada720_ts_average(jornada_ts->x_data); + y = jornada720_ts_average(jornada_ts->y_data); + + input_report_key(input, BTN_TOUCH, 1); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_sync(input); + } + + jornada_ssp_end(); + } + + return IRQ_HANDLED; +} + +static int __devinit jornada720_ts_probe(struct platform_device *pdev) +{ + struct jornada_ts *jornada_ts; + struct input_dev *input_dev; + int error; + + jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL); + input_dev = input_allocate_device(); + + if (!jornada_ts || !input_dev) { + error = -ENOMEM; + goto fail1; + } + + platform_set_drvdata(pdev, jornada_ts); + + jornada_ts->dev = input_dev; + + input_dev->name = "HP Jornada 7xx Touchscreen"; + input_dev->phys = "jornadats/input0"; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0); + + error = request_irq(IRQ_GPIO9, + jornada720_ts_interrupt, + IRQF_DISABLED | IRQF_TRIGGER_RISING, + "HP7XX Touchscreen driver", pdev); + if (error) { + printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n"); + goto fail1; + } + + error = input_register_device(jornada_ts->dev); + if (error) + goto fail2; + + return 0; + + fail2: + free_irq(IRQ_GPIO9, pdev); + fail1: + platform_set_drvdata(pdev, NULL); + input_free_device(input_dev); + kfree(jornada_ts); + return error; +} + +static int __devexit jornada720_ts_remove(struct platform_device *pdev) +{ + struct jornada_ts *jornada_ts = platform_get_drvdata(pdev); + + free_irq(IRQ_GPIO9, pdev); + platform_set_drvdata(pdev, NULL); + input_unregister_device(jornada_ts->dev); + kfree(jornada_ts); + + return 0; +} + +static struct platform_driver jornada720_ts_driver = { + .probe = jornada720_ts_probe, + .remove = __devexit_p(jornada720_ts_remove), + .driver = { + .name = "jornada_ts", + }, +}; + +static int __init jornada720_ts_init(void) +{ + return platform_driver_register(&jornada720_ts_driver); +} + +static void __exit jornada720_ts_exit(void) +{ + platform_driver_unregister(&jornada720_ts_driver); +} + +module_init(jornada720_ts_init); +module_exit(jornada720_ts_exit); diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index 36f94401915..86aed64ec0f 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -130,8 +130,7 @@ static unsigned int ucb1400_adc_read(struct ucb1400 *ucb, u16 adc_channel) if (val & UCB_ADC_DAT_VALID) break; /* yield to other processes */ - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(1); + schedule_timeout_uninterruptible(1); } return UCB_ADC_DAT_VALUE(val); diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 741f6c6f1e5..9fb3d5c3099 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -10,6 +10,7 @@ * - Gunze AHL61 * - DMC TSC-10/25 * - IRTOUCHSYSTEMS/UNITOP + * - IdealTEK URTC1000 * * Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch> * Copyright (C) by Todd E. Johnson (mtouchusb.c) @@ -92,7 +93,7 @@ struct usbtouch_usb { }; -#if defined(CONFIG_TOUCHSCREEN_USB_EGALAX) || defined(CONFIG_TOUCHSCREEN_USB_ETURBO) +#if defined(CONFIG_TOUCHSCREEN_USB_EGALAX) || defined(CONFIG_TOUCHSCREEN_USB_ETURBO) || defined(CONFIG_TOUCHSCREEN_USB_IDEALTEK) #define MULTI_PACKET #endif @@ -112,6 +113,8 @@ enum { DEVTYPE_GUNZE, DEVTYPE_DMC_TSC10, DEVTYPE_IRTOUCH, + DEVTYPE_IDEALTEK, + DEVTYPE_GENERAL_TOUCH, }; static struct usb_device_id usbtouch_devices[] = { @@ -157,6 +160,14 @@ static struct usb_device_id usbtouch_devices[] = { {USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH}, #endif +#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK + {USB_DEVICE(0x1391, 0x1000), .driver_info = DEVTYPE_IDEALTEK}, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH + {USB_DEVICE(0x0dfc, 0x0001), .driver_info = DEVTYPE_GENERAL_TOUCH}, +#endif + {} }; @@ -396,7 +407,8 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch) TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT); if (ret < 0) return ret; - if (buf[0] != 0x06 || buf[1] != 0x00) + if ((buf[0] != 0x06 || buf[1] != 0x00) && + (buf[0] != 0x15 || buf[1] != 0x01)) return -ENODEV; /* start sending data */ @@ -438,6 +450,57 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) /***************************************************************************** + * IdealTEK URTC1000 Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK +static int idealtek_get_pkt_len(unsigned char *buf, int len) +{ + if (buf[0] & 0x80) + return 5; + if (buf[0] == 0x01) + return len; + return 0; +} + +static int idealtek_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + switch (pkt[0] & 0x98) { + case 0x88: + /* touch data in IdealTEK mode */ + dev->x = (pkt[1] << 5) | (pkt[2] >> 2); + dev->y = (pkt[3] << 5) | (pkt[4] >> 2); + dev->touch = (pkt[0] & 0x40) ? 1 : 0; + return 1; + + case 0x98: + /* touch data in MT emulation mode */ + dev->x = (pkt[2] << 5) | (pkt[1] >> 2); + dev->y = (pkt[4] << 5) | (pkt[3] >> 2); + dev->touch = (pkt[0] & 0x40) ? 1 : 0; + return 1; + + default: + return 0; + } +} +#endif + +/***************************************************************************** + * General Touch Part + */ +#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH +static int general_touch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +{ + dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1] ; + dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3] ; + dev->press = pkt[5] & 0xff; + dev->touch = pkt[0] & 0x01; + + return 1; +} +#endif + +/***************************************************************************** * the different device descriptors */ static struct usbtouch_device_info usbtouch_dev_info[] = { @@ -537,6 +600,32 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .read_data = irtouch_read_data, }, #endif + +#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK + [DEVTYPE_IDEALTEK] = { + .min_xc = 0x0, + .max_xc = 0x0fff, + .min_yc = 0x0, + .max_yc = 0x0fff, + .rept_size = 8, + .flags = USBTOUCH_FLG_BUFFER, + .process_pkt = usbtouch_process_multi, + .get_pkt_len = idealtek_get_pkt_len, + .read_data = idealtek_read_data, + }, +#endif + +#ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH + [DEVTYPE_GENERAL_TOUCH] = { + .min_xc = 0x0, + .max_xc = 0x0500, + .min_yc = 0x0, + .max_yc = 0x0500, + .rept_size = 7, + .read_data = general_touch_read_data, + } +#endif + }; diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c index d2f882e98e5..12023349375 100644 --- 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,94 @@ static int tsdev_fasync(int fd, struct file *file, int on) 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); + if (retval) + tsdev->open--; + } + + 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 +250,16 @@ static int tsdev_open(struct inode *inode, struct file *file) 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 +267,42 @@ static int tsdev_open(struct inode *inode, struct file *file) 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) -{ - struct tsdev *tsdev = container_of(dev, struct tsdev, dev); - - tsdev_table[tsdev->minor] = NULL; - kfree(tsdev); -} - -static int tsdev_release(struct inode *inode, struct file *file) +static int tsdev_fetch_next_event(struct tsdev_client *client, + struct ts_event *event) { - struct tsdev_client *client = file->private_data; - struct tsdev *tsdev = client->tsdev; + int have_event; - tsdev_fasync(-1, file, 0); - - list_del(&client->node); - kfree(client); + spin_lock_irq(&client->buffer_lock); - 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 +310,11 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, { 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 +325,14 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, 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 +349,23 @@ static unsigned int tsdev_poll(struct file *file, poll_table *wait) (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 +374,7 @@ static int tsdev_ioctl(struct inode *inode, struct file *file, 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 +383,79 @@ static int tsdev_ioctl(struct inode *inode, struct file *file, 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 +465,9 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, 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 +477,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, case EV_REL: switch (code) { + case REL_X: tsdev->x += value; if (tsdev->x < 0) @@ -351,6 +499,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, case EV_KEY: if (code == BTN_TOUCH || code == BTN_MOUSE) { switch (value) { + case 0: tsdev->pressure = 0; break; @@ -362,49 +511,71 @@ static void tsdev_event(struct input_handle *handle, unsigned int type, } } 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); +} + +static int tsdev_install_chrdev(struct tsdev *tsdev) +{ + tsdev_table[tsdev->minor] = tsdev; + return 0; +} - list_for_each_entry(client, &tsdev->client_list, node) { - int x, 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); +} - do_gettimeofday(&time); - client->event[client->head].millisecs = time.tv_usec / 1000; - client->event[client->head].pressure = tsdev->pressure; +/* + * 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); +} - x = tsdev->x; - y = tsdev->y; +static void tsdev_cleanup(struct tsdev *tsdev) +{ + struct input_handle *handle = &tsdev->handle; - /* 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; - } - } + tsdev_mark_dead(tsdev); + tsdev_hangup(tsdev); + tsdev_remove_chrdev(tsdev); - 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); + /* 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 +584,18 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, 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 +610,31 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, 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 +643,10 @@ static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, 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 +677,13 @@ static const struct input_device_id tsdev_ids[] = { 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) |