Commit bf3204cb authored by Dmitry Torokhov's avatar Dmitry Torokhov

Input: fix locking in memoryless force-feedback devices

Now that input core acquires dev->event_lock spinlock and disables
interrupts when propagating input events, using spin_lock_bh() in
ff-memless driver is not allowed. Actually, the timer_lock itself
is not needed anymore, we should simply use dev->event_lock
as well.

Also do a small cleanup in force-feedback core.

Reported-by: kerneloops.org
Reported-by: http://www.kerneloops.org/searchweek.php?search=ml_ff_set_gainReported-by: default avatarArjan van de Ven <arjan@linux.intel.com>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 558a5e29
...@@ -337,16 +337,16 @@ int input_ff_create(struct input_dev *dev, int max_effects) ...@@ -337,16 +337,16 @@ int input_ff_create(struct input_dev *dev, int max_effects)
dev->ff = ff; dev->ff = ff;
dev->flush = flush_effects; dev->flush = flush_effects;
dev->event = input_ff_event; dev->event = input_ff_event;
set_bit(EV_FF, dev->evbit); __set_bit(EV_FF, dev->evbit);
/* Copy "true" bits into ff device bitmap */ /* Copy "true" bits into ff device bitmap */
for (i = 0; i <= FF_MAX; i++) for (i = 0; i <= FF_MAX; i++)
if (test_bit(i, dev->ffbit)) if (test_bit(i, dev->ffbit))
set_bit(i, ff->ffbit); __set_bit(i, ff->ffbit);
/* we can emulate RUMBLE with periodic effects */ /* we can emulate RUMBLE with periodic effects */
if (test_bit(FF_PERIODIC, ff->ffbit)) if (test_bit(FF_PERIODIC, ff->ffbit))
set_bit(FF_RUMBLE, dev->ffbit); __set_bit(FF_RUMBLE, dev->ffbit);
return 0; return 0;
} }
...@@ -362,12 +362,14 @@ EXPORT_SYMBOL_GPL(input_ff_create); ...@@ -362,12 +362,14 @@ EXPORT_SYMBOL_GPL(input_ff_create);
*/ */
void input_ff_destroy(struct input_dev *dev) void input_ff_destroy(struct input_dev *dev)
{ {
clear_bit(EV_FF, dev->evbit); struct ff_device *ff = dev->ff;
if (dev->ff) {
if (dev->ff->destroy) __clear_bit(EV_FF, dev->evbit);
dev->ff->destroy(dev->ff); if (ff) {
kfree(dev->ff->private); if (ff->destroy)
kfree(dev->ff); ff->destroy(ff);
kfree(ff->private);
kfree(ff);
dev->ff = NULL; dev->ff = NULL;
} }
} }
......
...@@ -61,7 +61,6 @@ struct ml_device { ...@@ -61,7 +61,6 @@ struct ml_device {
struct ml_effect_state states[FF_MEMLESS_EFFECTS]; struct ml_effect_state states[FF_MEMLESS_EFFECTS];
int gain; int gain;
struct timer_list timer; struct timer_list timer;
spinlock_t timer_lock;
struct input_dev *dev; struct input_dev *dev;
int (*play_effect)(struct input_dev *dev, void *data, int (*play_effect)(struct input_dev *dev, void *data,
...@@ -368,38 +367,38 @@ static void ml_effect_timer(unsigned long timer_data) ...@@ -368,38 +367,38 @@ static void ml_effect_timer(unsigned long timer_data)
{ {
struct input_dev *dev = (struct input_dev *)timer_data; struct input_dev *dev = (struct input_dev *)timer_data;
struct ml_device *ml = dev->ff->private; struct ml_device *ml = dev->ff->private;
unsigned long flags;
debug("timer: updating effects"); debug("timer: updating effects");
spin_lock(&ml->timer_lock); spin_lock_irqsave(&dev->event_lock, flags);
ml_play_effects(ml); ml_play_effects(ml);
spin_unlock(&ml->timer_lock); 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) static void ml_ff_set_gain(struct input_dev *dev, u16 gain)
{ {
struct ml_device *ml = dev->ff->private; struct ml_device *ml = dev->ff->private;
int i; int i;
spin_lock_bh(&ml->timer_lock);
ml->gain = gain; ml->gain = gain;
for (i = 0; i < FF_MEMLESS_EFFECTS; i++) for (i = 0; i < FF_MEMLESS_EFFECTS; i++)
__clear_bit(FF_EFFECT_PLAYING, &ml->states[i].flags); __clear_bit(FF_EFFECT_PLAYING, &ml->states[i].flags);
ml_play_effects(ml); ml_play_effects(ml);
spin_unlock_bh(&ml->timer_lock);
} }
/*
* 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) static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
{ {
struct ml_device *ml = dev->ff->private; struct ml_device *ml = dev->ff->private;
struct ml_effect_state *state = &ml->states[effect_id]; struct ml_effect_state *state = &ml->states[effect_id];
unsigned long flags;
spin_lock_irqsave(&ml->timer_lock, flags);
if (value > 0) { if (value > 0) {
debug("initiated play"); debug("initiated play");
...@@ -425,8 +424,6 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value) ...@@ -425,8 +424,6 @@ static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
ml_play_effects(ml); ml_play_effects(ml);
} }
spin_unlock_irqrestore(&ml->timer_lock, flags);
return 0; return 0;
} }
...@@ -436,7 +433,7 @@ static int ml_ff_upload(struct input_dev *dev, ...@@ -436,7 +433,7 @@ static int ml_ff_upload(struct input_dev *dev,
struct ml_device *ml = dev->ff->private; struct ml_device *ml = dev->ff->private;
struct ml_effect_state *state = &ml->states[effect->id]; struct ml_effect_state *state = &ml->states[effect->id];
spin_lock_bh(&ml->timer_lock); spin_lock_irq(&dev->event_lock);
if (test_bit(FF_EFFECT_STARTED, &state->flags)) { if (test_bit(FF_EFFECT_STARTED, &state->flags)) {
__clear_bit(FF_EFFECT_PLAYING, &state->flags); __clear_bit(FF_EFFECT_PLAYING, &state->flags);
...@@ -448,7 +445,7 @@ static int ml_ff_upload(struct input_dev *dev, ...@@ -448,7 +445,7 @@ static int ml_ff_upload(struct input_dev *dev,
ml_schedule_timer(ml); ml_schedule_timer(ml);
} }
spin_unlock_bh(&ml->timer_lock); spin_unlock_irq(&dev->event_lock);
return 0; return 0;
} }
...@@ -482,7 +479,6 @@ int input_ff_create_memless(struct input_dev *dev, void *data, ...@@ -482,7 +479,6 @@ int input_ff_create_memless(struct input_dev *dev, void *data,
ml->private = data; ml->private = data;
ml->play_effect = play_effect; ml->play_effect = play_effect;
ml->gain = 0xffff; ml->gain = 0xffff;
spin_lock_init(&ml->timer_lock);
setup_timer(&ml->timer, ml_effect_timer, (unsigned long)dev); setup_timer(&ml->timer, ml_effect_timer, (unsigned long)dev);
set_bit(FF_GAIN, dev->ffbit); set_bit(FF_GAIN, dev->ffbit);
......
...@@ -1377,6 +1377,10 @@ extern struct class input_class; ...@@ -1377,6 +1377,10 @@ extern struct class input_class;
* methods; erase() is optional. set_gain() and set_autocenter() need * methods; erase() is optional. set_gain() and set_autocenter() need
* only be implemented if driver sets up FF_GAIN and FF_AUTOCENTER * only be implemented if driver sets up FF_GAIN and FF_AUTOCENTER
* bits. * bits.
*
* Note that playback(), set_gain() and set_autocenter() are called with
* dev->event_lock spinlock held and interrupts off and thus may not
* sleep.
*/ */
struct ff_device { struct ff_device {
int (*upload)(struct input_dev *dev, struct ff_effect *effect, int (*upload)(struct input_dev *dev, struct ff_effect *effect,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment