GIT 0bfb22b4308bea5e7afd9434ac7e937b12fc5915 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/dtor/input.git commit Author: Dmitry Torokhov Date: Sun Oct 1 23:50:26 2006 -0400 Input: make input_{open|close}_device() more robust - add "dead" flag to input_dev structure and check the flag when opening, closing or trying to flush input device - fix error handling in input_open_device (was leaving device "used") - allow input_close_device() to be safely called for already closed devices Signed-off-by: Dmitry Torokhov commit a712be34e9e925f99b2c282783e193f89e17b630 Author: Dmitry Torokhov Date: Sun Oct 1 23:50:17 2006 -0400 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 commit aa682886065ac37c030ee97d2a726163b90ba9f9 Author: Dmitry Torokhov Date: Sun Oct 1 23:50:06 2006 -0400 Input: use krefs for refcounting in input handlers This should fix problems whith accessing memory already freed by another thread. Signed-off-by: Dmitry Torokhov drivers/char/keyboard.c | 27 +++++-- drivers/input/evbug.c | 29 +++++-- drivers/input/evdev.c | 156 +++++++++++++++++++++++--------------- drivers/input/input.c | 142 +++++++++++++++++++++++------------ drivers/input/joydev.c | 133 +++++++++++++++++++++------------ drivers/input/mousedev.c | 188 +++++++++++++++++++++++++++------------------- drivers/input/power.c | 27 +++++-- drivers/input/tsdev.c | 156 +++++++++++++++++++++++--------------- include/linux/input.h | 8 +- 9 files changed, 543 insertions(+), 323 deletions(-) diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index e201166..b1bb3ff 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -40,7 +40,6 @@ #include #include #include -static void kbd_disconnect(struct input_handle *handle); extern void ctrl_alt_del(void); /* @@ -1291,11 +1290,11 @@ static void kbd_event(struct input_handl * likes it, it can open it and get events from it. In this (kbd_connect) * function, we should decide which VT to bind that keyboard to initially. */ -static struct input_handle *kbd_connect(struct input_handler *handler, - struct input_dev *dev, - const struct input_device_id *id) +static int kbd_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct input_handle *handle; + int error; int i; for (i = KEY_RESERVED; i < BTN_MISC; i++) @@ -1303,19 +1302,31 @@ static struct input_handle *kbd_connect( break; if (i == BTN_MISC && !test_bit(EV_SND, dev->evbit)) - return NULL; + return -ENODEV; handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); if (!handle) - return NULL; + return -ENOMEM; handle->dev = dev; handle->handler = handler; handle->name = "kbd"; - input_open_device(handle); + error = input_link_handle(handle); + if (error) + goto err_free_handle; + + error = input_open_device(handle); + if (error) + goto err_unlink_handle; + + return 0; - return handle; + err_unlink_handle: + input_unlink_handle(handle); + err_free_handle: + kfree(handle); + return error; } static void kbd_disconnect(struct input_handle *handle) diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c index 5a9653c..2beee70 100644 --- a/drivers/input/evbug.c +++ b/drivers/input/evbug.c @@ -38,31 +38,41 @@ MODULE_AUTHOR("Vojtech Pavlik dev->phys, type, code, value); } -static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev, - const struct input_device_id *id) +static int evbug_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct input_handle *handle; + int error; - if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL))) - return NULL; + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; handle->dev = dev; handle->handler = handler; - handle->name = evbug_name; + handle->name = "evbug"; + + error = input_link_handle(handle); + if (error) + goto err_free_handle; - input_open_device(handle); + error = input_open_device(handle); + if (error) + goto err_unlink_handle; printk(KERN_DEBUG "evbug.c: Connected device: \"%s\", %s\n", dev->name, dev->phys); - return handle; + err_unlink_handle: + input_unlink_handle(handle); + err_free_handle: + kfree(handle); + return error; } static void evbug_disconnect(struct input_handle *handle) @@ -70,7 +80,6 @@ static void evbug_disconnect(struct inpu printk(KERN_DEBUG "evbug.c: Disconnected device: %s\n", handle->dev->phys); input_close_device(handle); - kfree(handle); } diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 6439f37..b71e2f3 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -23,14 +23,15 @@ #include #include struct evdev { - int exist; - int open; + int dead; int minor; char name[16]; struct input_handle handle; wait_queue_head_t wait; - struct evdev_list *grab; + struct kref kref; struct list_head list; + + struct evdev_list *grab; }; struct evdev_list { @@ -88,61 +89,72 @@ static int evdev_flush(struct file *file { struct evdev_list *list = file->private_data; - if (!list->evdev->exist) - return -ENODEV; - return input_flush_device(&list->evdev->handle, file); } -static void evdev_free(struct evdev *evdev) +static void evdev_free(struct kref *kref) { + struct evdev *evdev = container_of(kref, struct evdev, kref); + evdev_table[evdev->minor] = NULL; 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 *evdev = list->evdev; - if (list->evdev->grab == list) { - input_release_device(&list->evdev->handle); - list->evdev->grab = NULL; + if (evdev->grab == list) { + input_release_device(&evdev->handle); + evdev->grab = NULL; } evdev_fasync(-1, file, 0); + list_del(&list->node); + kfree(list); - if (!--list->evdev->open) { - if (list->evdev->exist) - input_close_device(&list->evdev->handle); - else - evdev_free(list->evdev); - } + input_close_device(&evdev->handle); + kref_put(&evdev->kref, evdev_free); - 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 *evdev; + int error; int i = iminor(inode) - EVDEV_MINOR_BASE; - if (i >= EVDEV_MINORS || !evdev_table[i] || !evdev_table[i]->exist) + if (i >= EVDEV_MINORS) return -ENODEV; - if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL))) + evdev = evdev_table[i]; + if (!evdev || evdev->dead) + return -ENODEV; + + list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL); + if (!list) return -ENOMEM; - list->evdev = evdev_table[i]; - list_add_tail(&list->node, &evdev_table[i]->list); + kref_get(&evdev->kref); + + list->evdev = evdev; + list_add_tail(&list->node, &evdev->list); file->private_data = list; - if (!list->evdev->open++) - if (list->evdev->exist) - input_open_device(&list->evdev->handle); + error = input_open_device(&evdev->handle); + if (error) + goto fail; return 0; + + fail: list_del(&list->node); + kfree(list); + kref_put(&evdev->kref, evdev_free); + return error; } #ifdef CONFIG_COMPAT @@ -243,44 +255,46 @@ static int evdev_event_to_user(char __us #endif /* CONFIG_COMPAT */ -static ssize_t evdev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) +static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { struct evdev_list *list = file->private_data; + struct evdev *evdev = list->evdev; struct input_event event; int retval = 0; - if (!list->evdev->exist) + if (evdev->dead) 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 *evdev = list->evdev; int retval; if (count < evdev_event_size()) return -EINVAL; - if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK)) + if (list->head == list->tail && !evdev->dead && (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, + list->head != list->tail || evdev->dead); if (retval) return retval; - if (!list->evdev->exist) + if (evdev->dead) return -ENODEV; while (list->head != list->tail && retval + evdev_event_size() <= count) { @@ -301,10 +315,11 @@ static ssize_t evdev_read(struct file * static unsigned int evdev_poll(struct file *file, poll_table *wait) { struct evdev_list *list = file->private_data; + struct evdev *evdev = list->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 (list->head != list->tail ? POLLIN | POLLRDNORM : 0) | + (evdev->dead ? POLLHUP | POLLERR : 0); } #ifdef CONFIG_COMPAT @@ -396,7 +411,7 @@ static long evdev_ioctl_handler(struct f int i, t, u, v; int error; - if (!evdev->exist) + if (evdev->dead) return -ENODEV; switch (cmd) { @@ -616,26 +631,29 @@ #endif .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; + kref_init(&evdev->kref); INIT_LIST_HEAD(&evdev->list); init_waitqueue_head(&evdev->wait); - evdev->exist = 1; evdev->minor = minor; evdev->handle.dev = dev; evdev->handle.name = evdev->name; @@ -643,17 +661,36 @@ static struct input_handle *evdev_connec evdev->handle.private = evdev; sprintf(evdev->name, "event%d", minor); - evdev_table[minor] = evdev; + devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), - cdev = class_device_create(&input_class, &dev->cdev, - MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), - dev->cdev.dev, evdev->name); + 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_link_handle(&evdev->handle); + if (error) + goto err_remove_link; + + evdev_table[minor] = evdev; + + return 0; - return &evdev->handle; + 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); + return error; } static void evdev_disconnect(struct input_handle *handle) @@ -664,16 +701,13 @@ static void evdev_disconnect(struct inpu sysfs_remove_link(&input_class.subsys.kset.kobj, evdev->name); class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + evdev->minor)); - evdev->exist = 0; - - if (evdev->open) { - 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); - } else - evdev_free(evdev); + + evdev->dead = 1; + wake_up_interruptible(&evdev->wait); + list_for_each_entry(list, &evdev->list, node) + kill_fasync(&list->fasync, SIGIO, POLL_HUP); + + kref_put(&evdev->kref, evdev_free); } static const struct input_device_id evdev_ids[] = { diff --git a/drivers/input/input.c b/drivers/input/input.c index 1c8c8a5..6ff85ca 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -261,26 +261,43 @@ int input_open_device(struct input_handl if (err) return err; + if (dev->dead) { + err = -ENODEV; + goto out; + } + handle->open++; - if (!dev->users++ && dev->open) + if (!dev->users++ && dev->open) { err = dev->open(dev); + if (err) { + dev->users--; + handle->open--; + } + } - if (err) - handle->open--; - + out: mutex_unlock(&dev->mutex); - return err; } 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 ret; - return 0; + mutex_lock(&dev->mutex); + + if (dev->dead) + ret = -ENODEV; + else if (dev->flush) + ret = dev->flush(dev, file); + else + ret = 0; + + mutex_unlock(&dev->mutex); + return ret; } EXPORT_SYMBOL(input_flush_device); @@ -292,20 +309,19 @@ void input_close_device(struct input_han mutex_lock(&dev->mutex); - if (!--dev->users && dev->close) - dev->close(dev); - handle->open--; + if (handle->open) { + if (dev->users) { + if (dev->close && !dev->dead) + dev->close(dev); + dev->users--; + } + handle->open--; + } mutex_unlock(&dev->mutex); } EXPORT_SYMBOL(input_close_device); -static void input_link_handle(struct input_handle *handle) -{ - list_add_tail(&handle->d_node, &handle->dev->h_list); - list_add_tail(&handle->h_node, &handle->handler->h_list); -} - #define MATCH_BIT(bit, max) \ for (i = 0; i < NBITS(max); i++) \ if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \ @@ -352,6 +368,28 @@ static const struct input_device_id *inp return NULL; } +static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) +{ + const struct input_device_id *id; + int error; + + if (handler->blacklist && input_match_device(handler->blacklist, dev)) + return -ENODEV; + + id = input_match_device(handler->id_table, dev); + if (!id) + return -ENODEV; + + error = handler->connect(handler, dev, id); + if (error && error != -ENODEV) + printk(KERN_ERR + "input: failed to attach handler %s to device %s, " + "error: %d\n", + handler->name, kobject_name(&dev->cdev.kobj), error); + + return error; +} + #ifdef CONFIG_PROC_FS static struct proc_dir_entry *proc_bus_input_dir; @@ -935,9 +973,7 @@ EXPORT_SYMBOL(input_free_device); int input_register_device(struct input_dev *dev) { static atomic_t input_no = ATOMIC_INIT(0); - struct input_handle *handle; struct input_handler *handler; - const struct input_device_id *id; const char *path; int error; @@ -983,13 +1019,7 @@ int input_register_device(struct input_d kfree(path); list_for_each_entry(handler, &input_handler_list, node) - if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) - if ((id = input_match_device(handler->id_table, dev))) - if ((handle = handler->connect(handler, dev, id))) { - input_link_handle(handle); - if (handler->start) - handler->start(handle); - } + input_attach_handler(dev, handler); input_wakeup_procfs_readers(); @@ -1004,7 +1034,7 @@ EXPORT_SYMBOL(input_register_device); void input_unregister_device(struct input_dev *dev) { - struct list_head *node, *next; + struct input_handle *handle, *next; int code; for (code = 0; code <= KEY_MAX; code++) @@ -1014,23 +1044,26 @@ void input_unregister_device(struct inpu del_timer_sync(&dev->timer); - list_for_each_safe(node, next, &dev->h_list) { - struct input_handle * handle = to_handle(node); - list_del_init(&handle->d_node); - list_del_init(&handle->h_node); + list_for_each_entry_safe(handle, next, &dev->h_list, d_node) { + input_unlink_handle(handle); handle->handler->disconnect(handle); } list_del_init(&dev->node); - sysfs_remove_group(&dev->cdev.kobj, &input_dev_caps_attr_group); - sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group); - sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group); - mutex_lock(&dev->mutex); + if (dev->flush) + dev->flush(dev, NULL); + if (dev->users && dev->close) + dev->close(dev); dev->name = dev->phys = dev->uniq = NULL; + dev->dead = 1; mutex_unlock(&dev->mutex); + sysfs_remove_group(&dev->cdev.kobj, &input_dev_caps_attr_group); + sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group); + sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group); + class_device_unregister(&dev->cdev); input_wakeup_procfs_readers(); @@ -1040,8 +1073,6 @@ EXPORT_SYMBOL(input_unregister_device); int input_register_handler(struct input_handler *handler) { struct input_dev *dev; - struct input_handle *handle; - const struct input_device_id *id; INIT_LIST_HEAD(&handler->h_list); @@ -1055,13 +1086,7 @@ int input_register_handler(struct input_ list_add_tail(&handler->node, &input_handler_list); list_for_each_entry(dev, &input_dev_list, node) - if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) - if ((id = input_match_device(handler->id_table, dev))) - if ((handle = handler->connect(handler, dev, id))) { - input_link_handle(handle); - if (handler->start) - handler->start(handle); - } + input_attach_handler(dev, handler); input_wakeup_procfs_readers(); return 0; @@ -1070,12 +1095,10 @@ EXPORT_SYMBOL(input_register_handler); void input_unregister_handler(struct input_handler *handler) { - struct list_head *node, *next; + struct input_handle *handle, *next; - list_for_each_safe(node, next, &handler->h_list) { - struct input_handle * handle = to_handle_h(node); - list_del_init(&handle->h_node); - list_del_init(&handle->d_node); + list_for_each_entry_safe(handle, next, &handler->h_list, h_node) { + input_unlink_handle(handle); handler->disconnect(handle); } @@ -1088,6 +1111,25 @@ void input_unregister_handler(struct inp } EXPORT_SYMBOL(input_unregister_handler); +int input_link_handle(struct input_handle *handle) +{ + list_add_tail(&handle->d_node, &handle->dev->h_list); + list_add_tail(&handle->h_node, &handle->handler->h_list); + + if (handle->handler->start) + handle->handler->start(handle); + + return 0; +} +EXPORT_SYMBOL(input_link_handle); + +void input_unlink_handle(struct input_handle *handle) +{ + list_del_init(&handle->h_node); + list_del_init(&handle->d_node); +} +EXPORT_SYMBOL(input_unlink_handle); + static int input_open_file(struct inode *inode, struct file *file) { struct input_handler *handler = input_table[iminor(inode) >> 5]; diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 9f3529a..67a3c68 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -37,13 +37,14 @@ #define JOYDEV_MINORS 16 #define JOYDEV_BUFFER_SIZE 64 struct joydev { - int exist; - int open; + int dead; int minor; char name[16]; struct input_handle handle; wait_queue_head_t wait; + struct kref kref; struct list_head list; + struct js_corr corr[ABS_MAX + 1]; struct JS_DATA_SAVE_TYPE glue; int nabs; @@ -139,8 +140,10 @@ static int joydev_fasync(int fd, struct return retval < 0 ? retval : 0; } -static void joydev_free(struct joydev *joydev) +static void joydev_free(struct kref *kref) { + struct joydev *joydev = container_of(kref, struct joydev, kref); + joydev_table[joydev->minor] = NULL; kfree(joydev); } @@ -148,42 +151,53 @@ static void joydev_free(struct joydev *j static int joydev_release(struct inode * inode, struct file * file) { struct joydev_list *list = file->private_data; + struct joydev *joydev = list->joydev; joydev_fasync(-1, file, 0); list_del(&list->node); + kfree(list); - if (!--list->joydev->open) { - if (list->joydev->exist) - input_close_device(&list->joydev->handle); - else - joydev_free(list->joydev); - } + input_close_device(&joydev->handle); + kref_put(&joydev->kref, joydev_free); - kfree(list); return 0; } static int joydev_open(struct inode *inode, struct file *file) { struct joydev_list *list; + struct joydev *joydev; + int error; int i = iminor(inode) - JOYDEV_MINOR_BASE; - if (i >= JOYDEV_MINORS || !joydev_table[i]) + if (i >= JOYDEV_MINORS) return -ENODEV; - if (!(list = kzalloc(sizeof(struct joydev_list), GFP_KERNEL))) + joydev = joydev_table[i]; + if (!joydev || joydev->dead) + return -ENODEV; + + list = kzalloc(sizeof(struct joydev_list), GFP_KERNEL); + if (!list) return -ENOMEM; - list->joydev = joydev_table[i]; - list_add_tail(&list->node, &joydev_table[i]->list); + kref_get(&joydev->kref); + + list->joydev = joydev; + list_add_tail(&list->node, &joydev->list); file->private_data = list; - if (!list->joydev->open++) - if (list->joydev->exist) - input_open_device(&list->joydev->handle); + error = input_open_device(&joydev->handle); + if (error) + goto fail; return 0; + + fail: list_del(&list->node); + kfree(list); + kref_put(&joydev->kref, joydev_free); + return error; } static ssize_t joydev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) @@ -198,7 +212,7 @@ static ssize_t joydev_read(struct file * struct input_dev *input = joydev->handle.dev; int retval = 0; - if (!list->joydev->exist) + if (joydev->dead) return -ENODEV; if (count < sizeof(struct js_event)) @@ -227,15 +241,15 @@ static ssize_t joydev_read(struct file * list->head == list->tail && (file->f_flags & O_NONBLOCK)) return -EAGAIN; - retval = wait_event_interruptible(list->joydev->wait, - !list->joydev->exist || + retval = wait_event_interruptible(joydev->wait, + joydev->dead || list->startup < joydev->nabs + joydev->nkey || list->head != list->tail); if (retval) return retval; - if (!list->joydev->exist) + if (joydev->dead) return -ENODEV; while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { @@ -277,10 +291,12 @@ static ssize_t joydev_read(struct file * static unsigned int joydev_poll(struct file *file, poll_table *wait) { struct joydev_list *list = file->private_data; + struct joydev *joydev = list->joydev; - poll_wait(file, &list->joydev->wait, wait); - return ((list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) ? - (POLLIN | POLLRDNORM) : 0) | (list->joydev->exist ? 0 : (POLLHUP | POLLERR)); + poll_wait(file, &joydev->wait, wait); + return (list->head != list->tail || + list->startup < joydev->nabs + joydev->nkey ? POLLIN | POLLRDNORM : 0) | + (joydev->dead ? POLLHUP | POLLERR : 0); } static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp) @@ -381,7 +397,7 @@ static long joydev_compat_ioctl(struct f struct JS_DATA_SAVE_TYPE_32 ds32; int err; - if (!joydev->exist) + if (joydev->dead) return -ENODEV; switch(cmd) { @@ -432,7 +448,7 @@ static int joydev_ioctl(struct inode *in struct joydev *joydev = list->joydev; void __user *argp = (void __user *)arg; - if (!joydev->exist) + if (joydev->dead) return -ENODEV; switch(cmd) { @@ -465,27 +481,30 @@ #endif .fasync = joydev_fasync, }; -static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, - const struct input_device_id *id) +static int joydev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct joydev *joydev; struct class_device *cdev; + dev_t devt; int i, j, t, minor; + int error; for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++); if (minor == JOYDEV_MINORS) { printk(KERN_ERR "joydev: no more free joydev devices\n"); - return NULL; + return -ENFILE; } - if (!(joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL))) - return NULL; + joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL); + if (!joydev) + return -ENOMEM; + kref_init(&joydev->kref); INIT_LIST_HEAD(&joydev->list); init_waitqueue_head(&joydev->wait); joydev->minor = minor; - joydev->exist = 1; joydev->handle.dev = dev; joydev->handle.name = joydev->name; joydev->handle.handler = handler; @@ -532,19 +551,39 @@ static struct input_handle *joydev_conne joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); } - joydev_table[minor] = joydev; + devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor), - cdev = class_device_create(&input_class, &dev->cdev, - MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor), - dev->cdev.dev, joydev->name); + cdev = class_device_create(&input_class, &dev->cdev, devt, + dev->cdev.dev, joydev->name); + if (IS_ERR(cdev)) { + error = PTR_ERR(cdev); + goto err_free_joydev; + } /* temporary symlink to keep userspace happy */ - sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj, - joydev->name); + error = sysfs_create_link(&input_class.subsys.kset.kobj, + &cdev->kobj, joydev->name); + if (error) + goto err_cdev_destroy; + + error = input_link_handle(&joydev->handle); + if (error) + goto err_remove_link; + + joydev_table[minor] = joydev; + + return 0; - return &joydev->handle; + err_remove_link: + sysfs_remove_link(&input_class.subsys.kset.kobj, joydev->name); + err_cdev_destroy: + class_device_destroy(&input_class, devt); + err_free_joydev: + kfree(joydev); + return error; } + static void joydev_disconnect(struct input_handle *handle) { struct joydev *joydev = handle->private; @@ -552,15 +591,13 @@ static void joydev_disconnect(struct inp sysfs_remove_link(&input_class.subsys.kset.kobj, joydev->name); class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + joydev->minor)); - joydev->exist = 0; - - if (joydev->open) { - input_close_device(handle); - wake_up_interruptible(&joydev->wait); - list_for_each_entry(list, &joydev->list, node) - kill_fasync(&list->fasync, SIGIO, POLL_HUP); - } else - joydev_free(joydev); + + joydev->dead = 1; + wake_up_interruptible(&joydev->wait); + list_for_each_entry(list, &joydev->list, node) + kill_fasync(&list->fasync, SIGIO, POLL_HUP); + + kref_put(&joydev->kref, joydev_free); } static const struct input_device_id joydev_blacklist[] = { diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index a22a74a..3c3ff63 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -58,13 +58,13 @@ struct mousedev_hw_data { }; struct mousedev { - int exist; - int open; + int dead; int minor; char name[16]; + struct input_handle handle; wait_queue_head_t wait; + struct kref kref; struct list_head list; - struct input_handle handle; struct mousedev_hw_data packet; unsigned int pkt_count; @@ -111,6 +111,7 @@ static struct input_handler mousedev_han static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; static struct mousedev mousedev_mix; +static int mousedev_mix_open; #define fx(i) (mousedev->old_x[(mousedev->pkt_count - (i)) & 03]) #define fy(i) (mousedev->old_y[(mousedev->pkt_count - (i)) & 03]) @@ -358,8 +359,10 @@ static int mousedev_fasync(int fd, struc return retval < 0 ? retval : 0; } -static void mousedev_free(struct mousedev *mousedev) +static void mousedev_free(struct kref *kref) { + struct mousedev *mousedev = container_of(kref, struct mousedev, kref); + mousedev_table[mousedev->minor] = NULL; kfree(mousedev); } @@ -368,46 +371,42 @@ static void mixdev_release(void) { struct input_handle *handle; + mousedev_mix_open--; + list_for_each_entry(handle, &mousedev_handler.h_list, h_node) { struct mousedev *mousedev = handle->private; - if (!mousedev->open) { - if (mousedev->exist) - input_close_device(&mousedev->handle); - else - mousedev_free(mousedev); - } + input_close_device(handle); + kref_put(&mousedev->kref, mousedev_free); } } -static int mousedev_release(struct inode * inode, struct file * file) +static int mousedev_release(struct inode *inode, struct file *file) { struct mousedev_list *list = file->private_data; + struct mousedev *mousedev = list->mousedev; mousedev_fasync(-1, file, 0); list_del(&list->node); + kfree(list); - if (!--list->mousedev->open) { - if (list->mousedev->minor == MOUSEDEV_MIX) - mixdev_release(); - else if (!mousedev_mix.open) { - if (list->mousedev->exist) - input_close_device(&list->mousedev->handle); - else - mousedev_free(list->mousedev); - } - } + if (mousedev->minor == MOUSEDEV_MIX) + mixdev_release(); + else + input_close_device(&mousedev->handle); + + kref_put(&mousedev->kref, mousedev_free); - kfree(list); return 0; } -static int mousedev_open(struct inode * inode, struct file * file) +static int mousedev_open(struct inode *inode, struct file *file) { struct mousedev_list *list; struct input_handle *handle; - struct mousedev *mousedev; + struct mousedev *mousedev, *md; + int error; int i; #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX @@ -417,29 +416,41 @@ #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX #endif i = iminor(inode) - MOUSEDEV_MINOR_BASE; - if (i >= MOUSEDEV_MINORS || !mousedev_table[i]) + if (i >= MOUSEDEV_MINORS) return -ENODEV; - if (!(list = kzalloc(sizeof(struct mousedev_list), GFP_KERNEL))) + mousedev = mousedev_table[i]; + if (!mousedev || mousedev->dead) + return -ENODEV; + + list = kzalloc(sizeof(struct mousedev_list), GFP_KERNEL); + if (!list) return -ENOMEM; + kref_get(&mousedev->kref); + spin_lock_init(&list->packet_lock); list->pos_x = xres / 2; list->pos_y = yres / 2; - list->mousedev = mousedev_table[i]; - list_add_tail(&list->node, &mousedev_table[i]->list); + list->mousedev = mousedev; + list_add_tail(&list->node, &mousedev->list); file->private_data = list; - if (!list->mousedev->open++) { - if (list->mousedev->minor == MOUSEDEV_MIX) { - list_for_each_entry(handle, &mousedev_handler.h_list, h_node) { - mousedev = handle->private; - if (!mousedev->open && mousedev->exist) - input_open_device(handle); - } - } else - if (!mousedev_mix.open && list->mousedev->exist) - input_open_device(&list->mousedev->handle); + if (mousedev->minor == MOUSEDEV_MIX) { + mousedev_mix_open++; + list_for_each_entry(handle, &mousedev_handler.h_list, h_node) { + md = handle->private; + kref_get(&md->kref); + input_open_device(handle); + } + } else { + error = input_open_device(&mousedev->handle); + if (error) { + list_del(&list->node); + kfree(list); + kref_put(&mousedev->kref, mousedev_free); + return error; + } } return 0; @@ -574,18 +585,19 @@ static ssize_t mousedev_write(struct fil static ssize_t mousedev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos) { struct mousedev_list *list = file->private_data; + struct mousedev *mousedev = list->mousedev; int retval = 0; if (!list->ready && !list->buffer && (file->f_flags & O_NONBLOCK)) return -EAGAIN; - retval = wait_event_interruptible(list->mousedev->wait, - !list->mousedev->exist || list->ready || list->buffer); + retval = wait_event_interruptible(mousedev->wait, + mousedev->dead || list->ready || list->buffer); if (retval) return retval; - if (!list->mousedev->exist) + if (mousedev->dead) return -ENODEV; if (!list->buffer && list->ready) { @@ -608,10 +620,11 @@ static ssize_t mousedev_read(struct file static unsigned int mousedev_poll(struct file *file, poll_table *wait) { struct mousedev_list *list = file->private_data; + struct mousedev *mousedev = list->mousedev; - poll_wait(file, &list->mousedev->wait, wait); - return ((list->ready || list->buffer) ? (POLLIN | POLLRDNORM) : 0) | - (list->mousedev->exist ? 0 : (POLLHUP | POLLERR)); + poll_wait(file, &mousedev->wait, wait); + return (list->ready || list->buffer ? POLLIN | POLLRDNORM : 0) | + (mousedev->dead ? POLLHUP | POLLERR : 0); } static const struct file_operations mousedev_fops = { @@ -624,47 +637,75 @@ static const struct file_operations mous .fasync = mousedev_fasync, }; -static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev, - const struct input_device_id *id) +static int mousedev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct mousedev *mousedev; struct class_device *cdev; - int minor = 0; + dev_t devt; + int minor; + int error; for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++); if (minor == MOUSEDEV_MINORS) { printk(KERN_ERR "mousedev: no more free mousedev devices\n"); - return NULL; + return -ENFILE; } - if (!(mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL))) - return NULL; + mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL); + if (!mousedev) + return -ENOMEM; + kref_init(&mousedev->kref); INIT_LIST_HEAD(&mousedev->list); init_waitqueue_head(&mousedev->wait); mousedev->minor = minor; - mousedev->exist = 1; mousedev->handle.dev = dev; mousedev->handle.name = mousedev->name; mousedev->handle.handler = handler; mousedev->handle.private = mousedev; sprintf(mousedev->name, "mouse%d", minor); - if (mousedev_mix.open) - input_open_device(&mousedev->handle); - - mousedev_table[minor] = mousedev; + devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor), - cdev = class_device_create(&input_class, &dev->cdev, - MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor), - dev->cdev.dev, mousedev->name); + cdev = class_device_create(&input_class, &dev->cdev, devt, + dev->cdev.dev, mousedev->name); + if (IS_ERR(cdev)) { + error = PTR_ERR(cdev); + goto err_free_mousedev; + } /* temporary symlink to keep userspace happy */ - sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj, - mousedev->name); + error = sysfs_create_link(&input_class.subsys.kset.kobj, + &cdev->kobj, mousedev->name); + if (error) + goto err_cdev_destroy; + + error = input_link_handle(&mousedev->handle); + if (error) + goto err_remove_link; + + if (mousedev_mix_open) { + error = input_open_device(&mousedev->handle); + if (error) + goto err_unlink_handle; + kref_get(&mousedev->kref); + } + + mousedev_table[minor] = mousedev; - return &mousedev->handle; + return 0; + + err_unlink_handle: + input_unlink_handle(&mousedev->handle); + err_remove_link: + sysfs_remove_link(&input_class.subsys.kset.kobj, mousedev->name); + err_cdev_destroy: + class_device_destroy(&input_class, devt); + err_free_mousedev: + kfree(mousedev); + return error; } static void mousedev_disconnect(struct input_handle *handle) @@ -675,18 +716,13 @@ static void mousedev_disconnect(struct i sysfs_remove_link(&input_class.subsys.kset.kobj, mousedev->name); class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + mousedev->minor)); - mousedev->exist = 0; - if (mousedev->open) { - input_close_device(handle); - wake_up_interruptible(&mousedev->wait); - list_for_each_entry(list, &mousedev->list, node) - kill_fasync(&list->fasync, SIGIO, POLL_HUP); - } else { - if (mousedev_mix.open) - input_close_device(handle); - mousedev_free(mousedev); - } + mousedev->dead = 1; + wake_up_interruptible(&mousedev->wait); + list_for_each_entry(list, &mousedev->list, node) + kill_fasync(&list->fasync, SIGIO, POLL_HUP); + + kref_put(&mousedev->kref, mousedev_free); } static const struct input_device_id mousedev_ids[] = { @@ -714,7 +750,7 @@ static const struct input_device_id mous .absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_TOOL_WIDTH) }, }, /* A touchpad */ - { }, /* Terminating entry */ + { }, /* Terminating entry */ }; MODULE_DEVICE_TABLE(input, mousedev_ids); @@ -745,13 +781,13 @@ static int __init mousedev_init(void) if (error) return error; - memset(&mousedev_mix, 0, sizeof(struct mousedev)); + kref_init(&mousedev_mix.kref); INIT_LIST_HEAD(&mousedev_mix.list); init_waitqueue_head(&mousedev_mix.wait); - mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; - mousedev_mix.exist = 1; mousedev_mix.minor = MOUSEDEV_MIX; + mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; + cdev = class_device_create(&input_class, NULL, MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), NULL, "mice"); if (IS_ERR(cdev)) { diff --git a/drivers/input/power.c b/drivers/input/power.c index ee82464..81d00fd 100644 --- a/drivers/input/power.c +++ b/drivers/input/power.c @@ -96,22 +96,33 @@ static void power_event(struct input_han return; } -static struct input_handle *power_connect(struct input_handler *handler, - struct input_dev *dev, - const struct input_device_id *id) +static int power_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct input_handle *handle; - if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL))) - return NULL; + handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; handle->dev = dev; handle->handler = handler; - input_open_device(handle); + error = input_add_handle(handle); + if (error) + goto err_free_handle; - printk(KERN_INFO "power.c: Adding power management to input layer\n"); - return handle; + error = input_open_device(handle); + if (error) + goto err_unlink_handle; + + return 0; + + err_unlink_handle: + input_unlink_handle(handle); + err_free_handle: + kfree(handle); + return error; } static void power_disconnect(struct input_handle *handle) diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c index a730c46..d61da4b 100644 --- a/drivers/input/tsdev.c +++ b/drivers/input/tsdev.c @@ -106,13 +106,14 @@ struct ts_calibration { }; struct tsdev { - int exist; - int open; + int dead; int minor; char name[8]; + struct input_handle handle; wait_queue_head_t wait; + struct kref kref; struct list_head list; - struct input_handle handle; + int x, y, pressure; struct ts_calibration cal; }; @@ -146,50 +147,64 @@ static int tsdev_fasync(int fd, struct f return retval < 0 ? retval : 0; } +static void tsdev_free(struct kref *kref) +{ + struct tsdev *tsdev = container_of(kref, struct tsdev, kref); + + tsdev_table[tsdev->minor] = NULL; + kfree(tsdev); +} + static int tsdev_open(struct inode *inode, struct file *file) { - int i = iminor(inode) - TSDEV_MINOR_BASE; struct tsdev_list *list; + struct tsdev *tsdev; + int error; + int i = iminor(inode) - TSDEV_MINOR_BASE; + + if (i >= TSDEV_MINORS) + return -ENODEV; - if (i >= TSDEV_MINORS || !tsdev_table[i & TSDEV_MINOR_MASK]) + tsdev = tsdev_table[i & TSDEV_MINOR_MASK]; + if (!tsdev || tsdev->dead) return -ENODEV; - if (!(list = kzalloc(sizeof(struct tsdev_list), GFP_KERNEL))) + list = kzalloc(sizeof(struct tsdev_list), GFP_KERNEL); + if (!list) return -ENOMEM; - list->raw = (i >= TSDEV_MINORS/2) ? 1 : 0; + kref_get(&tsdev->kref); - i &= TSDEV_MINOR_MASK; - list->tsdev = tsdev_table[i]; - list_add_tail(&list->node, &tsdev_table[i]->list); + list->tsdev = tsdev; + list->raw = (i >= TSDEV_MINORS / 2); + list_add_tail(&list->node, &tsdev->list); file->private_data = list; - if (!list->tsdev->open++) - if (list->tsdev->exist) - input_open_device(&list->tsdev->handle); + error = input_open_device(&tsdev->handle); + if (error) + goto fail; + return 0; -} -static void tsdev_free(struct tsdev *tsdev) -{ - tsdev_table[tsdev->minor] = NULL; - kfree(tsdev); + fail: list_del(&list->node); + kfree(list); + kref_put(&tsdev->kref, tsdev_free); + return error; } static int tsdev_release(struct inode *inode, struct file *file) { struct tsdev_list *list = file->private_data; + struct tsdev *tsdev = list->tsdev; tsdev_fasync(-1, file, 0); - list_del(&list->node); - if (!--list->tsdev->open) { - if (list->tsdev->exist) - input_close_device(&list->tsdev->handle); - else - tsdev_free(list->tsdev); - } + list_del(&list->node); kfree(list); + + input_close_device(&tsdev->handle); + kref_put(&tsdev->kref, tsdev_free); + return 0; } @@ -197,22 +212,23 @@ static ssize_t tsdev_read(struct file *f loff_t * ppos) { struct tsdev_list *list = file->private_data; + struct tsdev *tsdev = list->tsdev; int retval = 0; - if (list->head == list->tail && list->tsdev->exist && (file->f_flags & O_NONBLOCK)) + if (list->head == list->tail && !tsdev->dead && (file->f_flags & O_NONBLOCK)) return -EAGAIN; - retval = wait_event_interruptible(list->tsdev->wait, - list->head != list->tail || !list->tsdev->exist); + retval = wait_event_interruptible(tsdev->wait, + list->head != list->tail || tsdev->dead); if (retval) return retval; - if (!list->tsdev->exist) + if (tsdev->dead) return -ENODEV; while (list->head != list->tail && - retval + sizeof (struct ts_event) <= count) { + retval + sizeof(struct ts_event) <= count) { if (copy_to_user (buffer + retval, list->event + list->tail, sizeof (struct ts_event))) return -EFAULT; @@ -227,10 +243,11 @@ static ssize_t tsdev_read(struct file *f static unsigned int tsdev_poll(struct file *file, poll_table * wait) { struct tsdev_list *list = file->private_data; + struct tsdev *tsdev = list->tsdev; - poll_wait(file, &list->tsdev->wait, wait); - return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) | - (list->tsdev->exist ? 0 : (POLLHUP | POLLERR)); + poll_wait(file, &tsdev->wait, wait); + return (list->head != list->tail ? POLLIN | POLLRDNORM : 0) | + (tsdev->dead ? POLLHUP | POLLERR : 0); } static int tsdev_ioctl(struct inode *inode, struct file *file, @@ -242,14 +259,14 @@ static int tsdev_ioctl(struct inode *ino switch (cmd) { case TS_GET_CAL: - if (copy_to_user ((void __user *)arg, &tsdev->cal, - sizeof (struct ts_calibration))) + if (copy_to_user((void __user *)arg, &tsdev->cal, + sizeof (struct ts_calibration))) retval = -EFAULT; break; case TS_SET_CAL: - if (copy_from_user (&tsdev->cal, (void __user *)arg, - sizeof (struct ts_calibration))) + if (copy_from_user(&tsdev->cal, (void __user *)arg, + sizeof (struct ts_calibration))) retval = -EFAULT; break; @@ -366,30 +383,32 @@ static void tsdev_event(struct input_han wake_up_interruptible(&tsdev->wait); } -static struct input_handle *tsdev_connect(struct input_handler *handler, - struct input_dev *dev, - const struct input_device_id *id) +static int tsdev_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) { struct tsdev *tsdev; struct class_device *cdev; + dev_t devt; int minor, delta; + 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"); - return NULL; + return -ENFILE; } - if (!(tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL))) - return NULL; + tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL); + if (!tsdev) + return -ENOMEM; + kref_init(&tsdev->kref); INIT_LIST_HEAD(&tsdev->list); init_waitqueue_head(&tsdev->wait); sprintf(tsdev->name, "ts%d", minor); - tsdev->exist = 1; tsdev->minor = minor; tsdev->handle.dev = dev; tsdev->handle.name = tsdev->name; @@ -409,17 +428,36 @@ static struct input_handle *tsdev_connec tsdev->cal.yscale = (yres << 8) / delta; tsdev->cal.ytrans = - ((dev->absmin [ABS_Y] * tsdev->cal.yscale) >> 8); - tsdev_table[minor] = tsdev; + devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor), - cdev = class_device_create(&input_class, &dev->cdev, - MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor), - dev->cdev.dev, tsdev->name); + cdev = class_device_create(&input_class, &dev->cdev, devt, + dev->cdev.dev, tsdev->name); + if (IS_ERR(cdev)) { + error = PTR_ERR(cdev); + goto err_free_tsdev; + } /* temporary symlink to keep userspace happy */ - sysfs_create_link(&input_class.subsys.kset.kobj, &cdev->kobj, - tsdev->name); + error = sysfs_create_link(&input_class.subsys.kset.kobj, + &cdev->kobj, tsdev->name); + if (error) + goto err_cdev_destroy; - return &tsdev->handle; + error = input_link_handle(&tsdev->handle); + if (error) + goto err_remove_link; + + tsdev_table[minor] = tsdev; + + return 0; + + err_remove_link: + sysfs_remove_link(&input_class.subsys.kset.kobj, tsdev->name); + err_cdev_destroy: + class_device_destroy(&input_class, devt); + err_free_tsdev: + kfree(tsdev); + return error; } static void tsdev_disconnect(struct input_handle *handle) @@ -430,15 +468,13 @@ static void tsdev_disconnect(struct inpu sysfs_remove_link(&input_class.subsys.kset.kobj, tsdev->name); class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + tsdev->minor)); - tsdev->exist = 0; - - if (tsdev->open) { - input_close_device(handle); - wake_up_interruptible(&tsdev->wait); - list_for_each_entry(list, &tsdev->list, node) - kill_fasync(&list->fasync, SIGIO, POLL_HUP); - } else - tsdev_free(tsdev); + + tsdev->dead = 1; + wake_up_interruptible(&tsdev->wait); + list_for_each_entry(list, &tsdev->list, node) + kill_fasync(&list->fasync, SIGIO, POLL_HUP); + + kref_put(&tsdev->kref, tsdev_free); } static const struct input_device_id tsdev_ids[] = { diff --git a/include/linux/input.h b/include/linux/input.h index 5770105..a5799e7 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -954,7 +954,8 @@ struct input_dev { struct timer_list timer; struct pt_regs *regs; - int state; + + int dead; int sync; @@ -1060,7 +1061,7 @@ struct input_handler { void *private; void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); - struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); + int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); void (*disconnect)(struct input_handle *handle); void (*start)(struct input_handle *handle); @@ -1113,6 +1114,9 @@ void input_unregister_device(struct inpu int input_register_handler(struct input_handler *); void input_unregister_handler(struct input_handler *); +int input_link_handle(struct input_handle *); +void input_unlink_handle(struct input_handle *); + int input_grab_device(struct input_handle *); void input_release_device(struct input_handle *);