Skip to content

Commit 9fefb62

Browse files
pietroborrellobentiss
authored andcommitted
HID: bigben: use spinlock to protect concurrent accesses
bigben driver has a worker that may access data concurrently. Proct the accesses using a spinlock. Fixes: 256a90e ("HID: hid-bigbenff: driver for BigBen Interactive PS3OFMINIPAD gamepad") Signed-off-by: Pietro Borrello <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Benjamin Tissoires <[email protected]>
1 parent 0b02818 commit 9fefb62

File tree

1 file changed

+50
-2
lines changed

1 file changed

+50
-2
lines changed

drivers/hid/hid-bigbenff.c

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ static __u8 pid0902_rdesc_fixed[] = {
174174
struct bigben_device {
175175
struct hid_device *hid;
176176
struct hid_report *report;
177+
spinlock_t lock;
177178
bool removed;
178179
u8 led_state; /* LED1 = 1 .. LED4 = 8 */
179180
u8 right_motor_on; /* right motor off/on 0/1 */
@@ -190,12 +191,27 @@ static void bigben_worker(struct work_struct *work)
190191
struct bigben_device *bigben = container_of(work,
191192
struct bigben_device, worker);
192193
struct hid_field *report_field = bigben->report->field[0];
194+
bool do_work_led = false;
195+
bool do_work_ff = false;
196+
u8 *buf;
197+
u32 len;
198+
unsigned long flags;
193199

194200
if (bigben->removed || !report_field)
195201
return;
196202

203+
buf = hid_alloc_report_buf(bigben->report, GFP_KERNEL);
204+
if (!buf)
205+
return;
206+
207+
len = hid_report_len(bigben->report);
208+
209+
/* LED work */
210+
spin_lock_irqsave(&bigben->lock, flags);
211+
197212
if (bigben->work_led) {
198213
bigben->work_led = false;
214+
do_work_led = true;
199215
report_field->value[0] = 0x01; /* 1 = led message */
200216
report_field->value[1] = 0x08; /* reserved value, always 8 */
201217
report_field->value[2] = bigben->led_state;
@@ -204,11 +220,22 @@ static void bigben_worker(struct work_struct *work)
204220
report_field->value[5] = 0x00; /* padding */
205221
report_field->value[6] = 0x00; /* padding */
206222
report_field->value[7] = 0x00; /* padding */
207-
hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT);
223+
hid_output_report(bigben->report, buf);
224+
}
225+
226+
spin_unlock_irqrestore(&bigben->lock, flags);
227+
228+
if (do_work_led) {
229+
hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,
230+
bigben->report->type, HID_REQ_SET_REPORT);
208231
}
209232

233+
/* FF work */
234+
spin_lock_irqsave(&bigben->lock, flags);
235+
210236
if (bigben->work_ff) {
211237
bigben->work_ff = false;
238+
do_work_ff = true;
212239
report_field->value[0] = 0x02; /* 2 = rumble effect message */
213240
report_field->value[1] = 0x08; /* reserved value, always 8 */
214241
report_field->value[2] = bigben->right_motor_on;
@@ -217,8 +244,17 @@ static void bigben_worker(struct work_struct *work)
217244
report_field->value[5] = 0x00; /* padding */
218245
report_field->value[6] = 0x00; /* padding */
219246
report_field->value[7] = 0x00; /* padding */
220-
hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT);
247+
hid_output_report(bigben->report, buf);
248+
}
249+
250+
spin_unlock_irqrestore(&bigben->lock, flags);
251+
252+
if (do_work_ff) {
253+
hid_hw_raw_request(bigben->hid, bigben->report->id, buf, len,
254+
bigben->report->type, HID_REQ_SET_REPORT);
221255
}
256+
257+
kfree(buf);
222258
}
223259

224260
static int hid_bigben_play_effect(struct input_dev *dev, void *data,
@@ -228,6 +264,7 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data,
228264
struct bigben_device *bigben = hid_get_drvdata(hid);
229265
u8 right_motor_on;
230266
u8 left_motor_force;
267+
unsigned long flags;
231268

232269
if (!bigben) {
233270
hid_err(hid, "no device data\n");
@@ -242,9 +279,12 @@ static int hid_bigben_play_effect(struct input_dev *dev, void *data,
242279

243280
if (right_motor_on != bigben->right_motor_on ||
244281
left_motor_force != bigben->left_motor_force) {
282+
spin_lock_irqsave(&bigben->lock, flags);
245283
bigben->right_motor_on = right_motor_on;
246284
bigben->left_motor_force = left_motor_force;
247285
bigben->work_ff = true;
286+
spin_unlock_irqrestore(&bigben->lock, flags);
287+
248288
schedule_work(&bigben->worker);
249289
}
250290

@@ -259,6 +299,7 @@ static void bigben_set_led(struct led_classdev *led,
259299
struct bigben_device *bigben = hid_get_drvdata(hid);
260300
int n;
261301
bool work;
302+
unsigned long flags;
262303

263304
if (!bigben) {
264305
hid_err(hid, "no device data\n");
@@ -267,13 +308,15 @@ static void bigben_set_led(struct led_classdev *led,
267308

268309
for (n = 0; n < NUM_LEDS; n++) {
269310
if (led == bigben->leds[n]) {
311+
spin_lock_irqsave(&bigben->lock, flags);
270312
if (value == LED_OFF) {
271313
work = (bigben->led_state & BIT(n));
272314
bigben->led_state &= ~BIT(n);
273315
} else {
274316
work = !(bigben->led_state & BIT(n));
275317
bigben->led_state |= BIT(n);
276318
}
319+
spin_unlock_irqrestore(&bigben->lock, flags);
277320

278321
if (work) {
279322
bigben->work_led = true;
@@ -307,8 +350,12 @@ static enum led_brightness bigben_get_led(struct led_classdev *led)
307350
static void bigben_remove(struct hid_device *hid)
308351
{
309352
struct bigben_device *bigben = hid_get_drvdata(hid);
353+
unsigned long flags;
310354

355+
spin_lock_irqsave(&bigben->lock, flags);
311356
bigben->removed = true;
357+
spin_unlock_irqrestore(&bigben->lock, flags);
358+
312359
cancel_work_sync(&bigben->worker);
313360
hid_hw_stop(hid);
314361
}
@@ -362,6 +409,7 @@ static int bigben_probe(struct hid_device *hid,
362409
set_bit(FF_RUMBLE, hidinput->input->ffbit);
363410

364411
INIT_WORK(&bigben->worker, bigben_worker);
412+
spin_lock_init(&bigben->lock);
365413

366414
error = input_ff_create_memless(hidinput->input, NULL,
367415
hid_bigben_play_effect);

0 commit comments

Comments
 (0)