diff options
author | Philip Langdale <philipl@overt.org> | 2008-10-16 22:31:42 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-10-27 22:03:42 -0400 |
commit | 2d56f3a32c0e62f99c043d2579840f9731fe5855 (patch) | |
tree | 3bf1539bbed43e5309dcfd634f202bb9ad0f11b2 | |
parent | 49fdf6785fd660e18a1eb4588928f47e9fa29a9a (diff) |
Input: refactor evdev 32bit compat to be shareable with uinput
Currently, evdev has working 32bit compatibility and uinput does not. uinput
needs the input_event code that evdev uses, so let's refactor it so it can
be shared.
[dtor@mail.ru: add fix for force feedback compat issues]
Signed-off-by: Philip Langdale <philipl@overt.org>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r-- | drivers/input/Makefile | 2 | ||||
-rw-r--r-- | drivers/input/evdev.c | 197 | ||||
-rw-r--r-- | drivers/input/input-compat.c | 135 | ||||
-rw-r--r-- | drivers/input/input-compat.h | 94 | ||||
-rw-r--r-- | drivers/input/misc/uinput.c | 172 |
5 files changed, 383 insertions, 217 deletions
diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 98c4f9a77876..4c9c745a7020 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile | |||
@@ -5,7 +5,7 @@ | |||
5 | # Each configuration option enables a list of files. | 5 | # Each configuration option enables a list of files. |
6 | 6 | ||
7 | obj-$(CONFIG_INPUT) += input-core.o | 7 | obj-$(CONFIG_INPUT) += input-core.o |
8 | input-core-objs := input.o ff-core.o | 8 | input-core-objs := input.o input-compat.o ff-core.o |
9 | 9 | ||
10 | obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o | 10 | obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o |
11 | obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o | 11 | obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o |
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 3524bef62be6..377b2007377e 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c | |||
@@ -19,7 +19,7 @@ | |||
19 | #include <linux/input.h> | 19 | #include <linux/input.h> |
20 | #include <linux/major.h> | 20 | #include <linux/major.h> |
21 | #include <linux/device.h> | 21 | #include <linux/device.h> |
22 | #include <linux/compat.h> | 22 | #include "input-compat.h" |
23 | 23 | ||
24 | struct evdev { | 24 | struct evdev { |
25 | int exist; | 25 | int exist; |
@@ -291,187 +291,6 @@ static int evdev_open(struct inode *inode, struct file *file) | |||
291 | return error; | 291 | return error; |
292 | } | 292 | } |
293 | 293 | ||
294 | #ifdef CONFIG_COMPAT | ||
295 | |||
296 | struct input_event_compat { | ||
297 | struct compat_timeval time; | ||
298 | __u16 type; | ||
299 | __u16 code; | ||
300 | __s32 value; | ||
301 | }; | ||
302 | |||
303 | struct ff_periodic_effect_compat { | ||
304 | __u16 waveform; | ||
305 | __u16 period; | ||
306 | __s16 magnitude; | ||
307 | __s16 offset; | ||
308 | __u16 phase; | ||
309 | |||
310 | struct ff_envelope envelope; | ||
311 | |||
312 | __u32 custom_len; | ||
313 | compat_uptr_t custom_data; | ||
314 | }; | ||
315 | |||
316 | struct ff_effect_compat { | ||
317 | __u16 type; | ||
318 | __s16 id; | ||
319 | __u16 direction; | ||
320 | struct ff_trigger trigger; | ||
321 | struct ff_replay replay; | ||
322 | |||
323 | union { | ||
324 | struct ff_constant_effect constant; | ||
325 | struct ff_ramp_effect ramp; | ||
326 | struct ff_periodic_effect_compat periodic; | ||
327 | struct ff_condition_effect condition[2]; /* One for each axis */ | ||
328 | struct ff_rumble_effect rumble; | ||
329 | } u; | ||
330 | }; | ||
331 | |||
332 | /* Note to the author of this code: did it ever occur to | ||
333 | you why the ifdefs are needed? Think about it again. -AK */ | ||
334 | #ifdef CONFIG_X86_64 | ||
335 | # define COMPAT_TEST is_compat_task() | ||
336 | #elif defined(CONFIG_IA64) | ||
337 | # define COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current)) | ||
338 | #elif defined(CONFIG_S390) | ||
339 | # define COMPAT_TEST test_thread_flag(TIF_31BIT) | ||
340 | #elif defined(CONFIG_MIPS) | ||
341 | # define COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR) | ||
342 | #else | ||
343 | # define COMPAT_TEST test_thread_flag(TIF_32BIT) | ||
344 | #endif | ||
345 | |||
346 | static inline size_t evdev_event_size(void) | ||
347 | { | ||
348 | return COMPAT_TEST ? | ||
349 | sizeof(struct input_event_compat) : sizeof(struct input_event); | ||
350 | } | ||
351 | |||
352 | static int evdev_event_from_user(const char __user *buffer, | ||
353 | struct input_event *event) | ||
354 | { | ||
355 | if (COMPAT_TEST) { | ||
356 | struct input_event_compat compat_event; | ||
357 | |||
358 | if (copy_from_user(&compat_event, buffer, | ||
359 | sizeof(struct input_event_compat))) | ||
360 | return -EFAULT; | ||
361 | |||
362 | event->time.tv_sec = compat_event.time.tv_sec; | ||
363 | event->time.tv_usec = compat_event.time.tv_usec; | ||
364 | event->type = compat_event.type; | ||
365 | event->code = compat_event.code; | ||
366 | event->value = compat_event.value; | ||
367 | |||
368 | } else { | ||
369 | if (copy_from_user(event, buffer, sizeof(struct input_event))) | ||
370 | return -EFAULT; | ||
371 | } | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | static int evdev_event_to_user(char __user *buffer, | ||
377 | const struct input_event *event) | ||
378 | { | ||
379 | if (COMPAT_TEST) { | ||
380 | struct input_event_compat compat_event; | ||
381 | |||
382 | compat_event.time.tv_sec = event->time.tv_sec; | ||
383 | compat_event.time.tv_usec = event->time.tv_usec; | ||
384 | compat_event.type = event->type; | ||
385 | compat_event.code = event->code; | ||
386 | compat_event.value = event->value; | ||
387 | |||
388 | if (copy_to_user(buffer, &compat_event, | ||
389 | sizeof(struct input_event_compat))) | ||
390 | return -EFAULT; | ||
391 | |||
392 | } else { | ||
393 | if (copy_to_user(buffer, event, sizeof(struct input_event))) | ||
394 | return -EFAULT; | ||
395 | } | ||
396 | |||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | static int evdev_ff_effect_from_user(const char __user *buffer, size_t size, | ||
401 | struct ff_effect *effect) | ||
402 | { | ||
403 | if (COMPAT_TEST) { | ||
404 | struct ff_effect_compat *compat_effect; | ||
405 | |||
406 | if (size != sizeof(struct ff_effect_compat)) | ||
407 | return -EINVAL; | ||
408 | |||
409 | /* | ||
410 | * It so happens that the pointer which needs to be changed | ||
411 | * is the last field in the structure, so we can copy the | ||
412 | * whole thing and replace just the pointer. | ||
413 | */ | ||
414 | |||
415 | compat_effect = (struct ff_effect_compat *)effect; | ||
416 | |||
417 | if (copy_from_user(compat_effect, buffer, | ||
418 | sizeof(struct ff_effect_compat))) | ||
419 | return -EFAULT; | ||
420 | |||
421 | if (compat_effect->type == FF_PERIODIC && | ||
422 | compat_effect->u.periodic.waveform == FF_CUSTOM) | ||
423 | effect->u.periodic.custom_data = | ||
424 | compat_ptr(compat_effect->u.periodic.custom_data); | ||
425 | } else { | ||
426 | if (size != sizeof(struct ff_effect)) | ||
427 | return -EINVAL; | ||
428 | |||
429 | if (copy_from_user(effect, buffer, sizeof(struct ff_effect))) | ||
430 | return -EFAULT; | ||
431 | } | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | #else | ||
437 | |||
438 | static inline size_t evdev_event_size(void) | ||
439 | { | ||
440 | return sizeof(struct input_event); | ||
441 | } | ||
442 | |||
443 | static int evdev_event_from_user(const char __user *buffer, | ||
444 | struct input_event *event) | ||
445 | { | ||
446 | if (copy_from_user(event, buffer, sizeof(struct input_event))) | ||
447 | return -EFAULT; | ||
448 | |||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static int evdev_event_to_user(char __user *buffer, | ||
453 | const struct input_event *event) | ||
454 | { | ||
455 | if (copy_to_user(buffer, event, sizeof(struct input_event))) | ||
456 | return -EFAULT; | ||
457 | |||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | static int evdev_ff_effect_from_user(const char __user *buffer, size_t size, | ||
462 | struct ff_effect *effect) | ||
463 | { | ||
464 | if (size != sizeof(struct ff_effect)) | ||
465 | return -EINVAL; | ||
466 | |||
467 | if (copy_from_user(effect, buffer, sizeof(struct ff_effect))) | ||
468 | return -EFAULT; | ||
469 | |||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | #endif /* CONFIG_COMPAT */ | ||
474 | |||
475 | static ssize_t evdev_write(struct file *file, const char __user *buffer, | 294 | static ssize_t evdev_write(struct file *file, const char __user *buffer, |
476 | size_t count, loff_t *ppos) | 295 | size_t count, loff_t *ppos) |
477 | { | 296 | { |
@@ -491,14 +310,14 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer, | |||
491 | 310 | ||
492 | while (retval < count) { | 311 | while (retval < count) { |
493 | 312 | ||
494 | if (evdev_event_from_user(buffer + retval, &event)) { | 313 | if (input_event_from_user(buffer + retval, &event)) { |
495 | retval = -EFAULT; | 314 | retval = -EFAULT; |
496 | goto out; | 315 | goto out; |
497 | } | 316 | } |
498 | 317 | ||
499 | input_inject_event(&evdev->handle, | 318 | input_inject_event(&evdev->handle, |
500 | event.type, event.code, event.value); | 319 | event.type, event.code, event.value); |
501 | retval += evdev_event_size(); | 320 | retval += input_event_size(); |
502 | } | 321 | } |
503 | 322 | ||
504 | out: | 323 | out: |
@@ -532,7 +351,7 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, | |||
532 | struct input_event event; | 351 | struct input_event event; |
533 | int retval; | 352 | int retval; |
534 | 353 | ||
535 | if (count < evdev_event_size()) | 354 | if (count < input_event_size()) |
536 | return -EINVAL; | 355 | return -EINVAL; |
537 | 356 | ||
538 | if (client->head == client->tail && evdev->exist && | 357 | if (client->head == client->tail && evdev->exist && |
@@ -547,13 +366,13 @@ static ssize_t evdev_read(struct file *file, char __user *buffer, | |||
547 | if (!evdev->exist) | 366 | if (!evdev->exist) |
548 | return -ENODEV; | 367 | return -ENODEV; |
549 | 368 | ||
550 | while (retval + evdev_event_size() <= count && | 369 | while (retval + input_event_size() <= count && |
551 | evdev_fetch_next_event(client, &event)) { | 370 | evdev_fetch_next_event(client, &event)) { |
552 | 371 | ||
553 | if (evdev_event_to_user(buffer + retval, &event)) | 372 | if (input_event_to_user(buffer + retval, &event)) |
554 | return -EFAULT; | 373 | return -EFAULT; |
555 | 374 | ||
556 | retval += evdev_event_size(); | 375 | retval += input_event_size(); |
557 | } | 376 | } |
558 | 377 | ||
559 | return retval; | 378 | return retval; |
@@ -824,7 +643,7 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, | |||
824 | 643 | ||
825 | if (_IOC_NR(cmd) == _IOC_NR(EVIOCSFF)) { | 644 | if (_IOC_NR(cmd) == _IOC_NR(EVIOCSFF)) { |
826 | 645 | ||
827 | if (evdev_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect)) | 646 | if (input_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect)) |
828 | return -EFAULT; | 647 | return -EFAULT; |
829 | 648 | ||
830 | error = input_ff_upload(dev, &effect, file); | 649 | error = input_ff_upload(dev, &effect, file); |
diff --git a/drivers/input/input-compat.c b/drivers/input/input-compat.c new file mode 100644 index 000000000000..1accb89ae66f --- /dev/null +++ b/drivers/input/input-compat.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* | ||
2 | * 32bit compatibility wrappers for the input subsystem. | ||
3 | * | ||
4 | * Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License version 2 as published by | ||
8 | * the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <asm/uaccess.h> | ||
12 | #include "input-compat.h" | ||
13 | |||
14 | #ifdef CONFIG_COMPAT | ||
15 | |||
16 | int input_event_from_user(const char __user *buffer, | ||
17 | struct input_event *event) | ||
18 | { | ||
19 | if (INPUT_COMPAT_TEST) { | ||
20 | struct input_event_compat compat_event; | ||
21 | |||
22 | if (copy_from_user(&compat_event, buffer, | ||
23 | sizeof(struct input_event_compat))) | ||
24 | return -EFAULT; | ||
25 | |||
26 | event->time.tv_sec = compat_event.time.tv_sec; | ||
27 | event->time.tv_usec = compat_event.time.tv_usec; | ||
28 | event->type = compat_event.type; | ||
29 | event->code = compat_event.code; | ||
30 | event->value = compat_event.value; | ||
31 | |||
32 | } else { | ||
33 | if (copy_from_user(event, buffer, sizeof(struct input_event))) | ||
34 | return -EFAULT; | ||
35 | } | ||
36 | |||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | int input_event_to_user(char __user *buffer, | ||
41 | const struct input_event *event) | ||
42 | { | ||
43 | if (INPUT_COMPAT_TEST) { | ||
44 | struct input_event_compat compat_event; | ||
45 | |||
46 | compat_event.time.tv_sec = event->time.tv_sec; | ||
47 | compat_event.time.tv_usec = event->time.tv_usec; | ||
48 | compat_event.type = event->type; | ||
49 | compat_event.code = event->code; | ||
50 | compat_event.value = event->value; | ||
51 | |||
52 | if (copy_to_user(buffer, &compat_event, | ||
53 | sizeof(struct input_event_compat))) | ||
54 | return -EFAULT; | ||
55 | |||
56 | } else { | ||
57 | if (copy_to_user(buffer, event, sizeof(struct input_event))) | ||
58 | return -EFAULT; | ||
59 | } | ||
60 | |||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | int input_ff_effect_from_user(const char __user *buffer, size_t size, | ||
65 | struct ff_effect *effect) | ||
66 | { | ||
67 | if (INPUT_COMPAT_TEST) { | ||
68 | struct ff_effect_compat *compat_effect; | ||
69 | |||
70 | if (size != sizeof(struct ff_effect_compat)) | ||
71 | return -EINVAL; | ||
72 | |||
73 | /* | ||
74 | * It so happens that the pointer which needs to be changed | ||
75 | * is the last field in the structure, so we can retrieve the | ||
76 | * whole thing and replace just the pointer. | ||
77 | */ | ||
78 | compat_effect = (struct ff_effect_compat *)effect; | ||
79 | |||
80 | if (copy_from_user(compat_effect, buffer, | ||
81 | sizeof(struct ff_effect_compat))) | ||
82 | return -EFAULT; | ||
83 | |||
84 | if (compat_effect->type == FF_PERIODIC && | ||
85 | compat_effect->u.periodic.waveform == FF_CUSTOM) | ||
86 | effect->u.periodic.custom_data = | ||
87 | compat_ptr(compat_effect->u.periodic.custom_data); | ||
88 | } else { | ||
89 | if (size != sizeof(struct ff_effect)) | ||
90 | return -EINVAL; | ||
91 | |||
92 | if (copy_from_user(effect, buffer, sizeof(struct ff_effect))) | ||
93 | return -EFAULT; | ||
94 | } | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | #else | ||
100 | |||
101 | int input_event_from_user(const char __user *buffer, | ||
102 | struct input_event *event) | ||
103 | { | ||
104 | if (copy_from_user(event, buffer, sizeof(struct input_event))) | ||
105 | return -EFAULT; | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | int input_event_to_user(char __user *buffer, | ||
111 | const struct input_event *event) | ||
112 | { | ||
113 | if (copy_to_user(buffer, event, sizeof(struct input_event))) | ||
114 | return -EFAULT; | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | int input_ff_effect_from_user(const char __user *buffer, size_t size, | ||
120 | struct ff_effect *effect) | ||
121 | { | ||
122 | if (size != sizeof(struct ff_effect)) | ||
123 | return -EINVAL; | ||
124 | |||
125 | if (copy_from_user(effect, buffer, sizeof(struct ff_effect))) | ||
126 | return -EFAULT; | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | #endif /* CONFIG_COMPAT */ | ||
132 | |||
133 | EXPORT_SYMBOL_GPL(input_event_from_user); | ||
134 | EXPORT_SYMBOL_GPL(input_event_to_user); | ||
135 | EXPORT_SYMBOL_GPL(input_ff_effect_from_user); | ||
diff --git a/drivers/input/input-compat.h b/drivers/input/input-compat.h new file mode 100644 index 000000000000..47cd9eaee66a --- /dev/null +++ b/drivers/input/input-compat.h | |||
@@ -0,0 +1,94 @@ | |||
1 | #ifndef _INPUT_COMPAT_H | ||
2 | #define _INPUT_COMPAT_H | ||
3 | |||
4 | /* | ||
5 | * 32bit compatibility wrappers for the input subsystem. | ||
6 | * | ||
7 | * Very heavily based on evdev.c - Copyright (c) 1999-2002 Vojtech Pavlik | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License version 2 as published by | ||
11 | * the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/compiler.h> | ||
15 | #include <linux/compat.h> | ||
16 | #include <linux/input.h> | ||
17 | |||
18 | #ifdef CONFIG_COMPAT | ||
19 | |||
20 | /* Note to the author of this code: did it ever occur to | ||
21 | you why the ifdefs are needed? Think about it again. -AK */ | ||
22 | #ifdef CONFIG_X86_64 | ||
23 | # define INPUT_COMPAT_TEST is_compat_task() | ||
24 | #elif defined(CONFIG_IA64) | ||
25 | # define INPUT_COMPAT_TEST IS_IA32_PROCESS(task_pt_regs(current)) | ||
26 | #elif defined(CONFIG_S390) | ||
27 | # define INPUT_COMPAT_TEST test_thread_flag(TIF_31BIT) | ||
28 | #elif defined(CONFIG_MIPS) | ||
29 | # define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR) | ||
30 | #else | ||
31 | # define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT) | ||
32 | #endif | ||
33 | |||
34 | struct input_event_compat { | ||
35 | struct compat_timeval time; | ||
36 | __u16 type; | ||
37 | __u16 code; | ||
38 | __s32 value; | ||
39 | }; | ||
40 | |||
41 | struct ff_periodic_effect_compat { | ||
42 | __u16 waveform; | ||
43 | __u16 period; | ||
44 | __s16 magnitude; | ||
45 | __s16 offset; | ||
46 | __u16 phase; | ||
47 | |||
48 | struct ff_envelope envelope; | ||
49 | |||
50 | __u32 custom_len; | ||
51 | compat_uptr_t custom_data; | ||
52 | }; | ||
53 | |||
54 | struct ff_effect_compat { | ||
55 | __u16 type; | ||
56 | __s16 id; | ||
57 | __u16 direction; | ||
58 | struct ff_trigger trigger; | ||
59 | struct ff_replay replay; | ||
60 | |||
61 | union { | ||
62 | struct ff_constant_effect constant; | ||
63 | struct ff_ramp_effect ramp; | ||
64 | struct ff_periodic_effect_compat periodic; | ||
65 | struct ff_condition_effect condition[2]; /* One for each axis */ | ||
66 | struct ff_rumble_effect rumble; | ||
67 | } u; | ||
68 | }; | ||
69 | |||
70 | static inline size_t input_event_size(void) | ||
71 | { | ||
72 | return INPUT_COMPAT_TEST ? | ||
73 | sizeof(struct input_event_compat) : sizeof(struct input_event); | ||
74 | } | ||
75 | |||
76 | #else | ||
77 | |||
78 | static inline size_t input_event_size(void) | ||
79 | { | ||
80 | return sizeof(struct input_event); | ||
81 | } | ||
82 | |||
83 | #endif /* CONFIG_COMPAT */ | ||
84 | |||
85 | int input_event_from_user(const char __user *buffer, | ||
86 | struct input_event *event); | ||
87 | |||
88 | int input_event_to_user(char __user *buffer, | ||
89 | const struct input_event *event); | ||
90 | |||
91 | int input_ff_effect_from_user(const char __user *buffer, size_t size, | ||
92 | struct ff_effect *effect); | ||
93 | |||
94 | #endif /* _INPUT_COMPAT_H */ | ||
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 223d56d5555b..46b7caeb2817 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <linux/fs.h> | 37 | #include <linux/fs.h> |
38 | #include <linux/miscdevice.h> | 38 | #include <linux/miscdevice.h> |
39 | #include <linux/uinput.h> | 39 | #include <linux/uinput.h> |
40 | #include "../input-compat.h" | ||
40 | 41 | ||
41 | static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) | 42 | static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) |
42 | { | 43 | { |
@@ -78,6 +79,7 @@ static struct uinput_request* uinput_request_find(struct uinput_device *udev, in | |||
78 | /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ | 79 | /* Find an input request, by ID. Returns NULL if the ID isn't valid. */ |
79 | if (id >= UINPUT_NUM_REQUESTS || id < 0) | 80 | if (id >= UINPUT_NUM_REQUESTS || id < 0) |
80 | return NULL; | 81 | return NULL; |
82 | |||
81 | return udev->requests[id]; | 83 | return udev->requests[id]; |
82 | } | 84 | } |
83 | 85 | ||
@@ -127,6 +129,17 @@ static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *eff | |||
127 | struct uinput_request request; | 129 | struct uinput_request request; |
128 | int retval; | 130 | int retval; |
129 | 131 | ||
132 | /* | ||
133 | * uinput driver does not currently support periodic effects with | ||
134 | * custom waveform since it does not have a way to pass buffer of | ||
135 | * samples (custom_data) to userspace. If ever there is a device | ||
136 | * supporting custom waveforms we would need to define an additional | ||
137 | * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out. | ||
138 | */ | ||
139 | if (effect->type == FF_PERIODIC && | ||
140 | effect->u.periodic.waveform == FF_CUSTOM) | ||
141 | return -EINVAL; | ||
142 | |||
130 | request.id = -1; | 143 | request.id = -1; |
131 | init_completion(&request.done); | 144 | init_completion(&request.done); |
132 | request.code = UI_FF_UPLOAD; | 145 | request.code = UI_FF_UPLOAD; |
@@ -353,15 +366,15 @@ static inline ssize_t uinput_inject_event(struct uinput_device *udev, const char | |||
353 | { | 366 | { |
354 | struct input_event ev; | 367 | struct input_event ev; |
355 | 368 | ||
356 | if (count != sizeof(struct input_event)) | 369 | if (count < input_event_size()) |
357 | return -EINVAL; | 370 | return -EINVAL; |
358 | 371 | ||
359 | if (copy_from_user(&ev, buffer, sizeof(struct input_event))) | 372 | if (input_event_from_user(buffer, &ev)) |
360 | return -EFAULT; | 373 | return -EFAULT; |
361 | 374 | ||
362 | input_event(udev->dev, ev.type, ev.code, ev.value); | 375 | input_event(udev->dev, ev.type, ev.code, ev.value); |
363 | 376 | ||
364 | return sizeof(struct input_event); | 377 | return input_event_size(); |
365 | } | 378 | } |
366 | 379 | ||
367 | static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) | 380 | static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) |
@@ -407,13 +420,13 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, | |||
407 | goto out; | 420 | goto out; |
408 | } | 421 | } |
409 | 422 | ||
410 | while (udev->head != udev->tail && retval + sizeof(struct input_event) <= count) { | 423 | while (udev->head != udev->tail && retval + input_event_size() <= count) { |
411 | if (copy_to_user(buffer + retval, &udev->buff[udev->tail], sizeof(struct input_event))) { | 424 | if (input_event_to_user(buffer + retval, &udev->buff[udev->tail])) { |
412 | retval = -EFAULT; | 425 | retval = -EFAULT; |
413 | goto out; | 426 | goto out; |
414 | } | 427 | } |
415 | udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; | 428 | udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE; |
416 | retval += sizeof(struct input_event); | 429 | retval += input_event_size(); |
417 | } | 430 | } |
418 | 431 | ||
419 | out: | 432 | out: |
@@ -444,6 +457,93 @@ static int uinput_release(struct inode *inode, struct file *file) | |||
444 | return 0; | 457 | return 0; |
445 | } | 458 | } |
446 | 459 | ||
460 | #ifdef CONFIG_COMPAT | ||
461 | struct uinput_ff_upload_compat { | ||
462 | int request_id; | ||
463 | int retval; | ||
464 | struct ff_effect_compat effect; | ||
465 | struct ff_effect_compat old; | ||
466 | }; | ||
467 | |||
468 | static int uinput_ff_upload_to_user(char __user *buffer, | ||
469 | const struct uinput_ff_upload *ff_up) | ||
470 | { | ||
471 | if (INPUT_COMPAT_TEST) { | ||
472 | struct uinput_ff_upload_compat ff_up_compat; | ||
473 | |||
474 | ff_up_compat.request_id = ff_up->request_id; | ||
475 | ff_up_compat.retval = ff_up->retval; | ||
476 | /* | ||
477 | * It so happens that the pointer that gives us the trouble | ||
478 | * is the last field in the structure. Since we don't support | ||
479 | * custom waveforms in uinput anyway we can just copy the whole | ||
480 | * thing (to the compat size) and ignore the pointer. | ||
481 | */ | ||
482 | memcpy(&ff_up_compat.effect, &ff_up->effect, | ||
483 | sizeof(struct ff_effect_compat)); | ||
484 | memcpy(&ff_up_compat.old, &ff_up->old, | ||
485 | sizeof(struct ff_effect_compat)); | ||
486 | |||
487 | if (copy_to_user(buffer, &ff_up_compat, | ||
488 | sizeof(struct uinput_ff_upload_compat))) | ||
489 | return -EFAULT; | ||
490 | } else { | ||
491 | if (copy_to_user(buffer, ff_up, | ||
492 | sizeof(struct uinput_ff_upload))) | ||
493 | return -EFAULT; | ||
494 | } | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | static int uinput_ff_upload_from_user(const char __user *buffer, | ||
500 | struct uinput_ff_upload *ff_up) | ||
501 | { | ||
502 | if (INPUT_COMPAT_TEST) { | ||
503 | struct uinput_ff_upload_compat ff_up_compat; | ||
504 | |||
505 | if (copy_from_user(&ff_up_compat, buffer, | ||
506 | sizeof(struct uinput_ff_upload_compat))) | ||
507 | return -EFAULT; | ||
508 | |||
509 | ff_up->request_id = ff_up_compat.request_id; | ||
510 | ff_up->retval = ff_up_compat.retval; | ||
511 | memcpy(&ff_up->effect, &ff_up_compat.effect, | ||
512 | sizeof(struct ff_effect_compat)); | ||
513 | memcpy(&ff_up->old, &ff_up_compat.old, | ||
514 | sizeof(struct ff_effect_compat)); | ||
515 | |||
516 | } else { | ||
517 | if (copy_from_user(ff_up, buffer, | ||
518 | sizeof(struct uinput_ff_upload))) | ||
519 | return -EFAULT; | ||
520 | } | ||
521 | |||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | #else | ||
526 | |||
527 | static int uinput_ff_upload_to_user(char __user *buffer, | ||
528 | const struct uinput_ff_upload *ff_up) | ||
529 | { | ||
530 | if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload))) | ||
531 | return -EFAULT; | ||
532 | |||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static int uinput_ff_upload_from_user(const char __user *buffer, | ||
537 | struct uinput_ff_upload *ff_up) | ||
538 | { | ||
539 | if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload))) | ||
540 | return -EFAULT; | ||
541 | |||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | #endif | ||
546 | |||
447 | #define uinput_set_bit(_arg, _bit, _max) \ | 547 | #define uinput_set_bit(_arg, _bit, _max) \ |
448 | ({ \ | 548 | ({ \ |
449 | int __ret = 0; \ | 549 | int __ret = 0; \ |
@@ -455,19 +555,17 @@ static int uinput_release(struct inode *inode, struct file *file) | |||
455 | __ret; \ | 555 | __ret; \ |
456 | }) | 556 | }) |
457 | 557 | ||
458 | static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 558 | static long uinput_ioctl_handler(struct file *file, unsigned int cmd, |
559 | unsigned long arg, void __user *p) | ||
459 | { | 560 | { |
460 | int retval; | 561 | int retval; |
461 | struct uinput_device *udev; | 562 | struct uinput_device *udev = file->private_data; |
462 | void __user *p = (void __user *)arg; | ||
463 | struct uinput_ff_upload ff_up; | 563 | struct uinput_ff_upload ff_up; |
464 | struct uinput_ff_erase ff_erase; | 564 | struct uinput_ff_erase ff_erase; |
465 | struct uinput_request *req; | 565 | struct uinput_request *req; |
466 | int length; | 566 | int length; |
467 | char *phys; | 567 | char *phys; |
468 | 568 | ||
469 | udev = file->private_data; | ||
470 | |||
471 | retval = mutex_lock_interruptible(&udev->mutex); | 569 | retval = mutex_lock_interruptible(&udev->mutex); |
472 | if (retval) | 570 | if (retval) |
473 | return retval; | 571 | return retval; |
@@ -549,26 +647,24 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
549 | break; | 647 | break; |
550 | 648 | ||
551 | case UI_BEGIN_FF_UPLOAD: | 649 | case UI_BEGIN_FF_UPLOAD: |
552 | if (copy_from_user(&ff_up, p, sizeof(ff_up))) { | 650 | retval = uinput_ff_upload_from_user(p, &ff_up); |
553 | retval = -EFAULT; | 651 | if (retval) |
554 | break; | 652 | break; |
555 | } | 653 | |
556 | req = uinput_request_find(udev, ff_up.request_id); | 654 | req = uinput_request_find(udev, ff_up.request_id); |
557 | if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) { | 655 | if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) { |
558 | retval = -EINVAL; | 656 | retval = -EINVAL; |
559 | break; | 657 | break; |
560 | } | 658 | } |
659 | |||
561 | ff_up.retval = 0; | 660 | ff_up.retval = 0; |
562 | memcpy(&ff_up.effect, req->u.upload.effect, sizeof(struct ff_effect)); | 661 | ff_up.effect = *req->u.upload.effect; |
563 | if (req->u.upload.old) | 662 | if (req->u.upload.old) |
564 | memcpy(&ff_up.old, req->u.upload.old, sizeof(struct ff_effect)); | 663 | ff_up.old = *req->u.upload.old; |
565 | else | 664 | else |
566 | memset(&ff_up.old, 0, sizeof(struct ff_effect)); | 665 | memset(&ff_up.old, 0, sizeof(struct ff_effect)); |
567 | 666 | ||
568 | if (copy_to_user(p, &ff_up, sizeof(ff_up))) { | 667 | retval = uinput_ff_upload_to_user(p, &ff_up); |
569 | retval = -EFAULT; | ||
570 | break; | ||
571 | } | ||
572 | break; | 668 | break; |
573 | 669 | ||
574 | case UI_BEGIN_FF_ERASE: | 670 | case UI_BEGIN_FF_ERASE: |
@@ -576,29 +672,34 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
576 | retval = -EFAULT; | 672 | retval = -EFAULT; |
577 | break; | 673 | break; |
578 | } | 674 | } |
675 | |||
579 | req = uinput_request_find(udev, ff_erase.request_id); | 676 | req = uinput_request_find(udev, ff_erase.request_id); |
580 | if (!(req && req->code == UI_FF_ERASE)) { | 677 | if (!req || req->code != UI_FF_ERASE) { |
581 | retval = -EINVAL; | 678 | retval = -EINVAL; |
582 | break; | 679 | break; |
583 | } | 680 | } |
681 | |||
584 | ff_erase.retval = 0; | 682 | ff_erase.retval = 0; |
585 | ff_erase.effect_id = req->u.effect_id; | 683 | ff_erase.effect_id = req->u.effect_id; |
586 | if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { | 684 | if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { |
587 | retval = -EFAULT; | 685 | retval = -EFAULT; |
588 | break; | 686 | break; |
589 | } | 687 | } |
688 | |||
590 | break; | 689 | break; |
591 | 690 | ||
592 | case UI_END_FF_UPLOAD: | 691 | case UI_END_FF_UPLOAD: |
593 | if (copy_from_user(&ff_up, p, sizeof(ff_up))) { | 692 | retval = uinput_ff_upload_from_user(p, &ff_up); |
594 | retval = -EFAULT; | 693 | if (retval) |
595 | break; | 694 | break; |
596 | } | 695 | |
597 | req = uinput_request_find(udev, ff_up.request_id); | 696 | req = uinput_request_find(udev, ff_up.request_id); |
598 | if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) { | 697 | if (!req || req->code != UI_FF_UPLOAD || |
698 | !req->u.upload.effect) { | ||
599 | retval = -EINVAL; | 699 | retval = -EINVAL; |
600 | break; | 700 | break; |
601 | } | 701 | } |
702 | |||
602 | req->retval = ff_up.retval; | 703 | req->retval = ff_up.retval; |
603 | uinput_request_done(udev, req); | 704 | uinput_request_done(udev, req); |
604 | break; | 705 | break; |
@@ -608,11 +709,13 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
608 | retval = -EFAULT; | 709 | retval = -EFAULT; |
609 | break; | 710 | break; |
610 | } | 711 | } |
712 | |||
611 | req = uinput_request_find(udev, ff_erase.request_id); | 713 | req = uinput_request_find(udev, ff_erase.request_id); |
612 | if (!(req && req->code == UI_FF_ERASE)) { | 714 | if (!req || req->code != UI_FF_ERASE) { |
613 | retval = -EINVAL; | 715 | retval = -EINVAL; |
614 | break; | 716 | break; |
615 | } | 717 | } |
718 | |||
616 | req->retval = ff_erase.retval; | 719 | req->retval = ff_erase.retval; |
617 | uinput_request_done(udev, req); | 720 | uinput_request_done(udev, req); |
618 | break; | 721 | break; |
@@ -626,6 +729,18 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
626 | return retval; | 729 | return retval; |
627 | } | 730 | } |
628 | 731 | ||
732 | static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
733 | { | ||
734 | return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg); | ||
735 | } | ||
736 | |||
737 | #ifdef CONFIG_COMPAT | ||
738 | static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
739 | { | ||
740 | return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg)); | ||
741 | } | ||
742 | #endif | ||
743 | |||
629 | static const struct file_operations uinput_fops = { | 744 | static const struct file_operations uinput_fops = { |
630 | .owner = THIS_MODULE, | 745 | .owner = THIS_MODULE, |
631 | .open = uinput_open, | 746 | .open = uinput_open, |
@@ -634,6 +749,9 @@ static const struct file_operations uinput_fops = { | |||
634 | .write = uinput_write, | 749 | .write = uinput_write, |
635 | .poll = uinput_poll, | 750 | .poll = uinput_poll, |
636 | .unlocked_ioctl = uinput_ioctl, | 751 | .unlocked_ioctl = uinput_ioctl, |
752 | #ifdef CONFIG_COMPAT | ||
753 | .compat_ioctl = uinput_compat_ioctl, | ||
754 | #endif | ||
637 | }; | 755 | }; |
638 | 756 | ||
639 | static struct miscdevice uinput_misc = { | 757 | static struct miscdevice uinput_misc = { |