Skip to content

Commit

Permalink
Merge branch 'i2c-hid' into for-linus
Browse files Browse the repository at this point in the history
- ensure various commands do not interfere with each other (Dmitry
  Torokhov)
  • Loading branch information
Benjamin Tissoires committed Sep 13, 2024
2 parents 6937a82 + b4ed18a commit b169410
Showing 1 changed file with 27 additions and 15 deletions.
42 changes: 27 additions & 15 deletions drivers/hid/i2c-hid/i2c-hid-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ struct i2c_hid {

wait_queue_head_t wait; /* For waiting the interrupt */

struct mutex cmd_lock; /* protects cmdbuf and rawbuf */
struct mutex reset_lock;

struct i2chid_ops *ops;
Expand Down Expand Up @@ -220,6 +221,8 @@ static int i2c_hid_xfer(struct i2c_hid *ihid,
static int i2c_hid_read_register(struct i2c_hid *ihid, __le16 reg,
void *buf, size_t len)
{
guard(mutex)(&ihid->cmd_lock);

*(__le16 *)ihid->cmdbuf = reg;

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

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

guard(mutex)(&ihid->cmd_lock);

/* Command register goes first */
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
length += sizeof(__le16);
Expand Down Expand Up @@ -342,6 +347,8 @@ static int i2c_hid_set_or_send_report(struct i2c_hid *ihid,
if (!do_set && le16_to_cpu(ihid->hdesc.wMaxOutputLength) == 0)
return -ENOSYS;

guard(mutex)(&ihid->cmd_lock);

if (do_set) {
/* Command register goes first */
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
Expand Down Expand Up @@ -384,6 +391,8 @@ static int i2c_hid_set_power_command(struct i2c_hid *ihid, int power_state)
{
size_t length;

guard(mutex)(&ihid->cmd_lock);

/* SET_POWER uses command register */
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
length = sizeof(__le16);
Expand Down Expand Up @@ -440,25 +449,27 @@ static int i2c_hid_start_hwreset(struct i2c_hid *ihid)
if (ret)
return ret;

/* Prepare reset command. Command register goes first. */
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
length += sizeof(__le16);
/* Next is RESET command itself */
length += i2c_hid_encode_command(ihid->cmdbuf + length,
I2C_HID_OPCODE_RESET, 0, 0);
scoped_guard(mutex, &ihid->cmd_lock) {
/* Prepare reset command. Command register goes first. */
*(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister;
length += sizeof(__le16);
/* Next is RESET command itself */
length += i2c_hid_encode_command(ihid->cmdbuf + length,
I2C_HID_OPCODE_RESET, 0, 0);

set_bit(I2C_HID_RESET_PENDING, &ihid->flags);
set_bit(I2C_HID_RESET_PENDING, &ihid->flags);

ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
if (ret) {
dev_err(&ihid->client->dev,
"failed to reset device: %d\n", ret);
goto err_clear_reset;
}
ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0);
if (ret) {
dev_err(&ihid->client->dev,
"failed to reset device: %d\n", ret);
break;
}

return 0;
return 0;
}

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

init_waitqueue_head(&ihid->wait);
mutex_init(&ihid->cmd_lock);
mutex_init(&ihid->reset_lock);
INIT_WORK(&ihid->panel_follower_prepare_work, ihid_core_panel_prepare_work);

Expand Down

0 comments on commit b169410

Please sign in to comment.