Blame view

drivers/input/evdev.c 22.6 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
  /*
   * Event char devices, giving access to raw input device events.
   *
   * Copyright (c) 1999-2002 Vojtech Pavlik
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published by
   * the Free Software Foundation.
   */
da0c49011   Joe Perches   Input: use pr_fmt...
10
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
  #define EVDEV_MINOR_BASE	64
  #define EVDEV_MINORS		32
63a6404d8   Henrik Rydberg   Input: evdev - us...
13
14
  #define EVDEV_MIN_BUFFER_SIZE	64U
  #define EVDEV_BUF_PACKETS	8
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
  
  #include <linux/poll.h>
a99bbaf5e   Alexey Dobriyan   headers: remove s...
17
  #include <linux/sched.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
22
  #include <linux/slab.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/input.h>
  #include <linux/major.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
  #include <linux/device.h>
2d56f3a32   Philip Langdale   Input: refactor e...
24
  #include "input-compat.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
  
  struct evdev {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
  	int open;
  	int minor;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
  	struct input_handle handle;
  	wait_queue_head_t wait;
2be852792   Arnd Bergmann   input: __rcu anno...
31
  	struct evdev_client __rcu *grab;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
32
  	struct list_head client_list;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
33
34
  	spinlock_t client_lock; /* protects client_list */
  	struct mutex mutex;
9657d75c5   Dmitry Torokhov   Input: convert fr...
35
  	struct device dev;
20da92de8   Dmitry Torokhov   Input: change inp...
36
  	bool exist;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
  };
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
38
  struct evdev_client {
9fb0f14e3   Jeff Brown   Input: evdev - in...
39
40
  	unsigned int head;
  	unsigned int tail;
cdda911c3   Jeff Brown   Input: evdev - on...
41
  	unsigned int packet_head; /* [future] position of the first element of next packet */
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
42
  	spinlock_t buffer_lock; /* protects access to buffer, head and tail */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43
44
45
  	struct fasync_struct *fasync;
  	struct evdev *evdev;
  	struct list_head node;
9fb0f14e3   Jeff Brown   Input: evdev - in...
46
  	unsigned int bufsize;
b58f7086d   Henrik Rydberg   Input: evdev - co...
47
  	struct input_event buffer[];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
50
  };
  
  static struct evdev *evdev_table[EVDEV_MINORS];
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
51
  static DEFINE_MUTEX(evdev_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
53
54
55
  static void evdev_pass_event(struct evdev_client *client,
  			     struct input_event *event)
  {
9fb0f14e3   Jeff Brown   Input: evdev - in...
56
  	/* Interrupts are disabled, just acquire the lock. */
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
57
  	spin_lock(&client->buffer_lock);
9fb0f14e3   Jeff Brown   Input: evdev - in...
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  
  	client->buffer[client->head++] = *event;
  	client->head &= client->bufsize - 1;
  
  	if (unlikely(client->head == client->tail)) {
  		/*
  		 * This effectively "drops" all unconsumed events, leaving
  		 * EV_SYN/SYN_DROPPED plus the newest event in the queue.
  		 */
  		client->tail = (client->head - 2) & (client->bufsize - 1);
  
  		client->buffer[client->tail].time = event->time;
  		client->buffer[client->tail].type = EV_SYN;
  		client->buffer[client->tail].code = SYN_DROPPED;
  		client->buffer[client->tail].value = 0;
9fb0f14e3   Jeff Brown   Input: evdev - in...
73

cdda911c3   Jeff Brown   Input: evdev - on...
74
75
  		client->packet_head = client->tail;
  	}
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
76

cdda911c3   Jeff Brown   Input: evdev - on...
77
78
  	if (event->type == EV_SYN && event->code == SYN_REPORT) {
  		client->packet_head = client->head;
30a589fde   Adam Jackson   Input: evdev - be...
79
  		kill_fasync(&client->fasync, SIGIO, POLL_IN);
cdda911c3   Jeff Brown   Input: evdev - on...
80
81
82
  	}
  
  	spin_unlock(&client->buffer_lock);
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
83
84
85
  }
  
  /*
82ba56c27   Dmitry Torokhov   Input: use full R...
86
   * Pass incoming event to all connected clients.
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
87
88
89
   */
  static void evdev_event(struct input_handle *handle,
  			unsigned int type, unsigned int code, int value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
  {
  	struct evdev *evdev = handle->private;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
92
  	struct evdev_client *client;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
93
  	struct input_event event;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
95
96
97
98
  	do_gettimeofday(&event.time);
  	event.type = type;
  	event.code = code;
  	event.value = value;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99

82ba56c27   Dmitry Torokhov   Input: use full R...
100
  	rcu_read_lock();
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
101
102
103
104
105
106
  	client = rcu_dereference(evdev->grab);
  	if (client)
  		evdev_pass_event(client, &event);
  	else
  		list_for_each_entry_rcu(client, &evdev->client_list, node)
  			evdev_pass_event(client, &event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107

82ba56c27   Dmitry Torokhov   Input: use full R...
108
  	rcu_read_unlock();
da40b0b6b   Dmitry Torokhov   Input: evdev - tr...
109
110
  	if (type == EV_SYN && code == SYN_REPORT)
  		wake_up_interruptible(&evdev->wait);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
114
  }
  
  static int evdev_fasync(int fd, struct file *file, int on)
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
115
  	struct evdev_client *client = file->private_data;
1e0afb288   Dmitry Torokhov   Input: fix format...
116

60aa49243   Jonathan Corbet   Rationalize fasyn...
117
  	return fasync_helper(fd, file, on, &client->fasync);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
  }
1e0afb288   Dmitry Torokhov   Input: fix format...
119
  static int evdev_flush(struct file *file, fl_owner_t id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
121
122
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
123
124
125
126
127
  	int retval;
  
  	retval = mutex_lock_interruptible(&evdev->mutex);
  	if (retval)
  		return retval;
1e0afb288   Dmitry Torokhov   Input: fix format...
128

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
129
  	if (!evdev->exist)
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
130
131
132
  		retval = -ENODEV;
  	else
  		retval = input_flush_device(&evdev->handle, file);
1e0afb288   Dmitry Torokhov   Input: fix format...
133

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
134
135
  	mutex_unlock(&evdev->mutex);
  	return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  }
9657d75c5   Dmitry Torokhov   Input: convert fr...
137
  static void evdev_free(struct device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  {
9657d75c5   Dmitry Torokhov   Input: convert fr...
139
  	struct evdev *evdev = container_of(dev, struct evdev, dev);
a7097ff89   Dmitry Torokhov   Input: make sure ...
140
  	input_put_device(evdev->handle.dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
  	kfree(evdev);
  }
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  /*
   * Grabs an event device (along with underlying input device).
   * This function is called with evdev->mutex taken.
   */
  static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
  {
  	int error;
  
  	if (evdev->grab)
  		return -EBUSY;
  
  	error = input_grab_device(&evdev->handle);
  	if (error)
  		return error;
  
  	rcu_assign_pointer(evdev->grab, client);
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
159
160
161
162
163
164
165
166
167
168
  
  	return 0;
  }
  
  static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
  {
  	if (evdev->grab != client)
  		return  -EINVAL;
  
  	rcu_assign_pointer(evdev->grab, NULL);
82ba56c27   Dmitry Torokhov   Input: use full R...
169
  	synchronize_rcu();
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
170
171
172
173
174
175
176
177
178
179
180
  	input_release_device(&evdev->handle);
  
  	return 0;
  }
  
  static void evdev_attach_client(struct evdev *evdev,
  				struct evdev_client *client)
  {
  	spin_lock(&evdev->client_lock);
  	list_add_tail_rcu(&client->node, &evdev->client_list);
  	spin_unlock(&evdev->client_lock);
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
181
182
183
184
185
186
187
188
  }
  
  static void evdev_detach_client(struct evdev *evdev,
  				struct evdev_client *client)
  {
  	spin_lock(&evdev->client_lock);
  	list_del_rcu(&client->node);
  	spin_unlock(&evdev->client_lock);
82ba56c27   Dmitry Torokhov   Input: use full R...
189
  	synchronize_rcu();
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
190
191
192
193
194
195
196
197
198
199
200
201
  }
  
  static int evdev_open_device(struct evdev *evdev)
  {
  	int retval;
  
  	retval = mutex_lock_interruptible(&evdev->mutex);
  	if (retval)
  		return retval;
  
  	if (!evdev->exist)
  		retval = -ENODEV;
064450140   Oliver Neukum   Input: fix open c...
202
  	else if (!evdev->open++) {
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
203
  		retval = input_open_device(&evdev->handle);
064450140   Oliver Neukum   Input: fix open c...
204
205
206
  		if (retval)
  			evdev->open--;
  	}
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  
  	mutex_unlock(&evdev->mutex);
  	return retval;
  }
  
  static void evdev_close_device(struct evdev *evdev)
  {
  	mutex_lock(&evdev->mutex);
  
  	if (evdev->exist && !--evdev->open)
  		input_close_device(&evdev->handle);
  
  	mutex_unlock(&evdev->mutex);
  }
  
  /*
   * Wake up users waiting for IO so they can disconnect from
   * dead device.
   */
  static void evdev_hangup(struct evdev *evdev)
  {
  	struct evdev_client *client;
  
  	spin_lock(&evdev->client_lock);
  	list_for_each_entry(client, &evdev->client_list, node)
  		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
  	spin_unlock(&evdev->client_lock);
  
  	wake_up_interruptible(&evdev->wait);
  }
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
237
  static int evdev_release(struct inode *inode, struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
239
240
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
242
243
244
245
  	mutex_lock(&evdev->mutex);
  	if (evdev->grab == client)
  		evdev_ungrab(evdev, client);
  	mutex_unlock(&evdev->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
247
  	evdev_detach_client(evdev, client);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
248
  	kfree(client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
250
  	evdev_close_device(evdev);
9657d75c5   Dmitry Torokhov   Input: convert fr...
251
  	put_device(&evdev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
254
  	return 0;
  }
b58f7086d   Henrik Rydberg   Input: evdev - co...
255
256
  static unsigned int evdev_compute_buffer_size(struct input_dev *dev)
  {
63a6404d8   Henrik Rydberg   Input: evdev - us...
257
258
259
260
261
  	unsigned int n_events =
  		max(dev->hint_events_per_packet * EVDEV_BUF_PACKETS,
  		    EVDEV_MIN_BUFFER_SIZE);
  
  	return roundup_pow_of_two(n_events);
b58f7086d   Henrik Rydberg   Input: evdev - co...
262
  }
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
263
  static int evdev_open(struct inode *inode, struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
265
  	struct evdev *evdev;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
266
  	struct evdev_client *client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
  	int i = iminor(inode) - EVDEV_MINOR_BASE;
b58f7086d   Henrik Rydberg   Input: evdev - co...
268
  	unsigned int bufsize;
d542ed82f   Dmitry Torokhov   Input: handlers -...
269
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
271
  	if (i >= EVDEV_MINORS)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
  		return -ENODEV;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
273
274
275
  	error = mutex_lock_interruptible(&evdev_table_mutex);
  	if (error)
  		return error;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
276
  	evdev = evdev_table[i];
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
277
278
279
  	if (evdev)
  		get_device(&evdev->dev);
  	mutex_unlock(&evdev_table_mutex);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
280

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
281
  	if (!evdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
  		return -ENODEV;
b58f7086d   Henrik Rydberg   Input: evdev - co...
283
284
285
286
287
  	bufsize = evdev_compute_buffer_size(evdev->handle.dev);
  
  	client = kzalloc(sizeof(struct evdev_client) +
  				bufsize * sizeof(struct input_event),
  			 GFP_KERNEL);
9657d75c5   Dmitry Torokhov   Input: convert fr...
288
289
290
291
  	if (!client) {
  		error = -ENOMEM;
  		goto err_put_evdev;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292

b58f7086d   Henrik Rydberg   Input: evdev - co...
293
  	client->bufsize = bufsize;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
294
  	spin_lock_init(&client->buffer_lock);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
295
  	client->evdev = evdev;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
296
  	evdev_attach_client(evdev, client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
298
299
300
  	error = evdev_open_device(evdev);
  	if (error)
  		goto err_free_client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
302
  	file->private_data = client;
3d7bbd457   Dmitry Torokhov   Input: mark input...
303
  	nonseekable_open(inode, file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  	return 0;
9657d75c5   Dmitry Torokhov   Input: convert fr...
305
306
  
   err_free_client:
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
307
  	evdev_detach_client(evdev, client);
9657d75c5   Dmitry Torokhov   Input: convert fr...
308
309
310
311
  	kfree(client);
   err_put_evdev:
  	put_device(&evdev->dev);
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
  }
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
313
314
  static ssize_t evdev_write(struct file *file, const char __user *buffer,
  			   size_t count, loff_t *ppos)
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
315
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
316
317
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
318
  	struct input_event event;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
319
  	int retval;
52658bb68   Juergen Kreileder   Input: Add suppor...
320

439581ec0   Peter Korsgaard   Input: evdev - fi...
321
322
  	if (count < input_event_size())
  		return -EINVAL;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
323
324
325
326
327
328
329
330
  	retval = mutex_lock_interruptible(&evdev->mutex);
  	if (retval)
  		return retval;
  
  	if (!evdev->exist) {
  		retval = -ENODEV;
  		goto out;
  	}
52658bb68   Juergen Kreileder   Input: Add suppor...
331

439581ec0   Peter Korsgaard   Input: evdev - fi...
332
  	do {
2d56f3a32   Philip Langdale   Input: refactor e...
333
  		if (input_event_from_user(buffer + retval, &event)) {
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
334
335
336
  			retval = -EFAULT;
  			goto out;
  		}
439581ec0   Peter Korsgaard   Input: evdev - fi...
337
  		retval += input_event_size();
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
338
339
340
  
  		input_inject_event(&evdev->handle,
  				   event.type, event.code, event.value);
439581ec0   Peter Korsgaard   Input: evdev - fi...
341
  	} while (retval + input_event_size() <= count);
52658bb68   Juergen Kreileder   Input: Add suppor...
342

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
343
344
   out:
  	mutex_unlock(&evdev->mutex);
52658bb68   Juergen Kreileder   Input: Add suppor...
345
346
  	return retval;
  }
52658bb68   Juergen Kreileder   Input: Add suppor...
347

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
348
349
350
351
352
353
354
355
356
357
  static int evdev_fetch_next_event(struct evdev_client *client,
  				  struct input_event *event)
  {
  	int have_event;
  
  	spin_lock_irq(&client->buffer_lock);
  
  	have_event = client->head != client->tail;
  	if (have_event) {
  		*event = client->buffer[client->tail++];
b58f7086d   Henrik Rydberg   Input: evdev - co...
358
  		client->tail &= client->bufsize - 1;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
359
360
361
362
363
364
365
366
367
  	}
  
  	spin_unlock_irq(&client->buffer_lock);
  
  	return have_event;
  }
  
  static ssize_t evdev_read(struct file *file, char __user *buffer,
  			  size_t count, loff_t *ppos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
369
370
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
371
  	struct input_event event;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
  	int retval;
2d56f3a32   Philip Langdale   Input: refactor e...
373
  	if (count < input_event_size())
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
  		return -EINVAL;
cdda911c3   Jeff Brown   Input: evdev - on...
375
  	if (client->packet_head == client->tail && evdev->exist &&
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
376
  	    (file->f_flags & O_NONBLOCK))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
  		return -EAGAIN;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
378
  	retval = wait_event_interruptible(evdev->wait,
cdda911c3   Jeff Brown   Input: evdev - on...
379
  		client->packet_head != client->tail || !evdev->exist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
  	if (retval)
  		return retval;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
382
  	if (!evdev->exist)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383
  		return -ENODEV;
2d56f3a32   Philip Langdale   Input: refactor e...
384
  	while (retval + input_event_size() <= count &&
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
385
  	       evdev_fetch_next_event(client, &event)) {
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
386

2d56f3a32   Philip Langdale   Input: refactor e...
387
  		if (input_event_to_user(buffer + retval, &event))
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
388
  			return -EFAULT;
2d56f3a32   Philip Langdale   Input: refactor e...
389
  		retval += input_event_size();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390
391
392
393
394
395
396
397
  	}
  
  	return retval;
  }
  
  /* No kernel lock - fine */
  static unsigned int evdev_poll(struct file *file, poll_table *wait)
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
398
399
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
c18fb1396   Dmitry Torokhov   Input: evdev - si...
400
  	unsigned int mask;
1e0afb288   Dmitry Torokhov   Input: fix format...
401

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
402
  	poll_wait(file, &evdev->wait, wait);
c18fb1396   Dmitry Torokhov   Input: evdev - si...
403
404
  
  	mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;
cdda911c3   Jeff Brown   Input: evdev - on...
405
  	if (client->packet_head != client->tail)
c18fb1396   Dmitry Torokhov   Input: evdev - si...
406
407
408
  		mask |= POLLIN | POLLRDNORM;
  
  	return mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
  }
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
410
411
412
  #ifdef CONFIG_COMPAT
  
  #define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)
7b19ada2e   Jiri Slaby   get rid of input ...
413
  #define BITS_TO_LONGS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1)
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
414
415
416
417
418
419
420
421
  
  #ifdef __BIG_ENDIAN
  static int bits_to_user(unsigned long *bits, unsigned int maxbit,
  			unsigned int maxlen, void __user *p, int compat)
  {
  	int len, i;
  
  	if (compat) {
7b19ada2e   Jiri Slaby   get rid of input ...
422
  		len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t);
bf61f8d35   Kenichi Nagai   Input: evdev - fi...
423
  		if (len > maxlen)
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
424
425
426
427
428
429
430
431
432
  			len = maxlen;
  
  		for (i = 0; i < len / sizeof(compat_long_t); i++)
  			if (copy_to_user((compat_long_t __user *) p + i,
  					 (compat_long_t *) bits +
  						i + 1 - ((i % 2) << 1),
  					 sizeof(compat_long_t)))
  				return -EFAULT;
  	} else {
7b19ada2e   Jiri Slaby   get rid of input ...
433
  		len = BITS_TO_LONGS(maxbit) * sizeof(long);
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
434
435
436
437
438
439
440
441
442
443
444
445
446
447
  		if (len > maxlen)
  			len = maxlen;
  
  		if (copy_to_user(p, bits, len))
  			return -EFAULT;
  	}
  
  	return len;
  }
  #else
  static int bits_to_user(unsigned long *bits, unsigned int maxbit,
  			unsigned int maxlen, void __user *p, int compat)
  {
  	int len = compat ?
7b19ada2e   Jiri Slaby   get rid of input ...
448
449
  			BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t) :
  			BITS_TO_LONGS(maxbit) * sizeof(long);
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
450
451
452
453
454
455
456
457
458
459
460
461
462
  
  	if (len > maxlen)
  		len = maxlen;
  
  	return copy_to_user(p, bits, len) ? -EFAULT : len;
  }
  #endif /* __BIG_ENDIAN */
  
  #else
  
  static int bits_to_user(unsigned long *bits, unsigned int maxbit,
  			unsigned int maxlen, void __user *p, int compat)
  {
7b19ada2e   Jiri Slaby   get rid of input ...
463
  	int len = BITS_TO_LONGS(maxbit) * sizeof(long);
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
  
  	if (len > maxlen)
  		len = maxlen;
  
  	return copy_to_user(p, bits, len) ? -EFAULT : len;
  }
  
  #endif /* CONFIG_COMPAT */
  
  static int str_to_user(const char *str, unsigned int maxlen, void __user *p)
  {
  	int len;
  
  	if (!str)
  		return -ENOENT;
  
  	len = strlen(str) + 1;
  	if (len > maxlen)
  		len = maxlen;
  
  	return copy_to_user(p, str, len) ? -EFAULT : len;
  }
f2afa7711   Dmitry Torokhov   Input: paper over...
486
  #define OLD_KEY_MAX	0x1ff
448cd1664   Dmitry Torokhov   Input: evdev - re...
487
488
489
  static int handle_eviocgbit(struct input_dev *dev,
  			    unsigned int type, unsigned int size,
  			    void __user *p, int compat_mode)
5402a7349   Linus Torvalds   Input: evdev - sp...
490
  {
f2afa7711   Dmitry Torokhov   Input: paper over...
491
  	static unsigned long keymax_warn_time;
5402a7349   Linus Torvalds   Input: evdev - sp...
492
493
  	unsigned long *bits;
  	int len;
448cd1664   Dmitry Torokhov   Input: evdev - re...
494
  	switch (type) {
5402a7349   Linus Torvalds   Input: evdev - sp...
495
496
497
498
499
500
501
502
503
504
505
506
  
  	case      0: bits = dev->evbit;  len = EV_MAX;  break;
  	case EV_KEY: bits = dev->keybit; len = KEY_MAX; break;
  	case EV_REL: bits = dev->relbit; len = REL_MAX; break;
  	case EV_ABS: bits = dev->absbit; len = ABS_MAX; break;
  	case EV_MSC: bits = dev->mscbit; len = MSC_MAX; break;
  	case EV_LED: bits = dev->ledbit; len = LED_MAX; break;
  	case EV_SND: bits = dev->sndbit; len = SND_MAX; break;
  	case EV_FF:  bits = dev->ffbit;  len = FF_MAX;  break;
  	case EV_SW:  bits = dev->swbit;  len = SW_MAX;  break;
  	default: return -EINVAL;
  	}
f2afa7711   Dmitry Torokhov   Input: paper over...
507
508
509
510
511
512
  
  	/*
  	 * Work around bugs in userspace programs that like to do
  	 * EVIOCGBIT(EV_KEY, KEY_MAX) and not realize that 'len'
  	 * should be in bytes, not in bits.
  	 */
448cd1664   Dmitry Torokhov   Input: evdev - re...
513
  	if (type == EV_KEY && size == OLD_KEY_MAX) {
f2afa7711   Dmitry Torokhov   Input: paper over...
514
515
  		len = OLD_KEY_MAX;
  		if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000))
da0c49011   Joe Perches   Input: use pr_fmt...
516
517
518
519
520
521
  			pr_warning("(EVIOCGBIT): Suspicious buffer size %u, "
  				   "limiting output to %zu bytes. See "
  				   "http://userweb.kernel.org/~dtor/eviocgbit-bug.html
  ",
  				   OLD_KEY_MAX,
  				   BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
f2afa7711   Dmitry Torokhov   Input: paper over...
522
  	}
448cd1664   Dmitry Torokhov   Input: evdev - re...
523
  	return bits_to_user(bits, len, size, p, compat_mode);
5402a7349   Linus Torvalds   Input: evdev - sp...
524
  }
f2afa7711   Dmitry Torokhov   Input: paper over...
525
  #undef OLD_KEY_MAX
5402a7349   Linus Torvalds   Input: evdev - sp...
526

ab4e01921   Dmitry Torokhov   Input: define sep...
527
  static int evdev_handle_get_keycode(struct input_dev *dev, void __user *p)
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
528
  {
ab4e01921   Dmitry Torokhov   Input: define sep...
529
530
531
532
533
  	struct input_keymap_entry ke = {
  		.len	= sizeof(unsigned int),
  		.flags	= 0,
  	};
  	int __user *ip = (int __user *)p;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
534
  	int error;
ab4e01921   Dmitry Torokhov   Input: define sep...
535
536
537
  	/* legacy case */
  	if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
  		return -EFAULT;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
538

ab4e01921   Dmitry Torokhov   Input: define sep...
539
540
541
  	error = input_get_keycode(dev, &ke);
  	if (error)
  		return error;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
542

ab4e01921   Dmitry Torokhov   Input: define sep...
543
544
  	if (put_user(ke.keycode, ip + 1))
  		return -EFAULT;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
545

ab4e01921   Dmitry Torokhov   Input: define sep...
546
547
  	return 0;
  }
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
548

ab4e01921   Dmitry Torokhov   Input: define sep...
549
550
551
552
  static int evdev_handle_get_keycode_v2(struct input_dev *dev, void __user *p)
  {
  	struct input_keymap_entry ke;
  	int error;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
553

ab4e01921   Dmitry Torokhov   Input: define sep...
554
555
  	if (copy_from_user(&ke, p, sizeof(ke)))
  		return -EFAULT;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
556

ab4e01921   Dmitry Torokhov   Input: define sep...
557
558
559
  	error = input_get_keycode(dev, &ke);
  	if (error)
  		return error;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
560

ab4e01921   Dmitry Torokhov   Input: define sep...
561
562
  	if (copy_to_user(p, &ke, sizeof(ke)))
  		return -EFAULT;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
563

8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
564
565
  	return 0;
  }
ab4e01921   Dmitry Torokhov   Input: define sep...
566
  static int evdev_handle_set_keycode(struct input_dev *dev, void __user *p)
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
567
  {
ab4e01921   Dmitry Torokhov   Input: define sep...
568
569
570
571
572
  	struct input_keymap_entry ke = {
  		.len	= sizeof(unsigned int),
  		.flags	= 0,
  	};
  	int __user *ip = (int __user *)p;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
573

ab4e01921   Dmitry Torokhov   Input: define sep...
574
575
  	if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
  		return -EFAULT;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
576

ab4e01921   Dmitry Torokhov   Input: define sep...
577
578
  	if (get_user(ke.keycode, ip + 1))
  		return -EFAULT;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
579

ab4e01921   Dmitry Torokhov   Input: define sep...
580
581
  	return input_set_keycode(dev, &ke);
  }
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
582

ab4e01921   Dmitry Torokhov   Input: define sep...
583
584
585
  static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p)
  {
  	struct input_keymap_entry ke;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
586

ab4e01921   Dmitry Torokhov   Input: define sep...
587
588
  	if (copy_from_user(&ke, p, sizeof(ke)))
  		return -EFAULT;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
589

ab4e01921   Dmitry Torokhov   Input: define sep...
590
591
  	if (ke.len > sizeof(ke.scancode))
  		return -EINVAL;
8613e4c28   Mauro Carvalho Chehab   Input: add suppor...
592
593
594
  
  	return input_set_keycode(dev, &ke);
  }
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
595
596
  static long evdev_do_ioctl(struct file *file, unsigned int cmd,
  			   void __user *p, int compat_mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
598
599
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600
601
  	struct input_dev *dev = evdev->handle.dev;
  	struct input_absinfo abs;
509ca1a93   Anssi Hannula   Input: implement ...
602
  	struct ff_effect effect;
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
603
  	int __user *ip = (int __user *)p;
58b939959   Dmitry Torokhov   Input: scancode i...
604
  	unsigned int i, t, u, v;
448cd1664   Dmitry Torokhov   Input: evdev - re...
605
  	unsigned int size;
509ca1a93   Anssi Hannula   Input: implement ...
606
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607

448cd1664   Dmitry Torokhov   Input: evdev - re...
608
  	/* First we check for fixed-length commands */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609
  	switch (cmd) {
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
610
611
  	case EVIOCGVERSION:
  		return put_user(EV_VERSION, ip);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
612

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
613
614
615
616
  	case EVIOCGID:
  		if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
  			return -EFAULT;
  		return 0;
08791e5cf   Dmitry Torokhov   Input: ressurect ...
617

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
618
619
620
621
622
623
624
625
  	case EVIOCGREP:
  		if (!test_bit(EV_REP, dev->evbit))
  			return -ENOSYS;
  		if (put_user(dev->rep[REP_DELAY], ip))
  			return -EFAULT;
  		if (put_user(dev->rep[REP_PERIOD], ip + 1))
  			return -EFAULT;
  		return 0;
08791e5cf   Dmitry Torokhov   Input: ressurect ...
626

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
627
628
629
630
631
632
633
  	case EVIOCSREP:
  		if (!test_bit(EV_REP, dev->evbit))
  			return -ENOSYS;
  		if (get_user(u, ip))
  			return -EFAULT;
  		if (get_user(v, ip + 1))
  			return -EFAULT;
08791e5cf   Dmitry Torokhov   Input: ressurect ...
634

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
635
636
  		input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u);
  		input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v);
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
637

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
638
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
640
641
  	case EVIOCRMFF:
  		return input_ff_erase(dev, (int)(unsigned long) p, file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
643
644
645
646
647
648
649
650
651
652
653
654
  	case EVIOCGEFFECTS:
  		i = test_bit(EV_FF, dev->evbit) ?
  				dev->ff->max_effects : 0;
  		if (put_user(i, ip))
  			return -EFAULT;
  		return 0;
  
  	case EVIOCGRAB:
  		if (p)
  			return evdev_grab(evdev, client);
  		else
  			return evdev_ungrab(evdev, client);
ab4e01921   Dmitry Torokhov   Input: define sep...
655
656
657
658
659
660
661
662
663
664
665
666
  
  	case EVIOCGKEYCODE:
  		return evdev_handle_get_keycode(dev, p);
  
  	case EVIOCSKEYCODE:
  		return evdev_handle_set_keycode(dev, p);
  
  	case EVIOCGKEYCODE_V2:
  		return evdev_handle_get_keycode_v2(dev, p);
  
  	case EVIOCSKEYCODE_V2:
  		return evdev_handle_set_keycode_v2(dev, p);
448cd1664   Dmitry Torokhov   Input: evdev - re...
667
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668

448cd1664   Dmitry Torokhov   Input: evdev - re...
669
  	size = _IOC_SIZE(cmd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670

448cd1664   Dmitry Torokhov   Input: evdev - re...
671
672
  	/* Now check variable-length commands */
  #define EVIOC_MASK_SIZE(nr)	((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
448cd1664   Dmitry Torokhov   Input: evdev - re...
673
  	switch (EVIOC_MASK_SIZE(cmd)) {
41e979f82   Vojtech Pavlik   Input: Make EVIOS...
674

85b772003   Henrik Rydberg   Input: introduce ...
675
676
677
  	case EVIOCGPROP(0):
  		return bits_to_user(dev->propbit, INPUT_PROP_MAX,
  				    size, p, compat_mode);
448cd1664   Dmitry Torokhov   Input: evdev - re...
678
679
  	case EVIOCGKEY(0):
  		return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
680

448cd1664   Dmitry Torokhov   Input: evdev - re...
681
682
  	case EVIOCGLED(0):
  		return bits_to_user(dev->led, LED_MAX, size, p, compat_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
683

448cd1664   Dmitry Torokhov   Input: evdev - re...
684
685
  	case EVIOCGSND(0):
  		return bits_to_user(dev->snd, SND_MAX, size, p, compat_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686

448cd1664   Dmitry Torokhov   Input: evdev - re...
687
688
  	case EVIOCGSW(0):
  		return bits_to_user(dev->sw, SW_MAX, size, p, compat_mode);
315810668   Richard Purdie   [PATCH] Input: Ad...
689

448cd1664   Dmitry Torokhov   Input: evdev - re...
690
691
  	case EVIOCGNAME(0):
  		return str_to_user(dev->name, size, p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692

448cd1664   Dmitry Torokhov   Input: evdev - re...
693
694
  	case EVIOCGPHYS(0):
  		return str_to_user(dev->phys, size, p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
695

448cd1664   Dmitry Torokhov   Input: evdev - re...
696
697
  	case EVIOCGUNIQ(0):
  		return str_to_user(dev->uniq, size, p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698

448cd1664   Dmitry Torokhov   Input: evdev - re...
699
700
701
  	case EVIOC_MASK_SIZE(EVIOCSFF):
  		if (input_ff_effect_from_user(p, size, &effect))
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702

448cd1664   Dmitry Torokhov   Input: evdev - re...
703
  		error = input_ff_upload(dev, &effect, file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704

448cd1664   Dmitry Torokhov   Input: evdev - re...
705
706
  		if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
  			return -EFAULT;
41e979f82   Vojtech Pavlik   Input: Make EVIOS...
707

448cd1664   Dmitry Torokhov   Input: evdev - re...
708
709
  		return error;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710

448cd1664   Dmitry Torokhov   Input: evdev - re...
711
712
713
  	/* Multi-number variable-length handlers */
  	if (_IOC_TYPE(cmd) != 'E')
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714

448cd1664   Dmitry Torokhov   Input: evdev - re...
715
  	if (_IOC_DIR(cmd) == _IOC_READ) {
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
716

448cd1664   Dmitry Torokhov   Input: evdev - re...
717
718
719
720
  		if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0, 0)))
  			return handle_eviocgbit(dev,
  						_IOC_NR(cmd) & EV_MAX, size,
  						p, compat_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721

448cd1664   Dmitry Torokhov   Input: evdev - re...
722
  		if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
f2278f31d   Adam Dawidowski   Input: fix force ...
723

0a74a1df3   Daniel Mack   Input: evdev - fi...
724
725
  			if (!dev->absinfo)
  				return -EINVAL;
448cd1664   Dmitry Torokhov   Input: evdev - re...
726
727
  			t = _IOC_NR(cmd) & ABS_MAX;
  			abs = dev->absinfo[t];
f2278f31d   Adam Dawidowski   Input: fix force ...
728

448cd1664   Dmitry Torokhov   Input: evdev - re...
729
730
731
  			if (copy_to_user(p, &abs, min_t(size_t,
  					size, sizeof(struct input_absinfo))))
  				return -EFAULT;
f2278f31d   Adam Dawidowski   Input: fix force ...
732

448cd1664   Dmitry Torokhov   Input: evdev - re...
733
734
735
  			return 0;
  		}
  	}
f2278f31d   Adam Dawidowski   Input: fix force ...
736

f9ce6eb5b   Daniel Mack   Input: evdev - fi...
737
  	if (_IOC_DIR(cmd) == _IOC_WRITE) {
f2278f31d   Adam Dawidowski   Input: fix force ...
738

448cd1664   Dmitry Torokhov   Input: evdev - re...
739
  		if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740

0a74a1df3   Daniel Mack   Input: evdev - fi...
741
742
  			if (!dev->absinfo)
  				return -EINVAL;
448cd1664   Dmitry Torokhov   Input: evdev - re...
743
  			t = _IOC_NR(cmd) & ABS_MAX;
41e979f82   Vojtech Pavlik   Input: Make EVIOS...
744

448cd1664   Dmitry Torokhov   Input: evdev - re...
745
746
747
  			if (copy_from_user(&abs, p, min_t(size_t,
  					size, sizeof(struct input_absinfo))))
  				return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
748

448cd1664   Dmitry Torokhov   Input: evdev - re...
749
750
  			if (size < sizeof(struct input_absinfo))
  				abs.resolution = 0;
d31b2865a   Daniel Mack   Input: dynamicall...
751

448cd1664   Dmitry Torokhov   Input: evdev - re...
752
753
754
  			/* We can't change number of reserved MT slots */
  			if (t == ABS_MT_SLOT)
  				return -EINVAL;
40d007e7d   Henrik Rydberg   Input: introduce ...
755

448cd1664   Dmitry Torokhov   Input: evdev - re...
756
757
758
759
760
761
762
763
  			/*
  			 * Take event lock to ensure that we are not
  			 * changing device parameters in the middle
  			 * of event.
  			 */
  			spin_lock_irq(&dev->event_lock);
  			dev->absinfo[t] = abs;
  			spin_unlock_irq(&dev->event_lock);
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
764

448cd1664   Dmitry Torokhov   Input: evdev - re...
765
  			return 0;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
766
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
767
  	}
448cd1664   Dmitry Torokhov   Input: evdev - re...
768

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
770
  	return -EINVAL;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
771

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
  static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
  				void __user *p, int compat_mode)
  {
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
  	int retval;
  
  	retval = mutex_lock_interruptible(&evdev->mutex);
  	if (retval)
  		return retval;
  
  	if (!evdev->exist) {
  		retval = -ENODEV;
  		goto out;
  	}
  
  	retval = evdev_do_ioctl(file, cmd, p, compat_mode);
  
   out:
  	mutex_unlock(&evdev->mutex);
  	return retval;
  }
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
794
795
796
797
  static long evdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  {
  	return evdev_ioctl_handler(file, cmd, (void __user *)arg, 0);
  }
41e979f82   Vojtech Pavlik   Input: Make EVIOS...
798

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
799
  #ifdef CONFIG_COMPAT
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
800
801
  static long evdev_ioctl_compat(struct file *file,
  				unsigned int cmd, unsigned long arg)
52658bb68   Juergen Kreileder   Input: Add suppor...
802
  {
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
803
  	return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804
  }
52658bb68   Juergen Kreileder   Input: Add suppor...
805
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
806

66e661188   Dmitry Torokhov   Input: constify i...
807
  static const struct file_operations evdev_fops = {
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
808
809
810
811
812
813
814
  	.owner		= THIS_MODULE,
  	.read		= evdev_read,
  	.write		= evdev_write,
  	.poll		= evdev_poll,
  	.open		= evdev_open,
  	.release	= evdev_release,
  	.unlocked_ioctl	= evdev_ioctl,
52658bb68   Juergen Kreileder   Input: Add suppor...
815
  #ifdef CONFIG_COMPAT
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
816
  	.compat_ioctl	= evdev_ioctl_compat,
52658bb68   Juergen Kreileder   Input: Add suppor...
817
  #endif
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
818
  	.fasync		= evdev_fasync,
6038f373a   Arnd Bergmann   llseek: automatic...
819
820
  	.flush		= evdev_flush,
  	.llseek		= no_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
821
  };
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
  static int evdev_install_chrdev(struct evdev *evdev)
  {
  	/*
  	 * No need to do any locking here as calls to connect and
  	 * disconnect are serialized by the input core
  	 */
  	evdev_table[evdev->minor] = evdev;
  	return 0;
  }
  
  static void evdev_remove_chrdev(struct evdev *evdev)
  {
  	/*
  	 * Lock evdev table to prevent race with evdev_open()
  	 */
  	mutex_lock(&evdev_table_mutex);
  	evdev_table[evdev->minor] = NULL;
  	mutex_unlock(&evdev_table_mutex);
  }
  
  /*
   * Mark device non-existent. This disables writes, ioctls and
   * prevents new users from opening the device. Already posted
   * blocking reads will stay, however new ones will fail.
   */
  static void evdev_mark_dead(struct evdev *evdev)
  {
  	mutex_lock(&evdev->mutex);
20da92de8   Dmitry Torokhov   Input: change inp...
850
  	evdev->exist = false;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
  	mutex_unlock(&evdev->mutex);
  }
  
  static void evdev_cleanup(struct evdev *evdev)
  {
  	struct input_handle *handle = &evdev->handle;
  
  	evdev_mark_dead(evdev);
  	evdev_hangup(evdev);
  	evdev_remove_chrdev(evdev);
  
  	/* evdev is marked dead so no one else accesses evdev->open */
  	if (evdev->open) {
  		input_flush_device(handle, NULL);
  		input_close_device(handle);
  	}
  }
  
  /*
   * Create new evdev device. Note that input core serializes calls
   * to connect and disconnect so we don't need to lock evdev_table here.
   */
5b2a08262   Dmitry Torokhov   Input: rework han...
873
874
  static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
  			 const struct input_device_id *id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
875
876
877
  {
  	struct evdev *evdev;
  	int minor;
5b2a08262   Dmitry Torokhov   Input: rework han...
878
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
880
881
882
  	for (minor = 0; minor < EVDEV_MINORS; minor++)
  		if (!evdev_table[minor])
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
883
  	if (minor == EVDEV_MINORS) {
da0c49011   Joe Perches   Input: use pr_fmt...
884
885
  		pr_err("no more free evdev devices
  ");
5b2a08262   Dmitry Torokhov   Input: rework han...
886
  		return -ENFILE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
887
  	}
5b2a08262   Dmitry Torokhov   Input: rework han...
888
889
890
  	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
  	if (!evdev)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
891

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
892
  	INIT_LIST_HEAD(&evdev->client_list);
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
893
894
  	spin_lock_init(&evdev->client_lock);
  	mutex_init(&evdev->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
895
  	init_waitqueue_head(&evdev->wait);
3d5cb60ef   Thadeu Lima de Souza Cascardo   Input: simplify n...
896
  	dev_set_name(&evdev->dev, "event%d", minor);
20da92de8   Dmitry Torokhov   Input: change inp...
897
  	evdev->exist = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
898
  	evdev->minor = minor;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
899

a7097ff89   Dmitry Torokhov   Input: make sure ...
900
  	evdev->handle.dev = input_get_device(dev);
3d5cb60ef   Thadeu Lima de Souza Cascardo   Input: simplify n...
901
  	evdev->handle.name = dev_name(&evdev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902
903
  	evdev->handle.handler = handler;
  	evdev->handle.private = evdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
904

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
905
  	evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
9657d75c5   Dmitry Torokhov   Input: convert fr...
906
907
  	evdev->dev.class = &input_class;
  	evdev->dev.parent = &dev->dev;
9657d75c5   Dmitry Torokhov   Input: convert fr...
908
909
  	evdev->dev.release = evdev_free;
  	device_initialize(&evdev->dev);
5b2a08262   Dmitry Torokhov   Input: rework han...
910

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
911
  	error = input_register_handle(&evdev->handle);
5b2a08262   Dmitry Torokhov   Input: rework han...
912
  	if (error)
9657d75c5   Dmitry Torokhov   Input: convert fr...
913
  		goto err_free_evdev;
5b2a08262   Dmitry Torokhov   Input: rework han...
914

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
915
916
917
918
919
  	error = evdev_install_chrdev(evdev);
  	if (error)
  		goto err_unregister_handle;
  
  	error = device_add(&evdev->dev);
5b2a08262   Dmitry Torokhov   Input: rework han...
920
  	if (error)
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
921
  		goto err_cleanup_evdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
922

5b2a08262   Dmitry Torokhov   Input: rework han...
923
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
924

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
925
926
927
928
   err_cleanup_evdev:
  	evdev_cleanup(evdev);
   err_unregister_handle:
  	input_unregister_handle(&evdev->handle);
5b2a08262   Dmitry Torokhov   Input: rework han...
929
   err_free_evdev:
9657d75c5   Dmitry Torokhov   Input: convert fr...
930
  	put_device(&evdev->dev);
5b2a08262   Dmitry Torokhov   Input: rework han...
931
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
932
933
934
935
936
  }
  
  static void evdev_disconnect(struct input_handle *handle)
  {
  	struct evdev *evdev = handle->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
937

9657d75c5   Dmitry Torokhov   Input: convert fr...
938
  	device_del(&evdev->dev);
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
939
940
  	evdev_cleanup(evdev);
  	input_unregister_handle(handle);
9657d75c5   Dmitry Torokhov   Input: convert fr...
941
  	put_device(&evdev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
942
  }
66e661188   Dmitry Torokhov   Input: constify i...
943
  static const struct input_device_id evdev_ids[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
944
945
946
947
948
949
950
  	{ .driver_info = 1 },	/* Matches all devices */
  	{ },			/* Terminating zero entry */
  };
  
  MODULE_DEVICE_TABLE(input, evdev_ids);
  
  static struct input_handler evdev_handler = {
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
951
952
953
954
955
956
957
  	.event		= evdev_event,
  	.connect	= evdev_connect,
  	.disconnect	= evdev_disconnect,
  	.fops		= &evdev_fops,
  	.minor		= EVDEV_MINOR_BASE,
  	.name		= "evdev",
  	.id_table	= evdev_ids,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
959
960
961
  };
  
  static int __init evdev_init(void)
  {
4263cf0fa   Dmitry Torokhov   Input: make input...
962
  	return input_register_handler(&evdev_handler);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
963
964
965
966
967
968
969
970
971
972
973
974
975
  }
  
  static void __exit evdev_exit(void)
  {
  	input_unregister_handler(&evdev_handler);
  }
  
  module_init(evdev_init);
  module_exit(evdev_exit);
  
  MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
  MODULE_DESCRIPTION("Input driver event char devices");
  MODULE_LICENSE("GPL");