Skip to content

Commit 691ead1

Browse files
endriftJiri Kosina
authored andcommitted
HID: hid-steam: Clean up locking
This cleans up the locking logic so that the spinlock is consistently used for access to a small handful of struct variables, and the mutex is exclusively and consistently used for ensuring that mutliple threads aren't trying to send/receive reports at the same time. Previously, only some report transactions were guarded by this mutex, potentially breaking atomicity. The mutex has been renamed to reflect this usage. Signed-off-by: Vicki Pfau <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 9179726 commit 691ead1

File tree

1 file changed

+69
-53
lines changed

1 file changed

+69
-53
lines changed

drivers/hid/hid-steam.c

Lines changed: 69 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ struct steam_device {
124124
struct list_head list;
125125
spinlock_t lock;
126126
struct hid_device *hdev, *client_hdev;
127-
struct mutex mutex;
127+
struct mutex report_mutex;
128128
bool client_opened;
129129
struct input_dev __rcu *input;
130130
unsigned long quirks;
@@ -267,21 +267,26 @@ static int steam_get_serial(struct steam_device *steam)
267267
* Send: 0xae 0x15 0x01
268268
* Recv: 0xae 0x15 0x01 serialnumber (10 chars)
269269
*/
270-
int ret;
270+
int ret = 0;
271271
u8 cmd[] = {STEAM_CMD_GET_SERIAL, 0x15, 0x01};
272272
u8 reply[3 + STEAM_SERIAL_LEN + 1];
273273

274+
mutex_lock(&steam->report_mutex);
274275
ret = steam_send_report(steam, cmd, sizeof(cmd));
275276
if (ret < 0)
276-
return ret;
277+
goto out;
277278
ret = steam_recv_report(steam, reply, sizeof(reply));
278279
if (ret < 0)
279-
return ret;
280-
if (reply[0] != 0xae || reply[1] != 0x15 || reply[2] != 0x01)
281-
return -EIO;
280+
goto out;
281+
if (reply[0] != 0xae || reply[1] != 0x15 || reply[2] != 0x01) {
282+
ret = -EIO;
283+
goto out;
284+
}
282285
reply[3 + STEAM_SERIAL_LEN] = 0;
283286
strscpy(steam->serial_no, reply + 3, sizeof(steam->serial_no));
284-
return 0;
287+
out:
288+
mutex_unlock(&steam->report_mutex);
289+
return ret;
285290
}
286291

287292
/*
@@ -291,13 +296,18 @@ static int steam_get_serial(struct steam_device *steam)
291296
*/
292297
static inline int steam_request_conn_status(struct steam_device *steam)
293298
{
294-
return steam_send_report_byte(steam, STEAM_CMD_REQUEST_COMM_STATUS);
299+
int ret;
300+
mutex_lock(&steam->report_mutex);
301+
ret = steam_send_report_byte(steam, STEAM_CMD_REQUEST_COMM_STATUS);
302+
mutex_unlock(&steam->report_mutex);
303+
return ret;
295304
}
296305

297306
static inline int steam_haptic_rumble(struct steam_device *steam,
298307
u16 intensity, u16 left_speed, u16 right_speed,
299308
u8 left_gain, u8 right_gain)
300309
{
310+
int ret;
301311
u8 report[11] = {STEAM_CMD_HAPTIC_RUMBLE, 9};
302312

303313
report[3] = intensity & 0xFF;
@@ -309,7 +319,10 @@ static inline int steam_haptic_rumble(struct steam_device *steam,
309319
report[9] = left_gain;
310320
report[10] = right_gain;
311321

312-
return steam_send_report(steam, report, sizeof(report));
322+
mutex_lock(&steam->report_mutex);
323+
ret = steam_send_report(steam, report, sizeof(report));
324+
mutex_unlock(&steam->report_mutex);
325+
return ret;
313326
}
314327

315328
static void steam_haptic_rumble_cb(struct work_struct *work)
@@ -336,11 +349,14 @@ static int steam_play_effect(struct input_dev *dev, void *data,
336349
static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
337350
{
338351
if (enable) {
352+
mutex_lock(&steam->report_mutex);
339353
/* enable esc, enter, cursors */
340354
steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MAPPINGS);
341355
/* enable mouse */
342356
steam_send_report_byte(steam, STEAM_CMD_DEFAULT_MOUSE);
357+
mutex_unlock(&steam->report_mutex);
343358
} else {
359+
mutex_lock(&steam->report_mutex);
344360
/* disable esc, enter, cursor */
345361
steam_send_report_byte(steam, STEAM_CMD_CLEAR_MAPPINGS);
346362

@@ -352,34 +368,43 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
352368
STEAM_REG_RPAD_CLICK_PRESSURE, 0xFFFF, /* disable clicky pad */
353369
STEAM_REG_WATCHDOG_ENABLE, 0, /* disable watchdog that tests if Steam is active */
354370
0);
371+
mutex_unlock(&steam->report_mutex);
355372
} else {
356373
steam_write_registers(steam,
357374
STEAM_REG_LPAD_MODE, 0x07, /* disable mouse */
358375
STEAM_REG_RPAD_MODE, 0x07, /* disable mouse */
359376
0);
377+
mutex_unlock(&steam->report_mutex);
360378
}
361379
}
362380
}
363381

364382
static int steam_input_open(struct input_dev *dev)
365383
{
366384
struct steam_device *steam = input_get_drvdata(dev);
385+
unsigned long flags;
386+
bool set_lizard_mode;
367387

368-
mutex_lock(&steam->mutex);
369-
if (!steam->client_opened && lizard_mode)
388+
spin_lock_irqsave(&steam->lock, flags);
389+
set_lizard_mode = !steam->client_opened && lizard_mode;
390+
spin_unlock_irqrestore(&steam->lock, flags);
391+
if (set_lizard_mode)
370392
steam_set_lizard_mode(steam, false);
371-
mutex_unlock(&steam->mutex);
393+
372394
return 0;
373395
}
374396

375397
static void steam_input_close(struct input_dev *dev)
376398
{
377399
struct steam_device *steam = input_get_drvdata(dev);
400+
unsigned long flags;
401+
bool set_lizard_mode;
378402

379-
mutex_lock(&steam->mutex);
380-
if (!steam->client_opened && lizard_mode)
403+
spin_lock_irqsave(&steam->lock, flags);
404+
set_lizard_mode = !steam->client_opened && lizard_mode;
405+
spin_unlock_irqrestore(&steam->lock, flags);
406+
if (set_lizard_mode)
381407
steam_set_lizard_mode(steam, true);
382-
mutex_unlock(&steam->mutex);
383408
}
384409

385410
static enum power_supply_property steam_battery_props[] = {
@@ -624,6 +649,7 @@ static int steam_register(struct steam_device *steam)
624649
{
625650
int ret;
626651
bool client_opened;
652+
unsigned long flags;
627653

628654
/*
629655
* This function can be called several times in a row with the
@@ -636,11 +662,9 @@ static int steam_register(struct steam_device *steam)
636662
* Unlikely, but getting the serial could fail, and it is not so
637663
* important, so make up a serial number and go on.
638664
*/
639-
mutex_lock(&steam->mutex);
640665
if (steam_get_serial(steam) < 0)
641666
strscpy(steam->serial_no, "XXXXXXXXXX",
642667
sizeof(steam->serial_no));
643-
mutex_unlock(&steam->mutex);
644668

645669
hid_info(steam->hdev, "Steam Controller '%s' connected",
646670
steam->serial_no);
@@ -655,15 +679,13 @@ static int steam_register(struct steam_device *steam)
655679
mutex_unlock(&steam_devices_lock);
656680
}
657681

658-
mutex_lock(&steam->mutex);
682+
spin_lock_irqsave(&steam->lock, flags);
659683
client_opened = steam->client_opened;
660-
if (!client_opened)
684+
spin_unlock_irqrestore(&steam->lock, flags);
685+
if (!client_opened) {
661686
steam_set_lizard_mode(steam, lizard_mode);
662-
mutex_unlock(&steam->mutex);
663-
664-
if (!client_opened)
665687
ret = steam_input_register(steam);
666-
else
688+
} else
667689
ret = 0;
668690

669691
return ret;
@@ -746,10 +768,11 @@ static void steam_client_ll_stop(struct hid_device *hdev)
746768
static int steam_client_ll_open(struct hid_device *hdev)
747769
{
748770
struct steam_device *steam = hdev->driver_data;
771+
unsigned long flags;
749772

750-
mutex_lock(&steam->mutex);
773+
spin_lock_irqsave(&steam->lock, flags);
751774
steam->client_opened = true;
752-
mutex_unlock(&steam->mutex);
775+
spin_unlock_irqrestore(&steam->lock, flags);
753776

754777
steam_input_unregister(steam);
755778

@@ -764,17 +787,14 @@ static void steam_client_ll_close(struct hid_device *hdev)
764787
bool connected;
765788

766789
spin_lock_irqsave(&steam->lock, flags);
767-
connected = steam->connected;
790+
steam->client_opened = false;
791+
connected = steam->connected && !steam->client_opened;
768792
spin_unlock_irqrestore(&steam->lock, flags);
769793

770-
mutex_lock(&steam->mutex);
771-
steam->client_opened = false;
772-
if (connected)
794+
if (connected) {
773795
steam_set_lizard_mode(steam, lizard_mode);
774-
mutex_unlock(&steam->mutex);
775-
776-
if (connected)
777796
steam_input_register(steam);
797+
}
778798
}
779799

780800
static int steam_client_ll_raw_request(struct hid_device *hdev,
@@ -860,19 +880,12 @@ static int steam_probe(struct hid_device *hdev,
860880
steam->hdev = hdev;
861881
hid_set_drvdata(hdev, steam);
862882
spin_lock_init(&steam->lock);
863-
mutex_init(&steam->mutex);
883+
mutex_init(&steam->report_mutex);
864884
steam->quirks = id->driver_data;
865885
INIT_WORK(&steam->work_connect, steam_work_connect_cb);
866886
INIT_LIST_HEAD(&steam->list);
867887
INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb);
868888

869-
steam->client_hdev = steam_create_client_hid(hdev);
870-
if (IS_ERR(steam->client_hdev)) {
871-
ret = PTR_ERR(steam->client_hdev);
872-
goto client_hdev_fail;
873-
}
874-
steam->client_hdev->driver_data = steam;
875-
876889
/*
877890
* With the real steam controller interface, do not connect hidraw.
878891
* Instead, create the client_hid and connect that.
@@ -881,10 +894,6 @@ static int steam_probe(struct hid_device *hdev,
881894
if (ret)
882895
goto hid_hw_start_fail;
883896

884-
ret = hid_add_device(steam->client_hdev);
885-
if (ret)
886-
goto client_hdev_add_fail;
887-
888897
ret = hid_hw_open(hdev);
889898
if (ret) {
890899
hid_err(hdev,
@@ -910,15 +919,26 @@ static int steam_probe(struct hid_device *hdev,
910919
}
911920
}
912921

922+
steam->client_hdev = steam_create_client_hid(hdev);
923+
if (IS_ERR(steam->client_hdev)) {
924+
ret = PTR_ERR(steam->client_hdev);
925+
goto client_hdev_fail;
926+
}
927+
steam->client_hdev->driver_data = steam;
928+
929+
ret = hid_add_device(steam->client_hdev);
930+
if (ret)
931+
goto client_hdev_add_fail;
932+
913933
return 0;
914934

915-
input_register_fail:
916-
hid_hw_open_fail:
917935
client_hdev_add_fail:
918936
hid_hw_stop(hdev);
919-
hid_hw_start_fail:
920-
hid_destroy_device(steam->client_hdev);
921937
client_hdev_fail:
938+
hid_destroy_device(steam->client_hdev);
939+
input_register_fail:
940+
hid_hw_open_fail:
941+
hid_hw_start_fail:
922942
cancel_work_sync(&steam->work_connect);
923943
cancel_work_sync(&steam->rumble_work);
924944
steam_alloc_fail:
@@ -936,12 +956,10 @@ static void steam_remove(struct hid_device *hdev)
936956
return;
937957
}
938958

959+
cancel_work_sync(&steam->work_connect);
939960
hid_destroy_device(steam->client_hdev);
940-
mutex_lock(&steam->mutex);
941961
steam->client_hdev = NULL;
942962
steam->client_opened = false;
943-
mutex_unlock(&steam->mutex);
944-
cancel_work_sync(&steam->work_connect);
945963
if (steam->quirks & STEAM_QUIRK_WIRELESS) {
946964
hid_info(hdev, "Steam wireless receiver disconnected");
947965
}
@@ -1408,10 +1426,8 @@ static int steam_param_set_lizard_mode(const char *val,
14081426

14091427
mutex_lock(&steam_devices_lock);
14101428
list_for_each_entry(steam, &steam_devices, list) {
1411-
mutex_lock(&steam->mutex);
14121429
if (!steam->client_opened)
14131430
steam_set_lizard_mode(steam, lizard_mode);
1414-
mutex_unlock(&steam->mutex);
14151431
}
14161432
mutex_unlock(&steam_devices_lock);
14171433
return 0;

0 commit comments

Comments
 (0)