HID-over-I2C devices might reset on their own. Any device configuration applied before the reset might be brought back to defaults so we need to reconfigure to make sure the driver state is consistent. Add a reset callback to the hid driver structure. Issue it if the driver implements it and the device reset gets observed. Signed-off-by: Angela Czubak <acz@xxxxxxxxxxxx> --- drivers/hid/i2c-hid/i2c-hid-core.c | 21 +++++++++++++++++++++ include/linux/hid.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index c078f09a2318..37f134a3d9cb 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -116,6 +116,7 @@ struct i2c_hid { struct mutex reset_lock; struct i2chid_ops *ops; + struct work_struct reset_work; }; static const struct i2c_hid_quirks { @@ -504,6 +505,19 @@ static int i2c_hid_hwreset(struct i2c_hid *ihid) return ret; } +static void i2c_hid_reset_worker(struct work_struct *work) +{ + struct i2c_hid *ihid = container_of(work, struct i2c_hid, reset_work); + struct hid_device *hid = ihid->hid; + + down(&hid->driver_input_lock); + + if (hid->driver && hid->driver->reset) + hid->driver->reset(hid); + + up(&hid->driver_input_lock); +} + static void i2c_hid_get_input(struct i2c_hid *ihid) { u16 size = le16_to_cpu(ihid->hdesc.wMaxInputLength); @@ -529,6 +543,8 @@ static void i2c_hid_get_input(struct i2c_hid *ihid) /* host or device initiated RESET completed */ if (test_and_clear_bit(I2C_HID_RESET_PENDING, &ihid->flags)) wake_up(&ihid->wait); + else + schedule_work(&ihid->reset_work); return; } @@ -821,6 +837,10 @@ static int i2c_hid_start(struct hid_device *hid) static void i2c_hid_stop(struct hid_device *hid) { + struct i2c_client *client = hid->driver_data; + struct i2c_hid *ihid = i2c_get_clientdata(client); + + cancel_work_sync(&ihid->reset_work); hid->claimed = 0; } @@ -988,6 +1008,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, ihid->wHIDDescRegister = cpu_to_le16(hid_descriptor_address); init_waitqueue_head(&ihid->wait); + INIT_WORK(&ihid->reset_work, i2c_hid_reset_worker); mutex_init(&ihid->reset_lock); /* we need to allocate the command buffer without knowing the maximum diff --git a/include/linux/hid.h b/include/linux/hid.h index 3f5899c62821..9db9b7133f1a 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -784,6 +784,7 @@ struct hid_usage_id { * @suspend: invoked on suspend (NULL means nop) * @resume: invoked on resume if device was not reset (NULL means nop) * @reset_resume: invoked on resume if device was reset (NULL means nop) + * @reset: invoked if device was reset (NULL means nop) * * probe should return -errno on error, or 0 on success. During probe, * input will not be passed to raw_event unless hid_device_io_start is @@ -840,6 +841,7 @@ struct hid_driver { int (*resume)(struct hid_device *hdev); int (*reset_resume)(struct hid_device *hdev); #endif + int (*reset)(struct hid_device *hdev); /* private: */ struct device_driver driver; }; -- 2.36.0.550.gb090851708-goog