summaryrefslogtreecommitdiff
path: root/drivers/input/misc/uinput.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/misc/uinput.c')
-rw-r--r--drivers/input/misc/uinput.c747
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);