xref: /linux-6.15/drivers/input/evdev.c (revision 1dfa2812)
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/smp_lock.h>
221da177e4SLinus Torvalds #include <linux/device.h>
2352658bb6SJuergen Kreileder #include <linux/compat.h>
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds struct evdev {
261da177e4SLinus Torvalds 	int exist;
271da177e4SLinus Torvalds 	int open;
281da177e4SLinus Torvalds 	int minor;
291da177e4SLinus Torvalds 	char name[16];
301da177e4SLinus Torvalds 	struct input_handle handle;
311da177e4SLinus Torvalds 	wait_queue_head_t wait;
32d0ffb9beSDmitry Torokhov 	struct evdev_client *grab;
33d0ffb9beSDmitry Torokhov 	struct list_head client_list;
341da177e4SLinus Torvalds };
351da177e4SLinus Torvalds 
36d0ffb9beSDmitry Torokhov struct evdev_client {
371da177e4SLinus Torvalds 	struct input_event buffer[EVDEV_BUFFER_SIZE];
381da177e4SLinus Torvalds 	int head;
391da177e4SLinus Torvalds 	int tail;
401da177e4SLinus Torvalds 	struct fasync_struct *fasync;
411da177e4SLinus Torvalds 	struct evdev *evdev;
421da177e4SLinus Torvalds 	struct list_head node;
431da177e4SLinus Torvalds };
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds static struct evdev *evdev_table[EVDEV_MINORS];
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
481da177e4SLinus Torvalds {
491da177e4SLinus Torvalds 	struct evdev *evdev = handle->private;
50d0ffb9beSDmitry Torokhov 	struct evdev_client *client;
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds 	if (evdev->grab) {
53d0ffb9beSDmitry Torokhov 		client = evdev->grab;
541da177e4SLinus Torvalds 
55d0ffb9beSDmitry Torokhov 		do_gettimeofday(&client->buffer[client->head].time);
56d0ffb9beSDmitry Torokhov 		client->buffer[client->head].type = type;
57d0ffb9beSDmitry Torokhov 		client->buffer[client->head].code = code;
58d0ffb9beSDmitry Torokhov 		client->buffer[client->head].value = value;
59d0ffb9beSDmitry Torokhov 		client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
601da177e4SLinus Torvalds 
61d0ffb9beSDmitry Torokhov 		kill_fasync(&client->fasync, SIGIO, POLL_IN);
621da177e4SLinus Torvalds 	} else
63d0ffb9beSDmitry Torokhov 		list_for_each_entry(client, &evdev->client_list, node) {
641da177e4SLinus Torvalds 
65d0ffb9beSDmitry Torokhov 			do_gettimeofday(&client->buffer[client->head].time);
66d0ffb9beSDmitry Torokhov 			client->buffer[client->head].type = type;
67d0ffb9beSDmitry Torokhov 			client->buffer[client->head].code = code;
68d0ffb9beSDmitry Torokhov 			client->buffer[client->head].value = value;
69d0ffb9beSDmitry Torokhov 			client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
701da177e4SLinus Torvalds 
71d0ffb9beSDmitry Torokhov 			kill_fasync(&client->fasync, SIGIO, POLL_IN);
721da177e4SLinus Torvalds 		}
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 	wake_up_interruptible(&evdev->wait);
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds static int evdev_fasync(int fd, struct file *file, int on)
781da177e4SLinus Torvalds {
79d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
801da177e4SLinus Torvalds 	int retval;
811e0afb28SDmitry Torokhov 
82d0ffb9beSDmitry Torokhov 	retval = fasync_helper(fd, file, on, &client->fasync);
831e0afb28SDmitry Torokhov 
841da177e4SLinus Torvalds 	return retval < 0 ? retval : 0;
851da177e4SLinus Torvalds }
861da177e4SLinus Torvalds 
8775e1fcc0SMiklos Szeredi static int evdev_flush(struct file *file, fl_owner_t id)
881da177e4SLinus Torvalds {
89d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
90d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
911e0afb28SDmitry Torokhov 
92d0ffb9beSDmitry Torokhov 	if (!evdev->exist)
931e0afb28SDmitry Torokhov 		return -ENODEV;
941e0afb28SDmitry Torokhov 
95d0ffb9beSDmitry Torokhov 	return input_flush_device(&evdev->handle, file);
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds static void evdev_free(struct evdev *evdev)
991da177e4SLinus Torvalds {
1001da177e4SLinus Torvalds 	evdev_table[evdev->minor] = NULL;
1011da177e4SLinus Torvalds 	kfree(evdev);
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds static int evdev_release(struct inode *inode, struct file *file)
1051da177e4SLinus Torvalds {
106d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
107d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
1081da177e4SLinus Torvalds 
109d0ffb9beSDmitry Torokhov 	if (evdev->grab == client) {
110d0ffb9beSDmitry Torokhov 		input_release_device(&evdev->handle);
111d0ffb9beSDmitry Torokhov 		evdev->grab = NULL;
1121da177e4SLinus Torvalds 	}
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	evdev_fasync(-1, file, 0);
115d0ffb9beSDmitry Torokhov 	list_del(&client->node);
116d0ffb9beSDmitry Torokhov 	kfree(client);
1171da177e4SLinus Torvalds 
118d0ffb9beSDmitry Torokhov 	if (!--evdev->open) {
119d0ffb9beSDmitry Torokhov 		if (evdev->exist)
120d0ffb9beSDmitry Torokhov 			input_close_device(&evdev->handle);
1211da177e4SLinus Torvalds 		else
122d0ffb9beSDmitry Torokhov 			evdev_free(evdev);
1231da177e4SLinus Torvalds 	}
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 	return 0;
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds static int evdev_open(struct inode *inode, struct file *file)
1291da177e4SLinus Torvalds {
130d0ffb9beSDmitry Torokhov 	struct evdev_client *client;
131d0ffb9beSDmitry Torokhov 	struct evdev *evdev;
1321da177e4SLinus Torvalds 	int i = iminor(inode) - EVDEV_MINOR_BASE;
133d542ed82SDmitry Torokhov 	int error;
1341da177e4SLinus Torvalds 
135d0ffb9beSDmitry Torokhov 	if (i >= EVDEV_MINORS)
1361da177e4SLinus Torvalds 		return -ENODEV;
1371da177e4SLinus Torvalds 
138d0ffb9beSDmitry Torokhov 	evdev = evdev_table[i];
139d0ffb9beSDmitry Torokhov 
140d0ffb9beSDmitry Torokhov 	if (!evdev || !evdev->exist)
141d0ffb9beSDmitry Torokhov 		return -ENODEV;
142d0ffb9beSDmitry Torokhov 
143d0ffb9beSDmitry Torokhov 	client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
144d0ffb9beSDmitry Torokhov 	if (!client)
1451da177e4SLinus Torvalds 		return -ENOMEM;
1461da177e4SLinus Torvalds 
147d0ffb9beSDmitry Torokhov 	client->evdev = evdev;
148d0ffb9beSDmitry Torokhov 	list_add_tail(&client->node, &evdev->client_list);
1491da177e4SLinus Torvalds 
150d542ed82SDmitry Torokhov 	if (!evdev->open++ && evdev->exist) {
151d542ed82SDmitry Torokhov 		error = input_open_device(&evdev->handle);
152d542ed82SDmitry Torokhov 		if (error) {
153d542ed82SDmitry Torokhov 			list_del(&client->node);
154d542ed82SDmitry Torokhov 			kfree(client);
155d542ed82SDmitry Torokhov 			return error;
156d542ed82SDmitry Torokhov 		}
157d542ed82SDmitry Torokhov 	}
1581da177e4SLinus Torvalds 
159d0ffb9beSDmitry Torokhov 	file->private_data = client;
1601da177e4SLinus Torvalds 	return 0;
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds 
16352658bb6SJuergen Kreileder #ifdef CONFIG_COMPAT
1643a51f7c4SDmitry Torokhov 
16552658bb6SJuergen Kreileder struct input_event_compat {
16652658bb6SJuergen Kreileder 	struct compat_timeval time;
16752658bb6SJuergen Kreileder 	__u16 type;
16852658bb6SJuergen Kreileder 	__u16 code;
16952658bb6SJuergen Kreileder 	__s32 value;
17052658bb6SJuergen Kreileder };
17152658bb6SJuergen Kreileder 
172bf2fcc6fSAndi Kleen /* Note to the author of this code: did it ever occur to
173bf2fcc6fSAndi Kleen    you why the ifdefs are needed? Think about it again. -AK */
17452658bb6SJuergen Kreileder #ifdef CONFIG_X86_64
175bf2fcc6fSAndi Kleen #  define COMPAT_TEST is_compat_task()
17652658bb6SJuergen Kreileder #elif defined(CONFIG_IA64)
1776450578fSAl Viro #  define COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current))
178347a8dc3SMartin Schwidefsky #elif defined(CONFIG_S390)
17952658bb6SJuergen Kreileder #  define COMPAT_TEST test_thread_flag(TIF_31BIT)
18059df6bbfSRalf Baechle #elif defined(CONFIG_MIPS)
18159df6bbfSRalf Baechle #  define COMPAT_TEST (current->thread.mflags & MF_32BIT_ADDR)
18252658bb6SJuergen Kreileder #else
18352658bb6SJuergen Kreileder #  define COMPAT_TEST test_thread_flag(TIF_32BIT)
18452658bb6SJuergen Kreileder #endif
18552658bb6SJuergen Kreileder 
1863a51f7c4SDmitry Torokhov static inline size_t evdev_event_size(void)
18752658bb6SJuergen Kreileder {
1883a51f7c4SDmitry Torokhov 	return COMPAT_TEST ?
1893a51f7c4SDmitry Torokhov 		sizeof(struct input_event_compat) : sizeof(struct input_event);
1903a51f7c4SDmitry Torokhov }
19152658bb6SJuergen Kreileder 
1923a51f7c4SDmitry Torokhov static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
1933a51f7c4SDmitry Torokhov {
1943a51f7c4SDmitry Torokhov 	if (COMPAT_TEST) {
1953a51f7c4SDmitry Torokhov 		struct input_event_compat compat_event;
1963a51f7c4SDmitry Torokhov 
1973a51f7c4SDmitry Torokhov 		if (copy_from_user(&compat_event, buffer, sizeof(struct input_event_compat)))
19852658bb6SJuergen Kreileder 			return -EFAULT;
1993a51f7c4SDmitry Torokhov 
2003a51f7c4SDmitry Torokhov 		event->time.tv_sec = compat_event.time.tv_sec;
2013a51f7c4SDmitry Torokhov 		event->time.tv_usec = compat_event.time.tv_usec;
2023a51f7c4SDmitry Torokhov 		event->type = compat_event.type;
2033a51f7c4SDmitry Torokhov 		event->code = compat_event.code;
2043a51f7c4SDmitry Torokhov 		event->value = compat_event.value;
2053a51f7c4SDmitry Torokhov 
2063a51f7c4SDmitry Torokhov 	} else {
2073a51f7c4SDmitry Torokhov 		if (copy_from_user(event, buffer, sizeof(struct input_event)))
2083a51f7c4SDmitry Torokhov 			return -EFAULT;
20952658bb6SJuergen Kreileder 	}
21052658bb6SJuergen Kreileder 
2113a51f7c4SDmitry Torokhov 	return 0;
21252658bb6SJuergen Kreileder }
2133a51f7c4SDmitry Torokhov 
2143a51f7c4SDmitry Torokhov static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
2153a51f7c4SDmitry Torokhov {
2163a51f7c4SDmitry Torokhov 	if (COMPAT_TEST) {
2173a51f7c4SDmitry Torokhov 		struct input_event_compat compat_event;
2183a51f7c4SDmitry Torokhov 
2193a51f7c4SDmitry Torokhov 		compat_event.time.tv_sec = event->time.tv_sec;
2203a51f7c4SDmitry Torokhov 		compat_event.time.tv_usec = event->time.tv_usec;
2213a51f7c4SDmitry Torokhov 		compat_event.type = event->type;
2223a51f7c4SDmitry Torokhov 		compat_event.code = event->code;
2233a51f7c4SDmitry Torokhov 		compat_event.value = event->value;
2243a51f7c4SDmitry Torokhov 
2253a51f7c4SDmitry Torokhov 		if (copy_to_user(buffer, &compat_event, sizeof(struct input_event_compat)))
2263a51f7c4SDmitry Torokhov 			return -EFAULT;
2273a51f7c4SDmitry Torokhov 
2283a51f7c4SDmitry Torokhov 	} else {
2293a51f7c4SDmitry Torokhov 		if (copy_to_user(buffer, event, sizeof(struct input_event)))
2303a51f7c4SDmitry Torokhov 			return -EFAULT;
2313a51f7c4SDmitry Torokhov 	}
2323a51f7c4SDmitry Torokhov 
2333a51f7c4SDmitry Torokhov 	return 0;
2343a51f7c4SDmitry Torokhov }
2353a51f7c4SDmitry Torokhov 
2363a51f7c4SDmitry Torokhov #else
2373a51f7c4SDmitry Torokhov 
2383a51f7c4SDmitry Torokhov static inline size_t evdev_event_size(void)
2393a51f7c4SDmitry Torokhov {
2403a51f7c4SDmitry Torokhov 	return sizeof(struct input_event);
2413a51f7c4SDmitry Torokhov }
2423a51f7c4SDmitry Torokhov 
2433a51f7c4SDmitry Torokhov static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
2443a51f7c4SDmitry Torokhov {
2453a51f7c4SDmitry Torokhov 	if (copy_from_user(event, buffer, sizeof(struct input_event)))
2463a51f7c4SDmitry Torokhov 		return -EFAULT;
2473a51f7c4SDmitry Torokhov 
2483a51f7c4SDmitry Torokhov 	return 0;
2493a51f7c4SDmitry Torokhov }
2503a51f7c4SDmitry Torokhov 
2513a51f7c4SDmitry Torokhov static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
2523a51f7c4SDmitry Torokhov {
2533a51f7c4SDmitry Torokhov 	if (copy_to_user(buffer, event, sizeof(struct input_event)))
2543a51f7c4SDmitry Torokhov 		return -EFAULT;
2553a51f7c4SDmitry Torokhov 
2563a51f7c4SDmitry Torokhov 	return 0;
2573a51f7c4SDmitry Torokhov }
2583a51f7c4SDmitry Torokhov 
2593a51f7c4SDmitry Torokhov #endif /* CONFIG_COMPAT */
26052658bb6SJuergen Kreileder 
2611da177e4SLinus Torvalds static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
2621da177e4SLinus Torvalds {
263d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
264d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
2651da177e4SLinus Torvalds 	struct input_event event;
2661da177e4SLinus Torvalds 	int retval = 0;
2671da177e4SLinus Torvalds 
268d0ffb9beSDmitry Torokhov 	if (!evdev->exist)
26952658bb6SJuergen Kreileder 		return -ENODEV;
27052658bb6SJuergen Kreileder 
2713a51f7c4SDmitry Torokhov 	while (retval < count) {
27252658bb6SJuergen Kreileder 
2733a51f7c4SDmitry Torokhov 		if (evdev_event_from_user(buffer + retval, &event))
2743a51f7c4SDmitry Torokhov 			return -EFAULT;
275d0ffb9beSDmitry Torokhov 		input_inject_event(&evdev->handle, event.type, event.code, event.value);
2763a51f7c4SDmitry Torokhov 		retval += evdev_event_size();
27752658bb6SJuergen Kreileder 	}
27852658bb6SJuergen Kreileder 
27952658bb6SJuergen Kreileder 	return retval;
28052658bb6SJuergen Kreileder }
28152658bb6SJuergen Kreileder 
2821da177e4SLinus Torvalds static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
2831da177e4SLinus Torvalds {
284d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
285d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
2861da177e4SLinus Torvalds 	int retval;
2871da177e4SLinus Torvalds 
2883a51f7c4SDmitry Torokhov 	if (count < evdev_event_size())
2891da177e4SLinus Torvalds 		return -EINVAL;
2901da177e4SLinus Torvalds 
291d0ffb9beSDmitry Torokhov 	if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
2921da177e4SLinus Torvalds 		return -EAGAIN;
2931da177e4SLinus Torvalds 
294d0ffb9beSDmitry Torokhov 	retval = wait_event_interruptible(evdev->wait,
295d0ffb9beSDmitry Torokhov 		client->head != client->tail || !evdev->exist);
2961da177e4SLinus Torvalds 	if (retval)
2971da177e4SLinus Torvalds 		return retval;
2981da177e4SLinus Torvalds 
299d0ffb9beSDmitry Torokhov 	if (!evdev->exist)
3001da177e4SLinus Torvalds 		return -ENODEV;
3011da177e4SLinus Torvalds 
302d0ffb9beSDmitry Torokhov 	while (client->head != client->tail && retval + evdev_event_size() <= count) {
3033a51f7c4SDmitry Torokhov 
304d0ffb9beSDmitry Torokhov 		struct input_event *event = (struct input_event *) client->buffer + client->tail;
3053a51f7c4SDmitry Torokhov 
3063a51f7c4SDmitry Torokhov 		if (evdev_event_to_user(buffer + retval, event))
3073a51f7c4SDmitry Torokhov 			return -EFAULT;
3083a51f7c4SDmitry Torokhov 
309d0ffb9beSDmitry Torokhov 		client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
3103a51f7c4SDmitry Torokhov 		retval += evdev_event_size();
3111da177e4SLinus Torvalds 	}
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	return retval;
3141da177e4SLinus Torvalds }
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds /* No kernel lock - fine */
3171da177e4SLinus Torvalds static unsigned int evdev_poll(struct file *file, poll_table *wait)
3181da177e4SLinus Torvalds {
319d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
320d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
3211e0afb28SDmitry Torokhov 
322d0ffb9beSDmitry Torokhov 	poll_wait(file, &evdev->wait, wait);
323d0ffb9beSDmitry Torokhov 	return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) |
324d0ffb9beSDmitry Torokhov 		(evdev->exist ? 0 : (POLLHUP | POLLERR));
3251da177e4SLinus Torvalds }
3261da177e4SLinus Torvalds 
3273a51f7c4SDmitry Torokhov #ifdef CONFIG_COMPAT
3283a51f7c4SDmitry Torokhov 
3293a51f7c4SDmitry Torokhov #define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)
3303a51f7c4SDmitry Torokhov #define NBITS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1)
3313a51f7c4SDmitry Torokhov 
3323a51f7c4SDmitry Torokhov #ifdef __BIG_ENDIAN
3333a51f7c4SDmitry Torokhov static int bits_to_user(unsigned long *bits, unsigned int maxbit,
3343a51f7c4SDmitry Torokhov 			unsigned int maxlen, void __user *p, int compat)
3353a51f7c4SDmitry Torokhov {
3363a51f7c4SDmitry Torokhov 	int len, i;
3373a51f7c4SDmitry Torokhov 
3383a51f7c4SDmitry Torokhov 	if (compat) {
3393a51f7c4SDmitry Torokhov 		len = NBITS_COMPAT(maxbit) * sizeof(compat_long_t);
3403a51f7c4SDmitry Torokhov 		if (len < maxlen)
3413a51f7c4SDmitry Torokhov 			len = maxlen;
3423a51f7c4SDmitry Torokhov 
3433a51f7c4SDmitry Torokhov 		for (i = 0; i < len / sizeof(compat_long_t); i++)
3443a51f7c4SDmitry Torokhov 			if (copy_to_user((compat_long_t __user *) p + i,
3453a51f7c4SDmitry Torokhov 					 (compat_long_t *) bits +
3463a51f7c4SDmitry Torokhov 						i + 1 - ((i % 2) << 1),
3473a51f7c4SDmitry Torokhov 					 sizeof(compat_long_t)))
3483a51f7c4SDmitry Torokhov 				return -EFAULT;
3493a51f7c4SDmitry Torokhov 	} else {
3503a51f7c4SDmitry Torokhov 		len = NBITS(maxbit) * sizeof(long);
3513a51f7c4SDmitry Torokhov 		if (len > maxlen)
3523a51f7c4SDmitry Torokhov 			len = maxlen;
3533a51f7c4SDmitry Torokhov 
3543a51f7c4SDmitry Torokhov 		if (copy_to_user(p, bits, len))
3553a51f7c4SDmitry Torokhov 			return -EFAULT;
3563a51f7c4SDmitry Torokhov 	}
3573a51f7c4SDmitry Torokhov 
3583a51f7c4SDmitry Torokhov 	return len;
3593a51f7c4SDmitry Torokhov }
3603a51f7c4SDmitry Torokhov #else
3613a51f7c4SDmitry Torokhov static int bits_to_user(unsigned long *bits, unsigned int maxbit,
3623a51f7c4SDmitry Torokhov 			unsigned int maxlen, void __user *p, int compat)
3633a51f7c4SDmitry Torokhov {
3643a51f7c4SDmitry Torokhov 	int len = compat ?
3653a51f7c4SDmitry Torokhov 			NBITS_COMPAT(maxbit) * sizeof(compat_long_t) :
3663a51f7c4SDmitry Torokhov 			NBITS(maxbit) * sizeof(long);
3673a51f7c4SDmitry Torokhov 
3683a51f7c4SDmitry Torokhov 	if (len > maxlen)
3693a51f7c4SDmitry Torokhov 		len = maxlen;
3703a51f7c4SDmitry Torokhov 
3713a51f7c4SDmitry Torokhov 	return copy_to_user(p, bits, len) ? -EFAULT : len;
3723a51f7c4SDmitry Torokhov }
3733a51f7c4SDmitry Torokhov #endif /* __BIG_ENDIAN */
3743a51f7c4SDmitry Torokhov 
3753a51f7c4SDmitry Torokhov #else
3763a51f7c4SDmitry Torokhov 
3773a51f7c4SDmitry Torokhov static int bits_to_user(unsigned long *bits, unsigned int maxbit,
3783a51f7c4SDmitry Torokhov 			unsigned int maxlen, void __user *p, int compat)
3793a51f7c4SDmitry Torokhov {
3803a51f7c4SDmitry Torokhov 	int len = NBITS(maxbit) * sizeof(long);
3813a51f7c4SDmitry Torokhov 
3823a51f7c4SDmitry Torokhov 	if (len > maxlen)
3833a51f7c4SDmitry Torokhov 		len = maxlen;
3843a51f7c4SDmitry Torokhov 
3853a51f7c4SDmitry Torokhov 	return copy_to_user(p, bits, len) ? -EFAULT : len;
3863a51f7c4SDmitry Torokhov }
3873a51f7c4SDmitry Torokhov 
3883a51f7c4SDmitry Torokhov #endif /* CONFIG_COMPAT */
3893a51f7c4SDmitry Torokhov 
3903a51f7c4SDmitry Torokhov static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
3913a51f7c4SDmitry Torokhov {
3923a51f7c4SDmitry Torokhov 	int len;
3933a51f7c4SDmitry Torokhov 
3943a51f7c4SDmitry Torokhov 	if (!str)
3953a51f7c4SDmitry Torokhov 		return -ENOENT;
3963a51f7c4SDmitry Torokhov 
3973a51f7c4SDmitry Torokhov 	len = strlen(str) + 1;
3983a51f7c4SDmitry Torokhov 	if (len > maxlen)
3993a51f7c4SDmitry Torokhov 		len = maxlen;
4003a51f7c4SDmitry Torokhov 
4013a51f7c4SDmitry Torokhov 	return copy_to_user(p, str, len) ? -EFAULT : len;
4023a51f7c4SDmitry Torokhov }
4033a51f7c4SDmitry Torokhov 
4043a51f7c4SDmitry Torokhov static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
4053a51f7c4SDmitry Torokhov 				void __user *p, int compat_mode)
4061da177e4SLinus Torvalds {
407d0ffb9beSDmitry Torokhov 	struct evdev_client *client = file->private_data;
408d0ffb9beSDmitry Torokhov 	struct evdev *evdev = client->evdev;
4091da177e4SLinus Torvalds 	struct input_dev *dev = evdev->handle.dev;
4101da177e4SLinus Torvalds 	struct input_absinfo abs;
411509ca1a9SAnssi Hannula 	struct ff_effect effect;
4123a51f7c4SDmitry Torokhov 	int __user *ip = (int __user *)p;
4131da177e4SLinus Torvalds 	int i, t, u, v;
414509ca1a9SAnssi Hannula 	int error;
4151da177e4SLinus Torvalds 
4163a51f7c4SDmitry Torokhov 	if (!evdev->exist)
4173a51f7c4SDmitry Torokhov 		return -ENODEV;
4181da177e4SLinus Torvalds 
4191da177e4SLinus Torvalds 	switch (cmd) {
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds 		case EVIOCGVERSION:
4221da177e4SLinus Torvalds 			return put_user(EV_VERSION, ip);
4231da177e4SLinus Torvalds 
4241da177e4SLinus Torvalds 		case EVIOCGID:
4253a51f7c4SDmitry Torokhov 			if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
4263a51f7c4SDmitry Torokhov 				return -EFAULT;
42708791e5cSDmitry Torokhov 			return 0;
42808791e5cSDmitry Torokhov 
42908791e5cSDmitry Torokhov 		case EVIOCGREP:
43008791e5cSDmitry Torokhov 			if (!test_bit(EV_REP, dev->evbit))
43108791e5cSDmitry Torokhov 				return -ENOSYS;
43208791e5cSDmitry Torokhov 			if (put_user(dev->rep[REP_DELAY], ip))
43308791e5cSDmitry Torokhov 				return -EFAULT;
43408791e5cSDmitry Torokhov 			if (put_user(dev->rep[REP_PERIOD], ip + 1))
43508791e5cSDmitry Torokhov 				return -EFAULT;
43608791e5cSDmitry Torokhov 			return 0;
43708791e5cSDmitry Torokhov 
43808791e5cSDmitry Torokhov 		case EVIOCSREP:
43908791e5cSDmitry Torokhov 			if (!test_bit(EV_REP, dev->evbit))
44008791e5cSDmitry Torokhov 				return -ENOSYS;
44108791e5cSDmitry Torokhov 			if (get_user(u, ip))
44208791e5cSDmitry Torokhov 				return -EFAULT;
44308791e5cSDmitry Torokhov 			if (get_user(v, ip + 1))
44408791e5cSDmitry Torokhov 				return -EFAULT;
44508791e5cSDmitry Torokhov 
4460e739d28SDmitry Torokhov 			input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
4470e739d28SDmitry Torokhov 			input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
4483a51f7c4SDmitry Torokhov 
4493a51f7c4SDmitry Torokhov 			return 0;
4501da177e4SLinus Torvalds 
4511da177e4SLinus Torvalds 		case EVIOCGKEYCODE:
4523a51f7c4SDmitry Torokhov 			if (get_user(t, ip))
4533a51f7c4SDmitry Torokhov 				return -EFAULT;
454c8e4c772SMarvin Raaijmakers 
455c8e4c772SMarvin Raaijmakers 			error = dev->getkeycode(dev, t, &v);
456c8e4c772SMarvin Raaijmakers 			if (error)
457c8e4c772SMarvin Raaijmakers 				return error;
458c8e4c772SMarvin Raaijmakers 
459c8e4c772SMarvin Raaijmakers 			if (put_user(v, ip + 1))
4603a51f7c4SDmitry Torokhov 				return -EFAULT;
461c8e4c772SMarvin Raaijmakers 
4621da177e4SLinus Torvalds 			return 0;
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 		case EVIOCSKEYCODE:
465c8e4c772SMarvin Raaijmakers 			if (get_user(t, ip) || get_user(v, ip + 1))
4663a51f7c4SDmitry Torokhov 				return -EFAULT;
4673a51f7c4SDmitry Torokhov 
468c8e4c772SMarvin Raaijmakers 			return dev->setkeycode(dev, t, v);
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds 		case EVIOCSFF:
4711da177e4SLinus Torvalds 			if (copy_from_user(&effect, p, sizeof(effect)))
4721da177e4SLinus Torvalds 				return -EFAULT;
473509ca1a9SAnssi Hannula 
474509ca1a9SAnssi Hannula 			error = input_ff_upload(dev, &effect, file);
475509ca1a9SAnssi Hannula 
4763a51f7c4SDmitry Torokhov 			if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
4771da177e4SLinus Torvalds 				return -EFAULT;
478509ca1a9SAnssi Hannula 
479509ca1a9SAnssi Hannula 			return error;
4801da177e4SLinus Torvalds 
4811da177e4SLinus Torvalds 		case EVIOCRMFF:
482509ca1a9SAnssi Hannula 			return input_ff_erase(dev, (int)(unsigned long) p, file);
4831da177e4SLinus Torvalds 
4841da177e4SLinus Torvalds 		case EVIOCGEFFECTS:
485509ca1a9SAnssi Hannula 			i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0;
486509ca1a9SAnssi Hannula 			if (put_user(i, ip))
4871da177e4SLinus Torvalds 				return -EFAULT;
4881da177e4SLinus Torvalds 			return 0;
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 		case EVIOCGRAB:
4913a51f7c4SDmitry Torokhov 			if (p) {
4921da177e4SLinus Torvalds 				if (evdev->grab)
4931da177e4SLinus Torvalds 					return -EBUSY;
4941da177e4SLinus Torvalds 				if (input_grab_device(&evdev->handle))
4951da177e4SLinus Torvalds 					return -EBUSY;
496d0ffb9beSDmitry Torokhov 				evdev->grab = client;
4971da177e4SLinus Torvalds 				return 0;
4981da177e4SLinus Torvalds 			} else {
499d0ffb9beSDmitry Torokhov 				if (evdev->grab != client)
5001da177e4SLinus Torvalds 					return -EINVAL;
5011da177e4SLinus Torvalds 				input_release_device(&evdev->handle);
5021da177e4SLinus Torvalds 				evdev->grab = NULL;
5031da177e4SLinus Torvalds 				return 0;
5041da177e4SLinus Torvalds 			}
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds 		default:
5071da177e4SLinus Torvalds 
50841e979f8SVojtech Pavlik 			if (_IOC_TYPE(cmd) != 'E')
5091da177e4SLinus Torvalds 				return -EINVAL;
5101da177e4SLinus Torvalds 
51141e979f8SVojtech Pavlik 			if (_IOC_DIR(cmd) == _IOC_READ) {
51241e979f8SVojtech Pavlik 
5131da177e4SLinus Torvalds 				if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
5141da177e4SLinus Torvalds 
515ce305b6aSDmitry Torokhov 					unsigned long *bits;
5161da177e4SLinus Torvalds 					int len;
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds 					switch (_IOC_NR(cmd) & EV_MAX) {
5191da177e4SLinus Torvalds 						case      0: bits = dev->evbit;  len = EV_MAX;  break;
5201da177e4SLinus Torvalds 						case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
5211da177e4SLinus Torvalds 						case EV_REL: bits = dev->relbit; len = REL_MAX; break;
5221da177e4SLinus Torvalds 						case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
5231da177e4SLinus Torvalds 						case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
5241da177e4SLinus Torvalds 						case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
5251da177e4SLinus Torvalds 						case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
5261da177e4SLinus Torvalds 						case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  break;
52731581066SRichard Purdie 						case EV_SW:  bits = dev->swbit;  len = SW_MAX;  break;
5281da177e4SLinus Torvalds 						default: return -EINVAL;
5291da177e4SLinus Torvalds 					}
5303a51f7c4SDmitry Torokhov 					return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
53152658bb6SJuergen Kreileder 				}
53252658bb6SJuergen Kreileder 
53341e979f8SVojtech Pavlik 				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
5343a51f7c4SDmitry Torokhov 					return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
5353a51f7c4SDmitry Torokhov 							    p, compat_mode);
53652658bb6SJuergen Kreileder 
53741e979f8SVojtech Pavlik 				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
5383a51f7c4SDmitry Torokhov 					return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
5393a51f7c4SDmitry Torokhov 							    p, compat_mode);
54052658bb6SJuergen Kreileder 
54141e979f8SVojtech Pavlik 				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
5423a51f7c4SDmitry Torokhov 					return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
5433a51f7c4SDmitry Torokhov 							    p, compat_mode);
54452658bb6SJuergen Kreileder 
5457972720aSDmitry Torokhov 				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
5463a51f7c4SDmitry Torokhov 					return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
5473a51f7c4SDmitry Torokhov 							    p, compat_mode);
5487972720aSDmitry Torokhov 
5493a51f7c4SDmitry Torokhov 				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
5503a51f7c4SDmitry Torokhov 					return str_to_user(dev->name, _IOC_SIZE(cmd), p);
55152658bb6SJuergen Kreileder 
5523a51f7c4SDmitry Torokhov 				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
5533a51f7c4SDmitry Torokhov 					return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
55452658bb6SJuergen Kreileder 
5553a51f7c4SDmitry Torokhov 				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
5563a51f7c4SDmitry Torokhov 					return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
55752658bb6SJuergen Kreileder 
55852658bb6SJuergen Kreileder 				if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
55952658bb6SJuergen Kreileder 
560ce305b6aSDmitry Torokhov 					t = _IOC_NR(cmd) & ABS_MAX;
56152658bb6SJuergen Kreileder 
56252658bb6SJuergen Kreileder 					abs.value = dev->abs[t];
56352658bb6SJuergen Kreileder 					abs.minimum = dev->absmin[t];
56452658bb6SJuergen Kreileder 					abs.maximum = dev->absmax[t];
56552658bb6SJuergen Kreileder 					abs.fuzz = dev->absfuzz[t];
56652658bb6SJuergen Kreileder 					abs.flat = dev->absflat[t];
56752658bb6SJuergen Kreileder 
56852658bb6SJuergen Kreileder 					if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
56952658bb6SJuergen Kreileder 						return -EFAULT;
57052658bb6SJuergen Kreileder 
57152658bb6SJuergen Kreileder 					return 0;
57252658bb6SJuergen Kreileder 				}
5733a51f7c4SDmitry Torokhov 
57441e979f8SVojtech Pavlik 			}
57541e979f8SVojtech Pavlik 
57641e979f8SVojtech Pavlik 			if (_IOC_DIR(cmd) == _IOC_WRITE) {
57752658bb6SJuergen Kreileder 
57852658bb6SJuergen Kreileder 				if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
57952658bb6SJuergen Kreileder 
580ce305b6aSDmitry Torokhov 					t = _IOC_NR(cmd) & ABS_MAX;
58152658bb6SJuergen Kreileder 
58252658bb6SJuergen Kreileder 					if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
58352658bb6SJuergen Kreileder 						return -EFAULT;
58452658bb6SJuergen Kreileder 
58552658bb6SJuergen Kreileder 					dev->abs[t] = abs.value;
58652658bb6SJuergen Kreileder 					dev->absmin[t] = abs.minimum;
58752658bb6SJuergen Kreileder 					dev->absmax[t] = abs.maximum;
58852658bb6SJuergen Kreileder 					dev->absfuzz[t] = abs.fuzz;
58952658bb6SJuergen Kreileder 					dev->absflat[t] = abs.flat;
59052658bb6SJuergen Kreileder 
59152658bb6SJuergen Kreileder 					return 0;
59252658bb6SJuergen Kreileder 				}
59352658bb6SJuergen Kreileder 			}
59441e979f8SVojtech Pavlik 	}
59552658bb6SJuergen Kreileder 	return -EINVAL;
59652658bb6SJuergen Kreileder }
5973a51f7c4SDmitry Torokhov 
5983a51f7c4SDmitry Torokhov static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
5993a51f7c4SDmitry Torokhov {
6003a51f7c4SDmitry Torokhov 	return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
6013a51f7c4SDmitry Torokhov }
6023a51f7c4SDmitry Torokhov 
6033a51f7c4SDmitry Torokhov #ifdef CONFIG_COMPAT
6043a51f7c4SDmitry Torokhov static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
6053a51f7c4SDmitry Torokhov {
6063a51f7c4SDmitry Torokhov 	return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
6073a51f7c4SDmitry Torokhov }
60852658bb6SJuergen Kreileder #endif
60952658bb6SJuergen Kreileder 
61066e66118SDmitry Torokhov static const struct file_operations evdev_fops = {
6111da177e4SLinus Torvalds 	.owner =	THIS_MODULE,
6121da177e4SLinus Torvalds 	.read =		evdev_read,
6131da177e4SLinus Torvalds 	.write =	evdev_write,
6141da177e4SLinus Torvalds 	.poll =		evdev_poll,
6151da177e4SLinus Torvalds 	.open =		evdev_open,
6161da177e4SLinus Torvalds 	.release =	evdev_release,
61752658bb6SJuergen Kreileder 	.unlocked_ioctl = evdev_ioctl,
61852658bb6SJuergen Kreileder #ifdef CONFIG_COMPAT
61952658bb6SJuergen Kreileder 	.compat_ioctl =	evdev_ioctl_compat,
62052658bb6SJuergen Kreileder #endif
6211da177e4SLinus Torvalds 	.fasync =	evdev_fasync,
6221da177e4SLinus Torvalds 	.flush =	evdev_flush
6231da177e4SLinus Torvalds };
6241da177e4SLinus Torvalds 
6255b2a0826SDmitry Torokhov static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
62666e66118SDmitry Torokhov 			 const struct input_device_id *id)
6271da177e4SLinus Torvalds {
6281da177e4SLinus Torvalds 	struct evdev *evdev;
629c9bcd582SGreg Kroah-Hartman 	struct class_device *cdev;
6305b2a0826SDmitry Torokhov 	dev_t devt;
6311da177e4SLinus Torvalds 	int minor;
6325b2a0826SDmitry Torokhov 	int error;
6331da177e4SLinus Torvalds 
6341da177e4SLinus Torvalds 	for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
6351da177e4SLinus Torvalds 	if (minor == EVDEV_MINORS) {
6361da177e4SLinus Torvalds 		printk(KERN_ERR "evdev: no more free evdev devices\n");
6375b2a0826SDmitry Torokhov 		return -ENFILE;
6381da177e4SLinus Torvalds 	}
6391da177e4SLinus Torvalds 
6405b2a0826SDmitry Torokhov 	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
6415b2a0826SDmitry Torokhov 	if (!evdev)
6425b2a0826SDmitry Torokhov 		return -ENOMEM;
6431da177e4SLinus Torvalds 
644d0ffb9beSDmitry Torokhov 	INIT_LIST_HEAD(&evdev->client_list);
6451da177e4SLinus Torvalds 	init_waitqueue_head(&evdev->wait);
6461da177e4SLinus Torvalds 
6471da177e4SLinus Torvalds 	evdev->exist = 1;
6481da177e4SLinus Torvalds 	evdev->minor = minor;
6491da177e4SLinus Torvalds 	evdev->handle.dev = dev;
6501da177e4SLinus Torvalds 	evdev->handle.name = evdev->name;
6511da177e4SLinus Torvalds 	evdev->handle.handler = handler;
6521da177e4SLinus Torvalds 	evdev->handle.private = evdev;
6531da177e4SLinus Torvalds 	sprintf(evdev->name, "event%d", minor);
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds 	evdev_table[minor] = evdev;
6561da177e4SLinus Torvalds 
6575b2a0826SDmitry Torokhov 	devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
6585b2a0826SDmitry Torokhov 
6595b2a0826SDmitry Torokhov 	cdev = class_device_create(&input_class, &dev->cdev, devt,
660c9bcd582SGreg Kroah-Hartman 				   dev->cdev.dev, evdev->name);
6615b2a0826SDmitry Torokhov 	if (IS_ERR(cdev)) {
6625b2a0826SDmitry Torokhov 		error = PTR_ERR(cdev);
6635b2a0826SDmitry Torokhov 		goto err_free_evdev;
6645b2a0826SDmitry Torokhov 	}
665c9bcd582SGreg Kroah-Hartman 
666c9bcd582SGreg Kroah-Hartman 	/* temporary symlink to keep userspace happy */
667a3d52136SLinus Torvalds 	error = sysfs_create_link(&input_class.subsys.kobj,
6685b2a0826SDmitry Torokhov 				  &cdev->kobj, evdev->name);
6695b2a0826SDmitry Torokhov 	if (error)
6705b2a0826SDmitry Torokhov 		goto err_cdev_destroy;
6711da177e4SLinus Torvalds 
6725b2a0826SDmitry Torokhov 	error = input_register_handle(&evdev->handle);
6735b2a0826SDmitry Torokhov 	if (error)
6745b2a0826SDmitry Torokhov 		goto err_remove_link;
6755b2a0826SDmitry Torokhov 
6765b2a0826SDmitry Torokhov 	return 0;
6775b2a0826SDmitry Torokhov 
6785b2a0826SDmitry Torokhov  err_remove_link:
679a3d52136SLinus Torvalds 	sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
6805b2a0826SDmitry Torokhov  err_cdev_destroy:
6815b2a0826SDmitry Torokhov 	class_device_destroy(&input_class, devt);
6825b2a0826SDmitry Torokhov  err_free_evdev:
6835b2a0826SDmitry Torokhov 	kfree(evdev);
6845b2a0826SDmitry Torokhov 	evdev_table[minor] = NULL;
6855b2a0826SDmitry Torokhov 	return error;
6861da177e4SLinus Torvalds }
6871da177e4SLinus Torvalds 
6881da177e4SLinus Torvalds static void evdev_disconnect(struct input_handle *handle)
6891da177e4SLinus Torvalds {
6901da177e4SLinus Torvalds 	struct evdev *evdev = handle->private;
691d0ffb9beSDmitry Torokhov 	struct evdev_client *client;
6921da177e4SLinus Torvalds 
6935b2a0826SDmitry Torokhov 	input_unregister_handle(handle);
6945b2a0826SDmitry Torokhov 
695823bccfcSGreg Kroah-Hartman 	sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
696ea9f240bSGreg Kroah-Hartman 	class_device_destroy(&input_class,
6971235686fS[email protected] 			MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + evdev->minor));
6981da177e4SLinus Torvalds 	evdev->exist = 0;
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds 	if (evdev->open) {
701509ca1a9SAnssi Hannula 		input_flush_device(handle, NULL);
7021da177e4SLinus Torvalds 		input_close_device(handle);
703d0ffb9beSDmitry Torokhov 		list_for_each_entry(client, &evdev->client_list, node)
704d0ffb9beSDmitry Torokhov 			kill_fasync(&client->fasync, SIGIO, POLL_HUP);
705*1dfa2812SDmitry Torokhov 		wake_up_interruptible(&evdev->wait);
7061da177e4SLinus Torvalds 	} else
7071da177e4SLinus Torvalds 		evdev_free(evdev);
7081da177e4SLinus Torvalds }
7091da177e4SLinus Torvalds 
71066e66118SDmitry Torokhov static const struct input_device_id evdev_ids[] = {
7111da177e4SLinus Torvalds 	{ .driver_info = 1 },	/* Matches all devices */
7121da177e4SLinus Torvalds 	{ },			/* Terminating zero entry */
7131da177e4SLinus Torvalds };
7141da177e4SLinus Torvalds 
7151da177e4SLinus Torvalds MODULE_DEVICE_TABLE(input, evdev_ids);
7161da177e4SLinus Torvalds 
7171da177e4SLinus Torvalds static struct input_handler evdev_handler = {
7181da177e4SLinus Torvalds 	.event =	evdev_event,
7191da177e4SLinus Torvalds 	.connect =	evdev_connect,
7201da177e4SLinus Torvalds 	.disconnect =	evdev_disconnect,
7211da177e4SLinus Torvalds 	.fops =		&evdev_fops,
7221da177e4SLinus Torvalds 	.minor =	EVDEV_MINOR_BASE,
7231da177e4SLinus Torvalds 	.name =		"evdev",
7241da177e4SLinus Torvalds 	.id_table =	evdev_ids,
7251da177e4SLinus Torvalds };
7261da177e4SLinus Torvalds 
7271da177e4SLinus Torvalds static int __init evdev_init(void)
7281da177e4SLinus Torvalds {
7294263cf0fSDmitry Torokhov 	return input_register_handler(&evdev_handler);
7301da177e4SLinus Torvalds }
7311da177e4SLinus Torvalds 
7321da177e4SLinus Torvalds static void __exit evdev_exit(void)
7331da177e4SLinus Torvalds {
7341da177e4SLinus Torvalds 	input_unregister_handler(&evdev_handler);
7351da177e4SLinus Torvalds }
7361da177e4SLinus Torvalds 
7371da177e4SLinus Torvalds module_init(evdev_init);
7381da177e4SLinus Torvalds module_exit(evdev_exit);
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds MODULE_AUTHOR("Vojtech Pavlik <[email protected]>");
7411da177e4SLinus Torvalds MODULE_DESCRIPTION("Input driver event char devices");
7421da177e4SLinus Torvalds MODULE_LICENSE("GPL");
743