diff options
author | Adam Dawidowski <drake_ster@wp.pl> | 2008-06-02 01:08:10 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-06-30 09:37:06 -0400 |
commit | f2278f31d6feb9036eaa79f2e8abcce850420abd (patch) | |
tree | 85b6f3abd9f6e77c5073a8951e3ac5452bbabfb9 /drivers | |
parent | 82547e9074a23d9d722a5f6053f4734566127da6 (diff) |
Input: fix force feedback upload issue in compat mode
Force feedback upload of effects through the event device (ioctl
EVIOCSFF) is not working in 32 bit applications running on 64-bit
kernel due to the fact that struct ff_effect contains a pointer,
resulting in the structure having different sizes in 64 and 32 bit
programs and causing difference in ioctl numbers.
[dtor@mail.ru: refactor to keep all ugliness in evdev]
Signed-off-by: Adam Dawidowski <drake_ster@wp.pl>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/evdev.c | 101 |
1 files changed, 90 insertions, 11 deletions
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index b32984bc516f..2d65411f6763 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c | |||
@@ -300,6 +300,35 @@ struct input_event_compat { | |||
300 | __s32 value; | 300 | __s32 value; |
301 | }; | 301 | }; |
302 | 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 | |||
303 | /* Note to the author of this code: did it ever occur to | 332 | /* Note to the author of this code: did it ever occur to |
304 | you why the ifdefs are needed? Think about it again. -AK */ | 333 | you why the ifdefs are needed? Think about it again. -AK */ |
305 | #ifdef CONFIG_X86_64 | 334 | #ifdef CONFIG_X86_64 |
@@ -368,6 +397,42 @@ static int evdev_event_to_user(char __user *buffer, | |||
368 | return 0; | 397 | return 0; |
369 | } | 398 | } |
370 | 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 | |||
371 | #else | 436 | #else |
372 | 437 | ||
373 | static inline size_t evdev_event_size(void) | 438 | static inline size_t evdev_event_size(void) |
@@ -393,6 +458,18 @@ static int evdev_event_to_user(char __user *buffer, | |||
393 | return 0; | 458 | return 0; |
394 | } | 459 | } |
395 | 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 | |||
396 | #endif /* CONFIG_COMPAT */ | 473 | #endif /* CONFIG_COMPAT */ |
397 | 474 | ||
398 | static ssize_t evdev_write(struct file *file, const char __user *buffer, | 475 | static ssize_t evdev_write(struct file *file, const char __user *buffer, |
@@ -633,17 +710,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, | |||
633 | 710 | ||
634 | return input_set_keycode(dev, t, v); | 711 | return input_set_keycode(dev, t, v); |
635 | 712 | ||
636 | case EVIOCSFF: | ||
637 | if (copy_from_user(&effect, p, sizeof(effect))) | ||
638 | return -EFAULT; | ||
639 | |||
640 | error = input_ff_upload(dev, &effect, file); | ||
641 | |||
642 | if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) | ||
643 | return -EFAULT; | ||
644 | |||
645 | return error; | ||
646 | |||
647 | case EVIOCRMFF: | 713 | case EVIOCRMFF: |
648 | return input_ff_erase(dev, (int)(unsigned long) p, file); | 714 | return input_ff_erase(dev, (int)(unsigned long) p, file); |
649 | 715 | ||
@@ -733,6 +799,19 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, | |||
733 | 799 | ||
734 | if (_IOC_DIR(cmd) == _IOC_WRITE) { | 800 | if (_IOC_DIR(cmd) == _IOC_WRITE) { |
735 | 801 | ||
802 | if (_IOC_NR(cmd) == _IOC_NR(EVIOCSFF)) { | ||
803 | |||
804 | if (evdev_ff_effect_from_user(p, _IOC_SIZE(cmd), &effect)) | ||
805 | return -EFAULT; | ||
806 | |||
807 | error = input_ff_upload(dev, &effect, file); | ||
808 | |||
809 | if (put_user(effect.id, &(((struct ff_effect __user *)p)->id))) | ||
810 | return -EFAULT; | ||
811 | |||
812 | return error; | ||
813 | } | ||
814 | |||
736 | if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { | 815 | if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCSABS(0))) { |
737 | 816 | ||
738 | t = _IOC_NR(cmd) & ABS_MAX; | 817 | t = _IOC_NR(cmd) & ABS_MAX; |