Blame view

drivers/input/evdev.c 20.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * 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.
   */
  
  #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 {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
  	int head;
  	int tail;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
41
  	spinlock_t buffer_lock; /* protects access to buffer, head and tail */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
  	struct fasync_struct *fasync;
  	struct evdev *evdev;
  	struct list_head node;
b58f7086d   Henrik Rydberg   Input: evdev - co...
45
46
  	int bufsize;
  	struct input_event buffer[];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47
48
49
  };
  
  static struct evdev *evdev_table[EVDEV_MINORS];
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
50
  static DEFINE_MUTEX(evdev_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
52
53
54
55
  static void evdev_pass_event(struct evdev_client *client,
  			     struct input_event *event)
  {
  	/*
e725a4945   Henrik Rydberg   Input: evdev - ne...
56
57
58
  	 * Interrupts are disabled, just acquire the lock.
  	 * Make sure we don't leave with the client buffer
  	 * "empty" by having client->head == client->tail.
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
59
60
  	 */
  	spin_lock(&client->buffer_lock);
e725a4945   Henrik Rydberg   Input: evdev - ne...
61
62
63
64
  	do {
  		client->buffer[client->head++] = *event;
  		client->head &= client->bufsize - 1;
  	} while (client->head == client->tail);
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
65
  	spin_unlock(&client->buffer_lock);
30a589fde   Adam Jackson   Input: evdev - be...
66
67
  	if (event->type == EV_SYN)
  		kill_fasync(&client->fasync, SIGIO, POLL_IN);
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
68
69
70
  }
  
  /*
82ba56c27   Dmitry Torokhov   Input: use full R...
71
   * Pass incoming event to all connected clients.
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
72
73
74
   */
  static void evdev_event(struct input_handle *handle,
  			unsigned int type, unsigned int code, int value)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
  {
  	struct evdev *evdev = handle->private;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
77
  	struct evdev_client *client;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
78
  	struct input_event event;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
80
81
82
83
  	do_gettimeofday(&event.time);
  	event.type = type;
  	event.code = code;
  	event.value = value;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84

82ba56c27   Dmitry Torokhov   Input: use full R...
85
  	rcu_read_lock();
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
86
87
88
89
90
91
  	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
92

82ba56c27   Dmitry Torokhov   Input: use full R...
93
  	rcu_read_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
97
98
  	wake_up_interruptible(&evdev->wait);
  }
  
  static int evdev_fasync(int fd, struct file *file, int on)
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
99
  	struct evdev_client *client = file->private_data;
1e0afb288   Dmitry Torokhov   Input: fix format...
100

60aa49243   Jonathan Corbet   Rationalize fasyn...
101
  	return fasync_helper(fd, file, on, &client->fasync);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  }
1e0afb288   Dmitry Torokhov   Input: fix format...
103
  static int evdev_flush(struct file *file, fl_owner_t id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
105
106
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
107
108
109
110
111
  	int retval;
  
  	retval = mutex_lock_interruptible(&evdev->mutex);
  	if (retval)
  		return retval;
1e0afb288   Dmitry Torokhov   Input: fix format...
112

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
113
  	if (!evdev->exist)
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
114
115
116
  		retval = -ENODEV;
  	else
  		retval = input_flush_device(&evdev->handle, file);
1e0afb288   Dmitry Torokhov   Input: fix format...
117

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
118
119
  	mutex_unlock(&evdev->mutex);
  	return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
  }
9657d75c5   Dmitry Torokhov   Input: convert fr...
121
  static void evdev_free(struct device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
122
  {
9657d75c5   Dmitry Torokhov   Input: convert fr...
123
  	struct evdev *evdev = container_of(dev, struct evdev, dev);
a7097ff89   Dmitry Torokhov   Input: make sure ...
124
  	input_put_device(evdev->handle.dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
  	kfree(evdev);
  }
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
  /*
   * 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);
82ba56c27   Dmitry Torokhov   Input: use full R...
143
  	synchronize_rcu();
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
144
145
146
147
148
149
150
151
152
153
  
  	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...
154
  	synchronize_rcu();
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
155
156
157
158
159
160
161
162
163
164
165
  	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);
82ba56c27   Dmitry Torokhov   Input: use full R...
166
  	synchronize_rcu();
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
167
168
169
170
171
172
173
174
  }
  
  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...
175
  	synchronize_rcu();
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
176
177
178
179
180
181
182
183
184
185
186
187
  }
  
  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...
188
  	else if (!evdev->open++) {
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
189
  		retval = input_open_device(&evdev->handle);
064450140   Oliver Neukum   Input: fix open c...
190
191
192
  		if (retval)
  			evdev->open--;
  	}
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
  
  	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 -...
223
  static int evdev_release(struct inode *inode, struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
225
226
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
228
229
230
231
  	mutex_lock(&evdev->mutex);
  	if (evdev->grab == client)
  		evdev_ungrab(evdev, client);
  	mutex_unlock(&evdev->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232

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

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
  	return 0;
  }
b58f7086d   Henrik Rydberg   Input: evdev - co...
241
242
  static unsigned int evdev_compute_buffer_size(struct input_dev *dev)
  {
63a6404d8   Henrik Rydberg   Input: evdev - us...
243
244
245
246
247
  	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...
248
  }
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
249
  static int evdev_open(struct inode *inode, struct file *file)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
251
  	struct evdev *evdev;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
252
  	struct evdev_client *client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
253
  	int i = iminor(inode) - EVDEV_MINOR_BASE;
b58f7086d   Henrik Rydberg   Input: evdev - co...
254
  	unsigned int bufsize;
d542ed82f   Dmitry Torokhov   Input: handlers -...
255
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
257
  	if (i >= EVDEV_MINORS)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
  		return -ENODEV;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
259
260
261
  	error = mutex_lock_interruptible(&evdev_table_mutex);
  	if (error)
  		return error;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
262
  	evdev = evdev_table[i];
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
263
264
265
  	if (evdev)
  		get_device(&evdev->dev);
  	mutex_unlock(&evdev_table_mutex);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
266

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
267
  	if (!evdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
  		return -ENODEV;
b58f7086d   Henrik Rydberg   Input: evdev - co...
269
270
271
272
273
  	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...
274
275
276
277
  	if (!client) {
  		error = -ENOMEM;
  		goto err_put_evdev;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
278

b58f7086d   Henrik Rydberg   Input: evdev - co...
279
  	client->bufsize = bufsize;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
280
  	spin_lock_init(&client->buffer_lock);
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
281
  	client->evdev = evdev;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
282
  	evdev_attach_client(evdev, client);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
284
285
286
  	error = evdev_open_device(evdev);
  	if (error)
  		goto err_free_client;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
288
  	file->private_data = client;
3d7bbd457   Dmitry Torokhov   Input: mark input...
289
  	nonseekable_open(inode, file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
  	return 0;
9657d75c5   Dmitry Torokhov   Input: convert fr...
291
292
  
   err_free_client:
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
293
  	evdev_detach_client(evdev, client);
9657d75c5   Dmitry Torokhov   Input: convert fr...
294
295
296
297
  	kfree(client);
   err_put_evdev:
  	put_device(&evdev->dev);
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
  }
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
299
300
  static ssize_t evdev_write(struct file *file, const char __user *buffer,
  			   size_t count, loff_t *ppos)
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
301
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
302
303
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
304
  	struct input_event event;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
305
  	int retval;
52658bb68   Juergen Kreileder   Input: Add suppor...
306

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
307
308
309
310
311
312
313
314
  	retval = mutex_lock_interruptible(&evdev->mutex);
  	if (retval)
  		return retval;
  
  	if (!evdev->exist) {
  		retval = -ENODEV;
  		goto out;
  	}
52658bb68   Juergen Kreileder   Input: Add suppor...
315

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
316
  	while (retval < count) {
2d56f3a32   Philip Langdale   Input: refactor e...
317
  		if (input_event_from_user(buffer + retval, &event)) {
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
318
319
320
321
322
323
  			retval = -EFAULT;
  			goto out;
  		}
  
  		input_inject_event(&evdev->handle,
  				   event.type, event.code, event.value);
2d56f3a32   Philip Langdale   Input: refactor e...
324
  		retval += input_event_size();
52658bb68   Juergen Kreileder   Input: Add suppor...
325
  	}
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
326
327
   out:
  	mutex_unlock(&evdev->mutex);
52658bb68   Juergen Kreileder   Input: Add suppor...
328
329
  	return retval;
  }
52658bb68   Juergen Kreileder   Input: Add suppor...
330

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
331
332
333
334
335
336
337
338
339
340
  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...
341
  		client->tail &= client->bufsize - 1;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
342
343
344
345
346
347
348
349
350
  	}
  
  	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
351
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
352
353
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
354
  	struct input_event event;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355
  	int retval;
2d56f3a32   Philip Langdale   Input: refactor e...
356
  	if (count < input_event_size())
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
357
  		return -EINVAL;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
358
359
  	if (client->head == client->tail && evdev->exist &&
  	    (file->f_flags & O_NONBLOCK))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
  		return -EAGAIN;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
361
362
  	retval = wait_event_interruptible(evdev->wait,
  		client->head != client->tail || !evdev->exist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
  	if (retval)
  		return retval;
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
365
  	if (!evdev->exist)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
  		return -ENODEV;
2d56f3a32   Philip Langdale   Input: refactor e...
367
  	while (retval + input_event_size() <= count &&
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
368
  	       evdev_fetch_next_event(client, &event)) {
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
369

2d56f3a32   Philip Langdale   Input: refactor e...
370
  		if (input_event_to_user(buffer + retval, &event))
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
371
  			return -EFAULT;
2d56f3a32   Philip Langdale   Input: refactor e...
372
  		retval += input_event_size();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
373
374
375
376
377
378
379
380
  	}
  
  	return retval;
  }
  
  /* No kernel lock - fine */
  static unsigned int evdev_poll(struct file *file, poll_table *wait)
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
381
382
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
c18fb1396   Dmitry Torokhov   Input: evdev - si...
383
  	unsigned int mask;
1e0afb288   Dmitry Torokhov   Input: fix format...
384

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
385
  	poll_wait(file, &evdev->wait, wait);
c18fb1396   Dmitry Torokhov   Input: evdev - si...
386
387
388
389
390
391
  
  	mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR;
  	if (client->head != client->tail)
  		mask |= POLLIN | POLLRDNORM;
  
  	return mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
  }
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
393
394
395
  #ifdef CONFIG_COMPAT
  
  #define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)
7b19ada2e   Jiri Slaby   get rid of input ...
396
  #define BITS_TO_LONGS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1)
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
397
398
399
400
401
402
403
404
  
  #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 ...
405
  		len = BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t);
bf61f8d35   Kenichi Nagai   Input: evdev - fi...
406
  		if (len > maxlen)
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
407
408
409
410
411
412
413
414
415
  			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 ...
416
  		len = BITS_TO_LONGS(maxbit) * sizeof(long);
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
417
418
419
420
421
422
423
424
425
426
427
428
429
430
  		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 ...
431
432
  			BITS_TO_LONGS_COMPAT(maxbit) * sizeof(compat_long_t) :
  			BITS_TO_LONGS(maxbit) * sizeof(long);
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
433
434
435
436
437
438
439
440
441
442
443
444
445
  
  	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 ...
446
  	int len = BITS_TO_LONGS(maxbit) * sizeof(long);
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
  
  	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...
469
  #define OLD_KEY_MAX	0x1ff
448cd1664   Dmitry Torokhov   Input: evdev - re...
470
471
472
  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...
473
  {
f2afa7711   Dmitry Torokhov   Input: paper over...
474
  	static unsigned long keymax_warn_time;
5402a7349   Linus Torvalds   Input: evdev - sp...
475
476
  	unsigned long *bits;
  	int len;
448cd1664   Dmitry Torokhov   Input: evdev - re...
477
  	switch (type) {
5402a7349   Linus Torvalds   Input: evdev - sp...
478
479
480
481
482
483
484
485
486
487
488
489
  
  	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...
490
491
492
493
494
495
  
  	/*
  	 * 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...
496
  	if (type == EV_KEY && size == OLD_KEY_MAX) {
f2afa7711   Dmitry Torokhov   Input: paper over...
497
498
499
  		len = OLD_KEY_MAX;
  		if (printk_timed_ratelimit(&keymax_warn_time, 10 * 1000))
  			printk(KERN_WARNING
c85e2031e   Geert Uytterhoeven   Input: evdev - fi...
500
501
  				"evdev.c(EVIOCGBIT): Suspicious buffer size %u, "
  				"limiting output to %zu bytes. See "
f2afa7711   Dmitry Torokhov   Input: paper over...
502
503
504
505
506
  				"http://userweb.kernel.org/~dtor/eviocgbit-bug.html
  ",
  				OLD_KEY_MAX,
  				BITS_TO_LONGS(OLD_KEY_MAX) * sizeof(long));
  	}
448cd1664   Dmitry Torokhov   Input: evdev - re...
507
  	return bits_to_user(bits, len, size, p, compat_mode);
5402a7349   Linus Torvalds   Input: evdev - sp...
508
  }
f2afa7711   Dmitry Torokhov   Input: paper over...
509
  #undef OLD_KEY_MAX
5402a7349   Linus Torvalds   Input: evdev - sp...
510

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
511
512
  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
513
  {
d0ffb9be8   Dmitry Torokhov   Input: handlers -...
514
515
  	struct evdev_client *client = file->private_data;
  	struct evdev *evdev = client->evdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
516
517
  	struct input_dev *dev = evdev->handle.dev;
  	struct input_absinfo abs;
509ca1a93   Anssi Hannula   Input: implement ...
518
  	struct ff_effect effect;
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
519
  	int __user *ip = (int __user *)p;
58b939959   Dmitry Torokhov   Input: scancode i...
520
  	unsigned int i, t, u, v;
448cd1664   Dmitry Torokhov   Input: evdev - re...
521
  	unsigned int size;
509ca1a93   Anssi Hannula   Input: implement ...
522
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523

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

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
529
530
531
532
  	case EVIOCGID:
  		if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
  			return -EFAULT;
  		return 0;
08791e5cf   Dmitry Torokhov   Input: ressurect ...
533

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
534
535
536
537
538
539
540
541
  	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 ...
542

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
543
544
545
546
547
548
549
  	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 ...
550

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
551
552
  		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...
553

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

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
556
557
558
  	case EVIOCGKEYCODE:
  		if (get_user(t, ip))
  			return -EFAULT;
c8e4c7727   Marvin Raaijmakers   Input: add getkey...
559

f4f37c8ec   Dmitry Torokhov   Input: Add proper...
560
  		error = input_get_keycode(dev, t, &v);
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
561
562
  		if (error)
  			return error;
c8e4c7727   Marvin Raaijmakers   Input: add getkey...
563

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
564
565
  		if (put_user(v, ip + 1))
  			return -EFAULT;
c8e4c7727   Marvin Raaijmakers   Input: add getkey...
566

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

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
569
570
571
  	case EVIOCSKEYCODE:
  		if (get_user(t, ip) || get_user(v, ip + 1))
  			return -EFAULT;
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
572

f4f37c8ec   Dmitry Torokhov   Input: Add proper...
573
  		return input_set_keycode(dev, t, v);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574

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

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
578
579
580
581
582
583
584
585
586
587
588
589
  	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);
448cd1664   Dmitry Torokhov   Input: evdev - re...
590
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
591

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

448cd1664   Dmitry Torokhov   Input: evdev - re...
594
595
  	/* Now check variable-length commands */
  #define EVIOC_MASK_SIZE(nr)	((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
596

448cd1664   Dmitry Torokhov   Input: evdev - re...
597
  	switch (EVIOC_MASK_SIZE(cmd)) {
41e979f82   Vojtech Pavlik   Input: Make EVIOS...
598

448cd1664   Dmitry Torokhov   Input: evdev - re...
599
600
  	case EVIOCGKEY(0):
  		return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
601

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

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

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

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

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

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

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

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

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

448cd1664   Dmitry Torokhov   Input: evdev - re...
629
630
  		return error;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631

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

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

448cd1664   Dmitry Torokhov   Input: evdev - re...
638
639
640
641
  		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
642

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

448cd1664   Dmitry Torokhov   Input: evdev - re...
645
646
  			t = _IOC_NR(cmd) & ABS_MAX;
  			abs = dev->absinfo[t];
f2278f31d   Adam Dawidowski   Input: fix force ...
647

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

448cd1664   Dmitry Torokhov   Input: evdev - re...
652
653
654
  			return 0;
  		}
  	}
f2278f31d   Adam Dawidowski   Input: fix force ...
655

448cd1664   Dmitry Torokhov   Input: evdev - re...
656
  	if (_IOC_DIR(cmd) == _IOC_READ) {
f2278f31d   Adam Dawidowski   Input: fix force ...
657

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

448cd1664   Dmitry Torokhov   Input: evdev - re...
660
  			t = _IOC_NR(cmd) & ABS_MAX;
41e979f82   Vojtech Pavlik   Input: Make EVIOS...
661

448cd1664   Dmitry Torokhov   Input: evdev - re...
662
663
664
  			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
665

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

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

448cd1664   Dmitry Torokhov   Input: evdev - re...
673
674
675
676
677
678
679
680
  			/*
  			 * 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...
681

448cd1664   Dmitry Torokhov   Input: evdev - re...
682
  			return 0;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
683
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
684
  	}
448cd1664   Dmitry Torokhov   Input: evdev - re...
685

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
687
  	return -EINVAL;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
  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...
711
712
713
714
  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...
715

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
716
  #ifdef CONFIG_COMPAT
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
717
718
  static long evdev_ioctl_compat(struct file *file,
  				unsigned int cmd, unsigned long arg)
52658bb68   Juergen Kreileder   Input: Add suppor...
719
  {
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
720
  	return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721
  }
52658bb68   Juergen Kreileder   Input: Add suppor...
722
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723

66e661188   Dmitry Torokhov   Input: constify i...
724
  static const struct file_operations evdev_fops = {
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
725
726
727
728
729
730
731
  	.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...
732
  #ifdef CONFIG_COMPAT
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
733
  	.compat_ioctl	= evdev_ioctl_compat,
52658bb68   Juergen Kreileder   Input: Add suppor...
734
  #endif
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
735
736
  	.fasync		= evdev_fasync,
  	.flush		= evdev_flush
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
  };
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
  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...
766
  	evdev->exist = false;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
  	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...
789
790
  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
791
792
793
  {
  	struct evdev *evdev;
  	int minor;
5b2a08262   Dmitry Torokhov   Input: rework han...
794
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
796
797
798
  	for (minor = 0; minor < EVDEV_MINORS; minor++)
  		if (!evdev_table[minor])
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
799
800
801
  	if (minor == EVDEV_MINORS) {
  		printk(KERN_ERR "evdev: no more free evdev devices
  ");
5b2a08262   Dmitry Torokhov   Input: rework han...
802
  		return -ENFILE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
803
  	}
5b2a08262   Dmitry Torokhov   Input: rework han...
804
805
806
  	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
  	if (!evdev)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807

d0ffb9be8   Dmitry Torokhov   Input: handlers -...
808
  	INIT_LIST_HEAD(&evdev->client_list);
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
809
810
  	spin_lock_init(&evdev->client_lock);
  	mutex_init(&evdev->mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811
  	init_waitqueue_head(&evdev->wait);
3d5cb60ef   Thadeu Lima de Souza Cascardo   Input: simplify n...
812
  	dev_set_name(&evdev->dev, "event%d", minor);
20da92de8   Dmitry Torokhov   Input: change inp...
813
  	evdev->exist = true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814
  	evdev->minor = minor;
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
815

a7097ff89   Dmitry Torokhov   Input: make sure ...
816
  	evdev->handle.dev = input_get_device(dev);
3d5cb60ef   Thadeu Lima de Souza Cascardo   Input: simplify n...
817
  	evdev->handle.name = dev_name(&evdev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818
819
  	evdev->handle.handler = handler;
  	evdev->handle.private = evdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
820

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
821
  	evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
9657d75c5   Dmitry Torokhov   Input: convert fr...
822
823
  	evdev->dev.class = &input_class;
  	evdev->dev.parent = &dev->dev;
9657d75c5   Dmitry Torokhov   Input: convert fr...
824
825
  	evdev->dev.release = evdev_free;
  	device_initialize(&evdev->dev);
5b2a08262   Dmitry Torokhov   Input: rework han...
826

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
827
  	error = input_register_handle(&evdev->handle);
5b2a08262   Dmitry Torokhov   Input: rework han...
828
  	if (error)
9657d75c5   Dmitry Torokhov   Input: convert fr...
829
  		goto err_free_evdev;
5b2a08262   Dmitry Torokhov   Input: rework han...
830

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
831
832
833
834
835
  	error = evdev_install_chrdev(evdev);
  	if (error)
  		goto err_unregister_handle;
  
  	error = device_add(&evdev->dev);
5b2a08262   Dmitry Torokhov   Input: rework han...
836
  	if (error)
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
837
  		goto err_cleanup_evdev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838

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

6addb1d6d   Dmitry Torokhov   Input: evdev - im...
841
842
843
844
   err_cleanup_evdev:
  	evdev_cleanup(evdev);
   err_unregister_handle:
  	input_unregister_handle(&evdev->handle);
5b2a08262   Dmitry Torokhov   Input: rework han...
845
   err_free_evdev:
9657d75c5   Dmitry Torokhov   Input: convert fr...
846
  	put_device(&evdev->dev);
5b2a08262   Dmitry Torokhov   Input: rework han...
847
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
849
850
851
852
  }
  
  static void evdev_disconnect(struct input_handle *handle)
  {
  	struct evdev *evdev = handle->private;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
853

9657d75c5   Dmitry Torokhov   Input: convert fr...
854
  	device_del(&evdev->dev);
6addb1d6d   Dmitry Torokhov   Input: evdev - im...
855
856
  	evdev_cleanup(evdev);
  	input_unregister_handle(handle);
9657d75c5   Dmitry Torokhov   Input: convert fr...
857
  	put_device(&evdev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858
  }
66e661188   Dmitry Torokhov   Input: constify i...
859
  static const struct input_device_id evdev_ids[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860
861
862
863
864
865
866
  	{ .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...
867
868
869
870
871
872
873
  	.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
874
875
876
877
  };
  
  static int __init evdev_init(void)
  {
4263cf0fa   Dmitry Torokhov   Input: make input...
878
  	return input_register_handler(&evdev_handler);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879
880
881
882
883
884
885
886
887
888
889
890
891
  }
  
  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");