[PATCH v3 15/17] HID: add HID device reset callback

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux