diff mbox series

[v4,16/17] HID: haptic: implement HID haptic reset callback

Message ID 20221007182945.1654046-17-acz@semihalf.com (mailing list archive)
State New, archived
Delegated to: Jiri Kosina
Headers show
Series *** Implement simple haptic HID support *** | expand

Commit Message

Angela Czubak Oct. 7, 2022, 6:29 p.m. UTC
When a haptic device performs device initiated reset it puts itself back
into autonomous mode. This leads to incosistency in the actual device state
and the state configured by the kernel (manual mode). Hence user may
observe duplicated force feedback, initiated by the device itself
(due to its autonomous mode) and the host (by sending manual reports).

Configure the device and put it back into manual mode once reset is
noticed.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/hid/hid-haptic.c     | 21 +++++++++++++++++++++
 drivers/hid/hid-haptic.h     |  6 ++++++
 drivers/hid/hid-multitouch.c | 18 ++++++++++++++++++
 3 files changed, 45 insertions(+)
diff mbox series

Patch

diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index 4b3ad5168fbb4..d00103be536c2 100644
--- a/drivers/hid/hid-haptic.c
+++ b/drivers/hid/hid-haptic.c
@@ -226,6 +226,26 @@  static void switch_mode(struct hid_device *hdev, struct hid_haptic_device *hapti
 	haptic->mode = mode;
 }
 
+static void haptic_reset_work_handler(struct work_struct *work)
+{
+
+	struct hid_haptic_device *haptic = container_of(work,
+							struct hid_haptic_device,
+							reset_work);
+	struct hid_device *hdev = haptic->hdev;
+
+	if (haptic->press_ordinal_cur && haptic->release_ordinal_cur)
+		switch_mode(hdev, haptic, HID_HAPTIC_MODE_KERNEL);
+}
+
+
+void hid_haptic_reset(struct hid_device *hdev, struct hid_haptic_device *haptic)
+{
+	if (haptic->wq)
+		queue_work(haptic->wq, &haptic->reset_work);
+}
+EXPORT_SYMBOL_GPL(hid_haptic_reset);
+
 #ifdef CONFIG_PM
 void hid_haptic_resume(struct hid_device *hdev, struct hid_haptic_device *haptic)
 {
@@ -521,6 +541,7 @@  int hid_haptic_init(struct hid_device *hdev,
 	haptic->hid_usage_map[HID_HAPTIC_ORDINAL_WAVEFORMSTOP] =
 		HID_HP_WAVEFORMSTOP & HID_USAGE;
 
+	INIT_WORK(&haptic->reset_work, haptic_reset_work_handler);
 	mutex_init(&haptic->auto_trigger_mutex);
 	for (r = 0; r < haptic->auto_trigger_report->maxfield; r++)
 		parse_auto_trigger_field(haptic, haptic->auto_trigger_report->field[r]);
diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h
index 96757a4655ec7..86823c2de5dd4 100644
--- a/drivers/hid/hid-haptic.h
+++ b/drivers/hid/hid-haptic.h
@@ -65,6 +65,7 @@  struct hid_haptic_device {
 #define HID_HAPTIC_PRESS_EFFECT_ID 1
 	struct hid_haptic_effect *effect;
 	struct hid_haptic_effect stop_effect;
+	struct work_struct reset_work;
 	bool is_forcepad;
 };
 
@@ -82,6 +83,7 @@  int hid_haptic_input_mapping(struct hid_device *hdev,
 			     unsigned long **bit, int *max);
 bool hid_haptic_input_configured(struct hid_device *hdev,
 				 struct hid_haptic_device *haptic);
+void hid_haptic_reset(struct hid_device *hdev, struct hid_haptic_device *haptic);
 #ifdef CONFIG_PM
 void hid_haptic_resume(struct hid_device *hdev, struct hid_haptic_device *haptic);
 void hid_haptic_suspend(struct hid_device *hdev, struct hid_haptic_device *haptic);
@@ -120,6 +122,10 @@  bool hid_haptic_input_configured(struct hid_device *hdev,
 {
 	return 0;
 }
+static inline
+void hid_haptic_reset(struct hid_device *hdev, struct hid_haptic_device *haptic)
+{
+}
 #ifdef CONFIG_PM
 static inline
 void hid_haptic_resume(struct hid_device *hdev, struct hid_haptic_device *haptic) {}
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index c50425a4de62c..f492becbab374 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -1888,6 +1888,23 @@  static int mt_resume(struct hid_device *hdev)
 }
 #endif
 
+static int mt_reset(struct hid_device *hdev)
+{
+	struct mt_device *td = hid_get_drvdata(hdev);
+	struct hid_haptic_device *haptic = td->haptic;
+
+	if (!(hdev->claimed & HID_CLAIMED_INPUT))
+		return -ENODEV;
+
+	mt_release_contacts(hdev);
+	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
+
+	if (td->is_haptic_touchpad)
+		hid_haptic_reset(hdev, haptic);
+
+	return 0;
+}
+
 static void mt_remove(struct hid_device *hdev)
 {
 	struct mt_device *td = hid_get_drvdata(hdev);
@@ -2306,5 +2323,6 @@  static struct hid_driver mt_driver = {
 	.reset_resume = mt_reset_resume,
 	.resume = mt_resume,
 #endif
+	.reset = mt_reset,
 };
 module_hid_driver(mt_driver);