diff options
Diffstat (limited to 'drivers/input/misc/uinput.c')
| -rw-r--r-- | drivers/input/misc/uinput.c | 747 |
1 files changed, 507 insertions, 240 deletions
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index a0a4bbaef02c..13336a2fd49c 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -1,25 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * User level driver support for input subsystem * * Heavily based on evdev.c by Vojtech Pavlik * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org> * * Changes/Revisions: + * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>) + * - add UI_GET_SYSNAME ioctl * 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>) * - updated ff support for the changes in kernel interface * - added MODULE_VERSION @@ -29,6 +18,7 @@ * 0.1 20/06/2002 * - first public version */ +#include <uapi/linux/uinput.h> #include <linux/poll.h> #include <linux/sched.h> #include <linux/slab.h> @@ -36,19 +26,65 @@ #include <linux/init.h> #include <linux/fs.h> #include <linux/miscdevice.h> -#include <linux/uinput.h> +#include <linux/overflow.h> #include <linux/input/mt.h> #include "../input-compat.h" +#define UINPUT_NAME "uinput" +#define UINPUT_BUFFER_SIZE 16 +#define UINPUT_NUM_REQUESTS 16 +#define UINPUT_TIMESTAMP_ALLOWED_OFFSET_SECS 10 + +enum uinput_state { UIST_NEW_DEVICE, UIST_SETUP_COMPLETE, UIST_CREATED }; + +struct uinput_request { + unsigned int id; + unsigned int code; /* UI_FF_UPLOAD, UI_FF_ERASE */ + + int retval; + struct completion done; + + union { + unsigned int effect_id; + struct { + struct ff_effect *effect; + struct ff_effect *old; + } upload; + } u; +}; + +struct uinput_device { + struct input_dev *dev; + struct mutex mutex; + enum uinput_state state; + wait_queue_head_t waitq; + unsigned char ready; + unsigned char head; + unsigned char tail; + struct input_event buff[UINPUT_BUFFER_SIZE]; + unsigned int ff_effects_max; + + struct uinput_request *requests[UINPUT_NUM_REQUESTS]; + wait_queue_head_t requests_waitq; + spinlock_t requests_lock; +}; + static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct uinput_device *udev = input_get_drvdata(dev); + struct timespec64 ts; + + ktime_get_ts64(&ts); + + udev->buff[udev->head] = (struct input_event) { + .input_event_sec = ts.tv_sec, + .input_event_usec = ts.tv_nsec / NSEC_PER_USEC, + .type = type, + .code = code, + .value = value, + }; - udev->buff[udev->head].type = type; - udev->buff[udev->head].code = code; - udev->buff[udev->head].value = value; - do_gettimeofday(&udev->buff[udev->head].time); udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE; wake_up_interruptible(&udev->waitq); @@ -96,14 +132,15 @@ static int uinput_request_reserve_slot(struct uinput_device *udev, uinput_request_alloc_id(udev, request)); } -static void uinput_request_done(struct uinput_device *udev, - struct uinput_request *request) +static void uinput_request_release_slot(struct uinput_device *udev, + unsigned int id) { /* Mark slot as available */ - udev->requests[request->id] = NULL; - wake_up(&udev->requests_waitq); + spin_lock(&udev->requests_lock); + udev->requests[id] = NULL; + spin_unlock(&udev->requests_lock); - complete(&request->done); + wake_up(&udev->requests_waitq); } static int uinput_request_send(struct uinput_device *udev, @@ -136,20 +173,26 @@ static int uinput_request_send(struct uinput_device *udev, static int uinput_request_submit(struct uinput_device *udev, struct uinput_request *request) { - int error; + int retval; - error = uinput_request_reserve_slot(udev, request); - if (error) - return error; + retval = uinput_request_reserve_slot(udev, request); + if (retval) + return retval; - error = uinput_request_send(udev, request); - if (error) { - uinput_request_done(udev, request); - return error; + retval = uinput_request_send(udev, request); + if (retval) + goto out; + + if (!wait_for_completion_timeout(&request->done, 30 * HZ)) { + retval = -ETIMEDOUT; + goto out; } - wait_for_completion(&request->done); - return request->retval; + retval = request->retval; + + out: + uinput_request_release_slot(udev, request->id); + return retval; } /* @@ -167,7 +210,7 @@ static void uinput_flush_requests(struct uinput_device *udev) request = udev->requests[i]; if (request) { request->retval = -ENODEV; - uinput_request_done(udev, request); + complete(&request->done); } } @@ -228,6 +271,18 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id) return uinput_request_submit(udev, &request); } +static int uinput_dev_flush(struct input_dev *dev, struct file *file) +{ + /* + * If we are called with file == NULL that means we are tearing + * down the device, and therefore we can not handle FF erase + * requests: either we are handling UI_DEV_DESTROY (and holding + * the udev->mutex), or the file descriptor is closed and there is + * nobody on the other side anymore. + */ + return file ? input_ff_flush(dev, file) : 0; +} + static void uinput_destroy_device(struct uinput_device *udev) { const char *name, *phys; @@ -254,13 +309,37 @@ static void uinput_destroy_device(struct uinput_device *udev) static int uinput_create_device(struct uinput_device *udev) { struct input_dev *dev = udev->dev; - int error; + int error, nslot; if (udev->state != UIST_SETUP_COMPLETE) { printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME); return -EINVAL; } + if (test_bit(EV_ABS, dev->evbit)) { + input_alloc_absinfo(dev); + if (!dev->absinfo) { + error = -EINVAL; + goto fail1; + } + + if (test_bit(ABS_MT_SLOT, dev->absbit)) { + nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; + error = input_mt_init_slots(dev, nslot, 0); + if (error) + goto fail1; + } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { + input_set_events_per_packet(dev, 60); + } + } + + if (test_bit(EV_FF, dev->evbit) && !udev->ff_effects_max) { + printk(KERN_DEBUG "%s: ff_effects_max should be non-zero when FF_BIT is set\n", + UINPUT_NAME); + error = -EINVAL; + goto fail1; + } + if (udev->ff_effects_max) { error = input_ff_create(dev, udev->ff_effects_max); if (error) @@ -271,8 +350,18 @@ static int uinput_create_device(struct uinput_device *udev) dev->ff->playback = uinput_dev_playback; dev->ff->set_gain = uinput_dev_set_gain; dev->ff->set_autocenter = uinput_dev_set_autocenter; + /* + * The standard input_ff_flush() implementation does + * not quite work for uinput as we can't reasonably + * handle FF requests during device teardown. + */ + dev->flush = uinput_dev_flush; } + dev->event = uinput_dev_event; + + input_set_drvdata(udev->dev, udev); + error = input_register_device(udev->dev); if (error) goto fail2; @@ -290,7 +379,7 @@ static int uinput_open(struct inode *inode, struct file *file) { struct uinput_device *newdev; - newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL); + newdev = kzalloc(sizeof(*newdev), GFP_KERNEL); if (!newdev) return -ENOMEM; @@ -301,7 +390,46 @@ static int uinput_open(struct inode *inode, struct file *file) newdev->state = UIST_NEW_DEVICE; file->private_data = newdev; - nonseekable_open(inode, file); + stream_open(inode, file); + + return 0; +} + +static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code, + const struct input_absinfo *abs) +{ + int min, max, range; + + min = abs->minimum; + max = abs->maximum; + + if ((min != 0 || max != 0) && max < min) { + printk(KERN_DEBUG + "%s: invalid abs[%02x] min:%d max:%d\n", + UINPUT_NAME, code, min, max); + return -EINVAL; + } + + if (!check_sub_overflow(max, min, &range) && abs->flat > range) { + printk(KERN_DEBUG + "%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n", + UINPUT_NAME, code, abs->flat, min, max); + return -EINVAL; + } + + /* + * Limit number of contacts to a reasonable value (100). This + * ensures that we need less than 2 pages for struct input_mt + * (we are not using in-kernel slot assignment so not going to + * allocate memory for the "red" table), and we should have no + * trouble getting this much memory. + */ + if (code == ABS_MT_SLOT && max > 99) { + printk(KERN_DEBUG + "%s: unreasonably large number of slots requested: %d\n", + UINPUT_NAME, max); + return -EINVAL; + } return 0; } @@ -309,56 +437,92 @@ static int uinput_open(struct inode *inode, struct file *file) static int uinput_validate_absbits(struct input_dev *dev) { unsigned int cnt; - int retval = 0; - - for (cnt = 0; cnt < ABS_CNT; cnt++) { - int min, max; - if (!test_bit(cnt, dev->absbit)) - continue; - - min = input_abs_get_min(dev, cnt); - max = input_abs_get_max(dev, cnt); - - if ((min != 0 || max != 0) && max <= min) { - printk(KERN_DEBUG - "%s: invalid abs[%02x] min:%d max:%d\n", - UINPUT_NAME, cnt, - input_abs_get_min(dev, cnt), - input_abs_get_max(dev, cnt)); - retval = -EINVAL; - break; - } + int error; - if (input_abs_get_flat(dev, cnt) > - input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) { - printk(KERN_DEBUG - "%s: abs_flat #%02x out of range: %d " - "(min:%d/max:%d)\n", - UINPUT_NAME, cnt, - input_abs_get_flat(dev, cnt), - input_abs_get_min(dev, cnt), - input_abs_get_max(dev, cnt)); - retval = -EINVAL; - break; - } + if (!test_bit(EV_ABS, dev->evbit)) + return 0; + + /* + * Check if absmin/absmax/absfuzz/absflat are sane. + */ + + for_each_set_bit(cnt, dev->absbit, ABS_CNT) { + if (!dev->absinfo) + return -EINVAL; + + error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]); + if (error) + return error; } - return retval; + + return 0; } -static int uinput_allocate_device(struct uinput_device *udev) +static int uinput_dev_setup(struct uinput_device *udev, + struct uinput_setup __user *arg) { - udev->dev = input_allocate_device(); - if (!udev->dev) + struct uinput_setup setup; + struct input_dev *dev; + + if (udev->state == UIST_CREATED) + return -EINVAL; + + if (copy_from_user(&setup, arg, sizeof(setup))) + return -EFAULT; + + if (!setup.name[0]) + return -EINVAL; + + dev = udev->dev; + dev->id = setup.id; + udev->ff_effects_max = setup.ff_effects_max; + + kfree(dev->name); + dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL); + if (!dev->name) return -ENOMEM; - udev->dev->event = uinput_dev_event; - input_set_drvdata(udev->dev, udev); + udev->state = UIST_SETUP_COMPLETE; + return 0; +} + +static int uinput_abs_setup(struct uinput_device *udev, + struct uinput_setup __user *arg, size_t size) +{ + struct uinput_abs_setup setup = {}; + struct input_dev *dev; + int error; + if (size > sizeof(setup)) + return -E2BIG; + + if (udev->state == UIST_CREATED) + return -EINVAL; + + if (copy_from_user(&setup, arg, size)) + return -EFAULT; + + if (setup.code > ABS_MAX) + return -ERANGE; + + dev = udev->dev; + + error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo); + if (error) + return error; + + input_alloc_absinfo(dev); + if (!dev->absinfo) + return -ENOMEM; + + set_bit(setup.code, dev->absbit); + dev->absinfo[setup.code] = setup.absinfo; return 0; } -static int uinput_setup_device(struct uinput_device *udev, - const char __user *buffer, size_t count) +/* legacy setup via write() */ +static int uinput_setup_device_legacy(struct uinput_device *udev, + const char __user *buffer, size_t count) { struct uinput_user_dev *user_dev; struct input_dev *dev; @@ -369,9 +533,9 @@ static int uinput_setup_device(struct uinput_device *udev, return -EINVAL; if (!udev->dev) { - retval = uinput_allocate_device(udev); - if (retval) - return retval; + udev->dev = input_allocate_device(); + if (!udev->dev) + return -ENOMEM; } dev = udev->dev; @@ -408,19 +572,9 @@ static int uinput_setup_device(struct uinput_device *udev, input_abs_set_flat(dev, i, user_dev->absflat[i]); } - /* check if absmin/absmax/absfuzz/absflat are filled as - * told in Documentation/input/input-programming.txt */ - if (test_bit(EV_ABS, dev->evbit)) { - retval = uinput_validate_absbits(dev); - if (retval < 0) - goto exit; - if (test_bit(ABS_MT_SLOT, dev->absbit)) { - int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1; - input_mt_init_slots(dev, nslot, 0); - } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) { - input_set_events_per_packet(dev, 60); - } - } + retval = uinput_validate_absbits(dev); + if (retval < 0) + goto exit; udev->state = UIST_SETUP_COMPLETE; retval = count; @@ -430,20 +584,64 @@ static int uinput_setup_device(struct uinput_device *udev, return retval; } -static ssize_t uinput_inject_event(struct uinput_device *udev, - const char __user *buffer, size_t count) +/* + * Returns true if the given timestamp is valid (i.e., if all the following + * conditions are satisfied), false otherwise. + * 1) given timestamp is positive + * 2) it's within the allowed offset before the current time + * 3) it's not in the future + */ +static bool is_valid_timestamp(const ktime_t timestamp) +{ + ktime_t zero_time; + ktime_t current_time; + ktime_t min_time; + ktime_t offset; + + zero_time = ktime_set(0, 0); + if (ktime_compare(zero_time, timestamp) >= 0) + return false; + + current_time = ktime_get(); + offset = ktime_set(UINPUT_TIMESTAMP_ALLOWED_OFFSET_SECS, 0); + min_time = ktime_sub(current_time, offset); + + if (ktime_after(min_time, timestamp) || ktime_after(timestamp, current_time)) + return false; + + return true; +} + +static ssize_t uinput_inject_events(struct uinput_device *udev, + const char __user *buffer, size_t count) { struct input_event ev; + size_t bytes = 0; + ktime_t timestamp; - if (count < input_event_size()) + if (count != 0 && count < input_event_size()) return -EINVAL; - if (input_event_from_user(buffer, &ev)) - return -EFAULT; + while (bytes + input_event_size() <= count) { + /* + * Note that even if some events were fetched successfully + * we are still going to return EFAULT instead of partial + * count to let userspace know that it got it's buffers + * all wrong. + */ + if (input_event_from_user(buffer + bytes, &ev)) + return -EFAULT; + + timestamp = ktime_set(ev.input_event_sec, ev.input_event_usec * NSEC_PER_USEC); + if (is_valid_timestamp(timestamp)) + input_set_timestamp(udev->dev, timestamp); - input_event(udev->dev, ev.type, ev.code, ev.value); + input_event(udev->dev, ev.type, ev.code, ev.value); + bytes += input_event_size(); + cond_resched(); + } - return input_event_size(); + return bytes; } static ssize_t uinput_write(struct file *file, const char __user *buffer, @@ -460,8 +658,8 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer, return retval; retval = udev->state == UIST_CREATED ? - uinput_inject_event(udev, buffer, count) : - uinput_setup_device(udev, buffer, count); + uinput_inject_events(udev, buffer, count) : + uinput_setup_device_legacy(udev, buffer, count); mutex_unlock(&udev->mutex); @@ -540,16 +738,17 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, return retval; } -static unsigned int uinput_poll(struct file *file, poll_table *wait) +static __poll_t uinput_poll(struct file *file, poll_table *wait) { struct uinput_device *udev = file->private_data; + __poll_t mask = EPOLLOUT | EPOLLWRNORM; /* uinput is always writable */ poll_wait(file, &udev->waitq, wait); if (udev->head != udev->tail) - return POLLIN | POLLRDNORM; + mask |= EPOLLIN | EPOLLRDNORM; - return 0; + return mask; } static int uinput_release(struct inode *inode, struct file *file) @@ -573,9 +772,10 @@ struct uinput_ff_upload_compat { static int uinput_ff_upload_to_user(char __user *buffer, const struct uinput_ff_upload *ff_up) { - if (INPUT_COMPAT_TEST) { + if (in_compat_syscall()) { struct uinput_ff_upload_compat ff_up_compat; + memset(&ff_up_compat, 0, sizeof(ff_up_compat)); ff_up_compat.request_id = ff_up->request_id; ff_up_compat.retval = ff_up->retval; /* @@ -604,7 +804,7 @@ static int uinput_ff_upload_to_user(char __user *buffer, static int uinput_ff_upload_from_user(const char __user *buffer, struct uinput_ff_upload *ff_up) { - if (INPUT_COMPAT_TEST) { + if (in_compat_syscall()) { struct uinput_ff_upload_compat ff_up_compat; if (copy_from_user(&ff_up_compat, buffer, @@ -660,6 +860,31 @@ static int uinput_ff_upload_from_user(const char __user *buffer, __ret; \ }) +static int uinput_str_to_user(void __user *dest, const char *str, + unsigned int maxlen) +{ + char __user *p = dest; + int len, ret; + + if (!str) + return -ENOENT; + + if (maxlen == 0) + return -EINVAL; + + len = strlen(str) + 1; + if (len > maxlen) + len = maxlen; + + ret = copy_to_user(p, str, len); + if (ret) + return -EFAULT; + + /* force terminating '\0' */ + ret = put_user(0, p + len - 1); + return ret ? -EFAULT : len; +} + static long uinput_ioctl_handler(struct file *file, unsigned int cmd, unsigned long arg, void __user *p) { @@ -669,161 +894,192 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, struct uinput_ff_erase ff_erase; struct uinput_request *req; char *phys; + const char *name; + unsigned int size; retval = mutex_lock_interruptible(&udev->mutex); if (retval) return retval; if (!udev->dev) { - retval = uinput_allocate_device(udev); - if (retval) + udev->dev = input_allocate_device(); + if (!udev->dev) { + retval = -ENOMEM; goto out; + } } switch (cmd) { - case UI_DEV_CREATE: - retval = uinput_create_device(udev); - break; + case UI_GET_VERSION: + if (put_user(UINPUT_VERSION, (unsigned int __user *)p)) + retval = -EFAULT; + goto out; - case UI_DEV_DESTROY: - uinput_destroy_device(udev); - break; + case UI_DEV_CREATE: + retval = uinput_create_device(udev); + goto out; - case UI_SET_EVBIT: - retval = uinput_set_bit(arg, evbit, EV_MAX); - break; + case UI_DEV_DESTROY: + uinput_destroy_device(udev); + goto out; - case UI_SET_KEYBIT: - retval = uinput_set_bit(arg, keybit, KEY_MAX); - break; + case UI_DEV_SETUP: + retval = uinput_dev_setup(udev, p); + goto out; - case UI_SET_RELBIT: - retval = uinput_set_bit(arg, relbit, REL_MAX); - break; + /* UI_ABS_SETUP is handled in the variable size ioctls */ - case UI_SET_ABSBIT: - retval = uinput_set_bit(arg, absbit, ABS_MAX); - break; + case UI_SET_EVBIT: + retval = uinput_set_bit(arg, evbit, EV_MAX); + goto out; - case UI_SET_MSCBIT: - retval = uinput_set_bit(arg, mscbit, MSC_MAX); - break; + case UI_SET_KEYBIT: + retval = uinput_set_bit(arg, keybit, KEY_MAX); + goto out; - case UI_SET_LEDBIT: - retval = uinput_set_bit(arg, ledbit, LED_MAX); - break; + case UI_SET_RELBIT: + retval = uinput_set_bit(arg, relbit, REL_MAX); + goto out; - case UI_SET_SNDBIT: - retval = uinput_set_bit(arg, sndbit, SND_MAX); - break; + case UI_SET_ABSBIT: + retval = uinput_set_bit(arg, absbit, ABS_MAX); + goto out; - case UI_SET_FFBIT: - retval = uinput_set_bit(arg, ffbit, FF_MAX); - break; + case UI_SET_MSCBIT: + retval = uinput_set_bit(arg, mscbit, MSC_MAX); + goto out; - case UI_SET_SWBIT: - retval = uinput_set_bit(arg, swbit, SW_MAX); - break; + case UI_SET_LEDBIT: + retval = uinput_set_bit(arg, ledbit, LED_MAX); + goto out; - case UI_SET_PROPBIT: - retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX); - break; + case UI_SET_SNDBIT: + retval = uinput_set_bit(arg, sndbit, SND_MAX); + goto out; - case UI_SET_PHYS: - if (udev->state == UIST_CREATED) { - retval = -EINVAL; - goto out; - } + case UI_SET_FFBIT: + retval = uinput_set_bit(arg, ffbit, FF_MAX); + goto out; - phys = strndup_user(p, 1024); - if (IS_ERR(phys)) { - retval = PTR_ERR(phys); - goto out; - } + case UI_SET_SWBIT: + retval = uinput_set_bit(arg, swbit, SW_MAX); + goto out; - kfree(udev->dev->phys); - udev->dev->phys = phys; - break; + case UI_SET_PROPBIT: + retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX); + goto out; - case UI_BEGIN_FF_UPLOAD: - retval = uinput_ff_upload_from_user(p, &ff_up); - if (retval) - break; - - req = uinput_request_find(udev, ff_up.request_id); - if (!req || req->code != UI_FF_UPLOAD || - !req->u.upload.effect) { - retval = -EINVAL; - break; - } - - ff_up.retval = 0; - ff_up.effect = *req->u.upload.effect; - if (req->u.upload.old) - ff_up.old = *req->u.upload.old; - else - memset(&ff_up.old, 0, sizeof(struct ff_effect)); - - retval = uinput_ff_upload_to_user(p, &ff_up); - break; + case UI_SET_PHYS: + if (udev->state == UIST_CREATED) { + retval = -EINVAL; + goto out; + } - case UI_BEGIN_FF_ERASE: - if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { - retval = -EFAULT; - break; - } - - req = uinput_request_find(udev, ff_erase.request_id); - if (!req || req->code != UI_FF_ERASE) { - retval = -EINVAL; - break; - } - - ff_erase.retval = 0; - ff_erase.effect_id = req->u.effect_id; - if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { - retval = -EFAULT; - break; - } + phys = strndup_user(p, 1024); + if (IS_ERR(phys)) { + retval = PTR_ERR(phys); + goto out; + } - break; + kfree(udev->dev->phys); + udev->dev->phys = phys; + goto out; - case UI_END_FF_UPLOAD: - retval = uinput_ff_upload_from_user(p, &ff_up); - if (retval) - break; + case UI_BEGIN_FF_UPLOAD: + retval = uinput_ff_upload_from_user(p, &ff_up); + if (retval) + goto out; - req = uinput_request_find(udev, ff_up.request_id); - if (!req || req->code != UI_FF_UPLOAD || - !req->u.upload.effect) { - retval = -EINVAL; - break; - } + req = uinput_request_find(udev, ff_up.request_id); + if (!req || req->code != UI_FF_UPLOAD || + !req->u.upload.effect) { + retval = -EINVAL; + goto out; + } - req->retval = ff_up.retval; - uinput_request_done(udev, req); - break; + ff_up.retval = 0; + ff_up.effect = *req->u.upload.effect; + if (req->u.upload.old) + ff_up.old = *req->u.upload.old; + else + memset(&ff_up.old, 0, sizeof(struct ff_effect)); - case UI_END_FF_ERASE: - if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { - retval = -EFAULT; - break; - } + retval = uinput_ff_upload_to_user(p, &ff_up); + goto out; - req = uinput_request_find(udev, ff_erase.request_id); - if (!req || req->code != UI_FF_ERASE) { - retval = -EINVAL; - break; - } + case UI_BEGIN_FF_ERASE: + if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { + retval = -EFAULT; + goto out; + } - req->retval = ff_erase.retval; - uinput_request_done(udev, req); - break; + req = uinput_request_find(udev, ff_erase.request_id); + if (!req || req->code != UI_FF_ERASE) { + retval = -EINVAL; + goto out; + } + + ff_erase.retval = 0; + ff_erase.effect_id = req->u.effect_id; + if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { + retval = -EFAULT; + goto out; + } + + goto out; + + case UI_END_FF_UPLOAD: + retval = uinput_ff_upload_from_user(p, &ff_up); + if (retval) + goto out; + + req = uinput_request_find(udev, ff_up.request_id); + if (!req || req->code != UI_FF_UPLOAD || + !req->u.upload.effect) { + retval = -EINVAL; + goto out; + } + + req->retval = ff_up.retval; + complete(&req->done); + goto out; + + case UI_END_FF_ERASE: + if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { + retval = -EFAULT; + goto out; + } - default: + req = uinput_request_find(udev, ff_erase.request_id); + if (!req || req->code != UI_FF_ERASE) { retval = -EINVAL; + goto out; + } + + req->retval = ff_erase.retval; + complete(&req->done); + goto out; + } + + size = _IOC_SIZE(cmd); + + /* Now check variable-length commands */ + switch (cmd & ~IOCSIZE_MASK) { + case UI_GET_SYSNAME(0): + if (udev->state != UIST_CREATED) { + retval = -ENOENT; + goto out; + } + name = dev_name(&udev->dev->dev); + retval = uinput_str_to_user(p, name, size); + goto out; + + case UI_ABS_SETUP & ~IOCSIZE_MASK: + retval = uinput_abs_setup(udev, p, size); + goto out; } + retval = -EINVAL; out: mutex_unlock(&udev->mutex); return retval; @@ -835,9 +1091,33 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } #ifdef CONFIG_COMPAT + +/* + * These IOCTLs change their size and thus their numbers between + * 32 and 64 bits. + */ +#define UI_SET_PHYS_COMPAT \ + _IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t) +#define UI_BEGIN_FF_UPLOAD_COMPAT \ + _IOWR(UINPUT_IOCTL_BASE, 200, struct uinput_ff_upload_compat) +#define UI_END_FF_UPLOAD_COMPAT \ + _IOW(UINPUT_IOCTL_BASE, 201, struct uinput_ff_upload_compat) + static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + switch (cmd) { + case UI_SET_PHYS_COMPAT: + cmd = UI_SET_PHYS; + break; + case UI_BEGIN_FF_UPLOAD_COMPAT: + cmd = UI_BEGIN_FF_UPLOAD; + break; + case UI_END_FF_UPLOAD_COMPAT: + cmd = UI_END_FF_UPLOAD; + break; + } + return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg)); } #endif @@ -853,7 +1133,6 @@ static const struct file_operations uinput_fops = { #ifdef CONFIG_COMPAT .compat_ioctl = uinput_compat_ioctl, #endif - .llseek = no_llseek, }; static struct miscdevice uinput_misc = { @@ -861,23 +1140,11 @@ static struct miscdevice uinput_misc = { .minor = UINPUT_MINOR, .name = UINPUT_NAME, }; +module_misc_device(uinput_misc); + MODULE_ALIAS_MISCDEV(UINPUT_MINOR); MODULE_ALIAS("devname:" UINPUT_NAME); -static int __init uinput_init(void) -{ - return misc_register(&uinput_misc); -} - -static void __exit uinput_exit(void) -{ - misc_deregister(&uinput_misc); -} - MODULE_AUTHOR("Aristeu Sergio Rozanski Filho"); MODULE_DESCRIPTION("User level driver support for input subsystem"); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.3"); - -module_init(uinput_init); -module_exit(uinput_exit); |
