All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/24] input: Introduce ff-memless-next as an improved replacement for ff-memless
@ 2014-04-26 15:01 ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:01 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

ff-memless-next (MLNX) is a largely improved version of the current ff-memless
(FFML) driver. MLNX supports all force feedback effects currently available in
the Linux force feedback userspace API. All effects are handled in accordance
with Microsoft's DirectInput/XInput. Most notable changes include support for
conditional effects, proper handling of all periodic waveforms and improved
emulation of rumble effects through periodic effects. MLNX also uses its own
kernel API to pass processed effects to hardware-specific drivers instead of
abusing "ff_effect" struct. The API is documented in the respective header
file.

MLNX has been expanded to be a direct replacement for FFML.

Support for FF_PERIODIC and FF_RAMP has been added to all devices that
support FF_CONSTANT as a part of the port to the new API.

This patch series:
1) Adds "ff-memless-next" module [1]
2) Ports all hardware-specific drivers to MLNX's API [2-23]
3) Removes FFML and replaces it with MLNX [24]

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>

v4:
 - Add a summary of changes between MLNX and FFML to the last patch
 - Remove a stale empty line in hid-sony.c
 - Add "Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>" to hid-lg4ff patch.

v3:
 - Rebase against latest linux-next. Fixes conflict in hid-sony.c and
   max8997_haptic.c
 - Updated documentation in ff-memless-next.h. The documentation now describes
   parameters of the callback function and specifically mentions that
   HW-specific drivers must not keep a reference to mlnx_effect_command struct
   to which a pointer is passed in the callback function.
 - Fix a minor brace inconsistency in hid-lgff
 I believe that all concerns regarding v2 have been resolved as false alarms.

v2:
 - Add missing msecs to jiffies conversion in ff-memless-next
 - lgff: Properly convert force on Y axis from MLNX to device range
         Support periodic effects for "joystick_ac" device class
 - lg3ff: Properly convert forces from MLNX to device range
 - Very minor coding style issues fixed

Michal Malý (24):
  Add ff-memless-next module
  Port arizona-haptics to ff-memless-next
  Port twl4030-vibra to ff-memless-next
  Port twl6040-vibra to ff-memless-next
  Port max8997_haptic to ff-memless-next
  Port pm8xxx-vibrator to ff-memless-next
  Port hid-axff to ff-memless-next
  Port hid-emsff to ff-memless-next
  Port hid-dr to ff-memless-next
  Port hid-gaff to ff-memless-next
  Port hid-holtekff to ff-memless-next
  Port hid-lgff to ff-memless-next
  Port hid-lg3ff to ff-memless-next
  Port hid-pl to ff-memless-next
  Port hid-sjoy to ff-memless-next
  Port hid-sony to ff-memless-next
  Port hid-tmff to ff-memless-next
  Port hid-wiimote-modules to ff-memless-next
  Port hid-zpff to ff-memless-next
  Port gamecon to ff-memless-next
  Port xpad to ff-memless-next
  Port hid-lg2ff to ff-memless-next
  Port hid-lg4ff to ff-memless-next
  Replace ff-memless with ff-memless-next

 drivers/hid/Kconfig                   |   30 +-
 drivers/hid/hid-axff.c                |   32 +-
 drivers/hid/hid-dr.c                  |   59 +-
 drivers/hid/hid-emsff.c               |   38 +-
 drivers/hid/hid-gaff.c                |   32 +-
 drivers/hid/hid-holtekff.c            |   47 +-
 drivers/hid/hid-lg2ff.c               |   65 ++-
 drivers/hid/hid-lg3ff.c               |   60 +-
 drivers/hid/hid-lg4ff.c               |   93 +--
 drivers/hid/hid-lgff.c                |   70 ++-
 drivers/hid/hid-pl.c                  |   38 +-
 drivers/hid/hid-sjoy.c                |   35 +-
 drivers/hid/hid-sony.c                |   23 +-
 drivers/hid/hid-tmff.c                |   83 +--
 drivers/hid/hid-wiimote-modules.c     |   74 ++-
 drivers/hid/hid-zpff.c                |   30 +-
 drivers/input/Kconfig                 |    4 +-
 drivers/input/Makefile                |    2 +-
 drivers/input/ff-memless-next.c       | 1037 +++++++++++++++++++++++++++++++++
 drivers/input/ff-memless.c            |  547 -----------------
 drivers/input/joystick/Kconfig        |    4 +-
 drivers/input/joystick/gamecon.c      |   57 +-
 drivers/input/joystick/xpad.c         |  125 ++--
 drivers/input/misc/Kconfig            |   10 +-
 drivers/input/misc/arizona-haptics.c  |   39 +-
 drivers/input/misc/max8997_haptic.c   |   25 +-
 drivers/input/misc/pm8xxx-vibrator.c  |   28 +-
 drivers/input/misc/twl4030-vibra.c    |   31 +-
 drivers/input/misc/twl6040-vibra.c    |   27 +-
 include/linux/input.h                 |    3 -
 include/linux/input/ff-memless-next.h |  162 +++++
 31 files changed, 1951 insertions(+), 959 deletions(-)
 create mode 100644 drivers/input/ff-memless-next.c
 delete mode 100644 drivers/input/ff-memless.c
 create mode 100644 include/linux/input/ff-memless-next.h

-- 
1.9.2


^ permalink raw reply	[flat|nested] 83+ messages in thread

* [PATCH v4 00/24] input: Introduce ff-memless-next as an improved replacement for ff-memless
@ 2014-04-26 15:01 ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:01 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

ff-memless-next (MLNX) is a largely improved version of the current ff-memless
(FFML) driver. MLNX supports all force feedback effects currently available in
the Linux force feedback userspace API. All effects are handled in accordance
with Microsoft's DirectInput/XInput. Most notable changes include support for
conditional effects, proper handling of all periodic waveforms and improved
emulation of rumble effects through periodic effects. MLNX also uses its own
kernel API to pass processed effects to hardware-specific drivers instead of
abusing "ff_effect" struct. The API is documented in the respective header
file.

MLNX has been expanded to be a direct replacement for FFML.

Support for FF_PERIODIC and FF_RAMP has been added to all devices that
support FF_CONSTANT as a part of the port to the new API.

This patch series:
1) Adds "ff-memless-next" module [1]
2) Ports all hardware-specific drivers to MLNX's API [2-23]
3) Removes FFML and replaces it with MLNX [24]

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>

v4:
 - Add a summary of changes between MLNX and FFML to the last patch
 - Remove a stale empty line in hid-sony.c
 - Add "Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>" to hid-lg4ff patch.

v3:
 - Rebase against latest linux-next. Fixes conflict in hid-sony.c and
   max8997_haptic.c
 - Updated documentation in ff-memless-next.h. The documentation now describes
   parameters of the callback function and specifically mentions that
   HW-specific drivers must not keep a reference to mlnx_effect_command struct
   to which a pointer is passed in the callback function.
 - Fix a minor brace inconsistency in hid-lgff
 I believe that all concerns regarding v2 have been resolved as false alarms.

v2:
 - Add missing msecs to jiffies conversion in ff-memless-next
 - lgff: Properly convert force on Y axis from MLNX to device range
         Support periodic effects for "joystick_ac" device class
 - lg3ff: Properly convert forces from MLNX to device range
 - Very minor coding style issues fixed

Michal Malý (24):
  Add ff-memless-next module
  Port arizona-haptics to ff-memless-next
  Port twl4030-vibra to ff-memless-next
  Port twl6040-vibra to ff-memless-next
  Port max8997_haptic to ff-memless-next
  Port pm8xxx-vibrator to ff-memless-next
  Port hid-axff to ff-memless-next
  Port hid-emsff to ff-memless-next
  Port hid-dr to ff-memless-next
  Port hid-gaff to ff-memless-next
  Port hid-holtekff to ff-memless-next
  Port hid-lgff to ff-memless-next
  Port hid-lg3ff to ff-memless-next
  Port hid-pl to ff-memless-next
  Port hid-sjoy to ff-memless-next
  Port hid-sony to ff-memless-next
  Port hid-tmff to ff-memless-next
  Port hid-wiimote-modules to ff-memless-next
  Port hid-zpff to ff-memless-next
  Port gamecon to ff-memless-next
  Port xpad to ff-memless-next
  Port hid-lg2ff to ff-memless-next
  Port hid-lg4ff to ff-memless-next
  Replace ff-memless with ff-memless-next

 drivers/hid/Kconfig                   |   30 +-
 drivers/hid/hid-axff.c                |   32 +-
 drivers/hid/hid-dr.c                  |   59 +-
 drivers/hid/hid-emsff.c               |   38 +-
 drivers/hid/hid-gaff.c                |   32 +-
 drivers/hid/hid-holtekff.c            |   47 +-
 drivers/hid/hid-lg2ff.c               |   65 ++-
 drivers/hid/hid-lg3ff.c               |   60 +-
 drivers/hid/hid-lg4ff.c               |   93 +--
 drivers/hid/hid-lgff.c                |   70 ++-
 drivers/hid/hid-pl.c                  |   38 +-
 drivers/hid/hid-sjoy.c                |   35 +-
 drivers/hid/hid-sony.c                |   23 +-
 drivers/hid/hid-tmff.c                |   83 +--
 drivers/hid/hid-wiimote-modules.c     |   74 ++-
 drivers/hid/hid-zpff.c                |   30 +-
 drivers/input/Kconfig                 |    4 +-
 drivers/input/Makefile                |    2 +-
 drivers/input/ff-memless-next.c       | 1037 +++++++++++++++++++++++++++++++++
 drivers/input/ff-memless.c            |  547 -----------------
 drivers/input/joystick/Kconfig        |    4 +-
 drivers/input/joystick/gamecon.c      |   57 +-
 drivers/input/joystick/xpad.c         |  125 ++--
 drivers/input/misc/Kconfig            |   10 +-
 drivers/input/misc/arizona-haptics.c  |   39 +-
 drivers/input/misc/max8997_haptic.c   |   25 +-
 drivers/input/misc/pm8xxx-vibrator.c  |   28 +-
 drivers/input/misc/twl4030-vibra.c    |   31 +-
 drivers/input/misc/twl6040-vibra.c    |   27 +-
 include/linux/input.h                 |    3 -
 include/linux/input/ff-memless-next.h |  162 +++++
 31 files changed, 1951 insertions(+), 959 deletions(-)
 create mode 100644 drivers/input/ff-memless-next.c
 delete mode 100644 drivers/input/ff-memless.c
 create mode 100644 include/linux/input/ff-memless-next.h

-- 
1.9.2

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* [PATCH v4 01/24] input: Add ff-memless-next module
  2014-04-26 15:01 ` Michal Malý
@ 2014-04-26 15:02   ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Add ff-memless-next module

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>
---
 drivers/input/Kconfig                 |   11 +
 drivers/input/Makefile                |    1 +
 drivers/input/ff-memless-next.c       | 1037 +++++++++++++++++++++++++++++++++
 include/linux/input/ff-memless-next.h |  162 +++++
 4 files changed, 1211 insertions(+)
 create mode 100644 drivers/input/ff-memless-next.c
 create mode 100644 include/linux/input/ff-memless-next.h

diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index a11ff74..3780962 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -38,6 +38,17 @@ config INPUT_FF_MEMLESS
 	  To compile this driver as a module, choose M here: the
 	  module will be called ff-memless.
 
+config INPUT_FF_MEMLESS_NEXT
+	tristate "New version of support for memoryless force-feedback devices"
+	help
+	  Say Y here to enable a new version of support for memoryless force
+	  feedback devices.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ff-memless-next.
+
 config INPUT_POLLDEV
 	tristate "Polled input device skeleton"
 	help
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 5ca3f63..b4f11f5 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_INPUT)		+= input-core.o
 input-core-y := input.o input-compat.o input-mt.o ff-core.o
 
 obj-$(CONFIG_INPUT_FF_MEMLESS)	+= ff-memless.o
+obj-$(CONFIG_INPUT_FF_MEMLESS_NEXT)	+= ff-memless-next.o
 obj-$(CONFIG_INPUT_POLLDEV)	+= input-polldev.o
 obj-$(CONFIG_INPUT_SPARSEKMAP)	+= sparse-keymap.o
 obj-$(CONFIG_INPUT_MATRIXKMAP)	+= matrix-keymap.o
diff --git a/drivers/input/ff-memless-next.c b/drivers/input/ff-memless-next.c
new file mode 100644
index 0000000..24619e9
--- /dev/null
+++ b/drivers/input/ff-memless-next.c
@@ -0,0 +1,1037 @@
+/*
+ * Force feedback support for memoryless devices
+ *
+ * This module is based on "ff-memless" orignally written by Anssi Hannula.
+ * It is extended to support all force feedback effects currently supported
+ * by the Linux input stack.
+ * Logic of emulation of FF_RUMBLE through FF_PERIODIC provided by
+ * Elias Vanderstuyft <elias.vds@gmail.com>
+ *
+ * Copyright(c) 2014 Michal "MadCatX" Maly <madcatxster@devoid-pointer.net>
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+#include <linux/fixp-arith.h>
+#include <linux/input/ff-memless-next.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal \"MadCatX\" Maly");
+MODULE_DESCRIPTION("Force feedback support for memoryless force feedback devices");
+
+#define FF_MAX_EFFECTS 16
+#define FF_MIN_EFFECT_LENGTH ((MSEC_PER_SEC / CONFIG_HZ) + 1)
+#define FF_EFFECT_STARTED 1
+#define FF_EFFECT_PLAYING 2
+#define FF_EFFECT_EMULATED 3
+
+enum mlnx_emulate {
+	EMUL_NOTHING,	/* Do not emulate anything */
+	EMUL_RUMBLE,	/* Emulate FF_RUMBLE with FF_PERIODIC */
+	EMUL_PERIODIC	/* Emulate FF_PERIODIC with FF_RUMBLE */
+};
+
+struct mlnx_effect {
+	struct ff_effect effect;
+	unsigned long flags;
+	unsigned long begin_at;
+	unsigned long stop_at;
+	unsigned long updated_at;
+	unsigned long attack_stop;
+	unsigned long fade_begin;
+	int repeat;
+};
+
+struct mlnx_device {
+	u8 combinable_playing;
+	u8 rumble_playing;
+	unsigned long update_rate_jiffies;
+	void *private;
+	struct mlnx_effect effects[FF_MAX_EFFECTS];
+	u16 gain;
+	struct timer_list timer;
+	struct input_dev *dev;
+	enum mlnx_emulate emul;
+
+	int (*control_effect)(struct input_dev *, void *,
+			      const struct mlnx_effect_command *);
+};
+
+static s32 mlnx_calculate_x_force(const s32 level,
+						  const u16 direction)
+{
+	s32 new = (level * -fixp_sin(direction)) >> FRAC_N;
+	pr_debug("x force: %d\n", new);
+	return new;
+}
+
+static s32 mlnx_calculate_y_force(const s32 level,
+						  const u16 direction)
+{
+	s32 new = (level * fixp_cos(direction)) >> FRAC_N;
+	pr_debug("y force: %d\n", new);
+	return new;
+}
+
+static bool mlnx_is_combinable(const struct ff_effect *effect)
+{
+	switch (effect->type) {
+	case FF_CONSTANT:
+	case FF_PERIODIC:
+	case FF_RAMP:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool mlnx_is_conditional(const struct ff_effect *effect)
+{
+	switch (effect->type) {
+	case FF_DAMPER:
+	case FF_FRICTION:
+	case FF_INERTIA:
+	case FF_SPRING:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void mlnx_clr_emulated(struct mlnx_effect *mlnxeff)
+{
+	__clear_bit(FF_EFFECT_EMULATED, &mlnxeff->flags);
+}
+
+static void mlnx_clr_playing(struct mlnx_effect *mlnxeff)
+{
+	__clear_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+}
+
+static void mlnx_clr_started(struct mlnx_effect *mlnxeff)
+{
+	__clear_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static bool mlnx_is_emulated(const struct mlnx_effect *mlnxeff)
+{
+	return test_bit(FF_EFFECT_EMULATED, &mlnxeff->flags);
+}
+
+static bool mlnx_is_rumble(const struct ff_effect *effect)
+{
+	return effect->type == FF_RUMBLE;
+}
+
+static bool mlnx_is_playing(const struct mlnx_effect *mlnxeff)
+{
+	return test_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+}
+
+static bool mlnx_is_started(const struct mlnx_effect *mlnxeff)
+{
+	return test_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static bool mlnx_test_set_playing(struct mlnx_effect *mlnxeff)
+{
+	return test_and_set_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+}
+
+static const struct ff_envelope *mlnx_get_envelope(const struct ff_effect *effect)
+{
+	static const struct ff_envelope empty;
+
+	switch (effect->type) {
+	case FF_CONSTANT:
+		return &effect->u.constant.envelope;
+	case FF_PERIODIC:
+		return &effect->u.periodic.envelope;
+	case FF_RAMP:
+		return &effect->u.ramp.envelope;
+	default:
+		return &empty;
+	}
+}
+
+/* Some devices might have a limit on how many uncombinable effects
+ * can be played at once */
+static int mlnx_upload_conditional(struct mlnx_device *mlnxdev,
+				   const struct ff_effect *effect)
+{
+	struct mlnx_effect_command ecmd = {
+		.cmd = MLNX_UPLOAD_UNCOMB,
+		.u.uncomb.id = effect->id,
+		.u.uncomb.effect = effect
+	};
+	return mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+}
+
+static int mlnx_erase_conditional(struct mlnx_device *mlnxdev,
+				  const struct ff_effect *effect)
+{
+	struct mlnx_effect_command ecmd = {
+		.cmd = MLNX_ERASE_UNCOMB,
+		.u.uncomb.id = effect->id,
+		.u.uncomb.effect = effect
+	};
+	return mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+}
+
+static void mlnx_set_envelope_times(struct mlnx_effect *mlnxeff)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+
+	if (envelope->attack_length) {
+		unsigned long j = msecs_to_jiffies(envelope->attack_length);
+		mlnxeff->attack_stop = mlnxeff->begin_at + j;
+	}
+	if (effect->replay.length && envelope->fade_length) {
+		unsigned long j = msecs_to_jiffies(envelope->fade_length);
+		mlnxeff->fade_begin = mlnxeff->stop_at - j;
+	}
+}
+
+static void mlnx_set_trip_times(struct mlnx_effect *mlnxeff,
+				const unsigned long now)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const u16 replay_delay = effect->replay.delay;
+	const u16 replay_length = effect->replay.length;
+
+	mlnxeff->begin_at = now + msecs_to_jiffies(replay_delay);
+	mlnxeff->stop_at = mlnxeff->begin_at + msecs_to_jiffies(replay_length);
+	mlnxeff->updated_at = mlnxeff->begin_at;
+}
+
+static void mlnx_start_effect(struct mlnx_effect *mlnxeff)
+{
+	const unsigned long now = jiffies;
+
+	mlnx_set_trip_times(mlnxeff, now);
+	mlnx_set_envelope_times(mlnxeff);
+	__set_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static void mlnx_stop_effect(struct mlnx_device *mlnxdev,
+			     const struct mlnx_effect *mlnxeff)
+{
+	switch (mlnxeff->effect.type) {
+	case FF_PERIODIC:
+		if (mlnx_is_emulated(mlnxeff)) {
+			if (--mlnxdev->rumble_playing == 0) {
+				const struct mlnx_effect_command c = {
+					.cmd = MLNX_STOP_RUMBLE
+				};
+				mlnxdev->control_effect(mlnxdev->dev,
+							mlnxdev->private, &c);
+			}
+			return;
+		}
+	case FF_CONSTANT:
+	case FF_RAMP:
+		if (--mlnxdev->combinable_playing == 0) {
+			const struct mlnx_effect_command c = {
+				.cmd = MLNX_STOP_COMBINED
+			};
+			mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private,
+						&c);
+		}
+		return;
+	case FF_RUMBLE:
+		if (mlnx_is_emulated(mlnxeff)) {
+			if (--mlnxdev->combinable_playing == 0) {
+				const struct mlnx_effect_command c = {
+					.cmd = MLNX_STOP_COMBINED
+				};
+				mlnxdev->control_effect(mlnxdev->dev,
+							mlnxdev->private, &c);
+			}
+		} else {
+			if (--mlnxdev->rumble_playing == 0) {
+				const struct mlnx_effect_command c = {
+					.cmd = MLNX_STOP_RUMBLE
+				};
+				mlnxdev->control_effect(mlnxdev->dev,
+							mlnxdev->private, &c);
+			}
+		}
+		return;
+	case FF_DAMPER:
+	case FF_FRICTION:
+	case FF_INERTIA:
+	case FF_SPRING:
+	{
+		const struct mlnx_effect_command c = {
+			.cmd = MLNX_STOP_UNCOMB,
+			.u.uncomb.id = mlnxeff->effect.id,
+			.u.uncomb.effect = &mlnxeff->effect
+		};
+		mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &c);
+		return;
+	}
+	default:
+		return;
+	}
+}
+
+static int mlnx_restart_effect(struct mlnx_device *mlnxdev,
+			       struct mlnx_effect *mlnxeff)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const unsigned long now = jiffies;
+
+	if (mlnx_is_combinable(effect)) {
+		if (effect->replay.delay)
+			mlnx_stop_effect(mlnxdev, mlnxeff);
+		else {
+			if (mlnx_is_emulated(mlnxeff))
+				mlnxdev->rumble_playing--;
+			else
+				mlnxdev->combinable_playing--;
+		}
+	} else if (mlnx_is_rumble(effect)) {
+		if (effect->replay.delay)
+			mlnx_stop_effect(mlnxdev, mlnxeff);
+		else {
+			if (mlnx_is_emulated(mlnxeff))
+				mlnxdev->combinable_playing--;
+			else
+				mlnxdev->rumble_playing--;
+		}
+	} else if (mlnx_is_conditional(effect)) {
+		int ret;
+		if (effect->replay.delay)
+			mlnx_stop_effect(mlnxdev, mlnxeff);
+
+		ret = mlnx_upload_conditional(mlnxdev, &mlnxeff->effect);
+		if (ret)
+			return ret;
+	}
+
+	mlnx_set_trip_times(mlnxeff, now);
+	mlnx_set_envelope_times(mlnxeff);
+
+	return 0;
+}
+
+static s32 mlnx_apply_envelope(const struct mlnx_effect *mlnxeff,
+			       const s32 level)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+	const unsigned long now = jiffies;
+	const s32 alevel = abs(level);
+
+	/* Effect has an envelope with nonzero attack time */
+	if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+		const s32 clength = jiffies_to_msecs(now - mlnxeff->begin_at);
+		const s32 alength = envelope->attack_length;
+		const s32 dlevel = (alevel - envelope->attack_level)
+				 * clength / alength;
+		return level < 0 ? -(dlevel + envelope->attack_level) :
+				    (dlevel + envelope->attack_level);
+	} else if (envelope->fade_length && time_before_eq(mlnxeff->fade_begin, now)) {
+		const s32 clength = jiffies_to_msecs(now - mlnxeff->fade_begin);
+		const s32 flength = envelope->fade_length;
+		const s32 dlevel = (envelope->fade_level - alevel)
+				 * clength / flength;
+		return level < 0 ? -(dlevel + alevel) : (dlevel + alevel);
+	}
+
+	return level;
+}
+
+static s32 mlnx_calculate_periodic(struct mlnx_effect *mlnxeff, const s32 level)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const unsigned long now = jiffies;
+	const u16 period = effect->u.periodic.period;
+	const u16 phase = effect->u.periodic.phase;
+	const s16 offset = effect->u.periodic.offset;
+	s32 new = level;
+	u16 t = (jiffies_to_msecs(now - mlnxeff->begin_at) + phase) % period;
+
+	switch (effect->u.periodic.waveform) {
+	case FF_SINE:
+	{
+		u16 degrees = (360 * t) / period;
+		new = ((level * fixp_sin(degrees)) >> FRAC_N) + offset;
+		break;
+	}
+	case FF_SQUARE:
+	{
+		u16 degrees = (360 * t) / period;
+		new = level * (degrees < 180 ? 1 : -1) + offset;
+		break;
+	}
+	case FF_SAW_UP:
+		new = 2 * level * t / period - level + offset;
+		break;
+	case FF_SAW_DOWN:
+		new = level - 2 * level * t / period + offset;
+		break;
+	case FF_TRIANGLE:
+	{
+		new = (2 * abs(level - (2 * level * t) / period));
+		new = new - abs(level) + offset;
+		break;
+	}
+	case FF_CUSTOM:
+		pr_debug("Custom waveform is not handled by this driver\n");
+		return level;
+	default:
+		pr_err("Invalid waveform\n");
+		return level;
+	}
+
+	/* Ensure that the offset did not make the value exceed s16 range */
+	new = clamp(new, -0x7fff, 0x7fff);
+	pr_debug("level: %d, t: %u\n", new, t);
+	return new;
+}
+
+static s32 mlnx_calculate_ramp(const struct mlnx_effect *mlnxeff)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+	const unsigned long now = jiffies;
+	const u16 length = effect->replay.length;
+	const s16 mean = (effect->u.ramp.start_level + effect->u.ramp.end_level) / 2;
+	const u16 t = jiffies_to_msecs(now - mlnxeff->begin_at);
+	s32 start = effect->u.ramp.start_level;
+	s32 end = effect->u.ramp.end_level;
+	s32 new;
+
+	if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+		const s32 clength = jiffies_to_msecs(now - mlnxeff->begin_at);
+		const s32 alength = envelope->attack_length;
+		s32 attack_level;
+		if (end > start)
+			attack_level = mean - envelope->attack_level;
+		else
+			attack_level = mean + envelope->attack_level;
+		start = (start - attack_level) * clength / alength
+		      + attack_level;
+	} else if (envelope->fade_length && time_before_eq(mlnxeff->fade_begin, now)) {
+		const s32 clength = jiffies_to_msecs(now - mlnxeff->fade_begin);
+		const s32 flength = envelope->fade_length;
+		s32 fade_level;
+		if (end > start)
+			fade_level = mean + envelope->fade_level;
+		else
+			fade_level = mean - envelope->fade_level;
+		end = (fade_level - end) * clength / flength + end;
+	}
+
+	new = ((end - start) * t) / length + start;
+	new = clamp(new, -0x7fff, 0x7fff);
+	pr_debug("RAMP level: %d, t: %u\n", new, t);
+	return new;
+}
+
+static void mlnx_destroy(struct ff_device *dev)
+{
+	struct mlnx_device *mlnxdev = dev->private;
+	del_timer_sync(&mlnxdev->timer);
+
+	kfree(mlnxdev->private);
+}
+
+static unsigned long mlnx_get_envelope_update_time(const struct mlnx_effect *mlnxeff,
+						   const unsigned long update_rate_jiffies)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+	const unsigned long now = jiffies;
+	unsigned long fade_next;
+
+	/* Effect has an envelope with nonzero attack time */
+	if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+		if (time_before(mlnxeff->updated_at + update_rate_jiffies, mlnxeff->attack_stop))
+			return mlnxeff->updated_at + update_rate_jiffies;
+
+		return mlnxeff->attack_stop;
+	}
+
+	/* Effect has an envelope with nonzero fade time */
+	if (mlnxeff->effect.replay.length) {
+		if (!envelope->fade_length)
+			return mlnxeff->stop_at;
+
+		/* Schedule the next update when the fade begins */
+		if (time_before(mlnxeff->updated_at, mlnxeff->fade_begin))
+			return mlnxeff->fade_begin;
+
+		/* Already fading */
+		fade_next = mlnxeff->updated_at + update_rate_jiffies;
+
+		/* Schedule update when the effect stops */
+		if (time_after(fade_next, mlnxeff->stop_at))
+			return mlnxeff->stop_at;
+		/* Schedule update at the next checkpoint */
+			return fade_next;
+	}
+
+	/* There is no envelope */
+
+	/* Prevent the effect from being started twice */
+	if (mlnxeff->begin_at == now && mlnx_is_playing(mlnxeff))
+		return now - 1;
+
+	return mlnxeff->begin_at;
+}
+
+static unsigned long mlnx_get_update_time(struct mlnx_effect *mlnxeff,
+				const unsigned long update_rate_jiffies)
+{
+	const unsigned long now = jiffies;
+	unsigned long time, update_periodic;
+
+	switch (mlnxeff->effect.type) {
+	/* Constant effect does not change with time, but it can have
+	 * an envelope and a duration */
+	case FF_CONSTANT:
+		return mlnx_get_envelope_update_time(mlnxeff,
+						     update_rate_jiffies);
+	/* Periodic and ramp effects have to be periodically updated */
+	case FF_PERIODIC:
+	case FF_RAMP:
+		time = mlnx_get_envelope_update_time(mlnxeff,
+						     update_rate_jiffies);
+		if (mlnx_is_emulated(mlnxeff))
+			update_periodic = mlnxeff->stop_at;
+		else
+			update_periodic = mlnxeff->updated_at +
+					  update_rate_jiffies;
+
+		/* Periodic effect has to be updated earlier than envelope
+		 * or envelope update time is in the past */
+		if (time_before(update_periodic, time) || time_before(time, now))
+			return update_periodic;
+		/* Envelope needs to be updated */
+		return time;
+	case FF_RUMBLE:
+		if (mlnx_is_emulated(mlnxeff))
+			return mlnxeff->updated_at + update_rate_jiffies;
+	case FF_DAMPER:
+	case FF_FRICTION:
+	case FF_INERTIA:
+	case FF_SPRING:
+	default:
+		if (time_after_eq(mlnxeff->begin_at, now))
+			return mlnxeff->begin_at;
+
+		return mlnxeff->stop_at;
+	}
+}
+
+static void mlnx_schedule_playback(struct mlnx_device *mlnxdev)
+{
+	struct mlnx_effect *mlnxeff;
+	const unsigned long now = jiffies;
+	int events = 0;
+	int i;
+	unsigned long earliest = 0;
+	unsigned long time;
+
+	/* Iterate over all effects and determine the earliest
+	 * time when we have to attend to any */
+	for (i = 0; i < FF_MAX_EFFECTS; i++) {
+		mlnxeff = &mlnxdev->effects[i];
+
+		if (!mlnx_is_started(mlnxeff))
+			continue; /* Effect is not started, skip it */
+
+		if (mlnx_is_playing(mlnxeff))
+			time = mlnx_get_update_time(mlnxeff,
+						mlnxdev->update_rate_jiffies);
+		else
+			time = mlnxeff->begin_at;
+
+		pr_debug("Update time for effect %d: %lu\n", i, time);
+
+		/* Scheduled time is in the future and is either
+		 * before the current earliest time or it is
+		 * the first valid time value in this pass */
+		if (time_before_eq(now, time) &&
+		    (++events == 1 || time_before(time, earliest)))
+			earliest = time;
+	}
+
+	if (events) {
+		pr_debug("Events: %d, earliest: %lu\n", events, earliest);
+		mod_timer(&mlnxdev->timer, earliest);
+	} else {
+		pr_debug("No events, deactivating timer\n");
+		del_timer(&mlnxdev->timer);
+	}
+}
+
+static u16 mlnx_calculate_rumble_direction(const u32 total_mag, const u16 total_dir,
+					   const u32 new_mag, const u16 new_dir)
+{
+	if (!new_mag)
+		return total_dir;
+	if (!total_mag)
+		return new_dir;
+	return (((total_dir >> 1) * total_mag +
+		(new_dir >> 1) * new_mag) /
+		(total_mag + new_mag)) << 1;
+}
+
+static void mlnx_add_force(struct mlnx_effect *mlnxeff, s32 *cfx, s32 *cfy,
+			   const u16 gain)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	u16 direction;
+	s32 level;
+
+	pr_debug("Processing effect type %d, ID %d\n",
+		 mlnxeff->effect.type, mlnxeff->effect.id);
+
+	direction = mlnxeff->effect.direction * 360 / 0xffff;
+	pr_debug("Direction deg: %u\n", direction);
+
+	switch (mlnxeff->effect.type) {
+	case FF_CONSTANT:
+		level = mlnx_apply_envelope(mlnxeff, effect->u.constant.level);
+		break;
+	case FF_PERIODIC:
+		level = mlnx_apply_envelope(mlnxeff,
+					    effect->u.periodic.magnitude);
+		level = mlnx_calculate_periodic(mlnxeff, level);
+		break;
+	case FF_RAMP:
+		level = mlnx_calculate_ramp(mlnxeff);
+		break;
+	default:
+		pr_err("Effect %d not handled by mlnx_add_force\n",
+		       mlnxeff->effect.type);
+		return;
+	}
+
+	*cfx += mlnx_calculate_x_force(level, direction) * gain / 0xffff;
+	*cfy += mlnx_calculate_y_force(level, direction) * gain / 0xffff;
+}
+
+static void mlnx_add_rumble(const struct mlnx_effect *mlnxeff, u32 *strong_mag,
+			    u32 *weak_mag, u16 *strong_dir,
+			    u16 *weak_dir, const u16 gain)
+{
+	const struct ff_effect *eff = &mlnxeff->effect;
+	const struct ff_rumble_effect *reff = &mlnxeff->effect.u.rumble;
+	const u32 new_strong_mag = (u32)reff->strong_magnitude * gain / 0xffffU;
+	const u32 new_weak_mag = (u32)reff->weak_magnitude * gain / 0xffffU;
+
+	*strong_dir = mlnx_calculate_rumble_direction(*strong_mag, *strong_dir,
+						      new_strong_mag,
+						      eff->direction);
+	*weak_dir = mlnx_calculate_rumble_direction(*weak_mag, *weak_dir,
+						    new_weak_mag,
+						    eff->direction);
+	*strong_mag += new_strong_mag;
+	*weak_mag += new_weak_mag;
+}
+
+static void mlnx_add_emul_periodic(const struct mlnx_effect *mlnxeff,
+				   u32 *strong_mag, u32 *weak_mag,
+				   u16 *strong_dir, u16 *weak_dir,
+				   const u16 gain)
+{
+	const struct ff_effect *eff = &mlnxeff->effect;
+	const u32 level = (u32)abs(mlnx_apply_envelope(mlnxeff,
+						  eff->u.periodic.magnitude)) * gain / 0x7fffU;
+
+	*strong_dir = mlnx_calculate_rumble_direction(*strong_mag, *strong_dir,
+						      level, eff->direction);
+	*weak_dir = mlnx_calculate_rumble_direction(*weak_mag, *weak_dir,
+						    level, eff->direction);
+
+	*strong_mag += level;
+	*weak_mag += level;
+}
+
+static void mlnx_add_emul_rumble(const struct mlnx_effect *mlnxeff, s32 *cfx,
+				 s32 *cfy, const u16 gain,
+				 const unsigned long now,
+				 const unsigned long update_rate_jiffies)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const u16 strong = effect->u.rumble.strong_magnitude;
+	const u16 weak = effect->u.rumble.weak_magnitude;
+	/* To calculate 't', we pretend that mlnxeff->begin_at == 0, thus t == now.  */
+	/* This will synchronise all simultaneously playing emul rumble effects,     */
+	/* otherwise non-deterministic phase-inversions could occur depending on     */
+	/* upload time, which could lead to undesired cancellation of these effects. */
+	const unsigned long t = now % (4UL * update_rate_jiffies);
+	s32 level = 0;
+	bool direction_up;
+	bool direction_left;
+
+	if (strong)
+		level += (strong / 4) * (t < 2UL * update_rate_jiffies ? 1 : -1);
+	if (weak)
+		level += (weak / 4) * (t < 2UL * update_rate_jiffies ?
+					(t < 1UL * update_rate_jiffies ? 1 : -1) :
+					(t < 3UL * update_rate_jiffies ? 1 : -1));
+	direction_up = (effect->direction > 0x3fffU && effect->direction <= 0xbfffU);
+	direction_left = (effect->direction <= 0x7fffU);
+
+	pr_debug("Emulated cf: %d, t: %lu, n: %lu, begin: %lu, diff: %lu j: %lu\n",
+		 level, t, now, mlnxeff->begin_at, now - mlnxeff->begin_at,
+		 update_rate_jiffies);
+	level = (level * gain) / 0xffff;
+	*cfx += direction_left ? -level : level;
+	*cfy += direction_up ? -level : level;
+}
+
+static void mlnx_play_effects(struct mlnx_device *mlnxdev)
+{
+	const u16 gain = mlnxdev->gain;
+	const unsigned long now = jiffies;
+	int i;
+	s32 cfx = 0;
+	s32 cfy = 0;
+	u32 strong_mag = 0;
+	u32 weak_mag = 0;
+	u16 strong_dir = 0;
+	u16 weak_dir = 0;
+
+	for (i = 0; i < FF_MAX_EFFECTS; i++) {
+		struct mlnx_effect *mlnxeff = &mlnxdev->effects[i];
+
+		if (!mlnx_is_started(mlnxeff)) {
+			pr_debug("Effect %hd/%d not started\n",
+				 mlnxeff->effect.id, i);
+			continue;
+		}
+
+		if (time_before(now, mlnxeff->begin_at)) {
+			pr_debug("Effect %hd/%d begins at a later time\n",
+				 mlnxeff->effect.id, i);
+			continue;
+		}
+
+		if (time_before_eq(mlnxeff->stop_at, now) && mlnxeff->effect.replay.length) {
+			pr_debug("Effect %hd/%d has to be stopped\n",
+				 mlnxeff->effect.id, i);
+
+			mlnx_clr_playing(mlnxeff);
+			if (--mlnxeff->repeat > 0)
+				mlnx_restart_effect(mlnxdev, mlnxeff);
+			else {
+				mlnx_stop_effect(mlnxdev, mlnxeff);
+				mlnx_clr_started(mlnxeff);
+				mlnx_clr_emulated(mlnxeff);
+				if (mlnx_is_conditional(&mlnxeff->effect))
+					mlnx_erase_conditional(mlnxdev, &mlnxeff->effect);
+			}
+
+			continue;
+		}
+
+		switch (mlnxeff->effect.type) {
+		case FF_PERIODIC:
+			if (mlnxdev->emul == EMUL_PERIODIC) {
+				if (!mlnx_test_set_playing(mlnxeff)) {
+					mlnxdev->rumble_playing++;
+					pr_debug("Starting emul periodic, total rumble %u\n",
+						 mlnxdev->rumble_playing);
+				}
+				__set_bit(FF_EFFECT_EMULATED, &mlnxeff->flags);
+				mlnx_add_emul_periodic(mlnxeff, &strong_mag, &weak_mag,
+						       &strong_dir, &weak_dir, gain);
+				break;
+			}
+		case FF_CONSTANT:
+		case FF_RAMP:
+			if (!mlnx_test_set_playing(mlnxeff)) {
+				mlnxdev->combinable_playing++;
+				pr_debug("Starting combinable effect, total %u\n",
+					 mlnxdev->combinable_playing);
+			}
+			mlnx_add_force(mlnxeff, &cfx, &cfy, gain);
+			break;
+		case FF_RUMBLE:
+			if (mlnxdev->emul == EMUL_RUMBLE) {
+				if (!mlnx_test_set_playing(mlnxeff)) {
+					mlnxdev->combinable_playing++;
+					pr_debug("Starting emul rumble, total comb %u\n",
+						 mlnxdev->combinable_playing);
+				}
+				__set_bit(FF_EFFECT_EMULATED, &mlnxeff->flags);
+				mlnx_add_emul_rumble(mlnxeff, &cfx, &cfy, gain, now,
+						     mlnxdev->update_rate_jiffies);
+			} else {
+				if (!mlnx_test_set_playing(mlnxeff)) {
+					mlnxdev->rumble_playing++;
+					pr_debug("Starting rumble effect, total %u\n",
+						 mlnxdev->rumble_playing);
+				}
+				mlnx_add_rumble(mlnxeff, &strong_mag, &weak_mag,
+						&strong_dir, &weak_dir, gain);
+			}
+			break;
+		case FF_DAMPER:
+		case FF_FRICTION:
+		case FF_INERTIA:
+		case FF_SPRING:
+			if (!mlnx_test_set_playing(mlnxeff)) {
+				const struct mlnx_effect_command ecmd = {
+					.cmd = MLNX_START_UNCOMB,
+					.u.uncomb.id = i,
+					.u.uncomb.effect = &mlnxeff->effect
+				};
+				mlnxdev->control_effect(mlnxdev->dev,
+						      mlnxdev->private, &ecmd);
+			}
+			break;
+		default:
+			pr_debug("Unhandled type of effect\n");
+		}
+		mlnxeff->updated_at = now;
+	}
+
+	if (mlnxdev->combinable_playing) {
+		const struct mlnx_effect_command ecmd = {
+			.cmd = MLNX_START_COMBINED,
+			.u.simple_force = {
+				.x = clamp(cfx, -0x7fff, 0x7fff),
+				.y = clamp(cfy, -0x7fff, 0x7fff)
+			}
+		};
+		mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+	}
+	if (mlnxdev->rumble_playing) {
+		const struct mlnx_effect_command ecmd = {
+			.cmd = MLNX_START_RUMBLE,
+			.u.rumble_force = {
+				.strong = clamp(strong_mag, (u32)0, (u32)0xffffU),
+				.weak = clamp(weak_mag, (u32)0, (u32)0xffffU),
+				.strong_dir = clamp(strong_dir, (u16)0, (u16)0xffffU),
+				.weak_dir = clamp(weak_dir, (u16)0, (u16)0xffffU)
+			}
+		};
+		mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+	}
+
+	mlnx_schedule_playback(mlnxdev);
+}
+
+static void mlnx_set_gain(struct input_dev *dev, u16 gain)
+{
+	struct mlnx_device *mlnxdev = dev->ff->private;
+	int i;
+
+	mlnxdev->gain = gain;
+
+	for (i = 0; i < FF_MAX_EFFECTS; i++) {
+		struct mlnx_effect *eff = &mlnxdev->effects[i];
+		if (eff == NULL)
+			continue;
+		if (mlnx_is_playing(eff)) {
+			if (mlnx_is_combinable(&eff->effect)) {
+				mlnx_clr_playing(eff);
+				if (mlnx_is_emulated(eff))
+					--mlnxdev->rumble_playing;
+				else
+					--mlnxdev->combinable_playing;
+			} else if (mlnx_is_rumble(&eff->effect)) {
+				mlnx_clr_playing(eff);
+				if (mlnx_is_emulated(eff))
+					--mlnxdev->combinable_playing;
+				else
+					--mlnxdev->rumble_playing;
+			}
+		}
+	}
+
+	mlnx_play_effects(mlnxdev);
+}
+
+static int mlnx_startstop(struct input_dev *dev, int effect_id, int repeat)
+{
+	struct mlnx_device *mlnxdev = dev->ff->private;
+	struct mlnx_effect *mlnxeff = &mlnxdev->effects[effect_id];
+	int ret;
+
+	if (repeat > 0) {
+		pr_debug("Starting effect ID %d\n", effect_id);
+		mlnxeff->repeat = repeat;
+
+		if (!mlnx_is_started(mlnxeff)) {
+			/* Check that device has a free effect slot */
+			if (mlnx_is_conditional(&mlnxeff->effect)) {
+				ret = mlnx_upload_conditional(mlnxdev, &mlnxeff->effect);
+				if (ret) {
+					/* Device effect slots are all occupied */
+					pr_debug("No free effect slot for EID %d\n", effect_id);
+					return ret;
+				}
+			}
+			mlnx_start_effect(mlnxeff);
+		}
+	} else {
+		pr_debug("Stopping effect ID %d\n", effect_id);
+		if (mlnx_is_started(mlnxeff)) {
+			if (mlnx_is_playing(mlnxeff)) {
+				mlnx_clr_playing(mlnxeff);
+				mlnx_stop_effect(mlnxdev, mlnxeff);
+			}
+			mlnx_clr_started(mlnxeff);
+			mlnx_clr_emulated(mlnxeff);
+
+			if (mlnx_is_conditional(&mlnxeff->effect))
+				return mlnx_erase_conditional(mlnxdev, &mlnxeff->effect);
+		} else {
+			pr_debug("Effect ID %d already stopped\n", effect_id);
+			return 0;
+		}
+	}
+	mlnx_play_effects(mlnxdev);
+
+	return 0;
+}
+
+static void mlnx_timer_fired(unsigned long data)
+{
+	struct input_dev *dev = (struct input_dev *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	mlnx_play_effects(dev->ff->private);
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int mlnx_upload(struct input_dev *dev, struct ff_effect *effect,
+		       struct ff_effect *old)
+{
+	struct mlnx_device *mlnxdev = dev->ff->private;
+	struct mlnx_effect *mlnxeff = &mlnxdev->effects[effect->id];
+	const u16 length = effect->replay.length;
+	const u16 delay = effect->replay.delay;
+	int ret, fade_from;
+
+	/* Effect's timing is below kernel timer resolution */
+	if (length && length < FF_MIN_EFFECT_LENGTH)
+		effect->replay.length = FF_MIN_EFFECT_LENGTH;
+	if (delay && delay < FF_MIN_EFFECT_LENGTH)
+		effect->replay.delay = FF_MIN_EFFECT_LENGTH;
+
+	/* Periodic effects must have a non-zero period */
+	if (effect->type == FF_PERIODIC) {
+		if (!effect->u.periodic.period)
+			return -EINVAL;
+	}
+	/* Ramp effects cannot be infinite */
+	if (effect->type == FF_RAMP && !length)
+		return -EINVAL;
+
+	if (mlnx_is_combinable(effect)) {
+		const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+
+		/* Infinite effects cannot fade */
+		if (!length && envelope->fade_length > 0)
+			return -EINVAL;
+		/* Fade length cannot be greater than effect duration */
+		fade_from = (int)length - (int)envelope->fade_length;
+		if (fade_from < 0)
+			return -EINVAL;
+		/* Envelope cannot start fading before it finishes attacking */
+		if (fade_from < envelope->attack_length && fade_from > 0)
+			return -EINVAL;
+	}
+
+
+	spin_lock_irq(&dev->event_lock);
+	mlnxeff->effect = *effect; /* Keep internal copy of the effect */
+	/* Check if the effect being modified is playing */
+	if (mlnx_is_started(mlnxeff)) {
+		if (mlnx_is_playing(mlnxeff)) {
+			mlnx_clr_playing(mlnxeff);
+			ret = mlnx_restart_effect(mlnxdev, mlnxeff);
+
+			if (ret) {
+				/* Restore the original effect */
+				if (old)
+					mlnxeff->effect = *old;
+				spin_unlock_irq(&dev->event_lock);
+				return ret;
+			}
+		}
+
+		mlnx_schedule_playback(mlnxdev);
+	}
+
+	spin_unlock_irq(&dev->event_lock);
+
+	return 0;
+}
+
+int input_ff_create_mlnx(struct input_dev *dev, void *data,
+			 int (*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *),
+			 const u16 update_rate)
+{
+	struct mlnx_device *mlnxdev;
+	int ret;
+	const u16 min_update_rate = update_rate < FF_MIN_EFFECT_LENGTH ?
+				    FF_MIN_EFFECT_LENGTH : update_rate;
+
+	mlnxdev = kzalloc(sizeof(*mlnxdev), GFP_KERNEL);
+	if (!mlnxdev)
+		return -ENOMEM;
+
+	mlnxdev->dev = dev;
+	mlnxdev->private = data;
+	mlnxdev->control_effect = control_effect;
+	mlnxdev->gain = 0xffff;
+	mlnxdev->update_rate_jiffies = msecs_to_jiffies(min_update_rate);
+	input_set_capability(dev, EV_FF, FF_GAIN);
+	setup_timer(&mlnxdev->timer, mlnx_timer_fired, (unsigned long)dev);
+
+	/* Set up effect emulation if needed */
+	if (test_bit(FF_PERIODIC, dev->ffbit) &&
+	    !test_bit(FF_RUMBLE, dev->ffbit)) {
+		set_bit(FF_RUMBLE, dev->ffbit);
+		mlnxdev->emul = EMUL_RUMBLE;
+		pr_debug("Emulating RUMBLE with PERIODIC\n");
+	} else if (test_bit(FF_RUMBLE, dev->ffbit) &&
+		   !test_bit(FF_PERIODIC, dev->ffbit)) {
+		set_bit(FF_PERIODIC, dev->ffbit);
+		set_bit(FF_SINE, dev->ffbit);
+		set_bit(FF_SQUARE, dev->ffbit);
+		set_bit(FF_TRIANGLE, dev->ffbit);
+		set_bit(FF_SAW_DOWN, dev->ffbit);
+		set_bit(FF_SAW_UP, dev->ffbit);
+		mlnxdev->emul = EMUL_PERIODIC;
+		pr_debug("Emulating PERIODIC with RUMBLE\n");
+	} else {
+		mlnxdev->emul = EMUL_NOTHING;
+		pr_debug("No effect emulation is necessary\n");
+	}
+
+	ret = input_ff_create(dev, FF_MAX_EFFECTS);
+	if (ret) {
+		kfree(mlnxdev);
+		return ret;
+	}
+
+
+	dev->ff->private = mlnxdev;
+	dev->ff->upload = mlnx_upload;
+	dev->ff->set_gain = mlnx_set_gain;
+	dev->ff->destroy = mlnx_destroy;
+	dev->ff->playback = mlnx_startstop;
+
+	pr_debug("Device successfully registered.\n");
+	return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_create_mlnx);
diff --git a/include/linux/input/ff-memless-next.h b/include/linux/input/ff-memless-next.h
new file mode 100644
index 0000000..7522451
--- /dev/null
+++ b/include/linux/input/ff-memless-next.h
@@ -0,0 +1,162 @@
+#include <linux/input.h>
+
+/** DEFINITION OF TERMS
+ *
+ * Combined effect - An effect whose force is a superposition of forces
+ *                   generated by all effects that can be added together.
+ *                   Only one combined effect can be playing at a time.
+ *                   Effects that can be added together to create a combined
+ *                   effect are FF_CONSTANT, FF_PERIODIC and FF_RAMP.
+ * Uncombinable effect - An effect that cannot be combined with another effect.
+ *                       All conditional effects - FF_DAMPER, FF_FRICTION,
+ *                       FF_INERTIA and FF_SPRING are uncombinable.
+ *                       Number of uncombinable effects playing simultaneously
+ *                       depends on the capabilities of the hardware.
+ * Rumble effect - An effect generated by device's rumble motors instead of
+ *                 force feedback actuators.
+ *
+ *
+ * HANDLING OF UNCOMBINABLE EFFECTS
+ *
+ * Uncombinable effects cannot be combined together into just one effect, at
+ * least not in a clear and obvious manner. Therefore these effects have to
+ * be handled individually by ff-memless-next. Handling of these effects is
+ * left entirely to the hardware-specific driver, ff-memless-next merely
+ * passes these effects to the hardware-specific driver at appropriate time.
+ * ff-memless-next provides the UPLOAD command to notify the hardware-specific
+ * driver that the userspace is about to request playback of an uncombinable
+ * effect. The hardware-specific driver shall take all steps needed to make
+ * the device ready to play the effect when it receives the UPLOAD command.
+ * The actual playback shall commence when START_UNCOMB command is received.
+ * Opposite to the UPLOAD command is the ERASE command which tells
+ * the hardware-specific driver that the playback has finished and that
+ * the effect will not be restarted. STOP_UNCOMB command tells
+ * the hardware-specific driver that the playback shall stop but the device
+ * shall still be ready to resume the playback immediately.
+ *
+ * In case it is not possible to make the device ready to play an uncombinable
+ * effect (all hardware effect slots are occupied), the hardware-specific
+ * driver may return an error when it receives an UPLOAD command. If the
+ * hardware-specific driver returns 0, the upload is considered successful.
+ * START_UNCOMB and STOP_UNCOMB commands cannot fail and the device must always
+ * start the playback of the requested effect if the UPLOAD command of the
+ * respective effect has been successful. ff-memless-next will never send
+ * a START/STOP_UNCOMB command for an effect that has not been uploaded
+ * successfully, nor will it send an ERASE command for an effect that is
+ * playing (= has been started with START_UNCOMB command).
+ */
+
+enum mlnx_commands {
+	/* Start or update a combined effect. This command is sent whenever
+	 * a FF_CONSTANT, FF_PERIODIC or FF_RAMP is started, stopped or
+	 * updated by userspace, when the applied envelopes are recalculated
+	 * or when periodic effects are recalculated. */
+	MLNX_START_COMBINED,
+	/* Stop combined effect. This command is sent when all combinable
+	 * effects are stopped. */
+	MLNX_STOP_COMBINED,
+	/* Start or update a rumble effect. This command is sent whenever
+	 * a FF_RUMBLE effect is started or when its magnitudes or directions
+	 * change. */
+	MLNX_START_RUMBLE,
+	/* Stop a rumble effect. This command is sent when all FF_RUMBLE
+	 * effects are stopped. */
+	MLNX_STOP_RUMBLE,
+	/* Start or update an uncombinable effect. This command is sent
+	 * whenever an uncombinable effect is started or updated. */
+	MLNX_START_UNCOMB,
+	/* Stop uncombinable effect. This command is sent when an uncombinable
+	 * effect is stopped. */
+	MLNX_STOP_UNCOMB,
+	/* Upload uncombinable effect to device. This command is sent when the
+	 * effect is started from userspace. It is up to the hardware-specific
+	 * driver to handle this situation.
+	 */
+	MLNX_UPLOAD_UNCOMB,
+	/* Remove uncombinable effect from device, This command is sent when
+	 * and uncombinable effect has finished playing and will not be
+	 * restarted.
+	 */
+	MLNX_ERASE_UNCOMB
+};
+
+/** struct mlnx_simple_force - holds constant forces along X and Y axis
+ * @x: Force along X axis. Negative value denotes force pulling to the left,
+ *     positive value denotes force pulling to the right.
+ * @y: Force along Y axis. Negative value denotes force denotes force pulling
+ *     away from the user, positive value denotes force pulling towards
+ *     the user.
+ */
+struct mlnx_simple_force {
+	const s32 x;
+	const s32 y;
+};
+
+/** struct mlnx_rumble_force - holds information about rumble effect
+ * @strong: Magnitude of the strong vibration.
+ * @weak: Magnitude of the weak vibration.
+ * @strong_dir: Direction of the strong vibration expressed in the same way
+ *              as the direction of force feedback effect in struct ff_effect.
+ * @weak_dir: Direction of the weak vibration, same as above applies.
+ */
+struct mlnx_rumble_force {
+	const u32 strong;
+	const u32 weak;
+	const u16 strong_dir;
+	const u16 weak_dir;
+};
+
+/** struct mlnx_uncomb_effect - holds information about uncombinable effect
+ * @id: Id of the effect assigned by ff-core.
+ * @effect: Pointer to the uncombinable effect stored in ff-memless-next module
+ *          Hardware-specific driver must not alter this.
+ */
+struct mlnx_uncomb_effect {
+	const int id;
+	const struct ff_effect *effect;
+};
+
+/** struct mlnx_commands - describes what action shall the force feedback
+ *                         device perform
+ * @cmd: Type of the action.
+ * @u: Data associated with the action.
+ */
+struct mlnx_effect_command {
+	const enum mlnx_commands cmd;
+	union {
+		const struct mlnx_simple_force simple_force;
+		const struct mlnx_rumble_force rumble_force;
+		const struct mlnx_uncomb_effect uncomb;
+	} u;
+};
+
+/** input_ff_create_mlnx() - Register a device within ff-memless-next and
+ *                           the kernel force feedback system
+ * @dev: Pointer to the struct input_dev associated with the device.
+ * @data: Any device-specific data that shall be passed to the callback.
+ *        function called by ff-memless-next when a force feedback action
+ *        shall be performed.
+ * @control_effect: Pointer to the callback function.
+ * @update_date: Delay in milliseconds between two recalculations of periodic
+ *               effects, ramp effects and envelopes. Note that this value will
+ *               never be lower than (CONFIG_HZ / 1000) + 1 regardless of the
+ *               value specified here. This is not a "hard" rate limiter.
+ *               Userspace still can submit effects at a rate faster than
+ *               this value.
+ */
+int input_ff_create_mlnx(struct input_dev *dev, void *data,
+			 int (*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *),
+			 const u16 update_rate);
+
+/** int control_effect() - Callback to the HW-specific driver.
+ * @struct input_dev *: Pointer to the struct input_dev of the device that is
+ *                     is being controlled.
+ * @void *: Pointer to any device-specific data set by the HW-specific driver.
+ *         This data will be free'd automatically by ff-memless-next when the
+ *         device is destroyed.
+ * @const struct mlnx_effect_command *:
+ *         Action the device shall perform. Note that this pointer is valid
+ *         only within the context of the callback function. If the HW-specific
+ *         driver needs any data from this structure after the callback
+ *         function returns, it must copy it.
+ */
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-04-26 15:02   ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Add ff-memless-next module

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>
---
 drivers/input/Kconfig                 |   11 +
 drivers/input/Makefile                |    1 +
 drivers/input/ff-memless-next.c       | 1037 +++++++++++++++++++++++++++++++++
 include/linux/input/ff-memless-next.h |  162 +++++
 4 files changed, 1211 insertions(+)
 create mode 100644 drivers/input/ff-memless-next.c
 create mode 100644 include/linux/input/ff-memless-next.h

diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index a11ff74..3780962 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -38,6 +38,17 @@ config INPUT_FF_MEMLESS
 	  To compile this driver as a module, choose M here: the
 	  module will be called ff-memless.
 
+config INPUT_FF_MEMLESS_NEXT
+	tristate "New version of support for memoryless force-feedback devices"
+	help
+	  Say Y here to enable a new version of support for memoryless force
+	  feedback devices.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ff-memless-next.
+
 config INPUT_POLLDEV
 	tristate "Polled input device skeleton"
 	help
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 5ca3f63..b4f11f5 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_INPUT)		+= input-core.o
 input-core-y := input.o input-compat.o input-mt.o ff-core.o
 
 obj-$(CONFIG_INPUT_FF_MEMLESS)	+= ff-memless.o
+obj-$(CONFIG_INPUT_FF_MEMLESS_NEXT)	+= ff-memless-next.o
 obj-$(CONFIG_INPUT_POLLDEV)	+= input-polldev.o
 obj-$(CONFIG_INPUT_SPARSEKMAP)	+= sparse-keymap.o
 obj-$(CONFIG_INPUT_MATRIXKMAP)	+= matrix-keymap.o
diff --git a/drivers/input/ff-memless-next.c b/drivers/input/ff-memless-next.c
new file mode 100644
index 0000000..24619e9
--- /dev/null
+++ b/drivers/input/ff-memless-next.c
@@ -0,0 +1,1037 @@
+/*
+ * Force feedback support for memoryless devices
+ *
+ * This module is based on "ff-memless" orignally written by Anssi Hannula.
+ * It is extended to support all force feedback effects currently supported
+ * by the Linux input stack.
+ * Logic of emulation of FF_RUMBLE through FF_PERIODIC provided by
+ * Elias Vanderstuyft <elias.vds@gmail.com>
+ *
+ * Copyright(c) 2014 Michal "MadCatX" Maly <madcatxster@devoid-pointer.net>
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/jiffies.h>
+#include <linux/fixp-arith.h>
+#include <linux/input/ff-memless-next.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal \"MadCatX\" Maly");
+MODULE_DESCRIPTION("Force feedback support for memoryless force feedback devices");
+
+#define FF_MAX_EFFECTS 16
+#define FF_MIN_EFFECT_LENGTH ((MSEC_PER_SEC / CONFIG_HZ) + 1)
+#define FF_EFFECT_STARTED 1
+#define FF_EFFECT_PLAYING 2
+#define FF_EFFECT_EMULATED 3
+
+enum mlnx_emulate {
+	EMUL_NOTHING,	/* Do not emulate anything */
+	EMUL_RUMBLE,	/* Emulate FF_RUMBLE with FF_PERIODIC */
+	EMUL_PERIODIC	/* Emulate FF_PERIODIC with FF_RUMBLE */
+};
+
+struct mlnx_effect {
+	struct ff_effect effect;
+	unsigned long flags;
+	unsigned long begin_at;
+	unsigned long stop_at;
+	unsigned long updated_at;
+	unsigned long attack_stop;
+	unsigned long fade_begin;
+	int repeat;
+};
+
+struct mlnx_device {
+	u8 combinable_playing;
+	u8 rumble_playing;
+	unsigned long update_rate_jiffies;
+	void *private;
+	struct mlnx_effect effects[FF_MAX_EFFECTS];
+	u16 gain;
+	struct timer_list timer;
+	struct input_dev *dev;
+	enum mlnx_emulate emul;
+
+	int (*control_effect)(struct input_dev *, void *,
+			      const struct mlnx_effect_command *);
+};
+
+static s32 mlnx_calculate_x_force(const s32 level,
+						  const u16 direction)
+{
+	s32 new = (level * -fixp_sin(direction)) >> FRAC_N;
+	pr_debug("x force: %d\n", new);
+	return new;
+}
+
+static s32 mlnx_calculate_y_force(const s32 level,
+						  const u16 direction)
+{
+	s32 new = (level * fixp_cos(direction)) >> FRAC_N;
+	pr_debug("y force: %d\n", new);
+	return new;
+}
+
+static bool mlnx_is_combinable(const struct ff_effect *effect)
+{
+	switch (effect->type) {
+	case FF_CONSTANT:
+	case FF_PERIODIC:
+	case FF_RAMP:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool mlnx_is_conditional(const struct ff_effect *effect)
+{
+	switch (effect->type) {
+	case FF_DAMPER:
+	case FF_FRICTION:
+	case FF_INERTIA:
+	case FF_SPRING:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void mlnx_clr_emulated(struct mlnx_effect *mlnxeff)
+{
+	__clear_bit(FF_EFFECT_EMULATED, &mlnxeff->flags);
+}
+
+static void mlnx_clr_playing(struct mlnx_effect *mlnxeff)
+{
+	__clear_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+}
+
+static void mlnx_clr_started(struct mlnx_effect *mlnxeff)
+{
+	__clear_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static bool mlnx_is_emulated(const struct mlnx_effect *mlnxeff)
+{
+	return test_bit(FF_EFFECT_EMULATED, &mlnxeff->flags);
+}
+
+static bool mlnx_is_rumble(const struct ff_effect *effect)
+{
+	return effect->type == FF_RUMBLE;
+}
+
+static bool mlnx_is_playing(const struct mlnx_effect *mlnxeff)
+{
+	return test_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+}
+
+static bool mlnx_is_started(const struct mlnx_effect *mlnxeff)
+{
+	return test_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static bool mlnx_test_set_playing(struct mlnx_effect *mlnxeff)
+{
+	return test_and_set_bit(FF_EFFECT_PLAYING, &mlnxeff->flags);
+}
+
+static const struct ff_envelope *mlnx_get_envelope(const struct ff_effect *effect)
+{
+	static const struct ff_envelope empty;
+
+	switch (effect->type) {
+	case FF_CONSTANT:
+		return &effect->u.constant.envelope;
+	case FF_PERIODIC:
+		return &effect->u.periodic.envelope;
+	case FF_RAMP:
+		return &effect->u.ramp.envelope;
+	default:
+		return &empty;
+	}
+}
+
+/* Some devices might have a limit on how many uncombinable effects
+ * can be played at once */
+static int mlnx_upload_conditional(struct mlnx_device *mlnxdev,
+				   const struct ff_effect *effect)
+{
+	struct mlnx_effect_command ecmd = {
+		.cmd = MLNX_UPLOAD_UNCOMB,
+		.u.uncomb.id = effect->id,
+		.u.uncomb.effect = effect
+	};
+	return mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+}
+
+static int mlnx_erase_conditional(struct mlnx_device *mlnxdev,
+				  const struct ff_effect *effect)
+{
+	struct mlnx_effect_command ecmd = {
+		.cmd = MLNX_ERASE_UNCOMB,
+		.u.uncomb.id = effect->id,
+		.u.uncomb.effect = effect
+	};
+	return mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+}
+
+static void mlnx_set_envelope_times(struct mlnx_effect *mlnxeff)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+
+	if (envelope->attack_length) {
+		unsigned long j = msecs_to_jiffies(envelope->attack_length);
+		mlnxeff->attack_stop = mlnxeff->begin_at + j;
+	}
+	if (effect->replay.length && envelope->fade_length) {
+		unsigned long j = msecs_to_jiffies(envelope->fade_length);
+		mlnxeff->fade_begin = mlnxeff->stop_at - j;
+	}
+}
+
+static void mlnx_set_trip_times(struct mlnx_effect *mlnxeff,
+				const unsigned long now)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const u16 replay_delay = effect->replay.delay;
+	const u16 replay_length = effect->replay.length;
+
+	mlnxeff->begin_at = now + msecs_to_jiffies(replay_delay);
+	mlnxeff->stop_at = mlnxeff->begin_at + msecs_to_jiffies(replay_length);
+	mlnxeff->updated_at = mlnxeff->begin_at;
+}
+
+static void mlnx_start_effect(struct mlnx_effect *mlnxeff)
+{
+	const unsigned long now = jiffies;
+
+	mlnx_set_trip_times(mlnxeff, now);
+	mlnx_set_envelope_times(mlnxeff);
+	__set_bit(FF_EFFECT_STARTED, &mlnxeff->flags);
+}
+
+static void mlnx_stop_effect(struct mlnx_device *mlnxdev,
+			     const struct mlnx_effect *mlnxeff)
+{
+	switch (mlnxeff->effect.type) {
+	case FF_PERIODIC:
+		if (mlnx_is_emulated(mlnxeff)) {
+			if (--mlnxdev->rumble_playing == 0) {
+				const struct mlnx_effect_command c = {
+					.cmd = MLNX_STOP_RUMBLE
+				};
+				mlnxdev->control_effect(mlnxdev->dev,
+							mlnxdev->private, &c);
+			}
+			return;
+		}
+	case FF_CONSTANT:
+	case FF_RAMP:
+		if (--mlnxdev->combinable_playing == 0) {
+			const struct mlnx_effect_command c = {
+				.cmd = MLNX_STOP_COMBINED
+			};
+			mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private,
+						&c);
+		}
+		return;
+	case FF_RUMBLE:
+		if (mlnx_is_emulated(mlnxeff)) {
+			if (--mlnxdev->combinable_playing == 0) {
+				const struct mlnx_effect_command c = {
+					.cmd = MLNX_STOP_COMBINED
+				};
+				mlnxdev->control_effect(mlnxdev->dev,
+							mlnxdev->private, &c);
+			}
+		} else {
+			if (--mlnxdev->rumble_playing == 0) {
+				const struct mlnx_effect_command c = {
+					.cmd = MLNX_STOP_RUMBLE
+				};
+				mlnxdev->control_effect(mlnxdev->dev,
+							mlnxdev->private, &c);
+			}
+		}
+		return;
+	case FF_DAMPER:
+	case FF_FRICTION:
+	case FF_INERTIA:
+	case FF_SPRING:
+	{
+		const struct mlnx_effect_command c = {
+			.cmd = MLNX_STOP_UNCOMB,
+			.u.uncomb.id = mlnxeff->effect.id,
+			.u.uncomb.effect = &mlnxeff->effect
+		};
+		mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &c);
+		return;
+	}
+	default:
+		return;
+	}
+}
+
+static int mlnx_restart_effect(struct mlnx_device *mlnxdev,
+			       struct mlnx_effect *mlnxeff)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const unsigned long now = jiffies;
+
+	if (mlnx_is_combinable(effect)) {
+		if (effect->replay.delay)
+			mlnx_stop_effect(mlnxdev, mlnxeff);
+		else {
+			if (mlnx_is_emulated(mlnxeff))
+				mlnxdev->rumble_playing--;
+			else
+				mlnxdev->combinable_playing--;
+		}
+	} else if (mlnx_is_rumble(effect)) {
+		if (effect->replay.delay)
+			mlnx_stop_effect(mlnxdev, mlnxeff);
+		else {
+			if (mlnx_is_emulated(mlnxeff))
+				mlnxdev->combinable_playing--;
+			else
+				mlnxdev->rumble_playing--;
+		}
+	} else if (mlnx_is_conditional(effect)) {
+		int ret;
+		if (effect->replay.delay)
+			mlnx_stop_effect(mlnxdev, mlnxeff);
+
+		ret = mlnx_upload_conditional(mlnxdev, &mlnxeff->effect);
+		if (ret)
+			return ret;
+	}
+
+	mlnx_set_trip_times(mlnxeff, now);
+	mlnx_set_envelope_times(mlnxeff);
+
+	return 0;
+}
+
+static s32 mlnx_apply_envelope(const struct mlnx_effect *mlnxeff,
+			       const s32 level)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+	const unsigned long now = jiffies;
+	const s32 alevel = abs(level);
+
+	/* Effect has an envelope with nonzero attack time */
+	if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+		const s32 clength = jiffies_to_msecs(now - mlnxeff->begin_at);
+		const s32 alength = envelope->attack_length;
+		const s32 dlevel = (alevel - envelope->attack_level)
+				 * clength / alength;
+		return level < 0 ? -(dlevel + envelope->attack_level) :
+				    (dlevel + envelope->attack_level);
+	} else if (envelope->fade_length && time_before_eq(mlnxeff->fade_begin, now)) {
+		const s32 clength = jiffies_to_msecs(now - mlnxeff->fade_begin);
+		const s32 flength = envelope->fade_length;
+		const s32 dlevel = (envelope->fade_level - alevel)
+				 * clength / flength;
+		return level < 0 ? -(dlevel + alevel) : (dlevel + alevel);
+	}
+
+	return level;
+}
+
+static s32 mlnx_calculate_periodic(struct mlnx_effect *mlnxeff, const s32 level)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const unsigned long now = jiffies;
+	const u16 period = effect->u.periodic.period;
+	const u16 phase = effect->u.periodic.phase;
+	const s16 offset = effect->u.periodic.offset;
+	s32 new = level;
+	u16 t = (jiffies_to_msecs(now - mlnxeff->begin_at) + phase) % period;
+
+	switch (effect->u.periodic.waveform) {
+	case FF_SINE:
+	{
+		u16 degrees = (360 * t) / period;
+		new = ((level * fixp_sin(degrees)) >> FRAC_N) + offset;
+		break;
+	}
+	case FF_SQUARE:
+	{
+		u16 degrees = (360 * t) / period;
+		new = level * (degrees < 180 ? 1 : -1) + offset;
+		break;
+	}
+	case FF_SAW_UP:
+		new = 2 * level * t / period - level + offset;
+		break;
+	case FF_SAW_DOWN:
+		new = level - 2 * level * t / period + offset;
+		break;
+	case FF_TRIANGLE:
+	{
+		new = (2 * abs(level - (2 * level * t) / period));
+		new = new - abs(level) + offset;
+		break;
+	}
+	case FF_CUSTOM:
+		pr_debug("Custom waveform is not handled by this driver\n");
+		return level;
+	default:
+		pr_err("Invalid waveform\n");
+		return level;
+	}
+
+	/* Ensure that the offset did not make the value exceed s16 range */
+	new = clamp(new, -0x7fff, 0x7fff);
+	pr_debug("level: %d, t: %u\n", new, t);
+	return new;
+}
+
+static s32 mlnx_calculate_ramp(const struct mlnx_effect *mlnxeff)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+	const unsigned long now = jiffies;
+	const u16 length = effect->replay.length;
+	const s16 mean = (effect->u.ramp.start_level + effect->u.ramp.end_level) / 2;
+	const u16 t = jiffies_to_msecs(now - mlnxeff->begin_at);
+	s32 start = effect->u.ramp.start_level;
+	s32 end = effect->u.ramp.end_level;
+	s32 new;
+
+	if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+		const s32 clength = jiffies_to_msecs(now - mlnxeff->begin_at);
+		const s32 alength = envelope->attack_length;
+		s32 attack_level;
+		if (end > start)
+			attack_level = mean - envelope->attack_level;
+		else
+			attack_level = mean + envelope->attack_level;
+		start = (start - attack_level) * clength / alength
+		      + attack_level;
+	} else if (envelope->fade_length && time_before_eq(mlnxeff->fade_begin, now)) {
+		const s32 clength = jiffies_to_msecs(now - mlnxeff->fade_begin);
+		const s32 flength = envelope->fade_length;
+		s32 fade_level;
+		if (end > start)
+			fade_level = mean + envelope->fade_level;
+		else
+			fade_level = mean - envelope->fade_level;
+		end = (fade_level - end) * clength / flength + end;
+	}
+
+	new = ((end - start) * t) / length + start;
+	new = clamp(new, -0x7fff, 0x7fff);
+	pr_debug("RAMP level: %d, t: %u\n", new, t);
+	return new;
+}
+
+static void mlnx_destroy(struct ff_device *dev)
+{
+	struct mlnx_device *mlnxdev = dev->private;
+	del_timer_sync(&mlnxdev->timer);
+
+	kfree(mlnxdev->private);
+}
+
+static unsigned long mlnx_get_envelope_update_time(const struct mlnx_effect *mlnxeff,
+						   const unsigned long update_rate_jiffies)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+	const unsigned long now = jiffies;
+	unsigned long fade_next;
+
+	/* Effect has an envelope with nonzero attack time */
+	if (envelope->attack_length && time_before(now, mlnxeff->attack_stop)) {
+		if (time_before(mlnxeff->updated_at + update_rate_jiffies, mlnxeff->attack_stop))
+			return mlnxeff->updated_at + update_rate_jiffies;
+
+		return mlnxeff->attack_stop;
+	}
+
+	/* Effect has an envelope with nonzero fade time */
+	if (mlnxeff->effect.replay.length) {
+		if (!envelope->fade_length)
+			return mlnxeff->stop_at;
+
+		/* Schedule the next update when the fade begins */
+		if (time_before(mlnxeff->updated_at, mlnxeff->fade_begin))
+			return mlnxeff->fade_begin;
+
+		/* Already fading */
+		fade_next = mlnxeff->updated_at + update_rate_jiffies;
+
+		/* Schedule update when the effect stops */
+		if (time_after(fade_next, mlnxeff->stop_at))
+			return mlnxeff->stop_at;
+		/* Schedule update at the next checkpoint */
+			return fade_next;
+	}
+
+	/* There is no envelope */
+
+	/* Prevent the effect from being started twice */
+	if (mlnxeff->begin_at == now && mlnx_is_playing(mlnxeff))
+		return now - 1;
+
+	return mlnxeff->begin_at;
+}
+
+static unsigned long mlnx_get_update_time(struct mlnx_effect *mlnxeff,
+				const unsigned long update_rate_jiffies)
+{
+	const unsigned long now = jiffies;
+	unsigned long time, update_periodic;
+
+	switch (mlnxeff->effect.type) {
+	/* Constant effect does not change with time, but it can have
+	 * an envelope and a duration */
+	case FF_CONSTANT:
+		return mlnx_get_envelope_update_time(mlnxeff,
+						     update_rate_jiffies);
+	/* Periodic and ramp effects have to be periodically updated */
+	case FF_PERIODIC:
+	case FF_RAMP:
+		time = mlnx_get_envelope_update_time(mlnxeff,
+						     update_rate_jiffies);
+		if (mlnx_is_emulated(mlnxeff))
+			update_periodic = mlnxeff->stop_at;
+		else
+			update_periodic = mlnxeff->updated_at +
+					  update_rate_jiffies;
+
+		/* Periodic effect has to be updated earlier than envelope
+		 * or envelope update time is in the past */
+		if (time_before(update_periodic, time) || time_before(time, now))
+			return update_periodic;
+		/* Envelope needs to be updated */
+		return time;
+	case FF_RUMBLE:
+		if (mlnx_is_emulated(mlnxeff))
+			return mlnxeff->updated_at + update_rate_jiffies;
+	case FF_DAMPER:
+	case FF_FRICTION:
+	case FF_INERTIA:
+	case FF_SPRING:
+	default:
+		if (time_after_eq(mlnxeff->begin_at, now))
+			return mlnxeff->begin_at;
+
+		return mlnxeff->stop_at;
+	}
+}
+
+static void mlnx_schedule_playback(struct mlnx_device *mlnxdev)
+{
+	struct mlnx_effect *mlnxeff;
+	const unsigned long now = jiffies;
+	int events = 0;
+	int i;
+	unsigned long earliest = 0;
+	unsigned long time;
+
+	/* Iterate over all effects and determine the earliest
+	 * time when we have to attend to any */
+	for (i = 0; i < FF_MAX_EFFECTS; i++) {
+		mlnxeff = &mlnxdev->effects[i];
+
+		if (!mlnx_is_started(mlnxeff))
+			continue; /* Effect is not started, skip it */
+
+		if (mlnx_is_playing(mlnxeff))
+			time = mlnx_get_update_time(mlnxeff,
+						mlnxdev->update_rate_jiffies);
+		else
+			time = mlnxeff->begin_at;
+
+		pr_debug("Update time for effect %d: %lu\n", i, time);
+
+		/* Scheduled time is in the future and is either
+		 * before the current earliest time or it is
+		 * the first valid time value in this pass */
+		if (time_before_eq(now, time) &&
+		    (++events == 1 || time_before(time, earliest)))
+			earliest = time;
+	}
+
+	if (events) {
+		pr_debug("Events: %d, earliest: %lu\n", events, earliest);
+		mod_timer(&mlnxdev->timer, earliest);
+	} else {
+		pr_debug("No events, deactivating timer\n");
+		del_timer(&mlnxdev->timer);
+	}
+}
+
+static u16 mlnx_calculate_rumble_direction(const u32 total_mag, const u16 total_dir,
+					   const u32 new_mag, const u16 new_dir)
+{
+	if (!new_mag)
+		return total_dir;
+	if (!total_mag)
+		return new_dir;
+	return (((total_dir >> 1) * total_mag +
+		(new_dir >> 1) * new_mag) /
+		(total_mag + new_mag)) << 1;
+}
+
+static void mlnx_add_force(struct mlnx_effect *mlnxeff, s32 *cfx, s32 *cfy,
+			   const u16 gain)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	u16 direction;
+	s32 level;
+
+	pr_debug("Processing effect type %d, ID %d\n",
+		 mlnxeff->effect.type, mlnxeff->effect.id);
+
+	direction = mlnxeff->effect.direction * 360 / 0xffff;
+	pr_debug("Direction deg: %u\n", direction);
+
+	switch (mlnxeff->effect.type) {
+	case FF_CONSTANT:
+		level = mlnx_apply_envelope(mlnxeff, effect->u.constant.level);
+		break;
+	case FF_PERIODIC:
+		level = mlnx_apply_envelope(mlnxeff,
+					    effect->u.periodic.magnitude);
+		level = mlnx_calculate_periodic(mlnxeff, level);
+		break;
+	case FF_RAMP:
+		level = mlnx_calculate_ramp(mlnxeff);
+		break;
+	default:
+		pr_err("Effect %d not handled by mlnx_add_force\n",
+		       mlnxeff->effect.type);
+		return;
+	}
+
+	*cfx += mlnx_calculate_x_force(level, direction) * gain / 0xffff;
+	*cfy += mlnx_calculate_y_force(level, direction) * gain / 0xffff;
+}
+
+static void mlnx_add_rumble(const struct mlnx_effect *mlnxeff, u32 *strong_mag,
+			    u32 *weak_mag, u16 *strong_dir,
+			    u16 *weak_dir, const u16 gain)
+{
+	const struct ff_effect *eff = &mlnxeff->effect;
+	const struct ff_rumble_effect *reff = &mlnxeff->effect.u.rumble;
+	const u32 new_strong_mag = (u32)reff->strong_magnitude * gain / 0xffffU;
+	const u32 new_weak_mag = (u32)reff->weak_magnitude * gain / 0xffffU;
+
+	*strong_dir = mlnx_calculate_rumble_direction(*strong_mag, *strong_dir,
+						      new_strong_mag,
+						      eff->direction);
+	*weak_dir = mlnx_calculate_rumble_direction(*weak_mag, *weak_dir,
+						    new_weak_mag,
+						    eff->direction);
+	*strong_mag += new_strong_mag;
+	*weak_mag += new_weak_mag;
+}
+
+static void mlnx_add_emul_periodic(const struct mlnx_effect *mlnxeff,
+				   u32 *strong_mag, u32 *weak_mag,
+				   u16 *strong_dir, u16 *weak_dir,
+				   const u16 gain)
+{
+	const struct ff_effect *eff = &mlnxeff->effect;
+	const u32 level = (u32)abs(mlnx_apply_envelope(mlnxeff,
+						  eff->u.periodic.magnitude)) * gain / 0x7fffU;
+
+	*strong_dir = mlnx_calculate_rumble_direction(*strong_mag, *strong_dir,
+						      level, eff->direction);
+	*weak_dir = mlnx_calculate_rumble_direction(*weak_mag, *weak_dir,
+						    level, eff->direction);
+
+	*strong_mag += level;
+	*weak_mag += level;
+}
+
+static void mlnx_add_emul_rumble(const struct mlnx_effect *mlnxeff, s32 *cfx,
+				 s32 *cfy, const u16 gain,
+				 const unsigned long now,
+				 const unsigned long update_rate_jiffies)
+{
+	const struct ff_effect *effect = &mlnxeff->effect;
+	const u16 strong = effect->u.rumble.strong_magnitude;
+	const u16 weak = effect->u.rumble.weak_magnitude;
+	/* To calculate 't', we pretend that mlnxeff->begin_at == 0, thus t == now.  */
+	/* This will synchronise all simultaneously playing emul rumble effects,     */
+	/* otherwise non-deterministic phase-inversions could occur depending on     */
+	/* upload time, which could lead to undesired cancellation of these effects. */
+	const unsigned long t = now % (4UL * update_rate_jiffies);
+	s32 level = 0;
+	bool direction_up;
+	bool direction_left;
+
+	if (strong)
+		level += (strong / 4) * (t < 2UL * update_rate_jiffies ? 1 : -1);
+	if (weak)
+		level += (weak / 4) * (t < 2UL * update_rate_jiffies ?
+					(t < 1UL * update_rate_jiffies ? 1 : -1) :
+					(t < 3UL * update_rate_jiffies ? 1 : -1));
+	direction_up = (effect->direction > 0x3fffU && effect->direction <= 0xbfffU);
+	direction_left = (effect->direction <= 0x7fffU);
+
+	pr_debug("Emulated cf: %d, t: %lu, n: %lu, begin: %lu, diff: %lu j: %lu\n",
+		 level, t, now, mlnxeff->begin_at, now - mlnxeff->begin_at,
+		 update_rate_jiffies);
+	level = (level * gain) / 0xffff;
+	*cfx += direction_left ? -level : level;
+	*cfy += direction_up ? -level : level;
+}
+
+static void mlnx_play_effects(struct mlnx_device *mlnxdev)
+{
+	const u16 gain = mlnxdev->gain;
+	const unsigned long now = jiffies;
+	int i;
+	s32 cfx = 0;
+	s32 cfy = 0;
+	u32 strong_mag = 0;
+	u32 weak_mag = 0;
+	u16 strong_dir = 0;
+	u16 weak_dir = 0;
+
+	for (i = 0; i < FF_MAX_EFFECTS; i++) {
+		struct mlnx_effect *mlnxeff = &mlnxdev->effects[i];
+
+		if (!mlnx_is_started(mlnxeff)) {
+			pr_debug("Effect %hd/%d not started\n",
+				 mlnxeff->effect.id, i);
+			continue;
+		}
+
+		if (time_before(now, mlnxeff->begin_at)) {
+			pr_debug("Effect %hd/%d begins at a later time\n",
+				 mlnxeff->effect.id, i);
+			continue;
+		}
+
+		if (time_before_eq(mlnxeff->stop_at, now) && mlnxeff->effect.replay.length) {
+			pr_debug("Effect %hd/%d has to be stopped\n",
+				 mlnxeff->effect.id, i);
+
+			mlnx_clr_playing(mlnxeff);
+			if (--mlnxeff->repeat > 0)
+				mlnx_restart_effect(mlnxdev, mlnxeff);
+			else {
+				mlnx_stop_effect(mlnxdev, mlnxeff);
+				mlnx_clr_started(mlnxeff);
+				mlnx_clr_emulated(mlnxeff);
+				if (mlnx_is_conditional(&mlnxeff->effect))
+					mlnx_erase_conditional(mlnxdev, &mlnxeff->effect);
+			}
+
+			continue;
+		}
+
+		switch (mlnxeff->effect.type) {
+		case FF_PERIODIC:
+			if (mlnxdev->emul == EMUL_PERIODIC) {
+				if (!mlnx_test_set_playing(mlnxeff)) {
+					mlnxdev->rumble_playing++;
+					pr_debug("Starting emul periodic, total rumble %u\n",
+						 mlnxdev->rumble_playing);
+				}
+				__set_bit(FF_EFFECT_EMULATED, &mlnxeff->flags);
+				mlnx_add_emul_periodic(mlnxeff, &strong_mag, &weak_mag,
+						       &strong_dir, &weak_dir, gain);
+				break;
+			}
+		case FF_CONSTANT:
+		case FF_RAMP:
+			if (!mlnx_test_set_playing(mlnxeff)) {
+				mlnxdev->combinable_playing++;
+				pr_debug("Starting combinable effect, total %u\n",
+					 mlnxdev->combinable_playing);
+			}
+			mlnx_add_force(mlnxeff, &cfx, &cfy, gain);
+			break;
+		case FF_RUMBLE:
+			if (mlnxdev->emul == EMUL_RUMBLE) {
+				if (!mlnx_test_set_playing(mlnxeff)) {
+					mlnxdev->combinable_playing++;
+					pr_debug("Starting emul rumble, total comb %u\n",
+						 mlnxdev->combinable_playing);
+				}
+				__set_bit(FF_EFFECT_EMULATED, &mlnxeff->flags);
+				mlnx_add_emul_rumble(mlnxeff, &cfx, &cfy, gain, now,
+						     mlnxdev->update_rate_jiffies);
+			} else {
+				if (!mlnx_test_set_playing(mlnxeff)) {
+					mlnxdev->rumble_playing++;
+					pr_debug("Starting rumble effect, total %u\n",
+						 mlnxdev->rumble_playing);
+				}
+				mlnx_add_rumble(mlnxeff, &strong_mag, &weak_mag,
+						&strong_dir, &weak_dir, gain);
+			}
+			break;
+		case FF_DAMPER:
+		case FF_FRICTION:
+		case FF_INERTIA:
+		case FF_SPRING:
+			if (!mlnx_test_set_playing(mlnxeff)) {
+				const struct mlnx_effect_command ecmd = {
+					.cmd = MLNX_START_UNCOMB,
+					.u.uncomb.id = i,
+					.u.uncomb.effect = &mlnxeff->effect
+				};
+				mlnxdev->control_effect(mlnxdev->dev,
+						      mlnxdev->private, &ecmd);
+			}
+			break;
+		default:
+			pr_debug("Unhandled type of effect\n");
+		}
+		mlnxeff->updated_at = now;
+	}
+
+	if (mlnxdev->combinable_playing) {
+		const struct mlnx_effect_command ecmd = {
+			.cmd = MLNX_START_COMBINED,
+			.u.simple_force = {
+				.x = clamp(cfx, -0x7fff, 0x7fff),
+				.y = clamp(cfy, -0x7fff, 0x7fff)
+			}
+		};
+		mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+	}
+	if (mlnxdev->rumble_playing) {
+		const struct mlnx_effect_command ecmd = {
+			.cmd = MLNX_START_RUMBLE,
+			.u.rumble_force = {
+				.strong = clamp(strong_mag, (u32)0, (u32)0xffffU),
+				.weak = clamp(weak_mag, (u32)0, (u32)0xffffU),
+				.strong_dir = clamp(strong_dir, (u16)0, (u16)0xffffU),
+				.weak_dir = clamp(weak_dir, (u16)0, (u16)0xffffU)
+			}
+		};
+		mlnxdev->control_effect(mlnxdev->dev, mlnxdev->private, &ecmd);
+	}
+
+	mlnx_schedule_playback(mlnxdev);
+}
+
+static void mlnx_set_gain(struct input_dev *dev, u16 gain)
+{
+	struct mlnx_device *mlnxdev = dev->ff->private;
+	int i;
+
+	mlnxdev->gain = gain;
+
+	for (i = 0; i < FF_MAX_EFFECTS; i++) {
+		struct mlnx_effect *eff = &mlnxdev->effects[i];
+		if (eff == NULL)
+			continue;
+		if (mlnx_is_playing(eff)) {
+			if (mlnx_is_combinable(&eff->effect)) {
+				mlnx_clr_playing(eff);
+				if (mlnx_is_emulated(eff))
+					--mlnxdev->rumble_playing;
+				else
+					--mlnxdev->combinable_playing;
+			} else if (mlnx_is_rumble(&eff->effect)) {
+				mlnx_clr_playing(eff);
+				if (mlnx_is_emulated(eff))
+					--mlnxdev->combinable_playing;
+				else
+					--mlnxdev->rumble_playing;
+			}
+		}
+	}
+
+	mlnx_play_effects(mlnxdev);
+}
+
+static int mlnx_startstop(struct input_dev *dev, int effect_id, int repeat)
+{
+	struct mlnx_device *mlnxdev = dev->ff->private;
+	struct mlnx_effect *mlnxeff = &mlnxdev->effects[effect_id];
+	int ret;
+
+	if (repeat > 0) {
+		pr_debug("Starting effect ID %d\n", effect_id);
+		mlnxeff->repeat = repeat;
+
+		if (!mlnx_is_started(mlnxeff)) {
+			/* Check that device has a free effect slot */
+			if (mlnx_is_conditional(&mlnxeff->effect)) {
+				ret = mlnx_upload_conditional(mlnxdev, &mlnxeff->effect);
+				if (ret) {
+					/* Device effect slots are all occupied */
+					pr_debug("No free effect slot for EID %d\n", effect_id);
+					return ret;
+				}
+			}
+			mlnx_start_effect(mlnxeff);
+		}
+	} else {
+		pr_debug("Stopping effect ID %d\n", effect_id);
+		if (mlnx_is_started(mlnxeff)) {
+			if (mlnx_is_playing(mlnxeff)) {
+				mlnx_clr_playing(mlnxeff);
+				mlnx_stop_effect(mlnxdev, mlnxeff);
+			}
+			mlnx_clr_started(mlnxeff);
+			mlnx_clr_emulated(mlnxeff);
+
+			if (mlnx_is_conditional(&mlnxeff->effect))
+				return mlnx_erase_conditional(mlnxdev, &mlnxeff->effect);
+		} else {
+			pr_debug("Effect ID %d already stopped\n", effect_id);
+			return 0;
+		}
+	}
+	mlnx_play_effects(mlnxdev);
+
+	return 0;
+}
+
+static void mlnx_timer_fired(unsigned long data)
+{
+	struct input_dev *dev = (struct input_dev *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	mlnx_play_effects(dev->ff->private);
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static int mlnx_upload(struct input_dev *dev, struct ff_effect *effect,
+		       struct ff_effect *old)
+{
+	struct mlnx_device *mlnxdev = dev->ff->private;
+	struct mlnx_effect *mlnxeff = &mlnxdev->effects[effect->id];
+	const u16 length = effect->replay.length;
+	const u16 delay = effect->replay.delay;
+	int ret, fade_from;
+
+	/* Effect's timing is below kernel timer resolution */
+	if (length && length < FF_MIN_EFFECT_LENGTH)
+		effect->replay.length = FF_MIN_EFFECT_LENGTH;
+	if (delay && delay < FF_MIN_EFFECT_LENGTH)
+		effect->replay.delay = FF_MIN_EFFECT_LENGTH;
+
+	/* Periodic effects must have a non-zero period */
+	if (effect->type == FF_PERIODIC) {
+		if (!effect->u.periodic.period)
+			return -EINVAL;
+	}
+	/* Ramp effects cannot be infinite */
+	if (effect->type == FF_RAMP && !length)
+		return -EINVAL;
+
+	if (mlnx_is_combinable(effect)) {
+		const struct ff_envelope *envelope = mlnx_get_envelope(effect);
+
+		/* Infinite effects cannot fade */
+		if (!length && envelope->fade_length > 0)
+			return -EINVAL;
+		/* Fade length cannot be greater than effect duration */
+		fade_from = (int)length - (int)envelope->fade_length;
+		if (fade_from < 0)
+			return -EINVAL;
+		/* Envelope cannot start fading before it finishes attacking */
+		if (fade_from < envelope->attack_length && fade_from > 0)
+			return -EINVAL;
+	}
+
+
+	spin_lock_irq(&dev->event_lock);
+	mlnxeff->effect = *effect; /* Keep internal copy of the effect */
+	/* Check if the effect being modified is playing */
+	if (mlnx_is_started(mlnxeff)) {
+		if (mlnx_is_playing(mlnxeff)) {
+			mlnx_clr_playing(mlnxeff);
+			ret = mlnx_restart_effect(mlnxdev, mlnxeff);
+
+			if (ret) {
+				/* Restore the original effect */
+				if (old)
+					mlnxeff->effect = *old;
+				spin_unlock_irq(&dev->event_lock);
+				return ret;
+			}
+		}
+
+		mlnx_schedule_playback(mlnxdev);
+	}
+
+	spin_unlock_irq(&dev->event_lock);
+
+	return 0;
+}
+
+int input_ff_create_mlnx(struct input_dev *dev, void *data,
+			 int (*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *),
+			 const u16 update_rate)
+{
+	struct mlnx_device *mlnxdev;
+	int ret;
+	const u16 min_update_rate = update_rate < FF_MIN_EFFECT_LENGTH ?
+				    FF_MIN_EFFECT_LENGTH : update_rate;
+
+	mlnxdev = kzalloc(sizeof(*mlnxdev), GFP_KERNEL);
+	if (!mlnxdev)
+		return -ENOMEM;
+
+	mlnxdev->dev = dev;
+	mlnxdev->private = data;
+	mlnxdev->control_effect = control_effect;
+	mlnxdev->gain = 0xffff;
+	mlnxdev->update_rate_jiffies = msecs_to_jiffies(min_update_rate);
+	input_set_capability(dev, EV_FF, FF_GAIN);
+	setup_timer(&mlnxdev->timer, mlnx_timer_fired, (unsigned long)dev);
+
+	/* Set up effect emulation if needed */
+	if (test_bit(FF_PERIODIC, dev->ffbit) &&
+	    !test_bit(FF_RUMBLE, dev->ffbit)) {
+		set_bit(FF_RUMBLE, dev->ffbit);
+		mlnxdev->emul = EMUL_RUMBLE;
+		pr_debug("Emulating RUMBLE with PERIODIC\n");
+	} else if (test_bit(FF_RUMBLE, dev->ffbit) &&
+		   !test_bit(FF_PERIODIC, dev->ffbit)) {
+		set_bit(FF_PERIODIC, dev->ffbit);
+		set_bit(FF_SINE, dev->ffbit);
+		set_bit(FF_SQUARE, dev->ffbit);
+		set_bit(FF_TRIANGLE, dev->ffbit);
+		set_bit(FF_SAW_DOWN, dev->ffbit);
+		set_bit(FF_SAW_UP, dev->ffbit);
+		mlnxdev->emul = EMUL_PERIODIC;
+		pr_debug("Emulating PERIODIC with RUMBLE\n");
+	} else {
+		mlnxdev->emul = EMUL_NOTHING;
+		pr_debug("No effect emulation is necessary\n");
+	}
+
+	ret = input_ff_create(dev, FF_MAX_EFFECTS);
+	if (ret) {
+		kfree(mlnxdev);
+		return ret;
+	}
+
+
+	dev->ff->private = mlnxdev;
+	dev->ff->upload = mlnx_upload;
+	dev->ff->set_gain = mlnx_set_gain;
+	dev->ff->destroy = mlnx_destroy;
+	dev->ff->playback = mlnx_startstop;
+
+	pr_debug("Device successfully registered.\n");
+	return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_create_mlnx);
diff --git a/include/linux/input/ff-memless-next.h b/include/linux/input/ff-memless-next.h
new file mode 100644
index 0000000..7522451
--- /dev/null
+++ b/include/linux/input/ff-memless-next.h
@@ -0,0 +1,162 @@
+#include <linux/input.h>
+
+/** DEFINITION OF TERMS
+ *
+ * Combined effect - An effect whose force is a superposition of forces
+ *                   generated by all effects that can be added together.
+ *                   Only one combined effect can be playing at a time.
+ *                   Effects that can be added together to create a combined
+ *                   effect are FF_CONSTANT, FF_PERIODIC and FF_RAMP.
+ * Uncombinable effect - An effect that cannot be combined with another effect.
+ *                       All conditional effects - FF_DAMPER, FF_FRICTION,
+ *                       FF_INERTIA and FF_SPRING are uncombinable.
+ *                       Number of uncombinable effects playing simultaneously
+ *                       depends on the capabilities of the hardware.
+ * Rumble effect - An effect generated by device's rumble motors instead of
+ *                 force feedback actuators.
+ *
+ *
+ * HANDLING OF UNCOMBINABLE EFFECTS
+ *
+ * Uncombinable effects cannot be combined together into just one effect, at
+ * least not in a clear and obvious manner. Therefore these effects have to
+ * be handled individually by ff-memless-next. Handling of these effects is
+ * left entirely to the hardware-specific driver, ff-memless-next merely
+ * passes these effects to the hardware-specific driver at appropriate time.
+ * ff-memless-next provides the UPLOAD command to notify the hardware-specific
+ * driver that the userspace is about to request playback of an uncombinable
+ * effect. The hardware-specific driver shall take all steps needed to make
+ * the device ready to play the effect when it receives the UPLOAD command.
+ * The actual playback shall commence when START_UNCOMB command is received.
+ * Opposite to the UPLOAD command is the ERASE command which tells
+ * the hardware-specific driver that the playback has finished and that
+ * the effect will not be restarted. STOP_UNCOMB command tells
+ * the hardware-specific driver that the playback shall stop but the device
+ * shall still be ready to resume the playback immediately.
+ *
+ * In case it is not possible to make the device ready to play an uncombinable
+ * effect (all hardware effect slots are occupied), the hardware-specific
+ * driver may return an error when it receives an UPLOAD command. If the
+ * hardware-specific driver returns 0, the upload is considered successful.
+ * START_UNCOMB and STOP_UNCOMB commands cannot fail and the device must always
+ * start the playback of the requested effect if the UPLOAD command of the
+ * respective effect has been successful. ff-memless-next will never send
+ * a START/STOP_UNCOMB command for an effect that has not been uploaded
+ * successfully, nor will it send an ERASE command for an effect that is
+ * playing (= has been started with START_UNCOMB command).
+ */
+
+enum mlnx_commands {
+	/* Start or update a combined effect. This command is sent whenever
+	 * a FF_CONSTANT, FF_PERIODIC or FF_RAMP is started, stopped or
+	 * updated by userspace, when the applied envelopes are recalculated
+	 * or when periodic effects are recalculated. */
+	MLNX_START_COMBINED,
+	/* Stop combined effect. This command is sent when all combinable
+	 * effects are stopped. */
+	MLNX_STOP_COMBINED,
+	/* Start or update a rumble effect. This command is sent whenever
+	 * a FF_RUMBLE effect is started or when its magnitudes or directions
+	 * change. */
+	MLNX_START_RUMBLE,
+	/* Stop a rumble effect. This command is sent when all FF_RUMBLE
+	 * effects are stopped. */
+	MLNX_STOP_RUMBLE,
+	/* Start or update an uncombinable effect. This command is sent
+	 * whenever an uncombinable effect is started or updated. */
+	MLNX_START_UNCOMB,
+	/* Stop uncombinable effect. This command is sent when an uncombinable
+	 * effect is stopped. */
+	MLNX_STOP_UNCOMB,
+	/* Upload uncombinable effect to device. This command is sent when the
+	 * effect is started from userspace. It is up to the hardware-specific
+	 * driver to handle this situation.
+	 */
+	MLNX_UPLOAD_UNCOMB,
+	/* Remove uncombinable effect from device, This command is sent when
+	 * and uncombinable effect has finished playing and will not be
+	 * restarted.
+	 */
+	MLNX_ERASE_UNCOMB
+};
+
+/** struct mlnx_simple_force - holds constant forces along X and Y axis
+ * @x: Force along X axis. Negative value denotes force pulling to the left,
+ *     positive value denotes force pulling to the right.
+ * @y: Force along Y axis. Negative value denotes force denotes force pulling
+ *     away from the user, positive value denotes force pulling towards
+ *     the user.
+ */
+struct mlnx_simple_force {
+	const s32 x;
+	const s32 y;
+};
+
+/** struct mlnx_rumble_force - holds information about rumble effect
+ * @strong: Magnitude of the strong vibration.
+ * @weak: Magnitude of the weak vibration.
+ * @strong_dir: Direction of the strong vibration expressed in the same way
+ *              as the direction of force feedback effect in struct ff_effect.
+ * @weak_dir: Direction of the weak vibration, same as above applies.
+ */
+struct mlnx_rumble_force {
+	const u32 strong;
+	const u32 weak;
+	const u16 strong_dir;
+	const u16 weak_dir;
+};
+
+/** struct mlnx_uncomb_effect - holds information about uncombinable effect
+ * @id: Id of the effect assigned by ff-core.
+ * @effect: Pointer to the uncombinable effect stored in ff-memless-next module
+ *          Hardware-specific driver must not alter this.
+ */
+struct mlnx_uncomb_effect {
+	const int id;
+	const struct ff_effect *effect;
+};
+
+/** struct mlnx_commands - describes what action shall the force feedback
+ *                         device perform
+ * @cmd: Type of the action.
+ * @u: Data associated with the action.
+ */
+struct mlnx_effect_command {
+	const enum mlnx_commands cmd;
+	union {
+		const struct mlnx_simple_force simple_force;
+		const struct mlnx_rumble_force rumble_force;
+		const struct mlnx_uncomb_effect uncomb;
+	} u;
+};
+
+/** input_ff_create_mlnx() - Register a device within ff-memless-next and
+ *                           the kernel force feedback system
+ * @dev: Pointer to the struct input_dev associated with the device.
+ * @data: Any device-specific data that shall be passed to the callback.
+ *        function called by ff-memless-next when a force feedback action
+ *        shall be performed.
+ * @control_effect: Pointer to the callback function.
+ * @update_date: Delay in milliseconds between two recalculations of periodic
+ *               effects, ramp effects and envelopes. Note that this value will
+ *               never be lower than (CONFIG_HZ / 1000) + 1 regardless of the
+ *               value specified here. This is not a "hard" rate limiter.
+ *               Userspace still can submit effects at a rate faster than
+ *               this value.
+ */
+int input_ff_create_mlnx(struct input_dev *dev, void *data,
+			 int (*control_effect)(struct input_dev *, void *, const struct mlnx_effect_command *),
+			 const u16 update_rate);
+
+/** int control_effect() - Callback to the HW-specific driver.
+ * @struct input_dev *: Pointer to the struct input_dev of the device that is
+ *                     is being controlled.
+ * @void *: Pointer to any device-specific data set by the HW-specific driver.
+ *         This data will be free'd automatically by ff-memless-next when the
+ *         device is destroyed.
+ * @const struct mlnx_effect_command *:
+ *         Action the device shall perform. Note that this pointer is valid
+ *         only within the context of the callback function. If the HW-specific
+ *         driver needs any data from this structure after the callback
+ *         function returns, it must copy it.
+ */
-- 
1.9.2

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 02/24] input: Port arizona-haptics to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
  (?)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port arizona-haptics to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/input/misc/Kconfig           |  2 +-
 drivers/input/misc/arizona-haptics.c | 39 +++++++++++++++++++++++-------------
 2 files changed, 26 insertions(+), 15 deletions(-)

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 5928ea7..f669cc7 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -75,7 +75,7 @@ config INPUT_AD714X_SPI
 config INPUT_ARIZONA_HAPTICS
 	tristate "Arizona haptics support"
 	depends on MFD_ARIZONA && SND_SOC
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	help
 	  Say Y to enable support for the haptics module in Arizona CODECs.
 
diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c
index ef2e281..b4707cc 100644
--- a/drivers/input/misc/arizona-haptics.c
+++ b/drivers/input/misc/arizona-haptics.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/input.h>
+#include <linux/input/ff-memless-next.h>
 #include <linux/slab.h>
 
 #include <sound/soc.h>
@@ -22,6 +23,8 @@
 #include <linux/mfd/arizona/pdata.h>
 #include <linux/mfd/arizona/registers.h>
 
+#define FF_UPDATE_RATE 50
+
 struct arizona_haptics {
 	struct arizona *arizona;
 	struct input_dev *input_dev;
@@ -108,29 +111,37 @@ static void arizona_haptics_work(struct work_struct *work)
 }
 
 static int arizona_haptics_play(struct input_dev *input, void *data,
-				struct ff_effect *effect)
+				const struct mlnx_effect_command *command)
 {
 	struct arizona_haptics *haptics = input_get_drvdata(input);
 	struct arizona *arizona = haptics->arizona;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 
 	if (!arizona->dapm) {
 		dev_err(arizona->dev, "No DAPM context\n");
 		return -EBUSY;
 	}
 
-	if (effect->u.rumble.strong_magnitude) {
-		/* Scale the magnitude into the range the device supports */
-		if (arizona->pdata.hap_act) {
-			haptics->intensity =
-				effect->u.rumble.strong_magnitude >> 9;
-			if (effect->direction < 0x8000)
-				haptics->intensity += 0x7f;
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		if (rumble_force->strong) {
+			/* Scale the magnitude into the range the device supports */
+			if (arizona->pdata.hap_act) {
+				haptics->intensity = rumble_force->strong >> 9;
+				if (rumble_force->strong_dir < 0x8000)
+					haptics->intensity += 0x7f;
+			} else {
+				haptics->intensity = rumble_force->strong >> 8;
+			}
 		} else {
-			haptics->intensity =
-				effect->u.rumble.strong_magnitude >> 8;
+			haptics->intensity = 0;
 		}
-	} else {
+		break;
+	case MLNX_STOP_RUMBLE:
 		haptics->intensity = 0;
+		break;
+	default:
+		return -EINVAL;
 	}
 
 	schedule_work(&haptics->work);
@@ -183,10 +194,10 @@ static int arizona_haptics_probe(struct platform_device *pdev)
 	haptics->input_dev->close = arizona_haptics_close;
 	__set_bit(FF_RUMBLE, haptics->input_dev->ffbit);
 
-	ret = input_ff_create_memless(haptics->input_dev, NULL,
-				      arizona_haptics_play);
+	ret = input_ff_create_mlnx(haptics->input_dev, NULL,
+				   arizona_haptics_play, FF_UPDATE_RATE);
 	if (ret < 0) {
-		dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n",
+		dev_err(arizona->dev, "input_ff_create_mlnx() failed: %d\n",
 			ret);
 		goto err_ialloc;
 	}
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 03/24] input: Port twl4030-vibra to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (2 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port twl4030-vibra to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/input/misc/Kconfig         |  2 +-
 drivers/input/misc/twl4030-vibra.c | 31 +++++++++++++++++++++++++------
 2 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index f669cc7..817156f 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -407,7 +407,7 @@ config INPUT_TWL4030_VIBRA
 	tristate "Support for TWL4030 Vibrator"
 	depends on TWL4030_CORE
 	select MFD_TWL4030_AUDIO
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	help
 	  This option enables support for TWL4030 Vibrator Driver.
 
diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c
index 960ef2a..5274ad2 100644
--- a/drivers/input/misc/twl4030-vibra.c
+++ b/drivers/input/misc/twl4030-vibra.c
@@ -32,9 +32,11 @@
 #include <linux/mfd/twl4030-audio.h>
 #include <linux/input.h>
 #include <linux/slab.h>
+#include <linux/input/ff-memless-next.h>
 
 /* MODULE ID2 */
 #define LEDEN		0x00
+#define FF_UPDATE_RATE	50
 
 /* ForceFeedback */
 #define EFFECT_DIR_180_DEG	0x8000 /* range is 0 - 0xFFFF */
@@ -134,14 +136,31 @@ static void vibra_play_work(struct work_struct *work)
 /*** Input/ForceFeedback ***/
 
 static int vibra_play(struct input_dev *input, void *data,
-		      struct ff_effect *effect)
+		      const struct mlnx_effect_command *command)
 {
 	struct vibra_info *info = input_get_drvdata(input);
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
+
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		if (command->u.rumble_force.strong) {
+			info->speed = rumble_force->strong >> 8;
+			info->direction = rumble_force->strong_dir < EFFECT_DIR_180_DEG ?
+					  0 : 1;
+		} else {
+			info->speed = rumble_force->weak >> 9;
+			info->direction = rumble_force->weak_dir < EFFECT_DIR_180_DEG ?
+					  0 : 1;
+		}
+		break;
+	case MLNX_STOP_RUMBLE:
+		info->speed = 0;
+		info->direction = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
 
-	info->speed = effect->u.rumble.strong_magnitude >> 8;
-	if (!info->speed)
-		info->speed = effect->u.rumble.weak_magnitude >> 9;
-	info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;
 	schedule_work(&info->play_work);
 	return 0;
 }
@@ -227,7 +246,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev)
 	info->input_dev->close = twl4030_vibra_close;
 	__set_bit(FF_RUMBLE, info->input_dev->ffbit);
 
-	ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
+	ret = input_ff_create_mlnx(info->input_dev, NULL, vibra_play, FF_UPDATE_RATE);
 	if (ret < 0) {
 		dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
 		return ret;
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 04/24] input: Port twl6040-vibra to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (3 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port twl6040-vibra to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/input/misc/Kconfig         |  2 +-
 drivers/input/misc/twl6040-vibra.c | 27 ++++++++++++++++++++++-----
 2 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 817156f..b9cbb91 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -417,7 +417,7 @@ config INPUT_TWL4030_VIBRA
 config INPUT_TWL6040_VIBRA
 	tristate "Support for TWL6040 Vibrator"
 	depends on TWL6040_CORE
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	help
 	  This option enables support for TWL6040 Vibrator Driver.
 
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c
index 77dc23b..7440a74 100644
--- a/drivers/input/misc/twl6040-vibra.c
+++ b/drivers/input/misc/twl6040-vibra.c
@@ -30,12 +30,14 @@
 #include <linux/of.h>
 #include <linux/workqueue.h>
 #include <linux/input.h>
+#include <linux/input/ff-memless-next.h>
 #include <linux/mfd/twl6040.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/regulator/consumer.h>
 
 #define EFFECT_DIR_180_DEG	0x8000
+#define FF_UPDATE_RATE		50
 
 /* Recommended modulation index 85% */
 #define TWL6040_VIBRA_MOD	85
@@ -197,9 +199,10 @@ static void vibra_play_work(struct work_struct *work)
 }
 
 static int vibra_play(struct input_dev *input, void *data,
-		      struct ff_effect *effect)
+		      const struct mlnx_effect_command *command)
 {
 	struct vibra_info *info = input_get_drvdata(input);
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 	int ret;
 
 	/* Do not allow effect, while the routing is set to use audio */
@@ -209,9 +212,23 @@ static int vibra_play(struct input_dev *input, void *data,
 		return -EBUSY;
 	}
 
-	info->weak_speed = effect->u.rumble.weak_magnitude;
-	info->strong_speed = effect->u.rumble.strong_magnitude;
-	info->direction = effect->direction < EFFECT_DIR_180_DEG ? 1 : -1;
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		info->weak_speed = rumble_force->weak;
+		info->strong_speed = rumble_force->strong;
+		if (info->strong_speed >= info->weak_speed)
+			info->direction = rumble_force->strong_dir < EFFECT_DIR_180_DEG ? 1 : -1;
+		else
+			info->direction = rumble_force->weak_dir < EFFECT_DIR_180_DEG ? 1 : -1;
+		break;
+	case MLNX_STOP_RUMBLE:
+		info->weak_speed = 0;
+		info->strong_speed = 0;
+		info->direction = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	ret = queue_work(info->workqueue, &info->play_work);
 	if (!ret) {
@@ -367,7 +384,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev)
 	info->input_dev->close = twl6040_vibra_close;
 	__set_bit(FF_RUMBLE, info->input_dev->ffbit);
 
-	ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
+	ret = input_ff_create_mlnx(info->input_dev, NULL, vibra_play, FF_UPDATE_RATE);
 	if (ret < 0) {
 		dev_err(info->dev, "couldn't register vibrator to FF\n");
 		goto err_ialloc;
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 05/24] input: Port max8997_haptic to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (4 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port max8997_haptic to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/input/misc/Kconfig          |  2 +-
 drivers/input/misc/max8997_haptic.c | 25 +++++++++++++++++++------
 2 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index b9cbb91..a89dc22 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -157,7 +157,7 @@ config INPUT_MAX8925_ONKEY
 config INPUT_MAX8997_HAPTIC
 	tristate "MAXIM MAX8997 haptic controller support"
 	depends on PWM && MFD_MAX8997
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	help
 	  This option enables device driver support for the haptic controller
 	  on MAXIM MAX8997 chip. This driver supports ff-memless interface
diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c
index 1fea548..029ac8b 100644
--- a/drivers/input/misc/max8997_haptic.c
+++ b/drivers/input/misc/max8997_haptic.c
@@ -31,6 +31,7 @@
 #include <linux/mfd/max8997-private.h>
 #include <linux/mfd/max8997.h>
 #include <linux/regulator/consumer.h>
+#include <linux/input/ff-memless-next.h>
 
 /* Haptic configuration 2 register */
 #define MAX8997_MOTOR_TYPE_SHIFT	7
@@ -43,6 +44,8 @@
 #define MAX8997_SIG_DUTY_SHIFT		2
 #define MAX8997_PWM_DUTY_SHIFT		0
 
+#define FF_UPDATE_RATE 50
+
 struct max8997_haptic {
 	struct device *dev;
 	struct i2c_client *client;
@@ -219,13 +222,23 @@ static void max8997_haptic_play_effect_work(struct work_struct *work)
 }
 
 static int max8997_haptic_play_effect(struct input_dev *dev, void *data,
-				  struct ff_effect *effect)
+				  const struct mlnx_effect_command *command)
 {
 	struct max8997_haptic *chip = input_get_drvdata(dev);
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 
-	chip->level = effect->u.rumble.strong_magnitude;
-	if (!chip->level)
-		chip->level = effect->u.rumble.weak_magnitude;
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		chip->level = rumble_force->strong;
+		if (!chip->level)
+			chip->level = rumble_force->weak;
+		break;
+	case MLNX_STOP_RUMBLE:
+		chip->level = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	schedule_work(&chip->work);
 
@@ -319,8 +332,8 @@ static int max8997_haptic_probe(struct platform_device *pdev)
 	input_set_drvdata(input_dev, chip);
 	input_set_capability(input_dev, EV_FF, FF_RUMBLE);
 
-	error = input_ff_create_memless(input_dev, NULL,
-				max8997_haptic_play_effect);
+	error = input_ff_create_mlnx(input_dev, NULL,
+				max8997_haptic_play_effect, FF_UPDATE_RATE);
 	if (error) {
 		dev_err(&pdev->dev,
 			"unable to create FF device, error: %d\n",
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 06/24] input: Port pm8xxx-vibrator to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (5 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port pm8xxx-vibrator to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/input/misc/Kconfig           |  2 +-
 drivers/input/misc/pm8xxx-vibrator.c | 28 +++++++++++++++++++---------
 2 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index a89dc22..7b962e4 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -108,7 +108,7 @@ config INPUT_PCSPKR
 config INPUT_PM8XXX_VIBRATOR
 	tristate "Qualcomm PM8XXX vibrator support"
 	depends on MFD_PM8XXX
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	help
 	  This option enables device driver support for the vibrator
 	  on Qualcomm PM8xxx chip. This driver supports ff-memless interface
diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c
index 6a915ba..1796f4f 100644
--- a/drivers/input/misc/pm8xxx-vibrator.c
+++ b/drivers/input/misc/pm8xxx-vibrator.c
@@ -17,6 +17,7 @@
 #include <linux/input.h>
 #include <linux/slab.h>
 #include <linux/regmap.h>
+#include <linux/input/ff-memless-next.h>
 
 #define VIB_DRV			0x4A
 
@@ -29,7 +30,7 @@
 #define VIB_MAX_LEVELS		(VIB_MAX_LEVEL_mV - VIB_MIN_LEVEL_mV)
 
 #define MAX_FF_SPEED		0xff
-
+#define FF_UPDATE_RATE		50
 /**
  * struct pm8xxx_vib - structure to hold vibrator data
  * @vib_input_dev: input device supporting force feedback
@@ -128,14 +129,23 @@ static void pm8xxx_vib_close(struct input_dev *dev)
  * Currently this driver supports only rumble effects.
  */
 static int pm8xxx_vib_play_effect(struct input_dev *dev, void *data,
-				  struct ff_effect *effect)
+				  const struct mlnx_command *command)
 {
 	struct pm8xxx_vib *vib = input_get_drvdata(dev);
-
-	vib->speed = effect->u.rumble.strong_magnitude >> 8;
-	if (!vib->speed)
-		vib->speed = effect->u.rumble.weak_magnitude >> 9;
-
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
+
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		vib->speed = rumble_force->strong >> 8;
+		if (!vib->speed)
+			vib->speed = rumble_force->weak >> 9;
+		break;
+	case MLNX_STOP_RUMBLE:
+		vib->speed = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
 	schedule_work(&vib->work);
 
 	return 0;
@@ -181,8 +191,8 @@ static int pm8xxx_vib_probe(struct platform_device *pdev)
 	input_set_drvdata(input_dev, vib);
 	input_set_capability(vib->vib_input_dev, EV_FF, FF_RUMBLE);
 
-	error = input_ff_create_memless(input_dev, NULL,
-					pm8xxx_vib_play_effect);
+	error = input_ff_create_mlnx(input_dev, NULL,
+				     pm8xxx_vib_play_effect, FF_UPDATE_RATE);
 	if (error) {
 		dev_err(&pdev->dev,
 			"couldn't register vibrator as FF device\n");
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 07/24] hid: Port hid-axff to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
@ 2014-04-26 15:02   ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-axff to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig    |  2 +-
 drivers/hid/hid-axff.c | 32 +++++++++++++++++++++++---------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b..e076627 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -106,7 +106,7 @@ config HID_ACRUX
 config HID_ACRUX_FF
 	bool "ACRUX force feedback support"
 	depends on HID_ACRUX
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	Say Y here if you want to enable force feedback support for ACRUX
 	game controllers.
diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c
index a594e47..7fbfcbc 100644
--- a/drivers/hid/hid-axff.c
+++ b/drivers/hid/hid-axff.c
@@ -31,31 +31,45 @@
 #include <linux/slab.h>
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/input/ff-memless-next.h>
 
 #include "hid-ids.h"
 
+#define FF_UPDATE_RATE 50
 #ifdef CONFIG_HID_ACRUX_FF
 
 struct axff_device {
 	struct hid_report *report;
 };
 
-static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+static int axff_play(struct input_dev *dev, void *data,
+		     const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct axff_device *axff = data;
 	struct hid_report *report = axff->report;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 	int field_count = 0;
 	int left, right;
 	int i, j;
 
-	left = effect->u.rumble.strong_magnitude;
-	right = effect->u.rumble.weak_magnitude;
-
-	dbg_hid("called with 0x%04x 0x%04x", left, right);
-
-	left = left * 0xff / 0xffff;
-	right = right * 0xff / 0xffff;
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		left = rumble_force->strong;
+		right = rumble_force->weak;
+
+		dbg_hid("called with 0x%04x 0x%04x", left, right);
+
+		left = left * 0xff / 0xffff;
+		right = right * 0xff / 0xffff;
+		break;
+	case MLNX_STOP_RUMBLE:
+		left = 0;
+		right = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	for (i = 0; i < report->maxfield; i++) {
 		for (j = 0; j < report->field[i]->report_count; j++) {
@@ -107,7 +121,7 @@ static int axff_init(struct hid_device *hid)
 
 	set_bit(FF_RUMBLE, dev->ffbit);
 
-	error = input_ff_create_memless(dev, axff, axff_play);
+	error = input_ff_create_mlnx(dev, axff, axff_play, FF_UPDATE_RATE);
 	if (error)
 		goto err_free_mem;
 
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 07/24] hid: Port hid-axff to ff-memless-next
@ 2014-04-26 15:02   ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-axff to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig    |  2 +-
 drivers/hid/hid-axff.c | 32 +++++++++++++++++++++++---------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b..e076627 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -106,7 +106,7 @@ config HID_ACRUX
 config HID_ACRUX_FF
 	bool "ACRUX force feedback support"
 	depends on HID_ACRUX
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	Say Y here if you want to enable force feedback support for ACRUX
 	game controllers.
diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c
index a594e47..7fbfcbc 100644
--- a/drivers/hid/hid-axff.c
+++ b/drivers/hid/hid-axff.c
@@ -31,31 +31,45 @@
 #include <linux/slab.h>
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/input/ff-memless-next.h>
 
 #include "hid-ids.h"
 
+#define FF_UPDATE_RATE 50
 #ifdef CONFIG_HID_ACRUX_FF
 
 struct axff_device {
 	struct hid_report *report;
 };
 
-static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+static int axff_play(struct input_dev *dev, void *data,
+		     const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct axff_device *axff = data;
 	struct hid_report *report = axff->report;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 	int field_count = 0;
 	int left, right;
 	int i, j;
 
-	left = effect->u.rumble.strong_magnitude;
-	right = effect->u.rumble.weak_magnitude;
-
-	dbg_hid("called with 0x%04x 0x%04x", left, right);
-
-	left = left * 0xff / 0xffff;
-	right = right * 0xff / 0xffff;
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		left = rumble_force->strong;
+		right = rumble_force->weak;
+
+		dbg_hid("called with 0x%04x 0x%04x", left, right);
+
+		left = left * 0xff / 0xffff;
+		right = right * 0xff / 0xffff;
+		break;
+	case MLNX_STOP_RUMBLE:
+		left = 0;
+		right = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	for (i = 0; i < report->maxfield; i++) {
 		for (j = 0; j < report->field[i]->report_count; j++) {
@@ -107,7 +121,7 @@ static int axff_init(struct hid_device *hid)
 
 	set_bit(FF_RUMBLE, dev->ffbit);
 
-	error = input_ff_create_memless(dev, axff, axff_play);
+	error = input_ff_create_mlnx(dev, axff, axff_play, FF_UPDATE_RATE);
 	if (error)
 		goto err_free_mem;
 
-- 
1.9.2

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 08/24] hid: Port hid-emsff to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (7 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-emsff to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig     |  2 +-
 drivers/hid/hid-emsff.c | 38 ++++++++++++++++++++++++++------------
 2 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index e076627..a78b5d8 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -213,7 +213,7 @@ config DRAGONRISE_FF
 config HID_EMS_FF
 	tristate "EMS Production Inc. force feedback support"
 	depends on HID
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	Say Y here if you want to enable force feedback support for devices by
 	EMS Production Ltd.
diff --git a/drivers/hid/hid-emsff.c b/drivers/hid/hid-emsff.c
index d82d75b..c0cbe50 100644
--- a/drivers/hid/hid-emsff.c
+++ b/drivers/hid/hid-emsff.c
@@ -24,30 +24,44 @@
 #include <linux/hid.h>
 #include <linux/input.h>
 #include <linux/module.h>
+#include <linux/input/ff-memless-next.h>
 
 #include "hid-ids.h"
 
+#define FF_UPDATE_RATE 50
+
 struct emsff_device {
 	struct hid_report *report;
 };
 
 static int emsff_play(struct input_dev *dev, void *data,
-			 struct ff_effect *effect)
+			 const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct emsff_device *emsff = data;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 	int weak, strong;
 
-	weak = effect->u.rumble.weak_magnitude;
-	strong = effect->u.rumble.strong_magnitude;
-
-	dbg_hid("called with 0x%04x 0x%04x\n", strong, weak);
-
-	weak = weak * 0xff / 0xffff;
-	strong = strong * 0xff / 0xffff;
-
-	emsff->report->field[0]->value[1] = weak;
-	emsff->report->field[0]->value[2] = strong;
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		weak = rumble_force->weak;
+		strong = rumble_force->strong;
+
+		dbg_hid("called with 0x%04x 0x%04x\n", strong, weak);
+
+		weak = weak * 0xff / 0xffff;
+		strong = strong * 0xff / 0xffff;
+
+		emsff->report->field[0]->value[1] = weak;
+		emsff->report->field[0]->value[2] = strong;
+		break;
+	case MLNX_STOP_RUMBLE:
+		weak = 0;
+		strong = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	dbg_hid("running with 0x%02x 0x%02x\n", strong, weak);
 	hid_hw_request(hid, emsff->report, HID_REQ_SET_REPORT);
@@ -88,7 +102,7 @@ static int emsff_init(struct hid_device *hid)
 
 	set_bit(FF_RUMBLE, dev->ffbit);
 
-	error = input_ff_create_memless(dev, emsff, emsff_play);
+	error = input_ff_create_mlnx(dev, emsff, emsff_play, FF_UPDATE_RATE);
 	if (error) {
 		kfree(emsff);
 		return error;
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 09/24] hid: Port hid-dr to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
@ 2014-04-26 15:02   ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-dr to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig  |  2 +-
 drivers/hid/hid-dr.c | 59 ++++++++++++++++++++++++++++++++--------------------
 2 files changed, 37 insertions(+), 24 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index a78b5d8..1d4180c 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -205,7 +205,7 @@ config HID_DRAGONRISE
 config DRAGONRISE_FF
 	bool "DragonRise Inc. force feedback"
 	depends on HID_DRAGONRISE
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	Say Y here if you want to enable force feedback support for DragonRise Inc.
 	game controllers.
diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c
index ce06444..b95c676 100644
--- a/drivers/hid/hid-dr.c
+++ b/drivers/hid/hid-dr.c
@@ -31,8 +31,10 @@
 #include <linux/slab.h>
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/input/ff-memless-next.h>
 
 #include "hid-ids.h"
+#define FF_UPDATE_RATE 50
 
 #ifdef CONFIG_DRAGONRISE_FF
 
@@ -41,38 +43,49 @@ struct drff_device {
 };
 
 static int drff_play(struct input_dev *dev, void *data,
-				 struct ff_effect *effect)
+			const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct drff_device *drff = data;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 	int strong, weak;
 
-	strong = effect->u.rumble.strong_magnitude;
-	weak = effect->u.rumble.weak_magnitude;
+	strong = rumble_force->strong;
+	weak = rumble_force->weak;
 
 	dbg_hid("called with 0x%04x 0x%04x", strong, weak);
 
-	if (strong || weak) {
-		strong = strong * 0xff / 0xffff;
-		weak = weak * 0xff / 0xffff;
-
-		/* While reverse engineering this device, I found that when
-		   this value is set, it causes the strong rumble to function
-		   at a near maximum speed, so we'll bypass it. */
-		if (weak == 0x0a)
-			weak = 0x0b;
-
-		drff->report->field[0]->value[0] = 0x51;
-		drff->report->field[0]->value[1] = 0x00;
-		drff->report->field[0]->value[2] = weak;
-		drff->report->field[0]->value[4] = strong;
-		hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT);
-
-		drff->report->field[0]->value[0] = 0xfa;
-		drff->report->field[0]->value[1] = 0xfe;
-	} else {
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		if (strong || weak) {
+			strong = strong * 0xff / 0xffff;
+			weak = weak * 0xff / 0xffff;
+
+			/* While reverse engineering this device, I found that when
+			this value is set, it causes the strong rumble to function
+			at a near maximum speed, so we'll bypass it. */
+			if (weak == 0x0a)
+				weak = 0x0b;
+
+			drff->report->field[0]->value[0] = 0x51;
+			drff->report->field[0]->value[1] = 0x00;
+			drff->report->field[0]->value[2] = weak;
+			drff->report->field[0]->value[4] = strong;
+			hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT);
+
+			drff->report->field[0]->value[0] = 0xfa;
+			drff->report->field[0]->value[1] = 0xfe;
+		} else {
+			drff->report->field[0]->value[0] = 0xf3;
+			drff->report->field[0]->value[1] = 0x00;
+		}
+		break;
+	case MLNX_STOP_RUMBLE:
 		drff->report->field[0]->value[0] = 0xf3;
 		drff->report->field[0]->value[1] = 0x00;
+		break;
+	default:
+		return -EINVAL;
 	}
 
 	drff->report->field[0]->value[2] = 0x00;
@@ -116,7 +129,7 @@ static int drff_init(struct hid_device *hid)
 
 	set_bit(FF_RUMBLE, dev->ffbit);
 
-	error = input_ff_create_memless(dev, drff, drff_play);
+	error = input_ff_create_mlnx(dev, drff, drff_play, FF_UPDATE_RATE);
 	if (error) {
 		kfree(drff);
 		return error;
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 09/24] hid: Port hid-dr to ff-memless-next
@ 2014-04-26 15:02   ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-dr to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig  |  2 +-
 drivers/hid/hid-dr.c | 59 ++++++++++++++++++++++++++++++++--------------------
 2 files changed, 37 insertions(+), 24 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index a78b5d8..1d4180c 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -205,7 +205,7 @@ config HID_DRAGONRISE
 config DRAGONRISE_FF
 	bool "DragonRise Inc. force feedback"
 	depends on HID_DRAGONRISE
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	Say Y here if you want to enable force feedback support for DragonRise Inc.
 	game controllers.
diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c
index ce06444..b95c676 100644
--- a/drivers/hid/hid-dr.c
+++ b/drivers/hid/hid-dr.c
@@ -31,8 +31,10 @@
 #include <linux/slab.h>
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/input/ff-memless-next.h>
 
 #include "hid-ids.h"
+#define FF_UPDATE_RATE 50
 
 #ifdef CONFIG_DRAGONRISE_FF
 
@@ -41,38 +43,49 @@ struct drff_device {
 };
 
 static int drff_play(struct input_dev *dev, void *data,
-				 struct ff_effect *effect)
+			const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct drff_device *drff = data;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 	int strong, weak;
 
-	strong = effect->u.rumble.strong_magnitude;
-	weak = effect->u.rumble.weak_magnitude;
+	strong = rumble_force->strong;
+	weak = rumble_force->weak;
 
 	dbg_hid("called with 0x%04x 0x%04x", strong, weak);
 
-	if (strong || weak) {
-		strong = strong * 0xff / 0xffff;
-		weak = weak * 0xff / 0xffff;
-
-		/* While reverse engineering this device, I found that when
-		   this value is set, it causes the strong rumble to function
-		   at a near maximum speed, so we'll bypass it. */
-		if (weak == 0x0a)
-			weak = 0x0b;
-
-		drff->report->field[0]->value[0] = 0x51;
-		drff->report->field[0]->value[1] = 0x00;
-		drff->report->field[0]->value[2] = weak;
-		drff->report->field[0]->value[4] = strong;
-		hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT);
-
-		drff->report->field[0]->value[0] = 0xfa;
-		drff->report->field[0]->value[1] = 0xfe;
-	} else {
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		if (strong || weak) {
+			strong = strong * 0xff / 0xffff;
+			weak = weak * 0xff / 0xffff;
+
+			/* While reverse engineering this device, I found that when
+			this value is set, it causes the strong rumble to function
+			at a near maximum speed, so we'll bypass it. */
+			if (weak == 0x0a)
+				weak = 0x0b;
+
+			drff->report->field[0]->value[0] = 0x51;
+			drff->report->field[0]->value[1] = 0x00;
+			drff->report->field[0]->value[2] = weak;
+			drff->report->field[0]->value[4] = strong;
+			hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT);
+
+			drff->report->field[0]->value[0] = 0xfa;
+			drff->report->field[0]->value[1] = 0xfe;
+		} else {
+			drff->report->field[0]->value[0] = 0xf3;
+			drff->report->field[0]->value[1] = 0x00;
+		}
+		break;
+	case MLNX_STOP_RUMBLE:
 		drff->report->field[0]->value[0] = 0xf3;
 		drff->report->field[0]->value[1] = 0x00;
+		break;
+	default:
+		return -EINVAL;
 	}
 
 	drff->report->field[0]->value[2] = 0x00;
@@ -116,7 +129,7 @@ static int drff_init(struct hid_device *hid)
 
 	set_bit(FF_RUMBLE, dev->ffbit);
 
-	error = input_ff_create_memless(dev, drff, drff_play);
+	error = input_ff_create_mlnx(dev, drff, drff_play, FF_UPDATE_RATE);
 	if (error) {
 		kfree(drff);
 		return error;
-- 
1.9.2

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 10/24] hid: Port hid-gaff to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (9 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-gaff to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig    |  2 +-
 drivers/hid/hid-gaff.c | 32 +++++++++++++++++++++++---------
 2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 1d4180c..4c59a88 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -667,7 +667,7 @@ config HID_GREENASIA
 config GREENASIA_FF
 	bool "GreenAsia (Product ID 0x12) force feedback support"
 	depends on HID_GREENASIA
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	Say Y here if you have a GreenAsia (Product ID 0x12) based game controller
 	(like MANTA Warrior MM816 and SpeedLink Strike2 SL-6635) or adapter
diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c
index 2d8cead..f2f70c1 100644
--- a/drivers/hid/hid-gaff.c
+++ b/drivers/hid/hid-gaff.c
@@ -31,8 +31,11 @@
 #include <linux/slab.h>
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/input/ff-memless-next.h>
 #include "hid-ids.h"
 
+#define FF_UPDATE_RATE 50
+
 #ifdef CONFIG_GREENASIA_FF
 
 struct gaff_device {
@@ -40,19 +43,30 @@ struct gaff_device {
 };
 
 static int hid_gaff_play(struct input_dev *dev, void *data,
-			 struct ff_effect *effect)
+			 const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct gaff_device *gaff = data;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 	int left, right;
 
-	left = effect->u.rumble.strong_magnitude;
-	right = effect->u.rumble.weak_magnitude;
-
-	dbg_hid("called with 0x%04x 0x%04x", left, right);
-
-	left = left * 0xfe / 0xffff;
-	right = right * 0xfe / 0xffff;
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		left = rumble_force->strong;
+		right = rumble_force->weak;
+
+		dbg_hid("called with 0x%04x 0x%04x", left, right);
+
+		left = left * 0xfe / 0xffff;
+		right = right * 0xfe / 0xffff;
+		break;
+	case MLNX_STOP_RUMBLE:
+		left = 0;
+		right = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	gaff->report->field[0]->value[0] = 0x51;
 	gaff->report->field[0]->value[1] = 0x0;
@@ -109,7 +123,7 @@ static int gaff_init(struct hid_device *hid)
 
 	set_bit(FF_RUMBLE, dev->ffbit);
 
-	error = input_ff_create_memless(dev, gaff, hid_gaff_play);
+	error = input_ff_create_mlnx(dev, gaff, hid_gaff_play, FF_UPDATE_RATE);
 	if (error) {
 		kfree(gaff);
 		return error;
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 11/24] hid: Port hid-holtekff to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (10 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-holtekff to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig        |  2 +-
 drivers/hid/hid-holtekff.c | 47 +++++++++++++++++++++++++++++-----------------
 2 files changed, 31 insertions(+), 18 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 4c59a88..1749a4a 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -256,7 +256,7 @@ config HID_HOLTEK
 config HOLTEK_FF
 	bool "Holtek On Line Grip force feedback support"
 	depends on HID_HOLTEK
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	  Say Y here if you have a Holtek On Line Grip based game controller
 	  and want to have force feedback support for it.
diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c
index 9325545..9c6064d 100644
--- a/drivers/hid/hid-holtekff.c
+++ b/drivers/hid/hid-holtekff.c
@@ -27,9 +27,12 @@
 #include <linux/input.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/input/ff-memless-next.h>
 
 #include "hid-ids.h"
 
+#define FF_UPDATE_RATE 50
+
 #ifdef CONFIG_HOLTEK_FF
 
 MODULE_LICENSE("GPL");
@@ -104,34 +107,44 @@ static void holtekff_send(struct holtekff_device *holtekff,
 }
 
 static int holtekff_play(struct input_dev *dev, void *data,
-			 struct ff_effect *effect)
+			 const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct holtekff_device *holtekff = data;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 	int left, right;
 	/* effect type 1, length 65535 msec */
 	u8 buf[HOLTEKFF_MSG_LENGTH] =
 		{ 0x01, 0x01, 0xff, 0xff, 0x10, 0xe0, 0x00 };
 
-	left = effect->u.rumble.strong_magnitude;
-	right = effect->u.rumble.weak_magnitude;
-	dbg_hid("called with 0x%04x 0x%04x\n", left, right);
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		left = rumble_force->strong;
+		right = rumble_force->weak;
+		dbg_hid("called with 0x%04x 0x%04x\n", left, right);
 
-	if (!left && !right) {
-		holtekff_send(holtekff, hid, stop_all6);
-		return 0;
-	}
+		if (!left && !right) {
+			holtekff_send(holtekff, hid, stop_all6);
+			return 0;
+		}
 
-	if (left)
-		buf[1] |= 0x80;
-	if (right)
-		buf[1] |= 0x40;
+		if (left)
+			buf[1] |= 0x80;
+		if (right)
+			buf[1] |= 0x40;
 
-	/* The device takes a single magnitude, so we just sum them up. */
-	buf[6] = min(0xf, (left >> 12) + (right >> 12));
+		/* The device takes a single magnitude, so we just sum them up. */
+		buf[6] = min(0xf, (left >> 12) + (right >> 12));
 
-	holtekff_send(holtekff, hid, buf);
-	holtekff_send(holtekff, hid, start_effect_1);
+		holtekff_send(holtekff, hid, buf);
+		holtekff_send(holtekff, hid, start_effect_1);
+		return 0;
+	case MLNX_STOP_RUMBLE:
+		holtekff_send(holtekff, hid, stop_all6);
+		return 0;
+	default:
+		return -EINVAL;
+	}
 
 	return 0;
 }
@@ -171,7 +184,7 @@ static int holtekff_init(struct hid_device *hid)
 	holtekff_send(holtekff, hid, stop_all4);
 	holtekff_send(holtekff, hid, stop_all6);
 
-	error = input_ff_create_memless(dev, holtekff, holtekff_play);
+	error = input_ff_create_mlnx(dev, holtekff, holtekff_play, FF_UPDATE_RATE);
 	if (error) {
 		kfree(holtekff);
 		return error;
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 12/24] hid: Port hid-lgff to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (11 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-lgff to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig    |  2 +-
 drivers/hid/hid-lgff.c | 70 +++++++++++++++++++++++++++++++++++---------------
 2 files changed, 51 insertions(+), 21 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 1749a4a..c4b0cbb 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -365,7 +365,7 @@ config HID_LOGITECH_DJ
 config LOGITECH_FF
 	bool "Logitech force feedback support"
 	depends on HID_LOGITECH
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	help
 	  Say Y here if you have one of these devices:
 	  - Logitech WingMan Cordless RumblePad
diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c
index e1394af..40ad68a 100644
--- a/drivers/hid/hid-lgff.c
+++ b/drivers/hid/hid-lgff.c
@@ -31,9 +31,12 @@
 
 #include <linux/input.h>
 #include <linux/hid.h>
+#include <linux/input/ff-memless-next.h>
 
 #include "hid-lg.h"
 
+#define FF_UPDATE_RATE 50
+
 struct dev_type {
 	u16 idVendor;
 	u16 idProduct;
@@ -47,11 +50,25 @@ static const signed short ff_rumble[] = {
 
 static const signed short ff_joystick[] = {
 	FF_CONSTANT,
+	FF_RAMP,
+	FF_PERIODIC,
+	FF_SQUARE,
+	FF_TRIANGLE,
+	FF_SINE,
+	FF_SAW_UP,
+	FF_SAW_DOWN,
 	-1
 };
 
 static const signed short ff_joystick_ac[] = {
 	FF_CONSTANT,
+	FF_RAMP,
+	FF_PERIODIC,
+	FF_SQUARE,
+	FF_TRIANGLE,
+	FF_SINE,
+	FF_SAW_UP,
+	FF_SAW_DOWN,
 	FF_AUTOCENTER,
 	-1
 };
@@ -66,45 +83,58 @@ static const struct dev_type devices[] = {
 	{ 0x046d, 0xc295, ff_joystick },
 };
 
-static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+static int hid_lgff_play(struct input_dev *dev, void *data,
+			 const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 	struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-	int x, y;
-	unsigned int left, right;
 
-#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
+	switch (command->cmd) {
+	case MLNX_START_COMBINED: {
+		const struct mlnx_simple_force *simple_force = &command->u.simple_force;
+		/* Scale down from MLNX range */
+		const int x = 0x80 - (simple_force->x * 0xff / 0xffff);
+		const int y = 0x80 - (simple_force->y * 0xff / 0xffff);
 
-	switch (effect->type) {
-	case FF_CONSTANT:
-		x = effect->u.ramp.start_level + 0x7f;	/* 0x7f is center */
-		y = effect->u.ramp.end_level + 0x7f;
-		CLAMP(x);
-		CLAMP(y);
 		report->field[0]->value[0] = 0x51;
 		report->field[0]->value[1] = 0x08;
 		report->field[0]->value[2] = x;
 		report->field[0]->value[3] = y;
 		dbg_hid("(x, y)=(%04x, %04x)\n", x, y);
-		hid_hw_request(hid, report, HID_REQ_SET_REPORT);
 		break;
+	}
+	case MLNX_STOP_COMBINED:
+		report->field[0]->value[0] = 0x51;
+		report->field[0]->value[1] = 0x08;
+		report->field[0]->value[2] = 0x80;
+		report->field[0]->value[3] = 0x80;
+		break;
+	case MLNX_START_RUMBLE: {
+		const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
+		/* Scale down from MLNX range */
+		const unsigned int right = rumble_force->weak * 0xff / 0xffff;
+		const unsigned int left = rumble_force->strong * 0xff / 0xffff;
 
-	case FF_RUMBLE:
-		right = effect->u.rumble.strong_magnitude;
-		left = effect->u.rumble.weak_magnitude;
-		right = right * 0xff / 0xffff;
-		left = left * 0xff / 0xffff;
-		CLAMP(left);
-		CLAMP(right);
 		report->field[0]->value[0] = 0x42;
 		report->field[0]->value[1] = 0x00;
 		report->field[0]->value[2] = left;
 		report->field[0]->value[3] = right;
 		dbg_hid("(left, right)=(%04x, %04x)\n", left, right);
-		hid_hw_request(hid, report, HID_REQ_SET_REPORT);
 		break;
 	}
+	case MLNX_STOP_RUMBLE:
+		report->field[0]->value[0] = 0x42;
+		report->field[0]->value[1] = 0x00;
+		report->field[0]->value[2] = 0;
+		report->field[0]->value[3] = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+
 	return 0;
 }
 
@@ -148,7 +178,7 @@ int lgff_init(struct hid_device* hid)
 	for (i = 0; ff_bits[i] >= 0; i++)
 		set_bit(ff_bits[i], dev->ffbit);
 
-	error = input_ff_create_memless(dev, NULL, hid_lgff_play);
+	error = input_ff_create_mlnx(dev, NULL, hid_lgff_play, FF_UPDATE_RATE);
 	if (error)
 		return error;
 
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 13/24] hid: Port hid-lg3ff to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (12 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-lg3ff to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig     |  2 +-
 drivers/hid/hid-lg3ff.c | 60 +++++++++++++++++++++++++++++++------------------
 2 files changed, 39 insertions(+), 23 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index c4b0cbb..eb0c7f1 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -391,7 +391,7 @@ config LOGIRUMBLEPAD2_FF
 config LOGIG940_FF
 	bool "Logitech Flight System G940 force feedback support"
 	depends on HID_LOGITECH
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	help
 	  Say Y here if you want to enable force feedback support for Logitech
 	  Flight System G940 devices.
diff --git a/drivers/hid/hid-lg3ff.c b/drivers/hid/hid-lg3ff.c
index 8c2da18..c49b374 100644
--- a/drivers/hid/hid-lg3ff.c
+++ b/drivers/hid/hid-lg3ff.c
@@ -23,9 +23,12 @@
 
 #include <linux/input.h>
 #include <linux/hid.h>
+#include <linux/input/ff-memless-next.h>
 
 #include "hid-lg.h"
 
+#define FF_UPDATE_RATE 50
+
 /*
  * G940 Theory of Operation (from experimentation)
  *
@@ -58,12 +61,11 @@ struct lg3ff_device {
 };
 
 static int hid_lg3ff_play(struct input_dev *dev, void *data,
-			 struct ff_effect *effect)
+			 const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 	struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-	int x, y;
 
 /*
  * Available values in the field should always be 63, but we only use up to
@@ -72,30 +74,37 @@ static int hid_lg3ff_play(struct input_dev *dev, void *data,
 	memset(report->field[0]->value, 0,
 	       sizeof(__s32) * report->field[0]->report_count);
 
-	switch (effect->type) {
-	case FF_CONSTANT:
-/*
- * Already clamped in ff_memless
- * 0 is center (different then other logitech)
- */
-		x = effect->u.ramp.start_level;
-		y = effect->u.ramp.end_level;
-
-		/* send command byte */
-		report->field[0]->value[0] = 0x51;
-
-/*
- * Sign backwards from other Force3d pro
- * which get recast here in two's complement 8 bits
- */
-		report->field[0]->value[1] = (unsigned char)(-x);
-		report->field[0]->value[31] = (unsigned char)(-y);
+	/* send command byte */
+	report->field[0]->value[0] = 0x51;
 
-		hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+	switch (command->cmd) {
+	case MLNX_START_COMBINED: {
+		const struct mlnx_simple_force *simple_force = &command->u.simple_force;
+		/* Scale down from MLNX range */
+		const int x = simple_force->x * 0xff / 0xffff;
+		const int y = simple_force->y * 0xff / 0xffff;
+
+		/*
+		 * Sign backwards from other Force3d pro
+		 * which get recast here in two's complement 8 bits
+		*/
+		report->field[0]->value[1] = (unsigned char)x;
+		report->field[0]->value[31] = (unsigned char)y;
+		break;
+		}
+	case MLNX_STOP_COMBINED:
+		report->field[0]->value[1] = 0;
+		report->field[0]->value[31] = 0;
 		break;
+	default:
+		return -EINVAL;
 	}
+
+	hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+
 	return 0;
 }
+
 static void hid_lg3ff_set_autocenter(struct input_dev *dev, u16 magnitude)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
@@ -123,6 +132,13 @@ static void hid_lg3ff_set_autocenter(struct input_dev *dev, u16 magnitude)
 
 static const signed short ff3_joystick_ac[] = {
 	FF_CONSTANT,
+	FF_RAMP,
+	FF_PERIODIC,
+	FF_SQUARE,
+	FF_TRIANGLE,
+	FF_SINE,
+	FF_SAW_UP,
+	FF_SAW_DOWN,
 	FF_AUTOCENTER,
 	-1
 };
@@ -143,7 +159,7 @@ int lg3ff_init(struct hid_device *hid)
 	for (i = 0; ff_bits[i] >= 0; i++)
 		set_bit(ff_bits[i], dev->ffbit);
 
-	error = input_ff_create_memless(dev, NULL, hid_lg3ff_play);
+	error = input_ff_create_mlnx(dev, NULL, hid_lg3ff_play, FF_UPDATE_RATE);
 	if (error)
 		return error;
 
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 14/24] hid: Port hid-pl to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (13 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-pl to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig  |  2 +-
 drivers/hid/hid-pl.c | 38 ++++++++++++++++++++++++++------------
 2 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index eb0c7f1..42904e4 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -509,7 +509,7 @@ config HID_PANTHERLORD
 config PANTHERLORD_FF
 	bool "Pantherlord force feedback support"
 	depends on HID_PANTHERLORD
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	  Say Y here if you have a PantherLord/GreenAsia based game controller
 	  or adapter and want to enable force feedback support for it.
diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c
index 2dcd7d9..9b539d5 100644
--- a/drivers/hid/hid-pl.c
+++ b/drivers/hid/hid-pl.c
@@ -44,9 +44,12 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/hid.h>
+#include <linux/input/ff-memless-next.h>
 
 #include "hid-ids.h"
 
+#define FF_UPDATE_RATE 50
+
 #ifdef CONFIG_PANTHERLORD_FF
 
 struct plff_device {
@@ -57,24 +60,35 @@ struct plff_device {
 };
 
 static int hid_plff_play(struct input_dev *dev, void *data,
-			 struct ff_effect *effect)
+			 const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct plff_device *plff = data;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 	int left, right;
 
-	left = effect->u.rumble.strong_magnitude;
-	right = effect->u.rumble.weak_magnitude;
-	debug("called with 0x%04x 0x%04x", left, right);
-
-	left = left * plff->maxval / 0xffff;
-	right = right * plff->maxval / 0xffff;
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		left = rumble_force->strong;
+		right = rumble_force->weak;
+		debug("called with 0x%04x 0x%04x", left, right);
+
+		left = left * plff->maxval / 0xffff;
+		right = right * plff->maxval / 0xffff;
+
+		*plff->strong = left;
+		*plff->weak = right;
+		debug("running with 0x%02x 0x%02x", left, right);
+		break;
+	case MLNX_STOP_RUMBLE:
+		*plff->strong = 0;
+		*plff->weak = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
 
-	*plff->strong = left;
-	*plff->weak = right;
-	debug("running with 0x%02x 0x%02x", left, right);
 	hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT);
-
 	return 0;
 }
 
@@ -160,7 +174,7 @@ static int plff_init(struct hid_device *hid)
 
 		set_bit(FF_RUMBLE, dev->ffbit);
 
-		error = input_ff_create_memless(dev, plff, hid_plff_play);
+		error = input_ff_create_mlnx(dev, plff, hid_plff_play, FF_UPDATE_RATE);
 		if (error) {
 			kfree(plff);
 			return error;
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 15/24] hid: Port hid-sjoy to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (14 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-sjoy to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig    |  2 +-
 drivers/hid/hid-sjoy.c | 35 +++++++++++++++++++++++++----------
 2 files changed, 26 insertions(+), 11 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 42904e4..9260d14 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -692,7 +692,7 @@ config HID_SMARTJOYPLUS
 config SMARTJOYPLUS_FF
 	bool "SmartJoy PLUS PS2/USB adapter force feedback support"
 	depends on HID_SMARTJOYPLUS
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	Say Y here if you have a SmartJoy PLUS PS2/USB adapter and want to
 	enable force feedback support for it.
diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c
index 37845ec..a6f8cfe 100644
--- a/drivers/hid/hid-sjoy.c
+++ b/drivers/hid/hid-sjoy.c
@@ -30,8 +30,11 @@
 #include <linux/slab.h>
 #include <linux/hid.h>
 #include <linux/module.h>
+#include <linux/input/ff-memless-next.h>
 #include "hid-ids.h"
 
+#define FF_UPDATE_RATE 50
+
 #ifdef CONFIG_SMARTJOYPLUS_FF
 
 struct sjoyff_device {
@@ -39,21 +42,33 @@ struct sjoyff_device {
 };
 
 static int hid_sjoyff_play(struct input_dev *dev, void *data,
-			 struct ff_effect *effect)
+			const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct sjoyff_device *sjoyff = data;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 	u32 left, right;
 
-	left = effect->u.rumble.strong_magnitude;
-	right = effect->u.rumble.weak_magnitude;
-	dev_dbg(&dev->dev, "called with 0x%08x 0x%08x\n", left, right);
-
-	left = left * 0xff / 0xffff;
-	right = (right != 0); /* on/off only */
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		left = rumble_force->strong;
+		right = rumble_force->weak;
+		dev_dbg(&dev->dev, "called with 0x%08x 0x%08x\n", left, right);
+
+		left = left * 0xff / 0xffff;
+		right = (right != 0); /* on/off only */
+
+		sjoyff->report->field[0]->value[1] = right;
+		sjoyff->report->field[0]->value[2] = left;
+		break;
+	case MLNX_STOP_RUMBLE:
+		left = 0;
+		right = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
 
-	sjoyff->report->field[0]->value[1] = right;
-	sjoyff->report->field[0]->value[2] = left;
 	dev_dbg(&dev->dev, "running with 0x%02x 0x%02x\n", left, right);
 	hid_hw_request(hid, sjoyff->report, HID_REQ_SET_REPORT);
 
@@ -103,7 +118,7 @@ static int sjoyff_init(struct hid_device *hid)
 
 		set_bit(FF_RUMBLE, dev->ffbit);
 
-		error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play);
+		error = input_ff_create_mlnx(dev, sjoyff, hid_sjoyff_play, FF_UPDATE_RATE);
 		if (error) {
 			kfree(sjoyff);
 			return error;
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 16/24] hid: Port hid-sony to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (15 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-sony to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig    |  2 +-
 drivers/hid/hid-sony.c | 23 +++++++++++++++++------
 2 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 9260d14..e97c382 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -634,7 +634,7 @@ config HID_SONY
 config SONY_FF
 	bool "Sony PS2/3/4 accessories force feedback support" 
 	depends on HID_SONY
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	Say Y here if you have a Sony PS2/3/4 accessory and want to enable
 	force feedback support for it.
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 908de27..5a28833 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -34,6 +34,7 @@
 #include <linux/spinlock.h>
 #include <linux/list.h>
 #include <linux/input/mt.h>
+#include <linux/input/ff-memless-next.h>
 
 #include "hid-ids.h"
 
@@ -53,6 +54,7 @@
 #define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
 #define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER)
 
+#define FF_UPDATE_RATE 50
 #define MAX_LEDS 4
 
 static const u8 sixaxis_rdesc_fixup[] = {
@@ -1308,16 +1310,25 @@ static void dualshock4_state_worker(struct work_struct *work)
 
 #ifdef CONFIG_SONY_FF
 static int sony_play_effect(struct input_dev *dev, void *data,
-			    struct ff_effect *effect)
+			    const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct sony_sc *sc = hid_get_drvdata(hid);
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 
-	if (effect->type != FF_RUMBLE)
-		return 0;
 
-	sc->left = effect->u.rumble.strong_magnitude / 256;
-	sc->right = effect->u.rumble.weak_magnitude / 256;
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		sc->left = rumble_force->strong / 256;
+		sc->right = rumble_force->weak / 256;
+		break;
+	case MLNX_STOP_RUMBLE:
+		sc->left = 0;
+		sc->right = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	schedule_work(&sc->state_worker);
 	return 0;
@@ -1330,7 +1341,7 @@ static int sony_init_ff(struct hid_device *hdev)
 	struct input_dev *input_dev = hidinput->input;
 
 	input_set_capability(input_dev, EV_FF, FF_RUMBLE);
-	return input_ff_create_memless(input_dev, NULL, sony_play_effect);
+	return input_ff_create_mlnx(input_dev, NULL, sony_play_effect, FF_UPDATE_RATE);
 }
 
 #else
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 17/24] hid: Port hid-tmff to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (16 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-tmff to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig    |  2 +-
 drivers/hid/hid-tmff.c | 83 ++++++++++++++++++++++++++++++--------------------
 2 files changed, 51 insertions(+), 34 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index e97c382..17ed5cf 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -730,7 +730,7 @@ config HID_THRUSTMASTER
 config THRUSTMASTER_FF
 	bool "ThrustMaster devices force feedback support"
 	depends on HID_THRUSTMASTER
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or 3,
 	  a THRUSTMASTER Dual Trigger 3-in-1 or a THRUSTMASTER Ferrari GT
diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c
index b833760..3df000c 100644
--- a/drivers/hid/hid-tmff.c
+++ b/drivers/hid/hid-tmff.c
@@ -31,9 +31,12 @@
 #include <linux/input.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/input/ff-memless-next.h>
 
 #include "hid-ids.h"
 
+#define FF_UPDATE_RATE 50
+
 static const signed short ff_rumble[] = {
 	FF_RUMBLE,
 	-1
@@ -41,6 +44,13 @@ static const signed short ff_rumble[] = {
 
 static const signed short ff_joystick[] = {
 	FF_CONSTANT,
+	FF_RAMP,
+	FF_PERIODIC,
+	FF_SINE,
+	FF_SQUARE,
+	FF_TRIANGLE,
+	FF_SAW_DOWN,
+	FF_SAW_UP,
 	-1
 };
 
@@ -67,12 +77,12 @@ static inline int tmff_scale_u16(unsigned int in, int minimum, int maximum)
 	return ret;
 }
 
-/* Changes values from -0x80 to 0x7f into values from minimum to maximum */
-static inline int tmff_scale_s8(int in, int minimum, int maximum)
+/* Changes values from -0x7fff to 0x7fff into values from minimum to maximum */
+static inline int tmff_scale_s32(int in, int minimum, int maximum)
 {
 	int ret;
 
-	ret = (((in + 0x80) * (maximum - minimum)) / 0xff) + minimum;
+	ret = (((in + 0x7fff) * (maximum - minimum)) / 0xffff) + minimum;
 	if (ret < minimum)
 		return minimum;
 	if (ret > maximum)
@@ -81,43 +91,50 @@ static inline int tmff_scale_s8(int in, int minimum, int maximum)
 }
 
 static int tmff_play(struct input_dev *dev, void *data,
-		struct ff_effect *effect)
+		const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct tmff_device *tmff = data;
 	struct hid_field *ff_field = tmff->ff_field;
 	int x, y;
-	int left, right;	/* Rumbling */
-
-	switch (effect->type) {
-	case FF_CONSTANT:
-		x = tmff_scale_s8(effect->u.ramp.start_level,
-					ff_field->logical_minimum,
-					ff_field->logical_maximum);
-		y = tmff_scale_s8(effect->u.ramp.end_level,
-					ff_field->logical_minimum,
-					ff_field->logical_maximum);
-
-		dbg_hid("(x, y)=(%04x, %04x)\n", x, y);
-		ff_field->value[0] = x;
-		ff_field->value[1] = y;
-		hid_hw_request(hid, tmff->report, HID_REQ_SET_REPORT);
-		break;
 
-	case FF_RUMBLE:
-		left = tmff_scale_u16(effect->u.rumble.weak_magnitude,
-					ff_field->logical_minimum,
-					ff_field->logical_maximum);
-		right = tmff_scale_u16(effect->u.rumble.strong_magnitude,
-					ff_field->logical_minimum,
-					ff_field->logical_maximum);
-
-		dbg_hid("(left,right)=(%08x, %08x)\n", left, right);
-		ff_field->value[0] = left;
-		ff_field->value[1] = right;
-		hid_hw_request(hid, tmff->report, HID_REQ_SET_REPORT);
+	switch (command->cmd) {
+	case MLNX_START_COMBINED: {
+		const struct mlnx_simple_force *sf = &command->u.simple_force;
+		x = tmff_scale_s32(sf->x,
+				   ff_field->logical_minimum,
+				   ff_field->logical_maximum);
+		y = tmff_scale_s32(sf->y,
+				   ff_field->logical_minimum,
+				   ff_field->logical_maximum);
+		break;
+		}
+	case MLNX_STOP_COMBINED:
+		x = 0;
+		y = 0;
+		break;
+	case MLNX_START_RUMBLE: {
+		const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
+		x = tmff_scale_u16(rumble_force->weak,
+				   ff_field->logical_minimum,
+				   ff_field->logical_maximum);
+		y = tmff_scale_u16(rumble_force->strong,
+				   ff_field->logical_minimum,
+				   ff_field->logical_maximum);
 		break;
+		}
+	case MLNX_STOP_RUMBLE:
+		x = 0;
+		y = 0;
+		break;
+	default:
+		return -EINVAL;
 	}
+
+	ff_field->value[0] = x;
+	ff_field->value[1] = y;
+
+	hid_hw_request(hid, tmff->report, HID_REQ_SET_REPORT);
 	return 0;
 }
 
@@ -192,7 +209,7 @@ static int tmff_init(struct hid_device *hid, const signed short *ff_bits)
 		goto fail;
 	}
 
-	error = input_ff_create_memless(input_dev, tmff, tmff_play);
+	error = input_ff_create_mlnx(input_dev, tmff, tmff_play, FF_UPDATE_RATE);
 	if (error)
 		goto fail;
 
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 18/24] hid: Port hid-wiimote-modules to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (17 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-wiimote-modules to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig               |  2 +-
 drivers/hid/hid-wiimote-modules.c | 74 ++++++++++++++++++++++++---------------
 2 files changed, 46 insertions(+), 30 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 17ed5cf..23d9776 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -749,7 +749,7 @@ config HID_WIIMOTE
 	depends on HID
 	depends on LEDS_CLASS
 	select POWER_SUPPLY
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	Support for Nintendo Wii and Wii U Bluetooth peripherals. Supported
 	devices are the Wii Remote and its extension devices, but also devices
diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c
index 6b61f01..95b20ea 100644
--- a/drivers/hid/hid-wiimote-modules.c
+++ b/drivers/hid/hid-wiimote-modules.c
@@ -37,8 +37,11 @@
 #include <linux/hid.h>
 #include <linux/input.h>
 #include <linux/spinlock.h>
+#include <linux/input/ff-memless-next.h>
 #include "hid-wiimote.h"
 
+#define FF_UPDATE_RATE 50
+
 /*
  * Keys
  * The initial Wii Remote provided a bunch of buttons that are reported as
@@ -131,21 +134,27 @@ static void wiimod_rumble_worker(struct work_struct *work)
 }
 
 static int wiimod_rumble_play(struct input_dev *dev, void *data,
-			      struct ff_effect *eff)
+			      const struct mlnx_effect_command *command)
 {
 	struct wiimote_data *wdata = input_get_drvdata(dev);
-	__u8 value;
-
-	/*
-	 * The wiimote supports only a single rumble motor so if any magnitude
-	 * is set to non-zero then we start the rumble motor. If both are set to
-	 * zero, we stop the rumble motor.
-	 */
-
-	if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude)
-		value = 1;
-	else
-		value = 0;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
+	__u8 value = 0;
+
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		/*
+		 * The wiimote supports only a single rumble motor so if any magnitude
+		 * is set to non-zero then we start the rumble motor. If both are set to
+		 * zero, we stop the rumble motor.
+		 */
+		if (rumble_force->strong || rumble_force->weak)
+			value = 1;
+		break;
+	case MLNX_STOP_RUMBLE:
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	/* Locking state.lock here might deadlock with input_event() calls.
 	 * schedule_work acts as barrier. Merging multiple changes is fine. */
@@ -161,7 +170,7 @@ static int wiimod_rumble_probe(const struct wiimod_ops *ops,
 	INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker);
 
 	set_bit(FF_RUMBLE, wdata->input->ffbit);
-	if (input_ff_create_memless(wdata->input, NULL, wiimod_rumble_play))
+	if (input_ff_create_mlnx(wdata->input, NULL, wiimod_rumble_play, FF_UPDATE_RATE))
 		return -ENOMEM;
 
 	return 0;
@@ -1771,21 +1780,28 @@ static void wiimod_pro_close(struct input_dev *dev)
 }
 
 static int wiimod_pro_play(struct input_dev *dev, void *data,
-			   struct ff_effect *eff)
+			   const struct mlnx_effect_command *command)
 {
 	struct wiimote_data *wdata = input_get_drvdata(dev);
-	__u8 value;
-
-	/*
-	 * The wiimote supports only a single rumble motor so if any magnitude
-	 * is set to non-zero then we start the rumble motor. If both are set to
-	 * zero, we stop the rumble motor.
-	 */
-
-	if (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude)
-		value = 1;
-	else
-		value = 0;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
+	__u8 value = 0;
+
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		/*
+		 * The wiimote supports only a single rumble motor so if any magnitude
+		 * is set to non-zero then we start the rumble motor. If both are set to
+		 * zero, we stop the rumble motor.
+		 */
+
+		if (rumble_force->strong || rumble_force->weak)
+			value = 1;
+		break;
+	case MLNX_STOP_RUMBLE:
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	/* Locking state.lock here might deadlock with input_event() calls.
 	 * schedule_work acts as barrier. Merging multiple changes is fine. */
@@ -1867,8 +1883,8 @@ static int wiimod_pro_probe(const struct wiimod_ops *ops,
 	set_bit(FF_RUMBLE, wdata->extension.input->ffbit);
 	input_set_drvdata(wdata->extension.input, wdata);
 
-	if (input_ff_create_memless(wdata->extension.input, NULL,
-				    wiimod_pro_play)) {
+	if (input_ff_create_mlnx(wdata->extension.input, NULL,
+				 wiimod_pro_play, FF_UPDATE_RATE)) {
 		ret = -ENOMEM;
 		goto err_free;
 	}
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 19/24] hid: Port hid-zpff to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (18 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-zpff to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/hid/Kconfig    |  2 +-
 drivers/hid/hid-zpff.c | 30 ++++++++++++++++++++++--------
 2 files changed, 23 insertions(+), 9 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 23d9776..97d2d8f 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -786,7 +786,7 @@ config HID_ZEROPLUS
 config ZEROPLUS_FF
 	bool "Zeroplus based game controller force feedback support"
 	depends on HID_ZEROPLUS
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	  Say Y here if you have a Zeroplus based game controller and want
 	  to have force feedback support for it.
diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c
index a29756c..6912500 100644
--- a/drivers/hid/hid-zpff.c
+++ b/drivers/hid/hid-zpff.c
@@ -25,9 +25,12 @@
 #include <linux/input.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/input/ff-memless-next.h>
 
 #include "hid-ids.h"
 
+#define FF_UPDATE_RATE 50
+
 #ifdef CONFIG_ZEROPLUS_FF
 
 struct zpff_device {
@@ -35,10 +38,11 @@ struct zpff_device {
 };
 
 static int zpff_play(struct input_dev *dev, void *data,
-			 struct ff_effect *effect)
+			 const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct zpff_device *zpff = data;
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 	int left, right;
 
 	/*
@@ -47,12 +51,22 @@ static int zpff_play(struct input_dev *dev, void *data,
 	 * however it is possible that the XFX Executioner is an exception
 	 */
 
-	left = effect->u.rumble.strong_magnitude;
-	right = effect->u.rumble.weak_magnitude;
-	dbg_hid("called with 0x%04x 0x%04x\n", left, right);
-
-	left = left * 0x7f / 0xffff;
-	right = right * 0x7f / 0xffff;
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		left = rumble_force->strong;
+		right = rumble_force->weak;
+		dbg_hid("called with 0x%04x 0x%04x\n", left, right);
+
+		left = left * 0x7f / 0xffff;
+		right = right * 0x7f / 0xffff;
+		break;
+	case MLNX_STOP_RUMBLE:
+		left = 0;
+		right = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	zpff->report->field[2]->value[0] = left;
 	zpff->report->field[3]->value[0] = right;
@@ -83,7 +97,7 @@ static int zpff_init(struct hid_device *hid)
 
 	set_bit(FF_RUMBLE, dev->ffbit);
 
-	error = input_ff_create_memless(dev, zpff, zpff_play);
+	error = input_ff_create_mlnx(dev, zpff, zpff_play, FF_UPDATE_RATE);
 	if (error) {
 		kfree(zpff);
 		return error;
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 20/24] input: Port gamecon to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (19 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port gamecon to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/input/joystick/Kconfig   |  2 +-
 drivers/input/joystick/gamecon.c | 57 ++++++++++++++++++++++------------------
 2 files changed, 33 insertions(+), 26 deletions(-)

diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 56eb471..2dd3ba1 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -221,7 +221,7 @@ config JOYSTICK_DB9
 config JOYSTICK_GAMECON
 	tristate "Multisystem, NES, SNES, N64, PSX joysticks and gamepads"
 	depends on PARPORT
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	  Say Y here if you have a Nintendo Entertainment System gamepad,
 	  Super Nintendo Entertainment System gamepad, Nintendo 64 gamepad,
diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c
index e68e497..209d0fb 100644
--- a/drivers/input/joystick/gamecon.c
+++ b/drivers/input/joystick/gamecon.c
@@ -40,6 +40,7 @@
 #include <linux/input.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/input/ff-memless-next.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("NES, SNES, N64, MultiSystem, PSX gamepad driver");
@@ -47,6 +48,7 @@ MODULE_LICENSE("GPL");
 
 #define GC_MAX_PORTS		3
 #define GC_MAX_DEVICES		5
+#define FF_UPDATE_RATE 50
 
 struct gc_config {
 	int args[GC_MAX_DEVICES + 1];
@@ -263,43 +265,48 @@ static void gc_n64_process_packet(struct gc *gc)
 }
 
 static int gc_n64_play_effect(struct input_dev *dev, void *data,
-			      struct ff_effect *effect)
+			      const struct mlnx_effect_command *command)
 {
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
 	int i;
 	unsigned long flags;
 	struct gc *gc = input_get_drvdata(dev);
 	struct gc_subdev *sdev = data;
 	unsigned char target = 1 << sdev->idx; /* select desired pin */
+	unsigned int cmd;
 
-	if (effect->type == FF_RUMBLE) {
-		struct ff_rumble_effect *rumble = &effect->u.rumble;
-		unsigned int cmd =
-			rumble->strong_magnitude || rumble->weak_magnitude ?
-			GC_N64_CMD_01 : GC_N64_CMD_00;
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		cmd = (rumble_force->strong || rumble_force->weak) ? GC_N64_CMD_01 : GC_N64_CMD_00;
+		break;
+	case MLNX_STOP_RUMBLE:
+		cmd = GC_N64_CMD_00;
+		break;
+	default:
+		return -EINVAL;
+	}
 
-		local_irq_save(flags);
+	local_irq_save(flags);
 
-		/* Init Rumble - 0x03, 0x80, 0x01, (34)0x80 */
-		gc_n64_send_command(gc, GC_N64_CMD_03, target);
+	/* Init Rumble - 0x03, 0x80, 0x01, (34)0x80 */
+	gc_n64_send_command(gc, GC_N64_CMD_03, target);
+	gc_n64_send_command(gc, GC_N64_CMD_80, target);
+	gc_n64_send_command(gc, GC_N64_CMD_01, target);
+	for (i = 0; i < 32; i++)
 		gc_n64_send_command(gc, GC_N64_CMD_80, target);
-		gc_n64_send_command(gc, GC_N64_CMD_01, target);
-		for (i = 0; i < 32; i++)
-			gc_n64_send_command(gc, GC_N64_CMD_80, target);
-		gc_n64_send_stop_bit(gc, target);
+	gc_n64_send_stop_bit(gc, target);
 
-		udelay(GC_N64_DELAY);
-
-		/* Now start or stop it - 0x03, 0xc0, 0zx1b, (32)0x01/0x00 */
-		gc_n64_send_command(gc, GC_N64_CMD_03, target);
-		gc_n64_send_command(gc, GC_N64_CMD_c0, target);
-		gc_n64_send_command(gc, GC_N64_CMD_1b, target);
-		for (i = 0; i < 32; i++)
-			gc_n64_send_command(gc, cmd, target);
-		gc_n64_send_stop_bit(gc, target);
+	udelay(GC_N64_DELAY);
 
-		local_irq_restore(flags);
+	/* Now start or stop it - 0x03, 0xc0, 0zx1b, (32)0x01/0x00 */
+	gc_n64_send_command(gc, GC_N64_CMD_03, target);
+	gc_n64_send_command(gc, GC_N64_CMD_c0, target);
+	gc_n64_send_command(gc, GC_N64_CMD_1b, target);
+	for (i = 0; i < 32; i++)
+		gc_n64_send_command(gc, cmd, target);
+	gc_n64_send_stop_bit(gc, target);
 
-	}
+	local_irq_restore(flags);
 
 	return 0;
 }
@@ -317,7 +324,7 @@ static int __init gc_n64_init_ff(struct input_dev *dev, int i)
 
 	input_set_capability(dev, EV_FF, FF_RUMBLE);
 
-	err = input_ff_create_memless(dev, sdev, gc_n64_play_effect);
+	err = input_ff_create_mlnx(dev, sdev, gc_n64_play_effect, FF_UPDATE_RATE);
 	if (err) {
 		kfree(sdev);
 		return err;
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 21/24] input: Port xpad to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (20 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port xpad to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/input/joystick/Kconfig |   2 +-
 drivers/input/joystick/xpad.c  | 125 +++++++++++++++++++++++------------------
 2 files changed, 71 insertions(+), 56 deletions(-)

diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 2dd3ba1..9f26e48 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -294,7 +294,7 @@ config JOYSTICK_XPAD
 config JOYSTICK_XPAD_FF
 	bool "X-Box gamepad rumble support"
 	depends on JOYSTICK_XPAD && INPUT
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	---help---
 	  Say Y here if you want to take advantage of xbox 360 rumble features.
 
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 603fe0d..5d9c8a2 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -78,6 +78,7 @@
 #include <linux/stat.h>
 #include <linux/module.h>
 #include <linux/usb/input.h>
+#include <linux/input/ff-memless-next.h>
 
 #define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
 #define DRIVER_DESC "X-Box pad driver"
@@ -97,6 +98,8 @@
 #define XTYPE_XBOX360W    2
 #define XTYPE_UNKNOWN     3
 
+#define FF_UPDATE_RATE 50
+
 static bool dpad_to_buttons;
 module_param(dpad_to_buttons, bool, S_IRUGO);
 MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads");
@@ -628,63 +631,75 @@ static void xpad_stop_output(struct usb_xpad *xpad) {}
 #endif
 
 #ifdef CONFIG_JOYSTICK_XPAD_FF
-static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect)
+static int xpad_play_effect(struct input_dev *dev, void *data,
+			    const struct mlnx_effect_command *command)
 {
 	struct usb_xpad *xpad = input_get_drvdata(dev);
+	const struct mlnx_rumble_force *rumble_force = &command->u.rumble_force;
+	__u16 strong, weak;
 
-	if (effect->type == FF_RUMBLE) {
-		__u16 strong = effect->u.rumble.strong_magnitude;
-		__u16 weak = effect->u.rumble.weak_magnitude;
-
-		switch (xpad->xtype) {
-
-		case XTYPE_XBOX:
-			xpad->odata[0] = 0x00;
-			xpad->odata[1] = 0x06;
-			xpad->odata[2] = 0x00;
-			xpad->odata[3] = strong / 256;	/* left actuator */
-			xpad->odata[4] = 0x00;
-			xpad->odata[5] = weak / 256;	/* right actuator */
-			xpad->irq_out->transfer_buffer_length = 6;
-
-			return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
-
-		case XTYPE_XBOX360:
-			xpad->odata[0] = 0x00;
-			xpad->odata[1] = 0x08;
-			xpad->odata[2] = 0x00;
-			xpad->odata[3] = strong / 256;  /* left actuator? */
-			xpad->odata[4] = weak / 256;	/* right actuator? */
-			xpad->odata[5] = 0x00;
-			xpad->odata[6] = 0x00;
-			xpad->odata[7] = 0x00;
-			xpad->irq_out->transfer_buffer_length = 8;
-
-			return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
-
-		case XTYPE_XBOX360W:
-			xpad->odata[0] = 0x00;
-			xpad->odata[1] = 0x01;
-			xpad->odata[2] = 0x0F;
-			xpad->odata[3] = 0xC0;
-			xpad->odata[4] = 0x00;
-			xpad->odata[5] = strong / 256;
-			xpad->odata[6] = weak / 256;
-			xpad->odata[7] = 0x00;
-			xpad->odata[8] = 0x00;
-			xpad->odata[9] = 0x00;
-			xpad->odata[10] = 0x00;
-			xpad->odata[11] = 0x00;
-			xpad->irq_out->transfer_buffer_length = 12;
-
-			return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
-
-		default:
-			dev_dbg(&xpad->dev->dev,
-				"%s - rumble command sent to unsupported xpad type: %d\n",
-				__func__, xpad->xtype);
-			return -1;
-		}
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		strong = rumble_force->strong;
+		weak = rumble_force->weak;
+		break;
+	case MLNX_STOP_RUMBLE:
+		strong = 0;
+		weak = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+
+	switch (xpad->xtype) {
+
+	case XTYPE_XBOX:
+		xpad->odata[0] = 0x00;
+		xpad->odata[1] = 0x06;
+		xpad->odata[2] = 0x00;
+		xpad->odata[3] = strong / 256;	/* left actuator */
+		xpad->odata[4] = 0x00;
+		xpad->odata[5] = weak / 256;	/* right actuator */
+		xpad->irq_out->transfer_buffer_length = 6;
+
+		return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+
+	case XTYPE_XBOX360:
+		xpad->odata[0] = 0x00;
+		xpad->odata[1] = 0x08;
+		xpad->odata[2] = 0x00;
+		xpad->odata[3] = strong / 256;  /* left actuator? */
+		xpad->odata[4] = weak / 256;	/* right actuator? */
+		xpad->odata[5] = 0x00;
+		xpad->odata[6] = 0x00;
+		xpad->odata[7] = 0x00;
+		xpad->irq_out->transfer_buffer_length = 8;
+
+		return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+
+	case XTYPE_XBOX360W:
+		xpad->odata[0] = 0x00;
+		xpad->odata[1] = 0x01;
+		xpad->odata[2] = 0x0F;
+		xpad->odata[3] = 0xC0;
+		xpad->odata[4] = 0x00;
+		xpad->odata[5] = strong / 256;
+		xpad->odata[6] = weak / 256;
+		xpad->odata[7] = 0x00;
+		xpad->odata[8] = 0x00;
+		xpad->odata[9] = 0x00;
+		xpad->odata[10] = 0x00;
+		xpad->odata[11] = 0x00;
+		xpad->irq_out->transfer_buffer_length = 12;
+
+		return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+
+	default:
+		dev_dbg(&xpad->dev->dev,
+			"%s - rumble command sent to unsupported xpad type: %d\n",
+			__func__, xpad->xtype);
+		return -1;
 	}
 
 	return 0;
@@ -697,7 +712,7 @@ static int xpad_init_ff(struct usb_xpad *xpad)
 
 	input_set_capability(xpad->dev, EV_FF, FF_RUMBLE);
 
-	return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect);
+	return input_ff_create_mlnx(xpad->dev, NULL, xpad_play_effect, FF_UPDATE_RATE);
 }
 
 #else
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 22/24] hid: Port hid-lg2ff to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (21 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

- Port hid-lg2ff to ff-memless-next
- Clamp vibration magnitude to range <0x02; 0xfd> to prevent irregular
  shaking of the vibration motors.

Signed-off-by: Elias Vanderstuyft <elias.vds@gmail.com>
---
 drivers/hid/Kconfig     |  2 +-
 drivers/hid/hid-lg2ff.c | 65 ++++++++++++++++++++++++++++++++++---------------
 2 files changed, 47 insertions(+), 20 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 97d2d8f..5e70519 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -381,7 +381,7 @@ config LOGITECH_FF
 config LOGIRUMBLEPAD2_FF
 	bool "Logitech force feedback support (variant 2)"
 	depends on HID_LOGITECH
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	help
 	  Say Y here if you want to enable force feedback support for:
 	  - Logitech RumblePad
diff --git a/drivers/hid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c
index 0e3fb1a..6ab5327 100644
--- a/drivers/hid/hid-lg2ff.c
+++ b/drivers/hid/hid-lg2ff.c
@@ -22,42 +22,69 @@
 
 
 #include <linux/input.h>
+#include <linux/input/ff-memless-next.h>
 #include <linux/slab.h>
 #include <linux/hid.h>
 
 #include "hid-lg.h"
 
+#define FF_UPDATE_RATE 8
+
 struct lg2ff_device {
 	struct hid_report *report;
 };
 
-static int play_effect(struct input_dev *dev, void *data,
-			 struct ff_effect *effect)
+static int hid_lg2ff_start_rumble(struct hid_device *hid, struct hid_report *report,
+				const struct mlnx_rumble_force *rumble)
 {
-	struct hid_device *hid = input_get_drvdata(dev);
-	struct lg2ff_device *lg2ff = data;
 	int weak, strong;
 
-	strong = effect->u.rumble.strong_magnitude;
-	weak = effect->u.rumble.weak_magnitude;
+#define CLAMP_QUIRK(x) do { if (x < 2) x = 2; else if (x > 0xfd) x = 0xfd; } \
+			while (0)
 
-	if (weak || strong) {
-		weak = weak * 0xff / 0xffff;
-		strong = strong * 0xff / 0xffff;
+	/* Scale down from MLNX range */
+	strong = rumble->strong * 0xff / 0xffff;
+	weak = rumble->weak * 0xff / 0xffff;
+	CLAMP_QUIRK(weak);
+	CLAMP_QUIRK(strong);
 
-		lg2ff->report->field[0]->value[0] = 0x51;
-		lg2ff->report->field[0]->value[2] = weak;
-		lg2ff->report->field[0]->value[4] = strong;
-	} else {
-		lg2ff->report->field[0]->value[0] = 0xf3;
-		lg2ff->report->field[0]->value[2] = 0x00;
-		lg2ff->report->field[0]->value[4] = 0x00;
-	}
+	report->field[0]->value[0] = 0x51;
+	report->field[0]->value[2] = weak;
+	report->field[0]->value[4] = strong;
 
-	hid_hw_request(hid, lg2ff->report, HID_REQ_SET_REPORT);
+	hid_hw_request(hid, report, HID_REQ_SET_REPORT);
 	return 0;
 }
 
+static int hid_lg2ff_stop_rumble(struct hid_device *hid, struct hid_report *report)
+{
+	report->field[0]->value[0] = 0xf3;
+	report->field[0]->value[2] = 0x00;
+	report->field[0]->value[4] = 0x00;
+
+	hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+	return 0;
+}
+
+static int hid_lg2ff_control(struct input_dev *dev, void *data,
+				const struct mlnx_effect_command *command)
+{
+	struct hid_device *hid = input_get_drvdata(dev);
+	struct lg2ff_device *lg2ff = data;
+
+	switch (command->cmd) {
+	case MLNX_START_RUMBLE:
+		return hid_lg2ff_start_rumble(hid, lg2ff->report, &command->u.rumble_force);
+		break;
+	case MLNX_STOP_RUMBLE:
+		return hid_lg2ff_stop_rumble(hid, lg2ff->report);
+		break;
+	default:
+		dbg_hid("Unsupported effect command");
+		return -EINVAL;
+	}
+}
+
 int lg2ff_init(struct hid_device *hid)
 {
 	struct lg2ff_device *lg2ff;
@@ -78,7 +105,7 @@ int lg2ff_init(struct hid_device *hid)
 
 	set_bit(FF_RUMBLE, dev->ffbit);
 
-	error = input_ff_create_memless(dev, lg2ff, play_effect);
+	error = input_ff_create_mlnx(dev, lg2ff, hid_lg2ff_control, FF_UPDATE_RATE);
 	if (error) {
 		kfree(lg2ff);
 		return error;
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 23/24] hid: Port hid-lg4ff to ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (22 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  2014-04-29 16:05     ` simon
  -1 siblings, 1 reply; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Port hid-lg4ff to ff-memless-next

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
Tested-by: Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>
---
 drivers/hid/Kconfig     |  2 +-
 drivers/hid/hid-lg4ff.c | 93 ++++++++++++++++++++++++++++++-------------------
 2 files changed, 59 insertions(+), 36 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 5e70519..546ac4a 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -399,7 +399,7 @@ config LOGIG940_FF
 config LOGIWHEELS_FF
 	bool "Logitech wheels configuration and force feedback support"
 	depends on HID_LOGITECH
-	select INPUT_FF_MEMLESS
+	select INPUT_FF_MEMLESS_NEXT
 	default LOGITECH_FF
 	help
 	  Say Y here if you want to enable force feedback and range setting
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index 24883b4..d629093 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -25,6 +25,7 @@
 
 
 #include <linux/input.h>
+#include <linux/input/ff-memless-next.h>
 #include <linux/usb.h>
 #include <linux/hid.h>
 
@@ -45,6 +46,8 @@
 #define G27_REV_MIN 0x38
 #define G27_2_REV_MIN 0x39
 
+#define FF_UPDATE_RATE 8
+
 #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
 
 static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
@@ -69,6 +72,13 @@ struct lg4ff_device_entry {
 
 static const signed short lg4ff_wheel_effects[] = {
 	FF_CONSTANT,
+	FF_RAMP,
+	FF_PERIODIC,
+	FF_SQUARE,
+	FF_TRIANGLE,
+	FF_SINE,
+	FF_SAW_UP,
+	FF_SAW_DOWN,
 	FF_AUTOCENTER,
 	-1
 };
@@ -184,47 +194,60 @@ int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
 	}
 }
 
-static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+static int hid_lg4ff_start_combined(struct hid_device *hid, struct hid_report *report,
+				    const struct mlnx_simple_force *force)
+{
+	__s32 *value = report->field[0]->value;
+	int scaled_x;
+
+	/* Scale down from MLNX range */
+	scaled_x = 0x80 - (force->x * 0xff / 0xffff);
+
+	value[0] = 0x11;	/* Slot 1 */
+	value[1] = 0x08;
+	value[2] = scaled_x;
+	value[3] = 0x80;
+	value[4] = 0x00;
+	value[5] = 0x00;
+	value[6] = 0x00;
+
+	hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+	return 0;
+}
+
+static int hid_lg4ff_stop_combined(struct hid_device *hid, struct hid_report *report)
+{
+	__s32 *value = report->field[0]->value;
+
+	value[0] = 0x13;	/* Slot 1 */
+	value[1] = 0x00;
+	value[2] = 0x00;
+	value[3] = 0x00;
+	value[4] = 0x00;
+	value[5] = 0x00;
+	value[6] = 0x00;
+
+	hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+	return 0;
+}
+
+static int hid_lg4ff_control(struct input_dev *dev, void *data, const struct mlnx_effect_command *command)
 {
 	struct hid_device *hid = input_get_drvdata(dev);
 	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 	struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-	__s32 *value = report->field[0]->value;
-	int x;
-
-#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
-
-	switch (effect->type) {
-	case FF_CONSTANT:
-		x = effect->u.ramp.start_level + 0x80;	/* 0x80 is no force */
-		CLAMP(x);
-
-		if (x == 0x80) {
-			/* De-activate force in slot-1*/
-			value[0] = 0x13;
-			value[1] = 0x00;
-			value[2] = 0x00;
-			value[3] = 0x00;
-			value[4] = 0x00;
-			value[5] = 0x00;
-			value[6] = 0x00;
-
-			hid_hw_request(hid, report, HID_REQ_SET_REPORT);
-			return 0;
-		}
-
-		value[0] = 0x11;	/* Slot 1 */
-		value[1] = 0x08;
-		value[2] = x;
-		value[3] = 0x80;
-		value[4] = 0x00;
-		value[5] = 0x00;
-		value[6] = 0x00;
 
-		hid_hw_request(hid, report, HID_REQ_SET_REPORT);
+	switch (command->cmd) {
+	case MLNX_START_COMBINED:
+		return hid_lg4ff_start_combined(hid, report, &command->u.simple_force);
+		break;
+	case MLNX_STOP_COMBINED:
+		return hid_lg4ff_stop_combined(hid, report);
 		break;
+	default:
+		dbg_hid("Unsupported effect command");
+		return -EINVAL;
 	}
-	return 0;
 }
 
 /* Sends default autocentering command compatible with
@@ -610,7 +633,7 @@ int lg4ff_init(struct hid_device *hid)
 	for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
 		set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);
 
-	error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
+	error = input_ff_create_mlnx(dev, (void *)NULL, hid_lg4ff_control, FF_UPDATE_RATE);
 
 	if (error)
 		return error;
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* [PATCH v4 24/24] input: Replace ff-memless with ff-memless-next
  2014-04-26 15:01 ` Michal Malý
                   ` (23 preceding siblings ...)
  (?)
@ 2014-04-26 15:02 ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-04-26 15:02 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: dmitry.torokhov, jkosina, elias.vds, anssi.hannula, simon,
	Michal Malý

Replace ff-memless with ff-memless-next

ff-memless-next is a module for handling force feedback devices that
have no FFB effect memory of their own. It is based on the ff-memless
module but it has been largerly extended. Notable changes include:
- Support for all force feedback effects currently defined by Microsoft
  DirectInput.
- Improved emulation of periodic and rumble effects in case either of
  those is not supported by a device.
- New kernel API to interface with HW-specific drivers.

Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
---
 drivers/input/Kconfig      |  13 +-
 drivers/input/Makefile     |   1 -
 drivers/input/ff-memless.c | 547 ---------------------------------------------
 include/linux/input.h      |   3 -
 4 files changed, 1 insertion(+), 563 deletions(-)
 delete mode 100644 drivers/input/ff-memless.c

diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 3780962..061ae3c 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -25,7 +25,7 @@ config INPUT
 
 if INPUT
 
-config INPUT_FF_MEMLESS
+config INPUT_FF_MEMLESS_NEXT
 	tristate "Support for memoryless force-feedback devices"
 	help
 	  Say Y here if you have memoryless force-feedback input device
@@ -36,17 +36,6 @@ config INPUT_FF_MEMLESS
 	  If unsure, say N.
 
 	  To compile this driver as a module, choose M here: the
-	  module will be called ff-memless.
-
-config INPUT_FF_MEMLESS_NEXT
-	tristate "New version of support for memoryless force-feedback devices"
-	help
-	  Say Y here to enable a new version of support for memoryless force
-	  feedback devices.
-
-	  If unsure, say N.
-
-	  To compile this driver as a module, choose M here: the
 	  module will be called ff-memless-next.
 
 config INPUT_POLLDEV
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index b4f11f5..f7ae055 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -7,7 +7,6 @@
 obj-$(CONFIG_INPUT)		+= input-core.o
 input-core-y := input.o input-compat.o input-mt.o ff-core.o
 
-obj-$(CONFIG_INPUT_FF_MEMLESS)	+= ff-memless.o
 obj-$(CONFIG_INPUT_FF_MEMLESS_NEXT)	+= ff-memless-next.o
 obj-$(CONFIG_INPUT_POLLDEV)	+= input-polldev.o
 obj-$(CONFIG_INPUT_SPARSEKMAP)	+= sparse-keymap.o
diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
deleted file mode 100644
index 74c0d8c..0000000
--- a/drivers/input/ff-memless.c
+++ /dev/null
@@ -1,547 +0,0 @@
-/*
- *  Force feedback support for memoryless devices
- *
- *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
- *  Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/* #define DEBUG */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/slab.h>
-#include <linux/input.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/spinlock.h>
-#include <linux/jiffies.h>
-#include <linux/fixp-arith.h>
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Anssi Hannula <anssi.hannula@gmail.com>");
-MODULE_DESCRIPTION("Force feedback support for memoryless devices");
-
-/* Number of effects handled with memoryless devices */
-#define FF_MEMLESS_EFFECTS	16
-
-/* Envelope update interval in ms */
-#define FF_ENVELOPE_INTERVAL	50
-
-#define FF_EFFECT_STARTED	0
-#define FF_EFFECT_PLAYING	1
-#define FF_EFFECT_ABORTING	2
-
-struct ml_effect_state {
-	struct ff_effect *effect;
-	unsigned long flags;	/* effect state (STARTED, PLAYING, etc) */
-	int count;		/* loop count of the effect */
-	unsigned long play_at;	/* start time */
-	unsigned long stop_at;	/* stop time */
-	unsigned long adj_at;	/* last time the effect was sent */
-};
-
-struct ml_device {
-	void *private;
-	struct ml_effect_state states[FF_MEMLESS_EFFECTS];
-	int gain;
-	struct timer_list timer;
-	struct input_dev *dev;
-
-	int (*play_effect)(struct input_dev *dev, void *data,
-			   struct ff_effect *effect);
-};
-
-static const struct ff_envelope *get_envelope(const struct ff_effect *effect)
-{
-	static const struct ff_envelope empty_envelope;
-
-	switch (effect->type) {
-	case FF_PERIODIC:
-		return &effect->u.periodic.envelope;
-
-	case FF_CONSTANT:
-		return &effect->u.constant.envelope;
-
-	default:
-		return &empty_envelope;
-	}
-}
-
-/*
- * Check for the next time envelope requires an update on memoryless devices
- */
-static unsigned long calculate_next_time(struct ml_effect_state *state)
-{
-	const struct ff_envelope *envelope = get_envelope(state->effect);
-	unsigned long attack_stop, fade_start, next_fade;
-
-	if (envelope->attack_length) {
-		attack_stop = state->play_at +
-			msecs_to_jiffies(envelope->attack_length);
-		if (time_before(state->adj_at, attack_stop))
-			return state->adj_at +
-					msecs_to_jiffies(FF_ENVELOPE_INTERVAL);
-	}
-
-	if (state->effect->replay.length) {
-		if (envelope->fade_length) {
-			/* check when fading should start */
-			fade_start = state->stop_at -
-					msecs_to_jiffies(envelope->fade_length);
-
-			if (time_before(state->adj_at, fade_start))
-				return fade_start;
-
-			/* already fading, advance to next checkpoint */
-			next_fade = state->adj_at +
-					msecs_to_jiffies(FF_ENVELOPE_INTERVAL);
-			if (time_before(next_fade, state->stop_at))
-				return next_fade;
-		}
-
-		return state->stop_at;
-	}
-
-	return state->play_at;
-}
-
-static void ml_schedule_timer(struct ml_device *ml)
-{
-	struct ml_effect_state *state;
-	unsigned long now = jiffies;
-	unsigned long earliest = 0;
-	unsigned long next_at;
-	int events = 0;
-	int i;
-
-	pr_debug("calculating next timer\n");
-
-	for (i = 0; i < FF_MEMLESS_EFFECTS; i++) {
-
-		state = &ml->states[i];
-
-		if (!test_bit(FF_EFFECT_STARTED, &state->flags))
-			continue;
-
-		if (test_bit(FF_EFFECT_PLAYING, &state->flags))
-			next_at = calculate_next_time(state);
-		else
-			next_at = state->play_at;
-
-		if (time_before_eq(now, next_at) &&
-		    (++events == 1 || time_before(next_at, earliest)))
-			earliest = next_at;
-	}
-
-	if (!events) {
-		pr_debug("no actions\n");
-		del_timer(&ml->timer);
-	} else {
-		pr_debug("timer set\n");
-		mod_timer(&ml->timer, earliest);
-	}
-}
-
-/*
- * Apply an envelope to a value
- */
-static int apply_envelope(struct ml_effect_state *state, int value,
-			  struct ff_envelope *envelope)
-{
-	struct ff_effect *effect = state->effect;
-	unsigned long now = jiffies;
-	int time_from_level;
-	int time_of_envelope;
-	int envelope_level;
-	int difference;
-
-	if (envelope->attack_length &&
-	    time_before(now,
-			state->play_at + msecs_to_jiffies(envelope->attack_length))) {
-		pr_debug("value = 0x%x, attack_level = 0x%x\n",
-			 value, envelope->attack_level);
-		time_from_level = jiffies_to_msecs(now - state->play_at);
-		time_of_envelope = envelope->attack_length;
-		envelope_level = min_t(u16, envelope->attack_level, 0x7fff);
-
-	} else if (envelope->fade_length && effect->replay.length &&
-		   time_after(now,
-			      state->stop_at - msecs_to_jiffies(envelope->fade_length)) &&
-		   time_before(now, state->stop_at)) {
-		time_from_level = jiffies_to_msecs(state->stop_at - now);
-		time_of_envelope = envelope->fade_length;
-		envelope_level = min_t(u16, envelope->fade_level, 0x7fff);
-	} else
-		return value;
-
-	difference = abs(value) - envelope_level;
-
-	pr_debug("difference = %d\n", difference);
-	pr_debug("time_from_level = 0x%x\n", time_from_level);
-	pr_debug("time_of_envelope = 0x%x\n", time_of_envelope);
-
-	difference = difference * time_from_level / time_of_envelope;
-
-	pr_debug("difference = %d\n", difference);
-
-	return value < 0 ?
-		-(difference + envelope_level) : (difference + envelope_level);
-}
-
-/*
- * Return the type the effect has to be converted into (memless devices)
- */
-static int get_compatible_type(struct ff_device *ff, int effect_type)
-{
-
-	if (test_bit(effect_type, ff->ffbit))
-		return effect_type;
-
-	if (effect_type == FF_PERIODIC && test_bit(FF_RUMBLE, ff->ffbit))
-		return FF_RUMBLE;
-
-	pr_err("invalid type in get_compatible_type()\n");
-
-	return 0;
-}
-
-/*
- * Only left/right direction should be used (under/over 0x8000) for
- * forward/reverse motor direction (to keep calculation fast & simple).
- */
-static u16 ml_calculate_direction(u16 direction, u16 force,
-				  u16 new_direction, u16 new_force)
-{
-	if (!force)
-		return new_direction;
-	if (!new_force)
-		return direction;
-	return (((u32)(direction >> 1) * force +
-		 (new_direction >> 1) * new_force) /
-		(force + new_force)) << 1;
-}
-
-/*
- * Combine two effects and apply gain.
- */
-static void ml_combine_effects(struct ff_effect *effect,
-			       struct ml_effect_state *state,
-			       int gain)
-{
-	struct ff_effect *new = state->effect;
-	unsigned int strong, weak, i;
-	int x, y;
-	fixp_t level;
-
-	switch (new->type) {
-	case FF_CONSTANT:
-		i = new->direction * 360 / 0xffff;
-		level = fixp_new16(apply_envelope(state,
-					new->u.constant.level,
-					&new->u.constant.envelope));
-		x = fixp_mult(fixp_sin(i), level) * gain / 0xffff;
-		y = fixp_mult(-fixp_cos(i), level) * gain / 0xffff;
-		/*
-		 * here we abuse ff_ramp to hold x and y of constant force
-		 * If in future any driver wants something else than x and y
-		 * in s8, this should be changed to something more generic
-		 */
-		effect->u.ramp.start_level =
-			clamp_val(effect->u.ramp.start_level + x, -0x80, 0x7f);
-		effect->u.ramp.end_level =
-			clamp_val(effect->u.ramp.end_level + y, -0x80, 0x7f);
-		break;
-
-	case FF_RUMBLE:
-		strong = (u32)new->u.rumble.strong_magnitude * gain / 0xffff;
-		weak = (u32)new->u.rumble.weak_magnitude * gain / 0xffff;
-
-		if (effect->u.rumble.strong_magnitude + strong)
-			effect->direction = ml_calculate_direction(
-				effect->direction,
-				effect->u.rumble.strong_magnitude,
-				new->direction, strong);
-		else if (effect->u.rumble.weak_magnitude + weak)
-			effect->direction = ml_calculate_direction(
-				effect->direction,
-				effect->u.rumble.weak_magnitude,
-				new->direction, weak);
-		else
-			effect->direction = 0;
-		effect->u.rumble.strong_magnitude =
-			min(strong + effect->u.rumble.strong_magnitude,
-			    0xffffU);
-		effect->u.rumble.weak_magnitude =
-			min(weak + effect->u.rumble.weak_magnitude, 0xffffU);
-		break;
-
-	case FF_PERIODIC:
-		i = apply_envelope(state, abs(new->u.periodic.magnitude),
-				   &new->u.periodic.envelope);
-
-		/* here we also scale it 0x7fff => 0xffff */
-		i = i * gain / 0x7fff;
-
-		if (effect->u.rumble.strong_magnitude + i)
-			effect->direction = ml_calculate_direction(
-				effect->direction,
-				effect->u.rumble.strong_magnitude,
-				new->direction, i);
-		else
-			effect->direction = 0;
-		effect->u.rumble.strong_magnitude =
-			min(i + effect->u.rumble.strong_magnitude, 0xffffU);
-		effect->u.rumble.weak_magnitude =
-			min(i + effect->u.rumble.weak_magnitude, 0xffffU);
-		break;
-
-	default:
-		pr_err("invalid type in ml_combine_effects()\n");
-		break;
-	}
-
-}
-
-
-/*
- * Because memoryless devices have only one effect per effect type active
- * at one time we have to combine multiple effects into one
- */
-static int ml_get_combo_effect(struct ml_device *ml,
-			       unsigned long *effect_handled,
-			       struct ff_effect *combo_effect)
-{
-	struct ff_effect *effect;
-	struct ml_effect_state *state;
-	int effect_type;
-	int i;
-
-	memset(combo_effect, 0, sizeof(struct ff_effect));
-
-	for (i = 0; i < FF_MEMLESS_EFFECTS; i++) {
-		if (__test_and_set_bit(i, effect_handled))
-			continue;
-
-		state = &ml->states[i];
-		effect = state->effect;
-
-		if (!test_bit(FF_EFFECT_STARTED, &state->flags))
-			continue;
-
-		if (time_before(jiffies, state->play_at))
-			continue;
-
-		/*
-		 * here we have started effects that are either
-		 * currently playing (and may need be aborted)
-		 * or need to start playing.
-		 */
-		effect_type = get_compatible_type(ml->dev->ff, effect->type);
-		if (combo_effect->type != effect_type) {
-			if (combo_effect->type != 0) {
-				__clear_bit(i, effect_handled);
-				continue;
-			}
-			combo_effect->type = effect_type;
-		}
-
-		if (__test_and_clear_bit(FF_EFFECT_ABORTING, &state->flags)) {
-			__clear_bit(FF_EFFECT_PLAYING, &state->flags);
-			__clear_bit(FF_EFFECT_STARTED, &state->flags);
-		} else if (effect->replay.length &&
-			   time_after_eq(jiffies, state->stop_at)) {
-
-			__clear_bit(FF_EFFECT_PLAYING, &state->flags);
-
-			if (--state->count <= 0) {
-				__clear_bit(FF_EFFECT_STARTED, &state->flags);
-			} else {
-				state->play_at = jiffies +
-					msecs_to_jiffies(effect->replay.delay);
-				state->stop_at = state->play_at +
-					msecs_to_jiffies(effect->replay.length);
-			}
-		} else {
-			__set_bit(FF_EFFECT_PLAYING, &state->flags);
-			state->adj_at = jiffies;
-			ml_combine_effects(combo_effect, state, ml->gain);
-		}
-	}
-
-	return combo_effect->type != 0;
-}
-
-static void ml_play_effects(struct ml_device *ml)
-{
-	struct ff_effect effect;
-	DECLARE_BITMAP(handled_bm, FF_MEMLESS_EFFECTS);
-
-	memset(handled_bm, 0, sizeof(handled_bm));
-
-	while (ml_get_combo_effect(ml, handled_bm, &effect))
-		ml->play_effect(ml->dev, ml->private, &effect);
-
-	ml_schedule_timer(ml);
-}
-
-static void ml_effect_timer(unsigned long timer_data)
-{
-	struct input_dev *dev = (struct input_dev *)timer_data;
-	struct ml_device *ml = dev->ff->private;
-	unsigned long flags;
-
-	pr_debug("timer: updating effects\n");
-
-	spin_lock_irqsave(&dev->event_lock, flags);
-	ml_play_effects(ml);
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-}
-
-/*
- * Sets requested gain for FF effects. Called with dev->event_lock held.
- */
-static void ml_ff_set_gain(struct input_dev *dev, u16 gain)
-{
-	struct ml_device *ml = dev->ff->private;
-	int i;
-
-	ml->gain = gain;
-
-	for (i = 0; i < FF_MEMLESS_EFFECTS; i++)
-		__clear_bit(FF_EFFECT_PLAYING, &ml->states[i].flags);
-
-	ml_play_effects(ml);
-}
-
-/*
- * Start/stop specified FF effect. Called with dev->event_lock held.
- */
-static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
-{
-	struct ml_device *ml = dev->ff->private;
-	struct ml_effect_state *state = &ml->states[effect_id];
-
-	if (value > 0) {
-		pr_debug("initiated play\n");
-
-		__set_bit(FF_EFFECT_STARTED, &state->flags);
-		state->count = value;
-		state->play_at = jiffies +
-				 msecs_to_jiffies(state->effect->replay.delay);
-		state->stop_at = state->play_at +
-				 msecs_to_jiffies(state->effect->replay.length);
-		state->adj_at = state->play_at;
-
-	} else {
-		pr_debug("initiated stop\n");
-
-		if (test_bit(FF_EFFECT_PLAYING, &state->flags))
-			__set_bit(FF_EFFECT_ABORTING, &state->flags);
-		else
-			__clear_bit(FF_EFFECT_STARTED, &state->flags);
-	}
-
-	ml_play_effects(ml);
-
-	return 0;
-}
-
-static int ml_ff_upload(struct input_dev *dev,
-			struct ff_effect *effect, struct ff_effect *old)
-{
-	struct ml_device *ml = dev->ff->private;
-	struct ml_effect_state *state = &ml->states[effect->id];
-
-	spin_lock_irq(&dev->event_lock);
-
-	if (test_bit(FF_EFFECT_STARTED, &state->flags)) {
-		__clear_bit(FF_EFFECT_PLAYING, &state->flags);
-		state->play_at = jiffies +
-				 msecs_to_jiffies(state->effect->replay.delay);
-		state->stop_at = state->play_at +
-				 msecs_to_jiffies(state->effect->replay.length);
-		state->adj_at = state->play_at;
-		ml_schedule_timer(ml);
-	}
-
-	spin_unlock_irq(&dev->event_lock);
-
-	return 0;
-}
-
-static void ml_ff_destroy(struct ff_device *ff)
-{
-	struct ml_device *ml = ff->private;
-
-	kfree(ml->private);
-}
-
-/**
- * input_ff_create_memless() - create memoryless force-feedback device
- * @dev: input device supporting force-feedback
- * @data: driver-specific data to be passed into @play_effect
- * @play_effect: driver-specific method for playing FF effect
- */
-int input_ff_create_memless(struct input_dev *dev, void *data,
-		int (*play_effect)(struct input_dev *, void *, struct ff_effect *))
-{
-	struct ml_device *ml;
-	struct ff_device *ff;
-	int error;
-	int i;
-
-	ml = kzalloc(sizeof(struct ml_device), GFP_KERNEL);
-	if (!ml)
-		return -ENOMEM;
-
-	ml->dev = dev;
-	ml->private = data;
-	ml->play_effect = play_effect;
-	ml->gain = 0xffff;
-	setup_timer(&ml->timer, ml_effect_timer, (unsigned long)dev);
-
-	set_bit(FF_GAIN, dev->ffbit);
-
-	error = input_ff_create(dev, FF_MEMLESS_EFFECTS);
-	if (error) {
-		kfree(ml);
-		return error;
-	}
-
-	ff = dev->ff;
-	ff->private = ml;
-	ff->upload = ml_ff_upload;
-	ff->playback = ml_ff_playback;
-	ff->set_gain = ml_ff_set_gain;
-	ff->destroy = ml_ff_destroy;
-
-	/* we can emulate periodic effects with RUMBLE */
-	if (test_bit(FF_RUMBLE, ff->ffbit)) {
-		set_bit(FF_PERIODIC, dev->ffbit);
-		set_bit(FF_SINE, dev->ffbit);
-		set_bit(FF_TRIANGLE, dev->ffbit);
-		set_bit(FF_SQUARE, dev->ffbit);
-	}
-
-	for (i = 0; i < FF_MEMLESS_EFFECTS; i++)
-		ml->states[i].effect = &ff->effects[i];
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(input_ff_create_memless);
diff --git a/include/linux/input.h b/include/linux/input.h
index 82ce323..0c6c3f1 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -528,7 +528,4 @@ int input_ff_event(struct input_dev *dev, unsigned int type, unsigned int code,
 int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, struct file *file);
 int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file);
 
-int input_ff_create_memless(struct input_dev *dev, void *data,
-		int (*play_effect)(struct input_dev *, void *, struct ff_effect *));
-
 #endif
-- 
1.9.2


^ permalink raw reply related	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 00/24] input: Introduce ff-memless-next as an improved replacement for ff-memless
  2014-04-26 15:01 ` Michal Malý
@ 2014-04-29 15:59   ` simon
  -1 siblings, 0 replies; 83+ messages in thread
From: simon @ 2014-04-29 15:59 UTC (permalink / raw)
  To: "Michal Malý"
  Cc: linux-input, linux-kernel, dmitry.torokhov, jkosina, elias.vds,
	anssi.hannula, simon, "Michal Malý"

> This patch series:
> 1) Adds "ff-memless-next" module [1]
> 2) Ports all hardware-specific drivers to MLNX's API [2-23]
> 3) Removes FFML and replaces it with MLNX [24]
>
> Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
>
> v4:
>  - Add a summary of changes between MLNX and FFML to the last patch
>  - Remove a stale empty line in hid-sony.c
>  - Add "Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>" to hid-lg4ff
> patch.
>
> v3:
>  - Rebase against latest linux-next. Fixes conflict in hid-sony.c and
>    max8997_haptic.c
>  - Updated documentation in ff-memless-next.h. The documentation now
> describes
>    parameters of the callback function and specifically mentions that
>    HW-specific drivers must not keep a reference to mlnx_effect_command
> struct
>    to which a pointer is passed in the callback function.
>  - Fix a minor brace inconsistency in hid-lgff
>  I believe that all concerns regarding v2 have been resolved as false
> alarms.
>
> v2:
>  - Add missing msecs to jiffies conversion in ff-memless-next
>  - lgff: Properly convert force on Y axis from MLNX to device range
>          Support periodic effects for "joystick_ac" device class
>  - lg3ff: Properly convert forces from MLNX to device range
>  - Very minor coding style issues fixed

Hi all,
I'd confirm that I build v2 and tested on a number of devices (1), and it
appears to work OK.

The only slight hiccup was with an older version (Xubuntu 12.10)
'ffcfstress' application which did not correctly detect the CF
capabilities of my gaming wheel(s). This is believed to be a fault with
the application not using correct bit-field testing and appears to have
been fixed on later versions (Xubuntu 13.10).

I also built v4, but have not yet had time/access to all the devices
(other than DS4) to test.

Cheers,
Simon

Tested-by: Simon Wood <simon@mungewell.org>

(1) Devices:
Logitech Momo Red, Momo Black, DFGT, WiiWheel, G27
Sony DS3-SA, DS4, Intec 3rd Party PS3 controller
Nintendo Wii Remote



^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 00/24] input: Introduce ff-memless-next as an improved replacement for ff-memless
@ 2014-04-29 15:59   ` simon
  0 siblings, 0 replies; 83+ messages in thread
From: simon @ 2014-04-29 15:59 UTC (permalink / raw)
  Cc: linux-input, linux-kernel, dmitry.torokhov, jkosina, elias.vds,
	anssi.hannula, simon, "Michal Malý"

> This patch series:
> 1) Adds "ff-memless-next" module [1]
> 2) Ports all hardware-specific drivers to MLNX's API [2-23]
> 3) Removes FFML and replaces it with MLNX [24]
>
> Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
>
> v4:
>  - Add a summary of changes between MLNX and FFML to the last patch
>  - Remove a stale empty line in hid-sony.c
>  - Add "Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>" to hid-lg4ff
> patch.
>
> v3:
>  - Rebase against latest linux-next. Fixes conflict in hid-sony.c and
>    max8997_haptic.c
>  - Updated documentation in ff-memless-next.h. The documentation now
> describes
>    parameters of the callback function and specifically mentions that
>    HW-specific drivers must not keep a reference to mlnx_effect_command
> struct
>    to which a pointer is passed in the callback function.
>  - Fix a minor brace inconsistency in hid-lgff
>  I believe that all concerns regarding v2 have been resolved as false
> alarms.
>
> v2:
>  - Add missing msecs to jiffies conversion in ff-memless-next
>  - lgff: Properly convert force on Y axis from MLNX to device range
>          Support periodic effects for "joystick_ac" device class
>  - lg3ff: Properly convert forces from MLNX to device range
>  - Very minor coding style issues fixed

Hi all,
I'd confirm that I build v2 and tested on a number of devices (1), and it
appears to work OK.

The only slight hiccup was with an older version (Xubuntu 12.10)
'ffcfstress' application which did not correctly detect the CF
capabilities of my gaming wheel(s). This is believed to be a fault with
the application not using correct bit-field testing and appears to have
been fixed on later versions (Xubuntu 13.10).

I also built v4, but have not yet had time/access to all the devices
(other than DS4) to test.

Cheers,
Simon

Tested-by: Simon Wood <simon@mungewell.org>

(1) Devices:
Logitech Momo Red, Momo Black, DFGT, WiiWheel, G27
Sony DS3-SA, DS4, Intec 3rd Party PS3 controller
Nintendo Wii Remote


--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 23/24] hid: Port hid-lg4ff to ff-memless-next
  2014-04-26 15:02 ` [PATCH v4 23/24] hid: Port hid-lg4ff " Michal Malý
@ 2014-04-29 16:05     ` simon
  0 siblings, 0 replies; 83+ messages in thread
From: simon @ 2014-04-29 16:05 UTC (permalink / raw)
  To: "Michal Malý"
  Cc: linux-input, linux-kernel, dmitry.torokhov, jkosina, elias.vds,
	anssi.hannula, simon, "Michal Malý"

> Port hid-lg4ff to ff-memless-next
>
> Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
> Tested-by: Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>

Signed-off-by: Simon Wood <simon@mungewell.org>



^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 23/24] hid: Port hid-lg4ff to ff-memless-next
@ 2014-04-29 16:05     ` simon
  0 siblings, 0 replies; 83+ messages in thread
From: simon @ 2014-04-29 16:05 UTC (permalink / raw)
  Cc: linux-input, linux-kernel, dmitry.torokhov, jkosina, elias.vds,
	anssi.hannula, simon, "Michal Malý"

> Port hid-lg4ff to ff-memless-next
>
> Signed-off-by: Michal Malý <madcatxster@devoid-pointer.net>
> Tested-by: Tested-by: Elias Vanderstuyft <elias.vds@gmail.com>

Signed-off-by: Simon Wood <simon@mungewell.org>

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 00/24] input: Introduce ff-memless-next as an improved replacement for ff-memless
  2014-04-26 15:01 ` Michal Malý
@ 2014-05-12  9:14   ` Jiri Kosina
  -1 siblings, 0 replies; 83+ messages in thread
From: Jiri Kosina @ 2014-05-12  9:14 UTC (permalink / raw)
  To: Michal Malý
  Cc: linux-input, linux-kernel, dmitry.torokhov, elias.vds,
	anssi.hannula, simon

On Sat, 26 Apr 2014, Michal Malý wrote:

> ff-memless-next (MLNX) is a largely improved version of the current ff-memless
> (FFML) driver. MLNX supports all force feedback effects currently available in
> the Linux force feedback userspace API. All effects are handled in accordance
> with Microsoft's DirectInput/XInput. Most notable changes include support for
> conditional effects, proper handling of all periodic waveforms and improved
> emulation of rumble effects through periodic effects. MLNX also uses its own
> kernel API to pass processed effects to hardware-specific drivers instead of
> abusing "ff_effect" struct. The API is documented in the respective header
> file.
> 
> MLNX has been expanded to be a direct replacement for FFML.
> 
> Support for FF_PERIODIC and FF_RAMP has been added to all devices that
> support FF_CONSTANT as a part of the port to the new API.
> 
> This patch series:
> 1) Adds "ff-memless-next" module [1]
> 2) Ports all hardware-specific drivers to MLNX's API [2-23]
> 3) Removes FFML and replaces it with MLNX [24]

Dmitry,

what are your plans with this, please?

I personally don't really completely like having two implementations of FF 
in the kernel; can't it be really done as an extension to ff-memless?

Thanks,

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 00/24] input: Introduce ff-memless-next as an improved replacement for ff-memless
@ 2014-05-12  9:14   ` Jiri Kosina
  0 siblings, 0 replies; 83+ messages in thread
From: Jiri Kosina @ 2014-05-12  9:14 UTC (permalink / raw)
  To: Michal Malý
  Cc: linux-input, linux-kernel, dmitry.torokhov, elias.vds,
	anssi.hannula, simon

On Sat, 26 Apr 2014, Michal Malý wrote:

> ff-memless-next (MLNX) is a largely improved version of the current ff-memless
> (FFML) driver. MLNX supports all force feedback effects currently available in
> the Linux force feedback userspace API. All effects are handled in accordance
> with Microsoft's DirectInput/XInput. Most notable changes include support for
> conditional effects, proper handling of all periodic waveforms and improved
> emulation of rumble effects through periodic effects. MLNX also uses its own
> kernel API to pass processed effects to hardware-specific drivers instead of
> abusing "ff_effect" struct. The API is documented in the respective header
> file.
> 
> MLNX has been expanded to be a direct replacement for FFML.
> 
> Support for FF_PERIODIC and FF_RAMP has been added to all devices that
> support FF_CONSTANT as a part of the port to the new API.
> 
> This patch series:
> 1) Adds "ff-memless-next" module [1]
> 2) Ports all hardware-specific drivers to MLNX's API [2-23]
> 3) Removes FFML and replaces it with MLNX [24]

Dmitry,

what are your plans with this, please?

I personally don't really completely like having two implementations of FF 
in the kernel; can't it be really done as an extension to ff-memless?

Thanks,

-- 
Jiri Kosina
SUSE Labs
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 00/24] input: Introduce ff-memless-next as an improved replacement for ff-memless
  2014-05-12  9:14   ` Jiri Kosina
@ 2014-05-12  9:26     ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-12  9:26 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: linux-input, linux-kernel, dmitry.torokhov, elias.vds,
	anssi.hannula, simon

On Monday 12 of May 2014 11:14:42 Jiri Kosina wrote:
> On Sat, 26 Apr 2014, Michal Malý wrote:
> > ff-memless-next (MLNX) is a largely improved version of the current
> > ff-memless (FFML) driver. MLNX supports all force feedback effects
> > currently available in the Linux force feedback userspace API. All
> > effects are handled in accordance with Microsoft's DirectInput/XInput.
> > Most notable changes include support for conditional effects, proper
> > handling of all periodic waveforms and improved emulation of rumble
> > effects through periodic effects. MLNX also uses its own kernel API to
> > pass processed effects to hardware-specific drivers instead of abusing
> > "ff_effect" struct. The API is documented in the respective header file.
> > 
> > MLNX has been expanded to be a direct replacement for FFML.
> > 
> > Support for FF_PERIODIC and FF_RAMP has been added to all devices that
> > support FF_CONSTANT as a part of the port to the new API.
> > 
> > This patch series:
> > 1) Adds "ff-memless-next" module [1]
> > 2) Ports all hardware-specific drivers to MLNX's API [2-23]
> > 3) Removes FFML and replaces it with MLNX [24]
> 
> Dmitry,
> 
> what are your plans with this, please?
> 
> I personally don't really completely like having two implementations of FF
> in the kernel; can't it be really done as an extension to ff-memless?

There will be no duplication. This patchset fully replaces "ff-memless" with 
"ff-memless-next". Even though "ff-memless-next" is basically an extended 
version of "ff-memless", the extent of the changes makes it look very much like 
a rewrite.

If you are concerned about any confusion due to the "ff-memless" -> "ff-memless-
next" name change I can modify the code to make it look like "ff-memless" if 
you think that'd be more appropriate. However, given the scope of the changes 
I considered it a better idea to implement this as a new driver to make it 
clear that ff-memless-next behaves differently from the HW-specific drivers' 
perspective.

Michal

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 00/24] input: Introduce ff-memless-next as an improved replacement for ff-memless
@ 2014-05-12  9:26     ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-12  9:26 UTC (permalink / raw)
  To: Jiri Kosina
  Cc: linux-input, linux-kernel, dmitry.torokhov, elias.vds,
	anssi.hannula, simon

On Monday 12 of May 2014 11:14:42 Jiri Kosina wrote:
> On Sat, 26 Apr 2014, Michal Malý wrote:
> > ff-memless-next (MLNX) is a largely improved version of the current
> > ff-memless (FFML) driver. MLNX supports all force feedback effects
> > currently available in the Linux force feedback userspace API. All
> > effects are handled in accordance with Microsoft's DirectInput/XInput.
> > Most notable changes include support for conditional effects, proper
> > handling of all periodic waveforms and improved emulation of rumble
> > effects through periodic effects. MLNX also uses its own kernel API to
> > pass processed effects to hardware-specific drivers instead of abusing
> > "ff_effect" struct. The API is documented in the respective header file.
> > 
> > MLNX has been expanded to be a direct replacement for FFML.
> > 
> > Support for FF_PERIODIC and FF_RAMP has been added to all devices that
> > support FF_CONSTANT as a part of the port to the new API.
> > 
> > This patch series:
> > 1) Adds "ff-memless-next" module [1]
> > 2) Ports all hardware-specific drivers to MLNX's API [2-23]
> > 3) Removes FFML and replaces it with MLNX [24]
> 
> Dmitry,
> 
> what are your plans with this, please?
> 
> I personally don't really completely like having two implementations of FF
> in the kernel; can't it be really done as an extension to ff-memless?

There will be no duplication. This patchset fully replaces "ff-memless" with 
"ff-memless-next". Even though "ff-memless-next" is basically an extended 
version of "ff-memless", the extent of the changes makes it look very much like 
a rewrite.

If you are concerned about any confusion due to the "ff-memless" -> "ff-memless-
next" name change I can modify the code to make it look like "ff-memless" if 
you think that'd be more appropriate. However, given the scope of the changes 
I considered it a better idea to implement this as a new driver to make it 
clear that ff-memless-next behaves differently from the HW-specific drivers' 
perspective.

Michal
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-04-26 15:02   ` Michal Malý
@ 2014-05-14  6:38     ` Dmitry Torokhov
  -1 siblings, 0 replies; 83+ messages in thread
From: Dmitry Torokhov @ 2014-05-14  6:38 UTC (permalink / raw)
  To: Michal Malý
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

Hi Michal,

On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> +
> +/** DEFINITION OF TERMS
> + *
> + * Combined effect - An effect whose force is a superposition of forces
> + *                   generated by all effects that can be added together.
> + *                   Only one combined effect can be playing at a time.
> + *                   Effects that can be added together to create a combined
> + *                   effect are FF_CONSTANT, FF_PERIODIC and FF_RAMP.
> + * Uncombinable effect - An effect that cannot be combined with another effect.
> + *                       All conditional effects - FF_DAMPER, FF_FRICTION,
> + *                       FF_INERTIA and FF_SPRING are uncombinable.
> + *                       Number of uncombinable effects playing simultaneously
> + *                       depends on the capabilities of the hardware.
> + * Rumble effect - An effect generated by device's rumble motors instead of
> + *                 force feedback actuators.
> + *
> + *
> + * HANDLING OF UNCOMBINABLE EFFECTS
> + *
> + * Uncombinable effects cannot be combined together into just one effect, at
> + * least not in a clear and obvious manner. Therefore these effects have to
> + * be handled individually by ff-memless-next. Handling of these effects is
> + * left entirely to the hardware-specific driver, ff-memless-next merely
> + * passes these effects to the hardware-specific driver at appropriate time.
> + * ff-memless-next provides the UPLOAD command to notify the hardware-specific
> + * driver that the userspace is about to request playback of an uncombinable
> + * effect. The hardware-specific driver shall take all steps needed to make
> + * the device ready to play the effect when it receives the UPLOAD command.
> + * The actual playback shall commence when START_UNCOMB command is received.
> + * Opposite to the UPLOAD command is the ERASE command which tells
> + * the hardware-specific driver that the playback has finished and that
> + * the effect will not be restarted. STOP_UNCOMB command tells
> + * the hardware-specific driver that the playback shall stop but the device
> + * shall still be ready to resume the playback immediately.
> + *
> + * In case it is not possible to make the device ready to play an uncombinable
> + * effect (all hardware effect slots are occupied), the hardware-specific
> + * driver may return an error when it receives an UPLOAD command. If the

This part concerns me. It seems to me that devices supporting
"uncombinable" effects are in fact not memoryless devices and we should
not be introducing this term here. If the goal is to work around limited
number of effect slots in the devices by combining certain effects then
it needs to be done at ff-core level as it will be potentially useful
for all devices.

> + * hardware-specific driver returns 0, the upload is considered successful.
> + * START_UNCOMB and STOP_UNCOMB commands cannot fail and the device must always
> + * start the playback of the requested effect if the UPLOAD command of the
> + * respective effect has been successful. ff-memless-next will never send
> + * a START/STOP_UNCOMB command for an effect that has not been uploaded
> + * successfully, nor will it send an ERASE command for an effect that is
> + * playing (= has been started with START_UNCOMB command).

> + */
> +
> +enum mlnx_commands {
> +	/* Start or update a combined effect. This command is sent whenever
> +	 * a FF_CONSTANT, FF_PERIODIC or FF_RAMP is started, stopped or
> +	 * updated by userspace, when the applied envelopes are recalculated
> +	 * or when periodic effects are recalculated. */
> +	MLNX_START_COMBINED,
> +	/* Stop combined effect. This command is sent when all combinable
> +	 * effects are stopped. */
> +	MLNX_STOP_COMBINED,
> +	/* Start or update a rumble effect. This command is sent whenever
> +	 * a FF_RUMBLE effect is started or when its magnitudes or directions
> +	 * change. */
> +	MLNX_START_RUMBLE,
> +	/* Stop a rumble effect. This command is sent when all FF_RUMBLE
> +	 * effects are stopped. */
> +	MLNX_STOP_RUMBLE,
> +	/* Start or update an uncombinable effect. This command is sent
> +	 * whenever an uncombinable effect is started or updated. */

Why do we have make a distinction between rumble and all other effects?

-- 
Dmitry

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-14  6:38     ` Dmitry Torokhov
  0 siblings, 0 replies; 83+ messages in thread
From: Dmitry Torokhov @ 2014-05-14  6:38 UTC (permalink / raw)
  To: Michal Malý
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

Hi Michal,

On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> +
> +/** DEFINITION OF TERMS
> + *
> + * Combined effect - An effect whose force is a superposition of forces
> + *                   generated by all effects that can be added together.
> + *                   Only one combined effect can be playing at a time.
> + *                   Effects that can be added together to create a combined
> + *                   effect are FF_CONSTANT, FF_PERIODIC and FF_RAMP.
> + * Uncombinable effect - An effect that cannot be combined with another effect.
> + *                       All conditional effects - FF_DAMPER, FF_FRICTION,
> + *                       FF_INERTIA and FF_SPRING are uncombinable.
> + *                       Number of uncombinable effects playing simultaneously
> + *                       depends on the capabilities of the hardware.
> + * Rumble effect - An effect generated by device's rumble motors instead of
> + *                 force feedback actuators.
> + *
> + *
> + * HANDLING OF UNCOMBINABLE EFFECTS
> + *
> + * Uncombinable effects cannot be combined together into just one effect, at
> + * least not in a clear and obvious manner. Therefore these effects have to
> + * be handled individually by ff-memless-next. Handling of these effects is
> + * left entirely to the hardware-specific driver, ff-memless-next merely
> + * passes these effects to the hardware-specific driver at appropriate time.
> + * ff-memless-next provides the UPLOAD command to notify the hardware-specific
> + * driver that the userspace is about to request playback of an uncombinable
> + * effect. The hardware-specific driver shall take all steps needed to make
> + * the device ready to play the effect when it receives the UPLOAD command.
> + * The actual playback shall commence when START_UNCOMB command is received.
> + * Opposite to the UPLOAD command is the ERASE command which tells
> + * the hardware-specific driver that the playback has finished and that
> + * the effect will not be restarted. STOP_UNCOMB command tells
> + * the hardware-specific driver that the playback shall stop but the device
> + * shall still be ready to resume the playback immediately.
> + *
> + * In case it is not possible to make the device ready to play an uncombinable
> + * effect (all hardware effect slots are occupied), the hardware-specific
> + * driver may return an error when it receives an UPLOAD command. If the

This part concerns me. It seems to me that devices supporting
"uncombinable" effects are in fact not memoryless devices and we should
not be introducing this term here. If the goal is to work around limited
number of effect slots in the devices by combining certain effects then
it needs to be done at ff-core level as it will be potentially useful
for all devices.

> + * hardware-specific driver returns 0, the upload is considered successful.
> + * START_UNCOMB and STOP_UNCOMB commands cannot fail and the device must always
> + * start the playback of the requested effect if the UPLOAD command of the
> + * respective effect has been successful. ff-memless-next will never send
> + * a START/STOP_UNCOMB command for an effect that has not been uploaded
> + * successfully, nor will it send an ERASE command for an effect that is
> + * playing (= has been started with START_UNCOMB command).

> + */
> +
> +enum mlnx_commands {
> +	/* Start or update a combined effect. This command is sent whenever
> +	 * a FF_CONSTANT, FF_PERIODIC or FF_RAMP is started, stopped or
> +	 * updated by userspace, when the applied envelopes are recalculated
> +	 * or when periodic effects are recalculated. */
> +	MLNX_START_COMBINED,
> +	/* Stop combined effect. This command is sent when all combinable
> +	 * effects are stopped. */
> +	MLNX_STOP_COMBINED,
> +	/* Start or update a rumble effect. This command is sent whenever
> +	 * a FF_RUMBLE effect is started or when its magnitudes or directions
> +	 * change. */
> +	MLNX_START_RUMBLE,
> +	/* Stop a rumble effect. This command is sent when all FF_RUMBLE
> +	 * effects are stopped. */
> +	MLNX_STOP_RUMBLE,
> +	/* Start or update an uncombinable effect. This command is sent
> +	 * whenever an uncombinable effect is started or updated. */

Why do we have make a distinction between rumble and all other effects?

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-14  6:38     ` Dmitry Torokhov
@ 2014-05-14  8:35       ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-14  8:35 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

Hi Dmitry,

thank you for reviewing this.

On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
> On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> > +
> > +/** DEFINITION OF TERMS
> > + *
> > + * Combined effect - An effect whose force is a superposition of forces
> > + *                   generated by all effects that can be added together.
> > + *                   Only one combined effect can be playing at a time.
> > + *                   Effects that can be added together to create a
> > combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
> > FF_RAMP. + * Uncombinable effect - An effect that cannot be combined with
> > another effect. + *                       All conditional effects -
> > FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
> > FF_SPRING are uncombinable. + *                       Number of
> > uncombinable effects playing simultaneously + *                      
> > depends on the capabilities of the hardware. + * Rumble effect - An
> > effect generated by device's rumble motors instead of + *                
> > force feedback actuators.
> > + *
> > + *
> > + * HANDLING OF UNCOMBINABLE EFFECTS
> > + *
> > + * Uncombinable effects cannot be combined together into just one effect,
> > at + * least not in a clear and obvious manner. Therefore these effects
> > have to + * be handled individually by ff-memless-next. Handling of these
> > effects is + * left entirely to the hardware-specific driver,
> > ff-memless-next merely + * passes these effects to the hardware-specific
> > driver at appropriate time. + * ff-memless-next provides the UPLOAD
> > command to notify the hardware-specific + * driver that the userspace is
> > about to request playback of an uncombinable + * effect. The
> > hardware-specific driver shall take all steps needed to make + * the
> > device ready to play the effect when it receives the UPLOAD command. + *
> > The actual playback shall commence when START_UNCOMB command is received.
> > + * Opposite to the UPLOAD command is the ERASE command which tells + *
> > the hardware-specific driver that the playback has finished and that + *
> > the effect will not be restarted. STOP_UNCOMB command tells
> > + * the hardware-specific driver that the playback shall stop but the
> > device + * shall still be ready to resume the playback immediately.
> > + *
> > + * In case it is not possible to make the device ready to play an
> > uncombinable + * effect (all hardware effect slots are occupied), the
> > hardware-specific + * driver may return an error when it receives an
> > UPLOAD command. If the
> This part concerns me. It seems to me that devices supporting
> "uncombinable" effects are in fact not memoryless devices and we should
> not be introducing this term here. If the goal is to work around limited
> number of effect slots in the devices by combining certain effects then
> it needs to be done at ff-core level as it will be potentially useful
> for all devices.

Force generated by a conditional effect (referred to as "uncombinable" within
ff-memless-next to make the distinction clear) depends on a position of the 
device. For instance the more a device is deflected from a neutral position the 
greater force FF_SPRING generates. A truly memoryless device would have to 
report its position to the driver, have it calculate the appropriate force and 
send it back to the device. IMHO such a loop would require a very high USB 
polling rate to play conditional effects with acceptable quality.

We know for a fact that at least many (all?) Logitech devices that support 
conditional effects use this "semi-memoryless" approach where FF_CONSTANT and 
FF_PERIODIC are handled in the memoryless fashion and conditional effects are 
uploaded to the device (in a somewhat simplified form). The amount of effects 
that can be uploaded to a device is limited which is why ff-memless-next uses 
two steps (UPLOAD/ERASE and START/STOP) to handle these effects.

Conditional effects - even if they are of the same type - cannot be effectively 
combined into one because superposition doesn't seem to work here so they have 
to be processed one by one.

If we ever come across a really memoryless device it should not be 
particularly difficult to add another callback to ff-memless-next which would 
emulate conditional effects with constant force.

> > + * hardware-specific driver returns 0, the upload is considered
> > successful. + * START_UNCOMB and STOP_UNCOMB commands cannot fail and the
> > device must always + * start the playback of the requested effect if the
> > UPLOAD command of the + * respective effect has been successful.
> > ff-memless-next will never send + * a START/STOP_UNCOMB command for an
> > effect that has not been uploaded + * successfully, nor will it send an
> > ERASE command for an effect that is + * playing (= has been started with
> > START_UNCOMB command).
> > 
> > + */
> > +
> > +enum mlnx_commands {
> > +	/* Start or update a combined effect. This command is sent whenever
> > +	 * a FF_CONSTANT, FF_PERIODIC or FF_RAMP is started, stopped or
> > +	 * updated by userspace, when the applied envelopes are recalculated
> > +	 * or when periodic effects are recalculated. */
> > +	MLNX_START_COMBINED,
> > +	/* Stop combined effect. This command is sent when all combinable
> > +	 * effects are stopped. */
> > +	MLNX_STOP_COMBINED,
> > +	/* Start or update a rumble effect. This command is sent whenever
> > +	 * a FF_RUMBLE effect is started or when its magnitudes or directions
> > +	 * change. */
> > +	MLNX_START_RUMBLE,
> > +	/* Stop a rumble effect. This command is sent when all FF_RUMBLE
> > +	 * effects are stopped. */
> > +	MLNX_STOP_RUMBLE,
> > +	/* Start or update an uncombinable effect. This command is sent
> > +	 * whenever an uncombinable effect is started or updated. */
> 
> Why do we have make a distinction between rumble and all other effects?

Because "combined" effect consists of two vectors pointing along X and Y axes 
whereas "rumble" effect is a rotation speed of a rumble motor and the direction 
of rotation. Naturally these effects have to be handled in a different fashion. 
It is also possible to have a device with both rumble motors and FF actuator. 
Having such a distinction also makes it easier to handle emulation of rumble 
effects through constant force and vice versa.

Michal

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-14  8:35       ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-14  8:35 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

Hi Dmitry,

thank you for reviewing this.

On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
> On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> > +
> > +/** DEFINITION OF TERMS
> > + *
> > + * Combined effect - An effect whose force is a superposition of forces
> > + *                   generated by all effects that can be added together.
> > + *                   Only one combined effect can be playing at a time.
> > + *                   Effects that can be added together to create a
> > combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
> > FF_RAMP. + * Uncombinable effect - An effect that cannot be combined with
> > another effect. + *                       All conditional effects -
> > FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
> > FF_SPRING are uncombinable. + *                       Number of
> > uncombinable effects playing simultaneously + *                      
> > depends on the capabilities of the hardware. + * Rumble effect - An
> > effect generated by device's rumble motors instead of + *                
> > force feedback actuators.
> > + *
> > + *
> > + * HANDLING OF UNCOMBINABLE EFFECTS
> > + *
> > + * Uncombinable effects cannot be combined together into just one effect,
> > at + * least not in a clear and obvious manner. Therefore these effects
> > have to + * be handled individually by ff-memless-next. Handling of these
> > effects is + * left entirely to the hardware-specific driver,
> > ff-memless-next merely + * passes these effects to the hardware-specific
> > driver at appropriate time. + * ff-memless-next provides the UPLOAD
> > command to notify the hardware-specific + * driver that the userspace is
> > about to request playback of an uncombinable + * effect. The
> > hardware-specific driver shall take all steps needed to make + * the
> > device ready to play the effect when it receives the UPLOAD command. + *
> > The actual playback shall commence when START_UNCOMB command is received.
> > + * Opposite to the UPLOAD command is the ERASE command which tells + *
> > the hardware-specific driver that the playback has finished and that + *
> > the effect will not be restarted. STOP_UNCOMB command tells
> > + * the hardware-specific driver that the playback shall stop but the
> > device + * shall still be ready to resume the playback immediately.
> > + *
> > + * In case it is not possible to make the device ready to play an
> > uncombinable + * effect (all hardware effect slots are occupied), the
> > hardware-specific + * driver may return an error when it receives an
> > UPLOAD command. If the
> This part concerns me. It seems to me that devices supporting
> "uncombinable" effects are in fact not memoryless devices and we should
> not be introducing this term here. If the goal is to work around limited
> number of effect slots in the devices by combining certain effects then
> it needs to be done at ff-core level as it will be potentially useful
> for all devices.

Force generated by a conditional effect (referred to as "uncombinable" within
ff-memless-next to make the distinction clear) depends on a position of the 
device. For instance the more a device is deflected from a neutral position the 
greater force FF_SPRING generates. A truly memoryless device would have to 
report its position to the driver, have it calculate the appropriate force and 
send it back to the device. IMHO such a loop would require a very high USB 
polling rate to play conditional effects with acceptable quality.

We know for a fact that at least many (all?) Logitech devices that support 
conditional effects use this "semi-memoryless" approach where FF_CONSTANT and 
FF_PERIODIC are handled in the memoryless fashion and conditional effects are 
uploaded to the device (in a somewhat simplified form). The amount of effects 
that can be uploaded to a device is limited which is why ff-memless-next uses 
two steps (UPLOAD/ERASE and START/STOP) to handle these effects.

Conditional effects - even if they are of the same type - cannot be effectively 
combined into one because superposition doesn't seem to work here so they have 
to be processed one by one.

If we ever come across a really memoryless device it should not be 
particularly difficult to add another callback to ff-memless-next which would 
emulate conditional effects with constant force.

> > + * hardware-specific driver returns 0, the upload is considered
> > successful. + * START_UNCOMB and STOP_UNCOMB commands cannot fail and the
> > device must always + * start the playback of the requested effect if the
> > UPLOAD command of the + * respective effect has been successful.
> > ff-memless-next will never send + * a START/STOP_UNCOMB command for an
> > effect that has not been uploaded + * successfully, nor will it send an
> > ERASE command for an effect that is + * playing (= has been started with
> > START_UNCOMB command).
> > 
> > + */
> > +
> > +enum mlnx_commands {
> > +	/* Start or update a combined effect. This command is sent whenever
> > +	 * a FF_CONSTANT, FF_PERIODIC or FF_RAMP is started, stopped or
> > +	 * updated by userspace, when the applied envelopes are recalculated
> > +	 * or when periodic effects are recalculated. */
> > +	MLNX_START_COMBINED,
> > +	/* Stop combined effect. This command is sent when all combinable
> > +	 * effects are stopped. */
> > +	MLNX_STOP_COMBINED,
> > +	/* Start or update a rumble effect. This command is sent whenever
> > +	 * a FF_RUMBLE effect is started or when its magnitudes or directions
> > +	 * change. */
> > +	MLNX_START_RUMBLE,
> > +	/* Stop a rumble effect. This command is sent when all FF_RUMBLE
> > +	 * effects are stopped. */
> > +	MLNX_STOP_RUMBLE,
> > +	/* Start or update an uncombinable effect. This command is sent
> > +	 * whenever an uncombinable effect is started or updated. */
> 
> Why do we have make a distinction between rumble and all other effects?

Because "combined" effect consists of two vectors pointing along X and Y axes 
whereas "rumble" effect is a rotation speed of a rumble motor and the direction 
of rotation. Naturally these effects have to be handled in a different fashion. 
It is also possible to have a device with both rumble motors and FF actuator. 
Having such a distinction also makes it easier to handle emulation of rumble 
effects through constant force and vice versa.

Michal
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-14  8:35       ` Michal Malý
  (?)
@ 2014-05-14 11:26       ` Elias Vanderstuyft
  -1 siblings, 0 replies; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-14 11:26 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Michal Malý,
	linux-input, linux-kernel, Jiri Kosina, Anssi Hannula,
	Simon Wood

On Wed, May 14, 2014 at 10:35 AM, Michal Malý
<madcatxster@devoid-pointer.net> wrote:
> Hi Dmitry,
>
> thank you for reviewing this.
>
> On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
>> On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
>> > +
>> > +/** DEFINITION OF TERMS
>> > + *
>> > + * Combined effect - An effect whose force is a superposition of forces
>> > + *                   generated by all effects that can be added together.
>> > + *                   Only one combined effect can be playing at a time.
>> > + *                   Effects that can be added together to create a
>> > combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
>> > FF_RAMP. + * Uncombinable effect - An effect that cannot be combined with
>> > another effect. + *                       All conditional effects -
>> > FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
>> > FF_SPRING are uncombinable. + *                       Number of
>> > uncombinable effects playing simultaneously + *
>> > depends on the capabilities of the hardware. + * Rumble effect - An
>> > effect generated by device's rumble motors instead of + *
>> > force feedback actuators.
>> > + *
>> > + *
>> > + * HANDLING OF UNCOMBINABLE EFFECTS
>> > + *
>> > + * Uncombinable effects cannot be combined together into just one effect,
>> > at + * least not in a clear and obvious manner. Therefore these effects
>> > have to + * be handled individually by ff-memless-next. Handling of these
>> > effects is + * left entirely to the hardware-specific driver,
>> > ff-memless-next merely + * passes these effects to the hardware-specific
>> > driver at appropriate time. + * ff-memless-next provides the UPLOAD
>> > command to notify the hardware-specific + * driver that the userspace is
>> > about to request playback of an uncombinable + * effect. The
>> > hardware-specific driver shall take all steps needed to make + * the
>> > device ready to play the effect when it receives the UPLOAD command. + *
>> > The actual playback shall commence when START_UNCOMB command is received.
>> > + * Opposite to the UPLOAD command is the ERASE command which tells + *
>> > the hardware-specific driver that the playback has finished and that + *
>> > the effect will not be restarted. STOP_UNCOMB command tells
>> > + * the hardware-specific driver that the playback shall stop but the
>> > device + * shall still be ready to resume the playback immediately.
>> > + *
>> > + * In case it is not possible to make the device ready to play an
>> > uncombinable + * effect (all hardware effect slots are occupied), the
>> > hardware-specific + * driver may return an error when it receives an
>> > UPLOAD command. If the
>> This part concerns me. It seems to me that devices supporting
>> "uncombinable" effects are in fact not memoryless devices and we should
>> not be introducing this term here. If the goal is to work around limited
>> number of effect slots in the devices by combining certain effects then
>> it needs to be done at ff-core level as it will be potentially useful
>> for all devices.
>
> Force generated by a conditional effect (referred to as "uncombinable" within
> ff-memless-next to make the distinction clear) depends on a position of the
> device. For instance the more a device is deflected from a neutral position the
> greater force FF_SPRING generates. A truly memoryless device would have to
> report its position to the driver, have it calculate the appropriate force and
> send it back to the device. IMHO such a loop would require a very high USB
> polling rate to play conditional effects with acceptable quality.
>
> We know for a fact that at least many (all?) Logitech devices that support
> conditional effects use this "semi-memoryless" approach where FF_CONSTANT and
> FF_PERIODIC are handled in the memoryless fashion and conditional effects are
> uploaded to the device (in a somewhat simplified form). The amount of effects
> that can be uploaded to a device is limited which is why ff-memless-next uses
> two steps (UPLOAD/ERASE and START/STOP) to handle these effects.
>
> Conditional effects - even if they are of the same type - cannot be effectively
> combined into one because superposition doesn't seem to work here so they have
> to be processed one by one.
>
> If we ever come across a really memoryless device it should not be
> particularly difficult to add another callback to ff-memless-next which would
> emulate conditional effects with constant force.

And I should add that even the conditional effects themselves are
handled semi-memless by the Logitech devices:
the effects' time parameters are handled in a memless way: the
scheduling has to be done by the driver, and is not uploaded to the
device.

Compare this with a fully non-memless device, an example of a driver
that handles such devices: "hid-pidff.c"
Here, all effect data is sent directly to the device, also the
time-related parameters.

Elias

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-14  8:35       ` Michal Malý
  (?)
  (?)
@ 2014-05-14 15:11       ` simon
  2014-05-14 17:58         ` Dmitry Torokhov
  -1 siblings, 1 reply; 83+ messages in thread
From: simon @ 2014-05-14 15:11 UTC (permalink / raw)
  To: "Michal Malý"
  Cc: Dmitry Torokhov, linux-input, linux-kernel, jkosina, elias.vds,
	anssi.hannula, simon


> If we ever come across a really memoryless device it should not be
> particularly difficult to add another callback to ff-memless-next which
> would emulate conditional effects with constant force.

It should be noted that some (/many?) applications/games already use the
CF with position feedback to emulate behaviour such as springs. The feed
back loop is simply not fast enough to do emulation of friction/inertia.

I don't believe that the FF layer in the kernel should be 'tricked out'
with lots of emulation features.

As a game designer (ok... tinkerer) it's a bit of a pain that devices lie
about what they can really do - ie. gamepads claiming periodic ability
when all they are doing is faking it with rumble.

Simon.




^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-14 15:11       ` simon
@ 2014-05-14 17:58         ` Dmitry Torokhov
  0 siblings, 0 replies; 83+ messages in thread
From: Dmitry Torokhov @ 2014-05-14 17:58 UTC (permalink / raw)
  To: simon
  Cc: "Michal Malý",
	linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula

On Wed, May 14, 2014 at 11:11:40AM -0400, simon@mungewell.org wrote:
> 
> > If we ever come across a really memoryless device it should not be
> > particularly difficult to add another callback to ff-memless-next which
> > would emulate conditional effects with constant force.
> 
> It should be noted that some (/many?) applications/games already use the
> CF with position feedback to emulate behaviour such as springs. The feed
> back loop is simply not fast enough to do emulation of friction/inertia.
> 
> I don't believe that the FF layer in the kernel should be 'tricked out'
> with lots of emulation features.
> 
> As a game designer (ok... tinkerer) it's a bit of a pain that devices lie
> about what they can really do - ie. gamepads claiming periodic ability
> when all they are doing is faking it with rumble.

Would it help if kernel would indicate which effects are native and
which are emulated?

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-14  8:35       ` Michal Malý
@ 2014-05-14 18:05         ` Dmitry Torokhov
  -1 siblings, 0 replies; 83+ messages in thread
From: Dmitry Torokhov @ 2014-05-14 18:05 UTC (permalink / raw)
  To: Michal Malý
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

On Wed, May 14, 2014 at 10:35:25AM +0200, Michal Malý wrote:
> Hi Dmitry,
> 
> thank you for reviewing this.
> 
> On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
> > On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> > > +
> > > +/** DEFINITION OF TERMS
> > > + *
> > > + * Combined effect - An effect whose force is a superposition of forces
> > > + *                   generated by all effects that can be added together.
> > > + *                   Only one combined effect can be playing at a time.
> > > + *                   Effects that can be added together to create a
> > > combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
> > > FF_RAMP. + * Uncombinable effect - An effect that cannot be combined with
> > > another effect. + *                       All conditional effects -
> > > FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
> > > FF_SPRING are uncombinable. + *                       Number of
> > > uncombinable effects playing simultaneously + *                      
> > > depends on the capabilities of the hardware. + * Rumble effect - An
> > > effect generated by device's rumble motors instead of + *                
> > > force feedback actuators.
> > > + *
> > > + *
> > > + * HANDLING OF UNCOMBINABLE EFFECTS
> > > + *
> > > + * Uncombinable effects cannot be combined together into just one effect,
> > > at + * least not in a clear and obvious manner. Therefore these effects
> > > have to + * be handled individually by ff-memless-next. Handling of these
> > > effects is + * left entirely to the hardware-specific driver,
> > > ff-memless-next merely + * passes these effects to the hardware-specific
> > > driver at appropriate time. + * ff-memless-next provides the UPLOAD
> > > command to notify the hardware-specific + * driver that the userspace is
> > > about to request playback of an uncombinable + * effect. The
> > > hardware-specific driver shall take all steps needed to make + * the
> > > device ready to play the effect when it receives the UPLOAD command. + *
> > > The actual playback shall commence when START_UNCOMB command is received.
> > > + * Opposite to the UPLOAD command is the ERASE command which tells + *
> > > the hardware-specific driver that the playback has finished and that + *
> > > the effect will not be restarted. STOP_UNCOMB command tells
> > > + * the hardware-specific driver that the playback shall stop but the
> > > device + * shall still be ready to resume the playback immediately.
> > > + *
> > > + * In case it is not possible to make the device ready to play an
> > > uncombinable + * effect (all hardware effect slots are occupied), the
> > > hardware-specific + * driver may return an error when it receives an
> > > UPLOAD command. If the
> > This part concerns me. It seems to me that devices supporting
> > "uncombinable" effects are in fact not memoryless devices and we should
> > not be introducing this term here. If the goal is to work around limited
> > number of effect slots in the devices by combining certain effects then
> > it needs to be done at ff-core level as it will be potentially useful
> > for all devices.
> 
> Force generated by a conditional effect (referred to as "uncombinable" within
> ff-memless-next to make the distinction clear) depends on a position of the 
> device. For instance the more a device is deflected from a neutral position the 
> greater force FF_SPRING generates. A truly memoryless device would have to 
> report its position to the driver, have it calculate the appropriate force and 
> send it back to the device. IMHO such a loop would require a very high USB 
> polling rate to play conditional effects with acceptable quality.
> 
> We know for a fact that at least many (all?) Logitech devices that support 
> conditional effects use this "semi-memoryless" approach where FF_CONSTANT and 
> FF_PERIODIC are handled in the memoryless fashion and conditional effects are 
> uploaded to the device (in a somewhat simplified form). The amount of effects 
> that can be uploaded to a device is limited which is why ff-memless-next uses 
> two steps (UPLOAD/ERASE and START/STOP) to handle these effects.
> 
> Conditional effects - even if they are of the same type - cannot be effectively 
> combined into one because superposition doesn't seem to work here so they have 
> to be processed one by one.
> 
> If we ever come across a really memoryless device it should not be 
> particularly difficult to add another callback to ff-memless-next which would 
> emulate conditional effects with constant force.

Thank you for the explanation. This further solidifies for me the idea
that handling of such effects that are in fact uploaded to and managed
by the device should not be handled by the memoryless core but rather by
the driver itself. I.e. such drivers should implement their own play(),
upload(), erase(), etc, and decide whether to use a hardware slot for
the effect or handle effect in memoryless fashion (if possible). We can
open ff-memless to allow such drivers to use parts of memoryless
handling.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-14 18:05         ` Dmitry Torokhov
  0 siblings, 0 replies; 83+ messages in thread
From: Dmitry Torokhov @ 2014-05-14 18:05 UTC (permalink / raw)
  To: Michal Malý
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

On Wed, May 14, 2014 at 10:35:25AM +0200, Michal Malý wrote:
> Hi Dmitry,
> 
> thank you for reviewing this.
> 
> On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
> > On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> > > +
> > > +/** DEFINITION OF TERMS
> > > + *
> > > + * Combined effect - An effect whose force is a superposition of forces
> > > + *                   generated by all effects that can be added together.
> > > + *                   Only one combined effect can be playing at a time.
> > > + *                   Effects that can be added together to create a
> > > combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
> > > FF_RAMP. + * Uncombinable effect - An effect that cannot be combined with
> > > another effect. + *                       All conditional effects -
> > > FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
> > > FF_SPRING are uncombinable. + *                       Number of
> > > uncombinable effects playing simultaneously + *                      
> > > depends on the capabilities of the hardware. + * Rumble effect - An
> > > effect generated by device's rumble motors instead of + *                
> > > force feedback actuators.
> > > + *
> > > + *
> > > + * HANDLING OF UNCOMBINABLE EFFECTS
> > > + *
> > > + * Uncombinable effects cannot be combined together into just one effect,
> > > at + * least not in a clear and obvious manner. Therefore these effects
> > > have to + * be handled individually by ff-memless-next. Handling of these
> > > effects is + * left entirely to the hardware-specific driver,
> > > ff-memless-next merely + * passes these effects to the hardware-specific
> > > driver at appropriate time. + * ff-memless-next provides the UPLOAD
> > > command to notify the hardware-specific + * driver that the userspace is
> > > about to request playback of an uncombinable + * effect. The
> > > hardware-specific driver shall take all steps needed to make + * the
> > > device ready to play the effect when it receives the UPLOAD command. + *
> > > The actual playback shall commence when START_UNCOMB command is received.
> > > + * Opposite to the UPLOAD command is the ERASE command which tells + *
> > > the hardware-specific driver that the playback has finished and that + *
> > > the effect will not be restarted. STOP_UNCOMB command tells
> > > + * the hardware-specific driver that the playback shall stop but the
> > > device + * shall still be ready to resume the playback immediately.
> > > + *
> > > + * In case it is not possible to make the device ready to play an
> > > uncombinable + * effect (all hardware effect slots are occupied), the
> > > hardware-specific + * driver may return an error when it receives an
> > > UPLOAD command. If the
> > This part concerns me. It seems to me that devices supporting
> > "uncombinable" effects are in fact not memoryless devices and we should
> > not be introducing this term here. If the goal is to work around limited
> > number of effect slots in the devices by combining certain effects then
> > it needs to be done at ff-core level as it will be potentially useful
> > for all devices.
> 
> Force generated by a conditional effect (referred to as "uncombinable" within
> ff-memless-next to make the distinction clear) depends on a position of the 
> device. For instance the more a device is deflected from a neutral position the 
> greater force FF_SPRING generates. A truly memoryless device would have to 
> report its position to the driver, have it calculate the appropriate force and 
> send it back to the device. IMHO such a loop would require a very high USB 
> polling rate to play conditional effects with acceptable quality.
> 
> We know for a fact that at least many (all?) Logitech devices that support 
> conditional effects use this "semi-memoryless" approach where FF_CONSTANT and 
> FF_PERIODIC are handled in the memoryless fashion and conditional effects are 
> uploaded to the device (in a somewhat simplified form). The amount of effects 
> that can be uploaded to a device is limited which is why ff-memless-next uses 
> two steps (UPLOAD/ERASE and START/STOP) to handle these effects.
> 
> Conditional effects - even if they are of the same type - cannot be effectively 
> combined into one because superposition doesn't seem to work here so they have 
> to be processed one by one.
> 
> If we ever come across a really memoryless device it should not be 
> particularly difficult to add another callback to ff-memless-next which would 
> emulate conditional effects with constant force.

Thank you for the explanation. This further solidifies for me the idea
that handling of such effects that are in fact uploaded to and managed
by the device should not be handled by the memoryless core but rather by
the driver itself. I.e. such drivers should implement their own play(),
upload(), erase(), etc, and decide whether to use a hardware slot for
the effect or handle effect in memoryless fashion (if possible). We can
open ff-memless to allow such drivers to use parts of memoryless
handling.

Thanks.

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-04-26 15:02   ` Michal Malý
  (?)
  (?)
@ 2014-05-14 18:14   ` Dmitry Torokhov
  2014-05-14 19:40       ` Michal Malý
  -1 siblings, 1 reply; 83+ messages in thread
From: Dmitry Torokhov @ 2014-05-14 18:14 UTC (permalink / raw)
  To: Michal Malý
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> +
> +/** input_ff_create_mlnx() - Register a device within ff-memless-next and
> + *                           the kernel force feedback system
> + * @dev: Pointer to the struct input_dev associated with the device.
> + * @data: Any device-specific data that shall be passed to the callback.
> + *        function called by ff-memless-next when a force feedback action
> + *        shall be performed.
> + * @control_effect: Pointer to the callback function.
> + * @update_date: Delay in milliseconds between two recalculations of periodic
> + *               effects, ramp effects and envelopes. Note that this value will
> + *               never be lower than (CONFIG_HZ / 1000) + 1 regardless of the
> + *               value specified here. This is not a "hard" rate limiter.
> + *               Userspace still can submit effects at a rate faster than
> + *               this value.

The update rate change seems useful whether we use new ff implementation
or enhance the old one but I would prefer having a separate call to
control it.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-14 18:05         ` Dmitry Torokhov
@ 2014-05-14 19:38           ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-14 19:38 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula,
	simon, edwin

On Wednesday 14 of May 2014 11:05:58 Dmitry Torokhov wrote:
> On Wed, May 14, 2014 at 10:35:25AM +0200, Michal Malý wrote:
> > Hi Dmitry,
> > 
> > thank you for reviewing this.
> > 
> > On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
> > > On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> > > > +
> > > > +/** DEFINITION OF TERMS
> > > > + *
> > > > + * Combined effect - An effect whose force is a superposition of
> > > > forces
> > > > + *                   generated by all effects that can be added
> > > > together.
> > > > + *                   Only one combined effect can be playing at a
> > > > time.
> > > > + *                   Effects that can be added together to create a
> > > > combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
> > > > FF_RAMP. + * Uncombinable effect - An effect that cannot be combined
> > > > with
> > > > another effect. + *                       All conditional effects -
> > > > FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
> > > > FF_SPRING are uncombinable. + *                       Number of
> > > > uncombinable effects playing simultaneously + *
> > > > depends on the capabilities of the hardware. + * Rumble effect - An
> > > > effect generated by device's rumble motors instead of + *
> > > > force feedback actuators.
> > > > + *
> > > > + *
> > > > + * HANDLING OF UNCOMBINABLE EFFECTS
> > > > + *
> > > > + * Uncombinable effects cannot be combined together into just one
> > > > effect,
> > > > at + * least not in a clear and obvious manner. Therefore these
> > > > effects
> > > > have to + * be handled individually by ff-memless-next. Handling of
> > > > these
> > > > effects is + * left entirely to the hardware-specific driver,
> > > > ff-memless-next merely + * passes these effects to the
> > > > hardware-specific
> > > > driver at appropriate time. + * ff-memless-next provides the UPLOAD
> > > > command to notify the hardware-specific + * driver that the userspace
> > > > is
> > > > about to request playback of an uncombinable + * effect. The
> > > > hardware-specific driver shall take all steps needed to make + * the
> > > > device ready to play the effect when it receives the UPLOAD command. +
> > > > *
> > > > The actual playback shall commence when START_UNCOMB command is
> > > > received.
> > > > + * Opposite to the UPLOAD command is the ERASE command which tells +
> > > > *
> > > > the hardware-specific driver that the playback has finished and that +
> > > > *
> > > > the effect will not be restarted. STOP_UNCOMB command tells
> > > > + * the hardware-specific driver that the playback shall stop but the
> > > > device + * shall still be ready to resume the playback immediately.
> > > > + *
> > > > + * In case it is not possible to make the device ready to play an
> > > > uncombinable + * effect (all hardware effect slots are occupied), the
> > > > hardware-specific + * driver may return an error when it receives an
> > > > UPLOAD command. If the
> > > 
> > > This part concerns me. It seems to me that devices supporting
> > > "uncombinable" effects are in fact not memoryless devices and we should
> > > not be introducing this term here. If the goal is to work around limited
> > > number of effect slots in the devices by combining certain effects then
> > > it needs to be done at ff-core level as it will be potentially useful
> > > for all devices.
> > 
> > Force generated by a conditional effect (referred to as "uncombinable"
> > within ff-memless-next to make the distinction clear) depends on a
> > position of the device. For instance the more a device is deflected from
> > a neutral position the greater force FF_SPRING generates. A truly
> > memoryless device would have to report its position to the driver, have
> > it calculate the appropriate force and send it back to the device. IMHO
> > such a loop would require a very high USB polling rate to play
> > conditional effects with acceptable quality.
> > 
> > We know for a fact that at least many (all?) Logitech devices that support
> > conditional effects use this "semi-memoryless" approach where FF_CONSTANT
> > and FF_PERIODIC are handled in the memoryless fashion and conditional
> > effects are uploaded to the device (in a somewhat simplified form). The
> > amount of effects that can be uploaded to a device is limited which is
> > why ff-memless-next uses two steps (UPLOAD/ERASE and START/STOP) to
> > handle these effects.
> > 
> > Conditional effects - even if they are of the same type - cannot be
> > effectively combined into one because superposition doesn't seem to work
> > here so they have to be processed one by one.
> > 
> > If we ever come across a really memoryless device it should not be
> > particularly difficult to add another callback to ff-memless-next which
> > would emulate conditional effects with constant force.
> 
> Thank you for the explanation. This further solidifies for me the idea
> that handling of such effects that are in fact uploaded to and managed
> by the device should not be handled by the memoryless core but rather by
> the driver itself. I.e. such drivers should implement their own play(),
> upload(), erase(), etc, and decide whether to use a hardware slot for
> the effect or handle effect in memoryless fashion (if possible). We can
> open ff-memless to allow such drivers to use parts of memoryless
> handling.

Well, these effects are not exactly managed by the device. The only thing that 
is uploaded to the device are parameters of the force to be generated. Other 
parameters - such as timing - still have to be managed by the driver. Any 
driver supporting conditional effects would then have to reimplement at least 
the timing logic.

Another thing of concern is rate limiting. During our testing we have 
discovered that some games can fire off FF commands at a very fast rate - much 
faster than USB polling rate of a device. This eventually overfills the USB 
submit queue and messes everything up. A proper way to fix this would be to 
limit the rate at which the driver sends HW requests. We already have a few 
ideas as to how to implement this in ff-memless-next. If we had the driver use 
ff-memless-next to manage one category of effects and use its own logic to 
manage the rest it'd be next to impossible to do this properly.

It should also be noted that ff-memless-next almost passes the conditional 
effects through to the HW-specific driver that then takes care of everything. A 
practical example of how this works can be found in an experimental port of 
"hid-lg4ff" driver to ff-memless-next by Edwin Velds (https://github.com/edwin-v/linux-hid-lg4ff-next). The only thing that ff-memless-next does is that it 
tells the HW-specific driver that such an effect such start or stop playing.

The reasoning above made me implement the support for conditional effects in 
the way I did. As much as I agree that we're not dealing with purely 
memoryless devices here, I believe that most more advanced FFB wheels/sticks 
are in fact "semi-memoryless" and will therefore benefit from the approach used 
in ff-memless-next.

Michal

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-14 19:38           ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-14 19:38 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula,
	simon, edwin

On Wednesday 14 of May 2014 11:05:58 Dmitry Torokhov wrote:
> On Wed, May 14, 2014 at 10:35:25AM +0200, Michal Malý wrote:
> > Hi Dmitry,
> > 
> > thank you for reviewing this.
> > 
> > On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
> > > On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> > > > +
> > > > +/** DEFINITION OF TERMS
> > > > + *
> > > > + * Combined effect - An effect whose force is a superposition of
> > > > forces
> > > > + *                   generated by all effects that can be added
> > > > together.
> > > > + *                   Only one combined effect can be playing at a
> > > > time.
> > > > + *                   Effects that can be added together to create a
> > > > combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
> > > > FF_RAMP. + * Uncombinable effect - An effect that cannot be combined
> > > > with
> > > > another effect. + *                       All conditional effects -
> > > > FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
> > > > FF_SPRING are uncombinable. + *                       Number of
> > > > uncombinable effects playing simultaneously + *
> > > > depends on the capabilities of the hardware. + * Rumble effect - An
> > > > effect generated by device's rumble motors instead of + *
> > > > force feedback actuators.
> > > > + *
> > > > + *
> > > > + * HANDLING OF UNCOMBINABLE EFFECTS
> > > > + *
> > > > + * Uncombinable effects cannot be combined together into just one
> > > > effect,
> > > > at + * least not in a clear and obvious manner. Therefore these
> > > > effects
> > > > have to + * be handled individually by ff-memless-next. Handling of
> > > > these
> > > > effects is + * left entirely to the hardware-specific driver,
> > > > ff-memless-next merely + * passes these effects to the
> > > > hardware-specific
> > > > driver at appropriate time. + * ff-memless-next provides the UPLOAD
> > > > command to notify the hardware-specific + * driver that the userspace
> > > > is
> > > > about to request playback of an uncombinable + * effect. The
> > > > hardware-specific driver shall take all steps needed to make + * the
> > > > device ready to play the effect when it receives the UPLOAD command. +
> > > > *
> > > > The actual playback shall commence when START_UNCOMB command is
> > > > received.
> > > > + * Opposite to the UPLOAD command is the ERASE command which tells +
> > > > *
> > > > the hardware-specific driver that the playback has finished and that +
> > > > *
> > > > the effect will not be restarted. STOP_UNCOMB command tells
> > > > + * the hardware-specific driver that the playback shall stop but the
> > > > device + * shall still be ready to resume the playback immediately.
> > > > + *
> > > > + * In case it is not possible to make the device ready to play an
> > > > uncombinable + * effect (all hardware effect slots are occupied), the
> > > > hardware-specific + * driver may return an error when it receives an
> > > > UPLOAD command. If the
> > > 
> > > This part concerns me. It seems to me that devices supporting
> > > "uncombinable" effects are in fact not memoryless devices and we should
> > > not be introducing this term here. If the goal is to work around limited
> > > number of effect slots in the devices by combining certain effects then
> > > it needs to be done at ff-core level as it will be potentially useful
> > > for all devices.
> > 
> > Force generated by a conditional effect (referred to as "uncombinable"
> > within ff-memless-next to make the distinction clear) depends on a
> > position of the device. For instance the more a device is deflected from
> > a neutral position the greater force FF_SPRING generates. A truly
> > memoryless device would have to report its position to the driver, have
> > it calculate the appropriate force and send it back to the device. IMHO
> > such a loop would require a very high USB polling rate to play
> > conditional effects with acceptable quality.
> > 
> > We know for a fact that at least many (all?) Logitech devices that support
> > conditional effects use this "semi-memoryless" approach where FF_CONSTANT
> > and FF_PERIODIC are handled in the memoryless fashion and conditional
> > effects are uploaded to the device (in a somewhat simplified form). The
> > amount of effects that can be uploaded to a device is limited which is
> > why ff-memless-next uses two steps (UPLOAD/ERASE and START/STOP) to
> > handle these effects.
> > 
> > Conditional effects - even if they are of the same type - cannot be
> > effectively combined into one because superposition doesn't seem to work
> > here so they have to be processed one by one.
> > 
> > If we ever come across a really memoryless device it should not be
> > particularly difficult to add another callback to ff-memless-next which
> > would emulate conditional effects with constant force.
> 
> Thank you for the explanation. This further solidifies for me the idea
> that handling of such effects that are in fact uploaded to and managed
> by the device should not be handled by the memoryless core but rather by
> the driver itself. I.e. such drivers should implement their own play(),
> upload(), erase(), etc, and decide whether to use a hardware slot for
> the effect or handle effect in memoryless fashion (if possible). We can
> open ff-memless to allow such drivers to use parts of memoryless
> handling.

Well, these effects are not exactly managed by the device. The only thing that 
is uploaded to the device are parameters of the force to be generated. Other 
parameters - such as timing - still have to be managed by the driver. Any 
driver supporting conditional effects would then have to reimplement at least 
the timing logic.

Another thing of concern is rate limiting. During our testing we have 
discovered that some games can fire off FF commands at a very fast rate - much 
faster than USB polling rate of a device. This eventually overfills the USB 
submit queue and messes everything up. A proper way to fix this would be to 
limit the rate at which the driver sends HW requests. We already have a few 
ideas as to how to implement this in ff-memless-next. If we had the driver use 
ff-memless-next to manage one category of effects and use its own logic to 
manage the rest it'd be next to impossible to do this properly.

It should also be noted that ff-memless-next almost passes the conditional 
effects through to the HW-specific driver that then takes care of everything. A 
practical example of how this works can be found in an experimental port of 
"hid-lg4ff" driver to ff-memless-next by Edwin Velds (https://github.com/edwin-v/linux-hid-lg4ff-next). The only thing that ff-memless-next does is that it 
tells the HW-specific driver that such an effect such start or stop playing.

The reasoning above made me implement the support for conditional effects in 
the way I did. As much as I agree that we're not dealing with purely 
memoryless devices here, I believe that most more advanced FFB wheels/sticks 
are in fact "semi-memoryless" and will therefore benefit from the approach used 
in ff-memless-next.

Michal
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-14 18:14   ` Dmitry Torokhov
@ 2014-05-14 19:40       ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-14 19:40 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

On Wednesday 14 of May 2014 11:14:02 Dmitry Torokhov wrote:
> On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> > +
> > +/** input_ff_create_mlnx() - Register a device within ff-memless-next and
> > + *                           the kernel force feedback system
> > + * @dev: Pointer to the struct input_dev associated with the device.
> > + * @data: Any device-specific data that shall be passed to the callback.
> > + *        function called by ff-memless-next when a force feedback action
> > + *        shall be performed.
> > + * @control_effect: Pointer to the callback function.
> > + * @update_date: Delay in milliseconds between two recalculations of
> > periodic + *               effects, ramp effects and envelopes. Note that
> > this value will + *               never be lower than (CONFIG_HZ / 1000)
> > + 1 regardless of the + *               value specified here. This is not
> > a "hard" rate limiter. + *               Userspace still can submit
> > effects at a rate faster than + *               this value.
> 
> The update rate change seems useful whether we use new ff implementation
> or enhance the old one but I would prefer having a separate call to
> control it.

OK, that should not be a problem. Please note that the plan is to use this 
value to do proper "hard" rate limiting in the future.

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-14 19:40       ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-14 19:40 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

On Wednesday 14 of May 2014 11:14:02 Dmitry Torokhov wrote:
> On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> > +
> > +/** input_ff_create_mlnx() - Register a device within ff-memless-next and
> > + *                           the kernel force feedback system
> > + * @dev: Pointer to the struct input_dev associated with the device.
> > + * @data: Any device-specific data that shall be passed to the callback.
> > + *        function called by ff-memless-next when a force feedback action
> > + *        shall be performed.
> > + * @control_effect: Pointer to the callback function.
> > + * @update_date: Delay in milliseconds between two recalculations of
> > periodic + *               effects, ramp effects and envelopes. Note that
> > this value will + *               never be lower than (CONFIG_HZ / 1000)
> > + 1 regardless of the + *               value specified here. This is not
> > a "hard" rate limiter. + *               Userspace still can submit
> > effects at a rate faster than + *               this value.
> 
> The update rate change seems useful whether we use new ff implementation
> or enhance the old one but I would prefer having a separate call to
> control it.

OK, that should not be a problem. Please note that the plan is to use this 
value to do proper "hard" rate limiting in the future.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-14 18:05         ` Dmitry Torokhov
@ 2014-05-20  9:27           ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-20  9:27 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

On Wednesday 14 of May 2014 11:05:58 Dmitry Torokhov wrote:
> On Wed, May 14, 2014 at 10:35:25AM +0200, Michal Malý wrote:
> > Hi Dmitry,
> > 
> > thank you for reviewing this.
> > 
> > On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
> > > On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> > > > +
> > > > +/** DEFINITION OF TERMS
> > > > + *
> > > > + * Combined effect - An effect whose force is a superposition of
> > > > forces
> > > > + *                   generated by all effects that can be added
> > > > together.
> > > > + *                   Only one combined effect can be playing at a
> > > > time.
> > > > + *                   Effects that can be added together to create a
> > > > combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
> > > > FF_RAMP. + * Uncombinable effect - An effect that cannot be combined
> > > > with
> > > > another effect. + *                       All conditional effects -
> > > > FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
> > > > FF_SPRING are uncombinable. + *                       Number of
> > > > uncombinable effects playing simultaneously + *
> > > > depends on the capabilities of the hardware. + * Rumble effect - An
> > > > effect generated by device's rumble motors instead of + *
> > > > force feedback actuators.
> > > > + *
> > > > + *
> > > > + * HANDLING OF UNCOMBINABLE EFFECTS
> > > > + *
> > > > + * Uncombinable effects cannot be combined together into just one
> > > > effect,
> > > > at + * least not in a clear and obvious manner. Therefore these
> > > > effects
> > > > have to + * be handled individually by ff-memless-next. Handling of
> > > > these
> > > > effects is + * left entirely to the hardware-specific driver,
> > > > ff-memless-next merely + * passes these effects to the
> > > > hardware-specific
> > > > driver at appropriate time. + * ff-memless-next provides the UPLOAD
> > > > command to notify the hardware-specific + * driver that the userspace
> > > > is
> > > > about to request playback of an uncombinable + * effect. The
> > > > hardware-specific driver shall take all steps needed to make + * the
> > > > device ready to play the effect when it receives the UPLOAD command. +
> > > > *
> > > > The actual playback shall commence when START_UNCOMB command is
> > > > received.
> > > > + * Opposite to the UPLOAD command is the ERASE command which tells +
> > > > *
> > > > the hardware-specific driver that the playback has finished and that +
> > > > *
> > > > the effect will not be restarted. STOP_UNCOMB command tells
> > > > + * the hardware-specific driver that the playback shall stop but the
> > > > device + * shall still be ready to resume the playback immediately.
> > > > + *
> > > > + * In case it is not possible to make the device ready to play an
> > > > uncombinable + * effect (all hardware effect slots are occupied), the
> > > > hardware-specific + * driver may return an error when it receives an
> > > > UPLOAD command. If the
> > > 
> > > This part concerns me. It seems to me that devices supporting
> > > "uncombinable" effects are in fact not memoryless devices and we should
> > > not be introducing this term here. If the goal is to work around limited
> > > number of effect slots in the devices by combining certain effects then
> > > it needs to be done at ff-core level as it will be potentially useful
> > > for all devices.
> > 
> > Force generated by a conditional effect (referred to as "uncombinable"
> > within ff-memless-next to make the distinction clear) depends on a
> > position of the device. For instance the more a device is deflected from
> > a neutral position the greater force FF_SPRING generates. A truly
> > memoryless device would have to report its position to the driver, have
> > it calculate the appropriate force and send it back to the device. IMHO
> > such a loop would require a very high USB polling rate to play
> > conditional effects with acceptable quality.
> > 
> > We know for a fact that at least many (all?) Logitech devices that support
> > conditional effects use this "semi-memoryless" approach where FF_CONSTANT
> > and FF_PERIODIC are handled in the memoryless fashion and conditional
> > effects are uploaded to the device (in a somewhat simplified form). The
> > amount of effects that can be uploaded to a device is limited which is
> > why ff-memless-next uses two steps (UPLOAD/ERASE and START/STOP) to
> > handle these effects.
> > 
> > Conditional effects - even if they are of the same type - cannot be
> > effectively combined into one because superposition doesn't seem to work
> > here so they have to be processed one by one.
> > 
> > If we ever come across a really memoryless device it should not be
> > particularly difficult to add another callback to ff-memless-next which
> > would emulate conditional effects with constant force.
> 
> Thank you for the explanation. This further solidifies for me the idea
> that handling of such effects that are in fact uploaded to and managed
> by the device should not be handled by the memoryless core but rather by
> the driver itself. I.e. such drivers should implement their own play(),
> upload(), erase(), etc, and decide whether to use a hardware slot for
> the effect or handle effect in memoryless fashion (if possible). We can
> open ff-memless to allow such drivers to use parts of memoryless
> handling.
> 
> Thanks.

To bring this to a conclusion we could go from, would this be an acceptable 
solution?

- Have the HW-specific driver talk directly to ff-core and reimplement upload(), 
play(), etc.
- Rewrite "ff-memless-next" so that it is not a self-contained module but a 
library of functions.
- Have the driver either:
  - Upload an effect to a device directly if the device can fully manage the 
effect by itself.
  - Use provided timing functions to know when an effect should start, stop, 
restart etc...
  - Use provided timing AND processing functions to combine effects that can be 
combined into one, calculate periodic waveforms etc?

I have no problem with throwing my current approach away but before I start 
working on a new one I'd like to know which way to go...

Thanks,
Michal

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-20  9:27           ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-20  9:27 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

On Wednesday 14 of May 2014 11:05:58 Dmitry Torokhov wrote:
> On Wed, May 14, 2014 at 10:35:25AM +0200, Michal Malý wrote:
> > Hi Dmitry,
> > 
> > thank you for reviewing this.
> > 
> > On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
> > > On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> > > > +
> > > > +/** DEFINITION OF TERMS
> > > > + *
> > > > + * Combined effect - An effect whose force is a superposition of
> > > > forces
> > > > + *                   generated by all effects that can be added
> > > > together.
> > > > + *                   Only one combined effect can be playing at a
> > > > time.
> > > > + *                   Effects that can be added together to create a
> > > > combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
> > > > FF_RAMP. + * Uncombinable effect - An effect that cannot be combined
> > > > with
> > > > another effect. + *                       All conditional effects -
> > > > FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
> > > > FF_SPRING are uncombinable. + *                       Number of
> > > > uncombinable effects playing simultaneously + *
> > > > depends on the capabilities of the hardware. + * Rumble effect - An
> > > > effect generated by device's rumble motors instead of + *
> > > > force feedback actuators.
> > > > + *
> > > > + *
> > > > + * HANDLING OF UNCOMBINABLE EFFECTS
> > > > + *
> > > > + * Uncombinable effects cannot be combined together into just one
> > > > effect,
> > > > at + * least not in a clear and obvious manner. Therefore these
> > > > effects
> > > > have to + * be handled individually by ff-memless-next. Handling of
> > > > these
> > > > effects is + * left entirely to the hardware-specific driver,
> > > > ff-memless-next merely + * passes these effects to the
> > > > hardware-specific
> > > > driver at appropriate time. + * ff-memless-next provides the UPLOAD
> > > > command to notify the hardware-specific + * driver that the userspace
> > > > is
> > > > about to request playback of an uncombinable + * effect. The
> > > > hardware-specific driver shall take all steps needed to make + * the
> > > > device ready to play the effect when it receives the UPLOAD command. +
> > > > *
> > > > The actual playback shall commence when START_UNCOMB command is
> > > > received.
> > > > + * Opposite to the UPLOAD command is the ERASE command which tells +
> > > > *
> > > > the hardware-specific driver that the playback has finished and that +
> > > > *
> > > > the effect will not be restarted. STOP_UNCOMB command tells
> > > > + * the hardware-specific driver that the playback shall stop but the
> > > > device + * shall still be ready to resume the playback immediately.
> > > > + *
> > > > + * In case it is not possible to make the device ready to play an
> > > > uncombinable + * effect (all hardware effect slots are occupied), the
> > > > hardware-specific + * driver may return an error when it receives an
> > > > UPLOAD command. If the
> > > 
> > > This part concerns me. It seems to me that devices supporting
> > > "uncombinable" effects are in fact not memoryless devices and we should
> > > not be introducing this term here. If the goal is to work around limited
> > > number of effect slots in the devices by combining certain effects then
> > > it needs to be done at ff-core level as it will be potentially useful
> > > for all devices.
> > 
> > Force generated by a conditional effect (referred to as "uncombinable"
> > within ff-memless-next to make the distinction clear) depends on a
> > position of the device. For instance the more a device is deflected from
> > a neutral position the greater force FF_SPRING generates. A truly
> > memoryless device would have to report its position to the driver, have
> > it calculate the appropriate force and send it back to the device. IMHO
> > such a loop would require a very high USB polling rate to play
> > conditional effects with acceptable quality.
> > 
> > We know for a fact that at least many (all?) Logitech devices that support
> > conditional effects use this "semi-memoryless" approach where FF_CONSTANT
> > and FF_PERIODIC are handled in the memoryless fashion and conditional
> > effects are uploaded to the device (in a somewhat simplified form). The
> > amount of effects that can be uploaded to a device is limited which is
> > why ff-memless-next uses two steps (UPLOAD/ERASE and START/STOP) to
> > handle these effects.
> > 
> > Conditional effects - even if they are of the same type - cannot be
> > effectively combined into one because superposition doesn't seem to work
> > here so they have to be processed one by one.
> > 
> > If we ever come across a really memoryless device it should not be
> > particularly difficult to add another callback to ff-memless-next which
> > would emulate conditional effects with constant force.
> 
> Thank you for the explanation. This further solidifies for me the idea
> that handling of such effects that are in fact uploaded to and managed
> by the device should not be handled by the memoryless core but rather by
> the driver itself. I.e. such drivers should implement their own play(),
> upload(), erase(), etc, and decide whether to use a hardware slot for
> the effect or handle effect in memoryless fashion (if possible). We can
> open ff-memless to allow such drivers to use parts of memoryless
> handling.
> 
> Thanks.

To bring this to a conclusion we could go from, would this be an acceptable 
solution?

- Have the HW-specific driver talk directly to ff-core and reimplement upload(), 
play(), etc.
- Rewrite "ff-memless-next" so that it is not a self-contained module but a 
library of functions.
- Have the driver either:
  - Upload an effect to a device directly if the device can fully manage the 
effect by itself.
  - Use provided timing functions to know when an effect should start, stop, 
restart etc...
  - Use provided timing AND processing functions to combine effects that can be 
combined into one, calculate periodic waveforms etc?

I have no problem with throwing my current approach away but before I start 
working on a new one I'd like to know which way to go...

Thanks,
Michal
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20  9:27           ` Michal Malý
@ 2014-05-20 18:32             ` Roland Bosa
  -1 siblings, 0 replies; 83+ messages in thread
From: Roland Bosa @ 2014-05-20 18:32 UTC (permalink / raw)
  To: Michal Malý, Dmitry Torokhov
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

On 05/20/2014 02:27 AM, Michal Malý wrote:
> On Wednesday 14 of May 2014 11:05:58 Dmitry Torokhov wrote:
>> On Wed, May 14, 2014 at 10:35:25AM +0200, Michal Malý wrote:
>>> Hi Dmitry,
>>>
>>> thank you for reviewing this.
>>>
>>> On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
>>>> On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
>>>>> +
>>>>> +/** DEFINITION OF TERMS
>>>>> + *
>>>>> + * Combined effect - An effect whose force is a superposition of
>>>>> forces
>>>>> + *                   generated by all effects that can be added
>>>>> together.
>>>>> + *                   Only one combined effect can be playing at a
>>>>> time.
>>>>> + *                   Effects that can be added together to create a
>>>>> combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
>>>>> FF_RAMP. + * Uncombinable effect - An effect that cannot be combined
>>>>> with
>>>>> another effect. + *                       All conditional effects -
>>>>> FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
>>>>> FF_SPRING are uncombinable. + *                       Number of
>>>>> uncombinable effects playing simultaneously + *
>>>>> depends on the capabilities of the hardware. + * Rumble effect - An
>>>>> effect generated by device's rumble motors instead of + *
>>>>> force feedback actuators.
>>>>> + *
>>>>> + *
>>>>> + * HANDLING OF UNCOMBINABLE EFFECTS
>>>>> + *
>>>>> + * Uncombinable effects cannot be combined together into just one
>>>>> effect,
>>>>> at + * least not in a clear and obvious manner. Therefore these
>>>>> effects
>>>>> have to + * be handled individually by ff-memless-next. Handling of
>>>>> these
>>>>> effects is + * left entirely to the hardware-specific driver,
>>>>> ff-memless-next merely + * passes these effects to the
>>>>> hardware-specific
>>>>> driver at appropriate time. + * ff-memless-next provides the UPLOAD
>>>>> command to notify the hardware-specific + * driver that the userspace
>>>>> is
>>>>> about to request playback of an uncombinable + * effect. The
>>>>> hardware-specific driver shall take all steps needed to make + * the
>>>>> device ready to play the effect when it receives the UPLOAD command. +
>>>>> *
>>>>> The actual playback shall commence when START_UNCOMB command is
>>>>> received.
>>>>> + * Opposite to the UPLOAD command is the ERASE command which tells +
>>>>> *
>>>>> the hardware-specific driver that the playback has finished and that +
>>>>> *
>>>>> the effect will not be restarted. STOP_UNCOMB command tells
>>>>> + * the hardware-specific driver that the playback shall stop but the
>>>>> device + * shall still be ready to resume the playback immediately.
>>>>> + *
>>>>> + * In case it is not possible to make the device ready to play an
>>>>> uncombinable + * effect (all hardware effect slots are occupied), the
>>>>> hardware-specific + * driver may return an error when it receives an
>>>>> UPLOAD command. If the
>>>>
>>>> This part concerns me. It seems to me that devices supporting
>>>> "uncombinable" effects are in fact not memoryless devices and we should
>>>> not be introducing this term here. If the goal is to work around limited
>>>> number of effect slots in the devices by combining certain effects then
>>>> it needs to be done at ff-core level as it will be potentially useful
>>>> for all devices.
>>>
>>> Force generated by a conditional effect (referred to as "uncombinable"
>>> within ff-memless-next to make the distinction clear) depends on a
>>> position of the device. For instance the more a device is deflected from
>>> a neutral position the greater force FF_SPRING generates. A truly
>>> memoryless device would have to report its position to the driver, have
>>> it calculate the appropriate force and send it back to the device. IMHO
>>> such a loop would require a very high USB polling rate to play
>>> conditional effects with acceptable quality.
>>>
>>> We know for a fact that at least many (all?) Logitech devices that support
>>> conditional effects use this "semi-memoryless" approach where FF_CONSTANT
>>> and FF_PERIODIC are handled in the memoryless fashion and conditional
>>> effects are uploaded to the device (in a somewhat simplified form). The
>>> amount of effects that can be uploaded to a device is limited which is
>>> why ff-memless-next uses two steps (UPLOAD/ERASE and START/STOP) to
>>> handle these effects.
>>>
>>> Conditional effects - even if they are of the same type - cannot be
>>> effectively combined into one because superposition doesn't seem to work
>>> here so they have to be processed one by one.
>>>
>>> If we ever come across a really memoryless device it should not be
>>> particularly difficult to add another callback to ff-memless-next which
>>> would emulate conditional effects with constant force.
>>
>> Thank you for the explanation. This further solidifies for me the idea
>> that handling of such effects that are in fact uploaded to and managed
>> by the device should not be handled by the memoryless core but rather by
>> the driver itself. I.e. such drivers should implement their own play(),
>> upload(), erase(), etc, and decide whether to use a hardware slot for
>> the effect or handle effect in memoryless fashion (if possible). We can
>> open ff-memless to allow such drivers to use parts of memoryless
>> handling.
>>
>> Thanks.
> 
> To bring this to a conclusion we could go from, would this be an acceptable 
> solution?
> 
> - Have the HW-specific driver talk directly to ff-core and reimplement upload(), 
> play(), etc.
> - Rewrite "ff-memless-next" so that it is not a self-contained module but a 
> library of functions.
> - Have the driver either:
>   - Upload an effect to a device directly if the device can fully manage the 
> effect by itself.
>   - Use provided timing functions to know when an effect should start, stop, 
> restart etc...
>   - Use provided timing AND processing functions to combine effects that can be 
> combined into one, calculate periodic waveforms etc?
> 
> I have no problem with throwing my current approach away but before I start 
> working on a new one I'd like to know which way to go...
> 
> Thanks,
> Michal

Hi everyone

Allow me to introduce myself. I'm Roland Bosa and I work for Logitech,
more specifically the gaming group. I have been tasked to look into
gaming device support for Linux and I just started following this list
and studying the kernel code related to the Logitech Force Feedback
devices. The 'ff-memless-next' module sounds tailor-made for our
devices. Please let me know how I can help with its development.

Thanks
Roland


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-20 18:32             ` Roland Bosa
  0 siblings, 0 replies; 83+ messages in thread
From: Roland Bosa @ 2014-05-20 18:32 UTC (permalink / raw)
  To: Michal Malý, Dmitry Torokhov
  Cc: linux-input, linux-kernel, jkosina, elias.vds, anssi.hannula, simon

On 05/20/2014 02:27 AM, Michal Malý wrote:
> On Wednesday 14 of May 2014 11:05:58 Dmitry Torokhov wrote:
>> On Wed, May 14, 2014 at 10:35:25AM +0200, Michal Malý wrote:
>>> Hi Dmitry,
>>>
>>> thank you for reviewing this.
>>>
>>> On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
>>>> On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
>>>>> +
>>>>> +/** DEFINITION OF TERMS
>>>>> + *
>>>>> + * Combined effect - An effect whose force is a superposition of
>>>>> forces
>>>>> + *                   generated by all effects that can be added
>>>>> together.
>>>>> + *                   Only one combined effect can be playing at a
>>>>> time.
>>>>> + *                   Effects that can be added together to create a
>>>>> combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
>>>>> FF_RAMP. + * Uncombinable effect - An effect that cannot be combined
>>>>> with
>>>>> another effect. + *                       All conditional effects -
>>>>> FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
>>>>> FF_SPRING are uncombinable. + *                       Number of
>>>>> uncombinable effects playing simultaneously + *
>>>>> depends on the capabilities of the hardware. + * Rumble effect - An
>>>>> effect generated by device's rumble motors instead of + *
>>>>> force feedback actuators.
>>>>> + *
>>>>> + *
>>>>> + * HANDLING OF UNCOMBINABLE EFFECTS
>>>>> + *
>>>>> + * Uncombinable effects cannot be combined together into just one
>>>>> effect,
>>>>> at + * least not in a clear and obvious manner. Therefore these
>>>>> effects
>>>>> have to + * be handled individually by ff-memless-next. Handling of
>>>>> these
>>>>> effects is + * left entirely to the hardware-specific driver,
>>>>> ff-memless-next merely + * passes these effects to the
>>>>> hardware-specific
>>>>> driver at appropriate time. + * ff-memless-next provides the UPLOAD
>>>>> command to notify the hardware-specific + * driver that the userspace
>>>>> is
>>>>> about to request playback of an uncombinable + * effect. The
>>>>> hardware-specific driver shall take all steps needed to make + * the
>>>>> device ready to play the effect when it receives the UPLOAD command. +
>>>>> *
>>>>> The actual playback shall commence when START_UNCOMB command is
>>>>> received.
>>>>> + * Opposite to the UPLOAD command is the ERASE command which tells +
>>>>> *
>>>>> the hardware-specific driver that the playback has finished and that +
>>>>> *
>>>>> the effect will not be restarted. STOP_UNCOMB command tells
>>>>> + * the hardware-specific driver that the playback shall stop but the
>>>>> device + * shall still be ready to resume the playback immediately.
>>>>> + *
>>>>> + * In case it is not possible to make the device ready to play an
>>>>> uncombinable + * effect (all hardware effect slots are occupied), the
>>>>> hardware-specific + * driver may return an error when it receives an
>>>>> UPLOAD command. If the
>>>>
>>>> This part concerns me. It seems to me that devices supporting
>>>> "uncombinable" effects are in fact not memoryless devices and we should
>>>> not be introducing this term here. If the goal is to work around limited
>>>> number of effect slots in the devices by combining certain effects then
>>>> it needs to be done at ff-core level as it will be potentially useful
>>>> for all devices.
>>>
>>> Force generated by a conditional effect (referred to as "uncombinable"
>>> within ff-memless-next to make the distinction clear) depends on a
>>> position of the device. For instance the more a device is deflected from
>>> a neutral position the greater force FF_SPRING generates. A truly
>>> memoryless device would have to report its position to the driver, have
>>> it calculate the appropriate force and send it back to the device. IMHO
>>> such a loop would require a very high USB polling rate to play
>>> conditional effects with acceptable quality.
>>>
>>> We know for a fact that at least many (all?) Logitech devices that support
>>> conditional effects use this "semi-memoryless" approach where FF_CONSTANT
>>> and FF_PERIODIC are handled in the memoryless fashion and conditional
>>> effects are uploaded to the device (in a somewhat simplified form). The
>>> amount of effects that can be uploaded to a device is limited which is
>>> why ff-memless-next uses two steps (UPLOAD/ERASE and START/STOP) to
>>> handle these effects.
>>>
>>> Conditional effects - even if they are of the same type - cannot be
>>> effectively combined into one because superposition doesn't seem to work
>>> here so they have to be processed one by one.
>>>
>>> If we ever come across a really memoryless device it should not be
>>> particularly difficult to add another callback to ff-memless-next which
>>> would emulate conditional effects with constant force.
>>
>> Thank you for the explanation. This further solidifies for me the idea
>> that handling of such effects that are in fact uploaded to and managed
>> by the device should not be handled by the memoryless core but rather by
>> the driver itself. I.e. such drivers should implement their own play(),
>> upload(), erase(), etc, and decide whether to use a hardware slot for
>> the effect or handle effect in memoryless fashion (if possible). We can
>> open ff-memless to allow such drivers to use parts of memoryless
>> handling.
>>
>> Thanks.
> 
> To bring this to a conclusion we could go from, would this be an acceptable 
> solution?
> 
> - Have the HW-specific driver talk directly to ff-core and reimplement upload(), 
> play(), etc.
> - Rewrite "ff-memless-next" so that it is not a self-contained module but a 
> library of functions.
> - Have the driver either:
>   - Upload an effect to a device directly if the device can fully manage the 
> effect by itself.
>   - Use provided timing functions to know when an effect should start, stop, 
> restart etc...
>   - Use provided timing AND processing functions to combine effects that can be 
> combined into one, calculate periodic waveforms etc?
> 
> I have no problem with throwing my current approach away but before I start 
> working on a new one I'd like to know which way to go...
> 
> Thanks,
> Michal

Hi everyone

Allow me to introduce myself. I'm Roland Bosa and I work for Logitech,
more specifically the gaming group. I have been tasked to look into
gaming device support for Linux and I just started following this list
and studying the kernel code related to the Logitech Force Feedback
devices. The 'ff-memless-next' module sounds tailor-made for our
devices. Please let me know how I can help with its development.

Thanks
Roland

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20 18:32             ` Roland Bosa
@ 2014-05-20 19:00               ` Michal Malý
  -1 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-20 19:00 UTC (permalink / raw)
  To: Roland Bosa
  Cc: Dmitry Torokhov, linux-input, linux-kernel, jkosina, elias.vds,
	anssi.hannula, simon

On Tuesday 20 of May 2014 11:32:14 Roland Bosa wrote:
> On 05/20/2014 02:27 AM, Michal Malý wrote:
> > On Wednesday 14 of May 2014 11:05:58 Dmitry Torokhov wrote:
> >> On Wed, May 14, 2014 at 10:35:25AM +0200, Michal Malý wrote:
> >>> Hi Dmitry,
> >>> 
> >>> thank you for reviewing this.
> >>> 
> >>> On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
> >>>> On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> >>>>> +
> >>>>> +/** DEFINITION OF TERMS
> >>>>> + *
> >>>>> + * Combined effect - An effect whose force is a superposition of
> >>>>> forces
> >>>>> + *                   generated by all effects that can be added
> >>>>> together.
> >>>>> + *                   Only one combined effect can be playing at a
> >>>>> time.
> >>>>> + *                   Effects that can be added together to create a
> >>>>> combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
> >>>>> FF_RAMP. + * Uncombinable effect - An effect that cannot be combined
> >>>>> with
> >>>>> another effect. + *                       All conditional effects -
> >>>>> FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
> >>>>> FF_SPRING are uncombinable. + *                       Number of
> >>>>> uncombinable effects playing simultaneously + *
> >>>>> depends on the capabilities of the hardware. + * Rumble effect - An
> >>>>> effect generated by device's rumble motors instead of + *
> >>>>> force feedback actuators.
> >>>>> + *
> >>>>> + *
> >>>>> + * HANDLING OF UNCOMBINABLE EFFECTS
> >>>>> + *
> >>>>> + * Uncombinable effects cannot be combined together into just one
> >>>>> effect,
> >>>>> at + * least not in a clear and obvious manner. Therefore these
> >>>>> effects
> >>>>> have to + * be handled individually by ff-memless-next. Handling of
> >>>>> these
> >>>>> effects is + * left entirely to the hardware-specific driver,
> >>>>> ff-memless-next merely + * passes these effects to the
> >>>>> hardware-specific
> >>>>> driver at appropriate time. + * ff-memless-next provides the UPLOAD
> >>>>> command to notify the hardware-specific + * driver that the userspace
> >>>>> is
> >>>>> about to request playback of an uncombinable + * effect. The
> >>>>> hardware-specific driver shall take all steps needed to make + * the
> >>>>> device ready to play the effect when it receives the UPLOAD command. +
> >>>>> *
> >>>>> The actual playback shall commence when START_UNCOMB command is
> >>>>> received.
> >>>>> + * Opposite to the UPLOAD command is the ERASE command which tells +
> >>>>> *
> >>>>> the hardware-specific driver that the playback has finished and that +
> >>>>> *
> >>>>> the effect will not be restarted. STOP_UNCOMB command tells
> >>>>> + * the hardware-specific driver that the playback shall stop but the
> >>>>> device + * shall still be ready to resume the playback immediately.
> >>>>> + *
> >>>>> + * In case it is not possible to make the device ready to play an
> >>>>> uncombinable + * effect (all hardware effect slots are occupied), the
> >>>>> hardware-specific + * driver may return an error when it receives an
> >>>>> UPLOAD command. If the
> >>>> 
> >>>> This part concerns me. It seems to me that devices supporting
> >>>> "uncombinable" effects are in fact not memoryless devices and we should
> >>>> not be introducing this term here. If the goal is to work around
> >>>> limited
> >>>> number of effect slots in the devices by combining certain effects then
> >>>> it needs to be done at ff-core level as it will be potentially useful
> >>>> for all devices.
> >>> 
> >>> Force generated by a conditional effect (referred to as "uncombinable"
> >>> within ff-memless-next to make the distinction clear) depends on a
> >>> position of the device. For instance the more a device is deflected from
> >>> a neutral position the greater force FF_SPRING generates. A truly
> >>> memoryless device would have to report its position to the driver, have
> >>> it calculate the appropriate force and send it back to the device. IMHO
> >>> such a loop would require a very high USB polling rate to play
> >>> conditional effects with acceptable quality.
> >>> 
> >>> We know for a fact that at least many (all?) Logitech devices that
> >>> support
> >>> conditional effects use this "semi-memoryless" approach where
> >>> FF_CONSTANT
> >>> and FF_PERIODIC are handled in the memoryless fashion and conditional
> >>> effects are uploaded to the device (in a somewhat simplified form). The
> >>> amount of effects that can be uploaded to a device is limited which is
> >>> why ff-memless-next uses two steps (UPLOAD/ERASE and START/STOP) to
> >>> handle these effects.
> >>> 
> >>> Conditional effects - even if they are of the same type - cannot be
> >>> effectively combined into one because superposition doesn't seem to work
> >>> here so they have to be processed one by one.
> >>> 
> >>> If we ever come across a really memoryless device it should not be
> >>> particularly difficult to add another callback to ff-memless-next which
> >>> would emulate conditional effects with constant force.
> >> 
> >> Thank you for the explanation. This further solidifies for me the idea
> >> that handling of such effects that are in fact uploaded to and managed
> >> by the device should not be handled by the memoryless core but rather by
> >> the driver itself. I.e. such drivers should implement their own play(),
> >> upload(), erase(), etc, and decide whether to use a hardware slot for
> >> the effect or handle effect in memoryless fashion (if possible). We can
> >> open ff-memless to allow such drivers to use parts of memoryless
> >> handling.
> >> 
> >> Thanks.
> > 
> > To bring this to a conclusion we could go from, would this be an
> > acceptable
> > solution?
> > 
> > - Have the HW-specific driver talk directly to ff-core and reimplement
> > upload(), play(), etc.
> > - Rewrite "ff-memless-next" so that it is not a self-contained module but
> > a
> > library of functions.
> > 
> > - Have the driver either:
> >   - Upload an effect to a device directly if the device can fully manage
> >   the
> > 
> > effect by itself.
> > 
> >   - Use provided timing functions to know when an effect should start,
> >   stop,
> > 
> > restart etc...
> > 
> >   - Use provided timing AND processing functions to combine effects that
> >   can be> 
> > combined into one, calculate periodic waveforms etc?
> > 
> > I have no problem with throwing my current approach away but before I
> > start
> > working on a new one I'd like to know which way to go...
> > 
> > Thanks,
> > Michal
> 
> Hi everyone
> 
> Allow me to introduce myself. I'm Roland Bosa and I work for Logitech,
> more specifically the gaming group. I have been tasked to look into
> gaming device support for Linux and I just started following this list
> and studying the kernel code related to the Logitech Force Feedback
> devices. The 'ff-memless-next' module sounds tailor-made for our
> devices. Please let me know how I can help with its development.


Hi,

"ff-memless-next" was designed with behavior of Logitech devices in mind, 
however it was always meant as a general replacement for the current "ff-
memless". After some followup discussion it's unlikely that it will be 
mainlined in its current form. See the discussion here 
"http://www.spinics.net/lists/linux-input/msg31426.html" for more details.

We would however really appreciate some information regarding Logitech devices 
specifically. Although we have reverse engineered most of things, I believe 
there are still areas we're not entirely clear about. I realize that you'd 
probably have to take it up with the legal deparment but if someone at 
Logitech could at least take a look at what we've found so far at fill in the 
blanks it'd be most helpful.
Please note that we found numerous bugs and DirectInput specs violations in 
the Windows driver which might have impacted our understading of how the 
driver works. "ff-memless-next" and "lg4ff" tries to fix or work around these. A 
link to out-of-tree update to lg4ff which takes full advantage of "ff-memless-
next" is also provided in this thread.

Thanks for any help you can get us,
Michal

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-20 19:00               ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-20 19:00 UTC (permalink / raw)
  To: Roland Bosa
  Cc: Dmitry Torokhov, linux-input, linux-kernel, jkosina, elias.vds,
	anssi.hannula, simon

On Tuesday 20 of May 2014 11:32:14 Roland Bosa wrote:
> On 05/20/2014 02:27 AM, Michal Malý wrote:
> > On Wednesday 14 of May 2014 11:05:58 Dmitry Torokhov wrote:
> >> On Wed, May 14, 2014 at 10:35:25AM +0200, Michal Malý wrote:
> >>> Hi Dmitry,
> >>> 
> >>> thank you for reviewing this.
> >>> 
> >>> On Tuesday 13 of May 2014 23:38:06 Dmitry Torokhov wrote:
> >>>> On Sat, Apr 26, 2014 at 05:02:00PM +0200, Michal Malý wrote:
> >>>>> +
> >>>>> +/** DEFINITION OF TERMS
> >>>>> + *
> >>>>> + * Combined effect - An effect whose force is a superposition of
> >>>>> forces
> >>>>> + *                   generated by all effects that can be added
> >>>>> together.
> >>>>> + *                   Only one combined effect can be playing at a
> >>>>> time.
> >>>>> + *                   Effects that can be added together to create a
> >>>>> combined + *                   effect are FF_CONSTANT, FF_PERIODIC and
> >>>>> FF_RAMP. + * Uncombinable effect - An effect that cannot be combined
> >>>>> with
> >>>>> another effect. + *                       All conditional effects -
> >>>>> FF_DAMPER, FF_FRICTION, + *                       FF_INERTIA and
> >>>>> FF_SPRING are uncombinable. + *                       Number of
> >>>>> uncombinable effects playing simultaneously + *
> >>>>> depends on the capabilities of the hardware. + * Rumble effect - An
> >>>>> effect generated by device's rumble motors instead of + *
> >>>>> force feedback actuators.
> >>>>> + *
> >>>>> + *
> >>>>> + * HANDLING OF UNCOMBINABLE EFFECTS
> >>>>> + *
> >>>>> + * Uncombinable effects cannot be combined together into just one
> >>>>> effect,
> >>>>> at + * least not in a clear and obvious manner. Therefore these
> >>>>> effects
> >>>>> have to + * be handled individually by ff-memless-next. Handling of
> >>>>> these
> >>>>> effects is + * left entirely to the hardware-specific driver,
> >>>>> ff-memless-next merely + * passes these effects to the
> >>>>> hardware-specific
> >>>>> driver at appropriate time. + * ff-memless-next provides the UPLOAD
> >>>>> command to notify the hardware-specific + * driver that the userspace
> >>>>> is
> >>>>> about to request playback of an uncombinable + * effect. The
> >>>>> hardware-specific driver shall take all steps needed to make + * the
> >>>>> device ready to play the effect when it receives the UPLOAD command. +
> >>>>> *
> >>>>> The actual playback shall commence when START_UNCOMB command is
> >>>>> received.
> >>>>> + * Opposite to the UPLOAD command is the ERASE command which tells +
> >>>>> *
> >>>>> the hardware-specific driver that the playback has finished and that +
> >>>>> *
> >>>>> the effect will not be restarted. STOP_UNCOMB command tells
> >>>>> + * the hardware-specific driver that the playback shall stop but the
> >>>>> device + * shall still be ready to resume the playback immediately.
> >>>>> + *
> >>>>> + * In case it is not possible to make the device ready to play an
> >>>>> uncombinable + * effect (all hardware effect slots are occupied), the
> >>>>> hardware-specific + * driver may return an error when it receives an
> >>>>> UPLOAD command. If the
> >>>> 
> >>>> This part concerns me. It seems to me that devices supporting
> >>>> "uncombinable" effects are in fact not memoryless devices and we should
> >>>> not be introducing this term here. If the goal is to work around
> >>>> limited
> >>>> number of effect slots in the devices by combining certain effects then
> >>>> it needs to be done at ff-core level as it will be potentially useful
> >>>> for all devices.
> >>> 
> >>> Force generated by a conditional effect (referred to as "uncombinable"
> >>> within ff-memless-next to make the distinction clear) depends on a
> >>> position of the device. For instance the more a device is deflected from
> >>> a neutral position the greater force FF_SPRING generates. A truly
> >>> memoryless device would have to report its position to the driver, have
> >>> it calculate the appropriate force and send it back to the device. IMHO
> >>> such a loop would require a very high USB polling rate to play
> >>> conditional effects with acceptable quality.
> >>> 
> >>> We know for a fact that at least many (all?) Logitech devices that
> >>> support
> >>> conditional effects use this "semi-memoryless" approach where
> >>> FF_CONSTANT
> >>> and FF_PERIODIC are handled in the memoryless fashion and conditional
> >>> effects are uploaded to the device (in a somewhat simplified form). The
> >>> amount of effects that can be uploaded to a device is limited which is
> >>> why ff-memless-next uses two steps (UPLOAD/ERASE and START/STOP) to
> >>> handle these effects.
> >>> 
> >>> Conditional effects - even if they are of the same type - cannot be
> >>> effectively combined into one because superposition doesn't seem to work
> >>> here so they have to be processed one by one.
> >>> 
> >>> If we ever come across a really memoryless device it should not be
> >>> particularly difficult to add another callback to ff-memless-next which
> >>> would emulate conditional effects with constant force.
> >> 
> >> Thank you for the explanation. This further solidifies for me the idea
> >> that handling of such effects that are in fact uploaded to and managed
> >> by the device should not be handled by the memoryless core but rather by
> >> the driver itself. I.e. such drivers should implement their own play(),
> >> upload(), erase(), etc, and decide whether to use a hardware slot for
> >> the effect or handle effect in memoryless fashion (if possible). We can
> >> open ff-memless to allow such drivers to use parts of memoryless
> >> handling.
> >> 
> >> Thanks.
> > 
> > To bring this to a conclusion we could go from, would this be an
> > acceptable
> > solution?
> > 
> > - Have the HW-specific driver talk directly to ff-core and reimplement
> > upload(), play(), etc.
> > - Rewrite "ff-memless-next" so that it is not a self-contained module but
> > a
> > library of functions.
> > 
> > - Have the driver either:
> >   - Upload an effect to a device directly if the device can fully manage
> >   the
> > 
> > effect by itself.
> > 
> >   - Use provided timing functions to know when an effect should start,
> >   stop,
> > 
> > restart etc...
> > 
> >   - Use provided timing AND processing functions to combine effects that
> >   can be> 
> > combined into one, calculate periodic waveforms etc?
> > 
> > I have no problem with throwing my current approach away but before I
> > start
> > working on a new one I'd like to know which way to go...
> > 
> > Thanks,
> > Michal
> 
> Hi everyone
> 
> Allow me to introduce myself. I'm Roland Bosa and I work for Logitech,
> more specifically the gaming group. I have been tasked to look into
> gaming device support for Linux and I just started following this list
> and studying the kernel code related to the Logitech Force Feedback
> devices. The 'ff-memless-next' module sounds tailor-made for our
> devices. Please let me know how I can help with its development.


Hi,

"ff-memless-next" was designed with behavior of Logitech devices in mind, 
however it was always meant as a general replacement for the current "ff-
memless". After some followup discussion it's unlikely that it will be 
mainlined in its current form. See the discussion here 
"http://www.spinics.net/lists/linux-input/msg31426.html" for more details.

We would however really appreciate some information regarding Logitech devices 
specifically. Although we have reverse engineered most of things, I believe 
there are still areas we're not entirely clear about. I realize that you'd 
probably have to take it up with the legal deparment but if someone at 
Logitech could at least take a look at what we've found so far at fill in the 
blanks it'd be most helpful.
Please note that we found numerous bugs and DirectInput specs violations in 
the Windows driver which might have impacted our understading of how the 
driver works. "ff-memless-next" and "lg4ff" tries to fix or work around these. A 
link to out-of-tree update to lg4ff which takes full advantage of "ff-memless-
next" is also provided in this thread.

Thanks for any help you can get us,
Michal
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20 18:32             ` Roland Bosa
  (?)
  (?)
@ 2014-05-20 19:39             ` simon
  2014-05-20 22:04               ` Roland Bosa
  -1 siblings, 1 reply; 83+ messages in thread
From: simon @ 2014-05-20 19:39 UTC (permalink / raw)
  To: Roland Bosa
  Cc: "Michal Malý",
	Dmitry Torokhov, linux-input, linux-kernel, jkosina, elias.vds,
	anssi.hannula, simon

> Allow me to introduce myself. I'm Roland Bosa and I work for Logitech,
> more specifically the gaming group. I have been tasked to look into
> gaming device support for Linux and I just started following this list
> and studying the kernel code related to the Logitech Force Feedback
> devices. The 'ff-memless-next' module sounds tailor-made for our
> devices. Please let me know how I can help with its development.

Hello Roland,
Let me echo Michal's welcome.

As you will see from the 'ff-memless-next' driver we are looking to extend
force-feedback support to cover more than just ConstantForce support, and
hopefully bring Linux into scope for force-feedback of AAA game quality.

Mostly this has come from a small group of people reverse engineering the
Logitech wheels, which leads to the 'tailor-made' situation. But we'd like
the systems/interfaces to be device agnostic.

As you how you can help...? As Michal said having a contact at Logitech to
whom we can ask technical questions (off list) would probably be the
greatest help. This would clear up any assumptions we have made, and help
with decisions going forward.

It may also be the case that Logitech is working with studios/game
designers to improve controller performance. If any information can be
feed 'forward' to improve the Linux driver, that would be useful too.

Many thanks,
Simon Wood.


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20  9:27           ` Michal Malý
  (?)
  (?)
@ 2014-05-20 20:16           ` simon
  2014-05-20 20:58             ` Michal Malý
  -1 siblings, 1 reply; 83+ messages in thread
From: simon @ 2014-05-20 20:16 UTC (permalink / raw)
  To: "Michal Malý"
  Cc: Dmitry Torokhov, linux-input, linux-kernel, jkosina, elias.vds,
	anssi.hannula, simon


> To bring this to a conclusion we could go from, would this be an
> acceptable
> solution?
>
> - Have the HW-specific driver talk directly to ff-core and reimplement
> upload(),
> play(), etc.
> - Rewrite "ff-memless-next" so that it is not a self-contained module but
> a
> library of functions.
> - Have the driver either:
>   - Upload an effect to a device directly if the device can fully manage
> the
> effect by itself.
>   - Use provided timing functions to know when an effect should start,
> stop,
> restart etc...
>   - Use provided timing AND processing functions to combine effects that
> can be
> combined into one, calculate periodic waveforms etc?
>
> I have no problem with throwing my current approach away but before I
> start working on a new one I'd like to know which way to go...

Hi all,
If the driver itself (hid-logitech, via hid-lg4ff for example) is more
involved in the creation/timing/management of the effects, does this mean
that we end up with code duplicated in lots of places?

Also, does this mean that the 'old' ff-memless system would remain in
kernel? If not, who will reworking each driver?



Regarding the question of emulated vs. real effects, can we extend the API
so that applications can know which effects are really supported, and
enable/disable emulation somehow?

Apologies for asking some many questions, without answering anything...
Simon


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20 20:16           ` simon
@ 2014-05-20 20:58             ` Michal Malý
  2014-05-20 21:26               ` Elias Vanderstuyft
  2014-05-20 23:45               ` simon
  0 siblings, 2 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-20 20:58 UTC (permalink / raw)
  To: simon
  Cc: Dmitry Torokhov, linux-input, linux-kernel, jkosina, elias.vds,
	anssi.hannula

On Tuesday 20 of May 2014 16:16:12 simon@mungewell.org wrote:
> > To bring this to a conclusion we could go from, would this be an
> > acceptable
> > solution?
> > 
> > - Have the HW-specific driver talk directly to ff-core and reimplement
> > upload(),
> > play(), etc.
> > - Rewrite "ff-memless-next" so that it is not a self-contained module but
> > a
> > library of functions.
> > 
> > - Have the driver either:
> >   - Upload an effect to a device directly if the device can fully manage
> > 
> > the
> > effect by itself.
> > 
> >   - Use provided timing functions to know when an effect should start,
> > 
> > stop,
> > restart etc...
> > 
> >   - Use provided timing AND processing functions to combine effects that
> > 
> > can be
> > combined into one, calculate periodic waveforms etc?
> > 
> > I have no problem with throwing my current approach away but before I
> > start working on a new one I'd like to know which way to go...
> 
> Hi all,
> If the driver itself (hid-logitech, via hid-lg4ff for example) is more
> involved in the creation/timing/management of the effects, does this mean
> that we end up with code duplicated in lots of places?

Hopefully not. I tried to work out some scheme how this would work in my head 
today and the code duplication among other drivers should be minimal. 
Basically an equivalent of "play_effects()" - implemented in the HW-specific 
driver - would by called as needed. Such a function would then use provided 
helper functions to process combinable effects and check if any uncombinable 
effects changed state. A proper rate limiting will fit into this scheme nicely 
as well.
The biggest advantage of this design is that the HW-specific driver will be 
able to either handle an effect all by itself or use the provided "helper" 
infrastructure.

In retrospect I've got agree with Dmitry that the current handling of 
(semi)memless devices is not very flexible and therefore potentially 
problematic. It'll require some extra work but we don't have to invent 
anything entirely new here, we can just rearrange what we've already done in 
MLNX and package it into a library.

> Also, does this mean that the 'old' ff-memless system would remain in
> kernel? If not, who will reworking each driver?

I suppose that the maintainers would want us to move all FF drivers to this 
architecture. If we get it right, it shouldn't require a terrible amount of 
work to port the other drivers.

> Regarding the question of emulated vs. real effects, can we extend the API
> so that applications can know which effects are really supported, and
> enable/disable emulation somehow?

I suppose that a few extra flags (FF_PERIODIC_EMULATED etc.) defined in 
"uapi/linux/input.h" should suffice.

Michal

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20 20:58             ` Michal Malý
@ 2014-05-20 21:26               ` Elias Vanderstuyft
  2014-05-22  9:48                   ` Elias Vanderstuyft
  2014-05-20 23:45               ` simon
  1 sibling, 1 reply; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-20 21:26 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Simon Wood, linux-input, linux-kernel, Jiri Kosina,
	Anssi Hannula, Michal Malý

On Tue, May 20, 2014 at 10:58 PM, Michal Malý
<madcatxster@devoid-pointer.net> wrote:
> On Tuesday 20 of May 2014 16:16:12 simon@mungewell.org wrote:
>> Regarding the question of emulated vs. real effects, can we extend the API
>> so that applications can know which effects are really supported, and
>> enable/disable emulation somehow?
>
> I suppose that a few extra flags (FF_PERIODIC_EMULATED etc.) defined in
> "uapi/linux/input.h" should suffice.

@Dmitry:
Now that we're talking about API changes, I would like to propose some
additional things in the Linux FF API:

- Possibility to *get* FF state variables, such as the value of the
current GAIN, and the current AUTOCENTER:
At the moment, only *setting* is possible.
This poses a problem when implementing proper AUTOCENTER state
recovery (upon closing a Windows program) in Wine, because the initial
AUTOCENTER state was unknown.
The same applies for GAIN.

- Introduce multiple types of GAIN, apart from the current global
GAIN, e.g. GAIN_CONSTANT and GAIN_DAMPER:
This is mainly on request of some users, especially Logitech users: on
Windows Logitech additionally supports to set the global Damper and
Spring gain.
Maybe it would be nicer to create a global gain for every supported
Linux FF effect?

Thanks,
Elias

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20 19:00               ` Michal Malý
@ 2014-05-20 21:38                 ` Roland Bosa
  -1 siblings, 0 replies; 83+ messages in thread
From: Roland Bosa @ 2014-05-20 21:38 UTC (permalink / raw)
  To: Michal Malý
  Cc: Dmitry Torokhov, linux-input, linux-kernel, jkosina, elias.vds,
	anssi.hannula, simon

On 05/20/2014 12:00 PM, Michal Malý wrote:
> Hi,
> 
> "ff-memless-next" was designed with behavior of Logitech devices in mind, 
> however it was always meant as a general replacement for the current "ff-
> memless". After some followup discussion it's unlikely that it will be 
> mainlined in its current form. See the discussion here 
> "http://www.spinics.net/lists/linux-input/msg31426.html" for more details.
> 
> We would however really appreciate some information regarding Logitech devices 
> specifically. Although we have reverse engineered most of things, I believe 
> there are still areas we're not entirely clear about. I realize that you'd 
> probably have to take it up with the legal deparment but if someone at 
> Logitech could at least take a look at what we've found so far at fill in the 
> blanks it'd be most helpful.

I'm your guy, then. Please send any questions my way and I'll dig out
the information you need - with the lawyer's blessings, if need be.

> Please note that we found numerous bugs and DirectInput specs violations in 
> the Windows driver which might have impacted our understading of how the 
> driver works. "ff-memless-next" and "lg4ff" tries to fix or work around these. A 
> link to out-of-tree update to lg4ff which takes full advantage of "ff-memless-
> next" is also provided in this thread.

lg4ff is mostly wheels, from what I saw. We should be able to fold in
the G940 (Flight System) as well as other joysticks into the same
driver. The underlying model driving the forces of our devices is
essentially the same, there are just slight improvements made over the
years to accommodate higher resolutions and more powerful micro processors.

Each device has a unique number (say 2 or 4) of force 'slots' per axis,
which is running a force calculation in firmware. Typically, one of
these slots is allocated by the host driver for a constant force, that's
streamed to the device (and comprises all constant and periodic forces
sent by the 'game' - that's the combined type of your code, as far as I
can tell). The other slot may be used for position-based effects
(springs, centering spring) as well as velocity-based effects (dampers,
friction) - that's the un-combinable type. There's a healthy amount of
code in the Windows driver that you would call 'quirks' which deals with
deciding how to allocate multiple springs and dampers to a single slot.
Sometimes, even the springs and dampers are being streamed in via the
constants force, but that requires (as you pointed out earlier) a fast
update rate and some "smartness" (I'm getting in hot territory with the
lawyers now - let me stop).

One of the key features of the design is the decoupling of the USB
updates from the force feedback IOCTL's from the 'game'. I don't think
this is currently the case with today's drivers. I was able to send
force updates to a gamepad faster than the USB update rate, which led to
some lost packets which in turn left the device in a inconsistent state
- the motors were still rumbling although they should have stopped. If
the 'ff-memless-next' driver offers this decoupling, it is a step in the
right direction, IMO.

I will try to apply the patch mentioned in this thread and see where it
leads me...

Thanks
roland


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-20 21:38                 ` Roland Bosa
  0 siblings, 0 replies; 83+ messages in thread
From: Roland Bosa @ 2014-05-20 21:38 UTC (permalink / raw)
  To: Michal Malý
  Cc: Dmitry Torokhov, linux-input, linux-kernel, jkosina, elias.vds,
	anssi.hannula, simon

On 05/20/2014 12:00 PM, Michal Malý wrote:
> Hi,
> 
> "ff-memless-next" was designed with behavior of Logitech devices in mind, 
> however it was always meant as a general replacement for the current "ff-
> memless". After some followup discussion it's unlikely that it will be 
> mainlined in its current form. See the discussion here 
> "http://www.spinics.net/lists/linux-input/msg31426.html" for more details.
> 
> We would however really appreciate some information regarding Logitech devices 
> specifically. Although we have reverse engineered most of things, I believe 
> there are still areas we're not entirely clear about. I realize that you'd 
> probably have to take it up with the legal deparment but if someone at 
> Logitech could at least take a look at what we've found so far at fill in the 
> blanks it'd be most helpful.

I'm your guy, then. Please send any questions my way and I'll dig out
the information you need - with the lawyer's blessings, if need be.

> Please note that we found numerous bugs and DirectInput specs violations in 
> the Windows driver which might have impacted our understading of how the 
> driver works. "ff-memless-next" and "lg4ff" tries to fix or work around these. A 
> link to out-of-tree update to lg4ff which takes full advantage of "ff-memless-
> next" is also provided in this thread.

lg4ff is mostly wheels, from what I saw. We should be able to fold in
the G940 (Flight System) as well as other joysticks into the same
driver. The underlying model driving the forces of our devices is
essentially the same, there are just slight improvements made over the
years to accommodate higher resolutions and more powerful micro processors.

Each device has a unique number (say 2 or 4) of force 'slots' per axis,
which is running a force calculation in firmware. Typically, one of
these slots is allocated by the host driver for a constant force, that's
streamed to the device (and comprises all constant and periodic forces
sent by the 'game' - that's the combined type of your code, as far as I
can tell). The other slot may be used for position-based effects
(springs, centering spring) as well as velocity-based effects (dampers,
friction) - that's the un-combinable type. There's a healthy amount of
code in the Windows driver that you would call 'quirks' which deals with
deciding how to allocate multiple springs and dampers to a single slot.
Sometimes, even the springs and dampers are being streamed in via the
constants force, but that requires (as you pointed out earlier) a fast
update rate and some "smartness" (I'm getting in hot territory with the
lawyers now - let me stop).

One of the key features of the design is the decoupling of the USB
updates from the force feedback IOCTL's from the 'game'. I don't think
this is currently the case with today's drivers. I was able to send
force updates to a gamepad faster than the USB update rate, which led to
some lost packets which in turn left the device in a inconsistent state
- the motors were still rumbling although they should have stopped. If
the 'ff-memless-next' driver offers this decoupling, it is a step in the
right direction, IMO.

I will try to apply the patch mentioned in this thread and see where it
leads me...

Thanks
roland

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20 19:39             ` simon
@ 2014-05-20 22:04               ` Roland Bosa
  2014-05-20 23:30                 ` simon
  0 siblings, 1 reply; 83+ messages in thread
From: Roland Bosa @ 2014-05-20 22:04 UTC (permalink / raw)
  To: simon
  Cc: Michal Malý,
	Dmitry Torokhov, linux-input, linux-kernel, jkosina, elias.vds,
	anssi.hannula

On 05/20/2014 12:39 PM, simon@mungewell.org wrote:
> hopefully bring Linux into scope for force-feedback of AAA game quality.

^ That's my objective too.

> Mostly this has come from a small group of people reverse engineering the
> Logitech wheels, which leads to the 'tailor-made' situation. But we'd like
> the systems/interfaces to be device agnostic.

I'm sure we can consolidate the support into fewer files.

> As you how you can help...? As Michal said having a contact at Logitech to
> whom we can ask technical questions (off list) would probably be the
> greatest help. This would clear up any assumptions we have made, and help
> with decisions going forward.

Please don't hesitate to contact me. ;-)

> It may also be the case that Logitech is working with studios/game
> designers to improve controller performance. If any information can be
> feed 'forward' to improve the Linux driver, that would be useful too.

IMO, there are two types of games. The "arcade" ones, which have a set
of 'canned' force effects, which play whenever an event happens in game.
And the "simulation" ones, which base the game on a physics engine. The
latter can redirect some variables of their engine to the input layer
and typically drive a constant force and maybe a spring/damper too.

For the first type, you might want to keep a centering spring active all
the time. Maybe you want to tweak the gain in the game settings. Maybe
there's a gain for the centering and one for the special effects. The
canned effects are mostly periodics with varying waveform, duration,
amplitude and envelope. That's about it.

For the second type, you don't want a centering spring. You typically
will drive a constant force effect with the engine, with some sprinkled
damping and maybe a slight spring here and there.

I would personally put more weight and effort to get the second type
properly implemented. The players of those games demand more realism and
they need the subtle force changes to drive better than the competition.
That means having a FF driver that can deal with many force updates per
second without choking and a consistent, reproducible force output at
the device, maybe even similar across varying devices. This translates
into a decoupled design for accepting force updates and sending USB
updates, as well as a device-specific layer to "calibrate" and "unify"
the force responses across different models.

Does this make sense?

thanks
roland


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20 22:04               ` Roland Bosa
@ 2014-05-20 23:30                 ` simon
  2014-05-21  1:17                   ` Roland Bosa
  0 siblings, 1 reply; 83+ messages in thread
From: simon @ 2014-05-20 23:30 UTC (permalink / raw)
  To: Roland Bosa
  Cc: simon, "Michal Malý",
	Dmitry Torokhov, linux-input, linux-kernel, jkosina, elias.vds,
	anssi.hannula


> IMO, there are two types of games. The "arcade" ones, which have a set
> of 'canned' force effects, which play whenever an event happens in game.
> And the "simulation" ones, which base the game on a physics engine. The
> latter can redirect some variables of their engine to the input layer
> and typically drive a constant force and maybe a spring/damper too.
>
> For the first type, you might want to keep a centering spring active all
> the time. Maybe you want to tweak the gain in the game settings. Maybe
> there's a gain for the centering and one for the special effects. The
> canned effects are mostly periodics with varying waveform, duration,
> amplitude and envelope. That's about it.

Sounds like these are the effect files produced by FEdit tool (from MS
DirectX SDK), and/or played back by pressing buttons when configuring the
Logitech driver on Windows ('wooden bridge', etc)...

Do you know if there is a mechanism for playing these under Linux?

Under Windows do games just send a 'file' the DirectX driver, or do they
parse them into individual effects to send?

If format is known/public it shouldn't be hard to write a userland client
to play them back. If this needs to be supported in the kernel that would
be more problematic.

> For the second type, you don't want a centering spring. You typically
> will drive a constant force effect with the engine, with some sprinkled
> damping and maybe a slight spring here and there.
>
> I would personally put more weight and effort to get the second type
> properly implemented. The players of those games demand more realism and
> they need the subtle force changes to drive better than the competition.

I would agree, these are probably more higher 'priority' but I think that
the work Elias and Michal have done already implements these fairly well.

I assume that most AAA games, would implement these through some middle
layer. I think that is probably via Steam using SDL2 haptic API, we have
been testing against SDL2's 'testhaptic'.

Do you see another path (which we should be supporting/testing)?

> That means having a FF driver that can deal with many force updates per
> second without choking and a consistent, reproducible force output at
> the device, maybe even similar across varying devices. This translates
> into a decoupled design for accepting force updates and sending USB
> updates, as well as a device-specific layer to "calibrate" and "unify"
> the force responses across different models.

There was some discussion about rate limiting the USB packets to the
wheel, and how to deal if app updates too quickly. Is there an upper limit
for the wheel itself, or is it just the USB 'pipe' which is the limiting
factor?

Simon


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20 20:58             ` Michal Malý
  2014-05-20 21:26               ` Elias Vanderstuyft
@ 2014-05-20 23:45               ` simon
  2014-05-21  0:08                 ` Michal Malý
  1 sibling, 1 reply; 83+ messages in thread
From: simon @ 2014-05-20 23:45 UTC (permalink / raw)
  To: "Michal Malý"
  Cc: simon, Dmitry Torokhov, linux-input, linux-kernel, jkosina,
	elias.vds, anssi.hannula


>> Regarding the question of emulated vs. real effects, can we extend the
>> API
>> so that applications can know which effects are really supported, and
>> enable/disable emulation somehow?
>
> I suppose that a few extra flags (FF_PERIODIC_EMULATED etc.) defined in
> "uapi/linux/input.h" should suffice.

The only problem is that we probably want to maintain backward
compatibility so that older apps still see 'PERIODIC' (even though it is
emulated).
--
#define FF_RUMBLE	0x50
#define FF_PERIODIC	0x51
#define FF_CONSTANT	0x52
#define FF_SPRING	0x53
#define FF_FRICTION	0x54
#define FF_DAMPER	0x55
#define FF_INERTIA	0x56
#define FF_RAMP		0x57
--

Do we therefore have to list extra items in our capabilities?
--
static const signed short lg4ff_wheel_effects[] = {
	FF_CONSTANT,
	FF_PERIODIC,
	FF_PERIODIC_NOT_EMULATED,
	FF_AUTOCENTER,
	-1
};
--

Simon


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20 23:45               ` simon
@ 2014-05-21  0:08                 ` Michal Malý
  0 siblings, 0 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-21  0:08 UTC (permalink / raw)
  To: simon
  Cc: Dmitry Torokhov, linux-input, linux-kernel, jkosina, elias.vds,
	anssi.hannula

On Tuesday 20 of May 2014 19:45:44 simon@mungewell.org wrote:
> >> Regarding the question of emulated vs. real effects, can we extend the
> >> API
> >> so that applications can know which effects are really supported, and
> >> enable/disable emulation somehow?
> > 
> > I suppose that a few extra flags (FF_PERIODIC_EMULATED etc.) defined in
> > "uapi/linux/input.h" should suffice.
> 
> The only problem is that we probably want to maintain backward
> compatibility so that older apps still see 'PERIODIC' (even though it is
> emulated).
> --
> #define FF_RUMBLE	0x50
> #define FF_PERIODIC	0x51
> #define FF_CONSTANT	0x52
> #define FF_SPRING	0x53
> #define FF_FRICTION	0x54
> #define FF_DAMPER	0x55
> #define FF_INERTIA	0x56
> #define FF_RAMP		0x57
> --
> 
> Do we therefore have to list extra items in our capabilities?
> --
> static const signed short lg4ff_wheel_effects[] = {
> 	FF_CONSTANT,
> 	FF_PERIODIC,
> 	FF_PERIODIC_NOT_EMULATED,
> 	FF_AUTOCENTER,
> 	-1
> };
> --
> 
> Simon

Actually I was thinking something like this:

"input.h"
#define FF_DAMPER	0x55
#define FF_INERTIA	0x56
#define FF_RAMP		0x57
+# define FF_PERIODIC_EMULATED	0x58
+# define FF_RUMBLE_EMULATED	0x59

and

in the future "library-like" reimplementation of MLNX:
#define EMULATE_PERIODIC BIT(0)
#define EMULATE_RUMBLE BIT(1)

kfflib_init(..., EMULATE_PERIODIC | EMULATE_RUMBLE);
for full emulation.

Michal



^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20 23:30                 ` simon
@ 2014-05-21  1:17                   ` Roland Bosa
  2014-05-21  2:13                     ` Michal Malý
  0 siblings, 1 reply; 83+ messages in thread
From: Roland Bosa @ 2014-05-21  1:17 UTC (permalink / raw)
  To: simon
  Cc: Michal Malý,
	Dmitry Torokhov, linux-input, linux-kernel, jkosina, elias.vds,
	anssi.hannula

On 05/20/2014 04:30 PM, simon@mungewell.org wrote:
> Sounds like these are the effect files produced by FEdit tool (from MS
> DirectX SDK), and/or played back by pressing buttons when configuring the
> Logitech driver on Windows ('wooden bridge', etc)...

Prior to the FEdit tool, there was Immersion Studio [1]. It created IFR
files, which were read using a DLL that Immersion provided. This DLL
produced arrays of DIEFFECTs, which had specific durations and start
offsets. By starting the entire array, you would get the desired effect.
Most of the original effects in the Logitech Control Panel were done
that way (by yours truly, as a matter of fact). To "sell" the effect, a
sound-effect was queued concurrently with the force effect.

(Trivia: part of the "Wooden bridge" sound effect was made by smashing a
cigarette lighter against real wooden clogs of a co-worker, then slowing
down the audio a bit.)

In 2003, all of these effects were 'hardcoded' in a series of arrays of
arrays of DI_EFFECTs. This allowed us to drop the Immersion loader DLL
and create them on the fly.

> Do you know if there is a mechanism for playing these under Linux?

I don't - I'm fairly new to the Linux platform itself.

> Under Windows do games just send a 'file' the DirectX driver, or do they
> parse them into individual effects to send?

With the IFR approach, you call a function in a library in userland with
the filename, and it returns an array of effects. With the FEdit tool,
you call a function on the IDirectInputDevice and calls a given callback
the the loaded effects.[2]

At the end of the day, you end up with a series of user-land effect
structures. It's up to the 'game' to download and start them on a given
device.

> If format is known/public it shouldn't be hard to write a userland client
> to play them back.

The file format of an IFR is probably easily deducible. There's a lot of
textual clues to parameters and the values are also written out in
string form.

I don't have a FEdit file at hand, but I suppose it will be similar.

> I assume that most AAA games, would implement these through some middle
> layer. I think that is probably via Steam using SDL2 haptic API, we have
> been testing against SDL2's 'testhaptic'.

I wasn't aware of this layer. I must read up on it. It sounds like a
simple way to access force feedback - I guess a game developer should
shed some light on this...

> Do you see another path (which we should be supporting/testing)?

Nope, not at this time.

> There was some discussion about rate limiting the USB packets to the
> wheel, and how to deal if app updates too quickly. Is there an upper limit
> for the wheel itself, or is it just the USB 'pipe' which is the limiting
> factor?

On the Windows side we send 125 reports/sec. The entire simulation loop
runs with a 8ms resolution. I assume this value was chosen for some
hardware constraints back in the days, but it has proven to be a good
compromise for simulated periodics and physics constraints.

In any case, the USB traffic should be decoupled from the app. Any force
updates should only change state in the ff-memless[-next] driver. Any
change there should trickle down to a 'slot' representation of the
device. If there's any change in the slots, the device is marked as
'dirty' and USB transfers are scheduled to send the latest state to the
physical device.

The scheduling should keep track of how many requests are in-flight and
delay writing the next output, until the previous one has completed.

Question back to the community: are there APIs in the USB layer to check
for presence of in-progress requests? Can one add a 'completion'
callback to a request, that gets invoked on completion/cancellation?

Thanks
roland

--

[1]
http://www.immersion.com/developers/index.php?option=com_content&view=article&id=423&Itemid=684

[2]
http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.idirectinputdevice8.idirectinputdevice8.enumeffectsinfile(v=vs.85).aspx


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-21  1:17                   ` Roland Bosa
@ 2014-05-21  2:13                     ` Michal Malý
  2014-05-21 10:17                         ` Nestor Lopez Casado
                                         ` (2 more replies)
  0 siblings, 3 replies; 83+ messages in thread
From: Michal Malý @ 2014-05-21  2:13 UTC (permalink / raw)
  To: Roland Bosa
  Cc: simon, Dmitry Torokhov, linux-input, linux-kernel, jkosina,
	elias.vds, anssi.hannula

On Tuesday 20 of May 2014 18:17:51 Roland Bosa wrote:
> 
> The file format of an IFR is probably easily deducible. There's a lot of
> textual clues to parameters and the values are also written out in
> string form.
> 
> I don't have a FEdit file at hand, but I suppose it will be similar.

I believe that Elias successfully reverse engineered the effect file format 
produced by FEdit. There is no support for this kind of prefabricated effects 
in the Linux FF API.

> > I assume that most AAA games, would implement these through some middle
> > layer. I think that is probably via Steam using SDL2 haptic API, we have
> > been testing against SDL2's 'testhaptic'.
> 
> I wasn't aware of this layer. I must read up on it. It sounds like a
> simple way to access force feedback - I guess a game developer should
> shed some light on this...
>
>
> > Do you see another path (which we should be supporting/testing)?
> 
> Nope, not at this time.
> 
> > There was some discussion about rate limiting the USB packets to the
> > wheel, and how to deal if app updates too quickly. Is there an upper limit
> > for the wheel itself, or is it just the USB 'pipe' which is the limiting
> > factor?
> 
> On the Windows side we send 125 reports/sec. The entire simulation loop
> runs with a 8ms resolution. I assume this value was chosen for some
> hardware constraints back in the days, but it has proven to be a good
> compromise for simulated periodics and physics constraints.

Our current code uses 8 msecs delay as well.

> In any case, the USB traffic should be decoupled from the app. Any force
> updates should only change state in the ff-memless[-next] driver. Any
> change there should trickle down to a 'slot' representation of the
> device. If there's any change in the slots, the device is marked as
> 'dirty' and USB transfers are scheduled to send the latest state to the
> physical device.
> 
> The scheduling should keep track of how many requests are in-flight and
> delay writing the next output, until the previous one has completed.

The approach I had in mind would keep track of the last effect that made it to 
the device and the last effect that arrived from userspace. This would be 
stored for each effect slot. An update would be scheduled at the desired update 
rate. The updating routine would figure out the state change between last 
update and "now", send the required data to the device and reschedule itself. 
The routine could check if there are any USB transfers still running and 
reschedule itself immediately.

> Question back to the community: are there APIs in the USB layer to check
> for presence of in-progress requests? Can one add a 'completion'
> callback to a request, that gets invoked on completion/cancellation?

For instance "usb_submit_urb()" can have a completion handler that is called 
once the transfer is done. The current code uses "hid_hw_request()" which is 
asynchronous and doesn't report anything back.

Proper decoupling of the userspace and driver is the only important thing that 
is missing from the current code.

Michal

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-21  2:13                     ` Michal Malý
@ 2014-05-21 10:17                         ` Nestor Lopez Casado
  2014-05-21 13:35                         ` Elias Vanderstuyft
  2014-05-21 14:05                         ` Elias Vanderstuyft
  2 siblings, 0 replies; 83+ messages in thread
From: Nestor Lopez Casado @ 2014-05-21 10:17 UTC (permalink / raw)
  To: Michal Malý
  Cc: Roland Bosa, Simon Wood, Dmitry Torokhov,
	open list:HID CORE LAYER, linux-kernel, Jiri Kosina,
	Elias Vanderstuyft, anssi.hannula

Elias, Simon, Michal,

It is unfortunate that we didn't get back to  you in 2013 when you
asked for help in reverse engineering, oftentimes we have conflicting
priorities and a particular request may be left laying around for some
time before being addressed. But please, don't hesitate to re-kick us
if you feel we are not being reactive.

Let me clearly say that our intentions are to disclose as much
information as possible and to help the community in having great
support to all our hardware across all platforms. We have zero problem
with being asked for information.

Many thanks,
-nestor




On Wed, May 21, 2014 at 4:13 AM, Michal Malý
<madcatxster@devoid-pointer.net> wrote:
>
> On Tuesday 20 of May 2014 18:17:51 Roland Bosa wrote:
> >
> > The file format of an IFR is probably easily deducible. There's a lot of
> > textual clues to parameters and the values are also written out in
> > string form.
> >
> > I don't have a FEdit file at hand, but I suppose it will be similar.
>
> I believe that Elias successfully reverse engineered the effect file format
> produced by FEdit. There is no support for this kind of prefabricated effects
> in the Linux FF API.
>
> > > I assume that most AAA games, would implement these through some middle
> > > layer. I think that is probably via Steam using SDL2 haptic API, we have
> > > been testing against SDL2's 'testhaptic'.
> >
> > I wasn't aware of this layer. I must read up on it. It sounds like a
> > simple way to access force feedback - I guess a game developer should
> > shed some light on this...
> >
> >
> > > Do you see another path (which we should be supporting/testing)?
> >
> > Nope, not at this time.
> >
> > > There was some discussion about rate limiting the USB packets to the
> > > wheel, and how to deal if app updates too quickly. Is there an upper limit
> > > for the wheel itself, or is it just the USB 'pipe' which is the limiting
> > > factor?
> >
> > On the Windows side we send 125 reports/sec. The entire simulation loop
> > runs with a 8ms resolution. I assume this value was chosen for some
> > hardware constraints back in the days, but it has proven to be a good
> > compromise for simulated periodics and physics constraints.
>
> Our current code uses 8 msecs delay as well.
>
> > In any case, the USB traffic should be decoupled from the app. Any force
> > updates should only change state in the ff-memless[-next] driver. Any
> > change there should trickle down to a 'slot' representation of the
> > device. If there's any change in the slots, the device is marked as
> > 'dirty' and USB transfers are scheduled to send the latest state to the
> > physical device.
> >
> > The scheduling should keep track of how many requests are in-flight and
> > delay writing the next output, until the previous one has completed.
>
> The approach I had in mind would keep track of the last effect that made it to
> the device and the last effect that arrived from userspace. This would be
> stored for each effect slot. An update would be scheduled at the desired update
> rate. The updating routine would figure out the state change between last
> update and "now", send the required data to the device and reschedule itself.
> The routine could check if there are any USB transfers still running and
> reschedule itself immediately.
>
> > Question back to the community: are there APIs in the USB layer to check
> > for presence of in-progress requests? Can one add a 'completion'
> > callback to a request, that gets invoked on completion/cancellation?
>
> For instance "usb_submit_urb()" can have a completion handler that is called
> once the transfer is done. The current code uses "hid_hw_request()" which is
> asynchronous and doesn't report anything back.
>
> Proper decoupling of the userspace and driver is the only important thing that
> is missing from the current code.
>
> Michal
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-21 10:17                         ` Nestor Lopez Casado
  0 siblings, 0 replies; 83+ messages in thread
From: Nestor Lopez Casado @ 2014-05-21 10:17 UTC (permalink / raw)
  To: Michal Malý
  Cc: Roland Bosa, Simon Wood, Dmitry Torokhov,
	open list:HID CORE LAYER, linux-kernel, Jiri Kosina,
	Elias Vanderstuyft, anssi.hannula

Elias, Simon, Michal,

It is unfortunate that we didn't get back to  you in 2013 when you
asked for help in reverse engineering, oftentimes we have conflicting
priorities and a particular request may be left laying around for some
time before being addressed. But please, don't hesitate to re-kick us
if you feel we are not being reactive.

Let me clearly say that our intentions are to disclose as much
information as possible and to help the community in having great
support to all our hardware across all platforms. We have zero problem
with being asked for information.

Many thanks,
-nestor




On Wed, May 21, 2014 at 4:13 AM, Michal Malý
<madcatxster@devoid-pointer.net> wrote:
>
> On Tuesday 20 of May 2014 18:17:51 Roland Bosa wrote:
> >
> > The file format of an IFR is probably easily deducible. There's a lot of
> > textual clues to parameters and the values are also written out in
> > string form.
> >
> > I don't have a FEdit file at hand, but I suppose it will be similar.
>
> I believe that Elias successfully reverse engineered the effect file format
> produced by FEdit. There is no support for this kind of prefabricated effects
> in the Linux FF API.
>
> > > I assume that most AAA games, would implement these through some middle
> > > layer. I think that is probably via Steam using SDL2 haptic API, we have
> > > been testing against SDL2's 'testhaptic'.
> >
> > I wasn't aware of this layer. I must read up on it. It sounds like a
> > simple way to access force feedback - I guess a game developer should
> > shed some light on this...
> >
> >
> > > Do you see another path (which we should be supporting/testing)?
> >
> > Nope, not at this time.
> >
> > > There was some discussion about rate limiting the USB packets to the
> > > wheel, and how to deal if app updates too quickly. Is there an upper limit
> > > for the wheel itself, or is it just the USB 'pipe' which is the limiting
> > > factor?
> >
> > On the Windows side we send 125 reports/sec. The entire simulation loop
> > runs with a 8ms resolution. I assume this value was chosen for some
> > hardware constraints back in the days, but it has proven to be a good
> > compromise for simulated periodics and physics constraints.
>
> Our current code uses 8 msecs delay as well.
>
> > In any case, the USB traffic should be decoupled from the app. Any force
> > updates should only change state in the ff-memless[-next] driver. Any
> > change there should trickle down to a 'slot' representation of the
> > device. If there's any change in the slots, the device is marked as
> > 'dirty' and USB transfers are scheduled to send the latest state to the
> > physical device.
> >
> > The scheduling should keep track of how many requests are in-flight and
> > delay writing the next output, until the previous one has completed.
>
> The approach I had in mind would keep track of the last effect that made it to
> the device and the last effect that arrived from userspace. This would be
> stored for each effect slot. An update would be scheduled at the desired update
> rate. The updating routine would figure out the state change between last
> update and "now", send the required data to the device and reschedule itself.
> The routine could check if there are any USB transfers still running and
> reschedule itself immediately.
>
> > Question back to the community: are there APIs in the USB layer to check
> > for presence of in-progress requests? Can one add a 'completion'
> > callback to a request, that gets invoked on completion/cancellation?
>
> For instance "usb_submit_urb()" can have a completion handler that is called
> once the transfer is done. The current code uses "hid_hw_request()" which is
> asynchronous and doesn't report anything back.
>
> Proper decoupling of the userspace and driver is the only important thing that
> is missing from the current code.
>
> Michal
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-21  2:13                     ` Michal Malý
@ 2014-05-21 13:35                         ` Elias Vanderstuyft
  2014-05-21 13:35                         ` Elias Vanderstuyft
  2014-05-21 14:05                         ` Elias Vanderstuyft
  2 siblings, 0 replies; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-21 13:35 UTC (permalink / raw)
  To: Michal Malý
  Cc: Roland Bosa, Simon Wood, Dmitry Torokhov, linux-input,
	linux-kernel, Jiri Kosina, Anssi Hannula

On Wed, May 21, 2014 at 4:13 AM, Michal Malý
<madcatxster@devoid-pointer.net> wrote:
> On Tuesday 20 of May 2014 18:17:51 Roland Bosa wrote:
>> In any case, the USB traffic should be decoupled from the app. Any force
>> updates should only change state in the ff-memless[-next] driver. Any
>> change there should trickle down to a 'slot' representation of the
>> device. If there's any change in the slots, the device is marked as
>> 'dirty' and USB transfers are scheduled to send the latest state to the
>> physical device.
>>
>> The scheduling should keep track of how many requests are in-flight and
>> delay writing the next output, until the previous one has completed.
>
> The approach I had in mind would keep track of the last effect that made it to
> the device and the last effect that arrived from userspace. This would be
> stored for each effect slot. An update would be scheduled at the desired update
> rate. The updating routine would figure out the state change between last
> update and "now", send the required data to the device and reschedule itself.
> The routine could check if there are any USB transfers still running and
> reschedule itself immediately.

I've got another/(a more detailed?) idea about this:
We can use round-robin scheduling for the slots, and use a priority
queue for e.g. LED data or data that doesn't fit in the
slot-methodology and doesn't permit in-between packets to be lost.
And I believe that ff-memless-next should generate a periodic force
with a constant/defined sample-period/update-rate, while the layer
between ff-memless-next and USB transfers, i.e. the round-robin and
priority queue scheduler, should send the dirty slots as soon as
possible, i.e. when the USB pipe is clear or when some custom
#maxAllowedPacketsInUsbBuffer (optimized for somewhere between latency
and throughput) is not exceeded by the kernel USB queue.
Now the question is, can we query the state of the kernel USB queue?

Optionally, this method of packet scheduling could be used by other
kernel drivers (even non-FF drivers) as well, and can further increase
flexibility of ff-memless-next and the HW-specific drivers, and it
will reduce code duplication.

>> Question back to the community: are there APIs in the USB layer to check
>> for presence of in-progress requests? Can one add a 'completion'
>> callback to a request, that gets invoked on completion/cancellation?
>
> For instance "usb_submit_urb()" can have a completion handler that is called
> once the transfer is done. The current code uses "hid_hw_request()" which is
> asynchronous and doesn't report anything back.
>
> Proper decoupling of the userspace and driver is the only important thing that
> is missing from the current code.

Also dynamic updating of periodic effects is not yet supported in
"ff-memless-next".
This can also be important in simulation games (=second type), e.g.
the "rFactor" game.

Elias

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-21 13:35                         ` Elias Vanderstuyft
  0 siblings, 0 replies; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-21 13:35 UTC (permalink / raw)
  To: Michal Malý
  Cc: Roland Bosa, Simon Wood, Dmitry Torokhov, linux-input,
	linux-kernel, Jiri Kosina, Anssi Hannula

On Wed, May 21, 2014 at 4:13 AM, Michal Malý
<madcatxster@devoid-pointer.net> wrote:
> On Tuesday 20 of May 2014 18:17:51 Roland Bosa wrote:
>> In any case, the USB traffic should be decoupled from the app. Any force
>> updates should only change state in the ff-memless[-next] driver. Any
>> change there should trickle down to a 'slot' representation of the
>> device. If there's any change in the slots, the device is marked as
>> 'dirty' and USB transfers are scheduled to send the latest state to the
>> physical device.
>>
>> The scheduling should keep track of how many requests are in-flight and
>> delay writing the next output, until the previous one has completed.
>
> The approach I had in mind would keep track of the last effect that made it to
> the device and the last effect that arrived from userspace. This would be
> stored for each effect slot. An update would be scheduled at the desired update
> rate. The updating routine would figure out the state change between last
> update and "now", send the required data to the device and reschedule itself.
> The routine could check if there are any USB transfers still running and
> reschedule itself immediately.

I've got another/(a more detailed?) idea about this:
We can use round-robin scheduling for the slots, and use a priority
queue for e.g. LED data or data that doesn't fit in the
slot-methodology and doesn't permit in-between packets to be lost.
And I believe that ff-memless-next should generate a periodic force
with a constant/defined sample-period/update-rate, while the layer
between ff-memless-next and USB transfers, i.e. the round-robin and
priority queue scheduler, should send the dirty slots as soon as
possible, i.e. when the USB pipe is clear or when some custom
#maxAllowedPacketsInUsbBuffer (optimized for somewhere between latency
and throughput) is not exceeded by the kernel USB queue.
Now the question is, can we query the state of the kernel USB queue?

Optionally, this method of packet scheduling could be used by other
kernel drivers (even non-FF drivers) as well, and can further increase
flexibility of ff-memless-next and the HW-specific drivers, and it
will reduce code duplication.

>> Question back to the community: are there APIs in the USB layer to check
>> for presence of in-progress requests? Can one add a 'completion'
>> callback to a request, that gets invoked on completion/cancellation?
>
> For instance "usb_submit_urb()" can have a completion handler that is called
> once the transfer is done. The current code uses "hid_hw_request()" which is
> asynchronous and doesn't report anything back.
>
> Proper decoupling of the userspace and driver is the only important thing that
> is missing from the current code.

Also dynamic updating of periodic effects is not yet supported in
"ff-memless-next".
This can also be important in simulation games (=second type), e.g.
the "rFactor" game.

Elias
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-21  2:13                     ` Michal Malý
@ 2014-05-21 14:05                         ` Elias Vanderstuyft
  2014-05-21 13:35                         ` Elias Vanderstuyft
  2014-05-21 14:05                         ` Elias Vanderstuyft
  2 siblings, 0 replies; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-21 14:05 UTC (permalink / raw)
  To: Simon Wood
  Cc: Roland Bosa, Michal Malý,
	Dmitry Torokhov, linux-input, linux-kernel, Jiri Kosina,
	Anssi Hannula

On Wed, May 21, 2014 at 4:13 AM, Michal Malý
<madcatxster@devoid-pointer.net> wrote:
> On Tuesday 20 of May 2014 18:17:51 Roland Bosa wrote:
>>
>> The file format of an IFR is probably easily deducible. There's a lot of
>> textual clues to parameters and the values are also written out in
>> string form.
>>
>> I don't have a FEdit file at hand, but I suppose it will be similar.
>
> I believe that Elias successfully reverse engineered the effect file format
> produced by FEdit. There is no support for this kind of prefabricated effects
> in the Linux FF API.

Of course I did, Simon, what did you expect? :P
Here you are:
http://www.winehq.org/pipermail/wine-devel/2014-February/103108.html
It contains a description of the DInput effect file format (.ffe), a
working Python script to load these files and dump their contents, as
well as a bunch of example files and tests/results.
The FFE file format is binary RIFF, if you're interested.

But I prefer not to include such things in the Linux kernel, I believe
this is the task of the userspace application.
I'm planning to implement this function into Wine, but maybe SDL would
benefit from similar functionality too (using an open file-format
instead, of course)?
Anyway, this is off-topic.

Elias

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-21 14:05                         ` Elias Vanderstuyft
  0 siblings, 0 replies; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-21 14:05 UTC (permalink / raw)
  To: Simon Wood
  Cc: Roland Bosa, Michal Malý,
	Dmitry Torokhov, linux-input, linux-kernel, Jiri Kosina,
	Anssi Hannula

On Wed, May 21, 2014 at 4:13 AM, Michal Malý
<madcatxster@devoid-pointer.net> wrote:
> On Tuesday 20 of May 2014 18:17:51 Roland Bosa wrote:
>>
>> The file format of an IFR is probably easily deducible. There's a lot of
>> textual clues to parameters and the values are also written out in
>> string form.
>>
>> I don't have a FEdit file at hand, but I suppose it will be similar.
>
> I believe that Elias successfully reverse engineered the effect file format
> produced by FEdit. There is no support for this kind of prefabricated effects
> in the Linux FF API.

Of course I did, Simon, what did you expect? :P
Here you are:
http://www.winehq.org/pipermail/wine-devel/2014-February/103108.html
It contains a description of the DInput effect file format (.ffe), a
working Python script to load these files and dump their contents, as
well as a bunch of example files and tests/results.
The FFE file format is binary RIFF, if you're interested.

But I prefer not to include such things in the Linux kernel, I believe
this is the task of the userspace application.
I'm planning to implement this function into Wine, but maybe SDL would
benefit from similar functionality too (using an open file-format
instead, of course)?
Anyway, this is off-topic.

Elias
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-21 10:17                         ` Nestor Lopez Casado
  (?)
@ 2014-05-21 14:08                         ` Elias Vanderstuyft
  -1 siblings, 0 replies; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-21 14:08 UTC (permalink / raw)
  To: Nestor Lopez Casado
  Cc: Michal Malý,
	Roland Bosa, Simon Wood, Dmitry Torokhov,
	open list:HID CORE LAYER, linux-kernel, Jiri Kosina,
	Anssi Hannula

On Wed, May 21, 2014 at 12:17 PM, Nestor Lopez Casado
<nlopezcasad@logitech.com> wrote:
> Elias, Simon, Michal,
>
> It is unfortunate that we didn't get back to  you in 2013 when you
> asked for help in reverse engineering, oftentimes we have conflicting
> priorities and a particular request may be left laying around for some
> time before being addressed. But please, don't hesitate to re-kick us
> if you feel we are not being reactive.
>
> Let me clearly say that our intentions are to disclose as much
> information as possible and to help the community in having great
> support to all our hardware across all platforms. We have zero problem
> with being asked for information.

OK, thanks for your reply.
Don't worry, I understand ;)

Elias

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-21 13:35                         ` Elias Vanderstuyft
@ 2014-05-21 14:22                           ` Elias Vanderstuyft
  -1 siblings, 0 replies; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-21 14:22 UTC (permalink / raw)
  To: Michal Malý, Roland Bosa
  Cc: Simon Wood, Dmitry Torokhov, open list:HID CORE LAYER,
	linux-kernel, Jiri Kosina, Anssi Hannula

On Wed, May 21, 2014 at 3:35 PM, Elias Vanderstuyft <elias.vds@gmail.com> wrote:
> On Wed, May 21, 2014 at 4:13 AM, Michal Malý
> <madcatxster@devoid-pointer.net> wrote:
>> Proper decoupling of the userspace and driver is the only important thing that
>> is missing from the current code.
>
> Also dynamic updating of periodic effects is not yet supported in
> "ff-memless-next".
> This can also be important in simulation games (=second type), e.g.
> the "rFactor" game.

Oh, and I forgot some other things that need to be mentioned:
- Trigger-button is not yet implemented, as far as I know
- Custom force effects, in Linux it is called FF_CUSTOM. (however I
think we can neglect this effect for now)
- Infinite iterations in the Linux FF API is not supported.

@Roland:
Now I understand why Logitech Windows drivers have bugs in the
periodic implementation of the DInput specs:
Logitech seems to have based their drivers on the Immersion Studio
visualisation of the to-be-generated periodic effects, and these
visualisations are not conform with the DInput specs of MSDN.
Take a quick look at the MSDN specs for e.g. SawtoothUp:
http://msdn.microsoft.com/en-us/library/windows/desktop/ee418719%28v=vs.85%29.aspx
Now open the Immersion Studio (demo) and create a standard SawtoothUp
effect, and look at the visualisation... It's wrong, right?
Same applies for SawtoothDown and Triangle.

Elias

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-21 14:22                           ` Elias Vanderstuyft
  0 siblings, 0 replies; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-21 14:22 UTC (permalink / raw)
  To: Michal Malý, Roland Bosa
  Cc: Simon Wood, Dmitry Torokhov, open list:HID CORE LAYER,
	linux-kernel, Jiri Kosina, Anssi Hannula

On Wed, May 21, 2014 at 3:35 PM, Elias Vanderstuyft <elias.vds@gmail.com> wrote:
> On Wed, May 21, 2014 at 4:13 AM, Michal Malý
> <madcatxster@devoid-pointer.net> wrote:
>> Proper decoupling of the userspace and driver is the only important thing that
>> is missing from the current code.
>
> Also dynamic updating of periodic effects is not yet supported in
> "ff-memless-next".
> This can also be important in simulation games (=second type), e.g.
> the "rFactor" game.

Oh, and I forgot some other things that need to be mentioned:
- Trigger-button is not yet implemented, as far as I know
- Custom force effects, in Linux it is called FF_CUSTOM. (however I
think we can neglect this effect for now)
- Infinite iterations in the Linux FF API is not supported.

@Roland:
Now I understand why Logitech Windows drivers have bugs in the
periodic implementation of the DInput specs:
Logitech seems to have based their drivers on the Immersion Studio
visualisation of the to-be-generated periodic effects, and these
visualisations are not conform with the DInput specs of MSDN.
Take a quick look at the MSDN specs for e.g. SawtoothUp:
http://msdn.microsoft.com/en-us/library/windows/desktop/ee418719%28v=vs.85%29.aspx
Now open the Immersion Studio (demo) and create a standard SawtoothUp
effect, and look at the visualisation... It's wrong, right?
Same applies for SawtoothDown and Triangle.

Elias
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20 21:38                 ` Roland Bosa
@ 2014-05-21 14:53                   ` Elias Vanderstuyft
  -1 siblings, 0 replies; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-21 14:53 UTC (permalink / raw)
  To: Roland Bosa
  Cc: Michal Malý,
	Dmitry Torokhov, open list:HID CORE LAYER, linux-kernel,
	Jiri Kosina, Anssi Hannula, Simon Wood

On Tue, May 20, 2014 at 11:38 PM, Roland Bosa <rbosa@logitech.com> wrote:
> On 05/20/2014 12:00 PM, Michal Malı wrote:
> There's a healthy amount of
> code in the Windows driver that you would call 'quirks' which deals with
> deciding how to allocate multiple springs and dampers to a single slot.

But 2 Spring effects with different offsets and non-zero effective
force can't be combined into a single slot (without streaming them
with Constant force), right?

> Sometimes, even the springs and dampers are being streamed in via the
> constants force, but that requires (as you pointed out earlier) a fast
> update rate and some "smartness"

OK, is this method used when all force-slots are already full?
Or is it only used for some specific (restricted) devices, such as
devices with only 2 force slots?

And in this case, is the position of the device monitored at a speed
higher than 8ms, in order to keep the feedback-loop stable?

Thanks,
Elias

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-21 14:53                   ` Elias Vanderstuyft
  0 siblings, 0 replies; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-21 14:53 UTC (permalink / raw)
  To: Roland Bosa
  Cc: Michal Malý,
	Dmitry Torokhov, open list:HID CORE LAYER, linux-kernel,
	Jiri Kosina, Anssi Hannula, Simon Wood

On Tue, May 20, 2014 at 11:38 PM, Roland Bosa <rbosa@logitech.com> wrote:
> On 05/20/2014 12:00 PM, Michal Malı wrote:
> There's a healthy amount of
> code in the Windows driver that you would call 'quirks' which deals with
> deciding how to allocate multiple springs and dampers to a single slot.

But 2 Spring effects with different offsets and non-zero effective
force can't be combined into a single slot (without streaming them
with Constant force), right?

> Sometimes, even the springs and dampers are being streamed in via the
> constants force, but that requires (as you pointed out earlier) a fast
> update rate and some "smartness"

OK, is this method used when all force-slots are already full?
Or is it only used for some specific (restricted) devices, such as
devices with only 2 force slots?

And in this case, is the position of the device monitored at a speed
higher than 8ms, in order to keep the feedback-loop stable?

Thanks,
Elias
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
       [not found]                   ` <537D28E3.3000401@logitech.com>
@ 2014-05-21 22:42                     ` simon
  0 siblings, 0 replies; 83+ messages in thread
From: simon @ 2014-05-21 22:42 UTC (permalink / raw)
  To: Roland Bosa
  Cc: Elias Vanderstuyft, "Michal Malý",
	Dmitry Torokhov, HID CORE LAYER, linux-kernel, Jiri Kosina,
	Anssi Hannula, Simon Wood


>> But 2 Spring effects with different offsets and non-zero effective
>> force can't be combined into a single slot (without streaming them
>> with Constant force), right?
>
> Yes - you cannot *download* two springs to a single slot. You need to
> choose (allocate) one of them. The winner gets the slot. The losers go
> somewhere else (they might get discarded, they might get simulated).

In reverse engineering, I thought that a single slot could hold two
rotational angles/points (at which a springs would start) and that that
the direction could be set so that both acting in the same direction with
different forces.
--
    Byte 2 - 0x_1
    Byte 3 - Clockwise angle, nominally on left (0x00..0xFF - 0x00 is
fully left)
    Byte 4 - Anti-Clockwise angle, nominally on right(0x00..0xFF)
    Byte 5 - L/R Proportion force, upper nibble clockwise + lower nibble
anti-clockwise (each 0x0..0xF)
    Byte 6 - Reverse Direction; upper nibble clockwise + lower nibble
anti-clockwise (each 0x0..0x1)
    Byte 7 - Force (0x00..0xFF)
--

Ie. turning from fully left (00) with no force, you could hit a 'weak'
spring (at 40) and then a 'stronger' spring (at 80).

So to some degree you /can/ merge springs... but I'm not saying that you
should :-).
Simon


^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
  2014-05-20 21:26               ` Elias Vanderstuyft
@ 2014-05-22  9:48                   ` Elias Vanderstuyft
  0 siblings, 0 replies; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-22  9:48 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Simon Wood, open list:HID CORE LAYER, linux-kernel, Jiri Kosina,
	Anssi Hannula, Michal Malý,
	Roland Bosa

On Tue, May 20, 2014 at 11:26 PM, Elias Vanderstuyft
<elias.vds@gmail.com> wrote:
> On Tue, May 20, 2014 at 10:58 PM, Michal Malý
> <madcatxster@devoid-pointer.net> wrote:
>> On Tuesday 20 of May 2014 16:16:12 simon@mungewell.org wrote:
>>> Regarding the question of emulated vs. real effects, can we extend the API
>>> so that applications can know which effects are really supported, and
>>> enable/disable emulation somehow?
>>
>> I suppose that a few extra flags (FF_PERIODIC_EMULATED etc.) defined in
>> "uapi/linux/input.h" should suffice.
>
> @Dmitry:
> Now that we're talking about API changes, I would like to propose some
> additional things in the Linux FF API:
>
> - Possibility to *get* FF state variables, such as the value of the
> current GAIN, and the current AUTOCENTER:
> At the moment, only *setting* is possible.
> This poses a problem when implementing proper AUTOCENTER state
> recovery (upon closing a Windows program) in Wine, because the initial
> AUTOCENTER state was unknown.
> The same applies for GAIN.
>
> - Introduce multiple types of GAIN, apart from the current global
> GAIN, e.g. GAIN_CONSTANT and GAIN_DAMPER:
> This is mainly on request of some users, especially Logitech users: on
> Windows Logitech additionally supports to set the global Damper and
> Spring gain.
> Maybe it would be nicer to create a global gain for every supported
> Linux FF effect?

And Roland Bosa (from Logitech) also agrees with the following
addition/modification to the Linux FF API:

- Infinite iterations, or in Linux FF terms: playback of an effect
with an event count of infinity.
Right now, an event count of zero stops the effect, while the maximum
count is INT_MAX.
An event count value of '-1' can be used to indicate infinity.
(However note that it is somewhat inconsistent with the infinite
effect "duration" we already support: in that case '0' is used)
Is this feasible? (in terms of backward compatibility)

Thanks,
Elias

^ permalink raw reply	[flat|nested] 83+ messages in thread

* Re: [PATCH v4 01/24] input: Add ff-memless-next module
@ 2014-05-22  9:48                   ` Elias Vanderstuyft
  0 siblings, 0 replies; 83+ messages in thread
From: Elias Vanderstuyft @ 2014-05-22  9:48 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Simon Wood, open list:HID CORE LAYER, linux-kernel, Jiri Kosina,
	Anssi Hannula, Michal Malý,
	Roland Bosa

On Tue, May 20, 2014 at 11:26 PM, Elias Vanderstuyft
<elias.vds@gmail.com> wrote:
> On Tue, May 20, 2014 at 10:58 PM, Michal Malý
> <madcatxster@devoid-pointer.net> wrote:
>> On Tuesday 20 of May 2014 16:16:12 simon@mungewell.org wrote:
>>> Regarding the question of emulated vs. real effects, can we extend the API
>>> so that applications can know which effects are really supported, and
>>> enable/disable emulation somehow?
>>
>> I suppose that a few extra flags (FF_PERIODIC_EMULATED etc.) defined in
>> "uapi/linux/input.h" should suffice.
>
> @Dmitry:
> Now that we're talking about API changes, I would like to propose some
> additional things in the Linux FF API:
>
> - Possibility to *get* FF state variables, such as the value of the
> current GAIN, and the current AUTOCENTER:
> At the moment, only *setting* is possible.
> This poses a problem when implementing proper AUTOCENTER state
> recovery (upon closing a Windows program) in Wine, because the initial
> AUTOCENTER state was unknown.
> The same applies for GAIN.
>
> - Introduce multiple types of GAIN, apart from the current global
> GAIN, e.g. GAIN_CONSTANT and GAIN_DAMPER:
> This is mainly on request of some users, especially Logitech users: on
> Windows Logitech additionally supports to set the global Damper and
> Spring gain.
> Maybe it would be nicer to create a global gain for every supported
> Linux FF effect?

And Roland Bosa (from Logitech) also agrees with the following
addition/modification to the Linux FF API:

- Infinite iterations, or in Linux FF terms: playback of an effect
with an event count of infinity.
Right now, an event count of zero stops the effect, while the maximum
count is INT_MAX.
An event count value of '-1' can be used to indicate infinity.
(However note that it is somewhat inconsistent with the infinite
effect "duration" we already support: in that case '0' is used)
Is this feasible? (in terms of backward compatibility)

Thanks,
Elias
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 83+ messages in thread

end of thread, other threads:[~2014-05-22  9:48 UTC | newest]

Thread overview: 83+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-26 15:01 [PATCH v4 00/24] input: Introduce ff-memless-next as an improved replacement for ff-memless Michal Malý
2014-04-26 15:01 ` Michal Malý
2014-04-26 15:02 ` [PATCH v4 01/24] input: Add ff-memless-next module Michal Malý
2014-04-26 15:02   ` Michal Malý
2014-05-14  6:38   ` Dmitry Torokhov
2014-05-14  6:38     ` Dmitry Torokhov
2014-05-14  8:35     ` Michal Malý
2014-05-14  8:35       ` Michal Malý
2014-05-14 11:26       ` Elias Vanderstuyft
2014-05-14 15:11       ` simon
2014-05-14 17:58         ` Dmitry Torokhov
2014-05-14 18:05       ` Dmitry Torokhov
2014-05-14 18:05         ` Dmitry Torokhov
2014-05-14 19:38         ` Michal Malý
2014-05-14 19:38           ` Michal Malý
2014-05-20  9:27         ` Michal Malý
2014-05-20  9:27           ` Michal Malý
2014-05-20 18:32           ` Roland Bosa
2014-05-20 18:32             ` Roland Bosa
2014-05-20 19:00             ` Michal Malý
2014-05-20 19:00               ` Michal Malý
2014-05-20 21:38               ` Roland Bosa
2014-05-20 21:38                 ` Roland Bosa
2014-05-21 14:53                 ` Elias Vanderstuyft
2014-05-21 14:53                   ` Elias Vanderstuyft
     [not found]                   ` <537D28E3.3000401@logitech.com>
2014-05-21 22:42                     ` simon
2014-05-20 19:39             ` simon
2014-05-20 22:04               ` Roland Bosa
2014-05-20 23:30                 ` simon
2014-05-21  1:17                   ` Roland Bosa
2014-05-21  2:13                     ` Michal Malý
2014-05-21 10:17                       ` Nestor Lopez Casado
2014-05-21 10:17                         ` Nestor Lopez Casado
2014-05-21 14:08                         ` Elias Vanderstuyft
2014-05-21 13:35                       ` Elias Vanderstuyft
2014-05-21 13:35                         ` Elias Vanderstuyft
2014-05-21 14:22                         ` Elias Vanderstuyft
2014-05-21 14:22                           ` Elias Vanderstuyft
2014-05-21 14:05                       ` Elias Vanderstuyft
2014-05-21 14:05                         ` Elias Vanderstuyft
2014-05-20 20:16           ` simon
2014-05-20 20:58             ` Michal Malý
2014-05-20 21:26               ` Elias Vanderstuyft
2014-05-22  9:48                 ` Elias Vanderstuyft
2014-05-22  9:48                   ` Elias Vanderstuyft
2014-05-20 23:45               ` simon
2014-05-21  0:08                 ` Michal Malý
2014-05-14 18:14   ` Dmitry Torokhov
2014-05-14 19:40     ` Michal Malý
2014-05-14 19:40       ` Michal Malý
2014-04-26 15:02 ` [PATCH v4 02/24] input: Port arizona-haptics to ff-memless-next Michal Malý
2014-04-26 15:02 ` [PATCH v4 03/24] input: Port twl4030-vibra " Michal Malý
2014-04-26 15:02 ` [PATCH v4 04/24] input: Port twl6040-vibra " Michal Malý
2014-04-26 15:02 ` [PATCH v4 05/24] input: Port max8997_haptic " Michal Malý
2014-04-26 15:02 ` [PATCH v4 06/24] input: Port pm8xxx-vibrator " Michal Malý
2014-04-26 15:02 ` [PATCH v4 07/24] hid: Port hid-axff " Michal Malý
2014-04-26 15:02   ` Michal Malý
2014-04-26 15:02 ` [PATCH v4 08/24] hid: Port hid-emsff " Michal Malý
2014-04-26 15:02 ` [PATCH v4 09/24] hid: Port hid-dr " Michal Malý
2014-04-26 15:02   ` Michal Malý
2014-04-26 15:02 ` [PATCH v4 10/24] hid: Port hid-gaff " Michal Malý
2014-04-26 15:02 ` [PATCH v4 11/24] hid: Port hid-holtekff " Michal Malý
2014-04-26 15:02 ` [PATCH v4 12/24] hid: Port hid-lgff " Michal Malý
2014-04-26 15:02 ` [PATCH v4 13/24] hid: Port hid-lg3ff " Michal Malý
2014-04-26 15:02 ` [PATCH v4 14/24] hid: Port hid-pl " Michal Malý
2014-04-26 15:02 ` [PATCH v4 15/24] hid: Port hid-sjoy " Michal Malý
2014-04-26 15:02 ` [PATCH v4 16/24] hid: Port hid-sony " Michal Malý
2014-04-26 15:02 ` [PATCH v4 17/24] hid: Port hid-tmff " Michal Malý
2014-04-26 15:02 ` [PATCH v4 18/24] hid: Port hid-wiimote-modules " Michal Malý
2014-04-26 15:02 ` [PATCH v4 19/24] hid: Port hid-zpff " Michal Malý
2014-04-26 15:02 ` [PATCH v4 20/24] input: Port gamecon " Michal Malý
2014-04-26 15:02 ` [PATCH v4 21/24] input: Port xpad " Michal Malý
2014-04-26 15:02 ` [PATCH v4 22/24] hid: Port hid-lg2ff " Michal Malý
2014-04-26 15:02 ` [PATCH v4 23/24] hid: Port hid-lg4ff " Michal Malý
2014-04-29 16:05   ` simon
2014-04-29 16:05     ` simon
2014-04-26 15:02 ` [PATCH v4 24/24] input: Replace ff-memless with ff-memless-next Michal Malý
2014-04-29 15:59 ` [PATCH v4 00/24] input: Introduce ff-memless-next as an improved replacement for ff-memless simon
2014-04-29 15:59   ` simon
2014-05-12  9:14 ` Jiri Kosina
2014-05-12  9:14   ` Jiri Kosina
2014-05-12  9:26   ` Michal Malý
2014-05-12  9:26     ` Michal Malý

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.