/*
* 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 "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(__s16, 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;