[PATCH] Fix force feedback uploading for 32-bit apps on 64-bit kernel

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

Force feedback upload of effects through the event device (ioctl
EVIOCSFF) is not working in 32 bit apps like wine on a 64-bit kernel. 64
bit apps like fftest work.

This is due to the fact that struct ff_effect contains a pointer,
resulting in the structure having different sizes in 64 and 32 bit
programs. This means that the ioctl issued from 32 bit code isn't
handled, because it has a different number.

Attached patch (against 2.6.24.3) fixes this. Tested on x86_64.

Is this ok or are there some alignment issues, which forbid this simple
zero-copy solution?

PS. Please keep me in the CC list, as I'm not subscribing this list.

diff -ruNp 2.6.24.3/drivers/input/evdev.c 2.6.24.3-ioctl-fix/drivers/input/evdev.c
--- 2.6.24.3/drivers/input/evdev.c	2008-01-24 23:58:37.000000000 +0100
+++ 2.6.24.3-ioctl-fix/drivers/input/evdev.c	2008-03-26 22:12:02.000000000 +0100
@@ -577,6 +577,9 @@ static long evdev_do_ioctl(struct file *
 	struct input_dev *dev = evdev->handle.dev;
 	struct input_absinfo abs;
 	struct ff_effect effect;
+#ifdef CONFIG_COMPAT
+	struct ff_effect32 *effect32;
+#endif
 	int __user *ip = (int __user *)p;
 	int i, t, u, v;
 	int error;
@@ -632,10 +635,28 @@ static long evdev_do_ioctl(struct file *
 
 		return dev->setkeycode(dev, t, v);
 
+#ifdef CONFIG_COMPAT
+	case EVIOCSFF32:
+#endif
 	case EVIOCSFF:
-		if (copy_from_user(&effect, p, sizeof(effect)))
+		if (copy_from_user(&effect, p, _IOC_SIZE(cmd)))
 			return -EFAULT;
 
+#ifdef CONFIG_COMPAT
+		/*
+		 * It so happens that the pointer which needs to be changed
+		 * is the last field in the structure, so we can copy the
+		 * whole thing and replace just the pointer.
+		 */
+		if (cmd == EVIOCSFF32) {
+			effect32 = (struct ff_effect32 *)&effect;
+			if (effect32->type == FF_PERIODIC &&
+			    effect32->u.periodic.waveform == FF_CUSTOM)
+				effect.u.periodic.custom_data = compat_ptr(
+					effect32->u.periodic.custom_data);
+		}
+#endif
+
 		error = input_ff_upload(dev, &effect, file);
 
 		if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
diff -ruNp 2.6.24.3/include/linux/input.h 2.6.24.3-ioctl-fix/include/linux/input.h
--- 2.6.24.3/include/linux/input.h	2008-01-24 23:58:37.000000000 +0100
+++ 2.6.24.3-ioctl-fix/include/linux/input.h	2008-03-25 22:17:59.000000000 +0100
@@ -12,6 +12,7 @@
 #ifdef __KERNEL__
 #include <linux/time.h>
 #include <linux/list.h>
+#include <linux/compat.h>
 #else
 #include <sys/time.h>
 #include <sys/ioctl.h>
@@ -75,7 +76,10 @@ struct input_absinfo {
 #define EVIOCGABS(abs)		_IOR('E', 0x40 + abs, struct input_absinfo)		/* get abs value/limits */
 #define EVIOCSABS(abs)		_IOW('E', 0xc0 + abs, struct input_absinfo)		/* set abs value/limits */
 
-#define EVIOCSFF		_IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect))	/* send a force effect to a force feedback device */
+#define EVIOCSFF		_IOW('E', 0x80, struct ff_effect)	/* send a force effect to a force feedback device */
+#ifdef CONFIG_COMPAT
+#define EVIOCSFF32		_IOW('E', 0x80, struct ff_effect32)
+#endif
 #define EVIOCRMFF		_IOW('E', 0x81, int)			/* Erase a force effect */
 #define EVIOCGEFFECTS		_IOR('E', 0x84, int)			/* Report number of effects playable at the same time */
 
@@ -846,6 +850,21 @@ struct ff_periodic_effect {
 	__s16 *custom_data;
 };
 
+#ifdef CONFIG_COMPAT
+struct ff_periodic_effect32 {
+	__u16 waveform;
+	__u16 period;
+	__s16 magnitude;
+	__s16 offset;
+	__u16 phase;
+
+	struct ff_envelope envelope;
+
+	__u32 custom_len;
+	compat_uptr_t custom_data;
+};
+#endif
+
 /**
  * struct ff_rumble_effect - defines parameters of a periodic force-feedback effect
  * @strong_magnitude: magnitude of the heavy motor
@@ -898,6 +917,24 @@ struct ff_effect {
 	} u;
 };
 
+#ifdef CONFIG_COMPAT
+struct ff_effect32 {
+	__u16 type;
+	__s16 id;
+	__u16 direction;
+	struct ff_trigger trigger;
+	struct ff_replay replay;
+
+	union {
+		struct ff_constant_effect constant;
+		struct ff_ramp_effect ramp;
+		struct ff_periodic_effect32 periodic;
+		struct ff_condition_effect condition[2]; /* One for each axis */
+		struct ff_rumble_effect rumble;
+	} u;
+};
+#endif
+
 /*
  * Force feedback effect types
  */

[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux