*y = (level * fixp_sin16(degrees)) >> FRAC_16;
}
-static bool ffpl_is_combinable(const struct ff_effect *eff)
+static bool ffpl_process_memless(const struct klgd_plugin_private *priv, const struct ff_effect *eff)
{
- /* TODO: Proper decision of what is a combinable effect */
switch (eff->type) {
case FF_CONSTANT:
+ return priv->memless_mode;
case FF_PERIODIC:
+ return priv->memless_periodic;
case FF_RAMP:
- return true;
+ return priv->memless_ramp;
default:
return false;
}
static bool ffpl_is_effect_valid(const struct ff_effect *ueff)
{
const u16 length = ueff->replay.length;
+ const struct ff_envelope *env = ffpl_get_envelope(ueff);
/* Periodic effects must have a non-zero period */
if (ueff->type == FF_PERIODIC) {
if (ueff->type == FF_RAMP && !length)
return false;
- if (ffpl_is_combinable(ueff)) {
+ if (env) {
int fade_from;
- const struct ff_envelope *env = ffpl_get_envelope(ueff);
-
- if (!env) {
- printk(KERN_ERR "KLGDFF: NULL envelope in validity check. This cannot happen!\n");
- return false;
- }
/* Infinite effects cannot fade */
if (!length && env->fade_length > 0)
if (cmd == FFPL_SRT_TO_EMP)
eff->uploaded_to_device = false;
eff->state = FFPL_UPLOADED;
+
+
+ /* Report back that the effect has stopped */
+ if (eff->trigger == FFPL_TRIG_STOP)
+ input_report_ff_status(dev, eff->active.id, FF_STATUS_STOPPED);
+
return 0;
}
eff->repeat = value;
if (value > 0) {
- ffpl_calculate_trip_times(eff, now);
+ if (priv->control_timing && ffpl_process_memless(priv, &eff->active))
+ ffpl_calculate_trip_times(eff, now);
+ else
+ eff->start_at = now; /* Start the effect right away and let the device deal with the timing */
eff->trigger = FFPL_TRIG_START;
} else {
eff->change = FFPL_TO_STOP;
if (eff->replace) {
/* Uncombinable effect is about to be replaced by a combinable one */
- if (ffpl_is_combinable(&eff->latest)) {
+ if (ffpl_process_memless(priv, &eff->latest)) {
printk(KERN_NOTICE "KLGDFF: Replacing uncombinable with combinable\n");
switch (eff->state) {
case FFPL_STARTED:
continue;
}
} else {
- if (!ffpl_is_combinable(&eff->latest))
+ if (!ffpl_process_memless(priv, &eff->latest))
continue;
}
return ffpl_get_env_recalculation_time(eff, now);
}
-static bool ffpl_needs_recalculation(const struct ff_effect *ueff, const unsigned long start_at, const unsigned long stop_at, const unsigned long now)
+static bool ffpl_needs_recalculation(const struct klgd_plugin_private *priv, const struct ff_effect *ueff, const unsigned long start_at,
+ const unsigned long stop_at, const unsigned long now)
{
const struct ff_envelope *env = ffpl_get_envelope(ueff);
bool ticks = ueff->type == FF_PERIODIC || ueff->type == FF_RAMP;
- /* Only combinable effects can be periodically reprocessed */
- if (!ffpl_is_combinable(ueff)) {
+ /* Only effects handled by memless mode can be periodically reprocessed */
+ if (!ffpl_process_memless(priv, ueff)) {
printk(KERN_NOTICE "KLGDFF: Effect not combinable, won't recalculate\n");
return false;
}
return true;
}
-static void ffpl_advance_trigger(struct ffpl_effect *eff, const unsigned long now)
+static void ffpl_advance_trigger(const struct klgd_plugin_private *priv, struct ffpl_effect *eff, const unsigned long now)
{
switch (eff->trigger) {
case FFPL_TRIG_START:
- if (ffpl_needs_recalculation(&eff->latest, eff->start_at, eff->stop_at, now)) {
+ if (ffpl_needs_recalculation(priv, &eff->latest, eff->start_at, eff->stop_at, now)) {
eff->trigger = FFPL_TRIG_RECALC;
break;
}
- if (eff->latest.replay.length)
+ if (eff->latest.replay.length && priv->control_timing && ffpl_process_memless(priv, &eff->latest))
eff->trigger = FFPL_TRIG_STOP;
else
eff->trigger = FFPL_TRIG_NONE;
eff->trigger = FFPL_TRIG_STOP;
break;
case FFPL_TRIG_RECALC:
- if (ffpl_needs_recalculation(&eff->active, eff->start_at, eff->stop_at, now))
+ if (ffpl_needs_recalculation(priv, &eff->active, eff->start_at, eff->stop_at, now))
break;
- if (eff->active.replay.length) {
+ if (eff->active.replay.length && priv->control_timing && ffpl_process_memless(priv, &eff->active)) {
eff->trigger = FFPL_TRIG_STOP;
break;
}
eff->trigger = FFPL_TRIG_NONE;
break;
case FFPL_TRIG_STOP:
- if (eff->repeat > 0) {
+ if (eff->repeat > 0 && priv->control_timing && ffpl_process_memless(priv, &eff->active)) {
eff->trigger = FFPL_TRIG_RESTART;
break;
}
eff->trigger = FFPL_TRIG_NONE;
break;
case FFPL_TRIG_UPDATE:
- if (ffpl_needs_recalculation(&eff->active, eff->start_at, eff->stop_at, now) && eff->state == FFPL_STARTED)
+ if (ffpl_needs_recalculation(priv, &eff->active, eff->start_at, eff->stop_at, now) && eff->state == FFPL_STARTED)
eff->trigger = FFPL_TRIG_RECALC;
else
eff->trigger = FFPL_TRIG_NONE;
goto out;
}
- ffpl_advance_trigger(eff, now);
+ ffpl_advance_trigger(priv, eff, now);
}
out:
priv->has_owr_to_srt = true;
printk("KLGDFF: Using REPLACE STARTED\n");
}
+ if (FFPL_MEMLESS_MODE & flags) {
+ priv->memless_mode = true;
+ priv->control_timing = true;
+ }
+ if (FFPL_MEMLESS_PERIODIC & flags) {
+ priv->memless_mode = true;
+ priv->memless_periodic = true;
+ priv->control_timing = true;
+ }
+ if (FFPL_MEMLESS_RAMP & flags) {
+ priv->memless_mode = true;
+ priv->memless_ramp = true;
+ priv->control_timing = true;
+ }
+ if (FFPL_CONTROL_TIMING & flags)
+ priv->control_timing = true;
if (FFPL_HAS_NATIVE_GAIN & flags) {
priv->has_native_gain = true;
printk(KERN_NOTICE "KLGDFF: Using HAS_NATIVE_GAIN\n");
set_bit(FF_AUTOCENTER, dev->ffbit);
printk(KERN_NOTICE "KLGDFF: Using HAS_AUTOCENTER\n");
}
-
set_bit(FF_GAIN, dev->ffbit);
for (idx = 0; idx <= (FF_WAVEFORM_MAX - FF_EFFECT_MIN); idx++) {
if (test_bit(idx, &priv->supported_effects)) {
autocenter = _autocenter;
memcpy(c->bytes, text, len);
+ c->user = (unsigned long)0xDEADBEEF;
kfree(text);
return klgd_append_cmd(s, c);
}
size_t idx;
printk(KERN_NOTICE "KLGDTM - EFF...\n");
- for (idx = 0; idx < s->count; idx++)
+ for (idx = 0; idx < s->count; idx++) {
printk(KERN_NOTICE "KLGDFF-TD: EFF %s\n", s->commands[idx]->bytes);
+ if (s->commands[idx]->user)
+ printk("KLGDFF-TD: User 0x%x\n", (unsigned int)s->commands[idx]->user);
+ }
/* Simulate default USB polling rate of 125 Hz */
/*usleep_range(7500, 8500);*/
| FFPL_EFBIT(FF_SAW_UP)
| FFPL_EFBIT(FF_SAW_DOWN)
| FFPL_EFBIT(FF_TRIANGLE) |
- FFPL_EFBIT(FF_RAMP);
+ FFPL_EFBIT(FF_RAMP) |
+ FFPL_EFBIT(FF_SPRING);
+
int ret;
klgdff_obj = kobject_create_and_add("klgdff_obj", kernel_kobj);
input_set_abs_params(dev, ABS_Y, -0x7fff, 0x7fff, 0, 0);
ret = ffpl_init_plugin(&ff_plugin, dev, EFFECT_COUNT, ffbits,
- FFPL_HAS_EMP_TO_SRT | FFPL_REPLACE_STARTED | FFPL_HAS_AUTOCENTER,
+ FFPL_HAS_EMP_TO_SRT | FFPL_REPLACE_STARTED | FFPL_HAS_AUTOCENTER | FFPL_MEMLESS_MODE,
klgdff_control);
if (ret) {
printk(KERN_ERR "KLGDFF-TD: Cannot init plugin\n");