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