11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * User level driver support for input subsystem
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Heavily based on evdev.c by Vojtech Pavlik
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * Changes/Revisions:
10e3480a61SBenjamin Tissoires * 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
11e3480a61SBenjamin Tissoires * - add UI_GET_SYSNAME ioctl
12ff462551SAnssi Hannula * 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
13ff462551SAnssi Hannula * - updated ff support for the changes in kernel interface
14ff462551SAnssi Hannula * - added MODULE_VERSION
151da177e4SLinus Torvalds * 0.2 16/10/2004 (Micah Dowty <micah@navi.cx>)
161da177e4SLinus Torvalds * - added force feedback support
171da177e4SLinus Torvalds * - added UI_SET_PHYS
181da177e4SLinus Torvalds * 0.1 20/06/2002
191da177e4SLinus Torvalds * - first public version
201da177e4SLinus Torvalds */
21a11bc476SDmitry Torokhov #include <uapi/linux/uinput.h>
221da177e4SLinus Torvalds #include <linux/poll.h>
23a99bbaf5SAlexey Dobriyan #include <linux/sched.h>
241da177e4SLinus Torvalds #include <linux/slab.h>
251da177e4SLinus Torvalds #include <linux/module.h>
261da177e4SLinus Torvalds #include <linux/init.h>
271da177e4SLinus Torvalds #include <linux/fs.h>
281da177e4SLinus Torvalds #include <linux/miscdevice.h>
29d77651a2SDmitry Torokhov #include <linux/overflow.h>
3047c78e89SHenrik Rydberg #include <linux/input/mt.h>
312d56f3a3SPhilip Langdale #include "../input-compat.h"
321da177e4SLinus Torvalds
33a11bc476SDmitry Torokhov #define UINPUT_NAME "uinput"
34a11bc476SDmitry Torokhov #define UINPUT_BUFFER_SIZE 16
35a11bc476SDmitry Torokhov #define UINPUT_NUM_REQUESTS 16
36*3a2df602SBiswarup Pal #define UINPUT_TIMESTAMP_ALLOWED_OFFSET_SECS 10
37a11bc476SDmitry Torokhov
38a11bc476SDmitry Torokhov enum uinput_state { UIST_NEW_DEVICE, UIST_SETUP_COMPLETE, UIST_CREATED };
39a11bc476SDmitry Torokhov
40a11bc476SDmitry Torokhov struct uinput_request {
41a11bc476SDmitry Torokhov unsigned int id;
42a11bc476SDmitry Torokhov unsigned int code; /* UI_FF_UPLOAD, UI_FF_ERASE */
43a11bc476SDmitry Torokhov
44a11bc476SDmitry Torokhov int retval;
45a11bc476SDmitry Torokhov struct completion done;
46a11bc476SDmitry Torokhov
47a11bc476SDmitry Torokhov union {
48a11bc476SDmitry Torokhov unsigned int effect_id;
49a11bc476SDmitry Torokhov struct {
50a11bc476SDmitry Torokhov struct ff_effect *effect;
51a11bc476SDmitry Torokhov struct ff_effect *old;
52a11bc476SDmitry Torokhov } upload;
53a11bc476SDmitry Torokhov } u;
54a11bc476SDmitry Torokhov };
55a11bc476SDmitry Torokhov
56a11bc476SDmitry Torokhov struct uinput_device {
57a11bc476SDmitry Torokhov struct input_dev *dev;
58a11bc476SDmitry Torokhov struct mutex mutex;
59a11bc476SDmitry Torokhov enum uinput_state state;
60a11bc476SDmitry Torokhov wait_queue_head_t waitq;
61a11bc476SDmitry Torokhov unsigned char ready;
62a11bc476SDmitry Torokhov unsigned char head;
63a11bc476SDmitry Torokhov unsigned char tail;
64a11bc476SDmitry Torokhov struct input_event buff[UINPUT_BUFFER_SIZE];
65a11bc476SDmitry Torokhov unsigned int ff_effects_max;
66a11bc476SDmitry Torokhov
67a11bc476SDmitry Torokhov struct uinput_request *requests[UINPUT_NUM_REQUESTS];
68a11bc476SDmitry Torokhov wait_queue_head_t requests_waitq;
69a11bc476SDmitry Torokhov spinlock_t requests_lock;
70a11bc476SDmitry Torokhov };
71a11bc476SDmitry Torokhov
uinput_dev_event(struct input_dev * dev,unsigned int type,unsigned int code,int value)7254ce165eSDmitry Torokhov static int uinput_dev_event(struct input_dev *dev,
7354ce165eSDmitry Torokhov unsigned int type, unsigned int code, int value)
741da177e4SLinus Torvalds {
75373f9713SDmitry Torokhov struct uinput_device *udev = input_get_drvdata(dev);
76b3495cecSDeepa Dinamani struct timespec64 ts;
771da177e4SLinus Torvalds
78b3495cecSDeepa Dinamani ktime_get_ts64(&ts);
79f729a1b0SArnd Bergmann
80f729a1b0SArnd Bergmann udev->buff[udev->head] = (struct input_event) {
81f729a1b0SArnd Bergmann .input_event_sec = ts.tv_sec,
82f729a1b0SArnd Bergmann .input_event_usec = ts.tv_nsec / NSEC_PER_USEC,
83f729a1b0SArnd Bergmann .type = type,
84f729a1b0SArnd Bergmann .code = code,
85f729a1b0SArnd Bergmann .value = value,
86f729a1b0SArnd Bergmann };
87f729a1b0SArnd Bergmann
881da177e4SLinus Torvalds udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
891da177e4SLinus Torvalds
901da177e4SLinus Torvalds wake_up_interruptible(&udev->waitq);
911da177e4SLinus Torvalds
921da177e4SLinus Torvalds return 0;
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds
9505cebd38SAristeu Sergio Rozanski Filho /* Atomically allocate an ID for the given request. Returns 0 on success. */
uinput_request_alloc_id(struct uinput_device * udev,struct uinput_request * request)9600ce756cSDmitry Torokhov static bool uinput_request_alloc_id(struct uinput_device *udev,
9700ce756cSDmitry Torokhov struct uinput_request *request)
981da177e4SLinus Torvalds {
99c5b3533aSDmitry Torokhov unsigned int id;
10000ce756cSDmitry Torokhov bool reserved = false;
1011da177e4SLinus Torvalds
1020048e603SDmitry Torokhov spin_lock(&udev->requests_lock);
103152c12f5SDmitry Torokhov
10405cebd38SAristeu Sergio Rozanski Filho for (id = 0; id < UINPUT_NUM_REQUESTS; id++) {
1051da177e4SLinus Torvalds if (!udev->requests[id]) {
1061da177e4SLinus Torvalds request->id = id;
1070048e603SDmitry Torokhov udev->requests[id] = request;
10800ce756cSDmitry Torokhov reserved = true;
109152c12f5SDmitry Torokhov break;
1101da177e4SLinus Torvalds }
11105cebd38SAristeu Sergio Rozanski Filho }
112152c12f5SDmitry Torokhov
1130048e603SDmitry Torokhov spin_unlock(&udev->requests_lock);
11400ce756cSDmitry Torokhov return reserved;
1151da177e4SLinus Torvalds }
1161da177e4SLinus Torvalds
uinput_request_find(struct uinput_device * udev,unsigned int id)117c5b3533aSDmitry Torokhov static struct uinput_request *uinput_request_find(struct uinput_device *udev,
118c5b3533aSDmitry Torokhov unsigned int id)
1191da177e4SLinus Torvalds {
1201da177e4SLinus Torvalds /* Find an input request, by ID. Returns NULL if the ID isn't valid. */
121c5b3533aSDmitry Torokhov if (id >= UINPUT_NUM_REQUESTS)
1221da177e4SLinus Torvalds return NULL;
1232d56f3a3SPhilip Langdale
1241da177e4SLinus Torvalds return udev->requests[id];
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds
uinput_request_reserve_slot(struct uinput_device * udev,struct uinput_request * request)12700ce756cSDmitry Torokhov static int uinput_request_reserve_slot(struct uinput_device *udev,
12800ce756cSDmitry Torokhov struct uinput_request *request)
1291da177e4SLinus Torvalds {
1300048e603SDmitry Torokhov /* Allocate slot. If none are available right away, wait. */
1310048e603SDmitry Torokhov return wait_event_interruptible(udev->requests_waitq,
13200ce756cSDmitry Torokhov uinput_request_alloc_id(udev, request));
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds
uinput_request_release_slot(struct uinput_device * udev,unsigned int id)1356b4877c7SDmitry Torokhov static void uinput_request_release_slot(struct uinput_device *udev,
1366b4877c7SDmitry Torokhov unsigned int id)
1371da177e4SLinus Torvalds {
1380048e603SDmitry Torokhov /* Mark slot as available */
1396b4877c7SDmitry Torokhov spin_lock(&udev->requests_lock);
1406b4877c7SDmitry Torokhov udev->requests[id] = NULL;
1416b4877c7SDmitry Torokhov spin_unlock(&udev->requests_lock);
142e7507ed9SDmitry Torokhov
1436b4877c7SDmitry Torokhov wake_up(&udev->requests_waitq);
1440048e603SDmitry Torokhov }
1450048e603SDmitry Torokhov
uinput_request_send(struct uinput_device * udev,struct uinput_request * request)14600ce756cSDmitry Torokhov static int uinput_request_send(struct uinput_device *udev,
14700ce756cSDmitry Torokhov struct uinput_request *request)
1480048e603SDmitry Torokhov {
14905cebd38SAristeu Sergio Rozanski Filho int retval;
1501da177e4SLinus Torvalds
15105cebd38SAristeu Sergio Rozanski Filho retval = mutex_lock_interruptible(&udev->mutex);
15205cebd38SAristeu Sergio Rozanski Filho if (retval)
15305cebd38SAristeu Sergio Rozanski Filho return retval;
15405cebd38SAristeu Sergio Rozanski Filho
15505cebd38SAristeu Sergio Rozanski Filho if (udev->state != UIST_CREATED) {
15605cebd38SAristeu Sergio Rozanski Filho retval = -ENODEV;
15705cebd38SAristeu Sergio Rozanski Filho goto out;
15805cebd38SAristeu Sergio Rozanski Filho }
15905cebd38SAristeu Sergio Rozanski Filho
16000ce756cSDmitry Torokhov init_completion(&request->done);
16100ce756cSDmitry Torokhov
16200ce756cSDmitry Torokhov /*
16300ce756cSDmitry Torokhov * Tell our userspace application about this new request
16400ce756cSDmitry Torokhov * by queueing an input event.
16500ce756cSDmitry Torokhov */
16605cebd38SAristeu Sergio Rozanski Filho uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id);
16705cebd38SAristeu Sergio Rozanski Filho
16805cebd38SAristeu Sergio Rozanski Filho out:
16905cebd38SAristeu Sergio Rozanski Filho mutex_unlock(&udev->mutex);
17005cebd38SAristeu Sergio Rozanski Filho return retval;
17105cebd38SAristeu Sergio Rozanski Filho }
17205cebd38SAristeu Sergio Rozanski Filho
uinput_request_submit(struct uinput_device * udev,struct uinput_request * request)17300ce756cSDmitry Torokhov static int uinput_request_submit(struct uinput_device *udev,
17400ce756cSDmitry Torokhov struct uinput_request *request)
17500ce756cSDmitry Torokhov {
1766b4877c7SDmitry Torokhov int retval;
17700ce756cSDmitry Torokhov
1786b4877c7SDmitry Torokhov retval = uinput_request_reserve_slot(udev, request);
1796b4877c7SDmitry Torokhov if (retval)
1806b4877c7SDmitry Torokhov return retval;
18100ce756cSDmitry Torokhov
1826b4877c7SDmitry Torokhov retval = uinput_request_send(udev, request);
1836b4877c7SDmitry Torokhov if (retval)
1846b4877c7SDmitry Torokhov goto out;
18500ce756cSDmitry Torokhov
1868e009118SDmitry Torokhov if (!wait_for_completion_timeout(&request->done, 30 * HZ)) {
1878e009118SDmitry Torokhov retval = -ETIMEDOUT;
1888e009118SDmitry Torokhov goto out;
1898e009118SDmitry Torokhov }
1908e009118SDmitry Torokhov
1916b4877c7SDmitry Torokhov retval = request->retval;
1926b4877c7SDmitry Torokhov
1936b4877c7SDmitry Torokhov out:
1946b4877c7SDmitry Torokhov uinput_request_release_slot(udev, request->id);
1956b4877c7SDmitry Torokhov return retval;
19600ce756cSDmitry Torokhov }
19700ce756cSDmitry Torokhov
19805cebd38SAristeu Sergio Rozanski Filho /*
19954ce165eSDmitry Torokhov * Fail all outstanding requests so handlers don't wait for the userspace
20005cebd38SAristeu Sergio Rozanski Filho * to finish processing them.
20105cebd38SAristeu Sergio Rozanski Filho */
uinput_flush_requests(struct uinput_device * udev)20205cebd38SAristeu Sergio Rozanski Filho static void uinput_flush_requests(struct uinput_device *udev)
20305cebd38SAristeu Sergio Rozanski Filho {
20405cebd38SAristeu Sergio Rozanski Filho struct uinput_request *request;
20505cebd38SAristeu Sergio Rozanski Filho int i;
20605cebd38SAristeu Sergio Rozanski Filho
20705cebd38SAristeu Sergio Rozanski Filho spin_lock(&udev->requests_lock);
20805cebd38SAristeu Sergio Rozanski Filho
20905cebd38SAristeu Sergio Rozanski Filho for (i = 0; i < UINPUT_NUM_REQUESTS; i++) {
21005cebd38SAristeu Sergio Rozanski Filho request = udev->requests[i];
21105cebd38SAristeu Sergio Rozanski Filho if (request) {
21205cebd38SAristeu Sergio Rozanski Filho request->retval = -ENODEV;
2136b4877c7SDmitry Torokhov complete(&request->done);
21405cebd38SAristeu Sergio Rozanski Filho }
21505cebd38SAristeu Sergio Rozanski Filho }
21605cebd38SAristeu Sergio Rozanski Filho
21705cebd38SAristeu Sergio Rozanski Filho spin_unlock(&udev->requests_lock);
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds
uinput_dev_set_gain(struct input_dev * dev,u16 gain)220ff462551SAnssi Hannula static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
221ff462551SAnssi Hannula {
222ff462551SAnssi Hannula uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
223ff462551SAnssi Hannula }
224ff462551SAnssi Hannula
uinput_dev_set_autocenter(struct input_dev * dev,u16 magnitude)225ff462551SAnssi Hannula static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude)
226ff462551SAnssi Hannula {
227ff462551SAnssi Hannula uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);
228ff462551SAnssi Hannula }
229ff462551SAnssi Hannula
uinput_dev_playback(struct input_dev * dev,int effect_id,int value)230ff462551SAnssi Hannula static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
231ff462551SAnssi Hannula {
232ff462551SAnssi Hannula return uinput_dev_event(dev, EV_FF, effect_id, value);
233ff462551SAnssi Hannula }
234ff462551SAnssi Hannula
uinput_dev_upload_effect(struct input_dev * dev,struct ff_effect * effect,struct ff_effect * old)23554ce165eSDmitry Torokhov static int uinput_dev_upload_effect(struct input_dev *dev,
23654ce165eSDmitry Torokhov struct ff_effect *effect,
23754ce165eSDmitry Torokhov struct ff_effect *old)
2381da177e4SLinus Torvalds {
23905cebd38SAristeu Sergio Rozanski Filho struct uinput_device *udev = input_get_drvdata(dev);
2401da177e4SLinus Torvalds struct uinput_request request;
2411da177e4SLinus Torvalds
2422d56f3a3SPhilip Langdale /*
2432d56f3a3SPhilip Langdale * uinput driver does not currently support periodic effects with
2442d56f3a3SPhilip Langdale * custom waveform since it does not have a way to pass buffer of
2452d56f3a3SPhilip Langdale * samples (custom_data) to userspace. If ever there is a device
2462d56f3a3SPhilip Langdale * supporting custom waveforms we would need to define an additional
2472d56f3a3SPhilip Langdale * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out.
2482d56f3a3SPhilip Langdale */
2492d56f3a3SPhilip Langdale if (effect->type == FF_PERIODIC &&
2502d56f3a3SPhilip Langdale effect->u.periodic.waveform == FF_CUSTOM)
2512d56f3a3SPhilip Langdale return -EINVAL;
2522d56f3a3SPhilip Langdale
2530048e603SDmitry Torokhov request.code = UI_FF_UPLOAD;
254ff462551SAnssi Hannula request.u.upload.effect = effect;
255ff462551SAnssi Hannula request.u.upload.old = old;
2560048e603SDmitry Torokhov
25700ce756cSDmitry Torokhov return uinput_request_submit(udev, &request);
2581da177e4SLinus Torvalds }
2591da177e4SLinus Torvalds
uinput_dev_erase_effect(struct input_dev * dev,int effect_id)2601da177e4SLinus Torvalds static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
2611da177e4SLinus Torvalds {
26205cebd38SAristeu Sergio Rozanski Filho struct uinput_device *udev = input_get_drvdata(dev);
2631da177e4SLinus Torvalds struct uinput_request request;
2641da177e4SLinus Torvalds
2651da177e4SLinus Torvalds if (!test_bit(EV_FF, dev->evbit))
2661da177e4SLinus Torvalds return -ENOSYS;
2671da177e4SLinus Torvalds
2680048e603SDmitry Torokhov request.code = UI_FF_ERASE;
2691da177e4SLinus Torvalds request.u.effect_id = effect_id;
2700048e603SDmitry Torokhov
27100ce756cSDmitry Torokhov return uinput_request_submit(udev, &request);
2721da177e4SLinus Torvalds }
2731da177e4SLinus Torvalds
uinput_dev_flush(struct input_dev * dev,struct file * file)274e8b95728SDmitry Torokhov static int uinput_dev_flush(struct input_dev *dev, struct file *file)
275e8b95728SDmitry Torokhov {
276e8b95728SDmitry Torokhov /*
277e8b95728SDmitry Torokhov * If we are called with file == NULL that means we are tearing
278e8b95728SDmitry Torokhov * down the device, and therefore we can not handle FF erase
279e8b95728SDmitry Torokhov * requests: either we are handling UI_DEV_DESTROY (and holding
280e8b95728SDmitry Torokhov * the udev->mutex), or the file descriptor is closed and there is
281e8b95728SDmitry Torokhov * nobody on the other side anymore.
282e8b95728SDmitry Torokhov */
283e8b95728SDmitry Torokhov return file ? input_ff_flush(dev, file) : 0;
284e8b95728SDmitry Torokhov }
285e8b95728SDmitry Torokhov
uinput_destroy_device(struct uinput_device * udev)28629506415SDmitry Torokhov static void uinput_destroy_device(struct uinput_device *udev)
28729506415SDmitry Torokhov {
28829506415SDmitry Torokhov const char *name, *phys;
28905cebd38SAristeu Sergio Rozanski Filho struct input_dev *dev = udev->dev;
29005cebd38SAristeu Sergio Rozanski Filho enum uinput_state old_state = udev->state;
29129506415SDmitry Torokhov
29205cebd38SAristeu Sergio Rozanski Filho udev->state = UIST_NEW_DEVICE;
29305cebd38SAristeu Sergio Rozanski Filho
29405cebd38SAristeu Sergio Rozanski Filho if (dev) {
29505cebd38SAristeu Sergio Rozanski Filho name = dev->name;
29605cebd38SAristeu Sergio Rozanski Filho phys = dev->phys;
29705cebd38SAristeu Sergio Rozanski Filho if (old_state == UIST_CREATED) {
29805cebd38SAristeu Sergio Rozanski Filho uinput_flush_requests(udev);
29905cebd38SAristeu Sergio Rozanski Filho input_unregister_device(dev);
30005cebd38SAristeu Sergio Rozanski Filho } else {
30105cebd38SAristeu Sergio Rozanski Filho input_free_device(dev);
30205cebd38SAristeu Sergio Rozanski Filho }
30329506415SDmitry Torokhov kfree(name);
30429506415SDmitry Torokhov kfree(phys);
30529506415SDmitry Torokhov udev->dev = NULL;
30629506415SDmitry Torokhov }
30729506415SDmitry Torokhov }
30829506415SDmitry Torokhov
uinput_create_device(struct uinput_device * udev)3091da177e4SLinus Torvalds static int uinput_create_device(struct uinput_device *udev)
3101da177e4SLinus Torvalds {
311ff462551SAnssi Hannula struct input_dev *dev = udev->dev;
312fbae10dbSDavid Herrmann int error, nslot;
31329506415SDmitry Torokhov
31429506415SDmitry Torokhov if (udev->state != UIST_SETUP_COMPLETE) {
3151da177e4SLinus Torvalds printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
3161da177e4SLinus Torvalds return -EINVAL;
3171da177e4SLinus Torvalds }
3181da177e4SLinus Torvalds
319601bbbe0SDmitry Torokhov if (test_bit(EV_ABS, dev->evbit)) {
320601bbbe0SDmitry Torokhov input_alloc_absinfo(dev);
321601bbbe0SDmitry Torokhov if (!dev->absinfo) {
322601bbbe0SDmitry Torokhov error = -EINVAL;
323601bbbe0SDmitry Torokhov goto fail1;
324601bbbe0SDmitry Torokhov }
325601bbbe0SDmitry Torokhov
326fbae10dbSDavid Herrmann if (test_bit(ABS_MT_SLOT, dev->absbit)) {
327fbae10dbSDavid Herrmann nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
328fbae10dbSDavid Herrmann error = input_mt_init_slots(dev, nslot, 0);
329fbae10dbSDavid Herrmann if (error)
330fbae10dbSDavid Herrmann goto fail1;
331fbae10dbSDavid Herrmann } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
332fbae10dbSDavid Herrmann input_set_events_per_packet(dev, 60);
333fbae10dbSDavid Herrmann }
334601bbbe0SDmitry Torokhov }
335fbae10dbSDavid Herrmann
336daf6cd0cSElias Vanderstuyft if (test_bit(EV_FF, dev->evbit) && !udev->ff_effects_max) {
337daf6cd0cSElias Vanderstuyft printk(KERN_DEBUG "%s: ff_effects_max should be non-zero when FF_BIT is set\n",
338daf6cd0cSElias Vanderstuyft UINPUT_NAME);
339daf6cd0cSElias Vanderstuyft error = -EINVAL;
340daf6cd0cSElias Vanderstuyft goto fail1;
341daf6cd0cSElias Vanderstuyft }
342daf6cd0cSElias Vanderstuyft
343ff462551SAnssi Hannula if (udev->ff_effects_max) {
344ff462551SAnssi Hannula error = input_ff_create(dev, udev->ff_effects_max);
345ff462551SAnssi Hannula if (error)
346ff462551SAnssi Hannula goto fail1;
347ff462551SAnssi Hannula
348ff462551SAnssi Hannula dev->ff->upload = uinput_dev_upload_effect;
349ff462551SAnssi Hannula dev->ff->erase = uinput_dev_erase_effect;
350ff462551SAnssi Hannula dev->ff->playback = uinput_dev_playback;
351ff462551SAnssi Hannula dev->ff->set_gain = uinput_dev_set_gain;
352ff462551SAnssi Hannula dev->ff->set_autocenter = uinput_dev_set_autocenter;
353e8b95728SDmitry Torokhov /*
354e8b95728SDmitry Torokhov * The standard input_ff_flush() implementation does
355e8b95728SDmitry Torokhov * not quite work for uinput as we can't reasonably
356e8b95728SDmitry Torokhov * handle FF requests during device teardown.
357e8b95728SDmitry Torokhov */
358e8b95728SDmitry Torokhov dev->flush = uinput_dev_flush;
3591da177e4SLinus Torvalds }
3601da177e4SLinus Torvalds
36104ce40a6SDmitry Torokhov dev->event = uinput_dev_event;
36204ce40a6SDmitry Torokhov
36304ce40a6SDmitry Torokhov input_set_drvdata(udev->dev, udev);
36404ce40a6SDmitry Torokhov
365ff462551SAnssi Hannula error = input_register_device(udev->dev);
366ff462551SAnssi Hannula if (error)
367ff462551SAnssi Hannula goto fail2;
368ff462551SAnssi Hannula
36929506415SDmitry Torokhov udev->state = UIST_CREATED;
3701da177e4SLinus Torvalds
3711da177e4SLinus Torvalds return 0;
372ff462551SAnssi Hannula
373ff462551SAnssi Hannula fail2: input_ff_destroy(dev);
374ff462551SAnssi Hannula fail1: uinput_destroy_device(udev);
375ff462551SAnssi Hannula return error;
3761da177e4SLinus Torvalds }
3771da177e4SLinus Torvalds
uinput_open(struct inode * inode,struct file * file)3781da177e4SLinus Torvalds static int uinput_open(struct inode *inode, struct file *file)
3791da177e4SLinus Torvalds {
3801da177e4SLinus Torvalds struct uinput_device *newdev;
3811da177e4SLinus Torvalds
38229506415SDmitry Torokhov newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
3831da177e4SLinus Torvalds if (!newdev)
38429506415SDmitry Torokhov return -ENOMEM;
38529506415SDmitry Torokhov
386221979aaSDmitry Torokhov mutex_init(&newdev->mutex);
3870048e603SDmitry Torokhov spin_lock_init(&newdev->requests_lock);
3881da177e4SLinus Torvalds init_waitqueue_head(&newdev->requests_waitq);
38929506415SDmitry Torokhov init_waitqueue_head(&newdev->waitq);
39029506415SDmitry Torokhov newdev->state = UIST_NEW_DEVICE;
3911da177e4SLinus Torvalds
3921da177e4SLinus Torvalds file->private_data = newdev;
393c5bf68feSKirill Smelkov stream_open(inode, file);
3941da177e4SLinus Torvalds
3951da177e4SLinus Torvalds return 0;
3961da177e4SLinus Torvalds }
3971da177e4SLinus Torvalds
uinput_validate_absinfo(struct input_dev * dev,unsigned int code,const struct input_absinfo * abs)398fbae10dbSDavid Herrmann static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code,
399fbae10dbSDavid Herrmann const struct input_absinfo *abs)
400fbae10dbSDavid Herrmann {
401d77651a2SDmitry Torokhov int min, max, range;
402fbae10dbSDavid Herrmann
403fbae10dbSDavid Herrmann min = abs->minimum;
404fbae10dbSDavid Herrmann max = abs->maximum;
405fbae10dbSDavid Herrmann
4064fef1250SPeter Hutterer if ((min != 0 || max != 0) && max < min) {
407fbae10dbSDavid Herrmann printk(KERN_DEBUG
408fbae10dbSDavid Herrmann "%s: invalid abs[%02x] min:%d max:%d\n",
409fbae10dbSDavid Herrmann UINPUT_NAME, code, min, max);
410fbae10dbSDavid Herrmann return -EINVAL;
411fbae10dbSDavid Herrmann }
412fbae10dbSDavid Herrmann
413d77651a2SDmitry Torokhov if (!check_sub_overflow(max, min, &range) && abs->flat > range) {
414fbae10dbSDavid Herrmann printk(KERN_DEBUG
415fbae10dbSDavid Herrmann "%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n",
416fbae10dbSDavid Herrmann UINPUT_NAME, code, abs->flat, min, max);
417fbae10dbSDavid Herrmann return -EINVAL;
418fbae10dbSDavid Herrmann }
419fbae10dbSDavid Herrmann
420fbae10dbSDavid Herrmann return 0;
421fbae10dbSDavid Herrmann }
422fbae10dbSDavid Herrmann
uinput_validate_absbits(struct input_dev * dev)4231da177e4SLinus Torvalds static int uinput_validate_absbits(struct input_dev *dev)
4241da177e4SLinus Torvalds {
4251da177e4SLinus Torvalds unsigned int cnt;
426fbae10dbSDavid Herrmann int error;
427bcb898e5SDavid Herrmann
428bcb898e5SDavid Herrmann if (!test_bit(EV_ABS, dev->evbit))
429bcb898e5SDavid Herrmann return 0;
430bcb898e5SDavid Herrmann
431bcb898e5SDavid Herrmann /*
432bcb898e5SDavid Herrmann * Check if absmin/absmax/absfuzz/absflat are sane.
433bcb898e5SDavid Herrmann */
4341da177e4SLinus Torvalds
435b6d30968SAnshul Garg for_each_set_bit(cnt, dev->absbit, ABS_CNT) {
436fbae10dbSDavid Herrmann if (!dev->absinfo)
437bcb898e5SDavid Herrmann return -EINVAL;
4381da177e4SLinus Torvalds
439fbae10dbSDavid Herrmann error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]);
440fbae10dbSDavid Herrmann if (error)
441fbae10dbSDavid Herrmann return error;
442bcb898e5SDavid Herrmann }
443bcb898e5SDavid Herrmann
444bcb898e5SDavid Herrmann return 0;
4451da177e4SLinus Torvalds }
4461da177e4SLinus Torvalds
uinput_dev_setup(struct uinput_device * udev,struct uinput_setup __user * arg)447052876f8SBenjamin Tissoires static int uinput_dev_setup(struct uinput_device *udev,
448052876f8SBenjamin Tissoires struct uinput_setup __user *arg)
449052876f8SBenjamin Tissoires {
450052876f8SBenjamin Tissoires struct uinput_setup setup;
451052876f8SBenjamin Tissoires struct input_dev *dev;
452052876f8SBenjamin Tissoires
453052876f8SBenjamin Tissoires if (udev->state == UIST_CREATED)
454052876f8SBenjamin Tissoires return -EINVAL;
455052876f8SBenjamin Tissoires
456052876f8SBenjamin Tissoires if (copy_from_user(&setup, arg, sizeof(setup)))
457052876f8SBenjamin Tissoires return -EFAULT;
458052876f8SBenjamin Tissoires
459052876f8SBenjamin Tissoires if (!setup.name[0])
460052876f8SBenjamin Tissoires return -EINVAL;
461052876f8SBenjamin Tissoires
462052876f8SBenjamin Tissoires dev = udev->dev;
463052876f8SBenjamin Tissoires dev->id = setup.id;
464052876f8SBenjamin Tissoires udev->ff_effects_max = setup.ff_effects_max;
465052876f8SBenjamin Tissoires
466052876f8SBenjamin Tissoires kfree(dev->name);
467052876f8SBenjamin Tissoires dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL);
468052876f8SBenjamin Tissoires if (!dev->name)
469052876f8SBenjamin Tissoires return -ENOMEM;
470052876f8SBenjamin Tissoires
471052876f8SBenjamin Tissoires udev->state = UIST_SETUP_COMPLETE;
472052876f8SBenjamin Tissoires return 0;
473052876f8SBenjamin Tissoires }
474052876f8SBenjamin Tissoires
uinput_abs_setup(struct uinput_device * udev,struct uinput_setup __user * arg,size_t size)475052876f8SBenjamin Tissoires static int uinput_abs_setup(struct uinput_device *udev,
476052876f8SBenjamin Tissoires struct uinput_setup __user *arg, size_t size)
477052876f8SBenjamin Tissoires {
478052876f8SBenjamin Tissoires struct uinput_abs_setup setup = {};
479052876f8SBenjamin Tissoires struct input_dev *dev;
480fbae10dbSDavid Herrmann int error;
481052876f8SBenjamin Tissoires
482052876f8SBenjamin Tissoires if (size > sizeof(setup))
483052876f8SBenjamin Tissoires return -E2BIG;
484052876f8SBenjamin Tissoires
485052876f8SBenjamin Tissoires if (udev->state == UIST_CREATED)
486052876f8SBenjamin Tissoires return -EINVAL;
487052876f8SBenjamin Tissoires
488052876f8SBenjamin Tissoires if (copy_from_user(&setup, arg, size))
489052876f8SBenjamin Tissoires return -EFAULT;
490052876f8SBenjamin Tissoires
491052876f8SBenjamin Tissoires if (setup.code > ABS_MAX)
492052876f8SBenjamin Tissoires return -ERANGE;
493052876f8SBenjamin Tissoires
494052876f8SBenjamin Tissoires dev = udev->dev;
495052876f8SBenjamin Tissoires
496fbae10dbSDavid Herrmann error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo);
497fbae10dbSDavid Herrmann if (error)
498fbae10dbSDavid Herrmann return error;
499fbae10dbSDavid Herrmann
500052876f8SBenjamin Tissoires input_alloc_absinfo(dev);
501052876f8SBenjamin Tissoires if (!dev->absinfo)
502052876f8SBenjamin Tissoires return -ENOMEM;
503052876f8SBenjamin Tissoires
504052876f8SBenjamin Tissoires set_bit(setup.code, dev->absbit);
505052876f8SBenjamin Tissoires dev->absinfo[setup.code] = setup.absinfo;
506052876f8SBenjamin Tissoires return 0;
507052876f8SBenjamin Tissoires }
508052876f8SBenjamin Tissoires
509052876f8SBenjamin Tissoires /* legacy setup via write() */
uinput_setup_device_legacy(struct uinput_device * udev,const char __user * buffer,size_t count)510052876f8SBenjamin Tissoires static int uinput_setup_device_legacy(struct uinput_device *udev,
51154ce165eSDmitry Torokhov const char __user *buffer, size_t count)
5121da177e4SLinus Torvalds {
5131da177e4SLinus Torvalds struct uinput_user_dev *user_dev;
5141da177e4SLinus Torvalds struct input_dev *dev;
5155d9d6e91SDavid Herrmann int i;
516152c12f5SDmitry Torokhov int retval;
5171da177e4SLinus Torvalds
51829506415SDmitry Torokhov if (count != sizeof(struct uinput_user_dev))
51929506415SDmitry Torokhov return -EINVAL;
5201da177e4SLinus Torvalds
52129506415SDmitry Torokhov if (!udev->dev) {
52204ce40a6SDmitry Torokhov udev->dev = input_allocate_device();
52304ce40a6SDmitry Torokhov if (!udev->dev)
52404ce40a6SDmitry Torokhov return -ENOMEM;
52529506415SDmitry Torokhov }
52629506415SDmitry Torokhov
5271da177e4SLinus Torvalds dev = udev->dev;
5281da177e4SLinus Torvalds
5294dfcc271SDmitry Torokhov user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));
530163d2770SDan Carpenter if (IS_ERR(user_dev))
5314dfcc271SDmitry Torokhov return PTR_ERR(user_dev);
5321da177e4SLinus Torvalds
533ff462551SAnssi Hannula udev->ff_effects_max = user_dev->ff_effects_max;
534ff462551SAnssi Hannula
5355d9d6e91SDavid Herrmann /* Ensure name is filled in */
5365d9d6e91SDavid Herrmann if (!user_dev->name[0]) {
53729506415SDmitry Torokhov retval = -EINVAL;
53829506415SDmitry Torokhov goto exit;
53929506415SDmitry Torokhov }
54029506415SDmitry Torokhov
54129506415SDmitry Torokhov kfree(dev->name);
5425d9d6e91SDavid Herrmann dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE,
5435d9d6e91SDavid Herrmann GFP_KERNEL);
5445d9d6e91SDavid Herrmann if (!dev->name) {
5451da177e4SLinus Torvalds retval = -ENOMEM;
5461da177e4SLinus Torvalds goto exit;
5471da177e4SLinus Torvalds }
5481da177e4SLinus Torvalds
5491da177e4SLinus Torvalds dev->id.bustype = user_dev->id.bustype;
5501da177e4SLinus Torvalds dev->id.vendor = user_dev->id.vendor;
5511da177e4SLinus Torvalds dev->id.product = user_dev->id.product;
5521da177e4SLinus Torvalds dev->id.version = user_dev->id.version;
5531da177e4SLinus Torvalds
55472d47362SDmitry Torokhov for (i = 0; i < ABS_CNT; i++) {
555987a6c02SDaniel Mack input_abs_set_max(dev, i, user_dev->absmax[i]);
556987a6c02SDaniel Mack input_abs_set_min(dev, i, user_dev->absmin[i]);
557987a6c02SDaniel Mack input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);
558987a6c02SDaniel Mack input_abs_set_flat(dev, i, user_dev->absflat[i]);
559987a6c02SDaniel Mack }
5601da177e4SLinus Torvalds
56129506415SDmitry Torokhov retval = uinput_validate_absbits(dev);
56229506415SDmitry Torokhov if (retval < 0)
56329506415SDmitry Torokhov goto exit;
56429506415SDmitry Torokhov
56529506415SDmitry Torokhov udev->state = UIST_SETUP_COMPLETE;
56629506415SDmitry Torokhov retval = count;
5671da177e4SLinus Torvalds
5681da177e4SLinus Torvalds exit:
5691da177e4SLinus Torvalds kfree(user_dev);
5701da177e4SLinus Torvalds return retval;
5711da177e4SLinus Torvalds }
5721da177e4SLinus Torvalds
573*3a2df602SBiswarup Pal /*
574*3a2df602SBiswarup Pal * Returns true if the given timestamp is valid (i.e., if all the following
575*3a2df602SBiswarup Pal * conditions are satisfied), false otherwise.
576*3a2df602SBiswarup Pal * 1) given timestamp is positive
577*3a2df602SBiswarup Pal * 2) it's within the allowed offset before the current time
578*3a2df602SBiswarup Pal * 3) it's not in the future
579*3a2df602SBiswarup Pal */
is_valid_timestamp(const ktime_t timestamp)580*3a2df602SBiswarup Pal static bool is_valid_timestamp(const ktime_t timestamp)
581*3a2df602SBiswarup Pal {
582*3a2df602SBiswarup Pal ktime_t zero_time;
583*3a2df602SBiswarup Pal ktime_t current_time;
584*3a2df602SBiswarup Pal ktime_t min_time;
585*3a2df602SBiswarup Pal ktime_t offset;
586*3a2df602SBiswarup Pal
587*3a2df602SBiswarup Pal zero_time = ktime_set(0, 0);
588*3a2df602SBiswarup Pal if (ktime_compare(zero_time, timestamp) >= 0)
589*3a2df602SBiswarup Pal return false;
590*3a2df602SBiswarup Pal
591*3a2df602SBiswarup Pal current_time = ktime_get();
592*3a2df602SBiswarup Pal offset = ktime_set(UINPUT_TIMESTAMP_ALLOWED_OFFSET_SECS, 0);
593*3a2df602SBiswarup Pal min_time = ktime_sub(current_time, offset);
594*3a2df602SBiswarup Pal
595*3a2df602SBiswarup Pal if (ktime_after(min_time, timestamp) || ktime_after(timestamp, current_time))
596*3a2df602SBiswarup Pal return false;
597*3a2df602SBiswarup Pal
598*3a2df602SBiswarup Pal return true;
599*3a2df602SBiswarup Pal }
600*3a2df602SBiswarup Pal
uinput_inject_events(struct uinput_device * udev,const char __user * buffer,size_t count)601cbf05413SRyan Mallon static ssize_t uinput_inject_events(struct uinput_device *udev,
60254ce165eSDmitry Torokhov const char __user *buffer, size_t count)
6031da177e4SLinus Torvalds {
6041da177e4SLinus Torvalds struct input_event ev;
605cbf05413SRyan Mallon size_t bytes = 0;
606*3a2df602SBiswarup Pal ktime_t timestamp;
6071da177e4SLinus Torvalds
608cbf05413SRyan Mallon if (count != 0 && count < input_event_size())
60929506415SDmitry Torokhov return -EINVAL;
61029506415SDmitry Torokhov
611cbf05413SRyan Mallon while (bytes + input_event_size() <= count) {
612cbf05413SRyan Mallon /*
613cbf05413SRyan Mallon * Note that even if some events were fetched successfully
614cbf05413SRyan Mallon * we are still going to return EFAULT instead of partial
615cbf05413SRyan Mallon * count to let userspace know that it got it's buffers
616cbf05413SRyan Mallon * all wrong.
617cbf05413SRyan Mallon */
618cbf05413SRyan Mallon if (input_event_from_user(buffer + bytes, &ev))
6191da177e4SLinus Torvalds return -EFAULT;
6201da177e4SLinus Torvalds
621*3a2df602SBiswarup Pal timestamp = ktime_set(ev.input_event_sec, ev.input_event_usec * NSEC_PER_USEC);
622*3a2df602SBiswarup Pal if (is_valid_timestamp(timestamp))
623*3a2df602SBiswarup Pal input_set_timestamp(udev->dev, timestamp);
624*3a2df602SBiswarup Pal
62529506415SDmitry Torokhov input_event(udev->dev, ev.type, ev.code, ev.value);
626cbf05413SRyan Mallon bytes += input_event_size();
627cecf1070SDmitry Torokhov cond_resched();
628cbf05413SRyan Mallon }
62929506415SDmitry Torokhov
630cbf05413SRyan Mallon return bytes;
63129506415SDmitry Torokhov }
63229506415SDmitry Torokhov
uinput_write(struct file * file,const char __user * buffer,size_t count,loff_t * ppos)63354ce165eSDmitry Torokhov static ssize_t uinput_write(struct file *file, const char __user *buffer,
63454ce165eSDmitry Torokhov size_t count, loff_t *ppos)
63529506415SDmitry Torokhov {
63629506415SDmitry Torokhov struct uinput_device *udev = file->private_data;
63729506415SDmitry Torokhov int retval;
63829506415SDmitry Torokhov
63922ae19c6SDmitry Torokhov if (count == 0)
64022ae19c6SDmitry Torokhov return 0;
64122ae19c6SDmitry Torokhov
642221979aaSDmitry Torokhov retval = mutex_lock_interruptible(&udev->mutex);
64329506415SDmitry Torokhov if (retval)
64429506415SDmitry Torokhov return retval;
64529506415SDmitry Torokhov
64629506415SDmitry Torokhov retval = udev->state == UIST_CREATED ?
647cbf05413SRyan Mallon uinput_inject_events(udev, buffer, count) :
648052876f8SBenjamin Tissoires uinput_setup_device_legacy(udev, buffer, count);
64929506415SDmitry Torokhov
650221979aaSDmitry Torokhov mutex_unlock(&udev->mutex);
65129506415SDmitry Torokhov
65229506415SDmitry Torokhov return retval;
6531da177e4SLinus Torvalds }
6541da177e4SLinus Torvalds
uinput_fetch_next_event(struct uinput_device * udev,struct input_event * event)655929d1af5SDmitry Torokhov static bool uinput_fetch_next_event(struct uinput_device *udev,
656929d1af5SDmitry Torokhov struct input_event *event)
657929d1af5SDmitry Torokhov {
658929d1af5SDmitry Torokhov bool have_event;
659929d1af5SDmitry Torokhov
660929d1af5SDmitry Torokhov spin_lock_irq(&udev->dev->event_lock);
661929d1af5SDmitry Torokhov
662929d1af5SDmitry Torokhov have_event = udev->head != udev->tail;
663929d1af5SDmitry Torokhov if (have_event) {
664929d1af5SDmitry Torokhov *event = udev->buff[udev->tail];
665929d1af5SDmitry Torokhov udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
666929d1af5SDmitry Torokhov }
667929d1af5SDmitry Torokhov
668929d1af5SDmitry Torokhov spin_unlock_irq(&udev->dev->event_lock);
669929d1af5SDmitry Torokhov
670929d1af5SDmitry Torokhov return have_event;
671929d1af5SDmitry Torokhov }
672929d1af5SDmitry Torokhov
uinput_events_to_user(struct uinput_device * udev,char __user * buffer,size_t count)67322ae19c6SDmitry Torokhov static ssize_t uinput_events_to_user(struct uinput_device *udev,
67422ae19c6SDmitry Torokhov char __user *buffer, size_t count)
67522ae19c6SDmitry Torokhov {
67622ae19c6SDmitry Torokhov struct input_event event;
67722ae19c6SDmitry Torokhov size_t read = 0;
67822ae19c6SDmitry Torokhov
67922ae19c6SDmitry Torokhov while (read + input_event_size() <= count &&
68022ae19c6SDmitry Torokhov uinput_fetch_next_event(udev, &event)) {
68122ae19c6SDmitry Torokhov
68200ce756cSDmitry Torokhov if (input_event_to_user(buffer + read, &event))
68300ce756cSDmitry Torokhov return -EFAULT;
68422ae19c6SDmitry Torokhov
68522ae19c6SDmitry Torokhov read += input_event_size();
68622ae19c6SDmitry Torokhov }
68722ae19c6SDmitry Torokhov
68800ce756cSDmitry Torokhov return read;
68922ae19c6SDmitry Torokhov }
69022ae19c6SDmitry Torokhov
uinput_read(struct file * file,char __user * buffer,size_t count,loff_t * ppos)69122ae19c6SDmitry Torokhov static ssize_t uinput_read(struct file *file, char __user *buffer,
69222ae19c6SDmitry Torokhov size_t count, loff_t *ppos)
6931da177e4SLinus Torvalds {
6941da177e4SLinus Torvalds struct uinput_device *udev = file->private_data;
69522ae19c6SDmitry Torokhov ssize_t retval;
6961da177e4SLinus Torvalds
697f40033acSDavid Herrmann if (count != 0 && count < input_event_size())
698f40033acSDavid Herrmann return -EINVAL;
699f40033acSDavid Herrmann
70022ae19c6SDmitry Torokhov do {
701221979aaSDmitry Torokhov retval = mutex_lock_interruptible(&udev->mutex);
70229506415SDmitry Torokhov if (retval)
70329506415SDmitry Torokhov return retval;
7041da177e4SLinus Torvalds
70522ae19c6SDmitry Torokhov if (udev->state != UIST_CREATED)
70629506415SDmitry Torokhov retval = -ENODEV;
70722ae19c6SDmitry Torokhov else if (udev->head == udev->tail &&
70822ae19c6SDmitry Torokhov (file->f_flags & O_NONBLOCK))
70922ae19c6SDmitry Torokhov retval = -EAGAIN;
71022ae19c6SDmitry Torokhov else
71122ae19c6SDmitry Torokhov retval = uinput_events_to_user(udev, buffer, count);
71229506415SDmitry Torokhov
713221979aaSDmitry Torokhov mutex_unlock(&udev->mutex);
71429506415SDmitry Torokhov
71522ae19c6SDmitry Torokhov if (retval || count == 0)
71622ae19c6SDmitry Torokhov break;
71722ae19c6SDmitry Torokhov
71822ae19c6SDmitry Torokhov if (!(file->f_flags & O_NONBLOCK))
71922ae19c6SDmitry Torokhov retval = wait_event_interruptible(udev->waitq,
72022ae19c6SDmitry Torokhov udev->head != udev->tail ||
72122ae19c6SDmitry Torokhov udev->state != UIST_CREATED);
72222ae19c6SDmitry Torokhov } while (retval == 0);
72322ae19c6SDmitry Torokhov
7241da177e4SLinus Torvalds return retval;
7251da177e4SLinus Torvalds }
7261da177e4SLinus Torvalds
uinput_poll(struct file * file,poll_table * wait)727afc9a42bSAl Viro static __poll_t uinput_poll(struct file *file, poll_table *wait)
7281da177e4SLinus Torvalds {
7291da177e4SLinus Torvalds struct uinput_device *udev = file->private_data;
730add21809SDmitry Torokhov __poll_t mask = EPOLLOUT | EPOLLWRNORM; /* uinput is always writable */
7311da177e4SLinus Torvalds
7321da177e4SLinus Torvalds poll_wait(file, &udev->waitq, wait);
7331da177e4SLinus Torvalds
7341da177e4SLinus Torvalds if (udev->head != udev->tail)
735add21809SDmitry Torokhov mask |= EPOLLIN | EPOLLRDNORM;
7361da177e4SLinus Torvalds
737add21809SDmitry Torokhov return mask;
7381da177e4SLinus Torvalds }
7391da177e4SLinus Torvalds
uinput_release(struct inode * inode,struct file * file)74029506415SDmitry Torokhov static int uinput_release(struct inode *inode, struct file *file)
7411da177e4SLinus Torvalds {
74229506415SDmitry Torokhov struct uinput_device *udev = file->private_data;
7431da177e4SLinus Torvalds
74429506415SDmitry Torokhov uinput_destroy_device(udev);
7451da177e4SLinus Torvalds kfree(udev);
7461da177e4SLinus Torvalds
7471da177e4SLinus Torvalds return 0;
7481da177e4SLinus Torvalds }
7491da177e4SLinus Torvalds
7502d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
7512d56f3a3SPhilip Langdale struct uinput_ff_upload_compat {
752c5b3533aSDmitry Torokhov __u32 request_id;
753c5b3533aSDmitry Torokhov __s32 retval;
7542d56f3a3SPhilip Langdale struct ff_effect_compat effect;
7552d56f3a3SPhilip Langdale struct ff_effect_compat old;
7562d56f3a3SPhilip Langdale };
7572d56f3a3SPhilip Langdale
uinput_ff_upload_to_user(char __user * buffer,const struct uinput_ff_upload * ff_up)7582d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer,
7592d56f3a3SPhilip Langdale const struct uinput_ff_upload *ff_up)
7602d56f3a3SPhilip Langdale {
761b8b4ead1SAndrew Morton if (in_compat_syscall()) {
7622d56f3a3SPhilip Langdale struct uinput_ff_upload_compat ff_up_compat;
7632d56f3a3SPhilip Langdale
7642d56f3a3SPhilip Langdale ff_up_compat.request_id = ff_up->request_id;
7652d56f3a3SPhilip Langdale ff_up_compat.retval = ff_up->retval;
7662d56f3a3SPhilip Langdale /*
7672d56f3a3SPhilip Langdale * It so happens that the pointer that gives us the trouble
7682d56f3a3SPhilip Langdale * is the last field in the structure. Since we don't support
7692d56f3a3SPhilip Langdale * custom waveforms in uinput anyway we can just copy the whole
7702d56f3a3SPhilip Langdale * thing (to the compat size) and ignore the pointer.
7712d56f3a3SPhilip Langdale */
7722d56f3a3SPhilip Langdale memcpy(&ff_up_compat.effect, &ff_up->effect,
7732d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat));
7742d56f3a3SPhilip Langdale memcpy(&ff_up_compat.old, &ff_up->old,
7752d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat));
7762d56f3a3SPhilip Langdale
7772d56f3a3SPhilip Langdale if (copy_to_user(buffer, &ff_up_compat,
7782d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload_compat)))
7792d56f3a3SPhilip Langdale return -EFAULT;
7802d56f3a3SPhilip Langdale } else {
7812d56f3a3SPhilip Langdale if (copy_to_user(buffer, ff_up,
7822d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload)))
7832d56f3a3SPhilip Langdale return -EFAULT;
7842d56f3a3SPhilip Langdale }
7852d56f3a3SPhilip Langdale
7862d56f3a3SPhilip Langdale return 0;
7872d56f3a3SPhilip Langdale }
7882d56f3a3SPhilip Langdale
uinput_ff_upload_from_user(const char __user * buffer,struct uinput_ff_upload * ff_up)7892d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer,
7902d56f3a3SPhilip Langdale struct uinput_ff_upload *ff_up)
7912d56f3a3SPhilip Langdale {
792b8b4ead1SAndrew Morton if (in_compat_syscall()) {
7932d56f3a3SPhilip Langdale struct uinput_ff_upload_compat ff_up_compat;
7942d56f3a3SPhilip Langdale
7952d56f3a3SPhilip Langdale if (copy_from_user(&ff_up_compat, buffer,
7962d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload_compat)))
7972d56f3a3SPhilip Langdale return -EFAULT;
7982d56f3a3SPhilip Langdale
7992d56f3a3SPhilip Langdale ff_up->request_id = ff_up_compat.request_id;
8002d56f3a3SPhilip Langdale ff_up->retval = ff_up_compat.retval;
8012d56f3a3SPhilip Langdale memcpy(&ff_up->effect, &ff_up_compat.effect,
8022d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat));
8032d56f3a3SPhilip Langdale memcpy(&ff_up->old, &ff_up_compat.old,
8042d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat));
8052d56f3a3SPhilip Langdale
8062d56f3a3SPhilip Langdale } else {
8072d56f3a3SPhilip Langdale if (copy_from_user(ff_up, buffer,
8082d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload)))
8092d56f3a3SPhilip Langdale return -EFAULT;
8102d56f3a3SPhilip Langdale }
8112d56f3a3SPhilip Langdale
8122d56f3a3SPhilip Langdale return 0;
8132d56f3a3SPhilip Langdale }
8142d56f3a3SPhilip Langdale
8152d56f3a3SPhilip Langdale #else
8162d56f3a3SPhilip Langdale
uinput_ff_upload_to_user(char __user * buffer,const struct uinput_ff_upload * ff_up)8172d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer,
8182d56f3a3SPhilip Langdale const struct uinput_ff_upload *ff_up)
8192d56f3a3SPhilip Langdale {
8202d56f3a3SPhilip Langdale if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload)))
8212d56f3a3SPhilip Langdale return -EFAULT;
8222d56f3a3SPhilip Langdale
8232d56f3a3SPhilip Langdale return 0;
8242d56f3a3SPhilip Langdale }
8252d56f3a3SPhilip Langdale
uinput_ff_upload_from_user(const char __user * buffer,struct uinput_ff_upload * ff_up)8262d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer,
8272d56f3a3SPhilip Langdale struct uinput_ff_upload *ff_up)
8282d56f3a3SPhilip Langdale {
8292d56f3a3SPhilip Langdale if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload)))
8302d56f3a3SPhilip Langdale return -EFAULT;
8312d56f3a3SPhilip Langdale
8322d56f3a3SPhilip Langdale return 0;
8332d56f3a3SPhilip Langdale }
8342d56f3a3SPhilip Langdale
8352d56f3a3SPhilip Langdale #endif
8362d56f3a3SPhilip Langdale
83729506415SDmitry Torokhov #define uinput_set_bit(_arg, _bit, _max) \
83829506415SDmitry Torokhov ({ \
83929506415SDmitry Torokhov int __ret = 0; \
84029506415SDmitry Torokhov if (udev->state == UIST_CREATED) \
84129506415SDmitry Torokhov __ret = -EINVAL; \
84229506415SDmitry Torokhov else if ((_arg) > (_max)) \
84329506415SDmitry Torokhov __ret = -EINVAL; \
84429506415SDmitry Torokhov else set_bit((_arg), udev->dev->_bit); \
84529506415SDmitry Torokhov __ret; \
84629506415SDmitry Torokhov })
8471da177e4SLinus Torvalds
uinput_str_to_user(void __user * dest,const char * str,unsigned int maxlen)848e3480a61SBenjamin Tissoires static int uinput_str_to_user(void __user *dest, const char *str,
849e3480a61SBenjamin Tissoires unsigned int maxlen)
850e3480a61SBenjamin Tissoires {
851e3480a61SBenjamin Tissoires char __user *p = dest;
852e3480a61SBenjamin Tissoires int len, ret;
853e3480a61SBenjamin Tissoires
854e3480a61SBenjamin Tissoires if (!str)
855e3480a61SBenjamin Tissoires return -ENOENT;
856e3480a61SBenjamin Tissoires
857e3480a61SBenjamin Tissoires if (maxlen == 0)
858e3480a61SBenjamin Tissoires return -EINVAL;
859e3480a61SBenjamin Tissoires
860e3480a61SBenjamin Tissoires len = strlen(str) + 1;
861e3480a61SBenjamin Tissoires if (len > maxlen)
862e3480a61SBenjamin Tissoires len = maxlen;
863e3480a61SBenjamin Tissoires
864e3480a61SBenjamin Tissoires ret = copy_to_user(p, str, len);
865e3480a61SBenjamin Tissoires if (ret)
866e3480a61SBenjamin Tissoires return -EFAULT;
867e3480a61SBenjamin Tissoires
868e3480a61SBenjamin Tissoires /* force terminating '\0' */
869e3480a61SBenjamin Tissoires ret = put_user(0, p + len - 1);
870e3480a61SBenjamin Tissoires return ret ? -EFAULT : len;
871e3480a61SBenjamin Tissoires }
872e3480a61SBenjamin Tissoires
uinput_ioctl_handler(struct file * file,unsigned int cmd,unsigned long arg,void __user * p)8732d56f3a3SPhilip Langdale static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
8742d56f3a3SPhilip Langdale unsigned long arg, void __user *p)
8751da177e4SLinus Torvalds {
87629506415SDmitry Torokhov int retval;
8772d56f3a3SPhilip Langdale struct uinput_device *udev = file->private_data;
8781da177e4SLinus Torvalds struct uinput_ff_upload ff_up;
8791da177e4SLinus Torvalds struct uinput_ff_erase ff_erase;
8801da177e4SLinus Torvalds struct uinput_request *req;
8815b6271bdSDmitry Torokhov char *phys;
882e3480a61SBenjamin Tissoires const char *name;
883e3480a61SBenjamin Tissoires unsigned int size;
8841da177e4SLinus Torvalds
885221979aaSDmitry Torokhov retval = mutex_lock_interruptible(&udev->mutex);
88629506415SDmitry Torokhov if (retval)
88729506415SDmitry Torokhov return retval;
88829506415SDmitry Torokhov
88929506415SDmitry Torokhov if (!udev->dev) {
89004ce40a6SDmitry Torokhov udev->dev = input_allocate_device();
891781f2dd0SDan Carpenter if (!udev->dev) {
892781f2dd0SDan Carpenter retval = -ENOMEM;
893781f2dd0SDan Carpenter goto out;
894781f2dd0SDan Carpenter }
8951da177e4SLinus Torvalds }
8961da177e4SLinus Torvalds
8971da177e4SLinus Torvalds switch (cmd) {
898ba4e9a61SDavid Herrmann case UI_GET_VERSION:
899c0661652SDmitry Torokhov if (put_user(UINPUT_VERSION, (unsigned int __user *)p))
900ba4e9a61SDavid Herrmann retval = -EFAULT;
901ba4e9a61SDavid Herrmann goto out;
902ba4e9a61SDavid Herrmann
9031da177e4SLinus Torvalds case UI_DEV_CREATE:
9041da177e4SLinus Torvalds retval = uinput_create_device(udev);
9059d51e801SBenjamin Tisssoires goto out;
9061da177e4SLinus Torvalds
9071da177e4SLinus Torvalds case UI_DEV_DESTROY:
90829506415SDmitry Torokhov uinput_destroy_device(udev);
9099d51e801SBenjamin Tisssoires goto out;
9101da177e4SLinus Torvalds
911052876f8SBenjamin Tissoires case UI_DEV_SETUP:
912052876f8SBenjamin Tissoires retval = uinput_dev_setup(udev, p);
913052876f8SBenjamin Tissoires goto out;
914052876f8SBenjamin Tissoires
915052876f8SBenjamin Tissoires /* UI_ABS_SETUP is handled in the variable size ioctls */
916052876f8SBenjamin Tissoires
9171da177e4SLinus Torvalds case UI_SET_EVBIT:
91829506415SDmitry Torokhov retval = uinput_set_bit(arg, evbit, EV_MAX);
9199d51e801SBenjamin Tisssoires goto out;
9201da177e4SLinus Torvalds
9211da177e4SLinus Torvalds case UI_SET_KEYBIT:
92229506415SDmitry Torokhov retval = uinput_set_bit(arg, keybit, KEY_MAX);
9239d51e801SBenjamin Tisssoires goto out;
9241da177e4SLinus Torvalds
9251da177e4SLinus Torvalds case UI_SET_RELBIT:
92629506415SDmitry Torokhov retval = uinput_set_bit(arg, relbit, REL_MAX);
9279d51e801SBenjamin Tisssoires goto out;
9281da177e4SLinus Torvalds
9291da177e4SLinus Torvalds case UI_SET_ABSBIT:
93029506415SDmitry Torokhov retval = uinput_set_bit(arg, absbit, ABS_MAX);
9319d51e801SBenjamin Tisssoires goto out;
9321da177e4SLinus Torvalds
9331da177e4SLinus Torvalds case UI_SET_MSCBIT:
93429506415SDmitry Torokhov retval = uinput_set_bit(arg, mscbit, MSC_MAX);
9359d51e801SBenjamin Tisssoires goto out;
9361da177e4SLinus Torvalds
9371da177e4SLinus Torvalds case UI_SET_LEDBIT:
93829506415SDmitry Torokhov retval = uinput_set_bit(arg, ledbit, LED_MAX);
9399d51e801SBenjamin Tisssoires goto out;
9401da177e4SLinus Torvalds
9411da177e4SLinus Torvalds case UI_SET_SNDBIT:
94229506415SDmitry Torokhov retval = uinput_set_bit(arg, sndbit, SND_MAX);
9439d51e801SBenjamin Tisssoires goto out;
9441da177e4SLinus Torvalds
9451da177e4SLinus Torvalds case UI_SET_FFBIT:
94629506415SDmitry Torokhov retval = uinput_set_bit(arg, ffbit, FF_MAX);
9479d51e801SBenjamin Tisssoires goto out;
9481da177e4SLinus Torvalds
94959c7c037SDmitry Torokhov case UI_SET_SWBIT:
95059c7c037SDmitry Torokhov retval = uinput_set_bit(arg, swbit, SW_MAX);
9519d51e801SBenjamin Tisssoires goto out;
95259c7c037SDmitry Torokhov
95385b77200SHenrik Rydberg case UI_SET_PROPBIT:
95485b77200SHenrik Rydberg retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX);
9559d51e801SBenjamin Tisssoires goto out;
95685b77200SHenrik Rydberg
9571da177e4SLinus Torvalds case UI_SET_PHYS:
95829506415SDmitry Torokhov if (udev->state == UIST_CREATED) {
95929506415SDmitry Torokhov retval = -EINVAL;
96029506415SDmitry Torokhov goto out;
96129506415SDmitry Torokhov }
9624dfcc271SDmitry Torokhov
9634dfcc271SDmitry Torokhov phys = strndup_user(p, 1024);
9644dfcc271SDmitry Torokhov if (IS_ERR(phys)) {
9654dfcc271SDmitry Torokhov retval = PTR_ERR(phys);
9664dfcc271SDmitry Torokhov goto out;
9671da177e4SLinus Torvalds }
9684dfcc271SDmitry Torokhov
9691da177e4SLinus Torvalds kfree(udev->dev->phys);
9704dfcc271SDmitry Torokhov udev->dev->phys = phys;
9719d51e801SBenjamin Tisssoires goto out;
9721da177e4SLinus Torvalds
9731da177e4SLinus Torvalds case UI_BEGIN_FF_UPLOAD:
9742d56f3a3SPhilip Langdale retval = uinput_ff_upload_from_user(p, &ff_up);
9752d56f3a3SPhilip Langdale if (retval)
9769d51e801SBenjamin Tisssoires goto out;
9772d56f3a3SPhilip Langdale
9781da177e4SLinus Torvalds req = uinput_request_find(udev, ff_up.request_id);
97954ce165eSDmitry Torokhov if (!req || req->code != UI_FF_UPLOAD ||
98054ce165eSDmitry Torokhov !req->u.upload.effect) {
9811da177e4SLinus Torvalds retval = -EINVAL;
9829d51e801SBenjamin Tisssoires goto out;
9831da177e4SLinus Torvalds }
9842d56f3a3SPhilip Langdale
9851da177e4SLinus Torvalds ff_up.retval = 0;
9862d56f3a3SPhilip Langdale ff_up.effect = *req->u.upload.effect;
987ff462551SAnssi Hannula if (req->u.upload.old)
9882d56f3a3SPhilip Langdale ff_up.old = *req->u.upload.old;
989ff462551SAnssi Hannula else
990ff462551SAnssi Hannula memset(&ff_up.old, 0, sizeof(struct ff_effect));
991ff462551SAnssi Hannula
9922d56f3a3SPhilip Langdale retval = uinput_ff_upload_to_user(p, &ff_up);
9939d51e801SBenjamin Tisssoires goto out;
9941da177e4SLinus Torvalds
9951da177e4SLinus Torvalds case UI_BEGIN_FF_ERASE:
9961da177e4SLinus Torvalds if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
9971da177e4SLinus Torvalds retval = -EFAULT;
9989d51e801SBenjamin Tisssoires goto out;
9991da177e4SLinus Torvalds }
10002d56f3a3SPhilip Langdale
10011da177e4SLinus Torvalds req = uinput_request_find(udev, ff_erase.request_id);
10022d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_ERASE) {
10031da177e4SLinus Torvalds retval = -EINVAL;
10049d51e801SBenjamin Tisssoires goto out;
10051da177e4SLinus Torvalds }
10062d56f3a3SPhilip Langdale
10071da177e4SLinus Torvalds ff_erase.retval = 0;
10081da177e4SLinus Torvalds ff_erase.effect_id = req->u.effect_id;
10091da177e4SLinus Torvalds if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
10101da177e4SLinus Torvalds retval = -EFAULT;
10119d51e801SBenjamin Tisssoires goto out;
10121da177e4SLinus Torvalds }
10132d56f3a3SPhilip Langdale
10149d51e801SBenjamin Tisssoires goto out;
10151da177e4SLinus Torvalds
10161da177e4SLinus Torvalds case UI_END_FF_UPLOAD:
10172d56f3a3SPhilip Langdale retval = uinput_ff_upload_from_user(p, &ff_up);
10182d56f3a3SPhilip Langdale if (retval)
10199d51e801SBenjamin Tisssoires goto out;
10202d56f3a3SPhilip Langdale
10211da177e4SLinus Torvalds req = uinput_request_find(udev, ff_up.request_id);
10222d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_UPLOAD ||
10232d56f3a3SPhilip Langdale !req->u.upload.effect) {
10241da177e4SLinus Torvalds retval = -EINVAL;
10259d51e801SBenjamin Tisssoires goto out;
10261da177e4SLinus Torvalds }
10272d56f3a3SPhilip Langdale
10281da177e4SLinus Torvalds req->retval = ff_up.retval;
10296b4877c7SDmitry Torokhov complete(&req->done);
10309d51e801SBenjamin Tisssoires goto out;
10311da177e4SLinus Torvalds
10321da177e4SLinus Torvalds case UI_END_FF_ERASE:
10331da177e4SLinus Torvalds if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
10341da177e4SLinus Torvalds retval = -EFAULT;
10359d51e801SBenjamin Tisssoires goto out;
10361da177e4SLinus Torvalds }
10372d56f3a3SPhilip Langdale
10381da177e4SLinus Torvalds req = uinput_request_find(udev, ff_erase.request_id);
10392d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_ERASE) {
10401da177e4SLinus Torvalds retval = -EINVAL;
10419d51e801SBenjamin Tisssoires goto out;
10421da177e4SLinus Torvalds }
10432d56f3a3SPhilip Langdale
10441da177e4SLinus Torvalds req->retval = ff_erase.retval;
10456b4877c7SDmitry Torokhov complete(&req->done);
10469d51e801SBenjamin Tisssoires goto out;
10471da177e4SLinus Torvalds }
104829506415SDmitry Torokhov
1049e3480a61SBenjamin Tissoires size = _IOC_SIZE(cmd);
1050e3480a61SBenjamin Tissoires
1051e3480a61SBenjamin Tissoires /* Now check variable-length commands */
1052e3480a61SBenjamin Tissoires switch (cmd & ~IOCSIZE_MASK) {
1053e3480a61SBenjamin Tissoires case UI_GET_SYSNAME(0):
1054e3480a61SBenjamin Tissoires if (udev->state != UIST_CREATED) {
1055e3480a61SBenjamin Tissoires retval = -ENOENT;
1056e3480a61SBenjamin Tissoires goto out;
1057e3480a61SBenjamin Tissoires }
1058e3480a61SBenjamin Tissoires name = dev_name(&udev->dev->dev);
1059e3480a61SBenjamin Tissoires retval = uinput_str_to_user(p, name, size);
1060e3480a61SBenjamin Tissoires goto out;
1061052876f8SBenjamin Tissoires
1062052876f8SBenjamin Tissoires case UI_ABS_SETUP & ~IOCSIZE_MASK:
1063052876f8SBenjamin Tissoires retval = uinput_abs_setup(udev, p, size);
1064052876f8SBenjamin Tissoires goto out;
1065e3480a61SBenjamin Tissoires }
1066e3480a61SBenjamin Tissoires
10679d51e801SBenjamin Tisssoires retval = -EINVAL;
106829506415SDmitry Torokhov out:
1069221979aaSDmitry Torokhov mutex_unlock(&udev->mutex);
10701da177e4SLinus Torvalds return retval;
10711da177e4SLinus Torvalds }
10721da177e4SLinus Torvalds
uinput_ioctl(struct file * file,unsigned int cmd,unsigned long arg)10732d56f3a3SPhilip Langdale static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
10742d56f3a3SPhilip Langdale {
10752d56f3a3SPhilip Langdale return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg);
10762d56f3a3SPhilip Langdale }
10772d56f3a3SPhilip Langdale
10782d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
1079affa80bdSRicky Liang
10807c7da40dSAndrey Smirnov /*
10817c7da40dSAndrey Smirnov * These IOCTLs change their size and thus their numbers between
10827c7da40dSAndrey Smirnov * 32 and 64 bits.
10837c7da40dSAndrey Smirnov */
10847c7da40dSAndrey Smirnov #define UI_SET_PHYS_COMPAT \
10857c7da40dSAndrey Smirnov _IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t)
10867c7da40dSAndrey Smirnov #define UI_BEGIN_FF_UPLOAD_COMPAT \
10877c7da40dSAndrey Smirnov _IOWR(UINPUT_IOCTL_BASE, 200, struct uinput_ff_upload_compat)
10887c7da40dSAndrey Smirnov #define UI_END_FF_UPLOAD_COMPAT \
10897c7da40dSAndrey Smirnov _IOW(UINPUT_IOCTL_BASE, 201, struct uinput_ff_upload_compat)
1090affa80bdSRicky Liang
uinput_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)109154ce165eSDmitry Torokhov static long uinput_compat_ioctl(struct file *file,
109254ce165eSDmitry Torokhov unsigned int cmd, unsigned long arg)
10932d56f3a3SPhilip Langdale {
10947c7da40dSAndrey Smirnov switch (cmd) {
10957c7da40dSAndrey Smirnov case UI_SET_PHYS_COMPAT:
1096affa80bdSRicky Liang cmd = UI_SET_PHYS;
10977c7da40dSAndrey Smirnov break;
10987c7da40dSAndrey Smirnov case UI_BEGIN_FF_UPLOAD_COMPAT:
10997c7da40dSAndrey Smirnov cmd = UI_BEGIN_FF_UPLOAD;
11007c7da40dSAndrey Smirnov break;
11017c7da40dSAndrey Smirnov case UI_END_FF_UPLOAD_COMPAT:
11027c7da40dSAndrey Smirnov cmd = UI_END_FF_UPLOAD;
11037c7da40dSAndrey Smirnov break;
11047c7da40dSAndrey Smirnov }
1105affa80bdSRicky Liang
11062d56f3a3SPhilip Langdale return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
11072d56f3a3SPhilip Langdale }
11082d56f3a3SPhilip Langdale #endif
11092d56f3a3SPhilip Langdale
11102b8693c0SArjan van de Ven static const struct file_operations uinput_fops = {
11111da177e4SLinus Torvalds .owner = THIS_MODULE,
11121da177e4SLinus Torvalds .open = uinput_open,
111329506415SDmitry Torokhov .release = uinput_release,
11141da177e4SLinus Torvalds .read = uinput_read,
11151da177e4SLinus Torvalds .write = uinput_write,
11161da177e4SLinus Torvalds .poll = uinput_poll,
111729506415SDmitry Torokhov .unlocked_ioctl = uinput_ioctl,
11182d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
11192d56f3a3SPhilip Langdale .compat_ioctl = uinput_compat_ioctl,
11202d56f3a3SPhilip Langdale #endif
11216038f373SArnd Bergmann .llseek = no_llseek,
11221da177e4SLinus Torvalds };
11231da177e4SLinus Torvalds
11241da177e4SLinus Torvalds static struct miscdevice uinput_misc = {
11251da177e4SLinus Torvalds .fops = &uinput_fops,
11261da177e4SLinus Torvalds .minor = UINPUT_MINOR,
11271da177e4SLinus Torvalds .name = UINPUT_NAME,
11281da177e4SLinus Torvalds };
1129ca75d601SPrasannaKumar Muralidharan module_misc_device(uinput_misc);
1130ca75d601SPrasannaKumar Muralidharan
11318905aaafSKay Sievers MODULE_ALIAS_MISCDEV(UINPUT_MINOR);
11328905aaafSKay Sievers MODULE_ALIAS("devname:" UINPUT_NAME);
11331da177e4SLinus Torvalds
11341da177e4SLinus Torvalds MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
11351da177e4SLinus Torvalds MODULE_DESCRIPTION("User level driver support for input subsystem");
11361da177e4SLinus Torvalds MODULE_LICENSE("GPL");
1137