Skip to content

Commit

Permalink
hid-xpadneo, rumble: Protect concurrent access with spin locks
Browse files Browse the repository at this point in the history
Otherwise we may work on a partially updated structure and that may
leave the rumble motors running without letting the driver know it.

Signed-off-by: Kai Krakow <[email protected]>
  • Loading branch information
kakra committed Jun 26, 2020
1 parent 44e7210 commit e37ea45
Showing 1 changed file with 18 additions and 5 deletions.
23 changes: 18 additions & 5 deletions hid-xpadneo/src/hid-xpadneo.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ struct xpadneo_devdata {
s32 last_abs_rz;

/* buffer for ff_worker */
spinlock_t ff_lock;
struct work_struct ff_worker;
struct ff_data ff;
struct ff_data ff_shadow;
Expand Down Expand Up @@ -207,6 +208,7 @@ static void xpadneo_ff_worker(struct work_struct *work)
struct hid_device *hdev = xdata->hdev;
struct ff_report *r = xdata->output_report_dmabuf;
int ret;
unsigned long flags;

memset(r, 0, sizeof(*r));
r->report_id = XPADNEO_XB1S_FF_REPORT;
Expand All @@ -227,6 +229,8 @@ static void xpadneo_ff_worker(struct work_struct *work)
r->ff.loop_count = U8_MAX;
}

spin_lock_irqsave(&xdata->ff_lock, flags);

if (unlikely(xdata->quirks & XPADNEO_QUIRK_NO_TRIGGER_RUMBLE)) {
/* do not send these bits if not supported */
r->ff.enable &= ~FF_RUMBLE_TRIGGERS;
Expand All @@ -251,12 +255,16 @@ static void xpadneo_ff_worker(struct work_struct *work)
r->ff.enable &= ~FF_RUMBLE_RIGHT;

/* do not send a report if nothing changed */
if (unlikely(r->ff.enable == FF_RUMBLE_NONE))
if (unlikely(r->ff.enable == FF_RUMBLE_NONE)) {
spin_unlock_irqrestore(&xdata->ff_lock, flags);
return;
}

/* shadow our current rumble values for the next cycle */
memcpy(&xdata->ff_shadow, &xdata->ff, sizeof(xdata->ff));

spin_unlock_irqrestore(&xdata->ff_lock, flags);

/* do not send these bits if not supported */
if (unlikely(xdata->quirks & XPADNEO_QUIRK_NO_MOTOR_MASK)) {
r->ff.enable = 0;
Expand All @@ -277,6 +285,7 @@ static int xpadneo_ff_play(struct input_dev *dev, void *data, struct ff_effect *
QUARTER = DIRECTION_LEFT,
};

unsigned long flags;
int fraction_TL, fraction_TR, fraction_MAIN;
s32 weak, strong, direction, max_damped, max_unscaled;

Expand Down Expand Up @@ -357,10 +366,6 @@ static int xpadneo_ff_play(struct input_dev *dev, void *data, struct ff_effect *
break;
}

/* calculate the physical magnitudes, scale from 16 bit to 0..100 */
xdata->ff.magnitude_strong = (u8)((strong * fraction_MAIN) / U16_MAX);
xdata->ff.magnitude_weak = (u8)((weak * fraction_MAIN) / U16_MAX);

/*
* we want to keep the rumbling at the triggers at the maximum
* of the weak and strong main rumble
Expand All @@ -376,10 +381,18 @@ static int xpadneo_ff_play(struct input_dev *dev, void *data, struct ff_effect *
else
max_damped = max_unscaled;

spin_lock_irqsave(&xdata->ff_lock, flags);

/* calculate the physical magnitudes, scale from 16 bit to 0..100 */
xdata->ff.magnitude_strong = (u8)((strong * fraction_MAIN) / U16_MAX);
xdata->ff.magnitude_weak = (u8)((weak * fraction_MAIN) / U16_MAX);

/* calculate the physical magnitudes, scale from 16 bit to 0..100 */
xdata->ff.magnitude_left = (u8)((max_damped * fraction_TL) / U16_MAX);
xdata->ff.magnitude_right = (u8)((max_damped * fraction_TR) / U16_MAX);

spin_unlock_irqrestore(&xdata->ff_lock, flags);

/* schedule writing a rumble report to the controller */
schedule_work(&xdata->ff_worker);
return 0;
Expand Down

0 comments on commit e37ea45

Please sign in to comment.