xref: /openbmc/linux/drivers/input/misc/uinput.c (revision 05cebd38)
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>
331da177e4SLinus Torvalds #include <linux/slab.h>
341da177e4SLinus Torvalds #include <linux/module.h>
351da177e4SLinus Torvalds #include <linux/init.h>
361da177e4SLinus Torvalds #include <linux/smp_lock.h>
371da177e4SLinus Torvalds #include <linux/fs.h>
381da177e4SLinus Torvalds #include <linux/miscdevice.h>
391da177e4SLinus Torvalds #include <linux/uinput.h>
402d56f3a3SPhilip Langdale #include "../input-compat.h"
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
431da177e4SLinus Torvalds {
44373f9713SDmitry Torokhov 	struct uinput_device	*udev = input_get_drvdata(dev);
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds 	udev->buff[udev->head].type = type;
471da177e4SLinus Torvalds 	udev->buff[udev->head].code = code;
481da177e4SLinus Torvalds 	udev->buff[udev->head].value = value;
491da177e4SLinus Torvalds 	do_gettimeofday(&udev->buff[udev->head].time);
501da177e4SLinus Torvalds 	udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds 	wake_up_interruptible(&udev->waitq);
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 	return 0;
551da177e4SLinus Torvalds }
561da177e4SLinus Torvalds 
5705cebd38SAristeu Sergio Rozanski Filho /* Atomically allocate an ID for the given request. Returns 0 on success. */
580048e603SDmitry Torokhov static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request)
591da177e4SLinus Torvalds {
601da177e4SLinus Torvalds 	int id;
61152c12f5SDmitry Torokhov 	int err = -1;
621da177e4SLinus Torvalds 
630048e603SDmitry Torokhov 	spin_lock(&udev->requests_lock);
64152c12f5SDmitry Torokhov 
6505cebd38SAristeu Sergio Rozanski Filho 	for (id = 0; id < UINPUT_NUM_REQUESTS; id++) {
661da177e4SLinus Torvalds 		if (!udev->requests[id]) {
671da177e4SLinus Torvalds 			request->id = id;
680048e603SDmitry Torokhov 			udev->requests[id] = request;
69152c12f5SDmitry Torokhov 			err = 0;
70152c12f5SDmitry Torokhov 			break;
711da177e4SLinus Torvalds 		}
7205cebd38SAristeu Sergio Rozanski Filho 	}
73152c12f5SDmitry Torokhov 
740048e603SDmitry Torokhov 	spin_unlock(&udev->requests_lock);
75152c12f5SDmitry Torokhov 	return err;
761da177e4SLinus Torvalds }
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds static struct uinput_request *uinput_request_find(struct uinput_device *udev, int id)
791da177e4SLinus Torvalds {
801da177e4SLinus Torvalds 	/* Find an input request, by ID. Returns NULL if the ID isn't valid. */
811da177e4SLinus Torvalds 	if (id >= UINPUT_NUM_REQUESTS || id < 0)
821da177e4SLinus Torvalds 		return NULL;
832d56f3a3SPhilip Langdale 
841da177e4SLinus Torvalds 	return udev->requests[id];
851da177e4SLinus Torvalds }
861da177e4SLinus Torvalds 
870048e603SDmitry Torokhov static inline int uinput_request_reserve_slot(struct uinput_device *udev, struct uinput_request *request)
881da177e4SLinus Torvalds {
890048e603SDmitry Torokhov 	/* Allocate slot. If none are available right away, wait. */
900048e603SDmitry Torokhov 	return wait_event_interruptible(udev->requests_waitq,
910048e603SDmitry Torokhov 					!uinput_request_alloc_id(udev, request));
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds 
940048e603SDmitry Torokhov static void uinput_request_done(struct uinput_device *udev, struct uinput_request *request)
951da177e4SLinus Torvalds {
960048e603SDmitry Torokhov 	/* Mark slot as available */
970048e603SDmitry Torokhov 	udev->requests[request->id] = NULL;
98e597f0c8SDmitry Torokhov 	wake_up(&udev->requests_waitq);
99e7507ed9SDmitry Torokhov 
100e7507ed9SDmitry Torokhov 	complete(&request->done);
1010048e603SDmitry Torokhov }
1020048e603SDmitry Torokhov 
10305cebd38SAristeu Sergio Rozanski Filho static int uinput_request_submit(struct uinput_device *udev, struct uinput_request *request)
1040048e603SDmitry Torokhov {
10505cebd38SAristeu Sergio Rozanski Filho 	int retval;
1061da177e4SLinus Torvalds 
10705cebd38SAristeu Sergio Rozanski Filho 	retval = uinput_request_reserve_slot(udev, request);
10805cebd38SAristeu Sergio Rozanski Filho 	if (retval)
10905cebd38SAristeu Sergio Rozanski Filho 		return retval;
11005cebd38SAristeu Sergio Rozanski Filho 
11105cebd38SAristeu Sergio Rozanski Filho 	retval = mutex_lock_interruptible(&udev->mutex);
11205cebd38SAristeu Sergio Rozanski Filho 	if (retval)
11305cebd38SAristeu Sergio Rozanski Filho 		return retval;
11405cebd38SAristeu Sergio Rozanski Filho 
11505cebd38SAristeu Sergio Rozanski Filho 	if (udev->state != UIST_CREATED) {
11605cebd38SAristeu Sergio Rozanski Filho 		retval = -ENODEV;
11705cebd38SAristeu Sergio Rozanski Filho 		goto out;
11805cebd38SAristeu Sergio Rozanski Filho 	}
11905cebd38SAristeu Sergio Rozanski Filho 
12005cebd38SAristeu Sergio Rozanski Filho 	/* Tell our userspace app about this new request by queueing an input event */
12105cebd38SAristeu Sergio Rozanski Filho 	uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id);
12205cebd38SAristeu Sergio Rozanski Filho 
12305cebd38SAristeu Sergio Rozanski Filho  out:
12405cebd38SAristeu Sergio Rozanski Filho 	mutex_unlock(&udev->mutex);
12505cebd38SAristeu Sergio Rozanski Filho 	return retval;
12605cebd38SAristeu Sergio Rozanski Filho }
12705cebd38SAristeu Sergio Rozanski Filho 
12805cebd38SAristeu Sergio Rozanski Filho /*
12905cebd38SAristeu Sergio Rozanski Filho  * Fail all ouitstanding requests so handlers don't wait for the userspace
13005cebd38SAristeu Sergio Rozanski Filho  * to finish processing them.
13105cebd38SAristeu Sergio Rozanski Filho  */
13205cebd38SAristeu Sergio Rozanski Filho static void uinput_flush_requests(struct uinput_device *udev)
13305cebd38SAristeu Sergio Rozanski Filho {
13405cebd38SAristeu Sergio Rozanski Filho 	struct uinput_request *request;
13505cebd38SAristeu Sergio Rozanski Filho 	int i;
13605cebd38SAristeu Sergio Rozanski Filho 
13705cebd38SAristeu Sergio Rozanski Filho 	spin_lock(&udev->requests_lock);
13805cebd38SAristeu Sergio Rozanski Filho 
13905cebd38SAristeu Sergio Rozanski Filho 	for (i = 0; i < UINPUT_NUM_REQUESTS; i++) {
14005cebd38SAristeu Sergio Rozanski Filho 		request = udev->requests[i];
14105cebd38SAristeu Sergio Rozanski Filho 		if (request) {
14205cebd38SAristeu Sergio Rozanski Filho 			request->retval = -ENODEV;
14305cebd38SAristeu Sergio Rozanski Filho 			uinput_request_done(udev, request);
14405cebd38SAristeu Sergio Rozanski Filho 		}
14505cebd38SAristeu Sergio Rozanski Filho 	}
14605cebd38SAristeu Sergio Rozanski Filho 
14705cebd38SAristeu Sergio Rozanski Filho 	spin_unlock(&udev->requests_lock);
1481da177e4SLinus Torvalds }
1491da177e4SLinus Torvalds 
150ff462551SAnssi Hannula static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
151ff462551SAnssi Hannula {
152ff462551SAnssi Hannula 	uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
153ff462551SAnssi Hannula }
154ff462551SAnssi Hannula 
155ff462551SAnssi Hannula static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude)
156ff462551SAnssi Hannula {
157ff462551SAnssi Hannula 	uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);
158ff462551SAnssi Hannula }
159ff462551SAnssi Hannula 
160ff462551SAnssi Hannula static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
161ff462551SAnssi Hannula {
162ff462551SAnssi Hannula 	return uinput_dev_event(dev, EV_FF, effect_id, value);
163ff462551SAnssi Hannula }
164ff462551SAnssi Hannula 
165ff462551SAnssi Hannula static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
1661da177e4SLinus Torvalds {
16705cebd38SAristeu Sergio Rozanski Filho 	struct uinput_device *udev = input_get_drvdata(dev);
1681da177e4SLinus Torvalds 	struct uinput_request request;
1690048e603SDmitry Torokhov 	int retval;
1701da177e4SLinus Torvalds 
1712d56f3a3SPhilip Langdale 	/*
1722d56f3a3SPhilip Langdale 	 * uinput driver does not currently support periodic effects with
1732d56f3a3SPhilip Langdale 	 * custom waveform since it does not have a way to pass buffer of
1742d56f3a3SPhilip Langdale 	 * samples (custom_data) to userspace. If ever there is a device
1752d56f3a3SPhilip Langdale 	 * supporting custom waveforms we would need to define an additional
1762d56f3a3SPhilip Langdale 	 * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out.
1772d56f3a3SPhilip Langdale 	 */
1782d56f3a3SPhilip Langdale 	if (effect->type == FF_PERIODIC &&
1792d56f3a3SPhilip Langdale 			effect->u.periodic.waveform == FF_CUSTOM)
1802d56f3a3SPhilip Langdale 		return -EINVAL;
1812d56f3a3SPhilip Langdale 
1820048e603SDmitry Torokhov 	request.id = -1;
1830048e603SDmitry Torokhov 	init_completion(&request.done);
1840048e603SDmitry Torokhov 	request.code = UI_FF_UPLOAD;
185ff462551SAnssi Hannula 	request.u.upload.effect = effect;
186ff462551SAnssi Hannula 	request.u.upload.old = old;
1870048e603SDmitry Torokhov 
18805cebd38SAristeu Sergio Rozanski Filho 	retval = uinput_request_submit(udev, &request);
18905cebd38SAristeu Sergio Rozanski Filho 	if (!retval) {
19005cebd38SAristeu Sergio Rozanski Filho 		wait_for_completion(&request.done);
19105cebd38SAristeu Sergio Rozanski Filho 		retval = request.retval;
19205cebd38SAristeu Sergio Rozanski Filho 	}
1930048e603SDmitry Torokhov 
1940048e603SDmitry Torokhov 	return retval;
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
1981da177e4SLinus Torvalds {
19905cebd38SAristeu Sergio Rozanski Filho 	struct uinput_device *udev = input_get_drvdata(dev);
2001da177e4SLinus Torvalds 	struct uinput_request request;
2010048e603SDmitry Torokhov 	int retval;
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	if (!test_bit(EV_FF, dev->evbit))
2041da177e4SLinus Torvalds 		return -ENOSYS;
2051da177e4SLinus Torvalds 
2060048e603SDmitry Torokhov 	request.id = -1;
2070048e603SDmitry Torokhov 	init_completion(&request.done);
2080048e603SDmitry Torokhov 	request.code = UI_FF_ERASE;
2091da177e4SLinus Torvalds 	request.u.effect_id = effect_id;
2100048e603SDmitry Torokhov 
21105cebd38SAristeu Sergio Rozanski Filho 	retval = uinput_request_submit(udev, &request);
21205cebd38SAristeu Sergio Rozanski Filho 	if (!retval) {
21305cebd38SAristeu Sergio Rozanski Filho 		wait_for_completion(&request.done);
21405cebd38SAristeu Sergio Rozanski Filho 		retval = request.retval;
21505cebd38SAristeu Sergio Rozanski Filho 	}
2160048e603SDmitry Torokhov 
2170048e603SDmitry Torokhov 	return retval;
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds 
22029506415SDmitry Torokhov static void uinput_destroy_device(struct uinput_device *udev)
22129506415SDmitry Torokhov {
22229506415SDmitry Torokhov 	const char *name, *phys;
22305cebd38SAristeu Sergio Rozanski Filho 	struct input_dev *dev = udev->dev;
22405cebd38SAristeu Sergio Rozanski Filho 	enum uinput_state old_state = udev->state;
22529506415SDmitry Torokhov 
22605cebd38SAristeu Sergio Rozanski Filho 	udev->state = UIST_NEW_DEVICE;
22705cebd38SAristeu Sergio Rozanski Filho 
22805cebd38SAristeu Sergio Rozanski Filho 	if (dev) {
22905cebd38SAristeu Sergio Rozanski Filho 		name = dev->name;
23005cebd38SAristeu Sergio Rozanski Filho 		phys = dev->phys;
23105cebd38SAristeu Sergio Rozanski Filho 		if (old_state == UIST_CREATED) {
23205cebd38SAristeu Sergio Rozanski Filho 			uinput_flush_requests(udev);
23305cebd38SAristeu Sergio Rozanski Filho 			input_unregister_device(dev);
23405cebd38SAristeu Sergio Rozanski Filho 		} else {
23505cebd38SAristeu Sergio Rozanski Filho 			input_free_device(dev);
23605cebd38SAristeu Sergio Rozanski Filho 		}
23729506415SDmitry Torokhov 		kfree(name);
23829506415SDmitry Torokhov 		kfree(phys);
23929506415SDmitry Torokhov 		udev->dev = NULL;
24029506415SDmitry Torokhov 	}
24129506415SDmitry Torokhov }
24229506415SDmitry Torokhov 
2431da177e4SLinus Torvalds static int uinput_create_device(struct uinput_device *udev)
2441da177e4SLinus Torvalds {
245ff462551SAnssi Hannula 	struct input_dev *dev = udev->dev;
24629506415SDmitry Torokhov 	int error;
24729506415SDmitry Torokhov 
24829506415SDmitry Torokhov 	if (udev->state != UIST_SETUP_COMPLETE) {
2491da177e4SLinus Torvalds 		printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
2501da177e4SLinus Torvalds 		return -EINVAL;
2511da177e4SLinus Torvalds 	}
2521da177e4SLinus Torvalds 
253ff462551SAnssi Hannula 	if (udev->ff_effects_max) {
254ff462551SAnssi Hannula 		error = input_ff_create(dev, udev->ff_effects_max);
255ff462551SAnssi Hannula 		if (error)
256ff462551SAnssi Hannula 			goto fail1;
257ff462551SAnssi Hannula 
258ff462551SAnssi Hannula 		dev->ff->upload = uinput_dev_upload_effect;
259ff462551SAnssi Hannula 		dev->ff->erase = uinput_dev_erase_effect;
260ff462551SAnssi Hannula 		dev->ff->playback = uinput_dev_playback;
261ff462551SAnssi Hannula 		dev->ff->set_gain = uinput_dev_set_gain;
262ff462551SAnssi Hannula 		dev->ff->set_autocenter = uinput_dev_set_autocenter;
2631da177e4SLinus Torvalds 	}
2641da177e4SLinus Torvalds 
265ff462551SAnssi Hannula 	error = input_register_device(udev->dev);
266ff462551SAnssi Hannula 	if (error)
267ff462551SAnssi Hannula 		goto fail2;
268ff462551SAnssi Hannula 
26929506415SDmitry Torokhov 	udev->state = UIST_CREATED;
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 	return 0;
272ff462551SAnssi Hannula 
273ff462551SAnssi Hannula  fail2:	input_ff_destroy(dev);
274ff462551SAnssi Hannula  fail1: uinput_destroy_device(udev);
275ff462551SAnssi Hannula 	return error;
2761da177e4SLinus Torvalds }
2771da177e4SLinus Torvalds 
2781da177e4SLinus Torvalds static int uinput_open(struct inode *inode, struct file *file)
2791da177e4SLinus Torvalds {
2801da177e4SLinus Torvalds 	struct uinput_device *newdev;
2811da177e4SLinus Torvalds 
28229506415SDmitry Torokhov 	newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
2831da177e4SLinus Torvalds 	if (!newdev)
28429506415SDmitry Torokhov 		return -ENOMEM;
28529506415SDmitry Torokhov 
28687029658SArnd Bergmann 	lock_kernel();
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;
29487029658SArnd Bergmann 	unlock_kernel();
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 
3041da177e4SLinus Torvalds 	for (cnt = 0; cnt < ABS_MAX + 1; cnt++) {
3051da177e4SLinus Torvalds 		if (!test_bit(cnt, dev->absbit))
3061da177e4SLinus Torvalds 			continue;
3071da177e4SLinus Torvalds 
3081da177e4SLinus Torvalds 		if ((dev->absmax[cnt] <= dev->absmin[cnt])) {
3091da177e4SLinus Torvalds 			printk(KERN_DEBUG
3101da177e4SLinus Torvalds 				"%s: invalid abs[%02x] min:%d max:%d\n",
3111da177e4SLinus Torvalds 				UINPUT_NAME, cnt,
3121da177e4SLinus Torvalds 				dev->absmin[cnt], dev->absmax[cnt]);
3131da177e4SLinus Torvalds 			retval = -EINVAL;
3141da177e4SLinus Torvalds 			break;
3151da177e4SLinus Torvalds 		}
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 		if (dev->absflat[cnt] > (dev->absmax[cnt] - dev->absmin[cnt])) {
3181da177e4SLinus Torvalds 			printk(KERN_DEBUG
3191da177e4SLinus Torvalds 				"%s: absflat[%02x] out of range: %d "
3201da177e4SLinus Torvalds 				"(min:%d/max:%d)\n",
3211da177e4SLinus Torvalds 				UINPUT_NAME, cnt, dev->absflat[cnt],
3221da177e4SLinus Torvalds 				dev->absmin[cnt], dev->absmax[cnt]);
3231da177e4SLinus Torvalds 			retval = -EINVAL;
3241da177e4SLinus Torvalds 			break;
3251da177e4SLinus Torvalds 		}
3261da177e4SLinus Torvalds 	}
3271da177e4SLinus Torvalds 	return retval;
3281da177e4SLinus Torvalds }
3291da177e4SLinus Torvalds 
33029506415SDmitry Torokhov static int uinput_allocate_device(struct uinput_device *udev)
33129506415SDmitry Torokhov {
33229506415SDmitry Torokhov 	udev->dev = input_allocate_device();
33329506415SDmitry Torokhov 	if (!udev->dev)
33429506415SDmitry Torokhov 		return -ENOMEM;
33529506415SDmitry Torokhov 
33629506415SDmitry Torokhov 	udev->dev->event = uinput_dev_event;
337373f9713SDmitry Torokhov 	input_set_drvdata(udev->dev, udev);
33829506415SDmitry Torokhov 
33929506415SDmitry Torokhov 	return 0;
34029506415SDmitry Torokhov }
34129506415SDmitry Torokhov 
34229506415SDmitry Torokhov static int uinput_setup_device(struct uinput_device *udev, const char __user *buffer, size_t count)
3431da177e4SLinus Torvalds {
3441da177e4SLinus Torvalds 	struct uinput_user_dev	*user_dev;
3451da177e4SLinus Torvalds 	struct input_dev	*dev;
3465b6271bdSDmitry Torokhov 	char			*name;
347152c12f5SDmitry Torokhov 	int			size;
348152c12f5SDmitry Torokhov 	int			retval;
3491da177e4SLinus Torvalds 
35029506415SDmitry Torokhov 	if (count != sizeof(struct uinput_user_dev))
35129506415SDmitry Torokhov 		return -EINVAL;
3521da177e4SLinus Torvalds 
35329506415SDmitry Torokhov 	if (!udev->dev) {
35429506415SDmitry Torokhov 		retval = uinput_allocate_device(udev);
35529506415SDmitry Torokhov 		if (retval)
35629506415SDmitry Torokhov 			return retval;
35729506415SDmitry Torokhov 	}
35829506415SDmitry Torokhov 
3591da177e4SLinus Torvalds 	dev = udev->dev;
3601da177e4SLinus Torvalds 
361152c12f5SDmitry Torokhov 	user_dev = kmalloc(sizeof(struct uinput_user_dev), GFP_KERNEL);
36229506415SDmitry Torokhov 	if (!user_dev)
36329506415SDmitry Torokhov 		return -ENOMEM;
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds 	if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
3661da177e4SLinus Torvalds 		retval = -EFAULT;
3671da177e4SLinus Torvalds 		goto exit;
3681da177e4SLinus Torvalds 	}
3691da177e4SLinus Torvalds 
370ff462551SAnssi Hannula 	udev->ff_effects_max = user_dev->ff_effects_max;
371ff462551SAnssi Hannula 
3721da177e4SLinus Torvalds 	size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
37329506415SDmitry Torokhov 	if (!size) {
37429506415SDmitry Torokhov 		retval = -EINVAL;
37529506415SDmitry Torokhov 		goto exit;
37629506415SDmitry Torokhov 	}
37729506415SDmitry Torokhov 
37829506415SDmitry Torokhov 	kfree(dev->name);
3795b6271bdSDmitry Torokhov 	dev->name = name = kmalloc(size, GFP_KERNEL);
3805b6271bdSDmitry Torokhov 	if (!name) {
3811da177e4SLinus Torvalds 		retval = -ENOMEM;
3821da177e4SLinus Torvalds 		goto exit;
3831da177e4SLinus Torvalds 	}
3845b6271bdSDmitry Torokhov 	strlcpy(name, user_dev->name, size);
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds 	dev->id.bustype	= user_dev->id.bustype;
3871da177e4SLinus Torvalds 	dev->id.vendor	= user_dev->id.vendor;
3881da177e4SLinus Torvalds 	dev->id.product	= user_dev->id.product;
3891da177e4SLinus Torvalds 	dev->id.version	= user_dev->id.version;
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds 	size = sizeof(int) * (ABS_MAX + 1);
3921da177e4SLinus Torvalds 	memcpy(dev->absmax, user_dev->absmax, size);
3931da177e4SLinus Torvalds 	memcpy(dev->absmin, user_dev->absmin, size);
3941da177e4SLinus Torvalds 	memcpy(dev->absfuzz, user_dev->absfuzz, size);
3951da177e4SLinus Torvalds 	memcpy(dev->absflat, user_dev->absflat, size);
3961da177e4SLinus Torvalds 
3971da177e4SLinus Torvalds 	/* check if absmin/absmax/absfuzz/absflat are filled as
3981da177e4SLinus Torvalds 	 * told in Documentation/input/input-programming.txt */
3991da177e4SLinus Torvalds 	if (test_bit(EV_ABS, dev->evbit)) {
40029506415SDmitry Torokhov 		retval = uinput_validate_absbits(dev);
40129506415SDmitry Torokhov 		if (retval < 0)
40229506415SDmitry Torokhov 			goto exit;
4031da177e4SLinus Torvalds 	}
40429506415SDmitry Torokhov 
40529506415SDmitry Torokhov 	udev->state = UIST_SETUP_COMPLETE;
40629506415SDmitry Torokhov 	retval = count;
4071da177e4SLinus Torvalds 
4081da177e4SLinus Torvalds  exit:
4091da177e4SLinus Torvalds 	kfree(user_dev);
4101da177e4SLinus Torvalds 	return retval;
4111da177e4SLinus Torvalds }
4121da177e4SLinus Torvalds 
41329506415SDmitry Torokhov static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char __user *buffer, size_t count)
4141da177e4SLinus Torvalds {
4151da177e4SLinus Torvalds 	struct input_event ev;
4161da177e4SLinus Torvalds 
4172d56f3a3SPhilip Langdale 	if (count < input_event_size())
41829506415SDmitry Torokhov 		return -EINVAL;
41929506415SDmitry Torokhov 
4202d56f3a3SPhilip Langdale 	if (input_event_from_user(buffer, &ev))
4211da177e4SLinus Torvalds 		return -EFAULT;
4221da177e4SLinus Torvalds 
42329506415SDmitry Torokhov 	input_event(udev->dev, ev.type, ev.code, ev.value);
42429506415SDmitry Torokhov 
4252d56f3a3SPhilip Langdale 	return input_event_size();
42629506415SDmitry Torokhov }
42729506415SDmitry Torokhov 
42829506415SDmitry Torokhov static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
42929506415SDmitry Torokhov {
43029506415SDmitry Torokhov 	struct uinput_device *udev = file->private_data;
43129506415SDmitry Torokhov 	int retval;
43229506415SDmitry Torokhov 
433221979aaSDmitry Torokhov 	retval = mutex_lock_interruptible(&udev->mutex);
43429506415SDmitry Torokhov 	if (retval)
43529506415SDmitry Torokhov 		return retval;
43629506415SDmitry Torokhov 
43729506415SDmitry Torokhov 	retval = udev->state == UIST_CREATED ?
43829506415SDmitry Torokhov 			uinput_inject_event(udev, buffer, count) :
43929506415SDmitry Torokhov 			uinput_setup_device(udev, buffer, count);
44029506415SDmitry Torokhov 
441221979aaSDmitry Torokhov 	mutex_unlock(&udev->mutex);
44229506415SDmitry Torokhov 
44329506415SDmitry Torokhov 	return retval;
4441da177e4SLinus Torvalds }
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
4471da177e4SLinus Torvalds {
4481da177e4SLinus Torvalds 	struct uinput_device *udev = file->private_data;
4491da177e4SLinus Torvalds 	int retval = 0;
4501da177e4SLinus Torvalds 
45129506415SDmitry Torokhov 	if (udev->state != UIST_CREATED)
4521da177e4SLinus Torvalds 		return -ENODEV;
4531da177e4SLinus Torvalds 
454152c12f5SDmitry Torokhov 	if (udev->head == udev->tail && (file->f_flags & O_NONBLOCK))
4551da177e4SLinus Torvalds 		return -EAGAIN;
4561da177e4SLinus Torvalds 
4571da177e4SLinus Torvalds 	retval = wait_event_interruptible(udev->waitq,
45829506415SDmitry Torokhov 			udev->head != udev->tail || udev->state != UIST_CREATED);
4591da177e4SLinus Torvalds 	if (retval)
4601da177e4SLinus Torvalds 		return retval;
4611da177e4SLinus Torvalds 
462221979aaSDmitry Torokhov 	retval = mutex_lock_interruptible(&udev->mutex);
46329506415SDmitry Torokhov 	if (retval)
46429506415SDmitry Torokhov 		return retval;
4651da177e4SLinus Torvalds 
46629506415SDmitry Torokhov 	if (udev->state != UIST_CREATED) {
46729506415SDmitry Torokhov 		retval = -ENODEV;
46829506415SDmitry Torokhov 		goto out;
46929506415SDmitry Torokhov 	}
47029506415SDmitry Torokhov 
4712d56f3a3SPhilip Langdale 	while (udev->head != udev->tail && retval + input_event_size() <= count) {
4722d56f3a3SPhilip Langdale 		if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) {
47329506415SDmitry Torokhov 			retval = -EFAULT;
47429506415SDmitry Torokhov 			goto out;
47529506415SDmitry Torokhov 		}
4761da177e4SLinus Torvalds 		udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
4772d56f3a3SPhilip Langdale 		retval += input_event_size();
4781da177e4SLinus Torvalds 	}
4791da177e4SLinus Torvalds 
48029506415SDmitry Torokhov  out:
481221979aaSDmitry Torokhov 	mutex_unlock(&udev->mutex);
48229506415SDmitry Torokhov 
4831da177e4SLinus Torvalds 	return retval;
4841da177e4SLinus Torvalds }
4851da177e4SLinus Torvalds 
4861da177e4SLinus Torvalds static unsigned int uinput_poll(struct file *file, poll_table *wait)
4871da177e4SLinus Torvalds {
4881da177e4SLinus Torvalds 	struct uinput_device *udev = file->private_data;
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	poll_wait(file, &udev->waitq, wait);
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds 	if (udev->head != udev->tail)
4931da177e4SLinus Torvalds 		return POLLIN | POLLRDNORM;
4941da177e4SLinus Torvalds 
4951da177e4SLinus Torvalds 	return 0;
4961da177e4SLinus Torvalds }
4971da177e4SLinus Torvalds 
49829506415SDmitry Torokhov static int uinput_release(struct inode *inode, struct file *file)
4991da177e4SLinus Torvalds {
50029506415SDmitry Torokhov 	struct uinput_device *udev = file->private_data;
5011da177e4SLinus Torvalds 
50229506415SDmitry Torokhov 	uinput_destroy_device(udev);
5031da177e4SLinus Torvalds 	kfree(udev);
5041da177e4SLinus Torvalds 
5051da177e4SLinus Torvalds 	return 0;
5061da177e4SLinus Torvalds }
5071da177e4SLinus Torvalds 
5082d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
5092d56f3a3SPhilip Langdale struct uinput_ff_upload_compat {
5102d56f3a3SPhilip Langdale 	int			request_id;
5112d56f3a3SPhilip Langdale 	int			retval;
5122d56f3a3SPhilip Langdale 	struct ff_effect_compat	effect;
5132d56f3a3SPhilip Langdale 	struct ff_effect_compat	old;
5142d56f3a3SPhilip Langdale };
5152d56f3a3SPhilip Langdale 
5162d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer,
5172d56f3a3SPhilip Langdale 				    const struct uinput_ff_upload *ff_up)
5182d56f3a3SPhilip Langdale {
5192d56f3a3SPhilip Langdale 	if (INPUT_COMPAT_TEST) {
5202d56f3a3SPhilip Langdale 		struct uinput_ff_upload_compat ff_up_compat;
5212d56f3a3SPhilip Langdale 
5222d56f3a3SPhilip Langdale 		ff_up_compat.request_id = ff_up->request_id;
5232d56f3a3SPhilip Langdale 		ff_up_compat.retval = ff_up->retval;
5242d56f3a3SPhilip Langdale 		/*
5252d56f3a3SPhilip Langdale 		 * It so happens that the pointer that gives us the trouble
5262d56f3a3SPhilip Langdale 		 * is the last field in the structure. Since we don't support
5272d56f3a3SPhilip Langdale 		 * custom waveforms in uinput anyway we can just copy the whole
5282d56f3a3SPhilip Langdale 		 * thing (to the compat size) and ignore the pointer.
5292d56f3a3SPhilip Langdale 		 */
5302d56f3a3SPhilip Langdale 		memcpy(&ff_up_compat.effect, &ff_up->effect,
5312d56f3a3SPhilip Langdale 			sizeof(struct ff_effect_compat));
5322d56f3a3SPhilip Langdale 		memcpy(&ff_up_compat.old, &ff_up->old,
5332d56f3a3SPhilip Langdale 			sizeof(struct ff_effect_compat));
5342d56f3a3SPhilip Langdale 
5352d56f3a3SPhilip Langdale 		if (copy_to_user(buffer, &ff_up_compat,
5362d56f3a3SPhilip Langdale 				 sizeof(struct uinput_ff_upload_compat)))
5372d56f3a3SPhilip Langdale 			return -EFAULT;
5382d56f3a3SPhilip Langdale 	} else {
5392d56f3a3SPhilip Langdale 		if (copy_to_user(buffer, ff_up,
5402d56f3a3SPhilip Langdale 				 sizeof(struct uinput_ff_upload)))
5412d56f3a3SPhilip Langdale 			return -EFAULT;
5422d56f3a3SPhilip Langdale 	}
5432d56f3a3SPhilip Langdale 
5442d56f3a3SPhilip Langdale 	return 0;
5452d56f3a3SPhilip Langdale }
5462d56f3a3SPhilip Langdale 
5472d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer,
5482d56f3a3SPhilip Langdale 				      struct uinput_ff_upload *ff_up)
5492d56f3a3SPhilip Langdale {
5502d56f3a3SPhilip Langdale 	if (INPUT_COMPAT_TEST) {
5512d56f3a3SPhilip Langdale 		struct uinput_ff_upload_compat ff_up_compat;
5522d56f3a3SPhilip Langdale 
5532d56f3a3SPhilip Langdale 		if (copy_from_user(&ff_up_compat, buffer,
5542d56f3a3SPhilip Langdale 				   sizeof(struct uinput_ff_upload_compat)))
5552d56f3a3SPhilip Langdale 			return -EFAULT;
5562d56f3a3SPhilip Langdale 
5572d56f3a3SPhilip Langdale 		ff_up->request_id = ff_up_compat.request_id;
5582d56f3a3SPhilip Langdale 		ff_up->retval = ff_up_compat.retval;
5592d56f3a3SPhilip Langdale 		memcpy(&ff_up->effect, &ff_up_compat.effect,
5602d56f3a3SPhilip Langdale 			sizeof(struct ff_effect_compat));
5612d56f3a3SPhilip Langdale 		memcpy(&ff_up->old, &ff_up_compat.old,
5622d56f3a3SPhilip Langdale 			sizeof(struct ff_effect_compat));
5632d56f3a3SPhilip Langdale 
5642d56f3a3SPhilip Langdale 	} else {
5652d56f3a3SPhilip Langdale 		if (copy_from_user(ff_up, buffer,
5662d56f3a3SPhilip Langdale 				   sizeof(struct uinput_ff_upload)))
5672d56f3a3SPhilip Langdale 			return -EFAULT;
5682d56f3a3SPhilip Langdale 	}
5692d56f3a3SPhilip Langdale 
5702d56f3a3SPhilip Langdale 	return 0;
5712d56f3a3SPhilip Langdale }
5722d56f3a3SPhilip Langdale 
5732d56f3a3SPhilip Langdale #else
5742d56f3a3SPhilip Langdale 
5752d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer,
5762d56f3a3SPhilip Langdale 				    const struct uinput_ff_upload *ff_up)
5772d56f3a3SPhilip Langdale {
5782d56f3a3SPhilip Langdale 	if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload)))
5792d56f3a3SPhilip Langdale 		return -EFAULT;
5802d56f3a3SPhilip Langdale 
5812d56f3a3SPhilip Langdale 	return 0;
5822d56f3a3SPhilip Langdale }
5832d56f3a3SPhilip Langdale 
5842d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer,
5852d56f3a3SPhilip Langdale 				      struct uinput_ff_upload *ff_up)
5862d56f3a3SPhilip Langdale {
5872d56f3a3SPhilip Langdale 	if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload)))
5882d56f3a3SPhilip Langdale 		return -EFAULT;
5892d56f3a3SPhilip Langdale 
5902d56f3a3SPhilip Langdale 	return 0;
5912d56f3a3SPhilip Langdale }
5922d56f3a3SPhilip Langdale 
5932d56f3a3SPhilip Langdale #endif
5942d56f3a3SPhilip Langdale 
59529506415SDmitry Torokhov #define uinput_set_bit(_arg, _bit, _max)		\
59629506415SDmitry Torokhov ({							\
59729506415SDmitry Torokhov 	int __ret = 0;					\
59829506415SDmitry Torokhov 	if (udev->state == UIST_CREATED)		\
59929506415SDmitry Torokhov 		__ret =  -EINVAL;			\
60029506415SDmitry Torokhov 	else if ((_arg) > (_max))			\
60129506415SDmitry Torokhov 		__ret = -EINVAL;			\
60229506415SDmitry Torokhov 	else set_bit((_arg), udev->dev->_bit);		\
60329506415SDmitry Torokhov 	__ret;						\
60429506415SDmitry Torokhov })
6051da177e4SLinus Torvalds 
6062d56f3a3SPhilip Langdale static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
6072d56f3a3SPhilip Langdale 				 unsigned long arg, void __user *p)
6081da177e4SLinus Torvalds {
60929506415SDmitry Torokhov 	int			retval;
6102d56f3a3SPhilip Langdale 	struct uinput_device	*udev = file->private_data;
6111da177e4SLinus Torvalds 	struct uinput_ff_upload ff_up;
6121da177e4SLinus Torvalds 	struct uinput_ff_erase  ff_erase;
6131da177e4SLinus Torvalds 	struct uinput_request   *req;
6141da177e4SLinus Torvalds 	int                     length;
6155b6271bdSDmitry Torokhov 	char			*phys;
6161da177e4SLinus Torvalds 
617221979aaSDmitry Torokhov 	retval = mutex_lock_interruptible(&udev->mutex);
61829506415SDmitry Torokhov 	if (retval)
61929506415SDmitry Torokhov 		return retval;
62029506415SDmitry Torokhov 
62129506415SDmitry Torokhov 	if (!udev->dev) {
62229506415SDmitry Torokhov 		retval = uinput_allocate_device(udev);
62329506415SDmitry Torokhov 		if (retval)
62429506415SDmitry Torokhov 			goto out;
6251da177e4SLinus Torvalds 	}
6261da177e4SLinus Torvalds 
6271da177e4SLinus Torvalds 	switch (cmd) {
6281da177e4SLinus Torvalds 		case UI_DEV_CREATE:
6291da177e4SLinus Torvalds 			retval = uinput_create_device(udev);
6301da177e4SLinus Torvalds 			break;
6311da177e4SLinus Torvalds 
6321da177e4SLinus Torvalds 		case UI_DEV_DESTROY:
63329506415SDmitry Torokhov 			uinput_destroy_device(udev);
6341da177e4SLinus Torvalds 			break;
6351da177e4SLinus Torvalds 
6361da177e4SLinus Torvalds 		case UI_SET_EVBIT:
63729506415SDmitry Torokhov 			retval = uinput_set_bit(arg, evbit, EV_MAX);
6381da177e4SLinus Torvalds 			break;
6391da177e4SLinus Torvalds 
6401da177e4SLinus Torvalds 		case UI_SET_KEYBIT:
64129506415SDmitry Torokhov 			retval = uinput_set_bit(arg, keybit, KEY_MAX);
6421da177e4SLinus Torvalds 			break;
6431da177e4SLinus Torvalds 
6441da177e4SLinus Torvalds 		case UI_SET_RELBIT:
64529506415SDmitry Torokhov 			retval = uinput_set_bit(arg, relbit, REL_MAX);
6461da177e4SLinus Torvalds 			break;
6471da177e4SLinus Torvalds 
6481da177e4SLinus Torvalds 		case UI_SET_ABSBIT:
64929506415SDmitry Torokhov 			retval = uinput_set_bit(arg, absbit, ABS_MAX);
6501da177e4SLinus Torvalds 			break;
6511da177e4SLinus Torvalds 
6521da177e4SLinus Torvalds 		case UI_SET_MSCBIT:
65329506415SDmitry Torokhov 			retval = uinput_set_bit(arg, mscbit, MSC_MAX);
6541da177e4SLinus Torvalds 			break;
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds 		case UI_SET_LEDBIT:
65729506415SDmitry Torokhov 			retval = uinput_set_bit(arg, ledbit, LED_MAX);
6581da177e4SLinus Torvalds 			break;
6591da177e4SLinus Torvalds 
6601da177e4SLinus Torvalds 		case UI_SET_SNDBIT:
66129506415SDmitry Torokhov 			retval = uinput_set_bit(arg, sndbit, SND_MAX);
6621da177e4SLinus Torvalds 			break;
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds 		case UI_SET_FFBIT:
66529506415SDmitry Torokhov 			retval = uinput_set_bit(arg, ffbit, FF_MAX);
6661da177e4SLinus Torvalds 			break;
6671da177e4SLinus Torvalds 
66859c7c037SDmitry Torokhov 		case UI_SET_SWBIT:
66959c7c037SDmitry Torokhov 			retval = uinput_set_bit(arg, swbit, SW_MAX);
67059c7c037SDmitry Torokhov 			break;
67159c7c037SDmitry Torokhov 
6721da177e4SLinus Torvalds 		case UI_SET_PHYS:
67329506415SDmitry Torokhov 			if (udev->state == UIST_CREATED) {
67429506415SDmitry Torokhov 				retval = -EINVAL;
67529506415SDmitry Torokhov 				goto out;
67629506415SDmitry Torokhov 			}
6771da177e4SLinus Torvalds 			length = strnlen_user(p, 1024);
6781da177e4SLinus Torvalds 			if (length <= 0) {
6791da177e4SLinus Torvalds 				retval = -EFAULT;
6801da177e4SLinus Torvalds 				break;
6811da177e4SLinus Torvalds 			}
6821da177e4SLinus Torvalds 			kfree(udev->dev->phys);
6835b6271bdSDmitry Torokhov 			udev->dev->phys = phys = kmalloc(length, GFP_KERNEL);
6845b6271bdSDmitry Torokhov 			if (!phys) {
6851da177e4SLinus Torvalds 				retval = -ENOMEM;
6861da177e4SLinus Torvalds 				break;
6871da177e4SLinus Torvalds 			}
6885b6271bdSDmitry Torokhov 			if (copy_from_user(phys, p, length)) {
6891da177e4SLinus Torvalds 				udev->dev->phys = NULL;
6905b6271bdSDmitry Torokhov 				kfree(phys);
6915b6271bdSDmitry Torokhov 				retval = -EFAULT;
6921da177e4SLinus Torvalds 				break;
6931da177e4SLinus Torvalds 			}
6945b6271bdSDmitry Torokhov 			phys[length - 1] = '\0';
6951da177e4SLinus Torvalds 			break;
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds 		case UI_BEGIN_FF_UPLOAD:
6982d56f3a3SPhilip Langdale 			retval = uinput_ff_upload_from_user(p, &ff_up);
6992d56f3a3SPhilip Langdale 			if (retval)
7001da177e4SLinus Torvalds 				break;
7012d56f3a3SPhilip Langdale 
7021da177e4SLinus Torvalds 			req = uinput_request_find(udev, ff_up.request_id);
7032d56f3a3SPhilip Langdale 			if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) {
7041da177e4SLinus Torvalds 				retval = -EINVAL;
7051da177e4SLinus Torvalds 				break;
7061da177e4SLinus Torvalds 			}
7072d56f3a3SPhilip Langdale 
7081da177e4SLinus Torvalds 			ff_up.retval = 0;
7092d56f3a3SPhilip Langdale 			ff_up.effect = *req->u.upload.effect;
710ff462551SAnssi Hannula 			if (req->u.upload.old)
7112d56f3a3SPhilip Langdale 				ff_up.old = *req->u.upload.old;
712ff462551SAnssi Hannula 			else
713ff462551SAnssi Hannula 				memset(&ff_up.old, 0, sizeof(struct ff_effect));
714ff462551SAnssi Hannula 
7152d56f3a3SPhilip Langdale 			retval = uinput_ff_upload_to_user(p, &ff_up);
7161da177e4SLinus Torvalds 			break;
7171da177e4SLinus Torvalds 
7181da177e4SLinus Torvalds 		case UI_BEGIN_FF_ERASE:
7191da177e4SLinus Torvalds 			if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
7201da177e4SLinus Torvalds 				retval = -EFAULT;
7211da177e4SLinus Torvalds 				break;
7221da177e4SLinus Torvalds 			}
7232d56f3a3SPhilip Langdale 
7241da177e4SLinus Torvalds 			req = uinput_request_find(udev, ff_erase.request_id);
7252d56f3a3SPhilip Langdale 			if (!req || req->code != UI_FF_ERASE) {
7261da177e4SLinus Torvalds 				retval = -EINVAL;
7271da177e4SLinus Torvalds 				break;
7281da177e4SLinus Torvalds 			}
7292d56f3a3SPhilip Langdale 
7301da177e4SLinus Torvalds 			ff_erase.retval = 0;
7311da177e4SLinus Torvalds 			ff_erase.effect_id = req->u.effect_id;
7321da177e4SLinus Torvalds 			if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
7331da177e4SLinus Torvalds 				retval = -EFAULT;
7341da177e4SLinus Torvalds 				break;
7351da177e4SLinus Torvalds 			}
7362d56f3a3SPhilip Langdale 
7371da177e4SLinus Torvalds 			break;
7381da177e4SLinus Torvalds 
7391da177e4SLinus Torvalds 		case UI_END_FF_UPLOAD:
7402d56f3a3SPhilip Langdale 			retval = uinput_ff_upload_from_user(p, &ff_up);
7412d56f3a3SPhilip Langdale 			if (retval)
7421da177e4SLinus Torvalds 				break;
7432d56f3a3SPhilip Langdale 
7441da177e4SLinus Torvalds 			req = uinput_request_find(udev, ff_up.request_id);
7452d56f3a3SPhilip Langdale 			if (!req || req->code != UI_FF_UPLOAD ||
7462d56f3a3SPhilip Langdale 			    !req->u.upload.effect) {
7471da177e4SLinus Torvalds 				retval = -EINVAL;
7481da177e4SLinus Torvalds 				break;
7491da177e4SLinus Torvalds 			}
7502d56f3a3SPhilip Langdale 
7511da177e4SLinus Torvalds 			req->retval = ff_up.retval;
7520048e603SDmitry Torokhov 			uinput_request_done(udev, req);
7531da177e4SLinus Torvalds 			break;
7541da177e4SLinus Torvalds 
7551da177e4SLinus Torvalds 		case UI_END_FF_ERASE:
7561da177e4SLinus Torvalds 			if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
7571da177e4SLinus Torvalds 				retval = -EFAULT;
7581da177e4SLinus Torvalds 				break;
7591da177e4SLinus Torvalds 			}
7602d56f3a3SPhilip Langdale 
7611da177e4SLinus Torvalds 			req = uinput_request_find(udev, ff_erase.request_id);
7622d56f3a3SPhilip Langdale 			if (!req || req->code != UI_FF_ERASE) {
7631da177e4SLinus Torvalds 				retval = -EINVAL;
7641da177e4SLinus Torvalds 				break;
7651da177e4SLinus Torvalds 			}
7662d56f3a3SPhilip Langdale 
7671da177e4SLinus Torvalds 			req->retval = ff_erase.retval;
7680048e603SDmitry Torokhov 			uinput_request_done(udev, req);
7691da177e4SLinus Torvalds 			break;
7701da177e4SLinus Torvalds 
7711da177e4SLinus Torvalds 		default:
7721da177e4SLinus Torvalds 			retval = -EINVAL;
7731da177e4SLinus Torvalds 	}
77429506415SDmitry Torokhov 
77529506415SDmitry Torokhov  out:
776221979aaSDmitry Torokhov 	mutex_unlock(&udev->mutex);
7771da177e4SLinus Torvalds 	return retval;
7781da177e4SLinus Torvalds }
7791da177e4SLinus Torvalds 
7802d56f3a3SPhilip Langdale static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
7812d56f3a3SPhilip Langdale {
7822d56f3a3SPhilip Langdale 	return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg);
7832d56f3a3SPhilip Langdale }
7842d56f3a3SPhilip Langdale 
7852d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
7862d56f3a3SPhilip Langdale static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
7872d56f3a3SPhilip Langdale {
7882d56f3a3SPhilip Langdale 	return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
7892d56f3a3SPhilip Langdale }
7902d56f3a3SPhilip Langdale #endif
7912d56f3a3SPhilip Langdale 
7922b8693c0SArjan van de Ven static const struct file_operations uinput_fops = {
7931da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
7941da177e4SLinus Torvalds 	.open		= uinput_open,
79529506415SDmitry Torokhov 	.release	= uinput_release,
7961da177e4SLinus Torvalds 	.read		= uinput_read,
7971da177e4SLinus Torvalds 	.write		= uinput_write,
7981da177e4SLinus Torvalds 	.poll		= uinput_poll,
79929506415SDmitry Torokhov 	.unlocked_ioctl	= uinput_ioctl,
8002d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
8012d56f3a3SPhilip Langdale 	.compat_ioctl	= uinput_compat_ioctl,
8022d56f3a3SPhilip Langdale #endif
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 };
8101da177e4SLinus Torvalds 
8111da177e4SLinus Torvalds static int __init uinput_init(void)
8121da177e4SLinus Torvalds {
8131da177e4SLinus Torvalds 	return misc_register(&uinput_misc);
8141da177e4SLinus Torvalds }
8151da177e4SLinus Torvalds 
8161da177e4SLinus Torvalds static void __exit uinput_exit(void)
8171da177e4SLinus Torvalds {
8181da177e4SLinus Torvalds 	misc_deregister(&uinput_misc);
8191da177e4SLinus Torvalds }
8201da177e4SLinus Torvalds 
8211da177e4SLinus Torvalds MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
8221da177e4SLinus Torvalds MODULE_DESCRIPTION("User level driver support for input subsystem");
8231da177e4SLinus Torvalds MODULE_LICENSE("GPL");
824ff462551SAnssi Hannula MODULE_VERSION("0.3");
8251da177e4SLinus Torvalds 
8261da177e4SLinus Torvalds module_init(uinput_init);
8271da177e4SLinus Torvalds module_exit(uinput_exit);
8281da177e4SLinus Torvalds 
829