xref: /openbmc/linux/drivers/input/misc/uinput.c (revision 163d2770)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  User level driver support for input subsystem
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Heavily based on evdev.c by Vojtech Pavlik
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify
71da177e4SLinus Torvalds  * it under the terms of the GNU General Public License as published by
81da177e4SLinus Torvalds  * the Free Software Foundation; either version 2 of the License, or
91da177e4SLinus Torvalds  * (at your option) any later version.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * This program is distributed in the hope that it will be useful,
121da177e4SLinus Torvalds  * but WITHOUT ANY WARRANTY; without even the implied warranty of
131da177e4SLinus Torvalds  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
141da177e4SLinus Torvalds  * GNU General Public License for more details.
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  * You should have received a copy of the GNU General Public License
171da177e4SLinus Torvalds  * along with this program; if not, write to the Free Software
181da177e4SLinus Torvalds  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
211da177e4SLinus Torvalds  *
221da177e4SLinus Torvalds  * Changes/Revisions:
23ff462551SAnssi Hannula  *	0.3	09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
24ff462551SAnssi Hannula  *		- updated ff support for the changes in kernel interface
25ff462551SAnssi Hannula  *		- added MODULE_VERSION
261da177e4SLinus Torvalds  *	0.2	16/10/2004 (Micah Dowty <micah@navi.cx>)
271da177e4SLinus Torvalds  *		- added force feedback support
281da177e4SLinus Torvalds  *              - added UI_SET_PHYS
291da177e4SLinus Torvalds  *	0.1	20/06/2002
301da177e4SLinus Torvalds  *		- first public version
311da177e4SLinus Torvalds  */
321da177e4SLinus Torvalds #include <linux/poll.h>
33a99bbaf5SAlexey Dobriyan #include <linux/sched.h>
341da177e4SLinus Torvalds #include <linux/slab.h>
351da177e4SLinus Torvalds #include <linux/module.h>
361da177e4SLinus Torvalds #include <linux/init.h>
371da177e4SLinus Torvalds #include <linux/fs.h>
381da177e4SLinus Torvalds #include <linux/miscdevice.h>
391da177e4SLinus Torvalds #include <linux/uinput.h>
4047c78e89SHenrik Rydberg #include <linux/input/mt.h>
412d56f3a3SPhilip Langdale #include "../input-compat.h"
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
441da177e4SLinus Torvalds {
45373f9713SDmitry Torokhov 	struct uinput_device	*udev = input_get_drvdata(dev);
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds 	udev->buff[udev->head].type = type;
481da177e4SLinus Torvalds 	udev->buff[udev->head].code = code;
491da177e4SLinus Torvalds 	udev->buff[udev->head].value = value;
501da177e4SLinus Torvalds 	do_gettimeofday(&udev->buff[udev->head].time);
511da177e4SLinus Torvalds 	udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds 	wake_up_interruptible(&udev->waitq);
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 	return 0;
561da177e4SLinus Torvalds }
571da177e4SLinus Torvalds 
5805cebd38SAristeu Sergio Rozanski Filho /* Atomically allocate an ID for the given request. Returns 0 on success. */
590048e603SDmitry Torokhov static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request)
601da177e4SLinus Torvalds {
611da177e4SLinus Torvalds 	int id;
62152c12f5SDmitry Torokhov 	int err = -1;
631da177e4SLinus Torvalds 
640048e603SDmitry Torokhov 	spin_lock(&udev->requests_lock);
65152c12f5SDmitry Torokhov 
6605cebd38SAristeu Sergio Rozanski Filho 	for (id = 0; id < UINPUT_NUM_REQUESTS; id++) {
671da177e4SLinus Torvalds 		if (!udev->requests[id]) {
681da177e4SLinus Torvalds 			request->id = id;
690048e603SDmitry Torokhov 			udev->requests[id] = request;
70152c12f5SDmitry Torokhov 			err = 0;
71152c12f5SDmitry Torokhov 			break;
721da177e4SLinus Torvalds 		}
7305cebd38SAristeu Sergio Rozanski Filho 	}
74152c12f5SDmitry Torokhov 
750048e603SDmitry Torokhov 	spin_unlock(&udev->requests_lock);
76152c12f5SDmitry Torokhov 	return err;
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds static struct uinput_request *uinput_request_find(struct uinput_device *udev, int id)
801da177e4SLinus Torvalds {
811da177e4SLinus Torvalds 	/* Find an input request, by ID. Returns NULL if the ID isn't valid. */
821da177e4SLinus Torvalds 	if (id >= UINPUT_NUM_REQUESTS || id < 0)
831da177e4SLinus Torvalds 		return NULL;
842d56f3a3SPhilip Langdale 
851da177e4SLinus Torvalds 	return udev->requests[id];
861da177e4SLinus Torvalds }
871da177e4SLinus Torvalds 
880048e603SDmitry Torokhov static inline int uinput_request_reserve_slot(struct uinput_device *udev, struct uinput_request *request)
891da177e4SLinus Torvalds {
900048e603SDmitry Torokhov 	/* Allocate slot. If none are available right away, wait. */
910048e603SDmitry Torokhov 	return wait_event_interruptible(udev->requests_waitq,
920048e603SDmitry Torokhov 					!uinput_request_alloc_id(udev, request));
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds 
950048e603SDmitry Torokhov static void uinput_request_done(struct uinput_device *udev, struct uinput_request *request)
961da177e4SLinus Torvalds {
970048e603SDmitry Torokhov 	/* Mark slot as available */
980048e603SDmitry Torokhov 	udev->requests[request->id] = NULL;
99e597f0c8SDmitry Torokhov 	wake_up(&udev->requests_waitq);
100e7507ed9SDmitry Torokhov 
101e7507ed9SDmitry Torokhov 	complete(&request->done);
1020048e603SDmitry Torokhov }
1030048e603SDmitry Torokhov 
10405cebd38SAristeu Sergio Rozanski Filho static int uinput_request_submit(struct uinput_device *udev, struct uinput_request *request)
1050048e603SDmitry Torokhov {
10605cebd38SAristeu Sergio Rozanski Filho 	int retval;
1071da177e4SLinus Torvalds 
10805cebd38SAristeu Sergio Rozanski Filho 	retval = uinput_request_reserve_slot(udev, request);
10905cebd38SAristeu Sergio Rozanski Filho 	if (retval)
11005cebd38SAristeu Sergio Rozanski Filho 		return retval;
11105cebd38SAristeu Sergio Rozanski Filho 
11205cebd38SAristeu Sergio Rozanski Filho 	retval = mutex_lock_interruptible(&udev->mutex);
11305cebd38SAristeu Sergio Rozanski Filho 	if (retval)
11405cebd38SAristeu Sergio Rozanski Filho 		return retval;
11505cebd38SAristeu Sergio Rozanski Filho 
11605cebd38SAristeu Sergio Rozanski Filho 	if (udev->state != UIST_CREATED) {
11705cebd38SAristeu Sergio Rozanski Filho 		retval = -ENODEV;
11805cebd38SAristeu Sergio Rozanski Filho 		goto out;
11905cebd38SAristeu Sergio Rozanski Filho 	}
12005cebd38SAristeu Sergio Rozanski Filho 
12105cebd38SAristeu Sergio Rozanski Filho 	/* Tell our userspace app about this new request by queueing an input event */
12205cebd38SAristeu Sergio Rozanski Filho 	uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id);
12305cebd38SAristeu Sergio Rozanski Filho 
12405cebd38SAristeu Sergio Rozanski Filho  out:
12505cebd38SAristeu Sergio Rozanski Filho 	mutex_unlock(&udev->mutex);
12605cebd38SAristeu Sergio Rozanski Filho 	return retval;
12705cebd38SAristeu Sergio Rozanski Filho }
12805cebd38SAristeu Sergio Rozanski Filho 
12905cebd38SAristeu Sergio Rozanski Filho /*
13005cebd38SAristeu Sergio Rozanski Filho  * Fail all ouitstanding requests so handlers don't wait for the userspace
13105cebd38SAristeu Sergio Rozanski Filho  * to finish processing them.
13205cebd38SAristeu Sergio Rozanski Filho  */
13305cebd38SAristeu Sergio Rozanski Filho static void uinput_flush_requests(struct uinput_device *udev)
13405cebd38SAristeu Sergio Rozanski Filho {
13505cebd38SAristeu Sergio Rozanski Filho 	struct uinput_request *request;
13605cebd38SAristeu Sergio Rozanski Filho 	int i;
13705cebd38SAristeu Sergio Rozanski Filho 
13805cebd38SAristeu Sergio Rozanski Filho 	spin_lock(&udev->requests_lock);
13905cebd38SAristeu Sergio Rozanski Filho 
14005cebd38SAristeu Sergio Rozanski Filho 	for (i = 0; i < UINPUT_NUM_REQUESTS; i++) {
14105cebd38SAristeu Sergio Rozanski Filho 		request = udev->requests[i];
14205cebd38SAristeu Sergio Rozanski Filho 		if (request) {
14305cebd38SAristeu Sergio Rozanski Filho 			request->retval = -ENODEV;
14405cebd38SAristeu Sergio Rozanski Filho 			uinput_request_done(udev, request);
14505cebd38SAristeu Sergio Rozanski Filho 		}
14605cebd38SAristeu Sergio Rozanski Filho 	}
14705cebd38SAristeu Sergio Rozanski Filho 
14805cebd38SAristeu Sergio Rozanski Filho 	spin_unlock(&udev->requests_lock);
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds 
151ff462551SAnssi Hannula static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
152ff462551SAnssi Hannula {
153ff462551SAnssi Hannula 	uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
154ff462551SAnssi Hannula }
155ff462551SAnssi Hannula 
156ff462551SAnssi Hannula static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude)
157ff462551SAnssi Hannula {
158ff462551SAnssi Hannula 	uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);
159ff462551SAnssi Hannula }
160ff462551SAnssi Hannula 
161ff462551SAnssi Hannula static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
162ff462551SAnssi Hannula {
163ff462551SAnssi Hannula 	return uinput_dev_event(dev, EV_FF, effect_id, value);
164ff462551SAnssi Hannula }
165ff462551SAnssi Hannula 
166ff462551SAnssi Hannula static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
1671da177e4SLinus Torvalds {
16805cebd38SAristeu Sergio Rozanski Filho 	struct uinput_device *udev = input_get_drvdata(dev);
1691da177e4SLinus Torvalds 	struct uinput_request request;
1700048e603SDmitry Torokhov 	int retval;
1711da177e4SLinus Torvalds 
1722d56f3a3SPhilip Langdale 	/*
1732d56f3a3SPhilip Langdale 	 * uinput driver does not currently support periodic effects with
1742d56f3a3SPhilip Langdale 	 * custom waveform since it does not have a way to pass buffer of
1752d56f3a3SPhilip Langdale 	 * samples (custom_data) to userspace. If ever there is a device
1762d56f3a3SPhilip Langdale 	 * supporting custom waveforms we would need to define an additional
1772d56f3a3SPhilip Langdale 	 * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out.
1782d56f3a3SPhilip Langdale 	 */
1792d56f3a3SPhilip Langdale 	if (effect->type == FF_PERIODIC &&
1802d56f3a3SPhilip Langdale 			effect->u.periodic.waveform == FF_CUSTOM)
1812d56f3a3SPhilip Langdale 		return -EINVAL;
1822d56f3a3SPhilip Langdale 
1830048e603SDmitry Torokhov 	request.id = -1;
1840048e603SDmitry Torokhov 	init_completion(&request.done);
1850048e603SDmitry Torokhov 	request.code = UI_FF_UPLOAD;
186ff462551SAnssi Hannula 	request.u.upload.effect = effect;
187ff462551SAnssi Hannula 	request.u.upload.old = old;
1880048e603SDmitry Torokhov 
18905cebd38SAristeu Sergio Rozanski Filho 	retval = uinput_request_submit(udev, &request);
19005cebd38SAristeu Sergio Rozanski Filho 	if (!retval) {
19105cebd38SAristeu Sergio Rozanski Filho 		wait_for_completion(&request.done);
19205cebd38SAristeu Sergio Rozanski Filho 		retval = request.retval;
19305cebd38SAristeu Sergio Rozanski Filho 	}
1940048e603SDmitry Torokhov 
1950048e603SDmitry Torokhov 	return retval;
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
1991da177e4SLinus Torvalds {
20005cebd38SAristeu Sergio Rozanski Filho 	struct uinput_device *udev = input_get_drvdata(dev);
2011da177e4SLinus Torvalds 	struct uinput_request request;
2020048e603SDmitry Torokhov 	int retval;
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	if (!test_bit(EV_FF, dev->evbit))
2051da177e4SLinus Torvalds 		return -ENOSYS;
2061da177e4SLinus Torvalds 
2070048e603SDmitry Torokhov 	request.id = -1;
2080048e603SDmitry Torokhov 	init_completion(&request.done);
2090048e603SDmitry Torokhov 	request.code = UI_FF_ERASE;
2101da177e4SLinus Torvalds 	request.u.effect_id = effect_id;
2110048e603SDmitry Torokhov 
21205cebd38SAristeu Sergio Rozanski Filho 	retval = uinput_request_submit(udev, &request);
21305cebd38SAristeu Sergio Rozanski Filho 	if (!retval) {
21405cebd38SAristeu Sergio Rozanski Filho 		wait_for_completion(&request.done);
21505cebd38SAristeu Sergio Rozanski Filho 		retval = request.retval;
21605cebd38SAristeu Sergio Rozanski Filho 	}
2170048e603SDmitry Torokhov 
2180048e603SDmitry Torokhov 	return retval;
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds 
22129506415SDmitry Torokhov static void uinput_destroy_device(struct uinput_device *udev)
22229506415SDmitry Torokhov {
22329506415SDmitry Torokhov 	const char *name, *phys;
22405cebd38SAristeu Sergio Rozanski Filho 	struct input_dev *dev = udev->dev;
22505cebd38SAristeu Sergio Rozanski Filho 	enum uinput_state old_state = udev->state;
22629506415SDmitry Torokhov 
22705cebd38SAristeu Sergio Rozanski Filho 	udev->state = UIST_NEW_DEVICE;
22805cebd38SAristeu Sergio Rozanski Filho 
22905cebd38SAristeu Sergio Rozanski Filho 	if (dev) {
23005cebd38SAristeu Sergio Rozanski Filho 		name = dev->name;
23105cebd38SAristeu Sergio Rozanski Filho 		phys = dev->phys;
23205cebd38SAristeu Sergio Rozanski Filho 		if (old_state == UIST_CREATED) {
23305cebd38SAristeu Sergio Rozanski Filho 			uinput_flush_requests(udev);
23405cebd38SAristeu Sergio Rozanski Filho 			input_unregister_device(dev);
23505cebd38SAristeu Sergio Rozanski Filho 		} else {
23605cebd38SAristeu Sergio Rozanski Filho 			input_free_device(dev);
23705cebd38SAristeu Sergio Rozanski Filho 		}
23829506415SDmitry Torokhov 		kfree(name);
23929506415SDmitry Torokhov 		kfree(phys);
24029506415SDmitry Torokhov 		udev->dev = NULL;
24129506415SDmitry Torokhov 	}
24229506415SDmitry Torokhov }
24329506415SDmitry Torokhov 
2441da177e4SLinus Torvalds static int uinput_create_device(struct uinput_device *udev)
2451da177e4SLinus Torvalds {
246ff462551SAnssi Hannula 	struct input_dev *dev = udev->dev;
24729506415SDmitry Torokhov 	int error;
24829506415SDmitry Torokhov 
24929506415SDmitry Torokhov 	if (udev->state != UIST_SETUP_COMPLETE) {
2501da177e4SLinus Torvalds 		printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
2511da177e4SLinus Torvalds 		return -EINVAL;
2521da177e4SLinus Torvalds 	}
2531da177e4SLinus Torvalds 
254ff462551SAnssi Hannula 	if (udev->ff_effects_max) {
255ff462551SAnssi Hannula 		error = input_ff_create(dev, udev->ff_effects_max);
256ff462551SAnssi Hannula 		if (error)
257ff462551SAnssi Hannula 			goto fail1;
258ff462551SAnssi Hannula 
259ff462551SAnssi Hannula 		dev->ff->upload = uinput_dev_upload_effect;
260ff462551SAnssi Hannula 		dev->ff->erase = uinput_dev_erase_effect;
261ff462551SAnssi Hannula 		dev->ff->playback = uinput_dev_playback;
262ff462551SAnssi Hannula 		dev->ff->set_gain = uinput_dev_set_gain;
263ff462551SAnssi Hannula 		dev->ff->set_autocenter = uinput_dev_set_autocenter;
2641da177e4SLinus Torvalds 	}
2651da177e4SLinus Torvalds 
266ff462551SAnssi Hannula 	error = input_register_device(udev->dev);
267ff462551SAnssi Hannula 	if (error)
268ff462551SAnssi Hannula 		goto fail2;
269ff462551SAnssi Hannula 
27029506415SDmitry Torokhov 	udev->state = UIST_CREATED;
2711da177e4SLinus Torvalds 
2721da177e4SLinus Torvalds 	return 0;
273ff462551SAnssi Hannula 
274ff462551SAnssi Hannula  fail2:	input_ff_destroy(dev);
275ff462551SAnssi Hannula  fail1: uinput_destroy_device(udev);
276ff462551SAnssi Hannula 	return error;
2771da177e4SLinus Torvalds }
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds static int uinput_open(struct inode *inode, struct file *file)
2801da177e4SLinus Torvalds {
2811da177e4SLinus Torvalds 	struct uinput_device *newdev;
2821da177e4SLinus Torvalds 
28329506415SDmitry Torokhov 	newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
2841da177e4SLinus Torvalds 	if (!newdev)
28529506415SDmitry Torokhov 		return -ENOMEM;
28629506415SDmitry Torokhov 
287221979aaSDmitry Torokhov 	mutex_init(&newdev->mutex);
2880048e603SDmitry Torokhov 	spin_lock_init(&newdev->requests_lock);
2891da177e4SLinus Torvalds 	init_waitqueue_head(&newdev->requests_waitq);
29029506415SDmitry Torokhov 	init_waitqueue_head(&newdev->waitq);
29129506415SDmitry Torokhov 	newdev->state = UIST_NEW_DEVICE;
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds 	file->private_data = newdev;
294daf8a96bSDmitry Torokhov 	nonseekable_open(inode, file);
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds 	return 0;
2971da177e4SLinus Torvalds }
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds static int uinput_validate_absbits(struct input_dev *dev)
3001da177e4SLinus Torvalds {
3011da177e4SLinus Torvalds 	unsigned int cnt;
3021da177e4SLinus Torvalds 	int retval = 0;
3031da177e4SLinus Torvalds 
30481c2a3baSDaniel Mack 	for (cnt = 0; cnt < ABS_CNT; cnt++) {
3051da177e4SLinus Torvalds 		if (!test_bit(cnt, dev->absbit))
3061da177e4SLinus Torvalds 			continue;
3071da177e4SLinus Torvalds 
308987a6c02SDaniel Mack 		if (input_abs_get_max(dev, cnt) <= input_abs_get_min(dev, cnt)) {
3091da177e4SLinus Torvalds 			printk(KERN_DEBUG
3101da177e4SLinus Torvalds 				"%s: invalid abs[%02x] min:%d max:%d\n",
3111da177e4SLinus Torvalds 				UINPUT_NAME, cnt,
312987a6c02SDaniel Mack 				input_abs_get_min(dev, cnt),
313987a6c02SDaniel Mack 				input_abs_get_max(dev, cnt));
3141da177e4SLinus Torvalds 			retval = -EINVAL;
3151da177e4SLinus Torvalds 			break;
3161da177e4SLinus Torvalds 		}
3171da177e4SLinus Torvalds 
318987a6c02SDaniel Mack 		if (input_abs_get_flat(dev, cnt) >
319987a6c02SDaniel Mack 		    input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) {
3201da177e4SLinus Torvalds 			printk(KERN_DEBUG
321987a6c02SDaniel Mack 				"%s: abs_flat #%02x out of range: %d "
3221da177e4SLinus Torvalds 				"(min:%d/max:%d)\n",
323987a6c02SDaniel Mack 				UINPUT_NAME, cnt,
324987a6c02SDaniel Mack 				input_abs_get_flat(dev, cnt),
325987a6c02SDaniel Mack 				input_abs_get_min(dev, cnt),
326987a6c02SDaniel Mack 				input_abs_get_max(dev, cnt));
3271da177e4SLinus Torvalds 			retval = -EINVAL;
3281da177e4SLinus Torvalds 			break;
3291da177e4SLinus Torvalds 		}
3301da177e4SLinus Torvalds 	}
3311da177e4SLinus Torvalds 	return retval;
3321da177e4SLinus Torvalds }
3331da177e4SLinus Torvalds 
33429506415SDmitry Torokhov static int uinput_allocate_device(struct uinput_device *udev)
33529506415SDmitry Torokhov {
33629506415SDmitry Torokhov 	udev->dev = input_allocate_device();
33729506415SDmitry Torokhov 	if (!udev->dev)
33829506415SDmitry Torokhov 		return -ENOMEM;
33929506415SDmitry Torokhov 
34029506415SDmitry Torokhov 	udev->dev->event = uinput_dev_event;
341373f9713SDmitry Torokhov 	input_set_drvdata(udev->dev, udev);
34229506415SDmitry Torokhov 
34329506415SDmitry Torokhov 	return 0;
34429506415SDmitry Torokhov }
34529506415SDmitry Torokhov 
34629506415SDmitry Torokhov static int uinput_setup_device(struct uinput_device *udev, const char __user *buffer, size_t count)
3471da177e4SLinus Torvalds {
3481da177e4SLinus Torvalds 	struct uinput_user_dev	*user_dev;
3491da177e4SLinus Torvalds 	struct input_dev	*dev;
3505d9d6e91SDavid Herrmann 	int			i;
351152c12f5SDmitry Torokhov 	int			retval;
3521da177e4SLinus Torvalds 
35329506415SDmitry Torokhov 	if (count != sizeof(struct uinput_user_dev))
35429506415SDmitry Torokhov 		return -EINVAL;
3551da177e4SLinus Torvalds 
35629506415SDmitry Torokhov 	if (!udev->dev) {
35729506415SDmitry Torokhov 		retval = uinput_allocate_device(udev);
35829506415SDmitry Torokhov 		if (retval)
35929506415SDmitry Torokhov 			return retval;
36029506415SDmitry Torokhov 	}
36129506415SDmitry Torokhov 
3621da177e4SLinus Torvalds 	dev = udev->dev;
3631da177e4SLinus Torvalds 
3644dfcc271SDmitry Torokhov 	user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));
365163d2770SDan Carpenter 	if (IS_ERR(user_dev))
3664dfcc271SDmitry Torokhov 		return PTR_ERR(user_dev);
3671da177e4SLinus Torvalds 
368ff462551SAnssi Hannula 	udev->ff_effects_max = user_dev->ff_effects_max;
369ff462551SAnssi Hannula 
3705d9d6e91SDavid Herrmann 	/* Ensure name is filled in */
3715d9d6e91SDavid Herrmann 	if (!user_dev->name[0]) {
37229506415SDmitry Torokhov 		retval = -EINVAL;
37329506415SDmitry Torokhov 		goto exit;
37429506415SDmitry Torokhov 	}
37529506415SDmitry Torokhov 
37629506415SDmitry Torokhov 	kfree(dev->name);
3775d9d6e91SDavid Herrmann 	dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE,
3785d9d6e91SDavid Herrmann 			     GFP_KERNEL);
3795d9d6e91SDavid Herrmann 	if (!dev->name) {
3801da177e4SLinus Torvalds 		retval = -ENOMEM;
3811da177e4SLinus Torvalds 		goto exit;
3821da177e4SLinus Torvalds 	}
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 	dev->id.bustype	= user_dev->id.bustype;
3851da177e4SLinus Torvalds 	dev->id.vendor	= user_dev->id.vendor;
3861da177e4SLinus Torvalds 	dev->id.product	= user_dev->id.product;
3871da177e4SLinus Torvalds 	dev->id.version	= user_dev->id.version;
3881da177e4SLinus Torvalds 
389987a6c02SDaniel Mack 	for (i = 0; i < ABS_CNT; i++) {
390987a6c02SDaniel Mack 		input_abs_set_max(dev, i, user_dev->absmax[i]);
391987a6c02SDaniel Mack 		input_abs_set_min(dev, i, user_dev->absmin[i]);
392987a6c02SDaniel Mack 		input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);
393987a6c02SDaniel Mack 		input_abs_set_flat(dev, i, user_dev->absflat[i]);
394987a6c02SDaniel Mack 	}
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds 	/* check if absmin/absmax/absfuzz/absflat are filled as
3971da177e4SLinus Torvalds 	 * told in Documentation/input/input-programming.txt */
3981da177e4SLinus Torvalds 	if (test_bit(EV_ABS, dev->evbit)) {
39929506415SDmitry Torokhov 		retval = uinput_validate_absbits(dev);
40029506415SDmitry Torokhov 		if (retval < 0)
40129506415SDmitry Torokhov 			goto exit;
40238e7afe9SHenrik Rydberg 		if (test_bit(ABS_MT_SLOT, dev->absbit)) {
40338e7afe9SHenrik Rydberg 			int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
4048cde8100SHenrik Rydberg 			input_mt_init_slots(dev, nslot);
40538e7afe9SHenrik Rydberg 		} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
40638e7afe9SHenrik Rydberg 			input_set_events_per_packet(dev, 60);
40738e7afe9SHenrik Rydberg 		}
4081da177e4SLinus Torvalds 	}
40929506415SDmitry Torokhov 
41029506415SDmitry Torokhov 	udev->state = UIST_SETUP_COMPLETE;
41129506415SDmitry Torokhov 	retval = count;
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds  exit:
4141da177e4SLinus Torvalds 	kfree(user_dev);
4151da177e4SLinus Torvalds 	return retval;
4161da177e4SLinus Torvalds }
4171da177e4SLinus Torvalds 
41829506415SDmitry Torokhov static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char __user *buffer, size_t count)
4191da177e4SLinus Torvalds {
4201da177e4SLinus Torvalds 	struct input_event ev;
4211da177e4SLinus Torvalds 
4222d56f3a3SPhilip Langdale 	if (count < input_event_size())
42329506415SDmitry Torokhov 		return -EINVAL;
42429506415SDmitry Torokhov 
4252d56f3a3SPhilip Langdale 	if (input_event_from_user(buffer, &ev))
4261da177e4SLinus Torvalds 		return -EFAULT;
4271da177e4SLinus Torvalds 
42829506415SDmitry Torokhov 	input_event(udev->dev, ev.type, ev.code, ev.value);
42929506415SDmitry Torokhov 
4302d56f3a3SPhilip Langdale 	return input_event_size();
43129506415SDmitry Torokhov }
43229506415SDmitry Torokhov 
43329506415SDmitry Torokhov static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
43429506415SDmitry Torokhov {
43529506415SDmitry Torokhov 	struct uinput_device *udev = file->private_data;
43629506415SDmitry Torokhov 	int retval;
43729506415SDmitry Torokhov 
438221979aaSDmitry Torokhov 	retval = mutex_lock_interruptible(&udev->mutex);
43929506415SDmitry Torokhov 	if (retval)
44029506415SDmitry Torokhov 		return retval;
44129506415SDmitry Torokhov 
44229506415SDmitry Torokhov 	retval = udev->state == UIST_CREATED ?
44329506415SDmitry Torokhov 			uinput_inject_event(udev, buffer, count) :
44429506415SDmitry Torokhov 			uinput_setup_device(udev, buffer, count);
44529506415SDmitry Torokhov 
446221979aaSDmitry Torokhov 	mutex_unlock(&udev->mutex);
44729506415SDmitry Torokhov 
44829506415SDmitry Torokhov 	return retval;
4491da177e4SLinus Torvalds }
4501da177e4SLinus Torvalds 
4511da177e4SLinus Torvalds static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
4521da177e4SLinus Torvalds {
4531da177e4SLinus Torvalds 	struct uinput_device *udev = file->private_data;
4541da177e4SLinus Torvalds 	int retval = 0;
4551da177e4SLinus Torvalds 
45629506415SDmitry Torokhov 	if (udev->state != UIST_CREATED)
4571da177e4SLinus Torvalds 		return -ENODEV;
4581da177e4SLinus Torvalds 
459152c12f5SDmitry Torokhov 	if (udev->head == udev->tail && (file->f_flags & O_NONBLOCK))
4601da177e4SLinus Torvalds 		return -EAGAIN;
4611da177e4SLinus Torvalds 
4621da177e4SLinus Torvalds 	retval = wait_event_interruptible(udev->waitq,
46329506415SDmitry Torokhov 			udev->head != udev->tail || udev->state != UIST_CREATED);
4641da177e4SLinus Torvalds 	if (retval)
4651da177e4SLinus Torvalds 		return retval;
4661da177e4SLinus Torvalds 
467221979aaSDmitry Torokhov 	retval = mutex_lock_interruptible(&udev->mutex);
46829506415SDmitry Torokhov 	if (retval)
46929506415SDmitry Torokhov 		return retval;
4701da177e4SLinus Torvalds 
47129506415SDmitry Torokhov 	if (udev->state != UIST_CREATED) {
47229506415SDmitry Torokhov 		retval = -ENODEV;
47329506415SDmitry Torokhov 		goto out;
47429506415SDmitry Torokhov 	}
47529506415SDmitry Torokhov 
4762d56f3a3SPhilip Langdale 	while (udev->head != udev->tail && retval + input_event_size() <= count) {
4772d56f3a3SPhilip Langdale 		if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) {
47829506415SDmitry Torokhov 			retval = -EFAULT;
47929506415SDmitry Torokhov 			goto out;
48029506415SDmitry Torokhov 		}
4811da177e4SLinus Torvalds 		udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
4822d56f3a3SPhilip Langdale 		retval += input_event_size();
4831da177e4SLinus Torvalds 	}
4841da177e4SLinus Torvalds 
48529506415SDmitry Torokhov  out:
486221979aaSDmitry Torokhov 	mutex_unlock(&udev->mutex);
48729506415SDmitry Torokhov 
4881da177e4SLinus Torvalds 	return retval;
4891da177e4SLinus Torvalds }
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds static unsigned int uinput_poll(struct file *file, poll_table *wait)
4921da177e4SLinus Torvalds {
4931da177e4SLinus Torvalds 	struct uinput_device *udev = file->private_data;
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds 	poll_wait(file, &udev->waitq, wait);
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds 	if (udev->head != udev->tail)
4981da177e4SLinus Torvalds 		return POLLIN | POLLRDNORM;
4991da177e4SLinus Torvalds 
5001da177e4SLinus Torvalds 	return 0;
5011da177e4SLinus Torvalds }
5021da177e4SLinus Torvalds 
50329506415SDmitry Torokhov static int uinput_release(struct inode *inode, struct file *file)
5041da177e4SLinus Torvalds {
50529506415SDmitry Torokhov 	struct uinput_device *udev = file->private_data;
5061da177e4SLinus Torvalds 
50729506415SDmitry Torokhov 	uinput_destroy_device(udev);
5081da177e4SLinus Torvalds 	kfree(udev);
5091da177e4SLinus Torvalds 
5101da177e4SLinus Torvalds 	return 0;
5111da177e4SLinus Torvalds }
5121da177e4SLinus Torvalds 
5132d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
5142d56f3a3SPhilip Langdale struct uinput_ff_upload_compat {
5152d56f3a3SPhilip Langdale 	int			request_id;
5162d56f3a3SPhilip Langdale 	int			retval;
5172d56f3a3SPhilip Langdale 	struct ff_effect_compat	effect;
5182d56f3a3SPhilip Langdale 	struct ff_effect_compat	old;
5192d56f3a3SPhilip Langdale };
5202d56f3a3SPhilip Langdale 
5212d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer,
5222d56f3a3SPhilip Langdale 				    const struct uinput_ff_upload *ff_up)
5232d56f3a3SPhilip Langdale {
5242d56f3a3SPhilip Langdale 	if (INPUT_COMPAT_TEST) {
5252d56f3a3SPhilip Langdale 		struct uinput_ff_upload_compat ff_up_compat;
5262d56f3a3SPhilip Langdale 
5272d56f3a3SPhilip Langdale 		ff_up_compat.request_id = ff_up->request_id;
5282d56f3a3SPhilip Langdale 		ff_up_compat.retval = ff_up->retval;
5292d56f3a3SPhilip Langdale 		/*
5302d56f3a3SPhilip Langdale 		 * It so happens that the pointer that gives us the trouble
5312d56f3a3SPhilip Langdale 		 * is the last field in the structure. Since we don't support
5322d56f3a3SPhilip Langdale 		 * custom waveforms in uinput anyway we can just copy the whole
5332d56f3a3SPhilip Langdale 		 * thing (to the compat size) and ignore the pointer.
5342d56f3a3SPhilip Langdale 		 */
5352d56f3a3SPhilip Langdale 		memcpy(&ff_up_compat.effect, &ff_up->effect,
5362d56f3a3SPhilip Langdale 			sizeof(struct ff_effect_compat));
5372d56f3a3SPhilip Langdale 		memcpy(&ff_up_compat.old, &ff_up->old,
5382d56f3a3SPhilip Langdale 			sizeof(struct ff_effect_compat));
5392d56f3a3SPhilip Langdale 
5402d56f3a3SPhilip Langdale 		if (copy_to_user(buffer, &ff_up_compat,
5412d56f3a3SPhilip Langdale 				 sizeof(struct uinput_ff_upload_compat)))
5422d56f3a3SPhilip Langdale 			return -EFAULT;
5432d56f3a3SPhilip Langdale 	} else {
5442d56f3a3SPhilip Langdale 		if (copy_to_user(buffer, ff_up,
5452d56f3a3SPhilip Langdale 				 sizeof(struct uinput_ff_upload)))
5462d56f3a3SPhilip Langdale 			return -EFAULT;
5472d56f3a3SPhilip Langdale 	}
5482d56f3a3SPhilip Langdale 
5492d56f3a3SPhilip Langdale 	return 0;
5502d56f3a3SPhilip Langdale }
5512d56f3a3SPhilip Langdale 
5522d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer,
5532d56f3a3SPhilip Langdale 				      struct uinput_ff_upload *ff_up)
5542d56f3a3SPhilip Langdale {
5552d56f3a3SPhilip Langdale 	if (INPUT_COMPAT_TEST) {
5562d56f3a3SPhilip Langdale 		struct uinput_ff_upload_compat ff_up_compat;
5572d56f3a3SPhilip Langdale 
5582d56f3a3SPhilip Langdale 		if (copy_from_user(&ff_up_compat, buffer,
5592d56f3a3SPhilip Langdale 				   sizeof(struct uinput_ff_upload_compat)))
5602d56f3a3SPhilip Langdale 			return -EFAULT;
5612d56f3a3SPhilip Langdale 
5622d56f3a3SPhilip Langdale 		ff_up->request_id = ff_up_compat.request_id;
5632d56f3a3SPhilip Langdale 		ff_up->retval = ff_up_compat.retval;
5642d56f3a3SPhilip Langdale 		memcpy(&ff_up->effect, &ff_up_compat.effect,
5652d56f3a3SPhilip Langdale 			sizeof(struct ff_effect_compat));
5662d56f3a3SPhilip Langdale 		memcpy(&ff_up->old, &ff_up_compat.old,
5672d56f3a3SPhilip Langdale 			sizeof(struct ff_effect_compat));
5682d56f3a3SPhilip Langdale 
5692d56f3a3SPhilip Langdale 	} else {
5702d56f3a3SPhilip Langdale 		if (copy_from_user(ff_up, buffer,
5712d56f3a3SPhilip Langdale 				   sizeof(struct uinput_ff_upload)))
5722d56f3a3SPhilip Langdale 			return -EFAULT;
5732d56f3a3SPhilip Langdale 	}
5742d56f3a3SPhilip Langdale 
5752d56f3a3SPhilip Langdale 	return 0;
5762d56f3a3SPhilip Langdale }
5772d56f3a3SPhilip Langdale 
5782d56f3a3SPhilip Langdale #else
5792d56f3a3SPhilip Langdale 
5802d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer,
5812d56f3a3SPhilip Langdale 				    const struct uinput_ff_upload *ff_up)
5822d56f3a3SPhilip Langdale {
5832d56f3a3SPhilip Langdale 	if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload)))
5842d56f3a3SPhilip Langdale 		return -EFAULT;
5852d56f3a3SPhilip Langdale 
5862d56f3a3SPhilip Langdale 	return 0;
5872d56f3a3SPhilip Langdale }
5882d56f3a3SPhilip Langdale 
5892d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer,
5902d56f3a3SPhilip Langdale 				      struct uinput_ff_upload *ff_up)
5912d56f3a3SPhilip Langdale {
5922d56f3a3SPhilip Langdale 	if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload)))
5932d56f3a3SPhilip Langdale 		return -EFAULT;
5942d56f3a3SPhilip Langdale 
5952d56f3a3SPhilip Langdale 	return 0;
5962d56f3a3SPhilip Langdale }
5972d56f3a3SPhilip Langdale 
5982d56f3a3SPhilip Langdale #endif
5992d56f3a3SPhilip Langdale 
60029506415SDmitry Torokhov #define uinput_set_bit(_arg, _bit, _max)		\
60129506415SDmitry Torokhov ({							\
60229506415SDmitry Torokhov 	int __ret = 0;					\
60329506415SDmitry Torokhov 	if (udev->state == UIST_CREATED)		\
60429506415SDmitry Torokhov 		__ret =  -EINVAL;			\
60529506415SDmitry Torokhov 	else if ((_arg) > (_max))			\
60629506415SDmitry Torokhov 		__ret = -EINVAL;			\
60729506415SDmitry Torokhov 	else set_bit((_arg), udev->dev->_bit);		\
60829506415SDmitry Torokhov 	__ret;						\
60929506415SDmitry Torokhov })
6101da177e4SLinus Torvalds 
6112d56f3a3SPhilip Langdale static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
6122d56f3a3SPhilip Langdale 				 unsigned long arg, void __user *p)
6131da177e4SLinus Torvalds {
61429506415SDmitry Torokhov 	int			retval;
6152d56f3a3SPhilip Langdale 	struct uinput_device	*udev = file->private_data;
6161da177e4SLinus Torvalds 	struct uinput_ff_upload ff_up;
6171da177e4SLinus Torvalds 	struct uinput_ff_erase  ff_erase;
6181da177e4SLinus Torvalds 	struct uinput_request   *req;
6195b6271bdSDmitry Torokhov 	char			*phys;
6201da177e4SLinus Torvalds 
621221979aaSDmitry Torokhov 	retval = mutex_lock_interruptible(&udev->mutex);
62229506415SDmitry Torokhov 	if (retval)
62329506415SDmitry Torokhov 		return retval;
62429506415SDmitry Torokhov 
62529506415SDmitry Torokhov 	if (!udev->dev) {
62629506415SDmitry Torokhov 		retval = uinput_allocate_device(udev);
62729506415SDmitry Torokhov 		if (retval)
62829506415SDmitry Torokhov 			goto out;
6291da177e4SLinus Torvalds 	}
6301da177e4SLinus Torvalds 
6311da177e4SLinus Torvalds 	switch (cmd) {
6321da177e4SLinus Torvalds 		case UI_DEV_CREATE:
6331da177e4SLinus Torvalds 			retval = uinput_create_device(udev);
6341da177e4SLinus Torvalds 			break;
6351da177e4SLinus Torvalds 
6361da177e4SLinus Torvalds 		case UI_DEV_DESTROY:
63729506415SDmitry Torokhov 			uinput_destroy_device(udev);
6381da177e4SLinus Torvalds 			break;
6391da177e4SLinus Torvalds 
6401da177e4SLinus Torvalds 		case UI_SET_EVBIT:
64129506415SDmitry Torokhov 			retval = uinput_set_bit(arg, evbit, EV_MAX);
6421da177e4SLinus Torvalds 			break;
6431da177e4SLinus Torvalds 
6441da177e4SLinus Torvalds 		case UI_SET_KEYBIT:
64529506415SDmitry Torokhov 			retval = uinput_set_bit(arg, keybit, KEY_MAX);
6461da177e4SLinus Torvalds 			break;
6471da177e4SLinus Torvalds 
6481da177e4SLinus Torvalds 		case UI_SET_RELBIT:
64929506415SDmitry Torokhov 			retval = uinput_set_bit(arg, relbit, REL_MAX);
6501da177e4SLinus Torvalds 			break;
6511da177e4SLinus Torvalds 
6521da177e4SLinus Torvalds 		case UI_SET_ABSBIT:
65329506415SDmitry Torokhov 			retval = uinput_set_bit(arg, absbit, ABS_MAX);
6541da177e4SLinus Torvalds 			break;
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds 		case UI_SET_MSCBIT:
65729506415SDmitry Torokhov 			retval = uinput_set_bit(arg, mscbit, MSC_MAX);
6581da177e4SLinus Torvalds 			break;
6591da177e4SLinus Torvalds 
6601da177e4SLinus Torvalds 		case UI_SET_LEDBIT:
66129506415SDmitry Torokhov 			retval = uinput_set_bit(arg, ledbit, LED_MAX);
6621da177e4SLinus Torvalds 			break;
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds 		case UI_SET_SNDBIT:
66529506415SDmitry Torokhov 			retval = uinput_set_bit(arg, sndbit, SND_MAX);
6661da177e4SLinus Torvalds 			break;
6671da177e4SLinus Torvalds 
6681da177e4SLinus Torvalds 		case UI_SET_FFBIT:
66929506415SDmitry Torokhov 			retval = uinput_set_bit(arg, ffbit, FF_MAX);
6701da177e4SLinus Torvalds 			break;
6711da177e4SLinus Torvalds 
67259c7c037SDmitry Torokhov 		case UI_SET_SWBIT:
67359c7c037SDmitry Torokhov 			retval = uinput_set_bit(arg, swbit, SW_MAX);
67459c7c037SDmitry Torokhov 			break;
67559c7c037SDmitry Torokhov 
67685b77200SHenrik Rydberg 		case UI_SET_PROPBIT:
67785b77200SHenrik Rydberg 			retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX);
67885b77200SHenrik Rydberg 			break;
67985b77200SHenrik Rydberg 
6801da177e4SLinus Torvalds 		case UI_SET_PHYS:
68129506415SDmitry Torokhov 			if (udev->state == UIST_CREATED) {
68229506415SDmitry Torokhov 				retval = -EINVAL;
68329506415SDmitry Torokhov 				goto out;
68429506415SDmitry Torokhov 			}
6854dfcc271SDmitry Torokhov 
6864dfcc271SDmitry Torokhov 			phys = strndup_user(p, 1024);
6874dfcc271SDmitry Torokhov 			if (IS_ERR(phys)) {
6884dfcc271SDmitry Torokhov 				retval = PTR_ERR(phys);
6894dfcc271SDmitry Torokhov 				goto out;
6901da177e4SLinus Torvalds 			}
6914dfcc271SDmitry Torokhov 
6921da177e4SLinus Torvalds 			kfree(udev->dev->phys);
6934dfcc271SDmitry Torokhov 			udev->dev->phys = phys;
6941da177e4SLinus Torvalds 			break;
6951da177e4SLinus Torvalds 
6961da177e4SLinus Torvalds 		case UI_BEGIN_FF_UPLOAD:
6972d56f3a3SPhilip Langdale 			retval = uinput_ff_upload_from_user(p, &ff_up);
6982d56f3a3SPhilip Langdale 			if (retval)
6991da177e4SLinus Torvalds 				break;
7002d56f3a3SPhilip Langdale 
7011da177e4SLinus Torvalds 			req = uinput_request_find(udev, ff_up.request_id);
7022d56f3a3SPhilip Langdale 			if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) {
7031da177e4SLinus Torvalds 				retval = -EINVAL;
7041da177e4SLinus Torvalds 				break;
7051da177e4SLinus Torvalds 			}
7062d56f3a3SPhilip Langdale 
7071da177e4SLinus Torvalds 			ff_up.retval = 0;
7082d56f3a3SPhilip Langdale 			ff_up.effect = *req->u.upload.effect;
709ff462551SAnssi Hannula 			if (req->u.upload.old)
7102d56f3a3SPhilip Langdale 				ff_up.old = *req->u.upload.old;
711ff462551SAnssi Hannula 			else
712ff462551SAnssi Hannula 				memset(&ff_up.old, 0, sizeof(struct ff_effect));
713ff462551SAnssi Hannula 
7142d56f3a3SPhilip Langdale 			retval = uinput_ff_upload_to_user(p, &ff_up);
7151da177e4SLinus Torvalds 			break;
7161da177e4SLinus Torvalds 
7171da177e4SLinus Torvalds 		case UI_BEGIN_FF_ERASE:
7181da177e4SLinus Torvalds 			if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
7191da177e4SLinus Torvalds 				retval = -EFAULT;
7201da177e4SLinus Torvalds 				break;
7211da177e4SLinus Torvalds 			}
7222d56f3a3SPhilip Langdale 
7231da177e4SLinus Torvalds 			req = uinput_request_find(udev, ff_erase.request_id);
7242d56f3a3SPhilip Langdale 			if (!req || req->code != UI_FF_ERASE) {
7251da177e4SLinus Torvalds 				retval = -EINVAL;
7261da177e4SLinus Torvalds 				break;
7271da177e4SLinus Torvalds 			}
7282d56f3a3SPhilip Langdale 
7291da177e4SLinus Torvalds 			ff_erase.retval = 0;
7301da177e4SLinus Torvalds 			ff_erase.effect_id = req->u.effect_id;
7311da177e4SLinus Torvalds 			if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
7321da177e4SLinus Torvalds 				retval = -EFAULT;
7331da177e4SLinus Torvalds 				break;
7341da177e4SLinus Torvalds 			}
7352d56f3a3SPhilip Langdale 
7361da177e4SLinus Torvalds 			break;
7371da177e4SLinus Torvalds 
7381da177e4SLinus Torvalds 		case UI_END_FF_UPLOAD:
7392d56f3a3SPhilip Langdale 			retval = uinput_ff_upload_from_user(p, &ff_up);
7402d56f3a3SPhilip Langdale 			if (retval)
7411da177e4SLinus Torvalds 				break;
7422d56f3a3SPhilip Langdale 
7431da177e4SLinus Torvalds 			req = uinput_request_find(udev, ff_up.request_id);
7442d56f3a3SPhilip Langdale 			if (!req || req->code != UI_FF_UPLOAD ||
7452d56f3a3SPhilip Langdale 			    !req->u.upload.effect) {
7461da177e4SLinus Torvalds 				retval = -EINVAL;
7471da177e4SLinus Torvalds 				break;
7481da177e4SLinus Torvalds 			}
7492d56f3a3SPhilip Langdale 
7501da177e4SLinus Torvalds 			req->retval = ff_up.retval;
7510048e603SDmitry Torokhov 			uinput_request_done(udev, req);
7521da177e4SLinus Torvalds 			break;
7531da177e4SLinus Torvalds 
7541da177e4SLinus Torvalds 		case UI_END_FF_ERASE:
7551da177e4SLinus Torvalds 			if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
7561da177e4SLinus Torvalds 				retval = -EFAULT;
7571da177e4SLinus Torvalds 				break;
7581da177e4SLinus Torvalds 			}
7592d56f3a3SPhilip Langdale 
7601da177e4SLinus Torvalds 			req = uinput_request_find(udev, ff_erase.request_id);
7612d56f3a3SPhilip Langdale 			if (!req || req->code != UI_FF_ERASE) {
7621da177e4SLinus Torvalds 				retval = -EINVAL;
7631da177e4SLinus Torvalds 				break;
7641da177e4SLinus Torvalds 			}
7652d56f3a3SPhilip Langdale 
7661da177e4SLinus Torvalds 			req->retval = ff_erase.retval;
7670048e603SDmitry Torokhov 			uinput_request_done(udev, req);
7681da177e4SLinus Torvalds 			break;
7691da177e4SLinus Torvalds 
7701da177e4SLinus Torvalds 		default:
7711da177e4SLinus Torvalds 			retval = -EINVAL;
7721da177e4SLinus Torvalds 	}
77329506415SDmitry Torokhov 
77429506415SDmitry Torokhov  out:
775221979aaSDmitry Torokhov 	mutex_unlock(&udev->mutex);
7761da177e4SLinus Torvalds 	return retval;
7771da177e4SLinus Torvalds }
7781da177e4SLinus Torvalds 
7792d56f3a3SPhilip Langdale static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
7802d56f3a3SPhilip Langdale {
7812d56f3a3SPhilip Langdale 	return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg);
7822d56f3a3SPhilip Langdale }
7832d56f3a3SPhilip Langdale 
7842d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
7852d56f3a3SPhilip Langdale static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
7862d56f3a3SPhilip Langdale {
7872d56f3a3SPhilip Langdale 	return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
7882d56f3a3SPhilip Langdale }
7892d56f3a3SPhilip Langdale #endif
7902d56f3a3SPhilip Langdale 
7912b8693c0SArjan van de Ven static const struct file_operations uinput_fops = {
7921da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
7931da177e4SLinus Torvalds 	.open		= uinput_open,
79429506415SDmitry Torokhov 	.release	= uinput_release,
7951da177e4SLinus Torvalds 	.read		= uinput_read,
7961da177e4SLinus Torvalds 	.write		= uinput_write,
7971da177e4SLinus Torvalds 	.poll		= uinput_poll,
79829506415SDmitry Torokhov 	.unlocked_ioctl	= uinput_ioctl,
7992d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
8002d56f3a3SPhilip Langdale 	.compat_ioctl	= uinput_compat_ioctl,
8012d56f3a3SPhilip Langdale #endif
8026038f373SArnd Bergmann 	.llseek		= no_llseek,
8031da177e4SLinus Torvalds };
8041da177e4SLinus Torvalds 
8051da177e4SLinus Torvalds static struct miscdevice uinput_misc = {
8061da177e4SLinus Torvalds 	.fops		= &uinput_fops,
8071da177e4SLinus Torvalds 	.minor		= UINPUT_MINOR,
8081da177e4SLinus Torvalds 	.name		= UINPUT_NAME,
8091da177e4SLinus Torvalds };
8108905aaafSKay Sievers MODULE_ALIAS_MISCDEV(UINPUT_MINOR);
8118905aaafSKay Sievers MODULE_ALIAS("devname:" UINPUT_NAME);
8121da177e4SLinus Torvalds 
8131da177e4SLinus Torvalds static int __init uinput_init(void)
8141da177e4SLinus Torvalds {
8151da177e4SLinus Torvalds 	return misc_register(&uinput_misc);
8161da177e4SLinus Torvalds }
8171da177e4SLinus Torvalds 
8181da177e4SLinus Torvalds static void __exit uinput_exit(void)
8191da177e4SLinus Torvalds {
8201da177e4SLinus Torvalds 	misc_deregister(&uinput_misc);
8211da177e4SLinus Torvalds }
8221da177e4SLinus Torvalds 
8231da177e4SLinus Torvalds MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
8241da177e4SLinus Torvalds MODULE_DESCRIPTION("User level driver support for input subsystem");
8251da177e4SLinus Torvalds MODULE_LICENSE("GPL");
826ff462551SAnssi Hannula MODULE_VERSION("0.3");
8271da177e4SLinus Torvalds 
8281da177e4SLinus Torvalds module_init(uinput_init);
8291da177e4SLinus Torvalds module_exit(uinput_exit);
8301da177e4SLinus Torvalds 
831