xref: /linux-6.15/drivers/input/evdev.c (revision f4f37c8e)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Event char devices, giving access to raw input device events.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (c) 1999-2002 Vojtech Pavlik
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify it
71da177e4SLinus Torvalds  * under the terms of the GNU General Public License version 2 as published by
81da177e4SLinus Torvalds  * the Free Software Foundation.
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds #define EVDEV_MINOR_BASE	64
121da177e4SLinus Torvalds #define EVDEV_MINORS		32
131da177e4SLinus Torvalds #define EVDEV_BUFFER_SIZE	64
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds #include <linux/poll.h>
161da177e4SLinus Torvalds #include <linux/slab.h>
171da177e4SLinus Torvalds #include <linux/module.h>
181da177e4SLinus Torvalds #include <linux/init.h>
191da177e4SLinus Torvalds #include <linux/input.h>
201da177e4SLinus Torvalds #include <linux/major.h>
211da177e4SLinus Torvalds #include <linux/device.h>
2252658bb6SJuergen Kreileder #include <linux/compat.h>
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds struct evdev {
251da177e4SLinus Torvalds 	int exist;
261da177e4SLinus Torvalds 	int open;
271da177e4SLinus Torvalds 	int minor;
281da177e4SLinus Torvalds 	char name[16];
291da177e4SLinus Torvalds 	struct input_handle handle;
301da177e4SLinus Torvalds 	wait_queue_head_t wait;
31d0ffb9beSDmitry Torokhov 	struct evdev_client *grab;
32d0ffb9beSDmitry Torokhov 	struct list_head client_list;
336addb1d6SDmitry Torokhov 	spinlock_t client_lock; /* protects client_list */
346addb1d6SDmitry Torokhov 	struct mutex mutex;
359657d75cSDmitry Torokhov 	struct device dev;
361da177e4SLinus Torvalds };
371da177e4SLinus Torvalds 
38d0ffb9beSDmitry Torokhov struct evdev_client {
391da177e4SLinus Torvalds 	struct input_event buffer[EVDEV_BUFFER_SIZE];
401da177e4SLinus Torvalds 	int head;
411da177e4SLinus Torvalds 	int tail;
426addb1d6SDmitry Torokhov 	spinlock_t buffer_lock; /* protects access to buffer, head and tail */
431da177e4SLinus Torvalds 	struct fasync_struct *fasync;
441da177e4SLinus Torvalds 	struct evdev *evdev;
451da177e4SLinus Torvalds 	struct list_head node;
461da177e4SLinus Torvalds };
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds static struct evdev *evdev_table[EVDEV_MINORS];
496addb1d6SDmitry Torokhov static DEFINE_MUTEX(evdev_table_mutex);
501da177e4SLinus Torvalds 
516addb1d6SDmitry Torokhov static void evdev_pass_event(struct evdev_client *client,
526addb1d6SDmitry Torokhov 			     struct input_event *event)
531da177e4SLinus Torvalds {
546addb1d6SDmitry Torokhov 	/*
556addb1d6SDmitry Torokhov 	 * Interrupts are disabled, just acquire the lock
566addb1d6SDmitry Torokhov 	 */
576addb1d6SDmitry Torokhov 	spin_lock(&client->buffer_lock);
586addb1d6SDmitry Torokhov 	client->buffer[client->head++] = *event;
596addb1d6SDmitry Torokhov 	client->head &= EVDEV_BUFFER_SIZE - 1;
606addb1d6SDmitry Torokhov 	spin_unlock(&client->buffer_lock);
611da177e4SLinus Torvalds 
62d0ffb9beSDmitry Torokhov 	kill_fasync(&client->fasync, SIGIO, POLL_IN);
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds 
656addb1d6SDmitry Torokhov /*
6682ba56c2SDmitry Torokhov  * Pass incoming event to all connected clients.
676addb1d6SDmitry Torokhov  */
686addb1d6SDmitry Torokhov static void evdev_event(struct input_handle *handle,
696addb1d6SDmitry Torokhov 			unsigned int type, unsigned int code, int value)
706addb1d6SDmitry Torokhov {
716addb1d6SDmitry Torokhov 	struct evdev *evdev = handle->private;
726addb1d6SDmitry Torokhov 	struct evdev_client *client;
736addb1d6SDmitry Torokhov 	struct input_event event;
746addb1d6SDmitry Torokhov 
756addb1d6SDmitry Torokhov 	do_gettimeofday(&event.time);
766addb1d6SDmitry Torokhov 	event.type = type;
776addb1d6SDmitry Torokhov 	event.code = code;
786addb1d6SDmitry Torokhov 	event.value = value;
796addb1d6SDmitry Torokhov 
8082ba56c2SDmitry Torokhov 	rcu_read_lock();
8182ba56c2SDmitry Torokhov 
826addb1d6SDmitry Torokhov 	client = rcu_dereference(evdev->grab);
836addb1d6SDmitry Torokhov 	if (client)
846addb1d6SDmitry Torokhov 		evdev_pass_event(client, &event);
856addb1d6SDmitry Torokhov 	else
866addb1d6SDmitry Torokhov 		list_for_each_entry_rcu(client, &evdev->client_list, node)
876addb1d6SDmitry Torokhov 			evdev_pass_event(client, &event);
886addb1d6SDmitry Torokhov 
8982ba56c2SDmitry Torokhov 	rcu_read_unlock();
9082ba56c2SDmitry Torokhov 
911da177e4SLinus Torvalds 	wake_up_interruptible(&evdev->wait);
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds static int evdev_fasync(int fd, struct file *file, int on)
951da177e4SLinus Torvalds {
96d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
971da177e4SLinus Torvalds 	int retval;
981e0afb28SDmitry Torokhov 
99d0ffb9beSDmitry Torokhov 	retval = fasync_helper(fd, file, on, &client->fasync);
1001e0afb28SDmitry Torokhov 
1011da177e4SLinus Torvalds 	return retval < 0 ? retval : 0;
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds 
10475e1fcc0SMiklos Szeredi static int evdev_flush(struct file *file, fl_owner_t id)
1051da177e4SLinus Torvalds {
106d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
107d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
1086addb1d6SDmitry Torokhov 	int retval;
1096addb1d6SDmitry Torokhov 
1106addb1d6SDmitry Torokhov 	retval = mutex_lock_interruptible(&evdev->mutex);
1116addb1d6SDmitry Torokhov 	if (retval)
1126addb1d6SDmitry Torokhov 		return retval;
1131e0afb28SDmitry Torokhov 
114d0ffb9beSDmitry Torokhov 	if (!evdev->exist)
1156addb1d6SDmitry Torokhov 		retval = -ENODEV;
1166addb1d6SDmitry Torokhov 	else
1176addb1d6SDmitry Torokhov 		retval = input_flush_device(&evdev->handle, file);
1181e0afb28SDmitry Torokhov 
1196addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
1206addb1d6SDmitry Torokhov 	return retval;
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds 
1239657d75cSDmitry Torokhov static void evdev_free(struct device *dev)
1241da177e4SLinus Torvalds {
1259657d75cSDmitry Torokhov 	struct evdev *evdev = container_of(dev, struct evdev, dev);
1269657d75cSDmitry Torokhov 
1271da177e4SLinus Torvalds 	kfree(evdev);
1281da177e4SLinus Torvalds }
1291da177e4SLinus Torvalds 
1306addb1d6SDmitry Torokhov /*
1316addb1d6SDmitry Torokhov  * Grabs an event device (along with underlying input device).
1326addb1d6SDmitry Torokhov  * This function is called with evdev->mutex taken.
1336addb1d6SDmitry Torokhov  */
1346addb1d6SDmitry Torokhov static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
1356addb1d6SDmitry Torokhov {
1366addb1d6SDmitry Torokhov 	int error;
1376addb1d6SDmitry Torokhov 
1386addb1d6SDmitry Torokhov 	if (evdev->grab)
1396addb1d6SDmitry Torokhov 		return -EBUSY;
1406addb1d6SDmitry Torokhov 
1416addb1d6SDmitry Torokhov 	error = input_grab_device(&evdev->handle);
1426addb1d6SDmitry Torokhov 	if (error)
1436addb1d6SDmitry Torokhov 		return error;
1446addb1d6SDmitry Torokhov 
1456addb1d6SDmitry Torokhov 	rcu_assign_pointer(evdev->grab, client);
14682ba56c2SDmitry Torokhov 	synchronize_rcu();
1476addb1d6SDmitry Torokhov 
1486addb1d6SDmitry Torokhov 	return 0;
1496addb1d6SDmitry Torokhov }
1506addb1d6SDmitry Torokhov 
1516addb1d6SDmitry Torokhov static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
1526addb1d6SDmitry Torokhov {
1536addb1d6SDmitry Torokhov 	if (evdev->grab != client)
1546addb1d6SDmitry Torokhov 		return  -EINVAL;
1556addb1d6SDmitry Torokhov 
1566addb1d6SDmitry Torokhov 	rcu_assign_pointer(evdev->grab, NULL);
15782ba56c2SDmitry Torokhov 	synchronize_rcu();
1586addb1d6SDmitry Torokhov 	input_release_device(&evdev->handle);
1596addb1d6SDmitry Torokhov 
1606addb1d6SDmitry Torokhov 	return 0;
1616addb1d6SDmitry Torokhov }
1626addb1d6SDmitry Torokhov 
1636addb1d6SDmitry Torokhov static void evdev_attach_client(struct evdev *evdev,
1646addb1d6SDmitry Torokhov 				struct evdev_client *client)
1656addb1d6SDmitry Torokhov {
1666addb1d6SDmitry Torokhov 	spin_lock(&evdev->client_lock);
1676addb1d6SDmitry Torokhov 	list_add_tail_rcu(&client->node, &evdev->client_list);
1686addb1d6SDmitry Torokhov 	spin_unlock(&evdev->client_lock);
16982ba56c2SDmitry Torokhov 	synchronize_rcu();
1706addb1d6SDmitry Torokhov }
1716addb1d6SDmitry Torokhov 
1726addb1d6SDmitry Torokhov static void evdev_detach_client(struct evdev *evdev,
1736addb1d6SDmitry Torokhov 				struct evdev_client *client)
1746addb1d6SDmitry Torokhov {
1756addb1d6SDmitry Torokhov 	spin_lock(&evdev->client_lock);
1766addb1d6SDmitry Torokhov 	list_del_rcu(&client->node);
1776addb1d6SDmitry Torokhov 	spin_unlock(&evdev->client_lock);
17882ba56c2SDmitry Torokhov 	synchronize_rcu();
1796addb1d6SDmitry Torokhov }
1806addb1d6SDmitry Torokhov 
1816addb1d6SDmitry Torokhov static int evdev_open_device(struct evdev *evdev)
1826addb1d6SDmitry Torokhov {
1836addb1d6SDmitry Torokhov 	int retval;
1846addb1d6SDmitry Torokhov 
1856addb1d6SDmitry Torokhov 	retval = mutex_lock_interruptible(&evdev->mutex);
1866addb1d6SDmitry Torokhov 	if (retval)
1876addb1d6SDmitry Torokhov 		return retval;
1886addb1d6SDmitry Torokhov 
1896addb1d6SDmitry Torokhov 	if (!evdev->exist)
1906addb1d6SDmitry Torokhov 		retval = -ENODEV;
19106445014SOliver Neukum 	else if (!evdev->open++) {
1926addb1d6SDmitry Torokhov 		retval = input_open_device(&evdev->handle);
19306445014SOliver Neukum 		if (retval)
19406445014SOliver Neukum 			evdev->open--;
19506445014SOliver Neukum 	}
1966addb1d6SDmitry Torokhov 
1976addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
1986addb1d6SDmitry Torokhov 	return retval;
1996addb1d6SDmitry Torokhov }
2006addb1d6SDmitry Torokhov 
2016addb1d6SDmitry Torokhov static void evdev_close_device(struct evdev *evdev)
2026addb1d6SDmitry Torokhov {
2036addb1d6SDmitry Torokhov 	mutex_lock(&evdev->mutex);
2046addb1d6SDmitry Torokhov 
2056addb1d6SDmitry Torokhov 	if (evdev->exist && !--evdev->open)
2066addb1d6SDmitry Torokhov 		input_close_device(&evdev->handle);
2076addb1d6SDmitry Torokhov 
2086addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
2096addb1d6SDmitry Torokhov }
2106addb1d6SDmitry Torokhov 
2116addb1d6SDmitry Torokhov /*
2126addb1d6SDmitry Torokhov  * Wake up users waiting for IO so they can disconnect from
2136addb1d6SDmitry Torokhov  * dead device.
2146addb1d6SDmitry Torokhov  */
2156addb1d6SDmitry Torokhov static void evdev_hangup(struct evdev *evdev)
2166addb1d6SDmitry Torokhov {
2176addb1d6SDmitry Torokhov 	struct evdev_client *client;
2186addb1d6SDmitry Torokhov 
2196addb1d6SDmitry Torokhov 	spin_lock(&evdev->client_lock);
2206addb1d6SDmitry Torokhov 	list_for_each_entry(client, &evdev->client_list, node)
2216addb1d6SDmitry Torokhov 		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
2226addb1d6SDmitry Torokhov 	spin_unlock(&evdev->client_lock);
2236addb1d6SDmitry Torokhov 
2246addb1d6SDmitry Torokhov 	wake_up_interruptible(&evdev->wait);
2256addb1d6SDmitry Torokhov }
2266addb1d6SDmitry Torokhov 
2271da177e4SLinus Torvalds static int evdev_release(struct inode *inode, struct file *file)
2281da177e4SLinus Torvalds {
229d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
230d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
2311da177e4SLinus Torvalds 
2326addb1d6SDmitry Torokhov 	mutex_lock(&evdev->mutex);
2336addb1d6SDmitry Torokhov 	if (evdev->grab == client)
2346addb1d6SDmitry Torokhov 		evdev_ungrab(evdev, client);
2356addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	evdev_fasync(-1, file, 0);
2386addb1d6SDmitry Torokhov 	evdev_detach_client(evdev, client);
239d0ffb9beSDmitry Torokhov 	kfree(client);
2401da177e4SLinus Torvalds 
2416addb1d6SDmitry Torokhov 	evdev_close_device(evdev);
2429657d75cSDmitry Torokhov 	put_device(&evdev->dev);
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	return 0;
2451da177e4SLinus Torvalds }
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds static int evdev_open(struct inode *inode, struct file *file)
2481da177e4SLinus Torvalds {
249d0ffb9beSDmitry Torokhov 	struct evdev *evdev;
2506addb1d6SDmitry Torokhov 	struct evdev_client *client;
2511da177e4SLinus Torvalds 	int i = iminor(inode) - EVDEV_MINOR_BASE;
252d542ed82SDmitry Torokhov 	int error;
2531da177e4SLinus Torvalds 
254d0ffb9beSDmitry Torokhov 	if (i >= EVDEV_MINORS)
2551da177e4SLinus Torvalds 		return -ENODEV;
2561da177e4SLinus Torvalds 
2576addb1d6SDmitry Torokhov 	error = mutex_lock_interruptible(&evdev_table_mutex);
2586addb1d6SDmitry Torokhov 	if (error)
2596addb1d6SDmitry Torokhov 		return error;
260d0ffb9beSDmitry Torokhov 	evdev = evdev_table[i];
2616addb1d6SDmitry Torokhov 	if (evdev)
2629657d75cSDmitry Torokhov 		get_device(&evdev->dev);
2636addb1d6SDmitry Torokhov 	mutex_unlock(&evdev_table_mutex);
2646addb1d6SDmitry Torokhov 
2656addb1d6SDmitry Torokhov 	if (!evdev)
2666addb1d6SDmitry Torokhov 		return -ENODEV;
2679657d75cSDmitry Torokhov 
268d0ffb9beSDmitry Torokhov 	client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
2699657d75cSDmitry Torokhov 	if (!client) {
2709657d75cSDmitry Torokhov 		error = -ENOMEM;
2719657d75cSDmitry Torokhov 		goto err_put_evdev;
2729657d75cSDmitry Torokhov 	}
2731da177e4SLinus Torvalds 
2746addb1d6SDmitry Torokhov 	spin_lock_init(&client->buffer_lock);
275d0ffb9beSDmitry Torokhov 	client->evdev = evdev;
2766addb1d6SDmitry Torokhov 	evdev_attach_client(evdev, client);
2771da177e4SLinus Torvalds 
2786addb1d6SDmitry Torokhov 	error = evdev_open_device(evdev);
2799657d75cSDmitry Torokhov 	if (error)
2809657d75cSDmitry Torokhov 		goto err_free_client;
2811da177e4SLinus Torvalds 
282d0ffb9beSDmitry Torokhov 	file->private_data = client;
2831da177e4SLinus Torvalds 	return 0;
2849657d75cSDmitry Torokhov 
2859657d75cSDmitry Torokhov  err_free_client:
2866addb1d6SDmitry Torokhov 	evdev_detach_client(evdev, client);
2879657d75cSDmitry Torokhov 	kfree(client);
2889657d75cSDmitry Torokhov  err_put_evdev:
2899657d75cSDmitry Torokhov 	put_device(&evdev->dev);
2909657d75cSDmitry Torokhov 	return error;
2911da177e4SLinus Torvalds }
2921da177e4SLinus Torvalds 
29352658bb6SJuergen Kreileder #ifdef CONFIG_COMPAT
2943a51f7c4SDmitry Torokhov 
29552658bb6SJuergen Kreileder struct input_event_compat {
29652658bb6SJuergen Kreileder 	struct compat_timeval time;
29752658bb6SJuergen Kreileder 	__u16 type;
29852658bb6SJuergen Kreileder 	__u16 code;
29952658bb6SJuergen Kreileder 	__s32 value;
30052658bb6SJuergen Kreileder };
30152658bb6SJuergen Kreileder 
302bf2fcc6fSAndi Kleen /* Note to the author of this code: did it ever occur to
303bf2fcc6fSAndi Kleen    you why the ifdefs are needed? Think about it again. -AK */
30452658bb6SJuergen Kreileder #ifdef CONFIG_X86_64
305bf2fcc6fSAndi Kleen #  define COMPAT_TEST is_compat_task()
30652658bb6SJuergen Kreileder #elif defined(CONFIG_IA64)
3076450578fSAl Viro #  define COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current))
308347a8dc3SMartin Schwidefsky #elif defined(CONFIG_S390)
30952658bb6SJuergen Kreileder #  define COMPAT_TEST test_thread_flag(TIF_31BIT)
31059df6bbfSRalf Baechle #elif defined(CONFIG_MIPS)
311293c5bd1SRalf Baechle #  define COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR)
31252658bb6SJuergen Kreileder #else
31352658bb6SJuergen Kreileder #  define COMPAT_TEST test_thread_flag(TIF_32BIT)
31452658bb6SJuergen Kreileder #endif
31552658bb6SJuergen Kreileder 
3163a51f7c4SDmitry Torokhov static inline size_t evdev_event_size(void)
31752658bb6SJuergen Kreileder {
3183a51f7c4SDmitry Torokhov 	return COMPAT_TEST ?
3193a51f7c4SDmitry Torokhov 		sizeof(struct input_event_compat) : sizeof(struct input_event);
3203a51f7c4SDmitry Torokhov }
32152658bb6SJuergen Kreileder 
3226addb1d6SDmitry Torokhov static int evdev_event_from_user(const char __user *buffer,
3236addb1d6SDmitry Torokhov 				 struct input_event *event)
3243a51f7c4SDmitry Torokhov {
3253a51f7c4SDmitry Torokhov 	if (COMPAT_TEST) {
3263a51f7c4SDmitry Torokhov 		struct input_event_compat compat_event;
3273a51f7c4SDmitry Torokhov 
3286addb1d6SDmitry Torokhov 		if (copy_from_user(&compat_event, buffer,
3296addb1d6SDmitry Torokhov 				   sizeof(struct input_event_compat)))
33052658bb6SJuergen Kreileder 			return -EFAULT;
3313a51f7c4SDmitry Torokhov 
3323a51f7c4SDmitry Torokhov 		event->time.tv_sec = compat_event.time.tv_sec;
3333a51f7c4SDmitry Torokhov 		event->time.tv_usec = compat_event.time.tv_usec;
3343a51f7c4SDmitry Torokhov 		event->type = compat_event.type;
3353a51f7c4SDmitry Torokhov 		event->code = compat_event.code;
3363a51f7c4SDmitry Torokhov 		event->value = compat_event.value;
3373a51f7c4SDmitry Torokhov 
3383a51f7c4SDmitry Torokhov 	} else {
3393a51f7c4SDmitry Torokhov 		if (copy_from_user(event, buffer, sizeof(struct input_event)))
3403a51f7c4SDmitry Torokhov 			return -EFAULT;
34152658bb6SJuergen Kreileder 	}
34252658bb6SJuergen Kreileder 
3433a51f7c4SDmitry Torokhov 	return 0;
34452658bb6SJuergen Kreileder }
3453a51f7c4SDmitry Torokhov 
3466addb1d6SDmitry Torokhov static int evdev_event_to_user(char __user *buffer,
3476addb1d6SDmitry Torokhov 				const struct input_event *event)
3483a51f7c4SDmitry Torokhov {
3493a51f7c4SDmitry Torokhov 	if (COMPAT_TEST) {
3503a51f7c4SDmitry Torokhov 		struct input_event_compat compat_event;
3513a51f7c4SDmitry Torokhov 
3523a51f7c4SDmitry Torokhov 		compat_event.time.tv_sec = event->time.tv_sec;
3533a51f7c4SDmitry Torokhov 		compat_event.time.tv_usec = event->time.tv_usec;
3543a51f7c4SDmitry Torokhov 		compat_event.type = event->type;
3553a51f7c4SDmitry Torokhov 		compat_event.code = event->code;
3563a51f7c4SDmitry Torokhov 		compat_event.value = event->value;
3573a51f7c4SDmitry Torokhov 
3586addb1d6SDmitry Torokhov 		if (copy_to_user(buffer, &compat_event,
3596addb1d6SDmitry Torokhov 				 sizeof(struct input_event_compat)))
3603a51f7c4SDmitry Torokhov 			return -EFAULT;
3613a51f7c4SDmitry Torokhov 
3623a51f7c4SDmitry Torokhov 	} else {
3633a51f7c4SDmitry Torokhov 		if (copy_to_user(buffer, event, sizeof(struct input_event)))
3643a51f7c4SDmitry Torokhov 			return -EFAULT;
3653a51f7c4SDmitry Torokhov 	}
3663a51f7c4SDmitry Torokhov 
3673a51f7c4SDmitry Torokhov 	return 0;
3683a51f7c4SDmitry Torokhov }
3693a51f7c4SDmitry Torokhov 
3703a51f7c4SDmitry Torokhov #else
3713a51f7c4SDmitry Torokhov 
3723a51f7c4SDmitry Torokhov static inline size_t evdev_event_size(void)
3733a51f7c4SDmitry Torokhov {
3743a51f7c4SDmitry Torokhov 	return sizeof(struct input_event);
3753a51f7c4SDmitry Torokhov }
3763a51f7c4SDmitry Torokhov 
3776addb1d6SDmitry Torokhov static int evdev_event_from_user(const char __user *buffer,
3786addb1d6SDmitry Torokhov 				 struct input_event *event)
3793a51f7c4SDmitry Torokhov {
3803a51f7c4SDmitry Torokhov 	if (copy_from_user(event, buffer, sizeof(struct input_event)))
3813a51f7c4SDmitry Torokhov 		return -EFAULT;
3823a51f7c4SDmitry Torokhov 
3833a51f7c4SDmitry Torokhov 	return 0;
3843a51f7c4SDmitry Torokhov }
3853a51f7c4SDmitry Torokhov 
3866addb1d6SDmitry Torokhov static int evdev_event_to_user(char __user *buffer,
3876addb1d6SDmitry Torokhov 				const struct input_event *event)
3883a51f7c4SDmitry Torokhov {
3893a51f7c4SDmitry Torokhov 	if (copy_to_user(buffer, event, sizeof(struct input_event)))
3903a51f7c4SDmitry Torokhov 		return -EFAULT;
3913a51f7c4SDmitry Torokhov 
3923a51f7c4SDmitry Torokhov 	return 0;
3933a51f7c4SDmitry Torokhov }
3943a51f7c4SDmitry Torokhov 
3953a51f7c4SDmitry Torokhov #endif /* CONFIG_COMPAT */
39652658bb6SJuergen Kreileder 
3976addb1d6SDmitry Torokhov static ssize_t evdev_write(struct file *file, const char __user *buffer,
3986addb1d6SDmitry Torokhov 			   size_t count, loff_t *ppos)
3991da177e4SLinus Torvalds {
400d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
401d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
4021da177e4SLinus Torvalds 	struct input_event event;
4036addb1d6SDmitry Torokhov 	int retval;
4041da177e4SLinus Torvalds 
4056addb1d6SDmitry Torokhov 	retval = mutex_lock_interruptible(&evdev->mutex);
4066addb1d6SDmitry Torokhov 	if (retval)
4076addb1d6SDmitry Torokhov 		return retval;
4086addb1d6SDmitry Torokhov 
4096addb1d6SDmitry Torokhov 	if (!evdev->exist) {
4106addb1d6SDmitry Torokhov 		retval = -ENODEV;
4116addb1d6SDmitry Torokhov 		goto out;
4126addb1d6SDmitry Torokhov 	}
41352658bb6SJuergen Kreileder 
4143a51f7c4SDmitry Torokhov 	while (retval < count) {
41552658bb6SJuergen Kreileder 
4166addb1d6SDmitry Torokhov 		if (evdev_event_from_user(buffer + retval, &event)) {
4176addb1d6SDmitry Torokhov 			retval = -EFAULT;
4186addb1d6SDmitry Torokhov 			goto out;
4196addb1d6SDmitry Torokhov 		}
4206addb1d6SDmitry Torokhov 
4216addb1d6SDmitry Torokhov 		input_inject_event(&evdev->handle,
4226addb1d6SDmitry Torokhov 				   event.type, event.code, event.value);
4233a51f7c4SDmitry Torokhov 		retval += evdev_event_size();
42452658bb6SJuergen Kreileder 	}
42552658bb6SJuergen Kreileder 
4266addb1d6SDmitry Torokhov  out:
4276addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
42852658bb6SJuergen Kreileder 	return retval;
42952658bb6SJuergen Kreileder }
43052658bb6SJuergen Kreileder 
4316addb1d6SDmitry Torokhov static int evdev_fetch_next_event(struct evdev_client *client,
4326addb1d6SDmitry Torokhov 				  struct input_event *event)
4336addb1d6SDmitry Torokhov {
4346addb1d6SDmitry Torokhov 	int have_event;
4356addb1d6SDmitry Torokhov 
4366addb1d6SDmitry Torokhov 	spin_lock_irq(&client->buffer_lock);
4376addb1d6SDmitry Torokhov 
4386addb1d6SDmitry Torokhov 	have_event = client->head != client->tail;
4396addb1d6SDmitry Torokhov 	if (have_event) {
4406addb1d6SDmitry Torokhov 		*event = client->buffer[client->tail++];
4416addb1d6SDmitry Torokhov 		client->tail &= EVDEV_BUFFER_SIZE - 1;
4426addb1d6SDmitry Torokhov 	}
4436addb1d6SDmitry Torokhov 
4446addb1d6SDmitry Torokhov 	spin_unlock_irq(&client->buffer_lock);
4456addb1d6SDmitry Torokhov 
4466addb1d6SDmitry Torokhov 	return have_event;
4476addb1d6SDmitry Torokhov }
4486addb1d6SDmitry Torokhov 
4496addb1d6SDmitry Torokhov static ssize_t evdev_read(struct file *file, char __user *buffer,
4506addb1d6SDmitry Torokhov 			  size_t count, loff_t *ppos)
4511da177e4SLinus Torvalds {
452d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
453d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
4546addb1d6SDmitry Torokhov 	struct input_event event;
4551da177e4SLinus Torvalds 	int retval;
4561da177e4SLinus Torvalds 
4573a51f7c4SDmitry Torokhov 	if (count < evdev_event_size())
4581da177e4SLinus Torvalds 		return -EINVAL;
4591da177e4SLinus Torvalds 
4606addb1d6SDmitry Torokhov 	if (client->head == client->tail && evdev->exist &&
4616addb1d6SDmitry Torokhov 	    (file->f_flags & O_NONBLOCK))
4621da177e4SLinus Torvalds 		return -EAGAIN;
4631da177e4SLinus Torvalds 
464d0ffb9beSDmitry Torokhov 	retval = wait_event_interruptible(evdev->wait,
465d0ffb9beSDmitry Torokhov 		client->head != client->tail || !evdev->exist);
4661da177e4SLinus Torvalds 	if (retval)
4671da177e4SLinus Torvalds 		return retval;
4681da177e4SLinus Torvalds 
469d0ffb9beSDmitry Torokhov 	if (!evdev->exist)
4701da177e4SLinus Torvalds 		return -ENODEV;
4711da177e4SLinus Torvalds 
4726addb1d6SDmitry Torokhov 	while (retval + evdev_event_size() <= count &&
4736addb1d6SDmitry Torokhov 	       evdev_fetch_next_event(client, &event)) {
4743a51f7c4SDmitry Torokhov 
4756addb1d6SDmitry Torokhov 		if (evdev_event_to_user(buffer + retval, &event))
4763a51f7c4SDmitry Torokhov 			return -EFAULT;
4773a51f7c4SDmitry Torokhov 
4783a51f7c4SDmitry Torokhov 		retval += evdev_event_size();
4791da177e4SLinus Torvalds 	}
4801da177e4SLinus Torvalds 
4811da177e4SLinus Torvalds 	return retval;
4821da177e4SLinus Torvalds }
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds /* No kernel lock - fine */
4851da177e4SLinus Torvalds static unsigned int evdev_poll(struct file *file, poll_table *wait)
4861da177e4SLinus Torvalds {
487d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
488d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
4891e0afb28SDmitry Torokhov 
490d0ffb9beSDmitry Torokhov 	poll_wait(file, &evdev->wait, wait);
491d0ffb9beSDmitry Torokhov 	return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) |
492d0ffb9beSDmitry Torokhov 		(evdev->exist ? 0 : (POLLHUP | POLLERR));
4931da177e4SLinus Torvalds }
4941da177e4SLinus Torvalds 
4953a51f7c4SDmitry Torokhov #ifdef CONFIG_COMPAT
4963a51f7c4SDmitry Torokhov 
4973a51f7c4SDmitry Torokhov #define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)
4987b19ada2SJiri Slaby #define BITS_TO_LONGS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1)
4993a51f7c4SDmitry Torokhov 
5003a51f7c4SDmitry Torokhov #ifdef __BIG_ENDIAN
5013a51f7c4SDmitry Torokhov static int bits_to_user(unsigned long *bits, unsigned int maxbit,
5023a51f7c4SDmitry Torokhov 			unsigned int maxlen, void __user *p, int compat)
5033a51f7c4SDmitry Torokhov {
5043a51f7c4SDmitry Torokhov 	int len, i;
5053a51f7c4SDmitry Torokhov 
5063a51f7c4SDmitry Torokhov 	if (compat) {
5077b19ada2SJiri Slaby 		len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t);
508bf61f8d3SKenichi Nagai 		if (len > maxlen)
5093a51f7c4SDmitry Torokhov 			len = maxlen;
5103a51f7c4SDmitry Torokhov 
5113a51f7c4SDmitry Torokhov 		for (i = 0; i < len / sizeof(compat_long_t); i++)
5123a51f7c4SDmitry Torokhov 			if (copy_to_user((compat_long_t __user *) p + i,
5133a51f7c4SDmitry Torokhov 					 (compat_long_t *) bits +
5143a51f7c4SDmitry Torokhov 						i + 1 - ((i % 2) << 1),
5153a51f7c4SDmitry Torokhov 					 sizeof(compat_long_t)))
5163a51f7c4SDmitry Torokhov 				return -EFAULT;
5173a51f7c4SDmitry Torokhov 	} else {
5187b19ada2SJiri Slaby 		len = BITS_TO_LONGS(maxbit) * sizeof(long);
5193a51f7c4SDmitry Torokhov 		if (len > maxlen)
5203a51f7c4SDmitry Torokhov 			len = maxlen;
5213a51f7c4SDmitry Torokhov 
5223a51f7c4SDmitry Torokhov 		if (copy_to_user(p, bits, len))
5233a51f7c4SDmitry Torokhov 			return -EFAULT;
5243a51f7c4SDmitry Torokhov 	}
5253a51f7c4SDmitry Torokhov 
5263a51f7c4SDmitry Torokhov 	return len;
5273a51f7c4SDmitry Torokhov }
5283a51f7c4SDmitry Torokhov #else
5293a51f7c4SDmitry Torokhov static int bits_to_user(unsigned long *bits, unsigned int maxbit,
5303a51f7c4SDmitry Torokhov 			unsigned int maxlen, void __user *p, int compat)
5313a51f7c4SDmitry Torokhov {
5323a51f7c4SDmitry Torokhov 	int len = compat ?
5337b19ada2SJiri Slaby 			BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t) :
5347b19ada2SJiri Slaby 			BITS_TO_LONGS(maxbit) * sizeof(long);
5353a51f7c4SDmitry Torokhov 
5363a51f7c4SDmitry Torokhov 	if (len > maxlen)
5373a51f7c4SDmitry Torokhov 		len = maxlen;
5383a51f7c4SDmitry Torokhov 
5393a51f7c4SDmitry Torokhov 	return copy_to_user(p, bits, len) ? -EFAULT : len;
5403a51f7c4SDmitry Torokhov }
5413a51f7c4SDmitry Torokhov #endif /* __BIG_ENDIAN */
5423a51f7c4SDmitry Torokhov 
5433a51f7c4SDmitry Torokhov #else
5443a51f7c4SDmitry Torokhov 
5453a51f7c4SDmitry Torokhov static int bits_to_user(unsigned long *bits, unsigned int maxbit,
5463a51f7c4SDmitry Torokhov 			unsigned int maxlen, void __user *p, int compat)
5473a51f7c4SDmitry Torokhov {
5487b19ada2SJiri Slaby 	int len = BITS_TO_LONGS(maxbit) * sizeof(long);
5493a51f7c4SDmitry Torokhov 
5503a51f7c4SDmitry Torokhov 	if (len > maxlen)
5513a51f7c4SDmitry Torokhov 		len = maxlen;
5523a51f7c4SDmitry Torokhov 
5533a51f7c4SDmitry Torokhov 	return copy_to_user(p, bits, len) ? -EFAULT : len;
5543a51f7c4SDmitry Torokhov }
5553a51f7c4SDmitry Torokhov 
5563a51f7c4SDmitry Torokhov #endif /* CONFIG_COMPAT */
5573a51f7c4SDmitry Torokhov 
5583a51f7c4SDmitry Torokhov static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
5593a51f7c4SDmitry Torokhov {
5603a51f7c4SDmitry Torokhov 	int len;
5613a51f7c4SDmitry Torokhov 
5623a51f7c4SDmitry Torokhov 	if (!str)
5633a51f7c4SDmitry Torokhov 		return -ENOENT;
5643a51f7c4SDmitry Torokhov 
5653a51f7c4SDmitry Torokhov 	len = strlen(str) + 1;
5663a51f7c4SDmitry Torokhov 	if (len > maxlen)
5673a51f7c4SDmitry Torokhov 		len = maxlen;
5683a51f7c4SDmitry Torokhov 
5693a51f7c4SDmitry Torokhov 	return copy_to_user(p, str, len) ? -EFAULT : len;
5703a51f7c4SDmitry Torokhov }
5713a51f7c4SDmitry Torokhov 
5726addb1d6SDmitry Torokhov static long evdev_do_ioctl(struct file *file, unsigned int cmd,
5733a51f7c4SDmitry Torokhov 			   void __user *p, int compat_mode)
5741da177e4SLinus Torvalds {
575d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
576d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
5771da177e4SLinus Torvalds 	struct input_dev *dev = evdev->handle.dev;
5781da177e4SLinus Torvalds 	struct input_absinfo abs;
579509ca1a9SAnssi Hannula 	struct ff_effect effect;
5803a51f7c4SDmitry Torokhov 	int __user *ip = (int __user *)p;
5811da177e4SLinus Torvalds 	int i, t, u, v;
582509ca1a9SAnssi Hannula 	int error;
5831da177e4SLinus Torvalds 
5841da177e4SLinus Torvalds 	switch (cmd) {
5851da177e4SLinus Torvalds 
5861da177e4SLinus Torvalds 	case EVIOCGVERSION:
5871da177e4SLinus Torvalds 		return put_user(EV_VERSION, ip);
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 	case EVIOCGID:
5903a51f7c4SDmitry Torokhov 		if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
5913a51f7c4SDmitry Torokhov 			return -EFAULT;
59208791e5cSDmitry Torokhov 		return 0;
59308791e5cSDmitry Torokhov 
59408791e5cSDmitry Torokhov 	case EVIOCGREP:
59508791e5cSDmitry Torokhov 		if (!test_bit(EV_REP, dev->evbit))
59608791e5cSDmitry Torokhov 			return -ENOSYS;
59708791e5cSDmitry Torokhov 		if (put_user(dev->rep[REP_DELAY], ip))
59808791e5cSDmitry Torokhov 			return -EFAULT;
59908791e5cSDmitry Torokhov 		if (put_user(dev->rep[REP_PERIOD], ip + 1))
60008791e5cSDmitry Torokhov 			return -EFAULT;
60108791e5cSDmitry Torokhov 		return 0;
60208791e5cSDmitry Torokhov 
60308791e5cSDmitry Torokhov 	case EVIOCSREP:
60408791e5cSDmitry Torokhov 		if (!test_bit(EV_REP, dev->evbit))
60508791e5cSDmitry Torokhov 			return -ENOSYS;
60608791e5cSDmitry Torokhov 		if (get_user(u, ip))
60708791e5cSDmitry Torokhov 			return -EFAULT;
60808791e5cSDmitry Torokhov 		if (get_user(v, ip + 1))
60908791e5cSDmitry Torokhov 			return -EFAULT;
61008791e5cSDmitry Torokhov 
6110e739d28SDmitry Torokhov 		input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
6120e739d28SDmitry Torokhov 		input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
6133a51f7c4SDmitry Torokhov 
6143a51f7c4SDmitry Torokhov 		return 0;
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds 	case EVIOCGKEYCODE:
6173a51f7c4SDmitry Torokhov 		if (get_user(t, ip))
6183a51f7c4SDmitry Torokhov 			return -EFAULT;
619c8e4c772SMarvin Raaijmakers 
620*f4f37c8eSDmitry Torokhov 		error = input_get_keycode(dev, t, &v);
621c8e4c772SMarvin Raaijmakers 		if (error)
622c8e4c772SMarvin Raaijmakers 			return error;
623c8e4c772SMarvin Raaijmakers 
624c8e4c772SMarvin Raaijmakers 		if (put_user(v, ip + 1))
6253a51f7c4SDmitry Torokhov 			return -EFAULT;
626c8e4c772SMarvin Raaijmakers 
6271da177e4SLinus Torvalds 		return 0;
6281da177e4SLinus Torvalds 
6291da177e4SLinus Torvalds 	case EVIOCSKEYCODE:
630c8e4c772SMarvin Raaijmakers 		if (get_user(t, ip) || get_user(v, ip + 1))
6313a51f7c4SDmitry Torokhov 			return -EFAULT;
6323a51f7c4SDmitry Torokhov 
633*f4f37c8eSDmitry Torokhov 		return input_set_keycode(dev, t, v);
6341da177e4SLinus Torvalds 
6351da177e4SLinus Torvalds 	case EVIOCSFF:
6361da177e4SLinus Torvalds 		if (copy_from_user(&effect, p, sizeof(effect)))
6371da177e4SLinus Torvalds 			return -EFAULT;
638509ca1a9SAnssi Hannula 
639509ca1a9SAnssi Hannula 		error = input_ff_upload(dev, &effect, file);
640509ca1a9SAnssi Hannula 
6413a51f7c4SDmitry Torokhov 		if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
6421da177e4SLinus Torvalds 			return -EFAULT;
643509ca1a9SAnssi Hannula 
644509ca1a9SAnssi Hannula 		return error;
6451da177e4SLinus Torvalds 
6461da177e4SLinus Torvalds 	case EVIOCRMFF:
647509ca1a9SAnssi Hannula 		return input_ff_erase(dev, (int)(unsigned long) p, file);
6481da177e4SLinus Torvalds 
6491da177e4SLinus Torvalds 	case EVIOCGEFFECTS:
6506addb1d6SDmitry Torokhov 		i = test_bit(EV_FF, dev->evbit) ?
6516addb1d6SDmitry Torokhov 				dev->ff->max_effects : 0;
652509ca1a9SAnssi Hannula 		if (put_user(i, ip))
6531da177e4SLinus Torvalds 			return -EFAULT;
6541da177e4SLinus Torvalds 		return 0;
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds 	case EVIOCGRAB:
6576addb1d6SDmitry Torokhov 		if (p)
6586addb1d6SDmitry Torokhov 			return evdev_grab(evdev, client);
6596addb1d6SDmitry Torokhov 		else
6606addb1d6SDmitry Torokhov 			return evdev_ungrab(evdev, client);
6611da177e4SLinus Torvalds 
6621da177e4SLinus Torvalds 	default:
6631da177e4SLinus Torvalds 
66441e979f8SVojtech Pavlik 		if (_IOC_TYPE(cmd) != 'E')
6651da177e4SLinus Torvalds 			return -EINVAL;
6661da177e4SLinus Torvalds 
66741e979f8SVojtech Pavlik 		if (_IOC_DIR(cmd) == _IOC_READ) {
66841e979f8SVojtech Pavlik 
6691da177e4SLinus Torvalds 			if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) {
6701da177e4SLinus Torvalds 
671ce305b6aSDmitry Torokhov 				unsigned long *bits;
6721da177e4SLinus Torvalds 				int len;
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds 				switch (_IOC_NR(cmd) & EV_MAX) {
6756addb1d6SDmitry Torokhov 
6761da177e4SLinus Torvalds 				case      0: bits = dev->evbit;  len = EV_MAX;  break;
6771da177e4SLinus Torvalds 				case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
6781da177e4SLinus Torvalds 				case EV_REL: bits = dev->relbit; len = REL_MAX; break;
6791da177e4SLinus Torvalds 				case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
6801da177e4SLinus Torvalds 				case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
6811da177e4SLinus Torvalds 				case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
6821da177e4SLinus Torvalds 				case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
6831da177e4SLinus Torvalds 				case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  break;
68431581066SRichard Purdie 				case EV_SW:  bits = dev->swbit;  len = SW_MAX;  break;
6851da177e4SLinus Torvalds 				default: return -EINVAL;
6861da177e4SLinus Torvalds 				}
6873a51f7c4SDmitry Torokhov 				return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
68852658bb6SJuergen Kreileder 			}
68952658bb6SJuergen Kreileder 
69041e979f8SVojtech Pavlik 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
6913a51f7c4SDmitry Torokhov 				return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
6923a51f7c4SDmitry Torokhov 						    p, compat_mode);
69352658bb6SJuergen Kreileder 
69441e979f8SVojtech Pavlik 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
6953a51f7c4SDmitry Torokhov 				return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
6963a51f7c4SDmitry Torokhov 						    p, compat_mode);
69752658bb6SJuergen Kreileder 
69841e979f8SVojtech Pavlik 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
6993a51f7c4SDmitry Torokhov 				return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
7003a51f7c4SDmitry Torokhov 						    p, compat_mode);
70152658bb6SJuergen Kreileder 
7027972720aSDmitry Torokhov 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
7033a51f7c4SDmitry Torokhov 				return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
7043a51f7c4SDmitry Torokhov 						    p, compat_mode);
7057972720aSDmitry Torokhov 
7063a51f7c4SDmitry Torokhov 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
7073a51f7c4SDmitry Torokhov 				return str_to_user(dev->name, _IOC_SIZE(cmd), p);
70852658bb6SJuergen Kreileder 
7093a51f7c4SDmitry Torokhov 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
7103a51f7c4SDmitry Torokhov 				return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
71152658bb6SJuergen Kreileder 
7123a51f7c4SDmitry Torokhov 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
7133a51f7c4SDmitry Torokhov 				return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
71452658bb6SJuergen Kreileder 
71552658bb6SJuergen Kreileder 			if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
71652658bb6SJuergen Kreileder 
717ce305b6aSDmitry Torokhov 				t = _IOC_NR(cmd) & ABS_MAX;
71852658bb6SJuergen Kreileder 
71952658bb6SJuergen Kreileder 				abs.value = dev->abs[t];
72052658bb6SJuergen Kreileder 				abs.minimum = dev->absmin[t];
72152658bb6SJuergen Kreileder 				abs.maximum = dev->absmax[t];
72252658bb6SJuergen Kreileder 				abs.fuzz = dev->absfuzz[t];
72352658bb6SJuergen Kreileder 				abs.flat = dev->absflat[t];
72452658bb6SJuergen Kreileder 
72552658bb6SJuergen Kreileder 				if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
72652658bb6SJuergen Kreileder 					return -EFAULT;
72752658bb6SJuergen Kreileder 
72852658bb6SJuergen Kreileder 				return 0;
72952658bb6SJuergen Kreileder 			}
7303a51f7c4SDmitry Torokhov 
73141e979f8SVojtech Pavlik 		}
73241e979f8SVojtech Pavlik 
73341e979f8SVojtech Pavlik 		if (_IOC_DIR(cmd) == _IOC_WRITE) {
73452658bb6SJuergen Kreileder 
73552658bb6SJuergen Kreileder 			if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
73652658bb6SJuergen Kreileder 
737ce305b6aSDmitry Torokhov 				t = _IOC_NR(cmd) & ABS_MAX;
73852658bb6SJuergen Kreileder 
7396addb1d6SDmitry Torokhov 				if (copy_from_user(&abs, p,
7406addb1d6SDmitry Torokhov 						sizeof(struct input_absinfo)))
74152658bb6SJuergen Kreileder 					return -EFAULT;
74252658bb6SJuergen Kreileder 
7436addb1d6SDmitry Torokhov 				/*
7446addb1d6SDmitry Torokhov 				 * Take event lock to ensure that we are not
7456addb1d6SDmitry Torokhov 				 * changing device parameters in the middle
7466addb1d6SDmitry Torokhov 				 * of event.
7476addb1d6SDmitry Torokhov 				 */
7486addb1d6SDmitry Torokhov 				spin_lock_irq(&dev->event_lock);
7496addb1d6SDmitry Torokhov 
75052658bb6SJuergen Kreileder 				dev->abs[t] = abs.value;
75152658bb6SJuergen Kreileder 				dev->absmin[t] = abs.minimum;
75252658bb6SJuergen Kreileder 				dev->absmax[t] = abs.maximum;
75352658bb6SJuergen Kreileder 				dev->absfuzz[t] = abs.fuzz;
75452658bb6SJuergen Kreileder 				dev->absflat[t] = abs.flat;
75552658bb6SJuergen Kreileder 
7566addb1d6SDmitry Torokhov 				spin_unlock_irq(&dev->event_lock);
7576addb1d6SDmitry Torokhov 
75852658bb6SJuergen Kreileder 				return 0;
75952658bb6SJuergen Kreileder 			}
76052658bb6SJuergen Kreileder 		}
76141e979f8SVojtech Pavlik 	}
76252658bb6SJuergen Kreileder 	return -EINVAL;
76352658bb6SJuergen Kreileder }
7643a51f7c4SDmitry Torokhov 
7656addb1d6SDmitry Torokhov static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
7666addb1d6SDmitry Torokhov 				void __user *p, int compat_mode)
7676addb1d6SDmitry Torokhov {
7686addb1d6SDmitry Torokhov 	struct evdev_client *client = file->private_data;
7696addb1d6SDmitry Torokhov 	struct evdev *evdev = client->evdev;
7706addb1d6SDmitry Torokhov 	int retval;
7716addb1d6SDmitry Torokhov 
7726addb1d6SDmitry Torokhov 	retval = mutex_lock_interruptible(&evdev->mutex);
7736addb1d6SDmitry Torokhov 	if (retval)
7746addb1d6SDmitry Torokhov 		return retval;
7756addb1d6SDmitry Torokhov 
7766addb1d6SDmitry Torokhov 	if (!evdev->exist) {
7776addb1d6SDmitry Torokhov 		retval = -ENODEV;
7786addb1d6SDmitry Torokhov 		goto out;
7796addb1d6SDmitry Torokhov 	}
7806addb1d6SDmitry Torokhov 
7816addb1d6SDmitry Torokhov 	retval = evdev_do_ioctl(file, cmd, p, compat_mode);
7826addb1d6SDmitry Torokhov 
7836addb1d6SDmitry Torokhov  out:
7846addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
7856addb1d6SDmitry Torokhov 	return retval;
7866addb1d6SDmitry Torokhov }
7876addb1d6SDmitry Torokhov 
7883a51f7c4SDmitry Torokhov static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
7893a51f7c4SDmitry Torokhov {
7903a51f7c4SDmitry Torokhov 	return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
7913a51f7c4SDmitry Torokhov }
7923a51f7c4SDmitry Torokhov 
7933a51f7c4SDmitry Torokhov #ifdef CONFIG_COMPAT
7946addb1d6SDmitry Torokhov static long evdev_ioctl_compat(struct file *file,
7956addb1d6SDmitry Torokhov 				unsigned int cmd, unsigned long arg)
7963a51f7c4SDmitry Torokhov {
7973a51f7c4SDmitry Torokhov 	return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
7983a51f7c4SDmitry Torokhov }
79952658bb6SJuergen Kreileder #endif
80052658bb6SJuergen Kreileder 
80166e66118SDmitry Torokhov static const struct file_operations evdev_fops = {
8021da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
8031da177e4SLinus Torvalds 	.read		= evdev_read,
8041da177e4SLinus Torvalds 	.write		= evdev_write,
8051da177e4SLinus Torvalds 	.poll		= evdev_poll,
8061da177e4SLinus Torvalds 	.open		= evdev_open,
8071da177e4SLinus Torvalds 	.release	= evdev_release,
80852658bb6SJuergen Kreileder 	.unlocked_ioctl	= evdev_ioctl,
80952658bb6SJuergen Kreileder #ifdef CONFIG_COMPAT
81052658bb6SJuergen Kreileder 	.compat_ioctl	= evdev_ioctl_compat,
81152658bb6SJuergen Kreileder #endif
8121da177e4SLinus Torvalds 	.fasync		= evdev_fasync,
8131da177e4SLinus Torvalds 	.flush		= evdev_flush
8141da177e4SLinus Torvalds };
8151da177e4SLinus Torvalds 
8166addb1d6SDmitry Torokhov static int evdev_install_chrdev(struct evdev *evdev)
8176addb1d6SDmitry Torokhov {
8186addb1d6SDmitry Torokhov 	/*
8196addb1d6SDmitry Torokhov 	 * No need to do any locking here as calls to connect and
8206addb1d6SDmitry Torokhov 	 * disconnect are serialized by the input core
8216addb1d6SDmitry Torokhov 	 */
8226addb1d6SDmitry Torokhov 	evdev_table[evdev->minor] = evdev;
8236addb1d6SDmitry Torokhov 	return 0;
8246addb1d6SDmitry Torokhov }
8256addb1d6SDmitry Torokhov 
8266addb1d6SDmitry Torokhov static void evdev_remove_chrdev(struct evdev *evdev)
8276addb1d6SDmitry Torokhov {
8286addb1d6SDmitry Torokhov 	/*
8296addb1d6SDmitry Torokhov 	 * Lock evdev table to prevent race with evdev_open()
8306addb1d6SDmitry Torokhov 	 */
8316addb1d6SDmitry Torokhov 	mutex_lock(&evdev_table_mutex);
8326addb1d6SDmitry Torokhov 	evdev_table[evdev->minor] = NULL;
8336addb1d6SDmitry Torokhov 	mutex_unlock(&evdev_table_mutex);
8346addb1d6SDmitry Torokhov }
8356addb1d6SDmitry Torokhov 
8366addb1d6SDmitry Torokhov /*
8376addb1d6SDmitry Torokhov  * Mark device non-existent. This disables writes, ioctls and
8386addb1d6SDmitry Torokhov  * prevents new users from opening the device. Already posted
8396addb1d6SDmitry Torokhov  * blocking reads will stay, however new ones will fail.
8406addb1d6SDmitry Torokhov  */
8416addb1d6SDmitry Torokhov static void evdev_mark_dead(struct evdev *evdev)
8426addb1d6SDmitry Torokhov {
8436addb1d6SDmitry Torokhov 	mutex_lock(&evdev->mutex);
8446addb1d6SDmitry Torokhov 	evdev->exist = 0;
8456addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
8466addb1d6SDmitry Torokhov }
8476addb1d6SDmitry Torokhov 
8486addb1d6SDmitry Torokhov static void evdev_cleanup(struct evdev *evdev)
8496addb1d6SDmitry Torokhov {
8506addb1d6SDmitry Torokhov 	struct input_handle *handle = &evdev->handle;
8516addb1d6SDmitry Torokhov 
8526addb1d6SDmitry Torokhov 	evdev_mark_dead(evdev);
8536addb1d6SDmitry Torokhov 	evdev_hangup(evdev);
8546addb1d6SDmitry Torokhov 	evdev_remove_chrdev(evdev);
8556addb1d6SDmitry Torokhov 
8566addb1d6SDmitry Torokhov 	/* evdev is marked dead so no one else accesses evdev->open */
8576addb1d6SDmitry Torokhov 	if (evdev->open) {
8586addb1d6SDmitry Torokhov 		input_flush_device(handle, NULL);
8596addb1d6SDmitry Torokhov 		input_close_device(handle);
8606addb1d6SDmitry Torokhov 	}
8616addb1d6SDmitry Torokhov }
8626addb1d6SDmitry Torokhov 
8636addb1d6SDmitry Torokhov /*
8646addb1d6SDmitry Torokhov  * Create new evdev device. Note that input core serializes calls
8656addb1d6SDmitry Torokhov  * to connect and disconnect so we don't need to lock evdev_table here.
8666addb1d6SDmitry Torokhov  */
8675b2a0826SDmitry Torokhov static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
86866e66118SDmitry Torokhov 			 const struct input_device_id *id)
8691da177e4SLinus Torvalds {
8701da177e4SLinus Torvalds 	struct evdev *evdev;
8711da177e4SLinus Torvalds 	int minor;
8725b2a0826SDmitry Torokhov 	int error;
8731da177e4SLinus Torvalds 
8746addb1d6SDmitry Torokhov 	for (minor = 0; minor < EVDEV_MINORS; minor++)
8756addb1d6SDmitry Torokhov 		if (!evdev_table[minor])
8766addb1d6SDmitry Torokhov 			break;
8776addb1d6SDmitry Torokhov 
8781da177e4SLinus Torvalds 	if (minor == EVDEV_MINORS) {
8791da177e4SLinus Torvalds 		printk(KERN_ERR "evdev: no more free evdev devices\n");
8805b2a0826SDmitry Torokhov 		return -ENFILE;
8811da177e4SLinus Torvalds 	}
8821da177e4SLinus Torvalds 
8835b2a0826SDmitry Torokhov 	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
8845b2a0826SDmitry Torokhov 	if (!evdev)
8855b2a0826SDmitry Torokhov 		return -ENOMEM;
8861da177e4SLinus Torvalds 
887d0ffb9beSDmitry Torokhov 	INIT_LIST_HEAD(&evdev->client_list);
8886addb1d6SDmitry Torokhov 	spin_lock_init(&evdev->client_lock);
8896addb1d6SDmitry Torokhov 	mutex_init(&evdev->mutex);
8901da177e4SLinus Torvalds 	init_waitqueue_head(&evdev->wait);
8911da177e4SLinus Torvalds 
8926addb1d6SDmitry Torokhov 	snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
8931da177e4SLinus Torvalds 	evdev->exist = 1;
8941da177e4SLinus Torvalds 	evdev->minor = minor;
8956addb1d6SDmitry Torokhov 
8961da177e4SLinus Torvalds 	evdev->handle.dev = dev;
8971da177e4SLinus Torvalds 	evdev->handle.name = evdev->name;
8981da177e4SLinus Torvalds 	evdev->handle.handler = handler;
8991da177e4SLinus Torvalds 	evdev->handle.private = evdev;
9009657d75cSDmitry Torokhov 
9016addb1d6SDmitry Torokhov 	strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));
9026addb1d6SDmitry Torokhov 	evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
9039657d75cSDmitry Torokhov 	evdev->dev.class = &input_class;
9049657d75cSDmitry Torokhov 	evdev->dev.parent = &dev->dev;
9059657d75cSDmitry Torokhov 	evdev->dev.release = evdev_free;
9069657d75cSDmitry Torokhov 	device_initialize(&evdev->dev);
9071da177e4SLinus Torvalds 
9086addb1d6SDmitry Torokhov 	error = input_register_handle(&evdev->handle);
9095b2a0826SDmitry Torokhov 	if (error)
9109657d75cSDmitry Torokhov 		goto err_free_evdev;
9111da177e4SLinus Torvalds 
9126addb1d6SDmitry Torokhov 	error = evdev_install_chrdev(evdev);
9135b2a0826SDmitry Torokhov 	if (error)
9146addb1d6SDmitry Torokhov 		goto err_unregister_handle;
9156addb1d6SDmitry Torokhov 
9166addb1d6SDmitry Torokhov 	error = device_add(&evdev->dev);
9176addb1d6SDmitry Torokhov 	if (error)
9186addb1d6SDmitry Torokhov 		goto err_cleanup_evdev;
9195b2a0826SDmitry Torokhov 
9205b2a0826SDmitry Torokhov 	return 0;
9215b2a0826SDmitry Torokhov 
9226addb1d6SDmitry Torokhov  err_cleanup_evdev:
9236addb1d6SDmitry Torokhov 	evdev_cleanup(evdev);
9246addb1d6SDmitry Torokhov  err_unregister_handle:
9256addb1d6SDmitry Torokhov 	input_unregister_handle(&evdev->handle);
9265b2a0826SDmitry Torokhov  err_free_evdev:
9279657d75cSDmitry Torokhov 	put_device(&evdev->dev);
9285b2a0826SDmitry Torokhov 	return error;
9291da177e4SLinus Torvalds }
9301da177e4SLinus Torvalds 
9311da177e4SLinus Torvalds static void evdev_disconnect(struct input_handle *handle)
9321da177e4SLinus Torvalds {
9331da177e4SLinus Torvalds 	struct evdev *evdev = handle->private;
9341da177e4SLinus Torvalds 
9359657d75cSDmitry Torokhov 	device_del(&evdev->dev);
9366addb1d6SDmitry Torokhov 	evdev_cleanup(evdev);
9376addb1d6SDmitry Torokhov 	input_unregister_handle(handle);
9389657d75cSDmitry Torokhov 	put_device(&evdev->dev);
9391da177e4SLinus Torvalds }
9401da177e4SLinus Torvalds 
94166e66118SDmitry Torokhov static const struct input_device_id evdev_ids[] = {
9421da177e4SLinus Torvalds 	{ .driver_info = 1 },	/* Matches all devices */
9431da177e4SLinus Torvalds 	{ },			/* Terminating zero entry */
9441da177e4SLinus Torvalds };
9451da177e4SLinus Torvalds 
9461da177e4SLinus Torvalds MODULE_DEVICE_TABLE(input, evdev_ids);
9471da177e4SLinus Torvalds 
9481da177e4SLinus Torvalds static struct input_handler evdev_handler = {
9491da177e4SLinus Torvalds 	.event		= evdev_event,
9501da177e4SLinus Torvalds 	.connect	= evdev_connect,
9511da177e4SLinus Torvalds 	.disconnect	= evdev_disconnect,
9521da177e4SLinus Torvalds 	.fops		= &evdev_fops,
9531da177e4SLinus Torvalds 	.minor		= EVDEV_MINOR_BASE,
9541da177e4SLinus Torvalds 	.name		= "evdev",
9551da177e4SLinus Torvalds 	.id_table	= evdev_ids,
9561da177e4SLinus Torvalds };
9571da177e4SLinus Torvalds 
9581da177e4SLinus Torvalds static int __init evdev_init(void)
9591da177e4SLinus Torvalds {
9604263cf0fSDmitry Torokhov 	return input_register_handler(&evdev_handler);
9611da177e4SLinus Torvalds }
9621da177e4SLinus Torvalds 
9631da177e4SLinus Torvalds static void __exit evdev_exit(void)
9641da177e4SLinus Torvalds {
9651da177e4SLinus Torvalds 	input_unregister_handler(&evdev_handler);
9661da177e4SLinus Torvalds }
9671da177e4SLinus Torvalds 
9681da177e4SLinus Torvalds module_init(evdev_init);
9691da177e4SLinus Torvalds module_exit(evdev_exit);
9701da177e4SLinus Torvalds 
9711da177e4SLinus Torvalds MODULE_AUTHOR("Vojtech Pavlik <[email protected]>");
9721da177e4SLinus Torvalds MODULE_DESCRIPTION("Input driver event char devices");
9731da177e4SLinus Torvalds MODULE_LICENSE("GPL");
974