2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* 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:
|
2014-01-31 01:20:24 +00:00
|
|
|
* 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
|
|
|
|
* - add UI_GET_SYSNAME ioctl
|
2006-07-19 05:41:09 +00:00
|
|
|
* 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
|
|
|
|
* - updated ff support for the changes in kernel interface
|
|
|
|
* - added MODULE_VERSION
|
2005-04-16 22:20:36 +00:00
|
|
|
* 0.2 16/10/2004 (Micah Dowty <micah@navi.cx>)
|
|
|
|
* - added force feedback support
|
|
|
|
* - added UI_SET_PHYS
|
|
|
|
* 0.1 20/06/2002
|
|
|
|
* - first public version
|
|
|
|
*/
|
|
|
|
#include <linux/poll.h>
|
2009-10-04 12:11:37 +00:00
|
|
|
#include <linux/sched.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/miscdevice.h>
|
|
|
|
#include <linux/uinput.h>
|
2010-11-27 08:16:48 +00:00
|
|
|
#include <linux/input/mt.h>
|
2008-10-17 02:31:42 +00:00
|
|
|
#include "../input-compat.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
static int uinput_dev_event(struct input_dev *dev,
|
|
|
|
unsigned int type, unsigned int code, int value)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-04-12 05:34:33 +00:00
|
|
|
struct uinput_device *udev = input_get_drvdata(dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-05-15 05:01:57 +00:00
|
|
|
/* Atomically allocate an ID for the given request. Returns 0 on success. */
|
2012-07-30 05:48:32 +00:00
|
|
|
static bool uinput_request_alloc_id(struct uinput_device *udev,
|
|
|
|
struct uinput_request *request)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2012-07-30 05:48:32 +00:00
|
|
|
unsigned int id;
|
2012-07-30 05:48:32 +00:00
|
|
|
bool reserved = false;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-06-30 05:48:14 +00:00
|
|
|
spin_lock(&udev->requests_lock);
|
2005-06-30 05:47:50 +00:00
|
|
|
|
2009-05-15 05:01:57 +00:00
|
|
|
for (id = 0; id < UINPUT_NUM_REQUESTS; id++) {
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!udev->requests[id]) {
|
|
|
|
request->id = id;
|
2005-06-30 05:48:14 +00:00
|
|
|
udev->requests[id] = request;
|
2012-07-30 05:48:32 +00:00
|
|
|
reserved = true;
|
2005-06-30 05:47:50 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2009-05-15 05:01:57 +00:00
|
|
|
}
|
2005-06-30 05:47:50 +00:00
|
|
|
|
2005-06-30 05:48:14 +00:00
|
|
|
spin_unlock(&udev->requests_lock);
|
2012-07-30 05:48:32 +00:00
|
|
|
return reserved;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
static struct uinput_request *uinput_request_find(struct uinput_device *udev,
|
|
|
|
unsigned int id)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
/* Find an input request, by ID. Returns NULL if the ID isn't valid. */
|
2012-07-30 05:48:32 +00:00
|
|
|
if (id >= UINPUT_NUM_REQUESTS)
|
2005-04-16 22:20:36 +00:00
|
|
|
return NULL;
|
2008-10-17 02:31:42 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
return udev->requests[id];
|
|
|
|
}
|
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
static int uinput_request_reserve_slot(struct uinput_device *udev,
|
|
|
|
struct uinput_request *request)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-06-30 05:48:14 +00:00
|
|
|
/* Allocate slot. If none are available right away, wait. */
|
|
|
|
return wait_event_interruptible(udev->requests_waitq,
|
2012-07-30 05:48:32 +00:00
|
|
|
uinput_request_alloc_id(udev, request));
|
2005-06-30 05:48:14 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
Input: uinput - avoid crash when sending FF request to device going away
If FF request comes in while uinput device is going away,
uinput_request_send() will fail with -ENODEV, and uinput_request_submit()
will attempt to mark the slot as unused by calling uinput_request_done().
Unfortunately in this case we haven't initialized request->done completion
yet, and we get a crash:
[ 39.402036] BUG: spinlock bad magic on CPU#1, fftest/3108
[ 39.402046] lock: 0xffff88006a93bb00, .magic: 00000000, .owner: /39, .owner_cpu: 1217155072
[ 39.402055] CPU: 1 PID: 3108 Comm: fftest Tainted: G W 4.13.0+ #15
[ 39.402059] Hardware name: LENOVO 20HQS0EG02/20HQS0EG02, BIOS N1MET37W (1.22 ) 07/04/2017
[ 39.402064] 0000000000000086 f0fad82f3ceaa120 ffff88006a93b9a0 ffffffff9de941bb
[ 39.402077] ffff88026df8ae00 ffff88006a93bb00 ffff88006a93b9c0 ffffffff9dca62b7
[ 39.402088] ffff88006a93bb00 ffff88006a93baf8 ffff88006a93b9e0 ffffffff9dca62e7
[ 39.402099] Call Trace:
[ 39.402112] [<ffffffff9de941bb>] dump_stack+0x4d/0x63
[ 39.402123] [<ffffffff9dca62b7>] spin_dump+0x97/0x9c
[ 39.402130] [<ffffffff9dca62e7>] spin_bug+0x2b/0x2d
[ 39.402138] [<ffffffff9dca6373>] do_raw_spin_lock+0x28/0xfd
[ 39.402147] [<ffffffff9e3055cd>] _raw_spin_lock_irqsave+0x19/0x1f
[ 39.402154] [<ffffffff9dca05b7>] complete+0x1d/0x48
[ 39.402162] [<ffffffffc04f30af>] 0xffffffffc04f30af
[ 39.402167] [<ffffffffc04f468c>] 0xffffffffc04f468c
[ 39.402177] [<ffffffff9dd59c16>] ? __slab_free+0x22f/0x359
[ 39.402184] [<ffffffff9dcc13e9>] ? tk_clock_read+0xc/0xe
[ 39.402189] [<ffffffffc04f471f>] 0xffffffffc04f471f
[ 39.402195] [<ffffffff9dc9ffe5>] ? __wake_up+0x44/0x4b
[ 39.402200] [<ffffffffc04f3240>] ? 0xffffffffc04f3240
[ 39.402207] [<ffffffff9e0f57f3>] erase_effect+0xa1/0xd2
[ 39.402214] [<ffffffff9e0f58c6>] input_ff_flush+0x43/0x5c
[ 39.402219] [<ffffffffc04f32ad>] 0xffffffffc04f32ad
[ 39.402227] [<ffffffff9e0f174f>] input_flush_device+0x3d/0x51
[ 39.402234] [<ffffffff9e0f69ae>] evdev_flush+0x49/0x5c
[ 39.402243] [<ffffffff9dd62d6e>] filp_close+0x3f/0x65
[ 39.402253] [<ffffffff9dd7dcf7>] put_files_struct+0x66/0xc1
[ 39.402261] [<ffffffff9dd7ddeb>] exit_files+0x47/0x4e
[ 39.402270] [<ffffffff9dc6b329>] do_exit+0x483/0x969
[ 39.402278] [<ffffffff9dc73211>] ? recalc_sigpending_tsk+0x3d/0x44
[ 39.402285] [<ffffffff9dc6c7a2>] do_group_exit+0x42/0xb0
[ 39.402293] [<ffffffff9dc767e1>] get_signal+0x58d/0x5bf
[ 39.402300] [<ffffffff9dc03701>] do_signal+0x37/0x53e
[ 39.402307] [<ffffffff9e0f8401>] ? evdev_ioctl_handler+0xac8/0xb04
[ 39.402314] [<ffffffff9e0f8464>] ? evdev_ioctl+0x10/0x12
[ 39.402321] [<ffffffff9dd74cfa>] ? do_vfs_ioctl+0x42e/0x501
[ 39.402328] [<ffffffff9dc0170e>] prepare_exit_to_usermode+0x66/0x90
[ 39.402333] [<ffffffff9dc0181b>] syscall_return_slowpath+0xe3/0xec
[ 39.402339] [<ffffffff9e305b7b>] int_ret_from_sys_call+0x25/0x8f
While we could solve this by simply initializing the completion earlier, we
are better off rearranging the code a bit so we avoid calling complete() on
requests that we did not send out. This patch consolidates marking request
slots as free in one place (in uinput_request_submit(), the same place
where we acquire them) and having everyone else simply signal completion
of the requests.
Fixes: 00ce756ce53a ("Input: uinput - mark failed submission requests as free")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2017-09-06 23:22:59 +00:00
|
|
|
static void uinput_request_release_slot(struct uinput_device *udev,
|
|
|
|
unsigned int id)
|
2005-06-30 05:48:14 +00:00
|
|
|
{
|
|
|
|
/* Mark slot as available */
|
Input: uinput - avoid crash when sending FF request to device going away
If FF request comes in while uinput device is going away,
uinput_request_send() will fail with -ENODEV, and uinput_request_submit()
will attempt to mark the slot as unused by calling uinput_request_done().
Unfortunately in this case we haven't initialized request->done completion
yet, and we get a crash:
[ 39.402036] BUG: spinlock bad magic on CPU#1, fftest/3108
[ 39.402046] lock: 0xffff88006a93bb00, .magic: 00000000, .owner: /39, .owner_cpu: 1217155072
[ 39.402055] CPU: 1 PID: 3108 Comm: fftest Tainted: G W 4.13.0+ #15
[ 39.402059] Hardware name: LENOVO 20HQS0EG02/20HQS0EG02, BIOS N1MET37W (1.22 ) 07/04/2017
[ 39.402064] 0000000000000086 f0fad82f3ceaa120 ffff88006a93b9a0 ffffffff9de941bb
[ 39.402077] ffff88026df8ae00 ffff88006a93bb00 ffff88006a93b9c0 ffffffff9dca62b7
[ 39.402088] ffff88006a93bb00 ffff88006a93baf8 ffff88006a93b9e0 ffffffff9dca62e7
[ 39.402099] Call Trace:
[ 39.402112] [<ffffffff9de941bb>] dump_stack+0x4d/0x63
[ 39.402123] [<ffffffff9dca62b7>] spin_dump+0x97/0x9c
[ 39.402130] [<ffffffff9dca62e7>] spin_bug+0x2b/0x2d
[ 39.402138] [<ffffffff9dca6373>] do_raw_spin_lock+0x28/0xfd
[ 39.402147] [<ffffffff9e3055cd>] _raw_spin_lock_irqsave+0x19/0x1f
[ 39.402154] [<ffffffff9dca05b7>] complete+0x1d/0x48
[ 39.402162] [<ffffffffc04f30af>] 0xffffffffc04f30af
[ 39.402167] [<ffffffffc04f468c>] 0xffffffffc04f468c
[ 39.402177] [<ffffffff9dd59c16>] ? __slab_free+0x22f/0x359
[ 39.402184] [<ffffffff9dcc13e9>] ? tk_clock_read+0xc/0xe
[ 39.402189] [<ffffffffc04f471f>] 0xffffffffc04f471f
[ 39.402195] [<ffffffff9dc9ffe5>] ? __wake_up+0x44/0x4b
[ 39.402200] [<ffffffffc04f3240>] ? 0xffffffffc04f3240
[ 39.402207] [<ffffffff9e0f57f3>] erase_effect+0xa1/0xd2
[ 39.402214] [<ffffffff9e0f58c6>] input_ff_flush+0x43/0x5c
[ 39.402219] [<ffffffffc04f32ad>] 0xffffffffc04f32ad
[ 39.402227] [<ffffffff9e0f174f>] input_flush_device+0x3d/0x51
[ 39.402234] [<ffffffff9e0f69ae>] evdev_flush+0x49/0x5c
[ 39.402243] [<ffffffff9dd62d6e>] filp_close+0x3f/0x65
[ 39.402253] [<ffffffff9dd7dcf7>] put_files_struct+0x66/0xc1
[ 39.402261] [<ffffffff9dd7ddeb>] exit_files+0x47/0x4e
[ 39.402270] [<ffffffff9dc6b329>] do_exit+0x483/0x969
[ 39.402278] [<ffffffff9dc73211>] ? recalc_sigpending_tsk+0x3d/0x44
[ 39.402285] [<ffffffff9dc6c7a2>] do_group_exit+0x42/0xb0
[ 39.402293] [<ffffffff9dc767e1>] get_signal+0x58d/0x5bf
[ 39.402300] [<ffffffff9dc03701>] do_signal+0x37/0x53e
[ 39.402307] [<ffffffff9e0f8401>] ? evdev_ioctl_handler+0xac8/0xb04
[ 39.402314] [<ffffffff9e0f8464>] ? evdev_ioctl+0x10/0x12
[ 39.402321] [<ffffffff9dd74cfa>] ? do_vfs_ioctl+0x42e/0x501
[ 39.402328] [<ffffffff9dc0170e>] prepare_exit_to_usermode+0x66/0x90
[ 39.402333] [<ffffffff9dc0181b>] syscall_return_slowpath+0xe3/0xec
[ 39.402339] [<ffffffff9e305b7b>] int_ret_from_sys_call+0x25/0x8f
While we could solve this by simply initializing the completion earlier, we
are better off rearranging the code a bit so we avoid calling complete() on
requests that we did not send out. This patch consolidates marking request
slots as free in one place (in uinput_request_submit(), the same place
where we acquire them) and having everyone else simply signal completion
of the requests.
Fixes: 00ce756ce53a ("Input: uinput - mark failed submission requests as free")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2017-09-06 23:22:59 +00:00
|
|
|
spin_lock(&udev->requests_lock);
|
|
|
|
udev->requests[id] = NULL;
|
|
|
|
spin_unlock(&udev->requests_lock);
|
2005-10-17 23:43:32 +00:00
|
|
|
|
Input: uinput - avoid crash when sending FF request to device going away
If FF request comes in while uinput device is going away,
uinput_request_send() will fail with -ENODEV, and uinput_request_submit()
will attempt to mark the slot as unused by calling uinput_request_done().
Unfortunately in this case we haven't initialized request->done completion
yet, and we get a crash:
[ 39.402036] BUG: spinlock bad magic on CPU#1, fftest/3108
[ 39.402046] lock: 0xffff88006a93bb00, .magic: 00000000, .owner: /39, .owner_cpu: 1217155072
[ 39.402055] CPU: 1 PID: 3108 Comm: fftest Tainted: G W 4.13.0+ #15
[ 39.402059] Hardware name: LENOVO 20HQS0EG02/20HQS0EG02, BIOS N1MET37W (1.22 ) 07/04/2017
[ 39.402064] 0000000000000086 f0fad82f3ceaa120 ffff88006a93b9a0 ffffffff9de941bb
[ 39.402077] ffff88026df8ae00 ffff88006a93bb00 ffff88006a93b9c0 ffffffff9dca62b7
[ 39.402088] ffff88006a93bb00 ffff88006a93baf8 ffff88006a93b9e0 ffffffff9dca62e7
[ 39.402099] Call Trace:
[ 39.402112] [<ffffffff9de941bb>] dump_stack+0x4d/0x63
[ 39.402123] [<ffffffff9dca62b7>] spin_dump+0x97/0x9c
[ 39.402130] [<ffffffff9dca62e7>] spin_bug+0x2b/0x2d
[ 39.402138] [<ffffffff9dca6373>] do_raw_spin_lock+0x28/0xfd
[ 39.402147] [<ffffffff9e3055cd>] _raw_spin_lock_irqsave+0x19/0x1f
[ 39.402154] [<ffffffff9dca05b7>] complete+0x1d/0x48
[ 39.402162] [<ffffffffc04f30af>] 0xffffffffc04f30af
[ 39.402167] [<ffffffffc04f468c>] 0xffffffffc04f468c
[ 39.402177] [<ffffffff9dd59c16>] ? __slab_free+0x22f/0x359
[ 39.402184] [<ffffffff9dcc13e9>] ? tk_clock_read+0xc/0xe
[ 39.402189] [<ffffffffc04f471f>] 0xffffffffc04f471f
[ 39.402195] [<ffffffff9dc9ffe5>] ? __wake_up+0x44/0x4b
[ 39.402200] [<ffffffffc04f3240>] ? 0xffffffffc04f3240
[ 39.402207] [<ffffffff9e0f57f3>] erase_effect+0xa1/0xd2
[ 39.402214] [<ffffffff9e0f58c6>] input_ff_flush+0x43/0x5c
[ 39.402219] [<ffffffffc04f32ad>] 0xffffffffc04f32ad
[ 39.402227] [<ffffffff9e0f174f>] input_flush_device+0x3d/0x51
[ 39.402234] [<ffffffff9e0f69ae>] evdev_flush+0x49/0x5c
[ 39.402243] [<ffffffff9dd62d6e>] filp_close+0x3f/0x65
[ 39.402253] [<ffffffff9dd7dcf7>] put_files_struct+0x66/0xc1
[ 39.402261] [<ffffffff9dd7ddeb>] exit_files+0x47/0x4e
[ 39.402270] [<ffffffff9dc6b329>] do_exit+0x483/0x969
[ 39.402278] [<ffffffff9dc73211>] ? recalc_sigpending_tsk+0x3d/0x44
[ 39.402285] [<ffffffff9dc6c7a2>] do_group_exit+0x42/0xb0
[ 39.402293] [<ffffffff9dc767e1>] get_signal+0x58d/0x5bf
[ 39.402300] [<ffffffff9dc03701>] do_signal+0x37/0x53e
[ 39.402307] [<ffffffff9e0f8401>] ? evdev_ioctl_handler+0xac8/0xb04
[ 39.402314] [<ffffffff9e0f8464>] ? evdev_ioctl+0x10/0x12
[ 39.402321] [<ffffffff9dd74cfa>] ? do_vfs_ioctl+0x42e/0x501
[ 39.402328] [<ffffffff9dc0170e>] prepare_exit_to_usermode+0x66/0x90
[ 39.402333] [<ffffffff9dc0181b>] syscall_return_slowpath+0xe3/0xec
[ 39.402339] [<ffffffff9e305b7b>] int_ret_from_sys_call+0x25/0x8f
While we could solve this by simply initializing the completion earlier, we
are better off rearranging the code a bit so we avoid calling complete() on
requests that we did not send out. This patch consolidates marking request
slots as free in one place (in uinput_request_submit(), the same place
where we acquire them) and having everyone else simply signal completion
of the requests.
Fixes: 00ce756ce53a ("Input: uinput - mark failed submission requests as free")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2017-09-06 23:22:59 +00:00
|
|
|
wake_up(&udev->requests_waitq);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
static int uinput_request_send(struct uinput_device *udev,
|
|
|
|
struct uinput_request *request)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2009-05-15 05:01:57 +00:00
|
|
|
int retval;
|
|
|
|
|
|
|
|
retval = mutex_lock_interruptible(&udev->mutex);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
if (udev->state != UIST_CREATED) {
|
|
|
|
retval = -ENODEV;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
init_completion(&request->done);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tell our userspace application about this new request
|
|
|
|
* by queueing an input event.
|
|
|
|
*/
|
2009-05-15 05:01:57 +00:00
|
|
|
uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&udev->mutex);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
static int uinput_request_submit(struct uinput_device *udev,
|
|
|
|
struct uinput_request *request)
|
|
|
|
{
|
Input: uinput - avoid crash when sending FF request to device going away
If FF request comes in while uinput device is going away,
uinput_request_send() will fail with -ENODEV, and uinput_request_submit()
will attempt to mark the slot as unused by calling uinput_request_done().
Unfortunately in this case we haven't initialized request->done completion
yet, and we get a crash:
[ 39.402036] BUG: spinlock bad magic on CPU#1, fftest/3108
[ 39.402046] lock: 0xffff88006a93bb00, .magic: 00000000, .owner: /39, .owner_cpu: 1217155072
[ 39.402055] CPU: 1 PID: 3108 Comm: fftest Tainted: G W 4.13.0+ #15
[ 39.402059] Hardware name: LENOVO 20HQS0EG02/20HQS0EG02, BIOS N1MET37W (1.22 ) 07/04/2017
[ 39.402064] 0000000000000086 f0fad82f3ceaa120 ffff88006a93b9a0 ffffffff9de941bb
[ 39.402077] ffff88026df8ae00 ffff88006a93bb00 ffff88006a93b9c0 ffffffff9dca62b7
[ 39.402088] ffff88006a93bb00 ffff88006a93baf8 ffff88006a93b9e0 ffffffff9dca62e7
[ 39.402099] Call Trace:
[ 39.402112] [<ffffffff9de941bb>] dump_stack+0x4d/0x63
[ 39.402123] [<ffffffff9dca62b7>] spin_dump+0x97/0x9c
[ 39.402130] [<ffffffff9dca62e7>] spin_bug+0x2b/0x2d
[ 39.402138] [<ffffffff9dca6373>] do_raw_spin_lock+0x28/0xfd
[ 39.402147] [<ffffffff9e3055cd>] _raw_spin_lock_irqsave+0x19/0x1f
[ 39.402154] [<ffffffff9dca05b7>] complete+0x1d/0x48
[ 39.402162] [<ffffffffc04f30af>] 0xffffffffc04f30af
[ 39.402167] [<ffffffffc04f468c>] 0xffffffffc04f468c
[ 39.402177] [<ffffffff9dd59c16>] ? __slab_free+0x22f/0x359
[ 39.402184] [<ffffffff9dcc13e9>] ? tk_clock_read+0xc/0xe
[ 39.402189] [<ffffffffc04f471f>] 0xffffffffc04f471f
[ 39.402195] [<ffffffff9dc9ffe5>] ? __wake_up+0x44/0x4b
[ 39.402200] [<ffffffffc04f3240>] ? 0xffffffffc04f3240
[ 39.402207] [<ffffffff9e0f57f3>] erase_effect+0xa1/0xd2
[ 39.402214] [<ffffffff9e0f58c6>] input_ff_flush+0x43/0x5c
[ 39.402219] [<ffffffffc04f32ad>] 0xffffffffc04f32ad
[ 39.402227] [<ffffffff9e0f174f>] input_flush_device+0x3d/0x51
[ 39.402234] [<ffffffff9e0f69ae>] evdev_flush+0x49/0x5c
[ 39.402243] [<ffffffff9dd62d6e>] filp_close+0x3f/0x65
[ 39.402253] [<ffffffff9dd7dcf7>] put_files_struct+0x66/0xc1
[ 39.402261] [<ffffffff9dd7ddeb>] exit_files+0x47/0x4e
[ 39.402270] [<ffffffff9dc6b329>] do_exit+0x483/0x969
[ 39.402278] [<ffffffff9dc73211>] ? recalc_sigpending_tsk+0x3d/0x44
[ 39.402285] [<ffffffff9dc6c7a2>] do_group_exit+0x42/0xb0
[ 39.402293] [<ffffffff9dc767e1>] get_signal+0x58d/0x5bf
[ 39.402300] [<ffffffff9dc03701>] do_signal+0x37/0x53e
[ 39.402307] [<ffffffff9e0f8401>] ? evdev_ioctl_handler+0xac8/0xb04
[ 39.402314] [<ffffffff9e0f8464>] ? evdev_ioctl+0x10/0x12
[ 39.402321] [<ffffffff9dd74cfa>] ? do_vfs_ioctl+0x42e/0x501
[ 39.402328] [<ffffffff9dc0170e>] prepare_exit_to_usermode+0x66/0x90
[ 39.402333] [<ffffffff9dc0181b>] syscall_return_slowpath+0xe3/0xec
[ 39.402339] [<ffffffff9e305b7b>] int_ret_from_sys_call+0x25/0x8f
While we could solve this by simply initializing the completion earlier, we
are better off rearranging the code a bit so we avoid calling complete() on
requests that we did not send out. This patch consolidates marking request
slots as free in one place (in uinput_request_submit(), the same place
where we acquire them) and having everyone else simply signal completion
of the requests.
Fixes: 00ce756ce53a ("Input: uinput - mark failed submission requests as free")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2017-09-06 23:22:59 +00:00
|
|
|
int retval;
|
2012-07-30 05:48:32 +00:00
|
|
|
|
Input: uinput - avoid crash when sending FF request to device going away
If FF request comes in while uinput device is going away,
uinput_request_send() will fail with -ENODEV, and uinput_request_submit()
will attempt to mark the slot as unused by calling uinput_request_done().
Unfortunately in this case we haven't initialized request->done completion
yet, and we get a crash:
[ 39.402036] BUG: spinlock bad magic on CPU#1, fftest/3108
[ 39.402046] lock: 0xffff88006a93bb00, .magic: 00000000, .owner: /39, .owner_cpu: 1217155072
[ 39.402055] CPU: 1 PID: 3108 Comm: fftest Tainted: G W 4.13.0+ #15
[ 39.402059] Hardware name: LENOVO 20HQS0EG02/20HQS0EG02, BIOS N1MET37W (1.22 ) 07/04/2017
[ 39.402064] 0000000000000086 f0fad82f3ceaa120 ffff88006a93b9a0 ffffffff9de941bb
[ 39.402077] ffff88026df8ae00 ffff88006a93bb00 ffff88006a93b9c0 ffffffff9dca62b7
[ 39.402088] ffff88006a93bb00 ffff88006a93baf8 ffff88006a93b9e0 ffffffff9dca62e7
[ 39.402099] Call Trace:
[ 39.402112] [<ffffffff9de941bb>] dump_stack+0x4d/0x63
[ 39.402123] [<ffffffff9dca62b7>] spin_dump+0x97/0x9c
[ 39.402130] [<ffffffff9dca62e7>] spin_bug+0x2b/0x2d
[ 39.402138] [<ffffffff9dca6373>] do_raw_spin_lock+0x28/0xfd
[ 39.402147] [<ffffffff9e3055cd>] _raw_spin_lock_irqsave+0x19/0x1f
[ 39.402154] [<ffffffff9dca05b7>] complete+0x1d/0x48
[ 39.402162] [<ffffffffc04f30af>] 0xffffffffc04f30af
[ 39.402167] [<ffffffffc04f468c>] 0xffffffffc04f468c
[ 39.402177] [<ffffffff9dd59c16>] ? __slab_free+0x22f/0x359
[ 39.402184] [<ffffffff9dcc13e9>] ? tk_clock_read+0xc/0xe
[ 39.402189] [<ffffffffc04f471f>] 0xffffffffc04f471f
[ 39.402195] [<ffffffff9dc9ffe5>] ? __wake_up+0x44/0x4b
[ 39.402200] [<ffffffffc04f3240>] ? 0xffffffffc04f3240
[ 39.402207] [<ffffffff9e0f57f3>] erase_effect+0xa1/0xd2
[ 39.402214] [<ffffffff9e0f58c6>] input_ff_flush+0x43/0x5c
[ 39.402219] [<ffffffffc04f32ad>] 0xffffffffc04f32ad
[ 39.402227] [<ffffffff9e0f174f>] input_flush_device+0x3d/0x51
[ 39.402234] [<ffffffff9e0f69ae>] evdev_flush+0x49/0x5c
[ 39.402243] [<ffffffff9dd62d6e>] filp_close+0x3f/0x65
[ 39.402253] [<ffffffff9dd7dcf7>] put_files_struct+0x66/0xc1
[ 39.402261] [<ffffffff9dd7ddeb>] exit_files+0x47/0x4e
[ 39.402270] [<ffffffff9dc6b329>] do_exit+0x483/0x969
[ 39.402278] [<ffffffff9dc73211>] ? recalc_sigpending_tsk+0x3d/0x44
[ 39.402285] [<ffffffff9dc6c7a2>] do_group_exit+0x42/0xb0
[ 39.402293] [<ffffffff9dc767e1>] get_signal+0x58d/0x5bf
[ 39.402300] [<ffffffff9dc03701>] do_signal+0x37/0x53e
[ 39.402307] [<ffffffff9e0f8401>] ? evdev_ioctl_handler+0xac8/0xb04
[ 39.402314] [<ffffffff9e0f8464>] ? evdev_ioctl+0x10/0x12
[ 39.402321] [<ffffffff9dd74cfa>] ? do_vfs_ioctl+0x42e/0x501
[ 39.402328] [<ffffffff9dc0170e>] prepare_exit_to_usermode+0x66/0x90
[ 39.402333] [<ffffffff9dc0181b>] syscall_return_slowpath+0xe3/0xec
[ 39.402339] [<ffffffff9e305b7b>] int_ret_from_sys_call+0x25/0x8f
While we could solve this by simply initializing the completion earlier, we
are better off rearranging the code a bit so we avoid calling complete() on
requests that we did not send out. This patch consolidates marking request
slots as free in one place (in uinput_request_submit(), the same place
where we acquire them) and having everyone else simply signal completion
of the requests.
Fixes: 00ce756ce53a ("Input: uinput - mark failed submission requests as free")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2017-09-06 23:22:59 +00:00
|
|
|
retval = uinput_request_reserve_slot(udev, request);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
2012-07-30 05:48:32 +00:00
|
|
|
|
Input: uinput - avoid crash when sending FF request to device going away
If FF request comes in while uinput device is going away,
uinput_request_send() will fail with -ENODEV, and uinput_request_submit()
will attempt to mark the slot as unused by calling uinput_request_done().
Unfortunately in this case we haven't initialized request->done completion
yet, and we get a crash:
[ 39.402036] BUG: spinlock bad magic on CPU#1, fftest/3108
[ 39.402046] lock: 0xffff88006a93bb00, .magic: 00000000, .owner: /39, .owner_cpu: 1217155072
[ 39.402055] CPU: 1 PID: 3108 Comm: fftest Tainted: G W 4.13.0+ #15
[ 39.402059] Hardware name: LENOVO 20HQS0EG02/20HQS0EG02, BIOS N1MET37W (1.22 ) 07/04/2017
[ 39.402064] 0000000000000086 f0fad82f3ceaa120 ffff88006a93b9a0 ffffffff9de941bb
[ 39.402077] ffff88026df8ae00 ffff88006a93bb00 ffff88006a93b9c0 ffffffff9dca62b7
[ 39.402088] ffff88006a93bb00 ffff88006a93baf8 ffff88006a93b9e0 ffffffff9dca62e7
[ 39.402099] Call Trace:
[ 39.402112] [<ffffffff9de941bb>] dump_stack+0x4d/0x63
[ 39.402123] [<ffffffff9dca62b7>] spin_dump+0x97/0x9c
[ 39.402130] [<ffffffff9dca62e7>] spin_bug+0x2b/0x2d
[ 39.402138] [<ffffffff9dca6373>] do_raw_spin_lock+0x28/0xfd
[ 39.402147] [<ffffffff9e3055cd>] _raw_spin_lock_irqsave+0x19/0x1f
[ 39.402154] [<ffffffff9dca05b7>] complete+0x1d/0x48
[ 39.402162] [<ffffffffc04f30af>] 0xffffffffc04f30af
[ 39.402167] [<ffffffffc04f468c>] 0xffffffffc04f468c
[ 39.402177] [<ffffffff9dd59c16>] ? __slab_free+0x22f/0x359
[ 39.402184] [<ffffffff9dcc13e9>] ? tk_clock_read+0xc/0xe
[ 39.402189] [<ffffffffc04f471f>] 0xffffffffc04f471f
[ 39.402195] [<ffffffff9dc9ffe5>] ? __wake_up+0x44/0x4b
[ 39.402200] [<ffffffffc04f3240>] ? 0xffffffffc04f3240
[ 39.402207] [<ffffffff9e0f57f3>] erase_effect+0xa1/0xd2
[ 39.402214] [<ffffffff9e0f58c6>] input_ff_flush+0x43/0x5c
[ 39.402219] [<ffffffffc04f32ad>] 0xffffffffc04f32ad
[ 39.402227] [<ffffffff9e0f174f>] input_flush_device+0x3d/0x51
[ 39.402234] [<ffffffff9e0f69ae>] evdev_flush+0x49/0x5c
[ 39.402243] [<ffffffff9dd62d6e>] filp_close+0x3f/0x65
[ 39.402253] [<ffffffff9dd7dcf7>] put_files_struct+0x66/0xc1
[ 39.402261] [<ffffffff9dd7ddeb>] exit_files+0x47/0x4e
[ 39.402270] [<ffffffff9dc6b329>] do_exit+0x483/0x969
[ 39.402278] [<ffffffff9dc73211>] ? recalc_sigpending_tsk+0x3d/0x44
[ 39.402285] [<ffffffff9dc6c7a2>] do_group_exit+0x42/0xb0
[ 39.402293] [<ffffffff9dc767e1>] get_signal+0x58d/0x5bf
[ 39.402300] [<ffffffff9dc03701>] do_signal+0x37/0x53e
[ 39.402307] [<ffffffff9e0f8401>] ? evdev_ioctl_handler+0xac8/0xb04
[ 39.402314] [<ffffffff9e0f8464>] ? evdev_ioctl+0x10/0x12
[ 39.402321] [<ffffffff9dd74cfa>] ? do_vfs_ioctl+0x42e/0x501
[ 39.402328] [<ffffffff9dc0170e>] prepare_exit_to_usermode+0x66/0x90
[ 39.402333] [<ffffffff9dc0181b>] syscall_return_slowpath+0xe3/0xec
[ 39.402339] [<ffffffff9e305b7b>] int_ret_from_sys_call+0x25/0x8f
While we could solve this by simply initializing the completion earlier, we
are better off rearranging the code a bit so we avoid calling complete() on
requests that we did not send out. This patch consolidates marking request
slots as free in one place (in uinput_request_submit(), the same place
where we acquire them) and having everyone else simply signal completion
of the requests.
Fixes: 00ce756ce53a ("Input: uinput - mark failed submission requests as free")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2017-09-06 23:22:59 +00:00
|
|
|
retval = uinput_request_send(udev, request);
|
|
|
|
if (retval)
|
|
|
|
goto out;
|
2012-07-30 05:48:32 +00:00
|
|
|
|
2017-09-06 23:31:29 +00:00
|
|
|
if (!wait_for_completion_timeout(&request->done, 30 * HZ)) {
|
|
|
|
retval = -ETIMEDOUT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
Input: uinput - avoid crash when sending FF request to device going away
If FF request comes in while uinput device is going away,
uinput_request_send() will fail with -ENODEV, and uinput_request_submit()
will attempt to mark the slot as unused by calling uinput_request_done().
Unfortunately in this case we haven't initialized request->done completion
yet, and we get a crash:
[ 39.402036] BUG: spinlock bad magic on CPU#1, fftest/3108
[ 39.402046] lock: 0xffff88006a93bb00, .magic: 00000000, .owner: /39, .owner_cpu: 1217155072
[ 39.402055] CPU: 1 PID: 3108 Comm: fftest Tainted: G W 4.13.0+ #15
[ 39.402059] Hardware name: LENOVO 20HQS0EG02/20HQS0EG02, BIOS N1MET37W (1.22 ) 07/04/2017
[ 39.402064] 0000000000000086 f0fad82f3ceaa120 ffff88006a93b9a0 ffffffff9de941bb
[ 39.402077] ffff88026df8ae00 ffff88006a93bb00 ffff88006a93b9c0 ffffffff9dca62b7
[ 39.402088] ffff88006a93bb00 ffff88006a93baf8 ffff88006a93b9e0 ffffffff9dca62e7
[ 39.402099] Call Trace:
[ 39.402112] [<ffffffff9de941bb>] dump_stack+0x4d/0x63
[ 39.402123] [<ffffffff9dca62b7>] spin_dump+0x97/0x9c
[ 39.402130] [<ffffffff9dca62e7>] spin_bug+0x2b/0x2d
[ 39.402138] [<ffffffff9dca6373>] do_raw_spin_lock+0x28/0xfd
[ 39.402147] [<ffffffff9e3055cd>] _raw_spin_lock_irqsave+0x19/0x1f
[ 39.402154] [<ffffffff9dca05b7>] complete+0x1d/0x48
[ 39.402162] [<ffffffffc04f30af>] 0xffffffffc04f30af
[ 39.402167] [<ffffffffc04f468c>] 0xffffffffc04f468c
[ 39.402177] [<ffffffff9dd59c16>] ? __slab_free+0x22f/0x359
[ 39.402184] [<ffffffff9dcc13e9>] ? tk_clock_read+0xc/0xe
[ 39.402189] [<ffffffffc04f471f>] 0xffffffffc04f471f
[ 39.402195] [<ffffffff9dc9ffe5>] ? __wake_up+0x44/0x4b
[ 39.402200] [<ffffffffc04f3240>] ? 0xffffffffc04f3240
[ 39.402207] [<ffffffff9e0f57f3>] erase_effect+0xa1/0xd2
[ 39.402214] [<ffffffff9e0f58c6>] input_ff_flush+0x43/0x5c
[ 39.402219] [<ffffffffc04f32ad>] 0xffffffffc04f32ad
[ 39.402227] [<ffffffff9e0f174f>] input_flush_device+0x3d/0x51
[ 39.402234] [<ffffffff9e0f69ae>] evdev_flush+0x49/0x5c
[ 39.402243] [<ffffffff9dd62d6e>] filp_close+0x3f/0x65
[ 39.402253] [<ffffffff9dd7dcf7>] put_files_struct+0x66/0xc1
[ 39.402261] [<ffffffff9dd7ddeb>] exit_files+0x47/0x4e
[ 39.402270] [<ffffffff9dc6b329>] do_exit+0x483/0x969
[ 39.402278] [<ffffffff9dc73211>] ? recalc_sigpending_tsk+0x3d/0x44
[ 39.402285] [<ffffffff9dc6c7a2>] do_group_exit+0x42/0xb0
[ 39.402293] [<ffffffff9dc767e1>] get_signal+0x58d/0x5bf
[ 39.402300] [<ffffffff9dc03701>] do_signal+0x37/0x53e
[ 39.402307] [<ffffffff9e0f8401>] ? evdev_ioctl_handler+0xac8/0xb04
[ 39.402314] [<ffffffff9e0f8464>] ? evdev_ioctl+0x10/0x12
[ 39.402321] [<ffffffff9dd74cfa>] ? do_vfs_ioctl+0x42e/0x501
[ 39.402328] [<ffffffff9dc0170e>] prepare_exit_to_usermode+0x66/0x90
[ 39.402333] [<ffffffff9dc0181b>] syscall_return_slowpath+0xe3/0xec
[ 39.402339] [<ffffffff9e305b7b>] int_ret_from_sys_call+0x25/0x8f
While we could solve this by simply initializing the completion earlier, we
are better off rearranging the code a bit so we avoid calling complete() on
requests that we did not send out. This patch consolidates marking request
slots as free in one place (in uinput_request_submit(), the same place
where we acquire them) and having everyone else simply signal completion
of the requests.
Fixes: 00ce756ce53a ("Input: uinput - mark failed submission requests as free")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2017-09-06 23:22:59 +00:00
|
|
|
retval = request->retval;
|
|
|
|
|
|
|
|
out:
|
|
|
|
uinput_request_release_slot(udev, request->id);
|
|
|
|
return retval;
|
2012-07-30 05:48:32 +00:00
|
|
|
}
|
|
|
|
|
2009-05-15 05:01:57 +00:00
|
|
|
/*
|
2012-07-30 05:48:32 +00:00
|
|
|
* Fail all outstanding requests so handlers don't wait for the userspace
|
2009-05-15 05:01:57 +00:00
|
|
|
* to finish processing them.
|
|
|
|
*/
|
|
|
|
static void uinput_flush_requests(struct uinput_device *udev)
|
|
|
|
{
|
|
|
|
struct uinput_request *request;
|
|
|
|
int i;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-05-15 05:01:57 +00:00
|
|
|
spin_lock(&udev->requests_lock);
|
|
|
|
|
|
|
|
for (i = 0; i < UINPUT_NUM_REQUESTS; i++) {
|
|
|
|
request = udev->requests[i];
|
|
|
|
if (request) {
|
|
|
|
request->retval = -ENODEV;
|
Input: uinput - avoid crash when sending FF request to device going away
If FF request comes in while uinput device is going away,
uinput_request_send() will fail with -ENODEV, and uinput_request_submit()
will attempt to mark the slot as unused by calling uinput_request_done().
Unfortunately in this case we haven't initialized request->done completion
yet, and we get a crash:
[ 39.402036] BUG: spinlock bad magic on CPU#1, fftest/3108
[ 39.402046] lock: 0xffff88006a93bb00, .magic: 00000000, .owner: /39, .owner_cpu: 1217155072
[ 39.402055] CPU: 1 PID: 3108 Comm: fftest Tainted: G W 4.13.0+ #15
[ 39.402059] Hardware name: LENOVO 20HQS0EG02/20HQS0EG02, BIOS N1MET37W (1.22 ) 07/04/2017
[ 39.402064] 0000000000000086 f0fad82f3ceaa120 ffff88006a93b9a0 ffffffff9de941bb
[ 39.402077] ffff88026df8ae00 ffff88006a93bb00 ffff88006a93b9c0 ffffffff9dca62b7
[ 39.402088] ffff88006a93bb00 ffff88006a93baf8 ffff88006a93b9e0 ffffffff9dca62e7
[ 39.402099] Call Trace:
[ 39.402112] [<ffffffff9de941bb>] dump_stack+0x4d/0x63
[ 39.402123] [<ffffffff9dca62b7>] spin_dump+0x97/0x9c
[ 39.402130] [<ffffffff9dca62e7>] spin_bug+0x2b/0x2d
[ 39.402138] [<ffffffff9dca6373>] do_raw_spin_lock+0x28/0xfd
[ 39.402147] [<ffffffff9e3055cd>] _raw_spin_lock_irqsave+0x19/0x1f
[ 39.402154] [<ffffffff9dca05b7>] complete+0x1d/0x48
[ 39.402162] [<ffffffffc04f30af>] 0xffffffffc04f30af
[ 39.402167] [<ffffffffc04f468c>] 0xffffffffc04f468c
[ 39.402177] [<ffffffff9dd59c16>] ? __slab_free+0x22f/0x359
[ 39.402184] [<ffffffff9dcc13e9>] ? tk_clock_read+0xc/0xe
[ 39.402189] [<ffffffffc04f471f>] 0xffffffffc04f471f
[ 39.402195] [<ffffffff9dc9ffe5>] ? __wake_up+0x44/0x4b
[ 39.402200] [<ffffffffc04f3240>] ? 0xffffffffc04f3240
[ 39.402207] [<ffffffff9e0f57f3>] erase_effect+0xa1/0xd2
[ 39.402214] [<ffffffff9e0f58c6>] input_ff_flush+0x43/0x5c
[ 39.402219] [<ffffffffc04f32ad>] 0xffffffffc04f32ad
[ 39.402227] [<ffffffff9e0f174f>] input_flush_device+0x3d/0x51
[ 39.402234] [<ffffffff9e0f69ae>] evdev_flush+0x49/0x5c
[ 39.402243] [<ffffffff9dd62d6e>] filp_close+0x3f/0x65
[ 39.402253] [<ffffffff9dd7dcf7>] put_files_struct+0x66/0xc1
[ 39.402261] [<ffffffff9dd7ddeb>] exit_files+0x47/0x4e
[ 39.402270] [<ffffffff9dc6b329>] do_exit+0x483/0x969
[ 39.402278] [<ffffffff9dc73211>] ? recalc_sigpending_tsk+0x3d/0x44
[ 39.402285] [<ffffffff9dc6c7a2>] do_group_exit+0x42/0xb0
[ 39.402293] [<ffffffff9dc767e1>] get_signal+0x58d/0x5bf
[ 39.402300] [<ffffffff9dc03701>] do_signal+0x37/0x53e
[ 39.402307] [<ffffffff9e0f8401>] ? evdev_ioctl_handler+0xac8/0xb04
[ 39.402314] [<ffffffff9e0f8464>] ? evdev_ioctl+0x10/0x12
[ 39.402321] [<ffffffff9dd74cfa>] ? do_vfs_ioctl+0x42e/0x501
[ 39.402328] [<ffffffff9dc0170e>] prepare_exit_to_usermode+0x66/0x90
[ 39.402333] [<ffffffff9dc0181b>] syscall_return_slowpath+0xe3/0xec
[ 39.402339] [<ffffffff9e305b7b>] int_ret_from_sys_call+0x25/0x8f
While we could solve this by simply initializing the completion earlier, we
are better off rearranging the code a bit so we avoid calling complete() on
requests that we did not send out. This patch consolidates marking request
slots as free in one place (in uinput_request_submit(), the same place
where we acquire them) and having everyone else simply signal completion
of the requests.
Fixes: 00ce756ce53a ("Input: uinput - mark failed submission requests as free")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2017-09-06 23:22:59 +00:00
|
|
|
complete(&request->done);
|
2009-05-15 05:01:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock(&udev->requests_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-07-19 05:41:09 +00:00
|
|
|
static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
|
|
|
|
{
|
|
|
|
uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude)
|
|
|
|
{
|
|
|
|
uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
|
|
|
|
{
|
|
|
|
return uinput_dev_event(dev, EV_FF, effect_id, value);
|
|
|
|
}
|
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
static int uinput_dev_upload_effect(struct input_dev *dev,
|
|
|
|
struct ff_effect *effect,
|
|
|
|
struct ff_effect *old)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2009-05-15 05:01:57 +00:00
|
|
|
struct uinput_device *udev = input_get_drvdata(dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct uinput_request request;
|
|
|
|
|
2008-10-17 02:31:42 +00:00
|
|
|
/*
|
|
|
|
* uinput driver does not currently support periodic effects with
|
|
|
|
* custom waveform since it does not have a way to pass buffer of
|
|
|
|
* samples (custom_data) to userspace. If ever there is a device
|
|
|
|
* supporting custom waveforms we would need to define an additional
|
|
|
|
* ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out.
|
|
|
|
*/
|
|
|
|
if (effect->type == FF_PERIODIC &&
|
|
|
|
effect->u.periodic.waveform == FF_CUSTOM)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2005-06-30 05:48:14 +00:00
|
|
|
request.code = UI_FF_UPLOAD;
|
2006-07-19 05:41:09 +00:00
|
|
|
request.u.upload.effect = effect;
|
|
|
|
request.u.upload.old = old;
|
2005-06-30 05:48:14 +00:00
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
return uinput_request_submit(udev, &request);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
|
|
|
|
{
|
2009-05-15 05:01:57 +00:00
|
|
|
struct uinput_device *udev = input_get_drvdata(dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
struct uinput_request request;
|
|
|
|
|
|
|
|
if (!test_bit(EV_FF, dev->evbit))
|
|
|
|
return -ENOSYS;
|
|
|
|
|
2005-06-30 05:48:14 +00:00
|
|
|
request.code = UI_FF_ERASE;
|
2005-04-16 22:20:36 +00:00
|
|
|
request.u.effect_id = effect_id;
|
2005-06-30 05:48:14 +00:00
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
return uinput_request_submit(udev, &request);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2017-09-02 00:13:43 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2005-11-20 05:51:22 +00:00
|
|
|
static void uinput_destroy_device(struct uinput_device *udev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-20 05:51:22 +00:00
|
|
|
const char *name, *phys;
|
2009-05-15 05:01:57 +00:00
|
|
|
struct input_dev *dev = udev->dev;
|
|
|
|
enum uinput_state old_state = udev->state;
|
|
|
|
|
|
|
|
udev->state = UIST_NEW_DEVICE;
|
2005-11-20 05:51:22 +00:00
|
|
|
|
2009-05-15 05:01:57 +00:00
|
|
|
if (dev) {
|
|
|
|
name = dev->name;
|
|
|
|
phys = dev->phys;
|
|
|
|
if (old_state == UIST_CREATED) {
|
|
|
|
uinput_flush_requests(udev);
|
|
|
|
input_unregister_device(dev);
|
|
|
|
} else {
|
|
|
|
input_free_device(dev);
|
|
|
|
}
|
2005-11-20 05:51:22 +00:00
|
|
|
kfree(name);
|
|
|
|
kfree(phys);
|
|
|
|
udev->dev = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-20 05:51:22 +00:00
|
|
|
static int uinput_create_device(struct uinput_device *udev)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2006-07-19 05:41:09 +00:00
|
|
|
struct input_dev *dev = udev->dev;
|
2015-10-25 09:34:13 +00:00
|
|
|
int error, nslot;
|
2005-11-20 05:51:22 +00:00
|
|
|
|
|
|
|
if (udev->state != UIST_SETUP_COMPLETE) {
|
|
|
|
printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
|
2005-04-16 22:20:36 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
Input: uinput - fix crash when mixing old and new init style
If user tries to initialize uinput device mixing old and new style
initialization (i.e. using old UI_SET_ABSBIT instead of UI_ABS_SETUP,
we forget to allocate input->absinfo and will crash when trying to send
absolute events:
ioctl(ui, UI_DEV_SETUP, &us);
ioctl(ui, UI_SET_PHYS, "Test");
ioctl(ui, UI_SET_EVBIT, EV_ABS);
ioctl(ui, UI_SET_ABSBIT, ABS_X);
ioctl(ui, UI_SET_ABSBIT, ABS_Y);
ioctl(ui, UI_DEV_CREATE, 0);
Reported-by: Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=191811
Fixes: fbae10db0940 ("Input: uinput - rework ABS validation")
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2017-01-31 22:56:43 +00:00
|
|
|
if (test_bit(EV_ABS, dev->evbit)) {
|
|
|
|
input_alloc_absinfo(dev);
|
|
|
|
if (!dev->absinfo) {
|
|
|
|
error = -EINVAL;
|
2015-10-25 09:34:13 +00:00
|
|
|
goto fail1;
|
Input: uinput - fix crash when mixing old and new init style
If user tries to initialize uinput device mixing old and new style
initialization (i.e. using old UI_SET_ABSBIT instead of UI_ABS_SETUP,
we forget to allocate input->absinfo and will crash when trying to send
absolute events:
ioctl(ui, UI_DEV_SETUP, &us);
ioctl(ui, UI_SET_PHYS, "Test");
ioctl(ui, UI_SET_EVBIT, EV_ABS);
ioctl(ui, UI_SET_ABSBIT, ABS_X);
ioctl(ui, UI_SET_ABSBIT, ABS_Y);
ioctl(ui, UI_DEV_CREATE, 0);
Reported-by: Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=191811
Fixes: fbae10db0940 ("Input: uinput - rework ABS validation")
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2017-01-31 22:56:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2015-10-25 09:34:13 +00:00
|
|
|
}
|
|
|
|
|
2015-12-19 01:32:19 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-07-19 05:41:09 +00:00
|
|
|
if (udev->ff_effects_max) {
|
|
|
|
error = input_ff_create(dev, udev->ff_effects_max);
|
|
|
|
if (error)
|
|
|
|
goto fail1;
|
|
|
|
|
|
|
|
dev->ff->upload = uinput_dev_upload_effect;
|
|
|
|
dev->ff->erase = uinput_dev_erase_effect;
|
|
|
|
dev->ff->playback = uinput_dev_playback;
|
|
|
|
dev->ff->set_gain = uinput_dev_set_gain;
|
|
|
|
dev->ff->set_autocenter = uinput_dev_set_autocenter;
|
2017-09-02 00:13:43 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
2005-11-20 05:51:22 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:42:26 +00:00
|
|
|
dev->event = uinput_dev_event;
|
|
|
|
|
|
|
|
input_set_drvdata(udev->dev, udev);
|
|
|
|
|
2006-07-19 05:41:09 +00:00
|
|
|
error = input_register_device(udev->dev);
|
|
|
|
if (error)
|
|
|
|
goto fail2;
|
|
|
|
|
2005-11-20 05:51:22 +00:00
|
|
|
udev->state = UIST_CREATED;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return 0;
|
2006-07-19 05:41:09 +00:00
|
|
|
|
|
|
|
fail2: input_ff_destroy(dev);
|
|
|
|
fail1: uinput_destroy_device(udev);
|
|
|
|
return error;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int uinput_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
2005-11-20 05:51:22 +00:00
|
|
|
struct uinput_device *newdev;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-20 05:51:22 +00:00
|
|
|
newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!newdev)
|
2005-11-20 05:51:22 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
2006-02-19 05:22:36 +00:00
|
|
|
mutex_init(&newdev->mutex);
|
2005-06-30 05:48:14 +00:00
|
|
|
spin_lock_init(&newdev->requests_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
init_waitqueue_head(&newdev->requests_waitq);
|
2005-11-20 05:51:22 +00:00
|
|
|
init_waitqueue_head(&newdev->waitq);
|
|
|
|
newdev->state = UIST_NEW_DEVICE;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
file->private_data = newdev;
|
2010-02-04 08:30:39 +00:00
|
|
|
nonseekable_open(inode, file);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-25 09:34:13 +00:00
|
|
|
static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code,
|
|
|
|
const struct input_absinfo *abs)
|
|
|
|
{
|
|
|
|
int min, max;
|
|
|
|
|
|
|
|
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 (abs->flat > max - min) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
static int uinput_validate_absbits(struct input_dev *dev)
|
|
|
|
{
|
|
|
|
unsigned int cnt;
|
2015-10-25 09:34:13 +00:00
|
|
|
int error;
|
2014-07-21 00:16:23 +00:00
|
|
|
|
|
|
|
if (!test_bit(EV_ABS, dev->evbit))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if absmin/absmax/absfuzz/absflat are sane.
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2015-07-09 17:18:22 +00:00
|
|
|
for_each_set_bit(cnt, dev->absbit, ABS_CNT) {
|
2015-10-25 09:34:13 +00:00
|
|
|
if (!dev->absinfo)
|
2014-07-21 00:16:23 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2015-10-25 09:34:13 +00:00
|
|
|
error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2014-07-21 00:16:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2015-12-19 01:20:09 +00:00
|
|
|
static int uinput_dev_setup(struct uinput_device *udev,
|
|
|
|
struct uinput_setup __user *arg)
|
|
|
|
{
|
|
|
|
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->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;
|
2015-10-25 09:34:13 +00:00
|
|
|
int error;
|
2015-12-19 01:20:09 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2015-10-25 09:34:13 +00:00
|
|
|
error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2015-12-19 01:20:09 +00:00
|
|
|
input_alloc_absinfo(dev);
|
|
|
|
if (!dev->absinfo)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
set_bit(setup.code, dev->absbit);
|
|
|
|
dev->absinfo[setup.code] = setup.absinfo;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* legacy setup via write() */
|
|
|
|
static int uinput_setup_device_legacy(struct uinput_device *udev,
|
|
|
|
const char __user *buffer, size_t count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct uinput_user_dev *user_dev;
|
|
|
|
struct input_dev *dev;
|
2011-02-11 09:10:44 +00:00
|
|
|
int i;
|
2005-06-30 05:47:50 +00:00
|
|
|
int retval;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-20 05:51:22 +00:00
|
|
|
if (count != sizeof(struct uinput_user_dev))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!udev->dev) {
|
2017-09-06 23:42:26 +00:00
|
|
|
udev->dev = input_allocate_device();
|
|
|
|
if (!udev->dev)
|
|
|
|
return -ENOMEM;
|
2005-11-20 05:51:22 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
dev = udev->dev;
|
|
|
|
|
2011-02-11 09:10:45 +00:00
|
|
|
user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));
|
2011-02-18 16:30:52 +00:00
|
|
|
if (IS_ERR(user_dev))
|
2011-02-11 09:10:45 +00:00
|
|
|
return PTR_ERR(user_dev);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-07-19 05:41:09 +00:00
|
|
|
udev->ff_effects_max = user_dev->ff_effects_max;
|
|
|
|
|
2011-02-11 09:10:44 +00:00
|
|
|
/* Ensure name is filled in */
|
|
|
|
if (!user_dev->name[0]) {
|
2005-11-20 05:51:22 +00:00
|
|
|
retval = -EINVAL;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(dev->name);
|
2011-02-11 09:10:44 +00:00
|
|
|
dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!dev->name) {
|
2005-04-16 22:20:36 +00:00
|
|
|
retval = -ENOMEM;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->id.bustype = user_dev->id.bustype;
|
|
|
|
dev->id.vendor = user_dev->id.vendor;
|
|
|
|
dev->id.product = user_dev->id.product;
|
|
|
|
dev->id.version = user_dev->id.version;
|
|
|
|
|
2015-09-19 18:22:57 +00:00
|
|
|
for (i = 0; i < ABS_CNT; i++) {
|
2010-08-03 03:15:17 +00:00
|
|
|
input_abs_set_max(dev, i, user_dev->absmax[i]);
|
|
|
|
input_abs_set_min(dev, i, user_dev->absmin[i]);
|
|
|
|
input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);
|
|
|
|
input_abs_set_flat(dev, i, user_dev->absflat[i]);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2014-07-21 00:16:23 +00:00
|
|
|
retval = uinput_validate_absbits(dev);
|
|
|
|
if (retval < 0)
|
|
|
|
goto exit;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-20 05:51:22 +00:00
|
|
|
udev->state = UIST_SETUP_COMPLETE;
|
|
|
|
retval = count;
|
|
|
|
|
|
|
|
exit:
|
2005-04-16 22:20:36 +00:00
|
|
|
kfree(user_dev);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2013-09-18 19:40:47 +00:00
|
|
|
static ssize_t uinput_inject_events(struct uinput_device *udev,
|
|
|
|
const char __user *buffer, size_t count)
|
2005-11-20 05:51:22 +00:00
|
|
|
{
|
|
|
|
struct input_event ev;
|
2013-09-18 19:40:47 +00:00
|
|
|
size_t bytes = 0;
|
2005-11-20 05:51:22 +00:00
|
|
|
|
2013-09-18 19:40:47 +00:00
|
|
|
if (count != 0 && count < input_event_size())
|
2005-11-20 05:51:22 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2013-09-18 19:40:47 +00:00
|
|
|
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;
|
2005-11-20 05:51:22 +00:00
|
|
|
|
2013-09-18 19:40:47 +00:00
|
|
|
input_event(udev->dev, ev.type, ev.code, ev.value);
|
|
|
|
bytes += input_event_size();
|
|
|
|
}
|
2005-11-20 05:51:22 +00:00
|
|
|
|
2013-09-18 19:40:47 +00:00
|
|
|
return bytes;
|
2005-11-20 05:51:22 +00:00
|
|
|
}
|
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
static ssize_t uinput_write(struct file *file, const char __user *buffer,
|
|
|
|
size_t count, loff_t *ppos)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct uinput_device *udev = file->private_data;
|
2005-11-20 05:51:22 +00:00
|
|
|
int retval;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-07-30 05:48:31 +00:00
|
|
|
if (count == 0)
|
|
|
|
return 0;
|
|
|
|
|
2006-02-19 05:22:36 +00:00
|
|
|
retval = mutex_lock_interruptible(&udev->mutex);
|
2005-11-20 05:51:22 +00:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
retval = udev->state == UIST_CREATED ?
|
2013-09-18 19:40:47 +00:00
|
|
|
uinput_inject_events(udev, buffer, count) :
|
2015-12-19 01:20:09 +00:00
|
|
|
uinput_setup_device_legacy(udev, buffer, count);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-02-19 05:22:36 +00:00
|
|
|
mutex_unlock(&udev->mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-20 05:51:22 +00:00
|
|
|
return retval;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2012-07-30 05:48:31 +00:00
|
|
|
static bool uinput_fetch_next_event(struct uinput_device *udev,
|
|
|
|
struct input_event *event)
|
|
|
|
{
|
|
|
|
bool have_event;
|
|
|
|
|
|
|
|
spin_lock_irq(&udev->dev->event_lock);
|
|
|
|
|
|
|
|
have_event = udev->head != udev->tail;
|
|
|
|
if (have_event) {
|
|
|
|
*event = udev->buff[udev->tail];
|
|
|
|
udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irq(&udev->dev->event_lock);
|
|
|
|
|
|
|
|
return have_event;
|
|
|
|
}
|
|
|
|
|
2012-07-30 05:48:31 +00:00
|
|
|
static ssize_t uinput_events_to_user(struct uinput_device *udev,
|
|
|
|
char __user *buffer, size_t count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2012-07-30 05:48:31 +00:00
|
|
|
struct input_event event;
|
2012-07-30 05:48:31 +00:00
|
|
|
size_t read = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-07-30 05:48:31 +00:00
|
|
|
while (read + input_event_size() <= count &&
|
|
|
|
uinput_fetch_next_event(udev, &event)) {
|
2012-07-30 05:48:31 +00:00
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
if (input_event_to_user(buffer + read, &event))
|
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-07-30 05:48:31 +00:00
|
|
|
read += input_event_size();
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
return read;
|
2012-07-30 05:48:31 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-07-30 05:48:31 +00:00
|
|
|
static ssize_t uinput_read(struct file *file, char __user *buffer,
|
|
|
|
size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct uinput_device *udev = file->private_data;
|
|
|
|
ssize_t retval;
|
2005-11-20 05:51:22 +00:00
|
|
|
|
2012-07-30 05:48:31 +00:00
|
|
|
if (count != 0 && count < input_event_size())
|
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-07-30 05:48:31 +00:00
|
|
|
do {
|
|
|
|
retval = mutex_lock_interruptible(&udev->mutex);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
2012-07-30 05:48:31 +00:00
|
|
|
|
2012-07-30 05:48:31 +00:00
|
|
|
if (udev->state != UIST_CREATED)
|
|
|
|
retval = -ENODEV;
|
|
|
|
else if (udev->head == udev->tail &&
|
|
|
|
(file->f_flags & O_NONBLOCK))
|
|
|
|
retval = -EAGAIN;
|
|
|
|
else
|
|
|
|
retval = uinput_events_to_user(udev, buffer, count);
|
2012-07-30 05:48:31 +00:00
|
|
|
|
2012-07-30 05:48:31 +00:00
|
|
|
mutex_unlock(&udev->mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-07-30 05:48:31 +00:00
|
|
|
if (retval || count == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!(file->f_flags & O_NONBLOCK))
|
|
|
|
retval = wait_event_interruptible(udev->waitq,
|
|
|
|
udev->head != udev->tail ||
|
|
|
|
udev->state != UIST_CREATED);
|
|
|
|
} while (retval == 0);
|
2005-11-20 05:51:22 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int uinput_poll(struct file *file, poll_table *wait)
|
|
|
|
{
|
|
|
|
struct uinput_device *udev = file->private_data;
|
|
|
|
|
|
|
|
poll_wait(file, &udev->waitq, wait);
|
|
|
|
|
|
|
|
if (udev->head != udev->tail)
|
|
|
|
return POLLIN | POLLRDNORM;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-20 05:51:22 +00:00
|
|
|
static int uinput_release(struct inode *inode, struct file *file)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-20 05:51:22 +00:00
|
|
|
struct uinput_device *udev = file->private_data;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-20 05:51:22 +00:00
|
|
|
uinput_destroy_device(udev);
|
2005-04-16 22:20:36 +00:00
|
|
|
kfree(udev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-10-17 02:31:42 +00:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
struct uinput_ff_upload_compat {
|
2012-07-30 05:48:32 +00:00
|
|
|
__u32 request_id;
|
|
|
|
__s32 retval;
|
2008-10-17 02:31:42 +00:00
|
|
|
struct ff_effect_compat effect;
|
|
|
|
struct ff_effect_compat old;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int uinput_ff_upload_to_user(char __user *buffer,
|
|
|
|
const struct uinput_ff_upload *ff_up)
|
|
|
|
{
|
2016-03-25 21:20:47 +00:00
|
|
|
if (in_compat_syscall()) {
|
2008-10-17 02:31:42 +00:00
|
|
|
struct uinput_ff_upload_compat ff_up_compat;
|
|
|
|
|
|
|
|
ff_up_compat.request_id = ff_up->request_id;
|
|
|
|
ff_up_compat.retval = ff_up->retval;
|
|
|
|
/*
|
|
|
|
* It so happens that the pointer that gives us the trouble
|
|
|
|
* is the last field in the structure. Since we don't support
|
|
|
|
* custom waveforms in uinput anyway we can just copy the whole
|
|
|
|
* thing (to the compat size) and ignore the pointer.
|
|
|
|
*/
|
|
|
|
memcpy(&ff_up_compat.effect, &ff_up->effect,
|
|
|
|
sizeof(struct ff_effect_compat));
|
|
|
|
memcpy(&ff_up_compat.old, &ff_up->old,
|
|
|
|
sizeof(struct ff_effect_compat));
|
|
|
|
|
|
|
|
if (copy_to_user(buffer, &ff_up_compat,
|
|
|
|
sizeof(struct uinput_ff_upload_compat)))
|
|
|
|
return -EFAULT;
|
|
|
|
} else {
|
|
|
|
if (copy_to_user(buffer, ff_up,
|
|
|
|
sizeof(struct uinput_ff_upload)))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uinput_ff_upload_from_user(const char __user *buffer,
|
|
|
|
struct uinput_ff_upload *ff_up)
|
|
|
|
{
|
2016-03-25 21:20:47 +00:00
|
|
|
if (in_compat_syscall()) {
|
2008-10-17 02:31:42 +00:00
|
|
|
struct uinput_ff_upload_compat ff_up_compat;
|
|
|
|
|
|
|
|
if (copy_from_user(&ff_up_compat, buffer,
|
|
|
|
sizeof(struct uinput_ff_upload_compat)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
ff_up->request_id = ff_up_compat.request_id;
|
|
|
|
ff_up->retval = ff_up_compat.retval;
|
|
|
|
memcpy(&ff_up->effect, &ff_up_compat.effect,
|
|
|
|
sizeof(struct ff_effect_compat));
|
|
|
|
memcpy(&ff_up->old, &ff_up_compat.old,
|
|
|
|
sizeof(struct ff_effect_compat));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (copy_from_user(ff_up, buffer,
|
|
|
|
sizeof(struct uinput_ff_upload)))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
static int uinput_ff_upload_to_user(char __user *buffer,
|
|
|
|
const struct uinput_ff_upload *ff_up)
|
|
|
|
{
|
|
|
|
if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int uinput_ff_upload_from_user(const char __user *buffer,
|
|
|
|
struct uinput_ff_upload *ff_up)
|
|
|
|
{
|
|
|
|
if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2005-11-20 05:51:22 +00:00
|
|
|
#define uinput_set_bit(_arg, _bit, _max) \
|
|
|
|
({ \
|
|
|
|
int __ret = 0; \
|
|
|
|
if (udev->state == UIST_CREATED) \
|
|
|
|
__ret = -EINVAL; \
|
|
|
|
else if ((_arg) > (_max)) \
|
|
|
|
__ret = -EINVAL; \
|
|
|
|
else set_bit((_arg), udev->dev->_bit); \
|
|
|
|
__ret; \
|
|
|
|
})
|
|
|
|
|
2014-01-31 01:20:24 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-10-17 02:31:42 +00:00
|
|
|
static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
|
|
|
|
unsigned long arg, void __user *p)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2005-11-20 05:51:22 +00:00
|
|
|
int retval;
|
2008-10-17 02:31:42 +00:00
|
|
|
struct uinput_device *udev = file->private_data;
|
2005-04-16 22:20:36 +00:00
|
|
|
struct uinput_ff_upload ff_up;
|
|
|
|
struct uinput_ff_erase ff_erase;
|
|
|
|
struct uinput_request *req;
|
2005-06-30 05:50:38 +00:00
|
|
|
char *phys;
|
2014-01-31 01:20:24 +00:00
|
|
|
const char *name;
|
|
|
|
unsigned int size;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-02-19 05:22:36 +00:00
|
|
|
retval = mutex_lock_interruptible(&udev->mutex);
|
2005-11-20 05:51:22 +00:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
if (!udev->dev) {
|
2017-09-06 23:42:26 +00:00
|
|
|
udev->dev = input_allocate_device();
|
|
|
|
if (!udev->dev)
|
|
|
|
return -ENOMEM;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd) {
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_GET_VERSION:
|
|
|
|
if (put_user(UINPUT_VERSION, (unsigned int __user *)p))
|
|
|
|
retval = -EFAULT;
|
|
|
|
goto out;
|
2014-07-21 00:27:09 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_DEV_CREATE:
|
|
|
|
retval = uinput_create_device(udev);
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_DEV_DESTROY:
|
|
|
|
uinput_destroy_device(udev);
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_DEV_SETUP:
|
|
|
|
retval = uinput_dev_setup(udev, p);
|
|
|
|
goto out;
|
2015-12-19 01:20:09 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
/* UI_ABS_SETUP is handled in the variable size ioctls */
|
2015-12-19 01:20:09 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_SET_EVBIT:
|
|
|
|
retval = uinput_set_bit(arg, evbit, EV_MAX);
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_SET_KEYBIT:
|
|
|
|
retval = uinput_set_bit(arg, keybit, KEY_MAX);
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_SET_RELBIT:
|
|
|
|
retval = uinput_set_bit(arg, relbit, REL_MAX);
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_SET_ABSBIT:
|
|
|
|
retval = uinput_set_bit(arg, absbit, ABS_MAX);
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_SET_MSCBIT:
|
|
|
|
retval = uinput_set_bit(arg, mscbit, MSC_MAX);
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_SET_LEDBIT:
|
|
|
|
retval = uinput_set_bit(arg, ledbit, LED_MAX);
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
case UI_SET_SNDBIT:
|
|
|
|
retval = uinput_set_bit(arg, sndbit, SND_MAX);
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
case UI_SET_FFBIT:
|
|
|
|
retval = uinput_set_bit(arg, ffbit, FF_MAX);
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
case UI_SET_SWBIT:
|
|
|
|
retval = uinput_set_bit(arg, swbit, SW_MAX);
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
case UI_SET_PROPBIT:
|
|
|
|
retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX);
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_SET_PHYS:
|
|
|
|
if (udev->state == UIST_CREATED) {
|
|
|
|
retval = -EINVAL;
|
2014-01-31 01:16:36 +00:00
|
|
|
goto out;
|
2017-09-06 23:34:31 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
phys = strndup_user(p, 1024);
|
|
|
|
if (IS_ERR(phys)) {
|
|
|
|
retval = PTR_ERR(phys);
|
2014-01-31 01:16:36 +00:00
|
|
|
goto out;
|
2017-09-06 23:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
kfree(udev->dev->phys);
|
|
|
|
udev->dev->phys = phys;
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_BEGIN_FF_UPLOAD:
|
|
|
|
retval = uinput_ff_upload_from_user(p, &ff_up);
|
|
|
|
if (retval)
|
2014-01-31 01:16:36 +00:00
|
|
|
goto out;
|
2005-11-20 05:51:33 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
req = uinput_request_find(udev, ff_up.request_id);
|
|
|
|
if (!req || req->code != UI_FF_UPLOAD ||
|
|
|
|
!req->u.upload.effect) {
|
|
|
|
retval = -EINVAL;
|
2014-01-31 01:16:36 +00:00
|
|
|
goto out;
|
2017-09-06 23:34:31 +00:00
|
|
|
}
|
2010-12-18 19:51:13 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
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));
|
2011-02-11 09:10:45 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
retval = uinput_ff_upload_to_user(p, &ff_up);
|
|
|
|
goto out;
|
2011-02-11 09:10:45 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_BEGIN_FF_ERASE:
|
|
|
|
if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
|
|
|
|
retval = -EFAULT;
|
2014-01-31 01:16:36 +00:00
|
|
|
goto out;
|
2017-09-06 23:34:31 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
req = uinput_request_find(udev, ff_erase.request_id);
|
|
|
|
if (!req || req->code != UI_FF_ERASE) {
|
|
|
|
retval = -EINVAL;
|
2014-01-31 01:16:36 +00:00
|
|
|
goto out;
|
2017-09-06 23:34:31 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
ff_erase.retval = 0;
|
|
|
|
ff_erase.effect_id = req->u.effect_id;
|
|
|
|
if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
|
|
|
|
retval = -EFAULT;
|
2014-01-31 01:16:36 +00:00
|
|
|
goto out;
|
2017-09-06 23:34:31 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
goto out;
|
2008-10-17 02:31:42 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_END_FF_UPLOAD:
|
|
|
|
retval = uinput_ff_upload_from_user(p, &ff_up);
|
|
|
|
if (retval)
|
|
|
|
goto out;
|
2008-10-17 02:31:42 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
req = uinput_request_find(udev, ff_up.request_id);
|
|
|
|
if (!req || req->code != UI_FF_UPLOAD ||
|
|
|
|
!req->u.upload.effect) {
|
|
|
|
retval = -EINVAL;
|
2014-01-31 01:16:36 +00:00
|
|
|
goto out;
|
2017-09-06 23:34:31 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
req->retval = ff_up.retval;
|
|
|
|
complete(&req->done);
|
|
|
|
goto out;
|
2008-10-17 02:31:42 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
case UI_END_FF_ERASE:
|
|
|
|
if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
|
|
|
|
retval = -EFAULT;
|
|
|
|
goto out;
|
|
|
|
}
|
2008-10-17 02:31:42 +00:00
|
|
|
|
2017-09-06 23:34:31 +00:00
|
|
|
req = uinput_request_find(udev, ff_erase.request_id);
|
|
|
|
if (!req || req->code != UI_FF_ERASE) {
|
|
|
|
retval = -EINVAL;
|
2014-01-31 01:16:36 +00:00
|
|
|
goto out;
|
2017-09-06 23:34:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
req->retval = ff_erase.retval;
|
|
|
|
complete(&req->done);
|
|
|
|
goto out;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2005-11-20 05:51:22 +00:00
|
|
|
|
2014-01-31 01:20:24 +00:00
|
|
|
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;
|
2015-12-19 01:20:09 +00:00
|
|
|
|
|
|
|
case UI_ABS_SETUP & ~IOCSIZE_MASK:
|
|
|
|
retval = uinput_abs_setup(udev, p, size);
|
|
|
|
goto out;
|
2014-01-31 01:20:24 +00:00
|
|
|
}
|
|
|
|
|
2014-01-31 01:16:36 +00:00
|
|
|
retval = -EINVAL;
|
2005-11-20 05:51:22 +00:00
|
|
|
out:
|
2006-02-19 05:22:36 +00:00
|
|
|
mutex_unlock(&udev->mutex);
|
2005-04-16 22:20:36 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2008-10-17 02:31:42 +00:00
|
|
|
static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
2016-05-20 17:58:59 +00:00
|
|
|
|
|
|
|
#define UI_SET_PHYS_COMPAT _IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t)
|
|
|
|
|
2012-07-30 05:48:32 +00:00
|
|
|
static long uinput_compat_ioctl(struct file *file,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
2008-10-17 02:31:42 +00:00
|
|
|
{
|
2016-05-20 17:58:59 +00:00
|
|
|
if (cmd == UI_SET_PHYS_COMPAT)
|
|
|
|
cmd = UI_SET_PHYS;
|
|
|
|
|
2008-10-17 02:31:42 +00:00
|
|
|
return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-02-12 08:55:32 +00:00
|
|
|
static const struct file_operations uinput_fops = {
|
2005-11-20 05:51:22 +00:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = uinput_open,
|
|
|
|
.release = uinput_release,
|
|
|
|
.read = uinput_read,
|
|
|
|
.write = uinput_write,
|
|
|
|
.poll = uinput_poll,
|
|
|
|
.unlocked_ioctl = uinput_ioctl,
|
2008-10-17 02:31:42 +00:00
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
.compat_ioctl = uinput_compat_ioctl,
|
|
|
|
#endif
|
llseek: automatically add .llseek fop
All file_operations should get a .llseek operation so we can make
nonseekable_open the default for future file operations without a
.llseek pointer.
The three cases that we can automatically detect are no_llseek, seq_lseek
and default_llseek. For cases where we can we can automatically prove that
the file offset is always ignored, we use noop_llseek, which maintains
the current behavior of not returning an error from a seek.
New drivers should normally not use noop_llseek but instead use no_llseek
and call nonseekable_open at open time. Existing drivers can be converted
to do the same when the maintainer knows for certain that no user code
relies on calling seek on the device file.
The generated code is often incorrectly indented and right now contains
comments that clarify for each added line why a specific variant was
chosen. In the version that gets submitted upstream, the comments will
be gone and I will manually fix the indentation, because there does not
seem to be a way to do that using coccinelle.
Some amount of new code is currently sitting in linux-next that should get
the same modifications, which I will do at the end of the merge window.
Many thanks to Julia Lawall for helping me learn to write a semantic
patch that does all this.
===== begin semantic patch =====
// This adds an llseek= method to all file operations,
// as a preparation for making no_llseek the default.
//
// The rules are
// - use no_llseek explicitly if we do nonseekable_open
// - use seq_lseek for sequential files
// - use default_llseek if we know we access f_pos
// - use noop_llseek if we know we don't access f_pos,
// but we still want to allow users to call lseek
//
@ open1 exists @
identifier nested_open;
@@
nested_open(...)
{
<+...
nonseekable_open(...)
...+>
}
@ open exists@
identifier open_f;
identifier i, f;
identifier open1.nested_open;
@@
int open_f(struct inode *i, struct file *f)
{
<+...
(
nonseekable_open(...)
|
nested_open(...)
)
...+>
}
@ read disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ read_no_fpos disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
... when != off
}
@ write @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ write_no_fpos @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
... when != off
}
@ fops0 @
identifier fops;
@@
struct file_operations fops = {
...
};
@ has_llseek depends on fops0 @
identifier fops0.fops;
identifier llseek_f;
@@
struct file_operations fops = {
...
.llseek = llseek_f,
...
};
@ has_read depends on fops0 @
identifier fops0.fops;
identifier read_f;
@@
struct file_operations fops = {
...
.read = read_f,
...
};
@ has_write depends on fops0 @
identifier fops0.fops;
identifier write_f;
@@
struct file_operations fops = {
...
.write = write_f,
...
};
@ has_open depends on fops0 @
identifier fops0.fops;
identifier open_f;
@@
struct file_operations fops = {
...
.open = open_f,
...
};
// use no_llseek if we call nonseekable_open
////////////////////////////////////////////
@ nonseekable1 depends on !has_llseek && has_open @
identifier fops0.fops;
identifier nso ~= "nonseekable_open";
@@
struct file_operations fops = {
... .open = nso, ...
+.llseek = no_llseek, /* nonseekable */
};
@ nonseekable2 depends on !has_llseek @
identifier fops0.fops;
identifier open.open_f;
@@
struct file_operations fops = {
... .open = open_f, ...
+.llseek = no_llseek, /* open uses nonseekable */
};
// use seq_lseek for sequential files
/////////////////////////////////////
@ seq depends on !has_llseek @
identifier fops0.fops;
identifier sr ~= "seq_read";
@@
struct file_operations fops = {
... .read = sr, ...
+.llseek = seq_lseek, /* we have seq_read */
};
// use default_llseek if there is a readdir
///////////////////////////////////////////
@ fops1 depends on !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier readdir_e;
@@
// any other fop is used that changes pos
struct file_operations fops = {
... .readdir = readdir_e, ...
+.llseek = default_llseek, /* readdir is present */
};
// use default_llseek if at least one of read/write touches f_pos
/////////////////////////////////////////////////////////////////
@ fops2 depends on !fops1 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read.read_f;
@@
// read fops use offset
struct file_operations fops = {
... .read = read_f, ...
+.llseek = default_llseek, /* read accesses f_pos */
};
@ fops3 depends on !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write.write_f;
@@
// write fops use offset
struct file_operations fops = {
... .write = write_f, ...
+ .llseek = default_llseek, /* write accesses f_pos */
};
// Use noop_llseek if neither read nor write accesses f_pos
///////////////////////////////////////////////////////////
@ fops4 depends on !fops1 && !fops2 && !fops3 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
identifier write_no_fpos.write_f;
@@
// write fops use offset
struct file_operations fops = {
...
.write = write_f,
.read = read_f,
...
+.llseek = noop_llseek, /* read and write both use no f_pos */
};
@ depends on has_write && !has_read && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write_no_fpos.write_f;
@@
struct file_operations fops = {
... .write = write_f, ...
+.llseek = noop_llseek, /* write uses no f_pos */
};
@ depends on has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
@@
struct file_operations fops = {
... .read = read_f, ...
+.llseek = noop_llseek, /* read uses no f_pos */
};
@ depends on !has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
@@
struct file_operations fops = {
...
+.llseek = noop_llseek, /* no read or write fn */
};
===== End semantic patch =====
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Julia Lawall <julia@diku.dk>
Cc: Christoph Hellwig <hch@infradead.org>
2010-08-15 16:52:59 +00:00
|
|
|
.llseek = no_llseek,
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct miscdevice uinput_misc = {
|
2005-11-20 05:51:22 +00:00
|
|
|
.fops = &uinput_fops,
|
|
|
|
.minor = UINPUT_MINOR,
|
|
|
|
.name = UINPUT_NAME,
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
2016-08-25 17:00:49 +00:00
|
|
|
module_misc_device(uinput_misc);
|
|
|
|
|
2010-08-19 16:52:28 +00:00
|
|
|
MODULE_ALIAS_MISCDEV(UINPUT_MINOR);
|
|
|
|
MODULE_ALIAS("devname:" UINPUT_NAME);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
|
|
|
|
MODULE_DESCRIPTION("User level driver support for input subsystem");
|
|
|
|
MODULE_LICENSE("GPL");
|
2006-07-19 05:41:09 +00:00
|
|
|
MODULE_VERSION("0.3");
|