xref: /linux-6.15/drivers/input/misc/uinput.c (revision cb787f4a)
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