Skip to content

Commit b169410

Browse files
author
Benjamin Tissoires
committed
Merge branch 'i2c-hid' into for-linus
- ensure various commands do not interfere with each other (Dmitry Torokhov)
2 parents 6937a82 + b4ed18a commit b169410

File tree

1 file changed

+27
-15
lines changed

1 file changed

+27
-15
lines changed

drivers/hid/i2c-hid/i2c-hid-core.c

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ struct i2c_hid {
105105

106106
wait_queue_head_t wait; /* For waiting the interrupt */
107107

108+
struct mutex cmd_lock; /* protects cmdbuf and rawbuf */
108109
struct mutex reset_lock;
109110

110111
struct i2chid_ops *ops;
@@ -220,6 +221,8 @@ static int i2c_hid_xfer(struct i2c_hid *ihid,
220221
static int i2c_hid_read_register(struct i2c_hid *ihid, __le16 reg,
221222
void *buf, size_t len)
222223
{
224+
guard(mutex)(&ihid->cmd_lock);
225+
223226
*(__le16 *)ihid->cmdbuf = reg;
224227

225228
return i2c_hid_xfer(ihid, ihid->cmdbuf, sizeof(__le16), buf, len);
@@ -252,6 +255,8 @@ static int i2c_hid_get_report(struct i2c_hid *ihid,
252255

253256
i2c_hid_dbg(ihid, "%s\n", __func__);
254257

258+
guard(mutex)(&ihid->cmd_lock);
259+
255260
/* Command register goes first */
256261
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
257262
length += sizeof(__le16);
@@ -342,6 +347,8 @@ static int i2c_hid_set_or_send_report(struct i2c_hid *ihid,
342347
if (!do_set && le16_to_cpu(ihid->hdesc.wMaxOutputLength) == 0)
343348
return -ENOSYS;
344349

350+
guard(mutex)(&ihid->cmd_lock);
351+
345352
if (do_set) {
346353
/* Command register goes first */
347354
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
@@ -384,6 +391,8 @@ static int i2c_hid_set_power_command(struct i2c_hid *ihid, int power_state)
384391
{
385392
size_t length;
386393

394+
guard(mutex)(&ihid->cmd_lock);
395+
387396
/* SET_POWER uses command register */
388397
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
389398
length = sizeof(__le16);
@@ -440,25 +449,27 @@ static int i2c_hid_start_hwreset(struct i2c_hid *ihid)
440449
if (ret)
441450
return ret;
442451

443-
/* Prepare reset command. Command register goes first. */
444-
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
445-
length += sizeof(__le16);
446-
/* Next is RESET command itself */
447-
length += i2c_hid_encode_command(ihid->cmdbuf + length,
448-
I2C_HID_OPCODE_RESET, 0, 0);
452+
scoped_guard(mutex, &ihid->cmd_lock) {
453+
/* Prepare reset command. Command register goes first. */
454+
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
455+
length += sizeof(__le16);
456+
/* Next is RESET command itself */
457+
length += i2c_hid_encode_command(ihid->cmdbuf + length,
458+
I2C_HID_OPCODE_RESET, 0, 0);
449459

450-
set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
460+
set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
451461

452-
ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
453-
if (ret) {
454-
dev_err(&ihid->client->dev,
455-
"failed to reset device: %d\n", ret);
456-
goto err_clear_reset;
457-
}
462+
ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
463+
if (ret) {
464+
dev_err(&ihid->client->dev,
465+
"failed to reset device: %d\n", ret);
466+
break;
467+
}
458468

459-
return 0;
469+
return 0;
470+
}
460471

461-
err_clear_reset:
472+
/* Clean up if sending reset command failed */
462473
clear_bit(I2C_HID_RESET_PENDING, &ihid->flags);
463474
i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP);
464475
return ret;
@@ -1200,6 +1211,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
12001211
ihid->is_panel_follower = drm_is_panel_follower(&client->dev);
12011212

12021213
init_waitqueue_head(&ihid->wait);
1214+
mutex_init(&ihid->cmd_lock);
12031215
mutex_init(&ihid->reset_lock);
12041216
INIT_WORK(&ihid->panel_follower_prepare_work, ihid_core_panel_prepare_work);
12051217

0 commit comments

Comments
 (0)