xref: /openbmc/linux/drivers/input/misc/uinput.c (revision 2d56f3a3)
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>
40*2d56f3a3SPhilip 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 
570048e603SDmitry Torokhov static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request)
581da177e4SLinus Torvalds {
591da177e4SLinus Torvalds 	/* Atomically allocate an ID for the given request. Returns 0 on success. */
601da177e4SLinus Torvalds 	int id;
61152c12f5SDmitry Torokhov 	int err = -1;
621da177e4SLinus Torvalds 
630048e603SDmitry Torokhov 	spin_lock(&udev->requests_lock);
64152c12f5SDmitry Torokhov 
651da177e4SLinus Torvalds 	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 		}
72152c12f5SDmitry Torokhov 
730048e603SDmitry Torokhov 	spin_unlock(&udev->requests_lock);
74152c12f5SDmitry Torokhov 	return err;
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds static struct uinput_request* uinput_request_find(struct uinput_device *udev, int id)
781da177e4SLinus Torvalds {
791da177e4SLinus Torvalds 	/* Find an input request, by ID. Returns NULL if the ID isn't valid. */
801da177e4SLinus Torvalds 	if (id >= UINPUT_NUM_REQUESTS || id < 0)
811da177e4SLinus Torvalds 		return NULL;
82*2d56f3a3SPhilip Langdale 
831da177e4SLinus Torvalds 	return udev->requests[id];
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds 
860048e603SDmitry Torokhov static inline int uinput_request_reserve_slot(struct uinput_device *udev, struct uinput_request *request)
871da177e4SLinus Torvalds {
880048e603SDmitry Torokhov 	/* Allocate slot. If none are available right away, wait. */
890048e603SDmitry Torokhov 	return wait_event_interruptible(udev->requests_waitq,
900048e603SDmitry Torokhov 					!uinput_request_alloc_id(udev, request));
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds 
930048e603SDmitry Torokhov static void uinput_request_done(struct uinput_device *udev, struct uinput_request *request)
941da177e4SLinus Torvalds {
950048e603SDmitry Torokhov 	/* Mark slot as available */
960048e603SDmitry Torokhov 	udev->requests[request->id] = NULL;
97e597f0c8SDmitry Torokhov 	wake_up(&udev->requests_waitq);
98e7507ed9SDmitry Torokhov 
99e7507ed9SDmitry Torokhov 	complete(&request->done);
1000048e603SDmitry Torokhov }
1010048e603SDmitry Torokhov 
1020048e603SDmitry Torokhov static int uinput_request_submit(struct input_dev *dev, struct uinput_request *request)
1030048e603SDmitry Torokhov {
1041da177e4SLinus Torvalds 	/* Tell our userspace app about this new request by queueing an input event */
1051da177e4SLinus Torvalds 	uinput_dev_event(dev, EV_UINPUT, request->code, request->id);
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds 	/* Wait for the request to complete */
108e597f0c8SDmitry Torokhov 	wait_for_completion(&request->done);
109e597f0c8SDmitry Torokhov 	return request->retval;
1101da177e4SLinus Torvalds }
1111da177e4SLinus Torvalds 
112ff462551SAnssi Hannula static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
113ff462551SAnssi Hannula {
114ff462551SAnssi Hannula 	uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
115ff462551SAnssi Hannula }
116ff462551SAnssi Hannula 
117ff462551SAnssi Hannula static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude)
118ff462551SAnssi Hannula {
119ff462551SAnssi Hannula 	uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);
120ff462551SAnssi Hannula }
121ff462551SAnssi Hannula 
122ff462551SAnssi Hannula static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
123ff462551SAnssi Hannula {
124ff462551SAnssi Hannula 	return uinput_dev_event(dev, EV_FF, effect_id, value);
125ff462551SAnssi Hannula }
126ff462551SAnssi Hannula 
127ff462551SAnssi Hannula static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
1281da177e4SLinus Torvalds {
1291da177e4SLinus Torvalds 	struct uinput_request request;
1300048e603SDmitry Torokhov 	int retval;
1311da177e4SLinus Torvalds 
132*2d56f3a3SPhilip Langdale 	/*
133*2d56f3a3SPhilip Langdale 	 * uinput driver does not currently support periodic effects with
134*2d56f3a3SPhilip Langdale 	 * custom waveform since it does not have a way to pass buffer of
135*2d56f3a3SPhilip Langdale 	 * samples (custom_data) to userspace. If ever there is a device
136*2d56f3a3SPhilip Langdale 	 * supporting custom waveforms we would need to define an additional
137*2d56f3a3SPhilip Langdale 	 * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out.
138*2d56f3a3SPhilip Langdale 	 */
139*2d56f3a3SPhilip Langdale 	if (effect->type == FF_PERIODIC &&
140*2d56f3a3SPhilip Langdale 			effect->u.periodic.waveform == FF_CUSTOM)
141*2d56f3a3SPhilip Langdale 		return -EINVAL;
142*2d56f3a3SPhilip Langdale 
1430048e603SDmitry Torokhov 	request.id = -1;
1440048e603SDmitry Torokhov 	init_completion(&request.done);
1450048e603SDmitry Torokhov 	request.code = UI_FF_UPLOAD;
146ff462551SAnssi Hannula 	request.u.upload.effect = effect;
147ff462551SAnssi Hannula 	request.u.upload.old = old;
1480048e603SDmitry Torokhov 
149373f9713SDmitry Torokhov 	retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request);
1500048e603SDmitry Torokhov 	if (!retval)
1510048e603SDmitry Torokhov 		retval = uinput_request_submit(dev, &request);
1520048e603SDmitry Torokhov 
1530048e603SDmitry Torokhov 	return retval;
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
1571da177e4SLinus Torvalds {
1581da177e4SLinus Torvalds 	struct uinput_request request;
1590048e603SDmitry Torokhov 	int retval;
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	if (!test_bit(EV_FF, dev->evbit))
1621da177e4SLinus Torvalds 		return -ENOSYS;
1631da177e4SLinus Torvalds 
1640048e603SDmitry Torokhov 	request.id = -1;
1650048e603SDmitry Torokhov 	init_completion(&request.done);
1660048e603SDmitry Torokhov 	request.code = UI_FF_ERASE;
1671da177e4SLinus Torvalds 	request.u.effect_id = effect_id;
1680048e603SDmitry Torokhov 
169373f9713SDmitry Torokhov 	retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request);
1700048e603SDmitry Torokhov 	if (!retval)
1710048e603SDmitry Torokhov 		retval = uinput_request_submit(dev, &request);
1720048e603SDmitry Torokhov 
1730048e603SDmitry Torokhov 	return retval;
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds 
17629506415SDmitry Torokhov static void uinput_destroy_device(struct uinput_device *udev)
17729506415SDmitry Torokhov {
17829506415SDmitry Torokhov 	const char *name, *phys;
17929506415SDmitry Torokhov 
18029506415SDmitry Torokhov 	if (udev->dev) {
18129506415SDmitry Torokhov 		name = udev->dev->name;
18229506415SDmitry Torokhov 		phys = udev->dev->phys;
18329506415SDmitry Torokhov 		if (udev->state == UIST_CREATED)
18429506415SDmitry Torokhov 			input_unregister_device(udev->dev);
18529506415SDmitry Torokhov 		else
18629506415SDmitry Torokhov 			input_free_device(udev->dev);
18729506415SDmitry Torokhov 		kfree(name);
18829506415SDmitry Torokhov 		kfree(phys);
18929506415SDmitry Torokhov 		udev->dev = NULL;
19029506415SDmitry Torokhov 	}
19129506415SDmitry Torokhov 
19229506415SDmitry Torokhov 	udev->state = UIST_NEW_DEVICE;
19329506415SDmitry Torokhov }
19429506415SDmitry Torokhov 
1951da177e4SLinus Torvalds static int uinput_create_device(struct uinput_device *udev)
1961da177e4SLinus Torvalds {
197ff462551SAnssi Hannula 	struct input_dev *dev = udev->dev;
19829506415SDmitry Torokhov 	int error;
19929506415SDmitry Torokhov 
20029506415SDmitry Torokhov 	if (udev->state != UIST_SETUP_COMPLETE) {
2011da177e4SLinus Torvalds 		printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
2021da177e4SLinus Torvalds 		return -EINVAL;
2031da177e4SLinus Torvalds 	}
2041da177e4SLinus Torvalds 
205ff462551SAnssi Hannula 	if (udev->ff_effects_max) {
206ff462551SAnssi Hannula 		error = input_ff_create(dev, udev->ff_effects_max);
207ff462551SAnssi Hannula 		if (error)
208ff462551SAnssi Hannula 			goto fail1;
209ff462551SAnssi Hannula 
210ff462551SAnssi Hannula 		dev->ff->upload = uinput_dev_upload_effect;
211ff462551SAnssi Hannula 		dev->ff->erase = uinput_dev_erase_effect;
212ff462551SAnssi Hannula 		dev->ff->playback = uinput_dev_playback;
213ff462551SAnssi Hannula 		dev->ff->set_gain = uinput_dev_set_gain;
214ff462551SAnssi Hannula 		dev->ff->set_autocenter = uinput_dev_set_autocenter;
2151da177e4SLinus Torvalds 	}
2161da177e4SLinus Torvalds 
217ff462551SAnssi Hannula 	error = input_register_device(udev->dev);
218ff462551SAnssi Hannula 	if (error)
219ff462551SAnssi Hannula 		goto fail2;
220ff462551SAnssi Hannula 
22129506415SDmitry Torokhov 	udev->state = UIST_CREATED;
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds 	return 0;
224ff462551SAnssi Hannula 
225ff462551SAnssi Hannula  fail2:	input_ff_destroy(dev);
226ff462551SAnssi Hannula  fail1: uinput_destroy_device(udev);
227ff462551SAnssi Hannula 	return error;
2281da177e4SLinus Torvalds }
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds static int uinput_open(struct inode *inode, struct file *file)
2311da177e4SLinus Torvalds {
2321da177e4SLinus Torvalds 	struct uinput_device *newdev;
2331da177e4SLinus Torvalds 
23429506415SDmitry Torokhov 	newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
2351da177e4SLinus Torvalds 	if (!newdev)
23629506415SDmitry Torokhov 		return -ENOMEM;
23729506415SDmitry Torokhov 
23887029658SArnd Bergmann 	lock_kernel();
239221979aaSDmitry Torokhov 	mutex_init(&newdev->mutex);
2400048e603SDmitry Torokhov 	spin_lock_init(&newdev->requests_lock);
2411da177e4SLinus Torvalds 	init_waitqueue_head(&newdev->requests_waitq);
24229506415SDmitry Torokhov 	init_waitqueue_head(&newdev->waitq);
24329506415SDmitry Torokhov 	newdev->state = UIST_NEW_DEVICE;
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	file->private_data = newdev;
24687029658SArnd Bergmann 	unlock_kernel();
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	return 0;
2491da177e4SLinus Torvalds }
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds static int uinput_validate_absbits(struct input_dev *dev)
2521da177e4SLinus Torvalds {
2531da177e4SLinus Torvalds 	unsigned int cnt;
2541da177e4SLinus Torvalds 	int retval = 0;
2551da177e4SLinus Torvalds 
2561da177e4SLinus Torvalds 	for (cnt = 0; cnt < ABS_MAX + 1; cnt++) {
2571da177e4SLinus Torvalds 		if (!test_bit(cnt, dev->absbit))
2581da177e4SLinus Torvalds 			continue;
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds 		if ((dev->absmax[cnt] <= dev->absmin[cnt])) {
2611da177e4SLinus Torvalds 			printk(KERN_DEBUG
2621da177e4SLinus Torvalds 				"%s: invalid abs[%02x] min:%d max:%d\n",
2631da177e4SLinus Torvalds 				UINPUT_NAME, cnt,
2641da177e4SLinus Torvalds 				dev->absmin[cnt], dev->absmax[cnt]);
2651da177e4SLinus Torvalds 			retval = -EINVAL;
2661da177e4SLinus Torvalds 			break;
2671da177e4SLinus Torvalds 		}
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds 		if (dev->absflat[cnt] > (dev->absmax[cnt] - dev->absmin[cnt])) {
2701da177e4SLinus Torvalds 			printk(KERN_DEBUG
2711da177e4SLinus Torvalds 				"%s: absflat[%02x] out of range: %d "
2721da177e4SLinus Torvalds 				"(min:%d/max:%d)\n",
2731da177e4SLinus Torvalds 				UINPUT_NAME, cnt, dev->absflat[cnt],
2741da177e4SLinus Torvalds 				dev->absmin[cnt], dev->absmax[cnt]);
2751da177e4SLinus Torvalds 			retval = -EINVAL;
2761da177e4SLinus Torvalds 			break;
2771da177e4SLinus Torvalds 		}
2781da177e4SLinus Torvalds 	}
2791da177e4SLinus Torvalds 	return retval;
2801da177e4SLinus Torvalds }
2811da177e4SLinus Torvalds 
28229506415SDmitry Torokhov static int uinput_allocate_device(struct uinput_device *udev)
28329506415SDmitry Torokhov {
28429506415SDmitry Torokhov 	udev->dev = input_allocate_device();
28529506415SDmitry Torokhov 	if (!udev->dev)
28629506415SDmitry Torokhov 		return -ENOMEM;
28729506415SDmitry Torokhov 
28829506415SDmitry Torokhov 	udev->dev->event = uinput_dev_event;
289373f9713SDmitry Torokhov 	input_set_drvdata(udev->dev, udev);
29029506415SDmitry Torokhov 
29129506415SDmitry Torokhov 	return 0;
29229506415SDmitry Torokhov }
29329506415SDmitry Torokhov 
29429506415SDmitry Torokhov static int uinput_setup_device(struct uinput_device *udev, const char __user *buffer, size_t count)
2951da177e4SLinus Torvalds {
2961da177e4SLinus Torvalds 	struct uinput_user_dev	*user_dev;
2971da177e4SLinus Torvalds 	struct input_dev	*dev;
2985b6271bdSDmitry Torokhov 	char			*name;
299152c12f5SDmitry Torokhov 	int			size;
300152c12f5SDmitry Torokhov 	int			retval;
3011da177e4SLinus Torvalds 
30229506415SDmitry Torokhov 	if (count != sizeof(struct uinput_user_dev))
30329506415SDmitry Torokhov 		return -EINVAL;
3041da177e4SLinus Torvalds 
30529506415SDmitry Torokhov 	if (!udev->dev) {
30629506415SDmitry Torokhov 		retval = uinput_allocate_device(udev);
30729506415SDmitry Torokhov 		if (retval)
30829506415SDmitry Torokhov 			return retval;
30929506415SDmitry Torokhov 	}
31029506415SDmitry Torokhov 
3111da177e4SLinus Torvalds 	dev = udev->dev;
3121da177e4SLinus Torvalds 
313152c12f5SDmitry Torokhov 	user_dev = kmalloc(sizeof(struct uinput_user_dev), GFP_KERNEL);
31429506415SDmitry Torokhov 	if (!user_dev)
31529506415SDmitry Torokhov 		return -ENOMEM;
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	if (copy_from_user(user_dev, buffer, sizeof(struct uinput_user_dev))) {
3181da177e4SLinus Torvalds 		retval = -EFAULT;
3191da177e4SLinus Torvalds 		goto exit;
3201da177e4SLinus Torvalds 	}
3211da177e4SLinus Torvalds 
322ff462551SAnssi Hannula 	udev->ff_effects_max = user_dev->ff_effects_max;
323ff462551SAnssi Hannula 
3241da177e4SLinus Torvalds 	size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
32529506415SDmitry Torokhov 	if (!size) {
32629506415SDmitry Torokhov 		retval = -EINVAL;
32729506415SDmitry Torokhov 		goto exit;
32829506415SDmitry Torokhov 	}
32929506415SDmitry Torokhov 
33029506415SDmitry Torokhov 	kfree(dev->name);
3315b6271bdSDmitry Torokhov 	dev->name = name = kmalloc(size, GFP_KERNEL);
3325b6271bdSDmitry Torokhov 	if (!name) {
3331da177e4SLinus Torvalds 		retval = -ENOMEM;
3341da177e4SLinus Torvalds 		goto exit;
3351da177e4SLinus Torvalds 	}
3365b6271bdSDmitry Torokhov 	strlcpy(name, user_dev->name, size);
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 	dev->id.bustype	= user_dev->id.bustype;
3391da177e4SLinus Torvalds 	dev->id.vendor	= user_dev->id.vendor;
3401da177e4SLinus Torvalds 	dev->id.product	= user_dev->id.product;
3411da177e4SLinus Torvalds 	dev->id.version	= user_dev->id.version;
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 	size = sizeof(int) * (ABS_MAX + 1);
3441da177e4SLinus Torvalds 	memcpy(dev->absmax, user_dev->absmax, size);
3451da177e4SLinus Torvalds 	memcpy(dev->absmin, user_dev->absmin, size);
3461da177e4SLinus Torvalds 	memcpy(dev->absfuzz, user_dev->absfuzz, size);
3471da177e4SLinus Torvalds 	memcpy(dev->absflat, user_dev->absflat, size);
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds 	/* check if absmin/absmax/absfuzz/absflat are filled as
3501da177e4SLinus Torvalds 	 * told in Documentation/input/input-programming.txt */
3511da177e4SLinus Torvalds 	if (test_bit(EV_ABS, dev->evbit)) {
35229506415SDmitry Torokhov 		retval = uinput_validate_absbits(dev);
35329506415SDmitry Torokhov 		if (retval < 0)
35429506415SDmitry Torokhov 			goto exit;
3551da177e4SLinus Torvalds 	}
35629506415SDmitry Torokhov 
35729506415SDmitry Torokhov 	udev->state = UIST_SETUP_COMPLETE;
35829506415SDmitry Torokhov 	retval = count;
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds  exit:
3611da177e4SLinus Torvalds 	kfree(user_dev);
3621da177e4SLinus Torvalds 	return retval;
3631da177e4SLinus Torvalds }
3641da177e4SLinus Torvalds 
36529506415SDmitry Torokhov static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char __user *buffer, size_t count)
3661da177e4SLinus Torvalds {
3671da177e4SLinus Torvalds 	struct input_event ev;
3681da177e4SLinus Torvalds 
369*2d56f3a3SPhilip Langdale 	if (count < input_event_size())
37029506415SDmitry Torokhov 		return -EINVAL;
37129506415SDmitry Torokhov 
372*2d56f3a3SPhilip Langdale 	if (input_event_from_user(buffer, &ev))
3731da177e4SLinus Torvalds 		return -EFAULT;
3741da177e4SLinus Torvalds 
37529506415SDmitry Torokhov 	input_event(udev->dev, ev.type, ev.code, ev.value);
37629506415SDmitry Torokhov 
377*2d56f3a3SPhilip Langdale 	return input_event_size();
37829506415SDmitry Torokhov }
37929506415SDmitry Torokhov 
38029506415SDmitry Torokhov static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
38129506415SDmitry Torokhov {
38229506415SDmitry Torokhov 	struct uinput_device *udev = file->private_data;
38329506415SDmitry Torokhov 	int retval;
38429506415SDmitry Torokhov 
385221979aaSDmitry Torokhov 	retval = mutex_lock_interruptible(&udev->mutex);
38629506415SDmitry Torokhov 	if (retval)
38729506415SDmitry Torokhov 		return retval;
38829506415SDmitry Torokhov 
38929506415SDmitry Torokhov 	retval = udev->state == UIST_CREATED ?
39029506415SDmitry Torokhov 			uinput_inject_event(udev, buffer, count) :
39129506415SDmitry Torokhov 			uinput_setup_device(udev, buffer, count);
39229506415SDmitry Torokhov 
393221979aaSDmitry Torokhov 	mutex_unlock(&udev->mutex);
39429506415SDmitry Torokhov 
39529506415SDmitry Torokhov 	return retval;
3961da177e4SLinus Torvalds }
3971da177e4SLinus Torvalds 
3981da177e4SLinus Torvalds static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
3991da177e4SLinus Torvalds {
4001da177e4SLinus Torvalds 	struct uinput_device *udev = file->private_data;
4011da177e4SLinus Torvalds 	int retval = 0;
4021da177e4SLinus Torvalds 
40329506415SDmitry Torokhov 	if (udev->state != UIST_CREATED)
4041da177e4SLinus Torvalds 		return -ENODEV;
4051da177e4SLinus Torvalds 
406152c12f5SDmitry Torokhov 	if (udev->head == udev->tail && (file->f_flags & O_NONBLOCK))
4071da177e4SLinus Torvalds 		return -EAGAIN;
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds 	retval = wait_event_interruptible(udev->waitq,
41029506415SDmitry Torokhov 			udev->head != udev->tail || udev->state != UIST_CREATED);
4111da177e4SLinus Torvalds 	if (retval)
4121da177e4SLinus Torvalds 		return retval;
4131da177e4SLinus Torvalds 
414221979aaSDmitry Torokhov 	retval = mutex_lock_interruptible(&udev->mutex);
41529506415SDmitry Torokhov 	if (retval)
41629506415SDmitry Torokhov 		return retval;
4171da177e4SLinus Torvalds 
41829506415SDmitry Torokhov 	if (udev->state != UIST_CREATED) {
41929506415SDmitry Torokhov 		retval = -ENODEV;
42029506415SDmitry Torokhov 		goto out;
42129506415SDmitry Torokhov 	}
42229506415SDmitry Torokhov 
423*2d56f3a3SPhilip Langdale 	while (udev->head != udev->tail && retval + input_event_size() <= count) {
424*2d56f3a3SPhilip Langdale 		if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) {
42529506415SDmitry Torokhov 			retval = -EFAULT;
42629506415SDmitry Torokhov 			goto out;
42729506415SDmitry Torokhov 		}
4281da177e4SLinus Torvalds 		udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
429*2d56f3a3SPhilip Langdale 		retval += input_event_size();
4301da177e4SLinus Torvalds 	}
4311da177e4SLinus Torvalds 
43229506415SDmitry Torokhov  out:
433221979aaSDmitry Torokhov 	mutex_unlock(&udev->mutex);
43429506415SDmitry Torokhov 
4351da177e4SLinus Torvalds 	return retval;
4361da177e4SLinus Torvalds }
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds static unsigned int uinput_poll(struct file *file, poll_table *wait)
4391da177e4SLinus Torvalds {
4401da177e4SLinus Torvalds 	struct uinput_device *udev = file->private_data;
4411da177e4SLinus Torvalds 
4421da177e4SLinus Torvalds 	poll_wait(file, &udev->waitq, wait);
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 	if (udev->head != udev->tail)
4451da177e4SLinus Torvalds 		return POLLIN | POLLRDNORM;
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 	return 0;
4481da177e4SLinus Torvalds }
4491da177e4SLinus Torvalds 
45029506415SDmitry Torokhov static int uinput_release(struct inode *inode, struct file *file)
4511da177e4SLinus Torvalds {
45229506415SDmitry Torokhov 	struct uinput_device *udev = file->private_data;
4531da177e4SLinus Torvalds 
45429506415SDmitry Torokhov 	uinput_destroy_device(udev);
4551da177e4SLinus Torvalds 	kfree(udev);
4561da177e4SLinus Torvalds 
4571da177e4SLinus Torvalds 	return 0;
4581da177e4SLinus Torvalds }
4591da177e4SLinus Torvalds 
460*2d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
461*2d56f3a3SPhilip Langdale struct uinput_ff_upload_compat {
462*2d56f3a3SPhilip Langdale 	int			request_id;
463*2d56f3a3SPhilip Langdale 	int			retval;
464*2d56f3a3SPhilip Langdale 	struct ff_effect_compat	effect;
465*2d56f3a3SPhilip Langdale 	struct ff_effect_compat	old;
466*2d56f3a3SPhilip Langdale };
467*2d56f3a3SPhilip Langdale 
468*2d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer,
469*2d56f3a3SPhilip Langdale 				    const struct uinput_ff_upload *ff_up)
470*2d56f3a3SPhilip Langdale {
471*2d56f3a3SPhilip Langdale 	if (INPUT_COMPAT_TEST) {
472*2d56f3a3SPhilip Langdale 		struct uinput_ff_upload_compat ff_up_compat;
473*2d56f3a3SPhilip Langdale 
474*2d56f3a3SPhilip Langdale 		ff_up_compat.request_id = ff_up->request_id;
475*2d56f3a3SPhilip Langdale 		ff_up_compat.retval = ff_up->retval;
476*2d56f3a3SPhilip Langdale 		/*
477*2d56f3a3SPhilip Langdale 		 * It so happens that the pointer that gives us the trouble
478*2d56f3a3SPhilip Langdale 		 * is the last field in the structure. Since we don't support
479*2d56f3a3SPhilip Langdale 		 * custom waveforms in uinput anyway we can just copy the whole
480*2d56f3a3SPhilip Langdale 		 * thing (to the compat size) and ignore the pointer.
481*2d56f3a3SPhilip Langdale 		 */
482*2d56f3a3SPhilip Langdale 		memcpy(&ff_up_compat.effect, &ff_up->effect,
483*2d56f3a3SPhilip Langdale 			sizeof(struct ff_effect_compat));
484*2d56f3a3SPhilip Langdale 		memcpy(&ff_up_compat.old, &ff_up->old,
485*2d56f3a3SPhilip Langdale 			sizeof(struct ff_effect_compat));
486*2d56f3a3SPhilip Langdale 
487*2d56f3a3SPhilip Langdale 		if (copy_to_user(buffer, &ff_up_compat,
488*2d56f3a3SPhilip Langdale 				 sizeof(struct uinput_ff_upload_compat)))
489*2d56f3a3SPhilip Langdale 			return -EFAULT;
490*2d56f3a3SPhilip Langdale 	} else {
491*2d56f3a3SPhilip Langdale 		if (copy_to_user(buffer, ff_up,
492*2d56f3a3SPhilip Langdale 				 sizeof(struct uinput_ff_upload)))
493*2d56f3a3SPhilip Langdale 			return -EFAULT;
494*2d56f3a3SPhilip Langdale 	}
495*2d56f3a3SPhilip Langdale 
496*2d56f3a3SPhilip Langdale 	return 0;
497*2d56f3a3SPhilip Langdale }
498*2d56f3a3SPhilip Langdale 
499*2d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer,
500*2d56f3a3SPhilip Langdale 				      struct uinput_ff_upload *ff_up)
501*2d56f3a3SPhilip Langdale {
502*2d56f3a3SPhilip Langdale 	if (INPUT_COMPAT_TEST) {
503*2d56f3a3SPhilip Langdale 		struct uinput_ff_upload_compat ff_up_compat;
504*2d56f3a3SPhilip Langdale 
505*2d56f3a3SPhilip Langdale 		if (copy_from_user(&ff_up_compat, buffer,
506*2d56f3a3SPhilip Langdale 				   sizeof(struct uinput_ff_upload_compat)))
507*2d56f3a3SPhilip Langdale 			return -EFAULT;
508*2d56f3a3SPhilip Langdale 
509*2d56f3a3SPhilip Langdale 		ff_up->request_id = ff_up_compat.request_id;
510*2d56f3a3SPhilip Langdale 		ff_up->retval = ff_up_compat.retval;
511*2d56f3a3SPhilip Langdale 		memcpy(&ff_up->effect, &ff_up_compat.effect,
512*2d56f3a3SPhilip Langdale 			sizeof(struct ff_effect_compat));
513*2d56f3a3SPhilip Langdale 		memcpy(&ff_up->old, &ff_up_compat.old,
514*2d56f3a3SPhilip Langdale 			sizeof(struct ff_effect_compat));
515*2d56f3a3SPhilip Langdale 
516*2d56f3a3SPhilip Langdale 	} else {
517*2d56f3a3SPhilip Langdale 		if (copy_from_user(ff_up, buffer,
518*2d56f3a3SPhilip Langdale 				   sizeof(struct uinput_ff_upload)))
519*2d56f3a3SPhilip Langdale 			return -EFAULT;
520*2d56f3a3SPhilip Langdale 	}
521*2d56f3a3SPhilip Langdale 
522*2d56f3a3SPhilip Langdale 	return 0;
523*2d56f3a3SPhilip Langdale }
524*2d56f3a3SPhilip Langdale 
525*2d56f3a3SPhilip Langdale #else
526*2d56f3a3SPhilip Langdale 
527*2d56f3a3SPhilip Langdale static int uinput_ff_upload_to_user(char __user *buffer,
528*2d56f3a3SPhilip Langdale 				    const struct uinput_ff_upload *ff_up)
529*2d56f3a3SPhilip Langdale {
530*2d56f3a3SPhilip Langdale 	if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload)))
531*2d56f3a3SPhilip Langdale 		return -EFAULT;
532*2d56f3a3SPhilip Langdale 
533*2d56f3a3SPhilip Langdale 	return 0;
534*2d56f3a3SPhilip Langdale }
535*2d56f3a3SPhilip Langdale 
536*2d56f3a3SPhilip Langdale static int uinput_ff_upload_from_user(const char __user *buffer,
537*2d56f3a3SPhilip Langdale 				      struct uinput_ff_upload *ff_up)
538*2d56f3a3SPhilip Langdale {
539*2d56f3a3SPhilip Langdale 	if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload)))
540*2d56f3a3SPhilip Langdale 		return -EFAULT;
541*2d56f3a3SPhilip Langdale 
542*2d56f3a3SPhilip Langdale 	return 0;
543*2d56f3a3SPhilip Langdale }
544*2d56f3a3SPhilip Langdale 
545*2d56f3a3SPhilip Langdale #endif
546*2d56f3a3SPhilip Langdale 
54729506415SDmitry Torokhov #define uinput_set_bit(_arg, _bit, _max)		\
54829506415SDmitry Torokhov ({							\
54929506415SDmitry Torokhov 	int __ret = 0;					\
55029506415SDmitry Torokhov 	if (udev->state == UIST_CREATED)		\
55129506415SDmitry Torokhov 		__ret =  -EINVAL;			\
55229506415SDmitry Torokhov 	else if ((_arg) > (_max))			\
55329506415SDmitry Torokhov 		__ret = -EINVAL;			\
55429506415SDmitry Torokhov 	else set_bit((_arg), udev->dev->_bit);		\
55529506415SDmitry Torokhov 	__ret;						\
55629506415SDmitry Torokhov })
5571da177e4SLinus Torvalds 
558*2d56f3a3SPhilip Langdale static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
559*2d56f3a3SPhilip Langdale 				 unsigned long arg, void __user *p)
5601da177e4SLinus Torvalds {
56129506415SDmitry Torokhov 	int			retval;
562*2d56f3a3SPhilip Langdale 	struct uinput_device	*udev = file->private_data;
5631da177e4SLinus Torvalds 	struct uinput_ff_upload ff_up;
5641da177e4SLinus Torvalds 	struct uinput_ff_erase  ff_erase;
5651da177e4SLinus Torvalds 	struct uinput_request   *req;
5661da177e4SLinus Torvalds 	int                     length;
5675b6271bdSDmitry Torokhov 	char			*phys;
5681da177e4SLinus Torvalds 
569221979aaSDmitry Torokhov 	retval = mutex_lock_interruptible(&udev->mutex);
57029506415SDmitry Torokhov 	if (retval)
57129506415SDmitry Torokhov 		return retval;
57229506415SDmitry Torokhov 
57329506415SDmitry Torokhov 	if (!udev->dev) {
57429506415SDmitry Torokhov 		retval = uinput_allocate_device(udev);
57529506415SDmitry Torokhov 		if (retval)
57629506415SDmitry Torokhov 			goto out;
5771da177e4SLinus Torvalds 	}
5781da177e4SLinus Torvalds 
5791da177e4SLinus Torvalds 	switch (cmd) {
5801da177e4SLinus Torvalds 		case UI_DEV_CREATE:
5811da177e4SLinus Torvalds 			retval = uinput_create_device(udev);
5821da177e4SLinus Torvalds 			break;
5831da177e4SLinus Torvalds 
5841da177e4SLinus Torvalds 		case UI_DEV_DESTROY:
58529506415SDmitry Torokhov 			uinput_destroy_device(udev);
5861da177e4SLinus Torvalds 			break;
5871da177e4SLinus Torvalds 
5881da177e4SLinus Torvalds 		case UI_SET_EVBIT:
58929506415SDmitry Torokhov 			retval = uinput_set_bit(arg, evbit, EV_MAX);
5901da177e4SLinus Torvalds 			break;
5911da177e4SLinus Torvalds 
5921da177e4SLinus Torvalds 		case UI_SET_KEYBIT:
59329506415SDmitry Torokhov 			retval = uinput_set_bit(arg, keybit, KEY_MAX);
5941da177e4SLinus Torvalds 			break;
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds 		case UI_SET_RELBIT:
59729506415SDmitry Torokhov 			retval = uinput_set_bit(arg, relbit, REL_MAX);
5981da177e4SLinus Torvalds 			break;
5991da177e4SLinus Torvalds 
6001da177e4SLinus Torvalds 		case UI_SET_ABSBIT:
60129506415SDmitry Torokhov 			retval = uinput_set_bit(arg, absbit, ABS_MAX);
6021da177e4SLinus Torvalds 			break;
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds 		case UI_SET_MSCBIT:
60529506415SDmitry Torokhov 			retval = uinput_set_bit(arg, mscbit, MSC_MAX);
6061da177e4SLinus Torvalds 			break;
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds 		case UI_SET_LEDBIT:
60929506415SDmitry Torokhov 			retval = uinput_set_bit(arg, ledbit, LED_MAX);
6101da177e4SLinus Torvalds 			break;
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds 		case UI_SET_SNDBIT:
61329506415SDmitry Torokhov 			retval = uinput_set_bit(arg, sndbit, SND_MAX);
6141da177e4SLinus Torvalds 			break;
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds 		case UI_SET_FFBIT:
61729506415SDmitry Torokhov 			retval = uinput_set_bit(arg, ffbit, FF_MAX);
6181da177e4SLinus Torvalds 			break;
6191da177e4SLinus Torvalds 
62059c7c037SDmitry Torokhov 		case UI_SET_SWBIT:
62159c7c037SDmitry Torokhov 			retval = uinput_set_bit(arg, swbit, SW_MAX);
62259c7c037SDmitry Torokhov 			break;
62359c7c037SDmitry Torokhov 
6241da177e4SLinus Torvalds 		case UI_SET_PHYS:
62529506415SDmitry Torokhov 			if (udev->state == UIST_CREATED) {
62629506415SDmitry Torokhov 				retval = -EINVAL;
62729506415SDmitry Torokhov 				goto out;
62829506415SDmitry Torokhov 			}
6291da177e4SLinus Torvalds 			length = strnlen_user(p, 1024);
6301da177e4SLinus Torvalds 			if (length <= 0) {
6311da177e4SLinus Torvalds 				retval = -EFAULT;
6321da177e4SLinus Torvalds 				break;
6331da177e4SLinus Torvalds 			}
6341da177e4SLinus Torvalds 			kfree(udev->dev->phys);
6355b6271bdSDmitry Torokhov 			udev->dev->phys = phys = kmalloc(length, GFP_KERNEL);
6365b6271bdSDmitry Torokhov 			if (!phys) {
6371da177e4SLinus Torvalds 				retval = -ENOMEM;
6381da177e4SLinus Torvalds 				break;
6391da177e4SLinus Torvalds 			}
6405b6271bdSDmitry Torokhov 			if (copy_from_user(phys, p, length)) {
6411da177e4SLinus Torvalds 				udev->dev->phys = NULL;
6425b6271bdSDmitry Torokhov 				kfree(phys);
6435b6271bdSDmitry Torokhov 				retval = -EFAULT;
6441da177e4SLinus Torvalds 				break;
6451da177e4SLinus Torvalds 			}
6465b6271bdSDmitry Torokhov 			phys[length - 1] = '\0';
6471da177e4SLinus Torvalds 			break;
6481da177e4SLinus Torvalds 
6491da177e4SLinus Torvalds 		case UI_BEGIN_FF_UPLOAD:
650*2d56f3a3SPhilip Langdale 			retval = uinput_ff_upload_from_user(p, &ff_up);
651*2d56f3a3SPhilip Langdale 			if (retval)
6521da177e4SLinus Torvalds 				break;
653*2d56f3a3SPhilip Langdale 
6541da177e4SLinus Torvalds 			req = uinput_request_find(udev, ff_up.request_id);
655*2d56f3a3SPhilip Langdale 			if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) {
6561da177e4SLinus Torvalds 				retval = -EINVAL;
6571da177e4SLinus Torvalds 				break;
6581da177e4SLinus Torvalds 			}
659*2d56f3a3SPhilip Langdale 
6601da177e4SLinus Torvalds 			ff_up.retval = 0;
661*2d56f3a3SPhilip Langdale 			ff_up.effect = *req->u.upload.effect;
662ff462551SAnssi Hannula 			if (req->u.upload.old)
663*2d56f3a3SPhilip Langdale 				ff_up.old = *req->u.upload.old;
664ff462551SAnssi Hannula 			else
665ff462551SAnssi Hannula 				memset(&ff_up.old, 0, sizeof(struct ff_effect));
666ff462551SAnssi Hannula 
667*2d56f3a3SPhilip Langdale 			retval = uinput_ff_upload_to_user(p, &ff_up);
6681da177e4SLinus Torvalds 			break;
6691da177e4SLinus Torvalds 
6701da177e4SLinus Torvalds 		case UI_BEGIN_FF_ERASE:
6711da177e4SLinus Torvalds 			if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
6721da177e4SLinus Torvalds 				retval = -EFAULT;
6731da177e4SLinus Torvalds 				break;
6741da177e4SLinus Torvalds 			}
675*2d56f3a3SPhilip Langdale 
6761da177e4SLinus Torvalds 			req = uinput_request_find(udev, ff_erase.request_id);
677*2d56f3a3SPhilip Langdale 			if (!req || req->code != UI_FF_ERASE) {
6781da177e4SLinus Torvalds 				retval = -EINVAL;
6791da177e4SLinus Torvalds 				break;
6801da177e4SLinus Torvalds 			}
681*2d56f3a3SPhilip Langdale 
6821da177e4SLinus Torvalds 			ff_erase.retval = 0;
6831da177e4SLinus Torvalds 			ff_erase.effect_id = req->u.effect_id;
6841da177e4SLinus Torvalds 			if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
6851da177e4SLinus Torvalds 				retval = -EFAULT;
6861da177e4SLinus Torvalds 				break;
6871da177e4SLinus Torvalds 			}
688*2d56f3a3SPhilip Langdale 
6891da177e4SLinus Torvalds 			break;
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds 		case UI_END_FF_UPLOAD:
692*2d56f3a3SPhilip Langdale 			retval = uinput_ff_upload_from_user(p, &ff_up);
693*2d56f3a3SPhilip Langdale 			if (retval)
6941da177e4SLinus Torvalds 				break;
695*2d56f3a3SPhilip Langdale 
6961da177e4SLinus Torvalds 			req = uinput_request_find(udev, ff_up.request_id);
697*2d56f3a3SPhilip Langdale 			if (!req || req->code != UI_FF_UPLOAD ||
698*2d56f3a3SPhilip Langdale 			    !req->u.upload.effect) {
6991da177e4SLinus Torvalds 				retval = -EINVAL;
7001da177e4SLinus Torvalds 				break;
7011da177e4SLinus Torvalds 			}
702*2d56f3a3SPhilip Langdale 
7031da177e4SLinus Torvalds 			req->retval = ff_up.retval;
7040048e603SDmitry Torokhov 			uinput_request_done(udev, req);
7051da177e4SLinus Torvalds 			break;
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds 		case UI_END_FF_ERASE:
7081da177e4SLinus Torvalds 			if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
7091da177e4SLinus Torvalds 				retval = -EFAULT;
7101da177e4SLinus Torvalds 				break;
7111da177e4SLinus Torvalds 			}
712*2d56f3a3SPhilip Langdale 
7131da177e4SLinus Torvalds 			req = uinput_request_find(udev, ff_erase.request_id);
714*2d56f3a3SPhilip Langdale 			if (!req || req->code != UI_FF_ERASE) {
7151da177e4SLinus Torvalds 				retval = -EINVAL;
7161da177e4SLinus Torvalds 				break;
7171da177e4SLinus Torvalds 			}
718*2d56f3a3SPhilip Langdale 
7191da177e4SLinus Torvalds 			req->retval = ff_erase.retval;
7200048e603SDmitry Torokhov 			uinput_request_done(udev, req);
7211da177e4SLinus Torvalds 			break;
7221da177e4SLinus Torvalds 
7231da177e4SLinus Torvalds 		default:
7241da177e4SLinus Torvalds 			retval = -EINVAL;
7251da177e4SLinus Torvalds 	}
72629506415SDmitry Torokhov 
72729506415SDmitry Torokhov  out:
728221979aaSDmitry Torokhov 	mutex_unlock(&udev->mutex);
7291da177e4SLinus Torvalds 	return retval;
7301da177e4SLinus Torvalds }
7311da177e4SLinus Torvalds 
732*2d56f3a3SPhilip Langdale static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
733*2d56f3a3SPhilip Langdale {
734*2d56f3a3SPhilip Langdale 	return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg);
735*2d56f3a3SPhilip Langdale }
736*2d56f3a3SPhilip Langdale 
737*2d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
738*2d56f3a3SPhilip Langdale static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
739*2d56f3a3SPhilip Langdale {
740*2d56f3a3SPhilip Langdale 	return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
741*2d56f3a3SPhilip Langdale }
742*2d56f3a3SPhilip Langdale #endif
743*2d56f3a3SPhilip Langdale 
7442b8693c0SArjan van de Ven static const struct file_operations uinput_fops = {
7451da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
7461da177e4SLinus Torvalds 	.open		= uinput_open,
74729506415SDmitry Torokhov 	.release	= uinput_release,
7481da177e4SLinus Torvalds 	.read		= uinput_read,
7491da177e4SLinus Torvalds 	.write		= uinput_write,
7501da177e4SLinus Torvalds 	.poll		= uinput_poll,
75129506415SDmitry Torokhov 	.unlocked_ioctl	= uinput_ioctl,
752*2d56f3a3SPhilip Langdale #ifdef CONFIG_COMPAT
753*2d56f3a3SPhilip Langdale 	.compat_ioctl	= uinput_compat_ioctl,
754*2d56f3a3SPhilip Langdale #endif
7551da177e4SLinus Torvalds };
7561da177e4SLinus Torvalds 
7571da177e4SLinus Torvalds static struct miscdevice uinput_misc = {
7581da177e4SLinus Torvalds 	.fops		= &uinput_fops,
7591da177e4SLinus Torvalds 	.minor		= UINPUT_MINOR,
7601da177e4SLinus Torvalds 	.name		= UINPUT_NAME,
7611da177e4SLinus Torvalds };
7621da177e4SLinus Torvalds 
7631da177e4SLinus Torvalds static int __init uinput_init(void)
7641da177e4SLinus Torvalds {
7651da177e4SLinus Torvalds 	return misc_register(&uinput_misc);
7661da177e4SLinus Torvalds }
7671da177e4SLinus Torvalds 
7681da177e4SLinus Torvalds static void __exit uinput_exit(void)
7691da177e4SLinus Torvalds {
7701da177e4SLinus Torvalds 	misc_deregister(&uinput_misc);
7711da177e4SLinus Torvalds }
7721da177e4SLinus Torvalds 
7731da177e4SLinus Torvalds MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
7741da177e4SLinus Torvalds MODULE_DESCRIPTION("User level driver support for input subsystem");
7751da177e4SLinus Torvalds MODULE_LICENSE("GPL");
776ff462551SAnssi Hannula MODULE_VERSION("0.3");
7771da177e4SLinus Torvalds 
7781da177e4SLinus Torvalds module_init(uinput_init);
7791da177e4SLinus Torvalds module_exit(uinput_exit);
7801da177e4SLinus Torvalds 
781