Skip to content

Commit 052876f

Browse files
bentissdtor
authored andcommitted
Input: uinput - add new UINPUT_DEV_SETUP and UI_ABS_SETUP ioctl
This adds two new ioctls, UINPUT_DEV_SETUP and UI_ABS_SETUP, that replaces the old device setup method (by write()'ing "struct uinput_user_dev" to the node). The old method is not easily extendable and requires huge payloads. Furthermore, overloading write() without properly versioned objects is error-prone. Therefore, we introduce two new ioctls to replace the old method. These ioctls support all features of the old method, plus a "resolution" field for absinfo. Furthermore, it's properly forward-compatible to new ABS codes and a growing "struct input_absinfo" structure. UI_ABS_SETUP also allows user-space to skip unknown axes if not set. There is no need to copy the whole array temporarily into the kernel, but instead the caller issues several ioctl where we copy each value manually. Signed-off-by: David Herrmann <dh.herrmann@gmail.com> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Reviewed-by: David Herrmann <dh.herrmann@gmail.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
1 parent f01c5e6 commit 052876f

File tree

3 files changed

+168
-6
lines changed

3 files changed

+168
-6
lines changed

drivers/input/misc/uinput.c

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,78 @@ static int uinput_allocate_device(struct uinput_device *udev)
370370
return 0;
371371
}
372372

373-
static int uinput_setup_device(struct uinput_device *udev,
374-
const char __user *buffer, size_t count)
373+
static int uinput_dev_setup(struct uinput_device *udev,
374+
struct uinput_setup __user *arg)
375+
{
376+
struct uinput_setup setup;
377+
struct input_dev *dev;
378+
int retval;
379+
380+
if (udev->state == UIST_CREATED)
381+
return -EINVAL;
382+
383+
if (copy_from_user(&setup, arg, sizeof(setup)))
384+
return -EFAULT;
385+
386+
if (!setup.name[0])
387+
return -EINVAL;
388+
389+
dev = udev->dev;
390+
dev->id = setup.id;
391+
udev->ff_effects_max = setup.ff_effects_max;
392+
393+
kfree(dev->name);
394+
dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL);
395+
if (!dev->name)
396+
return -ENOMEM;
397+
398+
retval = uinput_validate_absbits(dev);
399+
if (retval < 0)
400+
return retval;
401+
402+
udev->state = UIST_SETUP_COMPLETE;
403+
return 0;
404+
}
405+
406+
static int uinput_abs_setup(struct uinput_device *udev,
407+
struct uinput_setup __user *arg, size_t size)
408+
{
409+
struct uinput_abs_setup setup = {};
410+
struct input_dev *dev;
411+
412+
if (size > sizeof(setup))
413+
return -E2BIG;
414+
415+
if (udev->state == UIST_CREATED)
416+
return -EINVAL;
417+
418+
if (copy_from_user(&setup, arg, size))
419+
return -EFAULT;
420+
421+
if (setup.code > ABS_MAX)
422+
return -ERANGE;
423+
424+
dev = udev->dev;
425+
426+
input_alloc_absinfo(dev);
427+
if (!dev->absinfo)
428+
return -ENOMEM;
429+
430+
set_bit(setup.code, dev->absbit);
431+
dev->absinfo[setup.code] = setup.absinfo;
432+
433+
/*
434+
* We restore the state to UIST_NEW_DEVICE because the user has to call
435+
* UI_DEV_SETUP in the last place before UI_DEV_CREATE to check the
436+
* validity of the absbits.
437+
*/
438+
udev->state = UIST_NEW_DEVICE;
439+
return 0;
440+
}
441+
442+
/* legacy setup via write() */
443+
static int uinput_setup_device_legacy(struct uinput_device *udev,
444+
const char __user *buffer, size_t count)
375445
{
376446
struct uinput_user_dev *user_dev;
377447
struct input_dev *dev;
@@ -474,7 +544,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer,
474544

475545
retval = udev->state == UIST_CREATED ?
476546
uinput_inject_events(udev, buffer, count) :
477-
uinput_setup_device(udev, buffer, count);
547+
uinput_setup_device_legacy(udev, buffer, count);
478548

479549
mutex_unlock(&udev->mutex);
480550

@@ -735,6 +805,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
735805
uinput_destroy_device(udev);
736806
goto out;
737807

808+
case UI_DEV_SETUP:
809+
retval = uinput_dev_setup(udev, p);
810+
goto out;
811+
812+
/* UI_ABS_SETUP is handled in the variable size ioctls */
813+
738814
case UI_SET_EVBIT:
739815
retval = uinput_set_bit(arg, evbit, EV_MAX);
740816
goto out;
@@ -879,6 +955,10 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
879955
name = dev_name(&udev->dev->dev);
880956
retval = uinput_str_to_user(p, name, size);
881957
goto out;
958+
959+
case UI_ABS_SETUP & ~IOCSIZE_MASK:
960+
retval = uinput_abs_setup(udev, p, size);
961+
goto out;
882962
}
883963

884964
retval = -EINVAL;

include/linux/uinput.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
2121
*
2222
* Changes/Revisions:
23+
* 0.5 08/13/2015 (David Herrmann <dh.herrmann@gmail.com> &
24+
* Benjamin Tissoires <benjamin.tissoires@redhat.com>)
25+
* - add UI_DEV_SETUP ioctl
26+
* - add UI_ABS_SETUP ioctl
27+
* - add UI_GET_VERSION ioctl
2328
* 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
2429
* - add UI_GET_SYSNAME ioctl
2530
* 0.3 24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)

include/uapi/linux/uinput.h

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
2121
*
2222
* Changes/Revisions:
23+
* 0.5 08/13/2015 (David Herrmann <dh.herrmann@gmail.com> &
24+
* Benjamin Tissoires <benjamin.tissoires@redhat.com>)
25+
* - add UI_DEV_SETUP ioctl
26+
* - add UI_ABS_SETUP ioctl
27+
* - add UI_GET_VERSION ioctl
2328
* 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
2429
* - add UI_GET_SYSNAME ioctl
2530
* 0.3 24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
@@ -37,8 +42,8 @@
3742
#include <linux/types.h>
3843
#include <linux/input.h>
3944

40-
#define UINPUT_VERSION 4
41-
45+
#define UINPUT_VERSION 5
46+
#define UINPUT_MAX_NAME_SIZE 80
4247

4348
struct uinput_ff_upload {
4449
__u32 request_id;
@@ -58,6 +63,79 @@ struct uinput_ff_erase {
5863
#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
5964
#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
6065

66+
struct uinput_setup {
67+
struct input_id id;
68+
char name[UINPUT_MAX_NAME_SIZE];
69+
__u32 ff_effects_max;
70+
};
71+
72+
/**
73+
* UI_DEV_SETUP - Set device parameters for setup
74+
*
75+
* This ioctl sets parameters for the input device to be created. It must be
76+
* issued *before* calling UI_DEV_CREATE or it will fail. This ioctl supersedes
77+
* the old "struct uinput_user_dev" method, which wrote this data via write().
78+
* To actually set the absolute axes, you also need to call the ioctl
79+
* UI_ABS_SETUP *before* calling this ioctl.
80+
*
81+
* This ioctl takes a "struct uinput_setup" object as argument. The fields of
82+
* this object are as follows:
83+
* id: See the description of "struct input_id". This field is
84+
* copied unchanged into the new device.
85+
* name: This is used unchanged as name for the new device.
86+
* ff_effects_max: This limits the maximum numbers of force-feedback effects.
87+
* See below for a description of FF with uinput.
88+
*
89+
* This ioctl can be called multiple times and will overwrite previous values.
90+
* If this ioctl fails with -EINVAL, you're recommended to use the old
91+
* "uinput_user_dev" method via write() as fallback, in case you run on an old
92+
* kernel that does not support this ioctl.
93+
*
94+
* This ioctl may fail with -EINVAL if it is not supported or if you passed
95+
* incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the
96+
* passed uinput_setup object cannot be read/written.
97+
* If this call fails, partial data may have already been applied to the
98+
* internal device.
99+
*/
100+
#define UI_DEV_SETUP _IOW(UINPUT_IOCTL_BASE, 3, struct uinput_setup)
101+
102+
struct uinput_abs_setup {
103+
__u16 code; /* axis code */
104+
/* __u16 filler; */
105+
struct input_absinfo absinfo;
106+
};
107+
108+
/**
109+
* UI_ABS_SETUP - Set absolute axis information for the device to setup
110+
*
111+
* This ioctl sets one absolute axis information for the input device to be
112+
* created. It must be issued *before* calling UI_DEV_SETUP and UI_DEV_CREATE
113+
* for every absolute axis the device exports.
114+
* This ioctl supersedes the old "struct uinput_user_dev" method, which wrote
115+
* part of this data and the content of UI_DEV_SETUP via write().
116+
*
117+
* This ioctl takes a "struct uinput_abs_setup" object as argument. The fields
118+
* of this object are as follows:
119+
* code: The corresponding input code associated with this axis
120+
* (ABS_X, ABS_Y, etc...)
121+
* absinfo: See "struct input_absinfo" for a description of this field.
122+
* This field is copied unchanged into the kernel for the
123+
* specified axis. If the axis is not enabled via
124+
* UI_SET_ABSBIT, this ioctl will enable it.
125+
*
126+
* This ioctl can be called multiple times and will overwrite previous values.
127+
* If this ioctl fails with -EINVAL, you're recommended to use the old
128+
* "uinput_user_dev" method via write() as fallback, in case you run on an old
129+
* kernel that does not support this ioctl.
130+
*
131+
* This ioctl may fail with -EINVAL if it is not supported or if you passed
132+
* incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the
133+
* passed uinput_setup object cannot be read/written.
134+
* If this call fails, partial data may have already been applied to the
135+
* internal device.
136+
*/
137+
#define UI_ABS_SETUP _IOW(UINPUT_IOCTL_BASE, 4, struct uinput_abs_setup)
138+
61139
#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int)
62140
#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int)
63141
#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int)
@@ -144,7 +222,6 @@ struct uinput_ff_erase {
144222
#define UI_FF_UPLOAD 1
145223
#define UI_FF_ERASE 2
146224

147-
#define UINPUT_MAX_NAME_SIZE 80
148225
struct uinput_user_dev {
149226
char name[UINPUT_MAX_NAME_SIZE];
150227
struct input_id id;

0 commit comments

Comments
 (0)