From c8e4c77277ca5db0c4ddbfb4bc628b8abad585b0 Mon Sep 17 00:00:00 2001 From: Marvin Raaijmakers Date: Wed, 14 Mar 2007 22:50:42 -0400 Subject: Input: add getkeycode and setkeycode methods Allow drivers to implement their own get and set keycode methods. This will allow drivers to change their keymaps without allocating huge tables covering entire range of possible scancodes. Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) (limited to 'drivers/input/evdev.c') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 6439f378f6c..64b47de052b 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -434,32 +434,21 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, case EVIOCGKEYCODE: if (get_user(t, ip)) return -EFAULT; - if (t < 0 || t >= dev->keycodemax || !dev->keycodesize) - return -EINVAL; - if (put_user(INPUT_KEYCODE(dev, t), ip + 1)) + + error = dev->getkeycode(dev, t, &v); + if (error) + return error; + + if (put_user(v, ip + 1)) return -EFAULT; + return 0; case EVIOCSKEYCODE: - if (get_user(t, ip)) - return -EFAULT; - if (t < 0 || t >= dev->keycodemax || !dev->keycodesize) - return -EINVAL; - if (get_user(v, ip + 1)) + if (get_user(t, ip) || get_user(v, ip + 1)) return -EFAULT; - if (v < 0 || v > KEY_MAX) - return -EINVAL; - if (dev->keycodesize < sizeof(v) && (v >> (dev->keycodesize * 8))) - return -EINVAL; - - u = SET_INPUT_KEYCODE(dev, t, v); - clear_bit(u, dev->keybit); - set_bit(v, dev->keybit); - for (i = 0; i < dev->keycodemax; i++) - if (INPUT_KEYCODE(dev, i) == u) - set_bit(u, dev->keybit); - return 0; + return dev->setkeycode(dev, t, v); case EVIOCSFF: if (copy_from_user(&effect, p, sizeof(effect))) -- cgit v1.2.3 From 5b2a08262a8c952fef008154933953f083ca5766 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 12 Apr 2007 01:29:46 -0400 Subject: Input: rework handle creation code - consolidate code for binding handlers to a device - return error codes from handlers connect() methods back to input core and log failures Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 47 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 11 deletions(-) (limited to 'drivers/input/evdev.c') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 64b47de052b..840fa198652 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -605,21 +605,24 @@ static const struct file_operations evdev_fops = { .flush = evdev_flush }; -static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, - const struct input_device_id *id) +static int evdev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct evdev *evdev; struct class_device *cdev; + dev_t devt; int minor; + int error; for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); if (minor == EVDEV_MINORS) { printk(KERN_ERR "evdev: no more free evdev devices\n"); - return NULL; + return -ENFILE; } - if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL))) - return NULL; + evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); + if (!evdev) + return -ENOMEM; INIT_LIST_HEAD(&evdev->list); init_waitqueue_head(&evdev->wait); @@ -634,15 +637,35 @@ static struct input_handle *evdev_connect(struct input_handler *handler, struct evdev_table[minor] = evdev; - cdev = class_device_create(&input_class, &dev->cdev, - MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), - dev->cdev.dev, evdev->name); + devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), + + cdev = class_device_create(&input_class, &dev->cdev, devt, + dev->cdev.dev, evdev->name); + if (IS_ERR(cdev)) { + error = PTR_ERR(cdev); + goto err_free_evdev; + } /* temporary symlink to keep userspace happy */ - sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj, - evdev->name); + error = sysfs_create_link(&input_class.subsys.kset.kobj, + &cdev->kobj, evdev->name); + if (error) + goto err_cdev_destroy; + + error = input_register_handle(&evdev->handle); + if (error) + goto err_remove_link; - return &evdev->handle; + return 0; + + err_remove_link: + sysfs_remove_link(&input_class.subsys.kset.kobj, evdev->name); + err_cdev_destroy: + class_device_destroy(&input_class, devt); + err_free_evdev: + kfree(evdev); + evdev_table[minor] = NULL; + return error; } static void evdev_disconnect(struct input_handle *handle) @@ -650,6 +673,8 @@ static void evdev_disconnect(struct input_handle *handle) struct evdev *evdev = handle->private; struct evdev_list *list; + input_unregister_handle(handle); + sysfs_remove_link(&input_class.subsys.kset.kobj, evdev->name); class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + evdev->minor)); -- cgit v1.2.3 From d0ffb9be866519775da19c0a6790f5431c1a8dc6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 12 Apr 2007 01:30:00 -0400 Subject: Input: handlers - rename 'list' to 'client' The naming convention in input handlers was very confusing - client stuctures were called lists, regular lists were also called lists making anyone looking at the code go mad. Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 150 +++++++++++++++++++++++++++----------------------- 1 file changed, 80 insertions(+), 70 deletions(-) (limited to 'drivers/input/evdev.c') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 840fa198652..8a4cce5c780 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -29,11 +29,11 @@ struct evdev { char name[16]; struct input_handle handle; wait_queue_head_t wait; - struct evdev_list *grab; - struct list_head list; + struct evdev_client *grab; + struct list_head client_list; }; -struct evdev_list { +struct evdev_client { struct input_event buffer[EVDEV_BUFFER_SIZE]; int head; int tail; @@ -47,28 +47,28 @@ static struct evdev *evdev_table[EVDEV_MINORS]; static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct evdev *evdev = handle->private; - struct evdev_list *list; + struct evdev_client *client; if (evdev->grab) { - list = evdev->grab; + client = evdev->grab; - do_gettimeofday(&list->buffer[list->head].time); - list->buffer[list->head].type = type; - list->buffer[list->head].code = code; - list->buffer[list->head].value = value; - list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1); + 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(&list->fasync, SIGIO, POLL_IN); + kill_fasync(&client->fasync, SIGIO, POLL_IN); } else - list_for_each_entry(list, &evdev->list, node) { + list_for_each_entry(client, &evdev->client_list, node) { - do_gettimeofday(&list->buffer[list->head].time); - list->buffer[list->head].type = type; - list->buffer[list->head].code = code; - list->buffer[list->head].value = value; - list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1); + 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(&list->fasync, SIGIO, POLL_IN); + kill_fasync(&client->fasync, SIGIO, POLL_IN); } wake_up_interruptible(&evdev->wait); @@ -76,22 +76,23 @@ static void evdev_event(struct input_handle *handle, unsigned int type, unsigned static int evdev_fasync(int fd, struct file *file, int on) { + struct evdev_client *client = file->private_data; int retval; - struct evdev_list *list = file->private_data; - retval = fasync_helper(fd, file, on, &list->fasync); + retval = fasync_helper(fd, file, on, &client->fasync); return retval < 0 ? retval : 0; } static int evdev_flush(struct file *file, fl_owner_t id) { - struct evdev_list *list = file->private_data; + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; - if (!list->evdev->exist) + if (!evdev->exist) return -ENODEV; - return input_flush_device(&list->evdev->handle, file); + return input_flush_device(&evdev->handle, file); } static void evdev_free(struct evdev *evdev) @@ -100,48 +101,55 @@ static void evdev_free(struct evdev *evdev) kfree(evdev); } -static int evdev_release(struct inode * inode, struct file * file) +static int evdev_release(struct inode *inode, struct file *file) { - struct evdev_list *list = file->private_data; + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; - if (list->evdev->grab == list) { - input_release_device(&list->evdev->handle); - list->evdev->grab = NULL; + if (evdev->grab == client) { + input_release_device(&evdev->handle); + evdev->grab = NULL; } evdev_fasync(-1, file, 0); - list_del(&list->node); + list_del(&client->node); + kfree(client); - if (!--list->evdev->open) { - if (list->evdev->exist) - input_close_device(&list->evdev->handle); + if (!--evdev->open) { + if (evdev->exist) + input_close_device(&evdev->handle); else - evdev_free(list->evdev); + evdev_free(evdev); } - kfree(list); return 0; } -static int evdev_open(struct inode * inode, struct file * file) +static int evdev_open(struct inode *inode, struct file *file) { - struct evdev_list *list; + struct evdev_client *client; + struct evdev *evdev; int i = iminor(inode) - EVDEV_MINOR_BASE; - if (i >= EVDEV_MINORS || !evdev_table[i] || !evdev_table[i]->exist) + if (i >= EVDEV_MINORS) + return -ENODEV; + + evdev = evdev_table[i]; + + if (!evdev || !evdev->exist) return -ENODEV; - if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL))) + client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); + if (!client) return -ENOMEM; - list->evdev = evdev_table[i]; - list_add_tail(&list->node, &evdev_table[i]->list); - file->private_data = list; + client->evdev = evdev; + list_add_tail(&client->node, &evdev->client_list); - if (!list->evdev->open++) - if (list->evdev->exist) - input_open_device(&list->evdev->handle); + if (!evdev->open++ && evdev->exist) + input_open_device(&evdev->handle); + file->private_data = client; return 0; } @@ -243,54 +251,55 @@ 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_list *list = file->private_data; + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; struct input_event event; int retval = 0; - if (!list->evdev->exist) + if (!evdev->exist) return -ENODEV; while (retval < count) { if (evdev_event_from_user(buffer + retval, &event)) return -EFAULT; - input_inject_event(&list->evdev->handle, event.type, event.code, event.value); + input_inject_event(&evdev->handle, event.type, event.code, event.value); retval += evdev_event_size(); } return retval; } -static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) +static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - struct evdev_list *list = file->private_data; + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; int retval; if (count < evdev_event_size()) return -EINVAL; - if (list->head == list->tail && list->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(list->evdev->wait, - list->head != list->tail || (!list->evdev->exist)); - + retval = wait_event_interruptible(evdev->wait, + client->head != client->tail || !evdev->exist); if (retval) return retval; - if (!list->evdev->exist) + if (!evdev->exist) return -ENODEV; - while (list->head != list->tail && retval + evdev_event_size() <= count) { + while (client->head != client->tail && retval + evdev_event_size() <= count) { - struct input_event *event = (struct input_event *) list->buffer + list->tail; + struct input_event *event = (struct input_event *) client->buffer + client->tail; if (evdev_event_to_user(buffer + retval, event)) return -EFAULT; - list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1); + client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1); retval += evdev_event_size(); } @@ -300,11 +309,12 @@ static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count /* No kernel lock - fine */ static unsigned int evdev_poll(struct file *file, poll_table *wait) { - struct evdev_list *list = file->private_data; + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; - poll_wait(file, &list->evdev->wait, wait); - return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) | - (list->evdev->exist ? 0 : (POLLHUP | POLLERR)); + poll_wait(file, &evdev->wait, wait); + return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) | + (evdev->exist ? 0 : (POLLHUP | POLLERR)); } #ifdef CONFIG_COMPAT @@ -387,8 +397,8 @@ static int str_to_user(const char *str, unsigned int maxlen, void __user *p) static long evdev_ioctl_handler(struct file *file, unsigned int cmd, void __user *p, int compat_mode) { - struct evdev_list *list = file->private_data; - struct evdev *evdev = list->evdev; + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; struct input_dev *dev = evdev->handle.dev; struct input_absinfo abs; struct ff_effect effect; @@ -476,10 +486,10 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, return -EBUSY; if (input_grab_device(&evdev->handle)) return -EBUSY; - evdev->grab = list; + evdev->grab = client; return 0; } else { - if (evdev->grab != list) + if (evdev->grab != client) return -EINVAL; input_release_device(&evdev->handle); evdev->grab = NULL; @@ -624,7 +634,7 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, if (!evdev) return -ENOMEM; - INIT_LIST_HEAD(&evdev->list); + INIT_LIST_HEAD(&evdev->client_list); init_waitqueue_head(&evdev->wait); evdev->exist = 1; @@ -671,7 +681,7 @@ 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_list *list; + struct evdev_client *client; input_unregister_handle(handle); @@ -684,8 +694,8 @@ static void evdev_disconnect(struct input_handle *handle) input_flush_device(handle, NULL); input_close_device(handle); wake_up_interruptible(&evdev->wait); - list_for_each_entry(list, &evdev->list, node) - kill_fasync(&list->fasync, SIGIO, POLL_HUP); + list_for_each_entry(client, &evdev->client_list, node) + kill_fasync(&client->fasync, SIGIO, POLL_HUP); } else evdev_free(evdev); } -- cgit v1.2.3 From d542ed82fdc72cf63549deec19e86ee4addf2499 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 12 Apr 2007 01:30:15 -0400 Subject: Input: handlers - handle errors from input_open_device() Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/input/evdev.c') diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 8a4cce5c780..6cff8096d56 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -130,6 +130,7 @@ static int evdev_open(struct inode *inode, struct file *file) struct evdev_client *client; struct evdev *evdev; int i = iminor(inode) - EVDEV_MINOR_BASE; + int error; if (i >= EVDEV_MINORS) return -ENODEV; @@ -146,8 +147,14 @@ static int evdev_open(struct inode *inode, struct file *file) client->evdev = evdev; list_add_tail(&client->node, &evdev->client_list); - if (!evdev->open++ && evdev->exist) - input_open_device(&evdev->handle); + if (!evdev->open++ && evdev->exist) { + error = input_open_device(&evdev->handle); + if (error) { + list_del(&client->node); + kfree(client); + return error; + } + } file->private_data = client; return 0; -- cgit v1.2.3