xref: /linux-6.15/drivers/input/evdev.c (revision a7097ff8)
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 
127*a7097ff8SDmitry Torokhov 	input_put_device(evdev->handle.dev);
1281da177e4SLinus Torvalds 	kfree(evdev);
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds 
1316addb1d6SDmitry Torokhov /*
1326addb1d6SDmitry Torokhov  * Grabs an event device (along with underlying input device).
1336addb1d6SDmitry Torokhov  * This function is called with evdev->mutex taken.
1346addb1d6SDmitry Torokhov  */
1356addb1d6SDmitry Torokhov static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
1366addb1d6SDmitry Torokhov {
1376addb1d6SDmitry Torokhov 	int error;
1386addb1d6SDmitry Torokhov 
1396addb1d6SDmitry Torokhov 	if (evdev->grab)
1406addb1d6SDmitry Torokhov 		return -EBUSY;
1416addb1d6SDmitry Torokhov 
1426addb1d6SDmitry Torokhov 	error = input_grab_device(&evdev->handle);
1436addb1d6SDmitry Torokhov 	if (error)
1446addb1d6SDmitry Torokhov 		return error;
1456addb1d6SDmitry Torokhov 
1466addb1d6SDmitry Torokhov 	rcu_assign_pointer(evdev->grab, client);
14782ba56c2SDmitry Torokhov 	synchronize_rcu();
1486addb1d6SDmitry Torokhov 
1496addb1d6SDmitry Torokhov 	return 0;
1506addb1d6SDmitry Torokhov }
1516addb1d6SDmitry Torokhov 
1526addb1d6SDmitry Torokhov static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
1536addb1d6SDmitry Torokhov {
1546addb1d6SDmitry Torokhov 	if (evdev->grab != client)
1556addb1d6SDmitry Torokhov 		return  -EINVAL;
1566addb1d6SDmitry Torokhov 
1576addb1d6SDmitry Torokhov 	rcu_assign_pointer(evdev->grab, NULL);
15882ba56c2SDmitry Torokhov 	synchronize_rcu();
1596addb1d6SDmitry Torokhov 	input_release_device(&evdev->handle);
1606addb1d6SDmitry Torokhov 
1616addb1d6SDmitry Torokhov 	return 0;
1626addb1d6SDmitry Torokhov }
1636addb1d6SDmitry Torokhov 
1646addb1d6SDmitry Torokhov static void evdev_attach_client(struct evdev *evdev,
1656addb1d6SDmitry Torokhov 				struct evdev_client *client)
1666addb1d6SDmitry Torokhov {
1676addb1d6SDmitry Torokhov 	spin_lock(&evdev->client_lock);
1686addb1d6SDmitry Torokhov 	list_add_tail_rcu(&client->node, &evdev->client_list);
1696addb1d6SDmitry Torokhov 	spin_unlock(&evdev->client_lock);
17082ba56c2SDmitry Torokhov 	synchronize_rcu();
1716addb1d6SDmitry Torokhov }
1726addb1d6SDmitry Torokhov 
1736addb1d6SDmitry Torokhov static void evdev_detach_client(struct evdev *evdev,
1746addb1d6SDmitry Torokhov 				struct evdev_client *client)
1756addb1d6SDmitry Torokhov {
1766addb1d6SDmitry Torokhov 	spin_lock(&evdev->client_lock);
1776addb1d6SDmitry Torokhov 	list_del_rcu(&client->node);
1786addb1d6SDmitry Torokhov 	spin_unlock(&evdev->client_lock);
17982ba56c2SDmitry Torokhov 	synchronize_rcu();
1806addb1d6SDmitry Torokhov }
1816addb1d6SDmitry Torokhov 
1826addb1d6SDmitry Torokhov static int evdev_open_device(struct evdev *evdev)
1836addb1d6SDmitry Torokhov {
1846addb1d6SDmitry Torokhov 	int retval;
1856addb1d6SDmitry Torokhov 
1866addb1d6SDmitry Torokhov 	retval = mutex_lock_interruptible(&evdev->mutex);
1876addb1d6SDmitry Torokhov 	if (retval)
1886addb1d6SDmitry Torokhov 		return retval;
1896addb1d6SDmitry Torokhov 
1906addb1d6SDmitry Torokhov 	if (!evdev->exist)
1916addb1d6SDmitry Torokhov 		retval = -ENODEV;
19206445014SOliver Neukum 	else if (!evdev->open++) {
1936addb1d6SDmitry Torokhov 		retval = input_open_device(&evdev->handle);
19406445014SOliver Neukum 		if (retval)
19506445014SOliver Neukum 			evdev->open--;
19606445014SOliver Neukum 	}
1976addb1d6SDmitry Torokhov 
1986addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
1996addb1d6SDmitry Torokhov 	return retval;
2006addb1d6SDmitry Torokhov }
2016addb1d6SDmitry Torokhov 
2026addb1d6SDmitry Torokhov static void evdev_close_device(struct evdev *evdev)
2036addb1d6SDmitry Torokhov {
2046addb1d6SDmitry Torokhov 	mutex_lock(&evdev->mutex);
2056addb1d6SDmitry Torokhov 
2066addb1d6SDmitry Torokhov 	if (evdev->exist && !--evdev->open)
2076addb1d6SDmitry Torokhov 		input_close_device(&evdev->handle);
2086addb1d6SDmitry Torokhov 
2096addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
2106addb1d6SDmitry Torokhov }
2116addb1d6SDmitry Torokhov 
2126addb1d6SDmitry Torokhov /*
2136addb1d6SDmitry Torokhov  * Wake up users waiting for IO so they can disconnect from
2146addb1d6SDmitry Torokhov  * dead device.
2156addb1d6SDmitry Torokhov  */
2166addb1d6SDmitry Torokhov static void evdev_hangup(struct evdev *evdev)
2176addb1d6SDmitry Torokhov {
2186addb1d6SDmitry Torokhov 	struct evdev_client *client;
2196addb1d6SDmitry Torokhov 
2206addb1d6SDmitry Torokhov 	spin_lock(&evdev->client_lock);
2216addb1d6SDmitry Torokhov 	list_for_each_entry(client, &evdev->client_list, node)
2226addb1d6SDmitry Torokhov 		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
2236addb1d6SDmitry Torokhov 	spin_unlock(&evdev->client_lock);
2246addb1d6SDmitry Torokhov 
2256addb1d6SDmitry Torokhov 	wake_up_interruptible(&evdev->wait);
2266addb1d6SDmitry Torokhov }
2276addb1d6SDmitry Torokhov 
2281da177e4SLinus Torvalds static int evdev_release(struct inode *inode, struct file *file)
2291da177e4SLinus Torvalds {
230d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
231d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
2321da177e4SLinus Torvalds 
2336addb1d6SDmitry Torokhov 	mutex_lock(&evdev->mutex);
2346addb1d6SDmitry Torokhov 	if (evdev->grab == client)
2356addb1d6SDmitry Torokhov 		evdev_ungrab(evdev, client);
2366addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds 	evdev_fasync(-1, file, 0);
2396addb1d6SDmitry Torokhov 	evdev_detach_client(evdev, client);
240d0ffb9beSDmitry Torokhov 	kfree(client);
2411da177e4SLinus Torvalds 
2426addb1d6SDmitry Torokhov 	evdev_close_device(evdev);
2439657d75cSDmitry Torokhov 	put_device(&evdev->dev);
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	return 0;
2461da177e4SLinus Torvalds }
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds static int evdev_open(struct inode *inode, struct file *file)
2491da177e4SLinus Torvalds {
250d0ffb9beSDmitry Torokhov 	struct evdev *evdev;
2516addb1d6SDmitry Torokhov 	struct evdev_client *client;
2521da177e4SLinus Torvalds 	int i = iminor(inode) - EVDEV_MINOR_BASE;
253d542ed82SDmitry Torokhov 	int error;
2541da177e4SLinus Torvalds 
255d0ffb9beSDmitry Torokhov 	if (i >= EVDEV_MINORS)
2561da177e4SLinus Torvalds 		return -ENODEV;
2571da177e4SLinus Torvalds 
2586addb1d6SDmitry Torokhov 	error = mutex_lock_interruptible(&evdev_table_mutex);
2596addb1d6SDmitry Torokhov 	if (error)
2606addb1d6SDmitry Torokhov 		return error;
261d0ffb9beSDmitry Torokhov 	evdev = evdev_table[i];
2626addb1d6SDmitry Torokhov 	if (evdev)
2639657d75cSDmitry Torokhov 		get_device(&evdev->dev);
2646addb1d6SDmitry Torokhov 	mutex_unlock(&evdev_table_mutex);
2656addb1d6SDmitry Torokhov 
2666addb1d6SDmitry Torokhov 	if (!evdev)
2676addb1d6SDmitry Torokhov 		return -ENODEV;
2689657d75cSDmitry Torokhov 
269d0ffb9beSDmitry Torokhov 	client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
2709657d75cSDmitry Torokhov 	if (!client) {
2719657d75cSDmitry Torokhov 		error = -ENOMEM;
2729657d75cSDmitry Torokhov 		goto err_put_evdev;
2739657d75cSDmitry Torokhov 	}
2741da177e4SLinus Torvalds 
2756addb1d6SDmitry Torokhov 	spin_lock_init(&client->buffer_lock);
276d0ffb9beSDmitry Torokhov 	client->evdev = evdev;
2776addb1d6SDmitry Torokhov 	evdev_attach_client(evdev, client);
2781da177e4SLinus Torvalds 
2796addb1d6SDmitry Torokhov 	error = evdev_open_device(evdev);
2809657d75cSDmitry Torokhov 	if (error)
2819657d75cSDmitry Torokhov 		goto err_free_client;
2821da177e4SLinus Torvalds 
283d0ffb9beSDmitry Torokhov 	file->private_data = client;
2841da177e4SLinus Torvalds 	return 0;
2859657d75cSDmitry Torokhov 
2869657d75cSDmitry Torokhov  err_free_client:
2876addb1d6SDmitry Torokhov 	evdev_detach_client(evdev, client);
2889657d75cSDmitry Torokhov 	kfree(client);
2899657d75cSDmitry Torokhov  err_put_evdev:
2909657d75cSDmitry Torokhov 	put_device(&evdev->dev);
2919657d75cSDmitry Torokhov 	return error;
2921da177e4SLinus Torvalds }
2931da177e4SLinus Torvalds 
29452658bb6SJuergen Kreileder #ifdef CONFIG_COMPAT
2953a51f7c4SDmitry Torokhov 
29652658bb6SJuergen Kreileder struct input_event_compat {
29752658bb6SJuergen Kreileder 	struct compat_timeval time;
29852658bb6SJuergen Kreileder 	__u16 type;
29952658bb6SJuergen Kreileder 	__u16 code;
30052658bb6SJuergen Kreileder 	__s32 value;
30152658bb6SJuergen Kreileder };
30252658bb6SJuergen Kreileder 
303bf2fcc6fSAndi Kleen /* Note to the author of this code: did it ever occur to
304bf2fcc6fSAndi Kleen    you why the ifdefs are needed? Think about it again. -AK */
30552658bb6SJuergen Kreileder #ifdef CONFIG_X86_64
306bf2fcc6fSAndi Kleen #  define COMPAT_TEST is_compat_task()
30752658bb6SJuergen Kreileder #elif defined(CONFIG_IA64)
3086450578fSAl Viro #  define COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current))
309347a8dc3SMartin Schwidefsky #elif defined(CONFIG_S390)
31052658bb6SJuergen Kreileder #  define COMPAT_TEST test_thread_flag(TIF_31BIT)
31159df6bbfSRalf Baechle #elif defined(CONFIG_MIPS)
312293c5bd1SRalf Baechle #  define COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR)
31352658bb6SJuergen Kreileder #else
31452658bb6SJuergen Kreileder #  define COMPAT_TEST test_thread_flag(TIF_32BIT)
31552658bb6SJuergen Kreileder #endif
31652658bb6SJuergen Kreileder 
3173a51f7c4SDmitry Torokhov static inline size_t evdev_event_size(void)
31852658bb6SJuergen Kreileder {
3193a51f7c4SDmitry Torokhov 	return COMPAT_TEST ?
3203a51f7c4SDmitry Torokhov 		sizeof(struct input_event_compat) : sizeof(struct input_event);
3213a51f7c4SDmitry Torokhov }
32252658bb6SJuergen Kreileder 
3236addb1d6SDmitry Torokhov static int evdev_event_from_user(const char __user *buffer,
3246addb1d6SDmitry Torokhov 				 struct input_event *event)
3253a51f7c4SDmitry Torokhov {
3263a51f7c4SDmitry Torokhov 	if (COMPAT_TEST) {
3273a51f7c4SDmitry Torokhov 		struct input_event_compat compat_event;
3283a51f7c4SDmitry Torokhov 
3296addb1d6SDmitry Torokhov 		if (copy_from_user(&compat_event, buffer,
3306addb1d6SDmitry Torokhov 				   sizeof(struct input_event_compat)))
33152658bb6SJuergen Kreileder 			return -EFAULT;
3323a51f7c4SDmitry Torokhov 
3333a51f7c4SDmitry Torokhov 		event->time.tv_sec = compat_event.time.tv_sec;
3343a51f7c4SDmitry Torokhov 		event->time.tv_usec = compat_event.time.tv_usec;
3353a51f7c4SDmitry Torokhov 		event->type = compat_event.type;
3363a51f7c4SDmitry Torokhov 		event->code = compat_event.code;
3373a51f7c4SDmitry Torokhov 		event->value = compat_event.value;
3383a51f7c4SDmitry Torokhov 
3393a51f7c4SDmitry Torokhov 	} else {
3403a51f7c4SDmitry Torokhov 		if (copy_from_user(event, buffer, sizeof(struct input_event)))
3413a51f7c4SDmitry Torokhov 			return -EFAULT;
34252658bb6SJuergen Kreileder 	}
34352658bb6SJuergen Kreileder 
3443a51f7c4SDmitry Torokhov 	return 0;
34552658bb6SJuergen Kreileder }
3463a51f7c4SDmitry Torokhov 
3476addb1d6SDmitry Torokhov static int evdev_event_to_user(char __user *buffer,
3486addb1d6SDmitry Torokhov 				const struct input_event *event)
3493a51f7c4SDmitry Torokhov {
3503a51f7c4SDmitry Torokhov 	if (COMPAT_TEST) {
3513a51f7c4SDmitry Torokhov 		struct input_event_compat compat_event;
3523a51f7c4SDmitry Torokhov 
3533a51f7c4SDmitry Torokhov 		compat_event.time.tv_sec = event->time.tv_sec;
3543a51f7c4SDmitry Torokhov 		compat_event.time.tv_usec = event->time.tv_usec;
3553a51f7c4SDmitry Torokhov 		compat_event.type = event->type;
3563a51f7c4SDmitry Torokhov 		compat_event.code = event->code;
3573a51f7c4SDmitry Torokhov 		compat_event.value = event->value;
3583a51f7c4SDmitry Torokhov 
3596addb1d6SDmitry Torokhov 		if (copy_to_user(buffer, &compat_event,
3606addb1d6SDmitry Torokhov 				 sizeof(struct input_event_compat)))
3613a51f7c4SDmitry Torokhov 			return -EFAULT;
3623a51f7c4SDmitry Torokhov 
3633a51f7c4SDmitry Torokhov 	} else {
3643a51f7c4SDmitry Torokhov 		if (copy_to_user(buffer, event, sizeof(struct input_event)))
3653a51f7c4SDmitry Torokhov 			return -EFAULT;
3663a51f7c4SDmitry Torokhov 	}
3673a51f7c4SDmitry Torokhov 
3683a51f7c4SDmitry Torokhov 	return 0;
3693a51f7c4SDmitry Torokhov }
3703a51f7c4SDmitry Torokhov 
3713a51f7c4SDmitry Torokhov #else
3723a51f7c4SDmitry Torokhov 
3733a51f7c4SDmitry Torokhov static inline size_t evdev_event_size(void)
3743a51f7c4SDmitry Torokhov {
3753a51f7c4SDmitry Torokhov 	return sizeof(struct input_event);
3763a51f7c4SDmitry Torokhov }
3773a51f7c4SDmitry Torokhov 
3786addb1d6SDmitry Torokhov static int evdev_event_from_user(const char __user *buffer,
3796addb1d6SDmitry Torokhov 				 struct input_event *event)
3803a51f7c4SDmitry Torokhov {
3813a51f7c4SDmitry Torokhov 	if (copy_from_user(event, buffer, sizeof(struct input_event)))
3823a51f7c4SDmitry Torokhov 		return -EFAULT;
3833a51f7c4SDmitry Torokhov 
3843a51f7c4SDmitry Torokhov 	return 0;
3853a51f7c4SDmitry Torokhov }
3863a51f7c4SDmitry Torokhov 
3876addb1d6SDmitry Torokhov static int evdev_event_to_user(char __user *buffer,
3886addb1d6SDmitry Torokhov 				const struct input_event *event)
3893a51f7c4SDmitry Torokhov {
3903a51f7c4SDmitry Torokhov 	if (copy_to_user(buffer, event, sizeof(struct input_event)))
3913a51f7c4SDmitry Torokhov 		return -EFAULT;
3923a51f7c4SDmitry Torokhov 
3933a51f7c4SDmitry Torokhov 	return 0;
3943a51f7c4SDmitry Torokhov }
3953a51f7c4SDmitry Torokhov 
3963a51f7c4SDmitry Torokhov #endif /* CONFIG_COMPAT */
39752658bb6SJuergen Kreileder 
3986addb1d6SDmitry Torokhov static ssize_t evdev_write(struct file *file, const char __user *buffer,
3996addb1d6SDmitry Torokhov 			   size_t count, loff_t *ppos)
4001da177e4SLinus Torvalds {
401d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
402d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
4031da177e4SLinus Torvalds 	struct input_event event;
4046addb1d6SDmitry Torokhov 	int retval;
4051da177e4SLinus Torvalds 
4066addb1d6SDmitry Torokhov 	retval = mutex_lock_interruptible(&evdev->mutex);
4076addb1d6SDmitry Torokhov 	if (retval)
4086addb1d6SDmitry Torokhov 		return retval;
4096addb1d6SDmitry Torokhov 
4106addb1d6SDmitry Torokhov 	if (!evdev->exist) {
4116addb1d6SDmitry Torokhov 		retval = -ENODEV;
4126addb1d6SDmitry Torokhov 		goto out;
4136addb1d6SDmitry Torokhov 	}
41452658bb6SJuergen Kreileder 
4153a51f7c4SDmitry Torokhov 	while (retval < count) {
41652658bb6SJuergen Kreileder 
4176addb1d6SDmitry Torokhov 		if (evdev_event_from_user(buffer + retval, &event)) {
4186addb1d6SDmitry Torokhov 			retval = -EFAULT;
4196addb1d6SDmitry Torokhov 			goto out;
4206addb1d6SDmitry Torokhov 		}
4216addb1d6SDmitry Torokhov 
4226addb1d6SDmitry Torokhov 		input_inject_event(&evdev->handle,
4236addb1d6SDmitry Torokhov 				   event.type, event.code, event.value);
4243a51f7c4SDmitry Torokhov 		retval += evdev_event_size();
42552658bb6SJuergen Kreileder 	}
42652658bb6SJuergen Kreileder 
4276addb1d6SDmitry Torokhov  out:
4286addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
42952658bb6SJuergen Kreileder 	return retval;
43052658bb6SJuergen Kreileder }
43152658bb6SJuergen Kreileder 
4326addb1d6SDmitry Torokhov static int evdev_fetch_next_event(struct evdev_client *client,
4336addb1d6SDmitry Torokhov 				  struct input_event *event)
4346addb1d6SDmitry Torokhov {
4356addb1d6SDmitry Torokhov 	int have_event;
4366addb1d6SDmitry Torokhov 
4376addb1d6SDmitry Torokhov 	spin_lock_irq(&client->buffer_lock);
4386addb1d6SDmitry Torokhov 
4396addb1d6SDmitry Torokhov 	have_event = client->head != client->tail;
4406addb1d6SDmitry Torokhov 	if (have_event) {
4416addb1d6SDmitry Torokhov 		*event = client->buffer[client->tail++];
4426addb1d6SDmitry Torokhov 		client->tail &= EVDEV_BUFFER_SIZE - 1;
4436addb1d6SDmitry Torokhov 	}
4446addb1d6SDmitry Torokhov 
4456addb1d6SDmitry Torokhov 	spin_unlock_irq(&client->buffer_lock);
4466addb1d6SDmitry Torokhov 
4476addb1d6SDmitry Torokhov 	return have_event;
4486addb1d6SDmitry Torokhov }
4496addb1d6SDmitry Torokhov 
4506addb1d6SDmitry Torokhov static ssize_t evdev_read(struct file *file, char __user *buffer,
4516addb1d6SDmitry Torokhov 			  size_t count, loff_t *ppos)
4521da177e4SLinus Torvalds {
453d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
454d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
4556addb1d6SDmitry Torokhov 	struct input_event event;
4561da177e4SLinus Torvalds 	int retval;
4571da177e4SLinus Torvalds 
4583a51f7c4SDmitry Torokhov 	if (count < evdev_event_size())
4591da177e4SLinus Torvalds 		return -EINVAL;
4601da177e4SLinus Torvalds 
4616addb1d6SDmitry Torokhov 	if (client->head == client->tail && evdev->exist &&
4626addb1d6SDmitry Torokhov 	    (file->f_flags & O_NONBLOCK))
4631da177e4SLinus Torvalds 		return -EAGAIN;
4641da177e4SLinus Torvalds 
465d0ffb9beSDmitry Torokhov 	retval = wait_event_interruptible(evdev->wait,
466d0ffb9beSDmitry Torokhov 		client->head != client->tail || !evdev->exist);
4671da177e4SLinus Torvalds 	if (retval)
4681da177e4SLinus Torvalds 		return retval;
4691da177e4SLinus Torvalds 
470d0ffb9beSDmitry Torokhov 	if (!evdev->exist)
4711da177e4SLinus Torvalds 		return -ENODEV;
4721da177e4SLinus Torvalds 
4736addb1d6SDmitry Torokhov 	while (retval + evdev_event_size() <= count &&
4746addb1d6SDmitry Torokhov 	       evdev_fetch_next_event(client, &event)) {
4753a51f7c4SDmitry Torokhov 
4766addb1d6SDmitry Torokhov 		if (evdev_event_to_user(buffer + retval, &event))
4773a51f7c4SDmitry Torokhov 			return -EFAULT;
4783a51f7c4SDmitry Torokhov 
4793a51f7c4SDmitry Torokhov 		retval += evdev_event_size();
4801da177e4SLinus Torvalds 	}
4811da177e4SLinus Torvalds 
4821da177e4SLinus Torvalds 	return retval;
4831da177e4SLinus Torvalds }
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds /* No kernel lock - fine */
4861da177e4SLinus Torvalds static unsigned int evdev_poll(struct file *file, poll_table *wait)
4871da177e4SLinus Torvalds {
488d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
489d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
4901e0afb28SDmitry Torokhov 
491d0ffb9beSDmitry Torokhov 	poll_wait(file, &evdev->wait, wait);
492d0ffb9beSDmitry Torokhov 	return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) |
493d0ffb9beSDmitry Torokhov 		(evdev->exist ? 0 : (POLLHUP | POLLERR));
4941da177e4SLinus Torvalds }
4951da177e4SLinus Torvalds 
4963a51f7c4SDmitry Torokhov #ifdef CONFIG_COMPAT
4973a51f7c4SDmitry Torokhov 
4983a51f7c4SDmitry Torokhov #define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)
4997b19ada2SJiri Slaby #define BITS_TO_LONGS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1)
5003a51f7c4SDmitry Torokhov 
5013a51f7c4SDmitry Torokhov #ifdef __BIG_ENDIAN
5023a51f7c4SDmitry Torokhov static int bits_to_user(unsigned long *bits, unsigned int maxbit,
5033a51f7c4SDmitry Torokhov 			unsigned int maxlen, void __user *p, int compat)
5043a51f7c4SDmitry Torokhov {
5053a51f7c4SDmitry Torokhov 	int len, i;
5063a51f7c4SDmitry Torokhov 
5073a51f7c4SDmitry Torokhov 	if (compat) {
5087b19ada2SJiri Slaby 		len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t);
509bf61f8d3SKenichi Nagai 		if (len > maxlen)
5103a51f7c4SDmitry Torokhov 			len = maxlen;
5113a51f7c4SDmitry Torokhov 
5123a51f7c4SDmitry Torokhov 		for (i = 0; i < len / sizeof(compat_long_t); i++)
5133a51f7c4SDmitry Torokhov 			if (copy_to_user((compat_long_t __user *) p + i,
5143a51f7c4SDmitry Torokhov 					 (compat_long_t *) bits +
5153a51f7c4SDmitry Torokhov 						i + 1 - ((i % 2) << 1),
5163a51f7c4SDmitry Torokhov 					 sizeof(compat_long_t)))
5173a51f7c4SDmitry Torokhov 				return -EFAULT;
5183a51f7c4SDmitry Torokhov 	} else {
5197b19ada2SJiri Slaby 		len = BITS_TO_LONGS(maxbit) * sizeof(long);
5203a51f7c4SDmitry Torokhov 		if (len > maxlen)
5213a51f7c4SDmitry Torokhov 			len = maxlen;
5223a51f7c4SDmitry Torokhov 
5233a51f7c4SDmitry Torokhov 		if (copy_to_user(p, bits, len))
5243a51f7c4SDmitry Torokhov 			return -EFAULT;
5253a51f7c4SDmitry Torokhov 	}
5263a51f7c4SDmitry Torokhov 
5273a51f7c4SDmitry Torokhov 	return len;
5283a51f7c4SDmitry Torokhov }
5293a51f7c4SDmitry Torokhov #else
5303a51f7c4SDmitry Torokhov static int bits_to_user(unsigned long *bits, unsigned int maxbit,
5313a51f7c4SDmitry Torokhov 			unsigned int maxlen, void __user *p, int compat)
5323a51f7c4SDmitry Torokhov {
5333a51f7c4SDmitry Torokhov 	int len = compat ?
5347b19ada2SJiri Slaby 			BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t) :
5357b19ada2SJiri Slaby 			BITS_TO_LONGS(maxbit) * sizeof(long);
5363a51f7c4SDmitry Torokhov 
5373a51f7c4SDmitry Torokhov 	if (len > maxlen)
5383a51f7c4SDmitry Torokhov 		len = maxlen;
5393a51f7c4SDmitry Torokhov 
5403a51f7c4SDmitry Torokhov 	return copy_to_user(p, bits, len) ? -EFAULT : len;
5413a51f7c4SDmitry Torokhov }
5423a51f7c4SDmitry Torokhov #endif /* __BIG_ENDIAN */
5433a51f7c4SDmitry Torokhov 
5443a51f7c4SDmitry Torokhov #else
5453a51f7c4SDmitry Torokhov 
5463a51f7c4SDmitry Torokhov static int bits_to_user(unsigned long *bits, unsigned int maxbit,
5473a51f7c4SDmitry Torokhov 			unsigned int maxlen, void __user *p, int compat)
5483a51f7c4SDmitry Torokhov {
5497b19ada2SJiri Slaby 	int len = BITS_TO_LONGS(maxbit) * sizeof(long);
5503a51f7c4SDmitry Torokhov 
5513a51f7c4SDmitry Torokhov 	if (len > maxlen)
5523a51f7c4SDmitry Torokhov 		len = maxlen;
5533a51f7c4SDmitry Torokhov 
5543a51f7c4SDmitry Torokhov 	return copy_to_user(p, bits, len) ? -EFAULT : len;
5553a51f7c4SDmitry Torokhov }
5563a51f7c4SDmitry Torokhov 
5573a51f7c4SDmitry Torokhov #endif /* CONFIG_COMPAT */
5583a51f7c4SDmitry Torokhov 
5593a51f7c4SDmitry Torokhov static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
5603a51f7c4SDmitry Torokhov {
5613a51f7c4SDmitry Torokhov 	int len;
5623a51f7c4SDmitry Torokhov 
5633a51f7c4SDmitry Torokhov 	if (!str)
5643a51f7c4SDmitry Torokhov 		return -ENOENT;
5653a51f7c4SDmitry Torokhov 
5663a51f7c4SDmitry Torokhov 	len = strlen(str) + 1;
5673a51f7c4SDmitry Torokhov 	if (len > maxlen)
5683a51f7c4SDmitry Torokhov 		len = maxlen;
5693a51f7c4SDmitry Torokhov 
5703a51f7c4SDmitry Torokhov 	return copy_to_user(p, str, len) ? -EFAULT : len;
5713a51f7c4SDmitry Torokhov }
5723a51f7c4SDmitry Torokhov 
5736addb1d6SDmitry Torokhov static long evdev_do_ioctl(struct file *file, unsigned int cmd,
5743a51f7c4SDmitry Torokhov 			   void __user *p, int compat_mode)
5751da177e4SLinus Torvalds {
576d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
577d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
5781da177e4SLinus Torvalds 	struct input_dev *dev = evdev->handle.dev;
5791da177e4SLinus Torvalds 	struct input_absinfo abs;
580509ca1a9SAnssi Hannula 	struct ff_effect effect;
5813a51f7c4SDmitry Torokhov 	int __user *ip = (int __user *)p;
5821da177e4SLinus Torvalds 	int i, t, u, v;
583509ca1a9SAnssi Hannula 	int error;
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds 	switch (cmd) {
5861da177e4SLinus Torvalds 
5871da177e4SLinus Torvalds 	case EVIOCGVERSION:
5881da177e4SLinus Torvalds 		return put_user(EV_VERSION, ip);
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds 	case EVIOCGID:
5913a51f7c4SDmitry Torokhov 		if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
5923a51f7c4SDmitry Torokhov 			return -EFAULT;
59308791e5cSDmitry Torokhov 		return 0;
59408791e5cSDmitry Torokhov 
59508791e5cSDmitry Torokhov 	case EVIOCGREP:
59608791e5cSDmitry Torokhov 		if (!test_bit(EV_REP, dev->evbit))
59708791e5cSDmitry Torokhov 			return -ENOSYS;
59808791e5cSDmitry Torokhov 		if (put_user(dev->rep[REP_DELAY], ip))
59908791e5cSDmitry Torokhov 			return -EFAULT;
60008791e5cSDmitry Torokhov 		if (put_user(dev->rep[REP_PERIOD], ip + 1))
60108791e5cSDmitry Torokhov 			return -EFAULT;
60208791e5cSDmitry Torokhov 		return 0;
60308791e5cSDmitry Torokhov 
60408791e5cSDmitry Torokhov 	case EVIOCSREP:
60508791e5cSDmitry Torokhov 		if (!test_bit(EV_REP, dev->evbit))
60608791e5cSDmitry Torokhov 			return -ENOSYS;
60708791e5cSDmitry Torokhov 		if (get_user(u, ip))
60808791e5cSDmitry Torokhov 			return -EFAULT;
60908791e5cSDmitry Torokhov 		if (get_user(v, ip + 1))
61008791e5cSDmitry Torokhov 			return -EFAULT;
61108791e5cSDmitry Torokhov 
6120e739d28SDmitry Torokhov 		input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
6130e739d28SDmitry Torokhov 		input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
6143a51f7c4SDmitry Torokhov 
6153a51f7c4SDmitry Torokhov 		return 0;
6161da177e4SLinus Torvalds 
6171da177e4SLinus Torvalds 	case EVIOCGKEYCODE:
6183a51f7c4SDmitry Torokhov 		if (get_user(t, ip))
6193a51f7c4SDmitry Torokhov 			return -EFAULT;
620c8e4c772SMarvin Raaijmakers 
621f4f37c8eSDmitry Torokhov 		error = input_get_keycode(dev, t, &v);
622c8e4c772SMarvin Raaijmakers 		if (error)
623c8e4c772SMarvin Raaijmakers 			return error;
624c8e4c772SMarvin Raaijmakers 
625c8e4c772SMarvin Raaijmakers 		if (put_user(v, ip + 1))
6263a51f7c4SDmitry Torokhov 			return -EFAULT;
627c8e4c772SMarvin Raaijmakers 
6281da177e4SLinus Torvalds 		return 0;
6291da177e4SLinus Torvalds 
6301da177e4SLinus Torvalds 	case EVIOCSKEYCODE:
631c8e4c772SMarvin Raaijmakers 		if (get_user(t, ip) || get_user(v, ip + 1))
6323a51f7c4SDmitry Torokhov 			return -EFAULT;
6333a51f7c4SDmitry Torokhov 
634f4f37c8eSDmitry Torokhov 		return input_set_keycode(dev, t, v);
6351da177e4SLinus Torvalds 
6361da177e4SLinus Torvalds 	case EVIOCSFF:
6371da177e4SLinus Torvalds 		if (copy_from_user(&effect, p, sizeof(effect)))
6381da177e4SLinus Torvalds 			return -EFAULT;
639509ca1a9SAnssi Hannula 
640509ca1a9SAnssi Hannula 		error = input_ff_upload(dev, &effect, file);
641509ca1a9SAnssi Hannula 
6423a51f7c4SDmitry Torokhov 		if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
6431da177e4SLinus Torvalds 			return -EFAULT;
644509ca1a9SAnssi Hannula 
645509ca1a9SAnssi Hannula 		return error;
6461da177e4SLinus Torvalds 
6471da177e4SLinus Torvalds 	case EVIOCRMFF:
648509ca1a9SAnssi Hannula 		return input_ff_erase(dev, (int)(unsigned long) p, file);
6491da177e4SLinus Torvalds 
6501da177e4SLinus Torvalds 	case EVIOCGEFFECTS:
6516addb1d6SDmitry Torokhov 		i = test_bit(EV_FF, dev->evbit) ?
6526addb1d6SDmitry Torokhov 				dev->ff->max_effects : 0;
653509ca1a9SAnssi Hannula 		if (put_user(i, ip))
6541da177e4SLinus Torvalds 			return -EFAULT;
6551da177e4SLinus Torvalds 		return 0;
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds 	case EVIOCGRAB:
6586addb1d6SDmitry Torokhov 		if (p)
6596addb1d6SDmitry Torokhov 			return evdev_grab(evdev, client);
6606addb1d6SDmitry Torokhov 		else
6616addb1d6SDmitry Torokhov 			return evdev_ungrab(evdev, client);
6621da177e4SLinus Torvalds 
6631da177e4SLinus Torvalds 	default:
6641da177e4SLinus Torvalds 
66541e979f8SVojtech Pavlik 		if (_IOC_TYPE(cmd) != 'E')
6661da177e4SLinus Torvalds 			return -EINVAL;
6671da177e4SLinus Torvalds 
66841e979f8SVojtech Pavlik 		if (_IOC_DIR(cmd) == _IOC_READ) {
66941e979f8SVojtech Pavlik 
6701da177e4SLinus Torvalds 			if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0))) {
6711da177e4SLinus Torvalds 
672ce305b6aSDmitry Torokhov 				unsigned long *bits;
6731da177e4SLinus Torvalds 				int len;
6741da177e4SLinus Torvalds 
6751da177e4SLinus Torvalds 				switch (_IOC_NR(cmd) & EV_MAX) {
6766addb1d6SDmitry Torokhov 
6771da177e4SLinus Torvalds 				case      0: bits = dev->evbit;  len = EV_MAX;  break;
6781da177e4SLinus Torvalds 				case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
6791da177e4SLinus Torvalds 				case EV_REL: bits = dev->relbit; len = REL_MAX; break;
6801da177e4SLinus Torvalds 				case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
6811da177e4SLinus Torvalds 				case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
6821da177e4SLinus Torvalds 				case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
6831da177e4SLinus Torvalds 				case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
6841da177e4SLinus Torvalds 				case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  break;
68531581066SRichard Purdie 				case EV_SW:  bits = dev->swbit;  len = SW_MAX;  break;
6861da177e4SLinus Torvalds 				default: return -EINVAL;
6871da177e4SLinus Torvalds 				}
6883a51f7c4SDmitry Torokhov 				return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
68952658bb6SJuergen Kreileder 			}
69052658bb6SJuergen Kreileder 
69141e979f8SVojtech Pavlik 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
6923a51f7c4SDmitry Torokhov 				return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
6933a51f7c4SDmitry Torokhov 						    p, compat_mode);
69452658bb6SJuergen Kreileder 
69541e979f8SVojtech Pavlik 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
6963a51f7c4SDmitry Torokhov 				return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
6973a51f7c4SDmitry Torokhov 						    p, compat_mode);
69852658bb6SJuergen Kreileder 
69941e979f8SVojtech Pavlik 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
7003a51f7c4SDmitry Torokhov 				return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
7013a51f7c4SDmitry Torokhov 						    p, compat_mode);
70252658bb6SJuergen Kreileder 
7037972720aSDmitry Torokhov 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
7043a51f7c4SDmitry Torokhov 				return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
7053a51f7c4SDmitry Torokhov 						    p, compat_mode);
7067972720aSDmitry Torokhov 
7073a51f7c4SDmitry Torokhov 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
7083a51f7c4SDmitry Torokhov 				return str_to_user(dev->name, _IOC_SIZE(cmd), p);
70952658bb6SJuergen Kreileder 
7103a51f7c4SDmitry Torokhov 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
7113a51f7c4SDmitry Torokhov 				return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
71252658bb6SJuergen Kreileder 
7133a51f7c4SDmitry Torokhov 			if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
7143a51f7c4SDmitry Torokhov 				return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
71552658bb6SJuergen Kreileder 
71652658bb6SJuergen Kreileder 			if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
71752658bb6SJuergen Kreileder 
718ce305b6aSDmitry Torokhov 				t = _IOC_NR(cmd) & ABS_MAX;
71952658bb6SJuergen Kreileder 
72052658bb6SJuergen Kreileder 				abs.value = dev->abs[t];
72152658bb6SJuergen Kreileder 				abs.minimum = dev->absmin[t];
72252658bb6SJuergen Kreileder 				abs.maximum = dev->absmax[t];
72352658bb6SJuergen Kreileder 				abs.fuzz = dev->absfuzz[t];
72452658bb6SJuergen Kreileder 				abs.flat = dev->absflat[t];
72552658bb6SJuergen Kreileder 
72652658bb6SJuergen Kreileder 				if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
72752658bb6SJuergen Kreileder 					return -EFAULT;
72852658bb6SJuergen Kreileder 
72952658bb6SJuergen Kreileder 				return 0;
73052658bb6SJuergen Kreileder 			}
7313a51f7c4SDmitry Torokhov 
73241e979f8SVojtech Pavlik 		}
73341e979f8SVojtech Pavlik 
73441e979f8SVojtech Pavlik 		if (_IOC_DIR(cmd) == _IOC_WRITE) {
73552658bb6SJuergen Kreileder 
73652658bb6SJuergen Kreileder 			if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
73752658bb6SJuergen Kreileder 
738ce305b6aSDmitry Torokhov 				t = _IOC_NR(cmd) & ABS_MAX;
73952658bb6SJuergen Kreileder 
7406addb1d6SDmitry Torokhov 				if (copy_from_user(&abs, p,
7416addb1d6SDmitry Torokhov 						sizeof(struct input_absinfo)))
74252658bb6SJuergen Kreileder 					return -EFAULT;
74352658bb6SJuergen Kreileder 
7446addb1d6SDmitry Torokhov 				/*
7456addb1d6SDmitry Torokhov 				 * Take event lock to ensure that we are not
7466addb1d6SDmitry Torokhov 				 * changing device parameters in the middle
7476addb1d6SDmitry Torokhov 				 * of event.
7486addb1d6SDmitry Torokhov 				 */
7496addb1d6SDmitry Torokhov 				spin_lock_irq(&dev->event_lock);
7506addb1d6SDmitry Torokhov 
75152658bb6SJuergen Kreileder 				dev->abs[t] = abs.value;
75252658bb6SJuergen Kreileder 				dev->absmin[t] = abs.minimum;
75352658bb6SJuergen Kreileder 				dev->absmax[t] = abs.maximum;
75452658bb6SJuergen Kreileder 				dev->absfuzz[t] = abs.fuzz;
75552658bb6SJuergen Kreileder 				dev->absflat[t] = abs.flat;
75652658bb6SJuergen Kreileder 
7576addb1d6SDmitry Torokhov 				spin_unlock_irq(&dev->event_lock);
7586addb1d6SDmitry Torokhov 
75952658bb6SJuergen Kreileder 				return 0;
76052658bb6SJuergen Kreileder 			}
76152658bb6SJuergen Kreileder 		}
76241e979f8SVojtech Pavlik 	}
76352658bb6SJuergen Kreileder 	return -EINVAL;
76452658bb6SJuergen Kreileder }
7653a51f7c4SDmitry Torokhov 
7666addb1d6SDmitry Torokhov static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
7676addb1d6SDmitry Torokhov 				void __user *p, int compat_mode)
7686addb1d6SDmitry Torokhov {
7696addb1d6SDmitry Torokhov 	struct evdev_client *client = file->private_data;
7706addb1d6SDmitry Torokhov 	struct evdev *evdev = client->evdev;
7716addb1d6SDmitry Torokhov 	int retval;
7726addb1d6SDmitry Torokhov 
7736addb1d6SDmitry Torokhov 	retval = mutex_lock_interruptible(&evdev->mutex);
7746addb1d6SDmitry Torokhov 	if (retval)
7756addb1d6SDmitry Torokhov 		return retval;
7766addb1d6SDmitry Torokhov 
7776addb1d6SDmitry Torokhov 	if (!evdev->exist) {
7786addb1d6SDmitry Torokhov 		retval = -ENODEV;
7796addb1d6SDmitry Torokhov 		goto out;
7806addb1d6SDmitry Torokhov 	}
7816addb1d6SDmitry Torokhov 
7826addb1d6SDmitry Torokhov 	retval = evdev_do_ioctl(file, cmd, p, compat_mode);
7836addb1d6SDmitry Torokhov 
7846addb1d6SDmitry Torokhov  out:
7856addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
7866addb1d6SDmitry Torokhov 	return retval;
7876addb1d6SDmitry Torokhov }
7886addb1d6SDmitry Torokhov 
7893a51f7c4SDmitry Torokhov static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
7903a51f7c4SDmitry Torokhov {
7913a51f7c4SDmitry Torokhov 	return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
7923a51f7c4SDmitry Torokhov }
7933a51f7c4SDmitry Torokhov 
7943a51f7c4SDmitry Torokhov #ifdef CONFIG_COMPAT
7956addb1d6SDmitry Torokhov static long evdev_ioctl_compat(struct file *file,
7966addb1d6SDmitry Torokhov 				unsigned int cmd, unsigned long arg)
7973a51f7c4SDmitry Torokhov {
7983a51f7c4SDmitry Torokhov 	return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
7993a51f7c4SDmitry Torokhov }
80052658bb6SJuergen Kreileder #endif
80152658bb6SJuergen Kreileder 
80266e66118SDmitry Torokhov static const struct file_operations evdev_fops = {
8031da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
8041da177e4SLinus Torvalds 	.read		= evdev_read,
8051da177e4SLinus Torvalds 	.write		= evdev_write,
8061da177e4SLinus Torvalds 	.poll		= evdev_poll,
8071da177e4SLinus Torvalds 	.open		= evdev_open,
8081da177e4SLinus Torvalds 	.release	= evdev_release,
80952658bb6SJuergen Kreileder 	.unlocked_ioctl	= evdev_ioctl,
81052658bb6SJuergen Kreileder #ifdef CONFIG_COMPAT
81152658bb6SJuergen Kreileder 	.compat_ioctl	= evdev_ioctl_compat,
81252658bb6SJuergen Kreileder #endif
8131da177e4SLinus Torvalds 	.fasync		= evdev_fasync,
8141da177e4SLinus Torvalds 	.flush		= evdev_flush
8151da177e4SLinus Torvalds };
8161da177e4SLinus Torvalds 
8176addb1d6SDmitry Torokhov static int evdev_install_chrdev(struct evdev *evdev)
8186addb1d6SDmitry Torokhov {
8196addb1d6SDmitry Torokhov 	/*
8206addb1d6SDmitry Torokhov 	 * No need to do any locking here as calls to connect and
8216addb1d6SDmitry Torokhov 	 * disconnect are serialized by the input core
8226addb1d6SDmitry Torokhov 	 */
8236addb1d6SDmitry Torokhov 	evdev_table[evdev->minor] = evdev;
8246addb1d6SDmitry Torokhov 	return 0;
8256addb1d6SDmitry Torokhov }
8266addb1d6SDmitry Torokhov 
8276addb1d6SDmitry Torokhov static void evdev_remove_chrdev(struct evdev *evdev)
8286addb1d6SDmitry Torokhov {
8296addb1d6SDmitry Torokhov 	/*
8306addb1d6SDmitry Torokhov 	 * Lock evdev table to prevent race with evdev_open()
8316addb1d6SDmitry Torokhov 	 */
8326addb1d6SDmitry Torokhov 	mutex_lock(&evdev_table_mutex);
8336addb1d6SDmitry Torokhov 	evdev_table[evdev->minor] = NULL;
8346addb1d6SDmitry Torokhov 	mutex_unlock(&evdev_table_mutex);
8356addb1d6SDmitry Torokhov }
8366addb1d6SDmitry Torokhov 
8376addb1d6SDmitry Torokhov /*
8386addb1d6SDmitry Torokhov  * Mark device non-existent. This disables writes, ioctls and
8396addb1d6SDmitry Torokhov  * prevents new users from opening the device. Already posted
8406addb1d6SDmitry Torokhov  * blocking reads will stay, however new ones will fail.
8416addb1d6SDmitry Torokhov  */
8426addb1d6SDmitry Torokhov static void evdev_mark_dead(struct evdev *evdev)
8436addb1d6SDmitry Torokhov {
8446addb1d6SDmitry Torokhov 	mutex_lock(&evdev->mutex);
8456addb1d6SDmitry Torokhov 	evdev->exist = 0;
8466addb1d6SDmitry Torokhov 	mutex_unlock(&evdev->mutex);
8476addb1d6SDmitry Torokhov }
8486addb1d6SDmitry Torokhov 
8496addb1d6SDmitry Torokhov static void evdev_cleanup(struct evdev *evdev)
8506addb1d6SDmitry Torokhov {
8516addb1d6SDmitry Torokhov 	struct input_handle *handle = &evdev->handle;
8526addb1d6SDmitry Torokhov 
8536addb1d6SDmitry Torokhov 	evdev_mark_dead(evdev);
8546addb1d6SDmitry Torokhov 	evdev_hangup(evdev);
8556addb1d6SDmitry Torokhov 	evdev_remove_chrdev(evdev);
8566addb1d6SDmitry Torokhov 
8576addb1d6SDmitry Torokhov 	/* evdev is marked dead so no one else accesses evdev->open */
8586addb1d6SDmitry Torokhov 	if (evdev->open) {
8596addb1d6SDmitry Torokhov 		input_flush_device(handle, NULL);
8606addb1d6SDmitry Torokhov 		input_close_device(handle);
8616addb1d6SDmitry Torokhov 	}
8626addb1d6SDmitry Torokhov }
8636addb1d6SDmitry Torokhov 
8646addb1d6SDmitry Torokhov /*
8656addb1d6SDmitry Torokhov  * Create new evdev device. Note that input core serializes calls
8666addb1d6SDmitry Torokhov  * to connect and disconnect so we don't need to lock evdev_table here.
8676addb1d6SDmitry Torokhov  */
8685b2a0826SDmitry Torokhov static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
86966e66118SDmitry Torokhov 			 const struct input_device_id *id)
8701da177e4SLinus Torvalds {
8711da177e4SLinus Torvalds 	struct evdev *evdev;
8721da177e4SLinus Torvalds 	int minor;
8735b2a0826SDmitry Torokhov 	int error;
8741da177e4SLinus Torvalds 
8756addb1d6SDmitry Torokhov 	for (minor = 0; minor < EVDEV_MINORS; minor++)
8766addb1d6SDmitry Torokhov 		if (!evdev_table[minor])
8776addb1d6SDmitry Torokhov 			break;
8786addb1d6SDmitry Torokhov 
8791da177e4SLinus Torvalds 	if (minor == EVDEV_MINORS) {
8801da177e4SLinus Torvalds 		printk(KERN_ERR "evdev: no more free evdev devices\n");
8815b2a0826SDmitry Torokhov 		return -ENFILE;
8821da177e4SLinus Torvalds 	}
8831da177e4SLinus Torvalds 
8845b2a0826SDmitry Torokhov 	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
8855b2a0826SDmitry Torokhov 	if (!evdev)
8865b2a0826SDmitry Torokhov 		return -ENOMEM;
8871da177e4SLinus Torvalds 
888d0ffb9beSDmitry Torokhov 	INIT_LIST_HEAD(&evdev->client_list);
8896addb1d6SDmitry Torokhov 	spin_lock_init(&evdev->client_lock);
8906addb1d6SDmitry Torokhov 	mutex_init(&evdev->mutex);
8911da177e4SLinus Torvalds 	init_waitqueue_head(&evdev->wait);
8921da177e4SLinus Torvalds 
8936addb1d6SDmitry Torokhov 	snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
8941da177e4SLinus Torvalds 	evdev->exist = 1;
8951da177e4SLinus Torvalds 	evdev->minor = minor;
8966addb1d6SDmitry Torokhov 
897*a7097ff8SDmitry Torokhov 	evdev->handle.dev = input_get_device(dev);
8981da177e4SLinus Torvalds 	evdev->handle.name = evdev->name;
8991da177e4SLinus Torvalds 	evdev->handle.handler = handler;
9001da177e4SLinus Torvalds 	evdev->handle.private = evdev;
9019657d75cSDmitry Torokhov 
9026addb1d6SDmitry Torokhov 	strlcpy(evdev->dev.bus_id, evdev->name, sizeof(evdev->dev.bus_id));
9036addb1d6SDmitry Torokhov 	evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
9049657d75cSDmitry Torokhov 	evdev->dev.class = &input_class;
9059657d75cSDmitry Torokhov 	evdev->dev.parent = &dev->dev;
9069657d75cSDmitry Torokhov 	evdev->dev.release = evdev_free;
9079657d75cSDmitry Torokhov 	device_initialize(&evdev->dev);
9081da177e4SLinus Torvalds 
9096addb1d6SDmitry Torokhov 	error = input_register_handle(&evdev->handle);
9105b2a0826SDmitry Torokhov 	if (error)
9119657d75cSDmitry Torokhov 		goto err_free_evdev;
9121da177e4SLinus Torvalds 
9136addb1d6SDmitry Torokhov 	error = evdev_install_chrdev(evdev);
9145b2a0826SDmitry Torokhov 	if (error)
9156addb1d6SDmitry Torokhov 		goto err_unregister_handle;
9166addb1d6SDmitry Torokhov 
9176addb1d6SDmitry Torokhov 	error = device_add(&evdev->dev);
9186addb1d6SDmitry Torokhov 	if (error)
9196addb1d6SDmitry Torokhov 		goto err_cleanup_evdev;
9205b2a0826SDmitry Torokhov 
9215b2a0826SDmitry Torokhov 	return 0;
9225b2a0826SDmitry Torokhov 
9236addb1d6SDmitry Torokhov  err_cleanup_evdev:
9246addb1d6SDmitry Torokhov 	evdev_cleanup(evdev);
9256addb1d6SDmitry Torokhov  err_unregister_handle:
9266addb1d6SDmitry Torokhov 	input_unregister_handle(&evdev->handle);
9275b2a0826SDmitry Torokhov  err_free_evdev:
9289657d75cSDmitry Torokhov 	put_device(&evdev->dev);
9295b2a0826SDmitry Torokhov 	return error;
9301da177e4SLinus Torvalds }
9311da177e4SLinus Torvalds 
9321da177e4SLinus Torvalds static void evdev_disconnect(struct input_handle *handle)
9331da177e4SLinus Torvalds {
9341da177e4SLinus Torvalds 	struct evdev *evdev = handle->private;
9351da177e4SLinus Torvalds 
9369657d75cSDmitry Torokhov 	device_del(&evdev->dev);
9376addb1d6SDmitry Torokhov 	evdev_cleanup(evdev);
9386addb1d6SDmitry Torokhov 	input_unregister_handle(handle);
9399657d75cSDmitry Torokhov 	put_device(&evdev->dev);
9401da177e4SLinus Torvalds }
9411da177e4SLinus Torvalds 
94266e66118SDmitry Torokhov static const struct input_device_id evdev_ids[] = {
9431da177e4SLinus Torvalds 	{ .driver_info = 1 },	/* Matches all devices */
9441da177e4SLinus Torvalds 	{ },			/* Terminating zero entry */
9451da177e4SLinus Torvalds };
9461da177e4SLinus Torvalds 
9471da177e4SLinus Torvalds MODULE_DEVICE_TABLE(input, evdev_ids);
9481da177e4SLinus Torvalds 
9491da177e4SLinus Torvalds static struct input_handler evdev_handler = {
9501da177e4SLinus Torvalds 	.event		= evdev_event,
9511da177e4SLinus Torvalds 	.connect	= evdev_connect,
9521da177e4SLinus Torvalds 	.disconnect	= evdev_disconnect,
9531da177e4SLinus Torvalds 	.fops		= &evdev_fops,
9541da177e4SLinus Torvalds 	.minor		= EVDEV_MINOR_BASE,
9551da177e4SLinus Torvalds 	.name		= "evdev",
9561da177e4SLinus Torvalds 	.id_table	= evdev_ids,
9571da177e4SLinus Torvalds };
9581da177e4SLinus Torvalds 
9591da177e4SLinus Torvalds static int __init evdev_init(void)
9601da177e4SLinus Torvalds {
9614263cf0fSDmitry Torokhov 	return input_register_handler(&evdev_handler);
9621da177e4SLinus Torvalds }
9631da177e4SLinus Torvalds 
9641da177e4SLinus Torvalds static void __exit evdev_exit(void)
9651da177e4SLinus Torvalds {
9661da177e4SLinus Torvalds 	input_unregister_handler(&evdev_handler);
9671da177e4SLinus Torvalds }
9681da177e4SLinus Torvalds 
9691da177e4SLinus Torvalds module_init(evdev_init);
9701da177e4SLinus Torvalds module_exit(evdev_exit);
9711da177e4SLinus Torvalds 
9721da177e4SLinus Torvalds MODULE_AUTHOR("Vojtech Pavlik <[email protected]>");
9731da177e4SLinus Torvalds MODULE_DESCRIPTION("Input driver event char devices");
9741da177e4SLinus Torvalds MODULE_LICENSE("GPL");
975