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 <[email protected]>
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * Changes/Revisions:
10e3480a61SBenjamin Tissoires * 0.4 01/09/2014 (Benjamin Tissoires <[email protected]>)
11e3480a61SBenjamin Tissoires * - add UI_GET_SYSNAME ioctl
12ff462551SAnssi Hannula * 0.3 09/04/2006 (Anssi Hannula <[email protected]>)
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 <[email protected]>)
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
363a2df602SBiswarup 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
382a0bd7adaSErick Archer newdev = kzalloc(sizeof(*newdev), 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
420*206f533aSDmitry Torokhov /*
421*206f533aSDmitry Torokhov * Limit number of contacts to a reasonable value (100). This
422*206f533aSDmitry Torokhov * ensures that we need less than 2 pages for struct input_mt
423*206f533aSDmitry Torokhov * (we are not using in-kernel slot assignment so not going to
424*206f533aSDmitry Torokhov * allocate memory for the "red" table), and we should have no
425*206f533aSDmitry Torokhov * trouble getting this much memory.
426*206f533aSDmitry Torokhov */
427*206f533aSDmitry Torokhov if (code == ABS_MT_SLOT && max > 99) {
428*206f533aSDmitry Torokhov printk(KERN_DEBUG
429*206f533aSDmitry Torokhov "%s: unreasonably large number of slots requested: %d\n",
430*206f533aSDmitry Torokhov UINPUT_NAME, max);
431*206f533aSDmitry Torokhov return -EINVAL;
432*206f533aSDmitry Torokhov }
433*206f533aSDmitry Torokhov
434fbae10dbSDavid Herrmann return 0;
435fbae10dbSDavid Herrmann }
436fbae10dbSDavid Herrmann
uinput_validate_absbits(struct input_dev * dev)4371da177e4SLinus Torvalds static int uinput_validate_absbits(struct input_dev *dev)
4381da177e4SLinus Torvalds {
4391da177e4SLinus Torvalds unsigned int cnt;
440fbae10dbSDavid Herrmann int error;
441bcb898e5SDavid Herrmann
442bcb898e5SDavid Herrmann if (!test_bit(EV_ABS, dev->evbit))
443bcb898e5SDavid Herrmann return 0;
444bcb898e5SDavid Herrmann
445bcb898e5SDavid Herrmann /*
446bcb898e5SDavid Herrmann * Check if absmin/absmax/absfuzz/absflat are sane.
447bcb898e5SDavid Herrmann */
4481da177e4SLinus Torvalds
449b6d30968SAnshul Garg for_each_set_bit(cnt, dev->absbit, ABS_CNT) {
450fbae10dbSDavid Herrmann if (!dev->absinfo)
451bcb898e5SDavid Herrmann return -EINVAL;
4521da177e4SLinus Torvalds
453fbae10dbSDavid Herrmann error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]);
454fbae10dbSDavid Herrmann if (error)
455fbae10dbSDavid Herrmann return error;
456bcb898e5SDavid Herrmann }
457bcb898e5SDavid Herrmann
458bcb898e5SDavid Herrmann return 0;
4591da177e4SLinus Torvalds }
4601da177e4SLinus Torvalds
uinput_dev_setup(struct uinput_device * udev,struct uinput_setup __user * arg)461052876f8SBenjamin Tissoires static int uinput_dev_setup(struct uinput_device *udev,
462052876f8SBenjamin Tissoires struct uinput_setup __user *arg)
463052876f8SBenjamin Tissoires {
464052876f8SBenjamin Tissoires struct uinput_setup setup;
465052876f8SBenjamin Tissoires struct input_dev *dev;
466052876f8SBenjamin Tissoires
467052876f8SBenjamin Tissoires if (udev->state == UIST_CREATED)
468052876f8SBenjamin Tissoires return -EINVAL;
469052876f8SBenjamin Tissoires
470052876f8SBenjamin Tissoires if (copy_from_user(&setup, arg, sizeof(setup)))
471052876f8SBenjamin Tissoires return -EFAULT;
472052876f8SBenjamin Tissoires
473052876f8SBenjamin Tissoires if (!setup.name[0])
474052876f8SBenjamin Tissoires return -EINVAL;
475052876f8SBenjamin Tissoires
476052876f8SBenjamin Tissoires dev = udev->dev;
477052876f8SBenjamin Tissoires dev->id = setup.id;
478052876f8SBenjamin Tissoires udev->ff_effects_max = setup.ff_effects_max;
479052876f8SBenjamin Tissoires
480052876f8SBenjamin Tissoires kfree(dev->name);
481052876f8SBenjamin Tissoires dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL);
482052876f8SBenjamin Tissoires if (!dev->name)
483052876f8SBenjamin Tissoires return -ENOMEM;
484052876f8SBenjamin Tissoires
485052876f8SBenjamin Tissoires udev->state = UIST_SETUP_COMPLETE;
486052876f8SBenjamin Tissoires return 0;
487052876f8SBenjamin Tissoires }
488052876f8SBenjamin Tissoires
uinput_abs_setup(struct uinput_device * udev,struct uinput_setup __user * arg,size_t size)489052876f8SBenjamin Tissoires static int uinput_abs_setup(struct uinput_device *udev,
490052876f8SBenjamin Tissoires struct uinput_setup __user *arg, size_t size)
491052876f8SBenjamin Tissoires {
492052876f8SBenjamin Tissoires struct uinput_abs_setup setup = {};
493052876f8SBenjamin Tissoires struct input_dev *dev;
494fbae10dbSDavid Herrmann int error;
495052876f8SBenjamin Tissoires
496052876f8SBenjamin Tissoires if (size > sizeof(setup))
497052876f8SBenjamin Tissoires return -E2BIG;
498052876f8SBenjamin Tissoires
499052876f8SBenjamin Tissoires if (udev->state == UIST_CREATED)
500052876f8SBenjamin Tissoires return -EINVAL;
501052876f8SBenjamin Tissoires
502052876f8SBenjamin Tissoires if (copy_from_user(&setup, arg, size))
503052876f8SBenjamin Tissoires return -EFAULT;
504052876f8SBenjamin Tissoires
505052876f8SBenjamin Tissoires if (setup.code > ABS_MAX)
506052876f8SBenjamin Tissoires return -ERANGE;
507052876f8SBenjamin Tissoires
508052876f8SBenjamin Tissoires dev = udev->dev;
509052876f8SBenjamin Tissoires
510fbae10dbSDavid Herrmann error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo);
511fbae10dbSDavid Herrmann if (error)
512fbae10dbSDavid Herrmann return error;
513fbae10dbSDavid Herrmann
514052876f8SBenjamin Tissoires input_alloc_absinfo(dev);
515052876f8SBenjamin Tissoires if (!dev->absinfo)
516052876f8SBenjamin Tissoires return -ENOMEM;
517052876f8SBenjamin Tissoires
518052876f8SBenjamin Tissoires set_bit(setup.code, dev->absbit);
519052876f8SBenjamin Tissoires dev->absinfo[setup.code] = setup.absinfo;
520052876f8SBenjamin Tissoires return 0;
521052876f8SBenjamin Tissoires }
522052876f8SBenjamin Tissoires
523052876f8SBenjamin Tissoires /* legacy setup via write() */
uinput_setup_device_legacy(struct uinput_device * udev,const char __user * buffer,size_t count)524052876f8SBenjamin Tissoires static int uinput_setup_device_legacy(struct uinput_device *udev,
52554ce165eSDmitry Torokhov const char __user *buffer, size_t count)
5261da177e4SLinus Torvalds {
5271da177e4SLinus Torvalds struct uinput_user_dev *user_dev;
5281da177e4SLinus Torvalds struct input_dev *dev;
5295d9d6e91SDavid Herrmann int i;
530152c12f5SDmitry Torokhov int retval;
5311da177e4SLinus Torvalds
53229506415SDmitry Torokhov if (count != sizeof(struct uinput_user_dev))
53329506415SDmitry Torokhov return -EINVAL;
5341da177e4SLinus Torvalds
53529506415SDmitry Torokhov if (!udev->dev) {
53604ce40a6SDmitry Torokhov udev->dev = input_allocate_device();
53704ce40a6SDmitry Torokhov if (!udev->dev)
53804ce40a6SDmitry Torokhov return -ENOMEM;
53929506415SDmitry Torokhov }
54029506415SDmitry Torokhov
5411da177e4SLinus Torvalds dev = udev->dev;
5421da177e4SLinus Torvalds
5434dfcc271SDmitry Torokhov user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));
544163d2770SDan Carpenter if (IS_ERR(user_dev))
5454dfcc271SDmitry Torokhov return PTR_ERR(user_dev);
5461da177e4SLinus Torvalds
547ff462551SAnssi Hannula udev->ff_effects_max = user_dev->ff_effects_max;
548ff462551SAnssi Hannula
5495d9d6e91SDavid Herrmann /* Ensure name is filled in */
5505d9d6e91SDavid Herrmann if (!user_dev->name[0]) {
55129506415SDmitry Torokhov retval = -EINVAL;
55229506415SDmitry Torokhov goto exit;
55329506415SDmitry Torokhov }
55429506415SDmitry Torokhov
55529506415SDmitry Torokhov kfree(dev->name);
5565d9d6e91SDavid Herrmann dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE,
5575d9d6e91SDavid Herrmann GFP_KERNEL);
5585d9d6e91SDavid Herrmann if (!dev->name) {
5591da177e4SLinus Torvalds retval = -ENOMEM;
5601da177e4SLinus Torvalds goto exit;
5611da177e4SLinus Torvalds }
5621da177e4SLinus Torvalds
5631da177e4SLinus Torvalds dev->id.bustype = user_dev->id.bustype;
5641da177e4SLinus Torvalds dev->id.vendor = user_dev->id.vendor;
5651da177e4SLinus Torvalds dev->id.product = user_dev->id.product;
5661da177e4SLinus Torvalds dev->id.version = user_dev->id.version;
5671da177e4SLinus Torvalds
56872d47362SDmitry Torokhov for (i = 0; i < ABS_CNT; i++) {
569987a6c02SDaniel Mack input_abs_set_max(dev, i, user_dev->absmax[i]);
570987a6c02SDaniel Mack input_abs_set_min(dev, i, user_dev->absmin[i]);
571987a6c02SDaniel Mack input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);
572987a6c02SDaniel Mack input_abs_set_flat(dev, i, user_dev->absflat[i]);
573987a6c02SDaniel Mack }
5741da177e4SLinus Torvalds
57529506415SDmitry Torokhov retval = uinput_validate_absbits(dev);
57629506415SDmitry Torokhov if (retval < 0)
57729506415SDmitry Torokhov goto exit;
57829506415SDmitry Torokhov
57929506415SDmitry Torokhov udev->state = UIST_SETUP_COMPLETE;
58029506415SDmitry Torokhov retval = count;
5811da177e4SLinus Torvalds
5821da177e4SLinus Torvalds exit:
5831da177e4SLinus Torvalds kfree(user_dev);
5841da177e4SLinus Torvalds return retval;
5851da177e4SLinus Torvalds }
5861da177e4SLinus Torvalds
5873a2df602SBiswarup Pal /*
5883a2df602SBiswarup Pal * Returns true if the given timestamp is valid (i.e., if all the following
5893a2df602SBiswarup Pal * conditions are satisfied), false otherwise.
5903a2df602SBiswarup Pal * 1) given timestamp is positive
5913a2df602SBiswarup Pal * 2) it's within the allowed offset before the current time
5923a2df602SBiswarup Pal * 3) it's not in the future
5933a2df602SBiswarup Pal */
is_valid_timestamp(const ktime_t timestamp)5943a2df602SBiswarup Pal static bool is_valid_timestamp(const ktime_t timestamp)
5953a2df602SBiswarup Pal {
5963a2df602SBiswarup Pal ktime_t zero_time;
5973a2df602SBiswarup Pal ktime_t current_time;
5983a2df602SBiswarup Pal ktime_t min_time;
5993a2df602SBiswarup Pal ktime_t offset;
6003a2df602SBiswarup Pal
6013a2df602SBiswarup Pal zero_time = ktime_set(0, 0);
6023a2df602SBiswarup Pal if (ktime_compare(zero_time, timestamp) >= 0)
6033a2df602SBiswarup Pal return false;
6043a2df602SBiswarup Pal
6053a2df602SBiswarup Pal current_time = ktime_get();
6063a2df602SBiswarup Pal offset = ktime_set(UINPUT_TIMESTAMP_ALLOWED_OFFSET_SECS, 0);
6073a2df602SBiswarup Pal min_time = ktime_sub(current_time, offset);
6083a2df602SBiswarup Pal
6093a2df602SBiswarup Pal if (ktime_after(min_time, timestamp) || ktime_after(timestamp, current_time))
6103a2df602SBiswarup Pal return false;
6113a2df602SBiswarup Pal
6123a2df602SBiswarup Pal return true;
6133a2df602SBiswarup Pal }
6143a2df602SBiswarup Pal
uinput_inject_events(struct uinput_device * udev,const char __user * buffer,size_t count)615cbf05413SRyan Mallon static ssize_t uinput_inject_events(struct uinput_device *udev,
61654ce165eSDmitry Torokhov const char __user *buffer, size_t count)
6171da177e4SLinus Torvalds {
6181da177e4SLinus Torvalds struct input_event ev;
619cbf05413SRyan Mallon size_t bytes = 0;
6203a2df602SBiswarup Pal ktime_t timestamp;
6211da177e4SLinus Torvalds
622cbf05413SRyan Mallon if (count != 0 && count < input_event_size())
62329506415SDmitry Torokhov return -EINVAL;
62429506415SDmitry Torokhov
625cbf05413SRyan Mallon while (bytes + input_event_size() <= count) {
626cbf05413SRyan Mallon /*
627cbf05413SRyan Mallon * Note that even if some events were fetched successfully
628cbf05413SRyan Mallon * we are still going to return EFAULT instead of partial
629cbf05413SRyan Mallon * count to let userspace know that it got it's buffers
630cbf05413SRyan Mallon * all wrong.
631cbf05413SRyan Mallon */
632cbf05413SRyan Mallon if (input_event_from_user(buffer + bytes, &ev))
6331da177e4SLinus Torvalds return -EFAULT;
6341da177e4SLinus Torvalds
6353a2df602SBiswarup Pal timestamp = ktime_set(ev.input_event_sec, ev.input_event_usec * NSEC_PER_USEC);
6363a2df602SBiswarup Pal if (is_valid_timestamp(timestamp))
6373a2df602SBiswarup Pal input_set_timestamp(udev->dev, timestamp);
6383a2df602SBiswarup Pal
63929506415SDmitry Torokhov input_event(udev->dev, ev.type, ev.code, ev.value);
640cbf05413SRyan Mallon bytes += input_event_size();
641cecf1070SDmitry Torokhov cond_resched();
642cbf05413SRyan Mallon }
64329506415SDmitry Torokhov
644cbf05413SRyan Mallon return bytes;
64529506415SDmitry Torokhov }
64629506415SDmitry Torokhov
uinput_write(struct file * file,const char __user * buffer,size_t count,loff_t * ppos)64754ce165eSDmitry Torokhov static ssize_t uinput_write(struct file *file, const char __user *buffer,
64854ce165eSDmitry Torokhov size_t count, loff_t *ppos)
64929506415SDmitry Torokhov {
65029506415SDmitry Torokhov struct uinput_device *udev = file->private_data;
65129506415SDmitry Torokhov int retval;
65229506415SDmitry Torokhov
65322ae19c6SDmitry Torokhov if (count == 0)
65422ae19c6SDmitry Torokhov return 0;
65522ae19c6SDmitry Torokhov
656221979aaSDmitry Torokhov retval = mutex_lock_interruptible(&udev->mutex);
65729506415SDmitry Torokhov if (retval)
65829506415SDmitry Torokhov return retval;
65929506415SDmitry Torokhov
66029506415SDmitry Torokhov retval = udev->state == UIST_CREATED ?
661cbf05413SRyan Mallon uinput_inject_events(udev, buffer, count) :
662052876f8SBenjamin Tissoires uinput_setup_device_legacy(udev, buffer, count);
66329506415SDmitry Torokhov
664221979aaSDmitry Torokhov mutex_unlock(&udev->mutex);
66529506415SDmitry Torokhov
66629506415SDmitry Torokhov return retval;
6671da177e4SLinus Torvalds }
6681da177e4SLinus Torvalds
uinput_fetch_next_event(struct uinput_device * udev,struct input_event * event)669929d1af5SDmitry Torokhov static bool uinput_fetch_next_event(struct uinput_device *udev,
670929d1af5SDmitry Torokhov struct input_event *event)
671929d1af5SDmitry Torokhov {
672929d1af5SDmitry Torokhov bool have_event;
673929d1af5SDmitry Torokhov
674929d1af5SDmitry Torokhov spin_lock_irq(&udev->dev->event_lock);
675929d1af5SDmitry Torokhov
676929d1af5SDmitry Torokhov have_event = udev->head != udev->tail;
677929d1af5SDmitry Torokhov if (have_event) {
678929d1af5SDmitry Torokhov *event = udev->buff[udev->tail];
679929d1af5SDmitry Torokhov udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
680929d1af5SDmitry Torokhov }
681929d1af5SDmitry Torokhov
682929d1af5SDmitry Torokhov spin_unlock_irq(&udev->dev->event_lock);
683929d1af5SDmitry Torokhov
684929d1af5SDmitry Torokhov return have_event;
685929d1af5SDmitry Torokhov }
686929d1af5SDmitry Torokhov
uinput_events_to_user(struct uinput_device * udev,char __user * buffer,size_t count)68722ae19c6SDmitry Torokhov static ssize_t uinput_events_to_user(struct uinput_device *udev,
68822ae19c6SDmitry Torokhov char __user *buffer, size_t count)
68922ae19c6SDmitry Torokhov {
69022ae19c6SDmitry Torokhov struct input_event event;
69122ae19c6SDmitry Torokhov size_t read = 0;
69222ae19c6SDmitry Torokhov
69322ae19c6SDmitry Torokhov while (read + input_event_size() <= count &&
69422ae19c6SDmitry Torokhov uinput_fetch_next_event(udev, &event)) {
69522ae19c6SDmitry Torokhov
69600ce756cSDmitry Torokhov if (input_event_to_user(buffer + read, &event))
69700ce756cSDmitry Torokhov return -EFAULT;
69822ae19c6SDmitry Torokhov
69922ae19c6SDmitry Torokhov read += input_event_size();
70022ae19c6SDmitry Torokhov }
70122ae19c6SDmitry Torokhov
70200ce756cSDmitry Torokhov return read;
70322ae19c6SDmitry Torokhov }
70422ae19c6SDmitry Torokhov
uinput_read(struct file * file,char __user * buffer,size_t count,loff_t * ppos)70522ae19c6SDmitry Torokhov static ssize_t uinput_read(struct file *file, char __user *buffer,
70622ae19c6SDmitry Torokhov size_t count, loff_t *ppos)
7071da177e4SLinus Torvalds {
7081da177e4SLinus Torvalds struct uinput_device *udev = file->private_data;
70922ae19c6SDmitry Torokhov ssize_t retval;
7101da177e4SLinus Torvalds
711f40033acSDavid Herrmann if (count != 0 && count < input_event_size())
712f40033acSDavid Herrmann return -EINVAL;
713f40033acSDavid Herrmann
71422ae19c6SDmitry Torokhov do {
715221979aaSDmitry Torokhov retval = mutex_lock_interruptible(&udev->mutex);
71629506415SDmitry Torokhov if (retval)
71729506415SDmitry Torokhov return retval;
7181da177e4SLinus Torvalds
71922ae19c6SDmitry Torokhov if (udev->state != UIST_CREATED)
72029506415SDmitry Torokhov retval = -ENODEV;
72122ae19c6SDmitry Torokhov else if (udev->head == udev->tail &&
72222ae19c6SDmitry Torokhov (file->f_flags & O_NONBLOCK))
72322ae19c6SDmitry Torokhov retval = -EAGAIN;
72422ae19c6SDmitry Torokhov else
72522ae19c6SDmitry Torokhov retval = uinput_events_to_user(udev, buffer, count);
72629506415SDmitry Torokhov
727221979aaSDmitry Torokhov mutex_unlock(&udev->mutex);
72829506415SDmitry Torokhov
72922ae19c6SDmitry Torokhov if (retval || count == 0)
73022ae19c6SDmitry Torokhov break;
73122ae19c6SDmitry Torokhov
73222ae19c6SDmitry Torokhov if (!(file->f_flags & O_NONBLOCK))
73322ae19c6SDmitry Torokhov retval = wait_event_interruptible(udev->waitq,
73422ae19c6SDmitry Torokhov udev->head != udev->tail ||
73522ae19c6SDmitry Torokhov udev->state != UIST_CREATED);
73622ae19c6SDmitry Torokhov } while (retval == 0);
73722ae19c6SDmitry Torokhov
7381da177e4SLinus Torvalds return retval;
7391da177e4SLinus Torvalds }
7401da177e4SLinus Torvalds
uinput_poll(struct file * file,poll_table * wait)741afc9a42bSAl Viro static __poll_t uinput_poll(struct file *file, poll_table *wait)
7421da177e4SLinus Torvalds {
7431da177e4SLinus Torvalds struct uinput_device *udev = file->private_data;
744add21809SDmitry Torokhov __poll_t mask = EPOLLOUT | EPOLLWRNORM; /* uinput is always writable */
7451da177e4SLinus Torvalds
7461da177e4SLinus Torvalds poll_wait(file, &udev->waitq, wait);
7471da177e4SLinus Torvalds
7481da177e4SLinus Torvalds if (udev->head != udev->tail)
749add21809SDmitry Torokhov mask |= EPOLLIN | EPOLLRDNORM;
7501da177e4SLinus Torvalds
751add21809SDmitry Torokhov return mask;
7521da177e4SLinus Torvalds }
7531da177e4SLinus Torvalds
uinput_release(struct inode * inode,struct file * file)75429506415SDmitry Torokhov static int uinput_release(struct inode *inode, struct file *file)
7551da177e4SLinus Torvalds {
75629506415SDmitry Torokhov struct uinput_device *udev = file->private_data;
7571da177e4SLinus Torvalds
75829506415SDmitry Torokhov uinput_destroy_device(udev);
7591da177e4SLinus Torvalds kfree(udev);
7601da177e4SLinus Torvalds
7611da177e4SLinus Torvalds return 0;
7621da177e4SLinus Torvalds }
7631da177e4SLinus Torvalds
7642d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
7652d56f3a3SPhilip Langdale struct uinput_ff_upload_compat {
766c5b3533aSDmitry Torokhov __u32 request_id;
767c5b3533aSDmitry Torokhov __s32 retval;
7682d56f3a3SPhilip Langdale struct ff_effect_compat effect;
7692d56f3a3SPhilip Langdale struct ff_effect_compat old;
7702d56f3a3SPhilip Langdale };
7712d56f3a3SPhilip Langdale
uinput_ff_upload_to_user(char __user * buffer,const struct uinput_ff_upload * ff_up)7722d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer,
7732d56f3a3SPhilip Langdale const struct uinput_ff_upload *ff_up)
7742d56f3a3SPhilip Langdale {
775b8b4ead1SAndrew Morton if (in_compat_syscall()) {
7762d56f3a3SPhilip Langdale struct uinput_ff_upload_compat ff_up_compat;
7772d56f3a3SPhilip Langdale
7782d56f3a3SPhilip Langdale ff_up_compat.request_id = ff_up->request_id;
7792d56f3a3SPhilip Langdale ff_up_compat.retval = ff_up->retval;
7802d56f3a3SPhilip Langdale /*
7812d56f3a3SPhilip Langdale * It so happens that the pointer that gives us the trouble
7822d56f3a3SPhilip Langdale * is the last field in the structure. Since we don't support
7832d56f3a3SPhilip Langdale * custom waveforms in uinput anyway we can just copy the whole
7842d56f3a3SPhilip Langdale * thing (to the compat size) and ignore the pointer.
7852d56f3a3SPhilip Langdale */
7862d56f3a3SPhilip Langdale memcpy(&ff_up_compat.effect, &ff_up->effect,
7872d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat));
7882d56f3a3SPhilip Langdale memcpy(&ff_up_compat.old, &ff_up->old,
7892d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat));
7902d56f3a3SPhilip Langdale
7912d56f3a3SPhilip Langdale if (copy_to_user(buffer, &ff_up_compat,
7922d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload_compat)))
7932d56f3a3SPhilip Langdale return -EFAULT;
7942d56f3a3SPhilip Langdale } else {
7952d56f3a3SPhilip Langdale if (copy_to_user(buffer, ff_up,
7962d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload)))
7972d56f3a3SPhilip Langdale return -EFAULT;
7982d56f3a3SPhilip Langdale }
7992d56f3a3SPhilip Langdale
8002d56f3a3SPhilip Langdale return 0;
8012d56f3a3SPhilip Langdale }
8022d56f3a3SPhilip Langdale
uinput_ff_upload_from_user(const char __user * buffer,struct uinput_ff_upload * ff_up)8032d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer,
8042d56f3a3SPhilip Langdale struct uinput_ff_upload *ff_up)
8052d56f3a3SPhilip Langdale {
806b8b4ead1SAndrew Morton if (in_compat_syscall()) {
8072d56f3a3SPhilip Langdale struct uinput_ff_upload_compat ff_up_compat;
8082d56f3a3SPhilip Langdale
8092d56f3a3SPhilip Langdale if (copy_from_user(&ff_up_compat, buffer,
8102d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload_compat)))
8112d56f3a3SPhilip Langdale return -EFAULT;
8122d56f3a3SPhilip Langdale
8132d56f3a3SPhilip Langdale ff_up->request_id = ff_up_compat.request_id;
8142d56f3a3SPhilip Langdale ff_up->retval = ff_up_compat.retval;
8152d56f3a3SPhilip Langdale memcpy(&ff_up->effect, &ff_up_compat.effect,
8162d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat));
8172d56f3a3SPhilip Langdale memcpy(&ff_up->old, &ff_up_compat.old,
8182d56f3a3SPhilip Langdale sizeof(struct ff_effect_compat));
8192d56f3a3SPhilip Langdale
8202d56f3a3SPhilip Langdale } else {
8212d56f3a3SPhilip Langdale if (copy_from_user(ff_up, buffer,
8222d56f3a3SPhilip Langdale sizeof(struct uinput_ff_upload)))
8232d56f3a3SPhilip Langdale return -EFAULT;
8242d56f3a3SPhilip Langdale }
8252d56f3a3SPhilip Langdale
8262d56f3a3SPhilip Langdale return 0;
8272d56f3a3SPhilip Langdale }
8282d56f3a3SPhilip Langdale
8292d56f3a3SPhilip Langdale #else
8302d56f3a3SPhilip Langdale
uinput_ff_upload_to_user(char __user * buffer,const struct uinput_ff_upload * ff_up)8312d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer,
8322d56f3a3SPhilip Langdale const struct uinput_ff_upload *ff_up)
8332d56f3a3SPhilip Langdale {
8342d56f3a3SPhilip Langdale if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload)))
8352d56f3a3SPhilip Langdale return -EFAULT;
8362d56f3a3SPhilip Langdale
8372d56f3a3SPhilip Langdale return 0;
8382d56f3a3SPhilip Langdale }
8392d56f3a3SPhilip Langdale
uinput_ff_upload_from_user(const char __user * buffer,struct uinput_ff_upload * ff_up)8402d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer,
8412d56f3a3SPhilip Langdale struct uinput_ff_upload *ff_up)
8422d56f3a3SPhilip Langdale {
8432d56f3a3SPhilip Langdale if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload)))
8442d56f3a3SPhilip Langdale return -EFAULT;
8452d56f3a3SPhilip Langdale
8462d56f3a3SPhilip Langdale return 0;
8472d56f3a3SPhilip Langdale }
8482d56f3a3SPhilip Langdale
8492d56f3a3SPhilip Langdale #endif
8502d56f3a3SPhilip Langdale
85129506415SDmitry Torokhov #define uinput_set_bit(_arg, _bit, _max) \
85229506415SDmitry Torokhov ({ \
85329506415SDmitry Torokhov int __ret = 0; \
85429506415SDmitry Torokhov if (udev->state == UIST_CREATED) \
85529506415SDmitry Torokhov __ret = -EINVAL; \
85629506415SDmitry Torokhov else if ((_arg) > (_max)) \
85729506415SDmitry Torokhov __ret = -EINVAL; \
85829506415SDmitry Torokhov else set_bit((_arg), udev->dev->_bit); \
85929506415SDmitry Torokhov __ret; \
86029506415SDmitry Torokhov })
8611da177e4SLinus Torvalds
uinput_str_to_user(void __user * dest,const char * str,unsigned int maxlen)862e3480a61SBenjamin Tissoires static int uinput_str_to_user(void __user *dest, const char *str,
863e3480a61SBenjamin Tissoires unsigned int maxlen)
864e3480a61SBenjamin Tissoires {
865e3480a61SBenjamin Tissoires char __user *p = dest;
866e3480a61SBenjamin Tissoires int len, ret;
867e3480a61SBenjamin Tissoires
868e3480a61SBenjamin Tissoires if (!str)
869e3480a61SBenjamin Tissoires return -ENOENT;
870e3480a61SBenjamin Tissoires
871e3480a61SBenjamin Tissoires if (maxlen == 0)
872e3480a61SBenjamin Tissoires return -EINVAL;
873e3480a61SBenjamin Tissoires
874e3480a61SBenjamin Tissoires len = strlen(str) + 1;
875e3480a61SBenjamin Tissoires if (len > maxlen)
876e3480a61SBenjamin Tissoires len = maxlen;
877e3480a61SBenjamin Tissoires
878e3480a61SBenjamin Tissoires ret = copy_to_user(p, str, len);
879e3480a61SBenjamin Tissoires if (ret)
880e3480a61SBenjamin Tissoires return -EFAULT;
881e3480a61SBenjamin Tissoires
882e3480a61SBenjamin Tissoires /* force terminating '\0' */
883e3480a61SBenjamin Tissoires ret = put_user(0, p + len - 1);
884e3480a61SBenjamin Tissoires return ret ? -EFAULT : len;
885e3480a61SBenjamin Tissoires }
886e3480a61SBenjamin Tissoires
uinput_ioctl_handler(struct file * file,unsigned int cmd,unsigned long arg,void __user * p)8872d56f3a3SPhilip Langdale static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
8882d56f3a3SPhilip Langdale unsigned long arg, void __user *p)
8891da177e4SLinus Torvalds {
89029506415SDmitry Torokhov int retval;
8912d56f3a3SPhilip Langdale struct uinput_device *udev = file->private_data;
8921da177e4SLinus Torvalds struct uinput_ff_upload ff_up;
8931da177e4SLinus Torvalds struct uinput_ff_erase ff_erase;
8941da177e4SLinus Torvalds struct uinput_request *req;
8955b6271bdSDmitry Torokhov char *phys;
896e3480a61SBenjamin Tissoires const char *name;
897e3480a61SBenjamin Tissoires unsigned int size;
8981da177e4SLinus Torvalds
899221979aaSDmitry Torokhov retval = mutex_lock_interruptible(&udev->mutex);
90029506415SDmitry Torokhov if (retval)
90129506415SDmitry Torokhov return retval;
90229506415SDmitry Torokhov
90329506415SDmitry Torokhov if (!udev->dev) {
90404ce40a6SDmitry Torokhov udev->dev = input_allocate_device();
905781f2dd0SDan Carpenter if (!udev->dev) {
906781f2dd0SDan Carpenter retval = -ENOMEM;
907781f2dd0SDan Carpenter goto out;
908781f2dd0SDan Carpenter }
9091da177e4SLinus Torvalds }
9101da177e4SLinus Torvalds
9111da177e4SLinus Torvalds switch (cmd) {
912ba4e9a61SDavid Herrmann case UI_GET_VERSION:
913c0661652SDmitry Torokhov if (put_user(UINPUT_VERSION, (unsigned int __user *)p))
914ba4e9a61SDavid Herrmann retval = -EFAULT;
915ba4e9a61SDavid Herrmann goto out;
916ba4e9a61SDavid Herrmann
9171da177e4SLinus Torvalds case UI_DEV_CREATE:
9181da177e4SLinus Torvalds retval = uinput_create_device(udev);
9199d51e801SBenjamin Tisssoires goto out;
9201da177e4SLinus Torvalds
9211da177e4SLinus Torvalds case UI_DEV_DESTROY:
92229506415SDmitry Torokhov uinput_destroy_device(udev);
9239d51e801SBenjamin Tisssoires goto out;
9241da177e4SLinus Torvalds
925052876f8SBenjamin Tissoires case UI_DEV_SETUP:
926052876f8SBenjamin Tissoires retval = uinput_dev_setup(udev, p);
927052876f8SBenjamin Tissoires goto out;
928052876f8SBenjamin Tissoires
929052876f8SBenjamin Tissoires /* UI_ABS_SETUP is handled in the variable size ioctls */
930052876f8SBenjamin Tissoires
9311da177e4SLinus Torvalds case UI_SET_EVBIT:
93229506415SDmitry Torokhov retval = uinput_set_bit(arg, evbit, EV_MAX);
9339d51e801SBenjamin Tisssoires goto out;
9341da177e4SLinus Torvalds
9351da177e4SLinus Torvalds case UI_SET_KEYBIT:
93629506415SDmitry Torokhov retval = uinput_set_bit(arg, keybit, KEY_MAX);
9379d51e801SBenjamin Tisssoires goto out;
9381da177e4SLinus Torvalds
9391da177e4SLinus Torvalds case UI_SET_RELBIT:
94029506415SDmitry Torokhov retval = uinput_set_bit(arg, relbit, REL_MAX);
9419d51e801SBenjamin Tisssoires goto out;
9421da177e4SLinus Torvalds
9431da177e4SLinus Torvalds case UI_SET_ABSBIT:
94429506415SDmitry Torokhov retval = uinput_set_bit(arg, absbit, ABS_MAX);
9459d51e801SBenjamin Tisssoires goto out;
9461da177e4SLinus Torvalds
9471da177e4SLinus Torvalds case UI_SET_MSCBIT:
94829506415SDmitry Torokhov retval = uinput_set_bit(arg, mscbit, MSC_MAX);
9499d51e801SBenjamin Tisssoires goto out;
9501da177e4SLinus Torvalds
9511da177e4SLinus Torvalds case UI_SET_LEDBIT:
95229506415SDmitry Torokhov retval = uinput_set_bit(arg, ledbit, LED_MAX);
9539d51e801SBenjamin Tisssoires goto out;
9541da177e4SLinus Torvalds
9551da177e4SLinus Torvalds case UI_SET_SNDBIT:
95629506415SDmitry Torokhov retval = uinput_set_bit(arg, sndbit, SND_MAX);
9579d51e801SBenjamin Tisssoires goto out;
9581da177e4SLinus Torvalds
9591da177e4SLinus Torvalds case UI_SET_FFBIT:
96029506415SDmitry Torokhov retval = uinput_set_bit(arg, ffbit, FF_MAX);
9619d51e801SBenjamin Tisssoires goto out;
9621da177e4SLinus Torvalds
96359c7c037SDmitry Torokhov case UI_SET_SWBIT:
96459c7c037SDmitry Torokhov retval = uinput_set_bit(arg, swbit, SW_MAX);
9659d51e801SBenjamin Tisssoires goto out;
96659c7c037SDmitry Torokhov
96785b77200SHenrik Rydberg case UI_SET_PROPBIT:
96885b77200SHenrik Rydberg retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX);
9699d51e801SBenjamin Tisssoires goto out;
97085b77200SHenrik Rydberg
9711da177e4SLinus Torvalds case UI_SET_PHYS:
97229506415SDmitry Torokhov if (udev->state == UIST_CREATED) {
97329506415SDmitry Torokhov retval = -EINVAL;
97429506415SDmitry Torokhov goto out;
97529506415SDmitry Torokhov }
9764dfcc271SDmitry Torokhov
9774dfcc271SDmitry Torokhov phys = strndup_user(p, 1024);
9784dfcc271SDmitry Torokhov if (IS_ERR(phys)) {
9794dfcc271SDmitry Torokhov retval = PTR_ERR(phys);
9804dfcc271SDmitry Torokhov goto out;
9811da177e4SLinus Torvalds }
9824dfcc271SDmitry Torokhov
9831da177e4SLinus Torvalds kfree(udev->dev->phys);
9844dfcc271SDmitry Torokhov udev->dev->phys = phys;
9859d51e801SBenjamin Tisssoires goto out;
9861da177e4SLinus Torvalds
9871da177e4SLinus Torvalds case UI_BEGIN_FF_UPLOAD:
9882d56f3a3SPhilip Langdale retval = uinput_ff_upload_from_user(p, &ff_up);
9892d56f3a3SPhilip Langdale if (retval)
9909d51e801SBenjamin Tisssoires goto out;
9912d56f3a3SPhilip Langdale
9921da177e4SLinus Torvalds req = uinput_request_find(udev, ff_up.request_id);
99354ce165eSDmitry Torokhov if (!req || req->code != UI_FF_UPLOAD ||
99454ce165eSDmitry Torokhov !req->u.upload.effect) {
9951da177e4SLinus Torvalds retval = -EINVAL;
9969d51e801SBenjamin Tisssoires goto out;
9971da177e4SLinus Torvalds }
9982d56f3a3SPhilip Langdale
9991da177e4SLinus Torvalds ff_up.retval = 0;
10002d56f3a3SPhilip Langdale ff_up.effect = *req->u.upload.effect;
1001ff462551SAnssi Hannula if (req->u.upload.old)
10022d56f3a3SPhilip Langdale ff_up.old = *req->u.upload.old;
1003ff462551SAnssi Hannula else
1004ff462551SAnssi Hannula memset(&ff_up.old, 0, sizeof(struct ff_effect));
1005ff462551SAnssi Hannula
10062d56f3a3SPhilip Langdale retval = uinput_ff_upload_to_user(p, &ff_up);
10079d51e801SBenjamin Tisssoires goto out;
10081da177e4SLinus Torvalds
10091da177e4SLinus Torvalds case UI_BEGIN_FF_ERASE:
10101da177e4SLinus Torvalds if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
10111da177e4SLinus Torvalds retval = -EFAULT;
10129d51e801SBenjamin Tisssoires goto out;
10131da177e4SLinus Torvalds }
10142d56f3a3SPhilip Langdale
10151da177e4SLinus Torvalds req = uinput_request_find(udev, ff_erase.request_id);
10162d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_ERASE) {
10171da177e4SLinus Torvalds retval = -EINVAL;
10189d51e801SBenjamin Tisssoires goto out;
10191da177e4SLinus Torvalds }
10202d56f3a3SPhilip Langdale
10211da177e4SLinus Torvalds ff_erase.retval = 0;
10221da177e4SLinus Torvalds ff_erase.effect_id = req->u.effect_id;
10231da177e4SLinus Torvalds if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
10241da177e4SLinus Torvalds retval = -EFAULT;
10259d51e801SBenjamin Tisssoires goto out;
10261da177e4SLinus Torvalds }
10272d56f3a3SPhilip Langdale
10289d51e801SBenjamin Tisssoires goto out;
10291da177e4SLinus Torvalds
10301da177e4SLinus Torvalds case UI_END_FF_UPLOAD:
10312d56f3a3SPhilip Langdale retval = uinput_ff_upload_from_user(p, &ff_up);
10322d56f3a3SPhilip Langdale if (retval)
10339d51e801SBenjamin Tisssoires goto out;
10342d56f3a3SPhilip Langdale
10351da177e4SLinus Torvalds req = uinput_request_find(udev, ff_up.request_id);
10362d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_UPLOAD ||
10372d56f3a3SPhilip Langdale !req->u.upload.effect) {
10381da177e4SLinus Torvalds retval = -EINVAL;
10399d51e801SBenjamin Tisssoires goto out;
10401da177e4SLinus Torvalds }
10412d56f3a3SPhilip Langdale
10421da177e4SLinus Torvalds req->retval = ff_up.retval;
10436b4877c7SDmitry Torokhov complete(&req->done);
10449d51e801SBenjamin Tisssoires goto out;
10451da177e4SLinus Torvalds
10461da177e4SLinus Torvalds case UI_END_FF_ERASE:
10471da177e4SLinus Torvalds if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
10481da177e4SLinus Torvalds retval = -EFAULT;
10499d51e801SBenjamin Tisssoires goto out;
10501da177e4SLinus Torvalds }
10512d56f3a3SPhilip Langdale
10521da177e4SLinus Torvalds req = uinput_request_find(udev, ff_erase.request_id);
10532d56f3a3SPhilip Langdale if (!req || req->code != UI_FF_ERASE) {
10541da177e4SLinus Torvalds retval = -EINVAL;
10559d51e801SBenjamin Tisssoires goto out;
10561da177e4SLinus Torvalds }
10572d56f3a3SPhilip Langdale
10581da177e4SLinus Torvalds req->retval = ff_erase.retval;
10596b4877c7SDmitry Torokhov complete(&req->done);
10609d51e801SBenjamin Tisssoires goto out;
10611da177e4SLinus Torvalds }
106229506415SDmitry Torokhov
1063e3480a61SBenjamin Tissoires size = _IOC_SIZE(cmd);
1064e3480a61SBenjamin Tissoires
1065e3480a61SBenjamin Tissoires /* Now check variable-length commands */
1066e3480a61SBenjamin Tissoires switch (cmd & ~IOCSIZE_MASK) {
1067e3480a61SBenjamin Tissoires case UI_GET_SYSNAME(0):
1068e3480a61SBenjamin Tissoires if (udev->state != UIST_CREATED) {
1069e3480a61SBenjamin Tissoires retval = -ENOENT;
1070e3480a61SBenjamin Tissoires goto out;
1071e3480a61SBenjamin Tissoires }
1072e3480a61SBenjamin Tissoires name = dev_name(&udev->dev->dev);
1073e3480a61SBenjamin Tissoires retval = uinput_str_to_user(p, name, size);
1074e3480a61SBenjamin Tissoires goto out;
1075052876f8SBenjamin Tissoires
1076052876f8SBenjamin Tissoires case UI_ABS_SETUP & ~IOCSIZE_MASK:
1077052876f8SBenjamin Tissoires retval = uinput_abs_setup(udev, p, size);
1078052876f8SBenjamin Tissoires goto out;
1079e3480a61SBenjamin Tissoires }
1080e3480a61SBenjamin Tissoires
10819d51e801SBenjamin Tisssoires retval = -EINVAL;
108229506415SDmitry Torokhov out:
1083221979aaSDmitry Torokhov mutex_unlock(&udev->mutex);
10841da177e4SLinus Torvalds return retval;
10851da177e4SLinus Torvalds }
10861da177e4SLinus Torvalds
uinput_ioctl(struct file * file,unsigned int cmd,unsigned long arg)10872d56f3a3SPhilip Langdale static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
10882d56f3a3SPhilip Langdale {
10892d56f3a3SPhilip Langdale return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg);
10902d56f3a3SPhilip Langdale }
10912d56f3a3SPhilip Langdale
10922d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
1093affa80bdSRicky Liang
10947c7da40dSAndrey Smirnov /*
10957c7da40dSAndrey Smirnov * These IOCTLs change their size and thus their numbers between
10967c7da40dSAndrey Smirnov * 32 and 64 bits.
10977c7da40dSAndrey Smirnov */
10987c7da40dSAndrey Smirnov #define UI_SET_PHYS_COMPAT \
10997c7da40dSAndrey Smirnov _IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t)
11007c7da40dSAndrey Smirnov #define UI_BEGIN_FF_UPLOAD_COMPAT \
11017c7da40dSAndrey Smirnov _IOWR(UINPUT_IOCTL_BASE, 200, struct uinput_ff_upload_compat)
11027c7da40dSAndrey Smirnov #define UI_END_FF_UPLOAD_COMPAT \
11037c7da40dSAndrey Smirnov _IOW(UINPUT_IOCTL_BASE, 201, struct uinput_ff_upload_compat)
1104affa80bdSRicky Liang
uinput_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)110554ce165eSDmitry Torokhov static long uinput_compat_ioctl(struct file *file,
110654ce165eSDmitry Torokhov unsigned int cmd, unsigned long arg)
11072d56f3a3SPhilip Langdale {
11087c7da40dSAndrey Smirnov switch (cmd) {
11097c7da40dSAndrey Smirnov case UI_SET_PHYS_COMPAT:
1110affa80bdSRicky Liang cmd = UI_SET_PHYS;
11117c7da40dSAndrey Smirnov break;
11127c7da40dSAndrey Smirnov case UI_BEGIN_FF_UPLOAD_COMPAT:
11137c7da40dSAndrey Smirnov cmd = UI_BEGIN_FF_UPLOAD;
11147c7da40dSAndrey Smirnov break;
11157c7da40dSAndrey Smirnov case UI_END_FF_UPLOAD_COMPAT:
11167c7da40dSAndrey Smirnov cmd = UI_END_FF_UPLOAD;
11177c7da40dSAndrey Smirnov break;
11187c7da40dSAndrey Smirnov }
1119affa80bdSRicky Liang
11202d56f3a3SPhilip Langdale return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
11212d56f3a3SPhilip Langdale }
11222d56f3a3SPhilip Langdale #endif
11232d56f3a3SPhilip Langdale
11242b8693c0SArjan van de Ven static const struct file_operations uinput_fops = {
11251da177e4SLinus Torvalds .owner = THIS_MODULE,
11261da177e4SLinus Torvalds .open = uinput_open,
112729506415SDmitry Torokhov .release = uinput_release,
11281da177e4SLinus Torvalds .read = uinput_read,
11291da177e4SLinus Torvalds .write = uinput_write,
11301da177e4SLinus Torvalds .poll = uinput_poll,
113129506415SDmitry Torokhov .unlocked_ioctl = uinput_ioctl,
11322d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
11332d56f3a3SPhilip Langdale .compat_ioctl = uinput_compat_ioctl,
11342d56f3a3SPhilip Langdale #endif
11351da177e4SLinus Torvalds };
11361da177e4SLinus Torvalds
11371da177e4SLinus Torvalds static struct miscdevice uinput_misc = {
11381da177e4SLinus Torvalds .fops = &uinput_fops,
11391da177e4SLinus Torvalds .minor = UINPUT_MINOR,
11401da177e4SLinus Torvalds .name = UINPUT_NAME,
11411da177e4SLinus Torvalds };
1142ca75d601SPrasannaKumar Muralidharan module_misc_device(uinput_misc);
1143ca75d601SPrasannaKumar Muralidharan
11448905aaafSKay Sievers MODULE_ALIAS_MISCDEV(UINPUT_MINOR);
11458905aaafSKay Sievers MODULE_ALIAS("devname:" UINPUT_NAME);
11461da177e4SLinus Torvalds
11471da177e4SLinus Torvalds MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
11481da177e4SLinus Torvalds MODULE_DESCRIPTION("User level driver support for input subsystem");
11491da177e4SLinus Torvalds MODULE_LICENSE("GPL");
1150