Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564// SPDX-License-Identifier: GPL-2.0-or-later/* * Force feedback support for memoryless devices * * Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com> * Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru> */
/* #define DEBUG */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/export.h>#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"); timer_delete(&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;}
#define FRAC_N 8static inline s16 fixp_new16(s16 a){ return ((s32)a) >> (16 - FRAC_N);}
static inline s16 fixp_mult(s16 a, s16 b){ a = ((s32)a * 0x100) / 0x7fff; return ((s32)(a * b)) >> FRAC_N;}
/* * 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; s16 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_sin16(i), level) * gain / 0xffff; y = fixp_mult(-fixp_cos16(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(struct timer_list *t){ struct ml_device *ml = timer_container_of(ml, t, timer); struct input_dev *dev = ml->dev;
pr_debug("timer: updating effects\n");
guard(spinlock_irqsave)(&dev->event_lock); ml_play_effects(ml);}
/* * 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];
guard(spinlock_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); }
return 0;}
static void ml_ff_destroy(struct ff_device *ff){ struct ml_device *ml = ff->private;
/* * The timer is normally shut down in ml_ff_stop() when the device * is unregistered. However, we still shut it down here as a safety * net and for cases where the device was never registered (e.g. * error paths during probe). */ timer_shutdown_sync(&ml->timer);
kfree(ml->private);}
static void ml_ff_stop(struct ff_device *ff){ struct ml_device *ml = ff->private;
/* * Even though we stop all playing effects when tearing down an * input device (by the way of evdev calling input_flush_device() * that calls into input_ff_flush() that stops and erases all * effects), we do not actually shutdown the timer, and therefore * we should do it here to prevent it firing after the input * device is unregistered and its associated resources are freed. */ timer_shutdown_sync(&ml->timer);}
/** * 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 ff_device *ff; int error; int i;
struct ml_device *ml __free(kfree) = kzalloc_obj(*ml); if (!ml) return -ENOMEM;
ml->dev = dev; ml->private = data; ml->play_effect = play_effect; ml->gain = 0xffff; timer_setup(&ml->timer, ml_effect_timer, 0);
set_bit(FF_GAIN, dev->ffbit);
error = input_ff_create(dev, FF_MEMLESS_EFFECTS); if (error) return error;
ff = dev->ff; ff->upload = ml_ff_upload; ff->playback = ml_ff_playback; ff->set_gain = ml_ff_set_gain; ff->destroy = ml_ff_destroy; ff->stop = ml_ff_stop;
/* 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];
ff->private = no_free_ptr(ml);
return 0;}EXPORT_SYMBOL_GPL(input_ff_create_memless);