Blame view

drivers/input/evdev.c 17 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  /*
   * 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
  #define EVDEV_BUFFER_SIZE	64
  
  #include <linux/poll.h>
  #include <linux/slab.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/input.h>
  #include <linux/major.h>
  #include <linux/smp_lock.h>
  #include <linux/device.h>
52658bb68   Juergen Kreileder   Input: Add suppor...
23
  #include <linux/compat.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  
  struct evdev {
  	int exist;
  	int open;
  	int minor;
  	char name[16];
  	struct input_handle handle;
  	wait_queue_head_t wait;
  	struct evdev_list *grab;
  	struct list_head list;
  };
  
  struct evdev_list {
  	struct input_event buffer[EVDEV_BUFFER_SIZE];
  	int head;
  	int tail;
  	struct fasync_struct *fasync;
  	struct evdev *evdev;
  	struct list_head node;
  };
  
  static struct evdev *evdev_table[EVDEV_MINORS];
  
  static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
  {
  	struct evdev *evdev = handle->private;
  	struct evdev_list *list;
  
  	if (evdev->grab) {
  		list = evdev->grab;
  
  		do_gettimeofday(&list->buffer[list->head].time);
  		list->buffer[list->head].type = type;
  		list->buffer[list->head].code = code;
  		list->buffer[list->head].value = value;
  		list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
  
  		kill_fasync(&list->fasync, SIGIO, POLL_IN);
  	} else
  		list_for_each_entry(list, &evdev->list, node) {
  
  			do_gettimeofday(&list->buffer[list->head].time);
  			list->buffer[list->head].type = type;
  			list->buffer[list->head].code = code;
  			list->buffer[list->head].value = value;
  			list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1);
  
  			kill_fasync(&list->fasync, SIGIO, POLL_IN);
  		}
  
  	wake_up_interruptible(&evdev->wait);
  }
  
  static int evdev_fasync(int fd, struct file *file, int on)
  {
  	int retval;
  	struct evdev_list *list = file->private_data;
1e0afb288   Dmitry Torokhov   Input: fix format...
81

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
  	retval = fasync_helper(fd, file, on, &list->fasync);
1e0afb288   Dmitry Torokhov   Input: fix format...
83

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
  	return retval < 0 ? retval : 0;
  }
1e0afb288   Dmitry Torokhov   Input: fix format...
86
  static int evdev_flush(struct file *file, fl_owner_t id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
88
  {
  	struct evdev_list *list = file->private_data;
1e0afb288   Dmitry Torokhov   Input: fix format...
89
90
91
  
  	if (!list->evdev->exist)
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  	return input_flush_device(&list->evdev->handle, file);
  }
  
  static void evdev_free(struct evdev *evdev)
  {
  	evdev_table[evdev->minor] = NULL;
  	kfree(evdev);
  }
  
  static int evdev_release(struct inode * inode, struct file * file)
  {
  	struct evdev_list *list = file->private_data;
  
  	if (list->evdev->grab == list) {
  		input_release_device(&list->evdev->handle);
  		list->evdev->grab = NULL;
  	}
  
  	evdev_fasync(-1, file, 0);
  	list_del(&list->node);
  
  	if (!--list->evdev->open) {
  		if (list->evdev->exist)
  			input_close_device(&list->evdev->handle);
  		else
  			evdev_free(list->evdev);
  	}
  
  	kfree(list);
  	return 0;
  }
  
  static int evdev_open(struct inode * inode, struct file * file)
  {
  	struct evdev_list *list;
  	int i = iminor(inode) - EVDEV_MINOR_BASE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
  
  	if (i >= EVDEV_MINORS || !evdev_table[i] || !evdev_table[i]->exist)
  		return -ENODEV;
b39787a97   Eric Sesterhenn   Input: use kzallo...
131
  	if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
135
136
137
138
139
140
141
142
143
  
  	list->evdev = evdev_table[i];
  	list_add_tail(&list->node, &evdev_table[i]->list);
  	file->private_data = list;
  
  	if (!list->evdev->open++)
  		if (list->evdev->exist)
  			input_open_device(&list->evdev->handle);
  
  	return 0;
  }
52658bb68   Juergen Kreileder   Input: Add suppor...
144
  #ifdef CONFIG_COMPAT
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
145

52658bb68   Juergen Kreileder   Input: Add suppor...
146
147
148
149
150
151
  struct input_event_compat {
  	struct compat_timeval time;
  	__u16 type;
  	__u16 code;
  	__s32 value;
  };
bf2fcc6fd   Andi Kleen   [PATCH] x86_64: I...
152
153
  /* Note to the author of this code: did it ever occur to
     you why the ifdefs are needed? Think about it again. -AK */
52658bb68   Juergen Kreileder   Input: Add suppor...
154
  #ifdef CONFIG_X86_64
bf2fcc6fd   Andi Kleen   [PATCH] x86_64: I...
155
  #  define COMPAT_TEST is_compat_task()
52658bb68   Juergen Kreileder   Input: Add suppor...
156
  #elif defined(CONFIG_IA64)
6450578f3   Al Viro   [PATCH] ia64: tas...
157
  #  define COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current))
347a8dc3b   Martin Schwidefsky   [PATCH] s390: cle...
158
  #elif defined(CONFIG_S390)
52658bb68   Juergen Kreileder   Input: Add suppor...
159
  #  define COMPAT_TEST test_thread_flag(TIF_31BIT)
59df6bbf3   Ralf Baechle   [PATCH] mips: klu...
160
161
  #elif defined(CONFIG_MIPS)
  #  define COMPAT_TEST (current->thread.mflags & MF_32BIT_ADDR)
52658bb68   Juergen Kreileder   Input: Add suppor...
162
163
164
  #else
  #  define COMPAT_TEST test_thread_flag(TIF_32BIT)
  #endif
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
165
  static inline size_t evdev_event_size(void)
52658bb68   Juergen Kreileder   Input: Add suppor...
166
  {
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
167
168
169
  	return COMPAT_TEST ?
  		sizeof(struct input_event_compat) : sizeof(struct input_event);
  }
52658bb68   Juergen Kreileder   Input: Add suppor...
170

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
  {
  	if (COMPAT_TEST) {
  		struct input_event_compat compat_event;
  
  		if (copy_from_user(&compat_event, buffer, sizeof(struct input_event_compat)))
  			return -EFAULT;
  
  		event->time.tv_sec = compat_event.time.tv_sec;
  		event->time.tv_usec = compat_event.time.tv_usec;
  		event->type = compat_event.type;
  		event->code = compat_event.code;
  		event->value = compat_event.value;
  
  	} else {
  		if (copy_from_user(event, buffer, sizeof(struct input_event)))
52658bb68   Juergen Kreileder   Input: Add suppor...
187
  			return -EFAULT;
52658bb68   Juergen Kreileder   Input: Add suppor...
188
  	}
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
189
  	return 0;
52658bb68   Juergen Kreileder   Input: Add suppor...
190
  }
52658bb68   Juergen Kreileder   Input: Add suppor...
191

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
192
  static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
  {
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
194
195
  	if (COMPAT_TEST) {
  		struct input_event_compat compat_event;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
197
198
199
200
201
  		compat_event.time.tv_sec = event->time.tv_sec;
  		compat_event.time.tv_usec = event->time.tv_usec;
  		compat_event.type = event->type;
  		compat_event.code = event->code;
  		compat_event.value = event->value;
52658bb68   Juergen Kreileder   Input: Add suppor...
202

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
203
204
  		if (copy_to_user(buffer, &compat_event, sizeof(struct input_event_compat)))
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
206
207
  	} else {
  		if (copy_to_user(buffer, event, sizeof(struct input_event)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
  	}
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
210
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
  }
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
212
213
214
  #else
  
  static inline size_t evdev_event_size(void)
52658bb68   Juergen Kreileder   Input: Add suppor...
215
  {
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
216
217
  	return sizeof(struct input_event);
  }
52658bb68   Juergen Kreileder   Input: Add suppor...
218

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
219
220
221
222
  static int evdev_event_from_user(const char __user *buffer, struct input_event *event)
  {
  	if (copy_from_user(event, buffer, sizeof(struct input_event)))
  		return -EFAULT;
52658bb68   Juergen Kreileder   Input: Add suppor...
223

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
224
225
  	return 0;
  }
52658bb68   Juergen Kreileder   Input: Add suppor...
226

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
227
228
229
230
  static int evdev_event_to_user(char __user *buffer, const struct input_event *event)
  {
  	if (copy_to_user(buffer, event, sizeof(struct input_event)))
  		return -EFAULT;
52658bb68   Juergen Kreileder   Input: Add suppor...
231

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
232
233
234
235
236
237
238
239
240
241
  	return 0;
  }
  
  #endif /* CONFIG_COMPAT */
  
  static ssize_t evdev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
  {
  	struct evdev_list *list = file->private_data;
  	struct input_event event;
  	int retval = 0;
52658bb68   Juergen Kreileder   Input: Add suppor...
242
243
244
  
  	if (!list->evdev->exist)
  		return -ENODEV;
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
245
246
247
248
  	while (retval < count) {
  
  		if (evdev_event_from_user(buffer + retval, &event))
  			return -EFAULT;
0e739d287   Dmitry Torokhov   Input: introduce ...
249
  		input_inject_event(&list->evdev->handle, event.type, event.code, event.value);
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
250
  		retval += evdev_event_size();
52658bb68   Juergen Kreileder   Input: Add suppor...
251
252
253
254
  	}
  
  	return retval;
  }
52658bb68   Juergen Kreileder   Input: Add suppor...
255

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
258
259
  static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
  {
  	struct evdev_list *list = file->private_data;
  	int retval;
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
260
  	if (count < evdev_event_size())
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
265
266
267
268
269
270
271
272
273
  		return -EINVAL;
  
  	if (list->head == list->tail && list->evdev->exist && (file->f_flags & O_NONBLOCK))
  		return -EAGAIN;
  
  	retval = wait_event_interruptible(list->evdev->wait,
  		list->head != list->tail || (!list->evdev->exist));
  
  	if (retval)
  		return retval;
  
  	if (!list->evdev->exist)
  		return -ENODEV;
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
274
275
276
277
278
279
  	while (list->head != list->tail && retval + evdev_event_size() <= count) {
  
  		struct input_event *event = (struct input_event *) list->buffer + list->tail;
  
  		if (evdev_event_to_user(buffer + retval, event))
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  		list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
281
  		retval += evdev_event_size();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
286
287
288
289
290
  	}
  
  	return retval;
  }
  
  /* No kernel lock - fine */
  static unsigned int evdev_poll(struct file *file, poll_table *wait)
  {
  	struct evdev_list *list = file->private_data;
1e0afb288   Dmitry Torokhov   Input: fix format...
291

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
295
  	poll_wait(file, &list->evdev->wait, wait);
  	return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) |
  		(list->evdev->exist ? 0 : (POLLHUP | POLLERR));
  }
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
  #ifdef CONFIG_COMPAT
  
  #define BITS_PER_LONG_COMPAT (sizeof(compat_long_t) * 8)
  #define NBITS_COMPAT(x) ((((x) - 1) / BITS_PER_LONG_COMPAT) + 1)
  
  #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) {
  		len = NBITS_COMPAT(maxbit) * sizeof(compat_long_t);
  		if (len < maxlen)
  			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 {
  		len = NBITS(maxbit) * sizeof(long);
  		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 ?
  			NBITS_COMPAT(maxbit) * sizeof(compat_long_t) :
  			NBITS(maxbit) * sizeof(long);
  
  	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)
  {
  	int len = NBITS(maxbit) * sizeof(long);
  
  	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;
  }
  
  static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
  				void __user *p, int compat_mode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
376
377
378
379
  {
  	struct evdev_list *list = file->private_data;
  	struct evdev *evdev = list->evdev;
  	struct input_dev *dev = evdev->handle.dev;
  	struct input_absinfo abs;
509ca1a93   Anssi Hannula   Input: implement ...
380
  	struct ff_effect effect;
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
381
  	int __user *ip = (int __user *)p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
  	int i, t, u, v;
509ca1a93   Anssi Hannula   Input: implement ...
383
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
385
386
  	if (!evdev->exist)
  		return -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
388
389
390
391
392
393
  
  	switch (cmd) {
  
  		case EVIOCGVERSION:
  			return put_user(EV_VERSION, ip);
  
  		case EVIOCGID:
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
394
395
  			if (copy_to_user(p, &dev->id, sizeof(struct input_id)))
  				return -EFAULT;
08791e5cf   Dmitry Torokhov   Input: ressurect ...
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  			return 0;
  
  		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;
  
  		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;
0e739d287   Dmitry Torokhov   Input: introduce ...
414
415
  			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...
416
417
  
  			return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
  
  		case EVIOCGKEYCODE:
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
420
421
  			if (get_user(t, ip))
  				return -EFAULT;
c8e4c7727   Marvin Raaijmakers   Input: add getkey...
422
423
424
425
426
427
  
  			error = dev->getkeycode(dev, t, &v);
  			if (error)
  				return error;
  
  			if (put_user(v, ip + 1))
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
428
  				return -EFAULT;
c8e4c7727   Marvin Raaijmakers   Input: add getkey...
429

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
431
432
  			return 0;
  
  		case EVIOCSKEYCODE:
c8e4c7727   Marvin Raaijmakers   Input: add getkey...
433
  			if (get_user(t, ip) || get_user(v, ip + 1))
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
434
  				return -EFAULT;
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
435

c8e4c7727   Marvin Raaijmakers   Input: add getkey...
436
  			return dev->setkeycode(dev, t, v);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
438
  
  		case EVIOCSFF:
509ca1a93   Anssi Hannula   Input: implement ...
439
440
  			if (copy_from_user(&effect, p, sizeof(effect)))
  				return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441

509ca1a93   Anssi Hannula   Input: implement ...
442
  			error = input_ff_upload(dev, &effect, file);
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
443

509ca1a93   Anssi Hannula   Input: implement ...
444
445
446
447
448
449
450
  			if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
  				return -EFAULT;
  
  			return error;
  
  		case EVIOCRMFF:
  			return input_ff_erase(dev, (int)(unsigned long) p, file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
  
  		case EVIOCGEFFECTS:
509ca1a93   Anssi Hannula   Input: implement ...
453
454
  			i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0;
  			if (put_user(i, ip))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
456
457
458
  				return -EFAULT;
  			return 0;
  
  		case EVIOCGRAB:
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
459
  			if (p) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
  				if (evdev->grab)
  					return -EBUSY;
  				if (input_grab_device(&evdev->handle))
  					return -EBUSY;
  				evdev->grab = list;
  				return 0;
  			} else {
  				if (evdev->grab != list)
  					return -EINVAL;
  				input_release_device(&evdev->handle);
  				evdev->grab = NULL;
  				return 0;
  			}
  
  		default:
41e979f82   Vojtech Pavlik   Input: Make EVIOS...
475
  			if (_IOC_TYPE(cmd) != 'E')
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
  				return -EINVAL;
41e979f82   Vojtech Pavlik   Input: Make EVIOS...
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
  			if (_IOC_DIR(cmd) == _IOC_READ) {
  
  				if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) {
  
  					long *bits;
  					int len;
  
  					switch (_IOC_NR(cmd) & EV_MAX) {
  						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;
315810668   Richard Purdie   [PATCH] Input: Ad...
493
  						case EV_SW:  bits = dev->swbit;  len = SW_MAX;  break;
41e979f82   Vojtech Pavlik   Input: Make EVIOS...
494
495
  						default: return -EINVAL;
  					}
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
496
  					return bits_to_user(bits, len, _IOC_SIZE(cmd), p, compat_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
  				}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
499
500
501
  				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGKEY(0)))
  					return bits_to_user(dev->key, KEY_MAX, _IOC_SIZE(cmd),
  							    p, compat_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
503
504
505
  				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGLED(0)))
  					return bits_to_user(dev->led, LED_MAX, _IOC_SIZE(cmd),
  							    p, compat_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
506

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
507
508
509
  				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSND(0)))
  					return bits_to_user(dev->snd, SND_MAX, _IOC_SIZE(cmd),
  							    p, compat_mode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
511
512
513
  				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGSW(0)))
  					return bits_to_user(dev->sw, SW_MAX, _IOC_SIZE(cmd),
  							    p, compat_mode);
315810668   Richard Purdie   [PATCH] Input: Ad...
514

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
515
516
  				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
  					return str_to_user(dev->name, _IOC_SIZE(cmd), p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
518
519
  				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
  					return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
521
522
  				if (_IOC_NR(cmd) == _IOC_NR(EVIOCGUNIQ(0)))
  					return str_to_user(dev->uniq, _IOC_SIZE(cmd), p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523

41e979f82   Vojtech Pavlik   Input: Make EVIOS...
524
  				if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525

41e979f82   Vojtech Pavlik   Input: Make EVIOS...
526
  					int t = _IOC_NR(cmd) & ABS_MAX;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527

41e979f82   Vojtech Pavlik   Input: Make EVIOS...
528
529
530
531
532
  					abs.value = dev->abs[t];
  					abs.minimum = dev->absmin[t];
  					abs.maximum = dev->absmax[t];
  					abs.fuzz = dev->absfuzz[t];
  					abs.flat = dev->absflat[t];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533

41e979f82   Vojtech Pavlik   Input: Make EVIOS...
534
535
536
537
538
  					if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
  						return -EFAULT;
  
  					return 0;
  				}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
539

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
  			}
41e979f82   Vojtech Pavlik   Input: Make EVIOS...
541
  			if (_IOC_DIR(cmd) == _IOC_WRITE) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
542

41e979f82   Vojtech Pavlik   Input: Make EVIOS...
543
  				if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544

41e979f82   Vojtech Pavlik   Input: Make EVIOS...
545
546
547
548
  					int t = _IOC_NR(cmd) & ABS_MAX;
  
  					if (copy_from_user(&abs, p, sizeof(struct input_absinfo)))
  						return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
549

41e979f82   Vojtech Pavlik   Input: Make EVIOS...
550
551
552
553
554
  					dev->abs[t] = abs.value;
  					dev->absmin[t] = abs.minimum;
  					dev->absmax[t] = abs.maximum;
  					dev->absfuzz[t] = abs.fuzz;
  					dev->absflat[t] = abs.flat;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
555

41e979f82   Vojtech Pavlik   Input: Make EVIOS...
556
557
  					return 0;
  				}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
560
561
  	}
  	return -EINVAL;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
563
564
565
566
  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...
567

3a51f7c40   Dmitry Torokhov   Input: evdev - co...
568
  #ifdef CONFIG_COMPAT
52658bb68   Juergen Kreileder   Input: Add suppor...
569
570
  static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
  {
3a51f7c40   Dmitry Torokhov   Input: evdev - co...
571
  	return evdev_ioctl_handler(file, cmd, compat_ptr(arg), 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
  }
52658bb68   Juergen Kreileder   Input: Add suppor...
573
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
574

66e661188   Dmitry Torokhov   Input: constify i...
575
  static const struct file_operations evdev_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
577
578
579
580
581
  	.owner =	THIS_MODULE,
  	.read =		evdev_read,
  	.write =	evdev_write,
  	.poll =		evdev_poll,
  	.open =		evdev_open,
  	.release =	evdev_release,
52658bb68   Juergen Kreileder   Input: Add suppor...
582
583
584
585
  	.unlocked_ioctl = evdev_ioctl,
  #ifdef CONFIG_COMPAT
  	.compat_ioctl =	evdev_ioctl_compat,
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
587
588
  	.fasync =	evdev_fasync,
  	.flush =	evdev_flush
  };
5b2a08262   Dmitry Torokhov   Input: rework han...
589
590
  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
591
592
  {
  	struct evdev *evdev;
c9bcd582d   Greg Kroah-Hartman   [PATCH] INPUT: Cr...
593
  	struct class_device *cdev;
5b2a08262   Dmitry Torokhov   Input: rework han...
594
  	dev_t devt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
595
  	int minor;
5b2a08262   Dmitry Torokhov   Input: rework han...
596
  	int error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597
598
599
600
601
  
  	for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
  	if (minor == EVDEV_MINORS) {
  		printk(KERN_ERR "evdev: no more free evdev devices
  ");
5b2a08262   Dmitry Torokhov   Input: rework han...
602
  		return -ENFILE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
  	}
5b2a08262   Dmitry Torokhov   Input: rework han...
604
605
606
  	evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
  	if (!evdev)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
608
609
610
611
612
613
614
615
616
617
618
619
  
  	INIT_LIST_HEAD(&evdev->list);
  	init_waitqueue_head(&evdev->wait);
  
  	evdev->exist = 1;
  	evdev->minor = minor;
  	evdev->handle.dev = dev;
  	evdev->handle.name = evdev->name;
  	evdev->handle.handler = handler;
  	evdev->handle.private = evdev;
  	sprintf(evdev->name, "event%d", minor);
  
  	evdev_table[minor] = evdev;
5b2a08262   Dmitry Torokhov   Input: rework han...
620
621
622
623
624
625
626
627
  	devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
  
  	cdev = class_device_create(&input_class, &dev->cdev, devt,
  				   dev->cdev.dev, evdev->name);
  	if (IS_ERR(cdev)) {
  		error = PTR_ERR(cdev);
  		goto err_free_evdev;
  	}
c9bcd582d   Greg Kroah-Hartman   [PATCH] INPUT: Cr...
628
629
  
  	/* temporary symlink to keep userspace happy */
5b2a08262   Dmitry Torokhov   Input: rework han...
630
631
632
633
634
635
636
637
  	error = sysfs_create_link(&input_class.subsys.kset.kobj,
  				  &cdev->kobj, evdev->name);
  	if (error)
  		goto err_cdev_destroy;
  
  	error = input_register_handle(&evdev->handle);
  	if (error)
  		goto err_remove_link;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638

5b2a08262   Dmitry Torokhov   Input: rework han...
639
640
641
642
643
644
645
646
647
648
  	return 0;
  
   err_remove_link:
  	sysfs_remove_link(&input_class.subsys.kset.kobj, evdev->name);
   err_cdev_destroy:
  	class_device_destroy(&input_class, devt);
   err_free_evdev:
  	kfree(evdev);
  	evdev_table[minor] = NULL;
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649
650
651
652
653
654
  }
  
  static void evdev_disconnect(struct input_handle *handle)
  {
  	struct evdev *evdev = handle->private;
  	struct evdev_list *list;
5b2a08262   Dmitry Torokhov   Input: rework han...
655
  	input_unregister_handle(handle);
c9bcd582d   Greg Kroah-Hartman   [PATCH] INPUT: Cr...
656
  	sysfs_remove_link(&input_class.subsys.kset.kobj, evdev->name);
ea9f240bd   Greg Kroah-Hartman   [PATCH] INPUT: re...
657
  	class_device_destroy(&input_class,
1235686f6   Greg Kroah-Hartman   [PATCH] INPUT: mo...
658
  			MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + evdev->minor));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
659
660
661
  	evdev->exist = 0;
  
  	if (evdev->open) {
509ca1a93   Anssi Hannula   Input: implement ...
662
  		input_flush_device(handle, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
663
664
665
666
667
668
669
  		input_close_device(handle);
  		wake_up_interruptible(&evdev->wait);
  		list_for_each_entry(list, &evdev->list, node)
  			kill_fasync(&list->fasync, SIGIO, POLL_HUP);
  	} else
  		evdev_free(evdev);
  }
66e661188   Dmitry Torokhov   Input: constify i...
670
  static const struct input_device_id evdev_ids[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
  	{ .driver_info = 1 },	/* Matches all devices */
  	{ },			/* Terminating zero entry */
  };
  
  MODULE_DEVICE_TABLE(input, evdev_ids);
  
  static struct input_handler evdev_handler = {
  	.event =	evdev_event,
  	.connect =	evdev_connect,
  	.disconnect =	evdev_disconnect,
  	.fops =		&evdev_fops,
  	.minor =	EVDEV_MINOR_BASE,
  	.name =		"evdev",
  	.id_table =	evdev_ids,
  };
  
  static int __init evdev_init(void)
  {
4263cf0fa   Dmitry Torokhov   Input: make input...
689
  	return input_register_handler(&evdev_handler);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
690
691
692
693
694
695
696
697
698
699
700
701
702
  }
  
  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");