linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/17] *** Implement simple haptic HID support ***
@ 2022-05-13  9:39 Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 01/17] HID: add haptics page defines Angela Czubak
                   ` (17 more replies)
  0 siblings, 18 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

This patch series introduces changes necessary to support devices
using simple haptic HID pages.
Implementation attempts to follow the discussion below:
https://www.spinics.net/lists/linux-input/msg61091.html

Introduce new haptic defines as specified in HID Usage Tables.

Add new force feedback effect type in order to facilitate using
simple haptic force feedback.

Add INPUT_PROP_HAPTIC_TOUCHPAD to mark touchpad exposing simple haptic
support.

Add new struct hid_haptic_device so as to gather simple haptic related
configuration and current state of the device.

Add new functions to be triggered during HID input mapping and
configuration in order to detect simple haptic devices.

Modify HID input so that haptic output reports are parsed.

Initialize a haptic device.

Modify FF core so that effect IDs can be shared between multiple open file
handles.

Add shared release and press effects for a simple haptic device.

Calculate pressure resolution if units are grams or newtons.

Add support for kernel-driven mode of simple haptic device.

Toggle ABS_PRESSURE generation by input-mt on request.

Implement functions allowing switching between kernel-managed mode
and autonomous mode.

Add simple haptic support for hid-multitouch driver.

Implement EVIOCFF(TAKE|RELEASE)CONTROL ioctls so that userspace can take
and release control of shared release and press effects.

Fix i2c hid sending and getting report so that report IDs larger than 0xF
are handled correctly.

v2:
- Describe INPUT_PROP_HAPTIC_TOUCHPAD in
  Documentation/input/event-codes.rst
- Do not extract mt_get_feature(), use hid_hw_wait() instead
- Define HID_UNIT_GRAM and HID_UNIT_NEWTON
- Calculate pressure sum in input-mt if INPUT_MT_TOTAL_FORCE flags set
- Use u* instead of __u* in struct hid_haptic_device
- Solve problems with report IDS >= 0xF as Dmitry suggests

v3:
- Get rid of INPUT_PROP_HAPTIC_TOUCHPAD property as haptic device does not
  gave to be a touchpad
- Introduce notion of haptic forcepads; generate haptic feedback in kernel
  mode only for forcepads
- Generate clicks based on maximum pressure across slots instead of the sum
- Fix off-by-one bug in hid_haptic_upload_effect()
- Fix resume/suspend: issue hid_haptic_resume() in mt_resume() and
  hid_haptic_suspend() in mt_suspend()
- Add reset callback for HID i2c devices
- Implement reset callback for HID multitouch haptic devices
- Implement lid handler triggering touchpad recalibration for Redrix

Angela Czubak (17):
  HID: add haptics page defines
  Input: add FF_HID effect type
  HID: haptic: introduce hid_haptic_device
  HID: input: allow mapping of haptic output
  HID: haptic: initialize haptic device
  Input: add shared effects
  HID: haptic: implement release and press effects
  HID: input: calculate resolution for pressure
  HID: haptic: add functions handling events
  Input: MT - add INPUT_MT_MAX_FORCE flags
  HID: haptic: add hid_haptic_switch_mode
  HID: multitouch: add haptic multitouch support
  Input: introduce EVIOCFF(TAKE|RELEASE)CONTROL
  HID: haptic: add hid_haptic_change_control
  HID: add HID device reset callback
  HID: haptic: implement HID haptic reset callback
  HID: multitouch: Add lid handler for touchpad on Redrix chromebook

 drivers/hid/Kconfig                |  14 +
 drivers/hid/Makefile               |   1 +
 drivers/hid/hid-haptic.c           | 753 +++++++++++++++++++++++++++++
 drivers/hid/hid-haptic.h           | 152 ++++++
 drivers/hid/hid-input.c            |  18 +-
 drivers/hid/hid-multitouch.c       | 313 +++++++++++-
 drivers/hid/i2c-hid/i2c-hid-core.c |  21 +
 drivers/input/evdev.c              |   6 +
 drivers/input/ff-core.c            | 129 ++++-
 drivers/input/input-mt.c           |  16 +-
 include/linux/hid.h                |  31 ++
 include/linux/input.h              |   5 +
 include/linux/input/mt.h           |   1 +
 include/uapi/linux/input.h         |  26 +-
 14 files changed, 1469 insertions(+), 17 deletions(-)
 create mode 100644 drivers/hid/hid-haptic.c
 create mode 100644 drivers/hid/hid-haptic.h

-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v3 01/17] HID: add haptics page defines
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 02/17] Input: add FF_HID effect type Angela Czubak
                   ` (16 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

Introduce haptic usages as defined in HID Usage Tables specification.
Add HID units for newton and gram.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 include/linux/hid.h | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/include/linux/hid.h b/include/linux/hid.h
index 4363a63b9775..3f5899c62821 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -153,6 +153,7 @@ struct hid_item {
 #define HID_UP_TELEPHONY	0x000b0000
 #define HID_UP_CONSUMER		0x000c0000
 #define HID_UP_DIGITIZER	0x000d0000
+#define HID_UP_HAPTIC		0x000e0000
 #define HID_UP_PID		0x000f0000
 #define HID_UP_BATTERY		0x00850000
 #define HID_UP_HPVENDOR         0xff7f0000
@@ -311,6 +312,28 @@ struct hid_item {
 #define HID_DG_TOOLSERIALNUMBER	0x000d005b
 #define HID_DG_LATENCYMODE	0x000d0060
 
+#define HID_HP_SIMPLECONTROLLER	0x000e0001
+#define HID_HP_WAVEFORMLIST	0x000e0010
+#define HID_HP_DURATIONLIST	0x000e0011
+#define HID_HP_AUTOTRIGGER	0x000e0020
+#define HID_HP_MANUALTRIGGER	0x000e0021
+#define HID_HP_AUTOTRIGGERASSOCIATEDCONTROL 0x000e0022
+#define HID_HP_INTENSITY	0x000e0023
+#define HID_HP_REPEATCOUNT	0x000e0024
+#define HID_HP_RETRIGGERPERIOD	0x000e0025
+#define HID_HP_WAVEFORMVENDORPAGE	0x000e0026
+#define HID_HP_WAVEFORMVENDORID	0x000e0027
+#define HID_HP_WAVEFORMCUTOFFTIME	0x000e0028
+#define HID_HP_WAVEFORMNONE	0x000e1001
+#define HID_HP_WAVEFORMSTOP	0x000e1002
+#define HID_HP_WAVEFORMCLICK	0x000e1003
+#define HID_HP_WAVEFORMBUZZCONTINUOUS	0x000e1004
+#define HID_HP_WAVEFORMRUMBLECONTINUOUS	0x000e1005
+#define HID_HP_WAVEFORMPRESS	0x000e1006
+#define HID_HP_WAVEFORMRELEASE	0x000e1007
+#define HID_HP_VENDORWAVEFORMMIN	0x000e2001
+#define HID_HP_VENDORWAVEFORMMAX	0x000e2fff
+
 #define HID_BAT_ABSOLUTESTATEOFCHARGE	0x00850065
 
 #define HID_VD_ASUS_CUSTOM_MEDIA_KEYS	0xff310076
@@ -398,6 +421,12 @@ struct hid_item {
 #define HID_REPORT_PROTOCOL	1
 #define HID_BOOT_PROTOCOL	0
 
+/*
+ * HID units
+ */
+#define HID_UNIT_GRAM		0x0101
+#define HID_UNIT_NEWTON		0xe111
+
 /*
  * This is the global environment of the parser. This information is
  * persistent for main-items. The global environment can be saved and
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 02/17] Input: add FF_HID effect type
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 01/17] HID: add haptics page defines Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 03/17] HID: haptic: introduce hid_haptic_device Angela Czubak
                   ` (15 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

FF_HID effect type can be used to trigger haptic feedback with HID simple
haptic usages.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 include/uapi/linux/input.h | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index ee3127461ee0..ccf43a21d43d 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -424,6 +424,24 @@ struct ff_rumble_effect {
 	__u16 weak_magnitude;
 };
 
+/**
+ * struct ff_hid_effect
+ * @hid_usage: hid_usage according to Haptics page (WAVEFORM_CLICK, etc.)
+ * @vendor_id: the waveform vendor ID if hid_usage is in the vendor-defined range
+ * @vendor_waveform_page: the vendor waveform page if hid_usage is in the vendor-defined range
+ * @intensity: strength of the effect as percentage
+ * @repeat_count: number of times to retrigger effect
+ * @retrigger_period: time before effect is retriggered (in ms)
+ */
+struct ff_hid_effect {
+	__u16 hid_usage;
+	__u16 vendor_id;
+	__u8  vendor_waveform_page;
+	__u16 intensity;
+	__u16 repeat_count;
+	__u16 retrigger_period;
+};
+
 /**
  * struct ff_effect - defines force feedback effect
  * @type: type of the effect (FF_CONSTANT, FF_PERIODIC, FF_RAMP, FF_SPRING,
@@ -460,6 +478,7 @@ struct ff_effect {
 		struct ff_periodic_effect periodic;
 		struct ff_condition_effect condition[2]; /* One for each axis */
 		struct ff_rumble_effect rumble;
+		struct ff_hid_effect hid;
 	} u;
 };
 
@@ -467,6 +486,7 @@ struct ff_effect {
  * Force feedback effect types
  */
 
+#define FF_HID		0x4f
 #define FF_RUMBLE	0x50
 #define FF_PERIODIC	0x51
 #define FF_CONSTANT	0x52
@@ -476,7 +496,7 @@ struct ff_effect {
 #define FF_INERTIA	0x56
 #define FF_RAMP		0x57
 
-#define FF_EFFECT_MIN	FF_RUMBLE
+#define FF_EFFECT_MIN	FF_HID
 #define FF_EFFECT_MAX	FF_RAMP
 
 /*
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 03/17] HID: haptic: introduce hid_haptic_device
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 01/17] HID: add haptics page defines Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 02/17] Input: add FF_HID effect type Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 04/17] HID: input: allow mapping of haptic output Angela Czubak
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

Define a new structure that contains simple haptic device configuration
as well as current state.
Add functions that recognize auto trigger and manual trigger reports
as well as save their addresses.
Recognize the input device as a haptic one if the reports are found.
Verify that the pressure unit is either grams or newtons.
Mark the input device as a haptic forcepad if the unit is correct.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/hid/Kconfig      |   3 ++
 drivers/hid/Makefile     |   1 +
 drivers/hid/hid-haptic.c |  65 ++++++++++++++++++++++
 drivers/hid/hid-haptic.h | 113 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 182 insertions(+)
 create mode 100644 drivers/hid/hid-haptic.c
 create mode 100644 drivers/hid/hid-haptic.h

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index a95a7cbc4a59..c892cfea3bb5 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -89,6 +89,9 @@ config HID_GENERIC
 
 	If unsure, say Y.
 
+config HID_HAPTIC
+	bool
+
 menu "Special HID drivers"
 	depends on HID
 
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 345ac5581bd8..bfd09957e58d 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -4,6 +4,7 @@
 #
 hid-y			:= hid-core.o hid-input.o hid-quirks.o
 hid-$(CONFIG_DEBUG_FS)		+= hid-debug.o
+hid-$(CONFIG_HID_HAPTIC)	+= hid-haptic.o
 
 obj-$(CONFIG_HID)		+= hid.o
 obj-$(CONFIG_UHID)		+= uhid.o
diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
new file mode 100644
index 000000000000..ea1004319bc0
--- /dev/null
+++ b/drivers/hid/hid-haptic.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  HID Haptic support for Linux
+ *
+ *  Copyright (c) 2021 Angela Czubak <acz@semihalf.com>
+ */
+
+#include "hid-haptic.h"
+
+void hid_haptic_feature_mapping(struct hid_device *hdev,
+				struct hid_haptic_device *haptic,
+				struct hid_field *field, struct hid_usage *usage)
+{
+	if (usage->hid == HID_HP_AUTOTRIGGER) {
+		if (usage->usage_index >= field->report_count) {
+			dev_err(&hdev->dev,
+				"HID_HP_AUTOTRIGGER out of range\n");
+			return;
+		}
+
+		hid_device_io_start(hdev);
+		hid_hw_request(hdev, field->report, HID_REQ_GET_REPORT);
+		hid_hw_wait(hdev);
+		hid_device_io_stop(hdev);
+		haptic->default_auto_trigger =
+			field->value[usage->usage_index];
+		haptic->auto_trigger_report = field->report;
+	}
+}
+EXPORT_SYMBOL_GPL(hid_haptic_feature_mapping);
+
+bool hid_haptic_is_forcepad(struct hid_haptic_device *haptic,
+			    struct hid_input *hi, struct hid_field *field)
+{
+	if (field->unit == HID_UNIT_GRAM || field->unit == HID_UNIT_NEWTON)
+		haptic->is_forcepad = true;
+	return haptic->is_forcepad;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_is_forcepad);
+
+int hid_haptic_input_mapping(struct hid_device *hdev,
+			     struct hid_haptic_device *haptic,
+			     struct hid_input *hi,
+			     struct hid_field *field, struct hid_usage *usage,
+			     unsigned long **bit, int *max)
+{
+	if (usage->hid == HID_HP_MANUALTRIGGER) {
+		haptic->manual_trigger_report = field->report;
+		/* we don't really want to map these fields */
+		return -1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_input_mapping);
+
+bool hid_haptic_input_configured(struct hid_device *hdev,
+				 struct hid_haptic_device *haptic)
+{
+	if (haptic->auto_trigger_report && haptic->manual_trigger_report)
+		return true;
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_input_configured);
diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h
new file mode 100644
index 000000000000..ea96b0fb540f
--- /dev/null
+++ b/drivers/hid/hid-haptic.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *  HID Haptic support for Linux
+ *
+ *  Copyright (c) 2021 Angela Czubak <acz@semihalf.com>
+ */
+
+/*
+ */
+
+
+#include <linux/hid.h>
+
+#define HID_HAPTIC_ORDINAL_WAVEFORMNONE 1
+#define HID_HAPTIC_ORDINAL_WAVEFORMSTOP 2
+
+#define HID_HAPTIC_PRESS_THRESH 200
+#define HID_HAPTIC_RELEASE_THRESH 180
+
+#define HID_HAPTIC_MODE_DEVICE 0
+#define HID_HAPTIC_MODE_KERNEL 1
+
+struct hid_haptic_effect {
+	u8 *report_buf;
+	struct input_dev *input_dev;
+	struct work_struct work;
+	struct list_head control;
+	struct mutex control_mutex;
+};
+
+struct hid_haptic_effect_node {
+	struct list_head node;
+	struct file *file;
+};
+
+struct hid_haptic_device {
+	struct input_dev *input_dev;
+	struct hid_device *hdev;
+	struct hid_report *auto_trigger_report;
+	struct mutex auto_trigger_mutex;
+	struct workqueue_struct *wq;
+	struct hid_report *manual_trigger_report;
+	struct mutex manual_trigger_mutex;
+	size_t manual_trigger_report_len;
+	int pressed_state;
+	s32 pressure;
+	s32 force_logical_minimum;
+	s32 force_physical_minimum;
+	s32 force_resolution;
+	u32 press_threshold;
+	u32 release_threshold;
+	u32 mode;
+	u32 default_auto_trigger;
+	u32 vendor_page;
+	u32 vendor_id;
+	u32 max_waveform_id;
+	u32 max_duration_id;
+	u16 *hid_usage_map;
+	u32 *duration_map;
+	u16 press_ordinal_orig;
+	u16 press_ordinal_cur;
+	u16 release_ordinal_orig;
+	u16 release_ordinal_cur;
+#define HID_HAPTIC_RELEASE_EFFECT_ID 0
+#define HID_HAPTIC_PRESS_EFFECT_ID 1
+	struct hid_haptic_effect *effect;
+	struct hid_haptic_effect stop_effect;
+	bool is_forcepad;
+};
+
+#ifdef CONFIG_MULTITOUCH_HAPTIC
+void hid_haptic_feature_mapping(struct hid_device *hdev,
+				struct hid_haptic_device *haptic,
+				struct hid_field *field, struct hid_usage
+				*usage);
+bool hid_haptic_is_forcepad(struct hid_haptic_device *haptic,
+			    struct hid_input *hi, struct hid_field *field);
+int hid_haptic_input_mapping(struct hid_device *hdev,
+			     struct hid_haptic_device *haptic,
+			     struct hid_input *hi,
+			     struct hid_field *field, struct hid_usage *usage,
+			     unsigned long **bit, int *max);
+bool hid_haptic_input_configured(struct hid_device *hdev,
+				 struct hid_haptic_device *haptic);
+#else
+static inline
+void hid_haptic_feature_mapping(struct hid_device *hdev,
+				struct hid_haptic_device *haptic,
+				struct hid_field *field, struct hid_usage
+				*usage)
+{}
+static inline
+bool hid_haptic_is_forcepad(struct hid_haptic_device *haptic,
+			    struct hid_input *hi, struct hid_field *field)
+{
+	return false;
+}
+static inline
+int hid_haptic_input_mapping(struct hid_device *hdev,
+			     struct hid_haptic_device *haptic,
+			     struct hid_input *hi,
+			     struct hid_field *field, struct hid_usage *usage,
+			     unsigned long **bit, int *max)
+{
+	return 0;
+}
+static inline
+bool hid_haptic_input_configured(struct hid_device *hdev,
+				 struct hid_haptic_device *haptic)
+{
+	return 0;
+}
+#endif
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 04/17] HID: input: allow mapping of haptic output
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (2 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 03/17] HID: haptic: introduce hid_haptic_device Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 05/17] HID: haptic: initialize haptic device Angela Czubak
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

This change makes it possible to parse output reports by input mapping
functions by HID drivers.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/hid/hid-input.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index c6b27aab9041..77f2930f78f5 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -649,9 +649,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
 	if (field->report_count < 1)
 		goto ignore;
 
-	/* only LED usages are supported in output fields */
+	/* only LED and HAPTIC usages are supported in output fields */
 	if (field->report_type == HID_OUTPUT_REPORT &&
-			(usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
+	    (usage->hid & HID_USAGE_PAGE) != HID_UP_LED &&
+	    (usage->hid & HID_USAGE_PAGE) != HID_UP_HAPTIC) {
 		goto ignore;
 	}
 
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 05/17] HID: haptic: initialize haptic device
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (3 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 04/17] HID: input: allow mapping of haptic output Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 06/17] Input: add shared effects Angela Czubak
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

Add hid_haptic_init(). Parse autotrigger report to retrieve ordinals for
press and release waveforms.
Implement force feedback functions.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/hid/hid-haptic.c | 447 +++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-haptic.h |   6 +
 2 files changed, 453 insertions(+)

diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index ea1004319bc0..327106152d79 100644
--- a/drivers/hid/hid-haptic.c
+++ b/drivers/hid/hid-haptic.c
@@ -5,12 +5,16 @@
  *  Copyright (c) 2021 Angela Czubak <acz@semihalf.com>
  */
 
+#include <linux/module.h>
+
 #include "hid-haptic.h"
 
 void hid_haptic_feature_mapping(struct hid_device *hdev,
 				struct hid_haptic_device *haptic,
 				struct hid_field *field, struct hid_usage *usage)
 {
+	u16 usage_hid;
+
 	if (usage->hid == HID_HP_AUTOTRIGGER) {
 		if (usage->usage_index >= field->report_count) {
 			dev_err(&hdev->dev,
@@ -25,6 +29,20 @@ void hid_haptic_feature_mapping(struct hid_device *hdev,
 		haptic->default_auto_trigger =
 			field->value[usage->usage_index];
 		haptic->auto_trigger_report = field->report;
+	} else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_ORDINAL) {
+		usage_hid = usage->hid & HID_USAGE;
+		switch (field->logical) {
+		case HID_HP_WAVEFORMLIST:
+			if (usage_hid > haptic->max_waveform_id)
+				haptic->max_waveform_id = usage_hid;
+			break;
+		case HID_HP_DURATIONLIST:
+			if (usage_hid > haptic->max_duration_id)
+				haptic->max_duration_id = usage_hid;
+			break;
+		default:
+			break;
+		}
 	}
 }
 EXPORT_SYMBOL_GPL(hid_haptic_feature_mapping);
@@ -63,3 +81,432 @@ bool hid_haptic_input_configured(struct hid_device *hdev,
 	return false;
 }
 EXPORT_SYMBOL_GPL(hid_haptic_input_configured);
+
+static void parse_auto_trigger_field(struct hid_haptic_device *haptic,
+				     struct hid_field *field)
+{
+	int count = field->report_count;
+	int n;
+	u16 usage_hid;
+
+	for (n = 0; n < count; n++) {
+		switch (field->usage[n].hid & HID_USAGE_PAGE) {
+		case HID_UP_ORDINAL:
+			usage_hid = field->usage[n].hid & HID_USAGE;
+			switch (field->logical) {
+			case HID_HP_WAVEFORMLIST:
+				haptic->hid_usage_map[usage_hid] = field->value[n];
+				if (field->value[n] ==
+				    (HID_HP_WAVEFORMPRESS & HID_USAGE)) {
+					haptic->press_ordinal_orig = usage_hid;
+					haptic->press_ordinal_cur = usage_hid;
+				} else if (field->value[n] ==
+					   (HID_HP_WAVEFORMRELEASE & HID_USAGE)) {
+					haptic->release_ordinal_orig = usage_hid;
+					haptic->release_ordinal_cur = usage_hid;
+				}
+				break;
+			case HID_HP_DURATIONLIST:
+				haptic->duration_map[usage_hid] =
+					field->value[n];
+				break;
+			default:
+				break;
+			}
+			break;
+		case HID_UP_HAPTIC:
+			switch (field->usage[n].hid) {
+			case HID_HP_WAVEFORMVENDORID:
+				haptic->vendor_id = field->value[n];
+				break;
+			case HID_HP_WAVEFORMVENDORPAGE:
+				haptic->vendor_page = field->value[n];
+				break;
+			default:
+				break;
+			}
+			break;
+		default:
+			/* Should not really happen */
+			break;
+		}
+	}
+}
+
+static void fill_effect_buf(struct hid_haptic_device *haptic,
+			    struct ff_hid_effect *effect,
+			    struct hid_haptic_effect *haptic_effect,
+			    int waveform_ordinal)
+{
+	struct hid_report *rep = haptic->manual_trigger_report;
+	struct hid_usage *usage;
+	struct hid_field *field;
+	s32 value;
+	int i, j;
+	u8 *buf = haptic_effect->report_buf;
+
+	mutex_lock(&haptic->manual_trigger_mutex);
+	for (i = 0; i < rep->maxfield; i++) {
+		field = rep->field[i];
+		/* Ignore if report count is out of bounds. */
+		if (field->report_count < 1)
+			continue;
+
+		for (j = 0; j < field->maxusage; j++) {
+			usage = &field->usage[j];
+
+			switch (usage->hid) {
+			case HID_HP_INTENSITY:
+				if (effect->intensity > 100) {
+					value = field->logical_maximum;
+				} else {
+					value = field->logical_minimum +
+						effect->intensity *
+						(field->logical_maximum -
+						 field->logical_minimum) / 100;
+				}
+				break;
+			case HID_HP_REPEATCOUNT:
+				value = effect->repeat_count;
+				break;
+			case HID_HP_RETRIGGERPERIOD:
+				value = effect->retrigger_period;
+				break;
+			case HID_HP_MANUALTRIGGER:
+				value = waveform_ordinal;
+				break;
+			default:
+				break;
+			}
+
+			field->value[j] = value;
+		}
+	}
+
+	hid_output_report(rep, buf);
+	mutex_unlock(&haptic->manual_trigger_mutex);
+}
+
+static int hid_haptic_upload_effect(struct input_dev *dev, struct ff_effect *effect,
+				    struct ff_effect *old)
+{
+	struct ff_device *ff = dev->ff;
+	struct hid_haptic_device *haptic = ff->private;
+	int i, ordinal = 0;
+
+	/* If vendor range, check vendor id and page */
+	if (effect->u.hid.hid_usage >= (HID_HP_VENDORWAVEFORMMIN & HID_USAGE) &&
+	    effect->u.hid.hid_usage <= (HID_HP_VENDORWAVEFORMMAX & HID_USAGE) &&
+	    (effect->u.hid.vendor_id != haptic->vendor_id ||
+	     effect->u.hid.vendor_waveform_page != haptic->vendor_page))
+		return -EINVAL;
+
+	/* Check hid_usage */
+	for (i = 1; i <= haptic->max_waveform_id; i++) {
+		if (haptic->hid_usage_map[i] == effect->u.hid.hid_usage) {
+			ordinal = i;
+			break;
+		}
+	}
+	if (ordinal < 1)
+		return -EINVAL;
+
+	/* Fill the buffer for the efect id */
+	fill_effect_buf(haptic, &effect->u.hid, &haptic->effect[effect->id],
+			ordinal);
+
+	return 0;
+}
+
+static int play_effect(struct hid_device *hdev, struct hid_haptic_device *haptic,
+		       struct hid_haptic_effect *effect)
+{
+	int ret;
+
+	ret = hid_hw_output_report(hdev, effect->report_buf,
+				   haptic->manual_trigger_report_len);
+	if (ret < 0) {
+		ret = hid_hw_raw_request(hdev,
+					 haptic->manual_trigger_report->id,
+					 effect->report_buf,
+					 haptic->manual_trigger_report_len,
+					 HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
+	}
+
+	return ret;
+}
+
+static void haptic_work_handler(struct work_struct *work)
+{
+
+	struct hid_haptic_effect *effect = container_of(work,
+							struct hid_haptic_effect,
+							work);
+	struct input_dev *dev = effect->input_dev;
+	struct hid_device *hdev = input_get_drvdata(dev);
+	struct hid_haptic_device *haptic = dev->ff->private;
+
+	mutex_lock(&haptic->manual_trigger_mutex);
+	if (effect != &haptic->stop_effect)
+		play_effect(hdev, haptic, &haptic->stop_effect);
+
+	play_effect(hdev, haptic, effect);
+	mutex_unlock(&haptic->manual_trigger_mutex);
+
+}
+
+static int hid_haptic_playback(struct input_dev *dev, int effect_id, int value)
+{
+	struct hid_haptic_device *haptic = dev->ff->private;
+
+	if (value)
+		queue_work(haptic->wq, &haptic->effect[effect_id].work);
+	else
+		queue_work(haptic->wq, &haptic->stop_effect.work);
+
+	return 0;
+}
+
+static void effect_set_default(struct ff_effect *effect)
+{
+	effect->type = FF_HID;
+	effect->id = -1;
+	effect->u.hid.hid_usage = HID_HP_WAVEFORMNONE & HID_USAGE;
+	effect->u.hid.intensity = 100;
+	effect->u.hid.retrigger_period = 0;
+	effect->u.hid.repeat_count = 0;
+}
+
+static int hid_haptic_erase(struct input_dev *dev, int effect_id)
+{
+	struct hid_haptic_device *haptic = dev->ff->private;
+	struct ff_effect effect;
+	int ordinal;
+
+	if (!haptic->is_forcepad)
+		return 0;
+
+	effect_set_default(&effect);
+	switch (effect_id) {
+	case HID_HAPTIC_RELEASE_EFFECT_ID:
+		ordinal = haptic->release_ordinal_orig;
+		if (!ordinal)
+			ordinal = HID_HAPTIC_ORDINAL_WAVEFORMNONE;
+		else
+			effect.u.hid.hid_usage = HID_HP_WAVEFORMRELEASE &
+				HID_USAGE;
+		fill_effect_buf(haptic, &effect.u.hid, &haptic->effect[effect_id],
+				ordinal);
+		break;
+	case HID_HAPTIC_PRESS_EFFECT_ID:
+		ordinal = haptic->press_ordinal_orig;
+		if (!ordinal)
+			ordinal = HID_HAPTIC_ORDINAL_WAVEFORMNONE;
+		else
+			effect.u.hid.hid_usage = HID_HP_WAVEFORMPRESS &
+				HID_USAGE;
+		fill_effect_buf(haptic, &effect.u.hid, &haptic->effect[effect_id],
+				ordinal);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static void hid_haptic_destroy(struct ff_device *ff)
+{
+	struct hid_haptic_device *haptic = ff->private;
+	struct hid_device *hdev = haptic->hdev;
+	int r;
+
+	if (hdev)
+		put_device(&hdev->dev);
+
+	kfree(haptic->stop_effect.report_buf);
+	haptic->stop_effect.report_buf = NULL;
+
+	if (haptic->effect) {
+		for (r = 0; r < ff->max_effects; r++)
+			kfree(haptic->effect[r].report_buf);
+		kfree(haptic->effect);
+	}
+	haptic->effect = NULL;
+
+	destroy_workqueue(haptic->wq);
+	haptic->wq = NULL;
+
+	kfree(haptic->duration_map);
+	haptic->duration_map = NULL;
+
+	kfree(haptic->hid_usage_map);
+	haptic->hid_usage_map = NULL;
+
+	module_put(THIS_MODULE);
+}
+
+int hid_haptic_init(struct hid_device *hdev,
+		    struct hid_haptic_device **haptic_ptr)
+{
+	struct hid_haptic_device *haptic = *haptic_ptr;
+	struct input_dev *dev = NULL;
+	struct hid_input *hidinput;
+	struct ff_device *ff;
+	int ret = 0, r;
+	struct ff_hid_effect stop_effect = {
+		.hid_usage = HID_HP_WAVEFORMSTOP & HID_USAGE,
+	};
+	const char *prefix = "hid-haptic";
+	char *name;
+	int (*flush)(struct input_dev *dev, struct file *file);
+	int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
+
+	haptic->hdev = hdev;
+	haptic->max_waveform_id = max(2u, haptic->max_waveform_id);
+	haptic->max_duration_id = max(2u, haptic->max_duration_id);
+
+	haptic->hid_usage_map = kcalloc(haptic->max_waveform_id + 1,
+					sizeof(u16), GFP_KERNEL);
+	if (!haptic->hid_usage_map) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+	haptic->duration_map = kcalloc(haptic->max_duration_id + 1,
+				       sizeof(u32), GFP_KERNEL);
+	if (!haptic->duration_map) {
+		ret = -ENOMEM;
+		goto usage_map;
+	}
+
+	if (haptic->max_waveform_id != haptic->max_duration_id)
+		dev_warn(&hdev->dev,
+			 "Haptic duration and waveform lists have different max id (%u and %u).\n",
+			 haptic->max_duration_id, haptic->max_waveform_id);
+
+	haptic->hid_usage_map[HID_HAPTIC_ORDINAL_WAVEFORMNONE] =
+		HID_HP_WAVEFORMNONE & HID_USAGE;
+	haptic->hid_usage_map[HID_HAPTIC_ORDINAL_WAVEFORMSTOP] =
+		HID_HP_WAVEFORMSTOP & HID_USAGE;
+
+	for (r = 0; r < haptic->auto_trigger_report->maxfield; r++)
+		parse_auto_trigger_field(haptic, haptic->auto_trigger_report->field[r]);
+
+	list_for_each_entry(hidinput, &hdev->inputs, list) {
+		if (hidinput->application == HID_DG_TOUCHPAD) {
+			dev = hidinput->input;
+			break;
+		}
+	}
+
+	if (!dev) {
+		dev_err(&hdev->dev, "Failed to find the input device\n");
+		ret = -ENODEV;
+		goto duration_map;
+	}
+
+	haptic->input_dev = dev;
+	haptic->manual_trigger_report_len =
+		hid_report_len(haptic->manual_trigger_report);
+	mutex_init(&haptic->manual_trigger_mutex);
+	name = kmalloc(strlen(prefix) + strlen(hdev->name) + 2, GFP_KERNEL);
+	if (name) {
+		sprintf(name, "%s %s", prefix, hdev->name);
+		haptic->wq = create_singlethread_workqueue(name);
+		kfree(name);
+	}
+	if (!haptic->wq) {
+		ret = -ENOMEM;
+		goto duration_map;
+	}
+	haptic->effect = kcalloc(FF_MAX_EFFECTS,
+				 sizeof(struct hid_haptic_effect), GFP_KERNEL);
+	if (!haptic->effect) {
+		ret = -ENOMEM;
+		goto output_queue;
+	}
+	for (r = 0; r < FF_MAX_EFFECTS; r++) {
+		haptic->effect[r].report_buf =
+			hid_alloc_report_buf(haptic->manual_trigger_report,
+					     GFP_KERNEL);
+		if (!haptic->effect[r].report_buf) {
+			dev_err(&hdev->dev,
+				"Failed to allocate a buffer for an effect.\n");
+			ret = -ENOMEM;
+			goto buffer_free;
+		}
+		haptic->effect[r].input_dev = dev;
+		INIT_WORK(&haptic->effect[r].work, haptic_work_handler);
+	}
+	haptic->stop_effect.report_buf =
+		hid_alloc_report_buf(haptic->manual_trigger_report,
+				     GFP_KERNEL);
+	if (!haptic->stop_effect.report_buf) {
+		dev_err(&hdev->dev,
+			"Failed to allocate a buffer for stop effect.\n");
+		ret = -ENOMEM;
+		goto buffer_free;
+	}
+	haptic->stop_effect.input_dev = dev;
+	INIT_WORK(&haptic->stop_effect.work, haptic_work_handler);
+	fill_effect_buf(haptic, &stop_effect, &haptic->stop_effect,
+			HID_HAPTIC_ORDINAL_WAVEFORMSTOP);
+
+	input_set_capability(dev, EV_FF, FF_HID);
+
+	flush = dev->flush;
+	event = dev->event;
+	ret = input_ff_create(dev, FF_MAX_EFFECTS);
+	if (ret) {
+		dev_err(&hdev->dev, "Failed to create ff device.\n");
+		goto stop_buffer_free;
+	}
+
+	ff = dev->ff;
+	ff->private = haptic;
+	ff->upload = hid_haptic_upload_effect;
+	ff->playback = hid_haptic_playback;
+	ff->erase = hid_haptic_erase;
+	ff->destroy = hid_haptic_destroy;
+	if (!try_module_get(THIS_MODULE)) {
+		dev_err(&hdev->dev, "Failed to increase module count.\n");
+		goto input_free;
+	}
+	if (!get_device(&hdev->dev)) {
+		dev_err(&hdev->dev, "Failed to get hdev device.\n");
+		module_put(THIS_MODULE);
+		goto input_free;
+	}
+	return 0;
+
+input_free:
+	input_ff_destroy(dev);
+	/* Do not let double free happen, input_ff_destroy will call
+	 * hid_haptic_destroy.
+	 */
+	*haptic_ptr = NULL;
+	/* Restore dev flush and event */
+	dev->flush = flush;
+	dev->event = event;
+	return ret;
+stop_buffer_free:
+	kfree(haptic->stop_effect.report_buf);
+	haptic->stop_effect.report_buf = NULL;
+buffer_free:
+	while (--r >= 0)
+		kfree(haptic->effect[r].report_buf);
+	kfree(haptic->effect);
+	haptic->effect = NULL;
+output_queue:
+	destroy_workqueue(haptic->wq);
+	haptic->wq = NULL;
+duration_map:
+	kfree(haptic->duration_map);
+	haptic->duration_map = NULL;
+usage_map:
+	kfree(haptic->hid_usage_map);
+	haptic->hid_usage_map = NULL;
+exit:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_init);
diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h
index ea96b0fb540f..67096cc8c233 100644
--- a/drivers/hid/hid-haptic.h
+++ b/drivers/hid/hid-haptic.h
@@ -82,6 +82,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);
+int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr);
 #else
 static inline
 void hid_haptic_feature_mapping(struct hid_device *hdev,
@@ -110,4 +111,9 @@ bool hid_haptic_input_configured(struct hid_device *hdev,
 {
 	return 0;
 }
+static inline
+int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr)
+{
+	return 0;
+}
 #endif
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 06/17] Input: add shared effects
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (4 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 05/17] HID: haptic: initialize haptic device Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 07/17] HID: haptic: implement release and press effects Angela Czubak
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

If an effect is uploaded with file handle equal UINTPTR_MAX assume this
effect should be shared and so may be modified using different file
handles.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/input/ff-core.c | 40 ++++++++++++++++++++++++++++++++++------
 1 file changed, 34 insertions(+), 6 deletions(-)

diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
index fa8d1a466014..1a9a5401acb5 100644
--- a/drivers/input/ff-core.c
+++ b/drivers/input/ff-core.c
@@ -34,6 +34,23 @@ static int check_effect_access(struct ff_device *ff, int effect_id,
 	return 0;
 }
 
+/*
+ * Check that the effect_id is a valid effect and whether the effect
+ * is shared
+ */
+static int check_effect_shared(struct ff_device *ff, int effect_id)
+{
+	if (effect_id < 0 || effect_id >= ff->max_effects ||
+	    !ff->effect_owners[effect_id])
+		return -EINVAL;
+
+	/* Shared effect */
+	if (ff->effect_owners[effect_id] == (struct file *)UINTPTR_MAX)
+		return 0;
+
+	return -EACCES;
+}
+
 /*
  * Checks whether 2 effects can be combined together
  */
@@ -139,8 +156,11 @@ int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
 		id = effect->id;
 
 		ret = check_effect_access(ff, id, file);
-		if (ret)
-			goto out;
+		if (ret) {
+			ret = check_effect_shared(ff, id);
+			if (ret)
+				goto out;
+		}
 
 		old = &ff->effects[id];
 
@@ -174,21 +194,29 @@ static int erase_effect(struct input_dev *dev, int effect_id,
 {
 	struct ff_device *ff = dev->ff;
 	int error;
+	bool shared = false;
 
 	error = check_effect_access(ff, effect_id, file);
-	if (error)
-		return error;
+	if (error) {
+		error = check_effect_shared(ff, effect_id);
+		if (!error)
+			shared = true;
+		else
+			return error;
+	}
 
 	spin_lock_irq(&dev->event_lock);
 	ff->playback(dev, effect_id, 0);
-	ff->effect_owners[effect_id] = NULL;
+	if (!shared)
+		ff->effect_owners[effect_id] = NULL;
 	spin_unlock_irq(&dev->event_lock);
 
 	if (ff->erase) {
 		error = ff->erase(dev, effect_id);
 		if (error) {
 			spin_lock_irq(&dev->event_lock);
-			ff->effect_owners[effect_id] = file;
+			if (!shared)
+				ff->effect_owners[effect_id] = file;
 			spin_unlock_irq(&dev->event_lock);
 
 			return error;
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 07/17] HID: haptic: implement release and press effects
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (5 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 06/17] Input: add shared effects Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 08/17] HID: input: calculate resolution for pressure Angela Czubak
                   ` (10 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

Upload shared haptic affects for release and press waveforms if a device
exposes them and it is a forcepad

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/hid/hid-haptic.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index 327106152d79..3301bf27dfde 100644
--- a/drivers/hid/hid-haptic.c
+++ b/drivers/hid/hid-haptic.c
@@ -361,6 +361,7 @@ int hid_haptic_init(struct hid_device *hdev,
 	char *name;
 	int (*flush)(struct input_dev *dev, struct file *file);
 	int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
+	struct ff_effect release_effect, press_effect;
 
 	haptic->hdev = hdev;
 	haptic->max_waveform_id = max(2u, haptic->max_waveform_id);
@@ -477,8 +478,44 @@ int hid_haptic_init(struct hid_device *hdev,
 		module_put(THIS_MODULE);
 		goto input_free;
 	}
+
+	if (!haptic->is_forcepad)
+		goto exit;
+
+	effect_set_default(&release_effect);
+	if (haptic->release_ordinal_orig)
+		release_effect.u.hid.hid_usage = HID_HP_WAVEFORMRELEASE &
+			HID_USAGE;
+	ret = input_ff_upload(dev, &release_effect, (struct file *)UINTPTR_MAX);
+	if (ret || release_effect.id != HID_HAPTIC_RELEASE_EFFECT_ID) {
+		if (!ret) {
+			ret = -EBUSY;
+			input_ff_erase(dev, release_effect.id,
+				       (struct file *)UINTPTR_MAX);
+		}
+		dev_err(&hdev->dev,
+			"Failed to allocate id 0 for release effect.\n");
+		goto input_free;
+	}
+	effect_set_default(&press_effect);
+	if (haptic->press_ordinal_orig)
+		press_effect.u.hid.hid_usage = HID_HP_WAVEFORMPRESS & HID_USAGE;
+	ret = input_ff_upload(dev, &press_effect, (struct file *)UINTPTR_MAX);
+	if (ret || press_effect.id != HID_HAPTIC_PRESS_EFFECT_ID) {
+		if (!ret) {
+			ret = -EBUSY;
+			input_ff_erase(dev, press_effect.id,
+				       (struct file *)UINTPTR_MAX);
+		}
+		dev_err(&hdev->dev,
+			"Failed to allocate id 1 for press effect.\n");
+		goto release_free;
+	}
+
 	return 0;
 
+release_free:
+	input_ff_erase(dev, release_effect.id, (struct file *)UINTPTR_MAX);
 input_free:
 	input_ff_destroy(dev);
 	/* Do not let double free happen, input_ff_destroy will call
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 08/17] HID: input: calculate resolution for pressure
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (6 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 07/17] HID: haptic: implement release and press effects Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 09/17] HID: haptic: add functions handling events Angela Czubak
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

Assume that if the pressure is given in newtons it should be normalized
to grams. If the pressure has no unit do not calculate resolution.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/hid/hid-input.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 77f2930f78f5..c9f8405c0522 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -303,6 +303,19 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
 		}
 		break;
 
+	case ABS_PRESSURE:
+	case ABS_MT_PRESSURE:
+		if (field->unit == HID_UNIT_NEWTON) {
+			/* Convert to grams, 1 newton is 101.97 grams */
+			prev = physical_extents;
+			physical_extents *= 10197;
+			if (physical_extents < prev)
+				return 0;
+			unit_exponent -= 2;
+		} else if (field->unit != HID_UNIT_GRAM) {
+			return 0;
+		}
+		break;
 	default:
 		return 0;
 	}
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 09/17] HID: haptic: add functions handling events
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (7 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 08/17] HID: input: calculate resolution for pressure Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 10/17] Input: MT - add INPUT_MT_MAX_FORCE flags Angela Czubak
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

Implement hid_haptic_handle_press_release() which generates haptic feedback
as well as saves the pressed state of the haptic device.
Function hid_haptic_handle_input() inserts BTN_LEFT and ABS_PRESSURE events
if the device is in kernel mode.
Add functions to increase and reset the state of the pressure detected by
the device.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/hid/hid-haptic.c | 73 +++++++++++++++++++++++++++++++++++++++-
 drivers/hid/hid-haptic.h | 18 ++++++++++
 2 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index 3301bf27dfde..9b89a1f8a631 100644
--- a/drivers/hid/hid-haptic.c
+++ b/drivers/hid/hid-haptic.c
@@ -50,8 +50,13 @@ EXPORT_SYMBOL_GPL(hid_haptic_feature_mapping);
 bool hid_haptic_is_forcepad(struct hid_haptic_device *haptic,
 			    struct hid_input *hi, struct hid_field *field)
 {
-	if (field->unit == HID_UNIT_GRAM || field->unit == HID_UNIT_NEWTON)
+	if (field->unit == HID_UNIT_GRAM || field->unit == HID_UNIT_NEWTON) {
+		haptic->force_logical_minimum = field->logical_minimum;
+		haptic->force_physical_minimum = field->physical_minimum;
+		haptic->force_resolution = input_abs_get_res(hi->input,
+							     ABS_MT_PRESSURE);
 		haptic->is_forcepad = true;
+	}
 	return haptic->is_forcepad;
 }
 EXPORT_SYMBOL_GPL(hid_haptic_is_forcepad);
@@ -346,6 +351,12 @@ static void hid_haptic_destroy(struct ff_device *ff)
 	module_put(THIS_MODULE);
 }
 
+static u32 convert_force_to_logical(struct hid_haptic_device *haptic, u32 value)
+{
+	return (value - haptic->force_physical_minimum) *
+		haptic->force_resolution + haptic->force_logical_minimum;
+}
+
 int hid_haptic_init(struct hid_device *hdev,
 		    struct hid_haptic_device **haptic_ptr)
 {
@@ -479,9 +490,16 @@ int hid_haptic_init(struct hid_device *hdev,
 		goto input_free;
 	}
 
+	haptic->mode = HID_HAPTIC_MODE_DEVICE;
+
 	if (!haptic->is_forcepad)
 		goto exit;
 
+	haptic->press_threshold = convert_force_to_logical(haptic,
+							   HID_HAPTIC_PRESS_THRESH);
+	haptic->release_threshold = convert_force_to_logical(haptic,
+							     HID_HAPTIC_RELEASE_THRESH);
+
 	effect_set_default(&release_effect);
 	if (haptic->release_ordinal_orig)
 		release_effect.u.hid.hid_usage = HID_HP_WAVEFORMRELEASE &
@@ -547,3 +565,56 @@ int hid_haptic_init(struct hid_device *hdev,
 	return ret;
 }
 EXPORT_SYMBOL_GPL(hid_haptic_init);
+
+void hid_haptic_handle_press_release(struct hid_haptic_device *haptic)
+{
+	int prev_pressed_state = haptic->pressed_state;
+	struct input_dev *input = haptic->input_dev;
+	unsigned long flags;
+
+	if (!haptic->is_forcepad)
+		return;
+
+	if (haptic->pressure > haptic->press_threshold)
+		haptic->pressed_state = 1;
+	else if (haptic->pressure < haptic->release_threshold)
+		haptic->pressed_state = 0;
+	if (!prev_pressed_state && haptic->pressed_state &&
+	    haptic->mode == HID_HAPTIC_MODE_KERNEL) {
+		spin_lock_irqsave(&input->event_lock, flags);
+		input->ff->playback(input, PRESS_HID_EFFECT_ID, 1);
+		spin_unlock_irqrestore(&input->event_lock, flags);
+	}
+	if (prev_pressed_state && !haptic->pressed_state &&
+	    haptic->mode == HID_HAPTIC_MODE_KERNEL) {
+		spin_lock_irqsave(&input->event_lock, flags);
+		input->ff->playback(input, RELEASE_HID_EFFECT_ID, 1);
+		spin_unlock_irqrestore(&input->event_lock, flags);
+	}
+}
+EXPORT_SYMBOL_GPL(hid_haptic_handle_press_release);
+
+bool hid_haptic_handle_input(struct hid_haptic_device *haptic)
+{
+	if (haptic->is_forcepad && haptic->mode == HID_HAPTIC_MODE_KERNEL) {
+		input_event(haptic->input_dev, EV_KEY, BTN_LEFT,
+			    haptic->pressed_state);
+		return true;
+	}
+	return false;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_handle_input);
+
+void hid_haptic_pressure_reset(struct hid_haptic_device *haptic)
+{
+	haptic->pressure = 0;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_pressure_reset);
+
+void hid_haptic_pressure_update(struct hid_haptic_device *haptic,
+				__s32 pressure)
+{
+	if (pressure > haptic->pressure)
+		haptic->pressure = pressure;
+}
+EXPORT_SYMBOL_GPL(hid_haptic_pressure_update);
diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h
index 67096cc8c233..c26093e3773d 100644
--- a/drivers/hid/hid-haptic.h
+++ b/drivers/hid/hid-haptic.h
@@ -83,6 +83,11 @@ int hid_haptic_input_mapping(struct hid_device *hdev,
 bool hid_haptic_input_configured(struct hid_device *hdev,
 				 struct hid_haptic_device *haptic);
 int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr);
+void hid_haptic_handle_press_release(struct hid_haptic_device *haptic);
+bool hid_haptic_handle_input(struct hid_haptic_device *haptic);
+void hid_haptic_pressure_reset(struct hid_haptic_device *haptic);
+void hid_haptic_pressure_update(struct hid_haptic_device *haptic,
+				__s32 pressure);
 #else
 static inline
 void hid_haptic_feature_mapping(struct hid_device *hdev,
@@ -116,4 +121,17 @@ int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_p
 {
 	return 0;
 }
+static inline
+void hid_haptic_handle_press_release(struct hid_haptic_device *haptic) {}
+static inline
+bool hid_haptic_handle_input(struct hid_haptic_device *haptic)
+{
+	return false;
+}
+static inline
+void hid_haptic_pressure_reset(struct hid_haptic_device *haptic) {}
+static inline
+void hid_haptic_pressure_update(struct hid_haptic_device *haptic,
+				  __s32 pressure)
+{}
 #endif
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 10/17] Input: MT - add INPUT_MT_MAX_FORCE flags
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (8 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 09/17] HID: haptic: add functions handling events Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 11/17] HID: haptic: add hid_haptic_switch_mode Angela Czubak
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

Add a flag to generate ABS_PRESSURE as a maximum of ABS_MT_PRESSURE across
all slots.
This flag should be set if one knows a device reports true force and would
like to report total force to the userspace.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/input/input-mt.c | 16 ++++++++++++----
 include/linux/input/mt.h |  1 +
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index 44fe6f2f063c..6dcef47aac71 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -197,6 +197,7 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
 	struct input_mt *mt = dev->mt;
 	struct input_mt_slot *oldest;
 	int oldid, count, i;
+	int p, reported_p = 0;
 
 	if (!mt)
 		return;
@@ -215,6 +216,15 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
 			oldest = ps;
 			oldid = id;
 		}
+		if (test_bit(ABS_MT_PRESSURE, dev->absbit)) {
+			p = input_mt_get_value(ps, ABS_MT_PRESSURE);
+			if (mt->flags & INPUT_MT_MAX_FORCE) {
+				if (p > reported_p)
+					reported_p = p;
+			} else if (oldid == id) {
+				reported_p = p;
+			}
+		}
 		count++;
 	}
 
@@ -244,10 +254,8 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
 		input_event(dev, EV_ABS, ABS_X, x);
 		input_event(dev, EV_ABS, ABS_Y, y);
 
-		if (test_bit(ABS_MT_PRESSURE, dev->absbit)) {
-			int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
-			input_event(dev, EV_ABS, ABS_PRESSURE, p);
-		}
+		if (test_bit(ABS_MT_PRESSURE, dev->absbit))
+			input_event(dev, EV_ABS, ABS_PRESSURE, reported_p);
 	} else {
 		if (test_bit(ABS_MT_PRESSURE, dev->absbit))
 			input_event(dev, EV_ABS, ABS_PRESSURE, 0);
diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h
index 3b8580bd33c1..b23216cf9923 100644
--- a/include/linux/input/mt.h
+++ b/include/linux/input/mt.h
@@ -17,6 +17,7 @@
 #define INPUT_MT_DROP_UNUSED	0x0004	/* drop contacts not seen in frame */
 #define INPUT_MT_TRACK		0x0008	/* use in-kernel tracking */
 #define INPUT_MT_SEMI_MT	0x0010	/* semi-mt device, finger count handled manually */
+#define INPUT_MT_MAX_FORCE	0x0020	/* choose max force from slots pressure */
 
 /**
  * struct input_mt_slot - represents the state of an input MT slot
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 11/17] HID: haptic: add hid_haptic_switch_mode
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (9 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 10/17] Input: MT - add INPUT_MT_MAX_FORCE flags Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 12/17] HID: multitouch: add haptic multitouch support Angela Czubak
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

Function hid_haptic_switch_mode() can be used to turn off and on the
autonomoums mode for the device. If the device is a forcepad andsupports
press and release waveforms, let the kernel handle generation of haptic
feedback instead of the device itself.
Implement hid_haptic_resume() and hid_haptic_suspend() so that the
autonomous mode gets switched off at resume and switched on at suspend.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/hid/hid-haptic.c | 92 +++++++++++++++++++++++++++++++++++++---
 drivers/hid/hid-haptic.h | 10 +++++
 2 files changed, 96 insertions(+), 6 deletions(-)

diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index 9b89a1f8a631..3d26bd5d15a4 100644
--- a/drivers/hid/hid-haptic.c
+++ b/drivers/hid/hid-haptic.c
@@ -5,6 +5,7 @@
  *  Copyright (c) 2021 Angela Czubak <acz@semihalf.com>
  */
 
+#include <linux/input/mt.h>
 #include <linux/module.h>
 
 #include "hid-haptic.h"
@@ -192,9 +193,60 @@ static void fill_effect_buf(struct hid_haptic_device *haptic,
 	mutex_unlock(&haptic->manual_trigger_mutex);
 }
 
+static void switch_mode(struct hid_device *hdev, struct hid_haptic_device *haptic,
+			int mode)
+{
+	struct hid_report *rep = haptic->auto_trigger_report;
+	struct hid_field *field;
+	s32 value;
+	int i, j;
+
+	if (mode == HID_HAPTIC_MODE_KERNEL)
+		value = HID_HAPTIC_ORDINAL_WAVEFORMSTOP;
+	else
+		value = haptic->default_auto_trigger;
+
+	mutex_lock(&haptic->auto_trigger_mutex);
+	for (i = 0; i < rep->maxfield; i++) {
+		field = rep->field[i];
+		/* Ignore if report count is out of bounds. */
+		if (field->report_count < 1)
+			continue;
+
+		for (j = 0; j < field->maxusage; j++) {
+			if (field->usage[j].hid == HID_HP_AUTOTRIGGER)
+				field->value[j] = value;
+		}
+	}
+
+	/* send the report */
+	hid_hw_request(hdev, rep, HID_REQ_SET_REPORT);
+	mutex_unlock(&haptic->auto_trigger_mutex);
+	haptic->mode = mode;
+}
+
+#ifdef CONFIG_PM
+void hid_haptic_resume(struct hid_device *hdev, struct hid_haptic_device *haptic)
+{
+	if (haptic->is_forcepad && haptic->press_ordinal_cur &&
+	    haptic->release_ordinal_cur)
+		switch_mode(hdev, haptic, HID_HAPTIC_MODE_KERNEL);
+}
+EXPORT_SYMBOL_GPL(hid_haptic_resume);
+
+void hid_haptic_suspend(struct hid_device *hdev, struct hid_haptic_device *haptic)
+{
+	if (haptic->is_forcepad && haptic->press_ordinal_cur &&
+	    haptic->release_ordinal_cur)
+		switch_mode(hdev, haptic, HID_HAPTIC_MODE_DEVICE);
+}
+EXPORT_SYMBOL_GPL(hid_haptic_suspend);
+#endif
+
 static int hid_haptic_upload_effect(struct input_dev *dev, struct ff_effect *effect,
 				    struct ff_effect *old)
 {
+	struct hid_device *hdev = input_get_drvdata(dev);
 	struct ff_device *ff = dev->ff;
 	struct hid_haptic_device *haptic = ff->private;
 	int i, ordinal = 0;
@@ -220,6 +272,24 @@ static int hid_haptic_upload_effect(struct input_dev *dev, struct ff_effect *eff
 	fill_effect_buf(haptic, &effect->u.hid, &haptic->effect[effect->id],
 			ordinal);
 
+	/* Mode switching needed only for forcepads. */
+	if (!haptic->is_forcepad)
+		return 0;
+
+	if (effect->id == HID_HAPTIC_RELEASE_EFFECT_ID) {
+		if (haptic->press_ordinal_cur &&
+		    haptic->mode == HID_HAPTIC_MODE_DEVICE) {
+			switch_mode(hdev, haptic, HID_HAPTIC_MODE_KERNEL);
+		}
+		haptic->release_ordinal_cur = ordinal;
+	} else if (effect->id == HID_HAPTIC_PRESS_EFFECT_ID) {
+		if (haptic->release_ordinal_cur &&
+		    haptic->mode == HID_HAPTIC_MODE_DEVICE) {
+			switch_mode(hdev, haptic, HID_HAPTIC_MODE_KERNEL);
+		}
+		haptic->press_ordinal_cur = ordinal;
+	}
+
 	return 0;
 }
 
@@ -285,6 +355,7 @@ static void effect_set_default(struct ff_effect *effect)
 static int hid_haptic_erase(struct input_dev *dev, int effect_id)
 {
 	struct hid_haptic_device *haptic = dev->ff->private;
+	struct hid_device *hdev = input_get_drvdata(dev);
 	struct ff_effect effect;
 	int ordinal;
 
@@ -295,21 +366,29 @@ static int hid_haptic_erase(struct input_dev *dev, int effect_id)
 	switch (effect_id) {
 	case HID_HAPTIC_RELEASE_EFFECT_ID:
 		ordinal = haptic->release_ordinal_orig;
-		if (!ordinal)
+		haptic->release_ordinal_cur = ordinal;
+		if (!ordinal) {
 			ordinal = HID_HAPTIC_ORDINAL_WAVEFORMNONE;
-		else
+			if (haptic->mode == HID_HAPTIC_MODE_KERNEL)
+				switch_mode(hdev, haptic, HID_HAPTIC_MODE_DEVICE);
+		} else {
 			effect.u.hid.hid_usage = HID_HP_WAVEFORMRELEASE &
 				HID_USAGE;
+		}
 		fill_effect_buf(haptic, &effect.u.hid, &haptic->effect[effect_id],
 				ordinal);
 		break;
 	case HID_HAPTIC_PRESS_EFFECT_ID:
 		ordinal = haptic->press_ordinal_orig;
-		if (!ordinal)
+		haptic->press_ordinal_cur = ordinal;
+		if (!ordinal) {
 			ordinal = HID_HAPTIC_ORDINAL_WAVEFORMNONE;
-		else
+			if (haptic->mode == HID_HAPTIC_MODE_KERNEL)
+				switch_mode(hdev, haptic, HID_HAPTIC_MODE_DEVICE);
+		} else {
 			effect.u.hid.hid_usage = HID_HP_WAVEFORMPRESS &
 				HID_USAGE;
+		}
 		fill_effect_buf(haptic, &effect.u.hid, &haptic->effect[effect_id],
 				ordinal);
 		break;
@@ -401,6 +480,7 @@ int hid_haptic_init(struct hid_device *hdev,
 	haptic->hid_usage_map[HID_HAPTIC_ORDINAL_WAVEFORMSTOP] =
 		HID_HP_WAVEFORMSTOP & HID_USAGE;
 
+	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]);
 
@@ -582,13 +662,13 @@ void hid_haptic_handle_press_release(struct hid_haptic_device *haptic)
 	if (!prev_pressed_state && haptic->pressed_state &&
 	    haptic->mode == HID_HAPTIC_MODE_KERNEL) {
 		spin_lock_irqsave(&input->event_lock, flags);
-		input->ff->playback(input, PRESS_HID_EFFECT_ID, 1);
+		input->ff->playback(input, HID_HAPTIC_PRESS_EFFECT_ID, 1);
 		spin_unlock_irqrestore(&input->event_lock, flags);
 	}
 	if (prev_pressed_state && !haptic->pressed_state &&
 	    haptic->mode == HID_HAPTIC_MODE_KERNEL) {
 		spin_lock_irqsave(&input->event_lock, flags);
-		input->ff->playback(input, RELEASE_HID_EFFECT_ID, 1);
+		input->ff->playback(input, HID_HAPTIC_RELEASE_EFFECT_ID, 1);
 		spin_unlock_irqrestore(&input->event_lock, flags);
 	}
 }
diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h
index c26093e3773d..96757a4655ec 100644
--- a/drivers/hid/hid-haptic.h
+++ b/drivers/hid/hid-haptic.h
@@ -82,6 +82,10 @@ 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);
+#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);
+#endif
 int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr);
 void hid_haptic_handle_press_release(struct hid_haptic_device *haptic);
 bool hid_haptic_handle_input(struct hid_haptic_device *haptic);
@@ -116,6 +120,12 @@ bool hid_haptic_input_configured(struct hid_device *hdev,
 {
 	return 0;
 }
+#ifdef CONFIG_PM
+static inline
+void hid_haptic_resume(struct hid_device *hdev, struct hid_haptic_device *haptic) {}
+static inline
+void hid_haptic_suspend(struct hid_device *hdev, struct hid_haptic_device *haptic) {}
+#endif
 static inline
 int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device **haptic_ptr)
 {
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 12/17] HID: multitouch: add haptic multitouch support
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (10 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 11/17] HID: haptic: add hid_haptic_switch_mode Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 13/17] Input: introduce EVIOCFF(TAKE|RELEASE)CONTROL Angela Czubak
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

Add new option (MULTITOUCH_HAPTIC) to mark whether hid-multitouch
should try and configure simple haptic device.
Once this option is configured, and the device is recognized to have simple
haptic capabilities, check input frames for pressure and handle it using
hid_haptic_* API.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/hid/Kconfig          | 11 +++++
 drivers/hid/hid-multitouch.c | 78 +++++++++++++++++++++++++++++++++++-
 2 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index c892cfea3bb5..58f42489bdb1 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -768,6 +768,17 @@ config HID_MULTITOUCH
 	  To compile this driver as a module, choose M here: the
 	  module will be called hid-multitouch.
 
+config MULTITOUCH_HAPTIC
+	bool "Simple haptic multitouch support"
+	depends on HID_MULTITOUCH
+	select HID_HAPTIC
+	default n
+	help
+	Support for simple multitouch haptic devices.
+	Adds extra parsing and FF device for the hid multitouch driver.
+	It can be used for Elan 2703 haptic touchpad.
+	To enable, say Y.
+
 config HID_NINTENDO
 	tristate "Nintendo Joy-Con and Pro Controller support"
 	depends on HID
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 99eabfb4145b..7546c98de503 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -48,6 +48,8 @@ MODULE_LICENSE("GPL");
 
 #include "hid-ids.h"
 
+#include "hid-haptic.h"
+
 /* quirks to control the device */
 #define MT_QUIRK_NOT_SEEN_MEANS_UP	BIT(0)
 #define MT_QUIRK_SLOT_IS_CONTACTID	BIT(1)
@@ -159,11 +161,14 @@ struct mt_report_data {
 struct mt_device {
 	struct mt_class mtclass;	/* our mt device class */
 	struct timer_list release_timer;	/* to release sticky fingers */
+	struct hid_haptic_device *haptic;	/* haptic related configuration */
 	struct hid_device *hdev;	/* hid_device we're attached to */
 	unsigned long mt_io_flags;	/* mt flags (MT_IO_FLAGS_*) */
 	__u8 inputmode_value;	/* InputMode HID feature value */
 	__u8 maxcontacts;
 	bool is_buttonpad;	/* is this device a button pad? */
+	bool is_haptic_touchpad;	/* is this device a haptic touchpad? */
+	bool is_forcepad;	/* is this device a forcepad? */
 	bool serial_maybe;	/* need to check for serial protocol */
 
 	struct list_head applications;
@@ -501,6 +506,8 @@ static void mt_feature_mapping(struct hid_device *hdev,
 			mt_get_feature(hdev, field->report);
 		break;
 	}
+
+	hid_haptic_feature_mapping(hdev, td->haptic, field, usage);
 }
 
 static void set_abs(struct input_dev *input, unsigned int code,
@@ -831,6 +838,8 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 		case HID_DG_TIPPRESSURE:
 			set_abs(hi->input, ABS_MT_PRESSURE, field,
 				cls->sn_pressure);
+			td->is_forcepad =
+				hid_haptic_is_forcepad(td->haptic, hi, field);
 			MT_STORE_FIELD(p);
 			return 1;
 		case HID_DG_SCANTIME:
@@ -944,8 +953,16 @@ static void mt_release_pending_palms(struct mt_device *td,
 static void mt_sync_frame(struct mt_device *td, struct mt_application *app,
 			  struct input_dev *input)
 {
-	if (app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS)
-		input_event(input, EV_KEY, BTN_LEFT, app->left_button_state);
+	if (td->is_haptic_touchpad)
+		hid_haptic_handle_press_release(td->haptic);
+
+	if (app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) {
+		if (!(td->is_haptic_touchpad &&
+		    hid_haptic_handle_input(td->haptic))) {
+			input_event(input, EV_KEY, BTN_LEFT,
+				    app->left_button_state);
+		}
+	}
 
 	input_mt_sync_frame(input);
 	input_event(input, EV_MSC, MSC_TIMESTAMP, app->timestamp);
@@ -955,6 +972,8 @@ static void mt_sync_frame(struct mt_device *td, struct mt_application *app,
 
 	app->num_received = 0;
 	app->left_button_state = 0;
+	if (td->is_haptic_touchpad)
+		hid_haptic_pressure_reset(td->haptic);
 
 	if (test_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags))
 		set_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags);
@@ -1104,6 +1123,9 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
 			minor = minor >> 1;
 		}
 
+		if (td->is_haptic_touchpad)
+			hid_haptic_pressure_update(td->haptic, *slot->p);
+
 		input_event(input, EV_ABS, ABS_MT_POSITION_X, *slot->x);
 		input_event(input, EV_ABS, ABS_MT_POSITION_Y, *slot->y);
 		input_event(input, EV_ABS, ABS_MT_TOOL_X, *slot->cx);
@@ -1278,6 +1300,10 @@ static int mt_touch_input_configured(struct hid_device *hdev,
 	if (cls->is_indirect)
 		app->mt_flags |= INPUT_MT_POINTER;
 
+	/* Get rid of and use default behaviour? */
+	if (td->is_haptic_touchpad)
+		app->mt_flags |= INPUT_MT_MAX_FORCE;
+
 	if (app->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
 		app->mt_flags |= INPUT_MT_DROP_UNUSED;
 
@@ -1313,6 +1339,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 	struct mt_device *td = hid_get_drvdata(hdev);
 	struct mt_application *application;
 	struct mt_report_data *rdata;
+	int ret;
 
 	rdata = mt_find_report_data(td, field->report);
 	if (!rdata) {
@@ -1375,6 +1402,11 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 	if (field->physical == HID_DG_STYLUS)
 		hi->application = HID_DG_STYLUS;
 
+	ret = hid_haptic_input_mapping(hdev, td->haptic, hi, field, usage, bit,
+				       max);
+	if (ret != 0)
+		return ret;
+
 	/* let hid-core decide for the others */
 	return 0;
 }
@@ -1568,6 +1600,17 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
 	struct hid_report *report;
 	int ret;
 
+	/* check if hi application is touchpad? */
+	if (hi->application == HID_DG_TOUCHPAD) {
+		if (td->is_forcepad && (td->mtclass.name == MT_CLS_WIN_8 ||
+		    td->mtclass.name == MT_CLS_WIN_8_FORCE_MULTI_INPUT)) {
+			if (hid_haptic_input_configured(hdev, td->haptic))
+				td->is_haptic_touchpad = true;
+		} else {
+			td->is_haptic_touchpad = false;
+		}
+	}
+
 	list_for_each_entry(report, &hi->reports, hidinput_list) {
 		rdata = mt_find_report_data(td, report);
 		if (!rdata) {
@@ -1713,6 +1756,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		dev_err(&hdev->dev, "cannot allocate multitouch data\n");
 		return -ENOMEM;
 	}
+	td->haptic = kzalloc(sizeof(*(td->haptic)), GFP_KERNEL);
+	if (!td->haptic)
+		return -ENOMEM;
 	td->hdev = hdev;
 	td->mtclass = *mtclass;
 	td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
@@ -1764,6 +1810,17 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 
 	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
 
+	if (td->is_haptic_touchpad) {
+		if (hid_haptic_init(hdev, &td->haptic)) {
+			dev_warn(&hdev->dev, "Cannot allocate haptic for %s\n",
+				 hdev->name);
+			td->is_haptic_touchpad = false;
+			kfree(td->haptic);
+		}
+	} else {
+		kfree(td->haptic);
+	}
+
 	return 0;
 }
 
@@ -1771,6 +1828,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 static int mt_suspend(struct hid_device *hdev, pm_message_t state)
 {
 	struct mt_device *td = hid_get_drvdata(hdev);
+	struct hid_haptic_device *haptic = td->haptic;
 
 	/* High latency is desirable for power savings during S3/S0ix */
 	if ((td->mtclass.quirks & MT_QUIRK_DISABLE_WAKEUP) ||
@@ -1779,18 +1837,31 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state)
 	else
 		mt_set_modes(hdev, HID_LATENCY_HIGH, true, true);
 
+	if (td->is_haptic_touchpad)
+		hid_haptic_suspend(hdev, haptic);
+
 	return 0;
 }
 
 static int mt_reset_resume(struct hid_device *hdev)
 {
+	struct mt_device *td = hid_get_drvdata(hdev);
+	struct hid_haptic_device *haptic = td->haptic;
+
 	mt_release_contacts(hdev);
 	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
+
+	if (td->is_haptic_touchpad)
+		hid_haptic_resume(hdev, haptic);
+
 	return 0;
 }
 
 static int mt_resume(struct hid_device *hdev)
 {
+	struct mt_device *td = hid_get_drvdata(hdev);
+	struct hid_haptic_device *haptic = td->haptic;
+
 	/* Some Elan legacy devices require SET_IDLE to be set on resume.
 	 * It should be safe to send it to other devices too.
 	 * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */
@@ -1799,6 +1870,9 @@ static int mt_resume(struct hid_device *hdev)
 
 	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
 
+	if (td->is_haptic_touchpad)
+		hid_haptic_resume(hdev, haptic);
+
 	return 0;
 }
 #endif
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 13/17] Input: introduce EVIOCFF(TAKE|RELEASE)CONTROL
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (11 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 12/17] HID: multitouch: add haptic multitouch support Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 14/17] HID: haptic: add hid_haptic_change_control Angela Czubak
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

Add new ioctls which can be used for simple haptic force feedback effects.
Once the control is taken over the effect the kernel does not generate it
on its own (EVIOCFFTAKECONTROL).
To revert this action use EVIOCFFRELEASECONTROL.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/input/evdev.c      |  6 +++
 drivers/input/ff-core.c    | 89 +++++++++++++++++++++++++++++++++++++-
 include/linux/input.h      |  5 +++
 include/uapi/linux/input.h |  4 ++
 4 files changed, 103 insertions(+), 1 deletion(-)

diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 95f90699d2b1..6d25eb19e28e 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -1076,6 +1076,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
 	case EVIOCRMFF:
 		return input_ff_erase(dev, (int)(unsigned long) p, file);
 
+	case EVIOCFFTAKECONTROL:
+		return input_ff_take_control(dev, (int)(unsigned long) p, file);
+
+	case EVIOCFFRELEASECONTROL:
+		return input_ff_release_control(dev, (int)(unsigned long) p, file);
+
 	case EVIOCGEFFECTS:
 		i = test_bit(EV_FF, dev->evbit) ?
 				dev->ff->max_effects : 0;
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
index 1a9a5401acb5..d6b76a479fc2 100644
--- a/drivers/input/ff-core.c
+++ b/drivers/input/ff-core.c
@@ -252,6 +252,91 @@ int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file)
 }
 EXPORT_SYMBOL_GPL(input_ff_erase);
 
+/*
+ * Take control over the effect if the requester is also the effect owner.
+ * The mutex should already be locked before calling this function.
+ */
+static int control_effect(struct input_dev *dev, int effect_id,
+			  struct file *file, int take)
+{
+	struct ff_device *ff = dev->ff;
+	int error;
+
+	error = check_effect_access(ff, effect_id, file);
+	if (error) {
+		error = check_effect_shared(ff, effect_id);
+		if (error)
+			return error;
+	}
+
+	if (ff->change_control) {
+		error = ff->change_control(dev, effect_id, file, take);
+		if (error)
+			return error;
+	}
+
+	return 0;
+}
+/**
+ * input_ff_take_control - take control over a force-feedback effect from kernel
+ * @dev: input device to take control over effect from
+ * @effect_id: id of the effect to take control over
+ * @file: purported owner of the request
+ *
+ * This function switches user-controlled mode on for the given force-feedback
+ * effect. The user-mode will persist unitl the last caller releases control.
+ * The effect will only be taken control of if it was uploaded through the same
+ * file handle that is requesting taking control or for simple haptic effects
+ * 0 and 1.
+ * Valid only for simple haptic effects (ff_hid_effect).
+ */
+int input_ff_take_control(struct input_dev *dev, int effect_id,
+			  struct file *file)
+{
+	struct ff_device *ff = dev->ff;
+	int ret;
+
+	if (!test_bit(EV_FF, dev->evbit))
+		return -EINVAL;
+
+	mutex_lock(&ff->mutex);
+	ret = control_effect(dev, effect_id, file, 1);
+	mutex_unlock(&ff->mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(input_ff_take_control);
+
+/**
+ * input_ff_release_control - release control over a force-feedback effect
+ * @dev: input device to release control over effect to
+ * @effect_id: id of the effect to release control
+ * @file: purported owner of the request
+ *
+ * This function switches user-controlled mode off for the given force-feedback
+ * effect. The user-mode will persist unitl the last caller releases control.
+ * The control will be released of if it was uploaded through the same
+ * file handle that is requesting taking control or for simple haptic effects
+ * 0 and 1.
+ * Valid only for simple haptic effects (ff_hid_effect).
+ */
+int input_ff_release_control(struct input_dev *dev, int effect_id,
+			     struct file *file)
+{
+	struct ff_device *ff = dev->ff;
+	int ret;
+
+	if (!test_bit(EV_FF, dev->evbit))
+		return -EINVAL;
+
+	mutex_lock(&ff->mutex);
+	ret = control_effect(dev, effect_id, file, 0);
+	mutex_unlock(&ff->mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(input_ff_release_control);
+
 /*
  * input_ff_flush - erase all effects owned by a file handle
  * @dev: input device to erase effect from
@@ -270,8 +355,10 @@ int input_ff_flush(struct input_dev *dev, struct file *file)
 
 	mutex_lock(&ff->mutex);
 
-	for (i = 0; i < ff->max_effects; i++)
+	for (i = 0; i < ff->max_effects; i++) {
+		control_effect(dev, i, file, 0);
 		erase_effect(dev, i, file);
+	}
 
 	mutex_unlock(&ff->mutex);
 
diff --git a/include/linux/input.h b/include/linux/input.h
index 49790c1bd2c4..39091730c2fc 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -521,6 +521,7 @@ extern struct class input_class;
  * @upload: Called to upload an new effect into device
  * @erase: Called to erase an effect from device
  * @playback: Called to request device to start playing specified effect
+ * @change_control: Called to change control over specified effect
  * @set_gain: Called to set specified gain
  * @set_autocenter: Called to auto-center device
  * @destroy: called by input core when parent input device is being
@@ -549,6 +550,8 @@ struct ff_device {
 	int (*erase)(struct input_dev *dev, int effect_id);
 
 	int (*playback)(struct input_dev *dev, int effect_id, int value);
+	int (*change_control)(struct input_dev *dev, int effect_id,
+			      struct file *file, int take);
 	void (*set_gain)(struct input_dev *dev, u16 gain);
 	void (*set_autocenter)(struct input_dev *dev, u16 magnitude);
 
@@ -572,6 +575,8 @@ int input_ff_event(struct input_dev *dev, unsigned int type, unsigned int code,
 
 int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, struct file *file);
 int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file);
+int input_ff_take_control(struct input_dev *dev, int effect_id, struct file *file);
+int input_ff_release_control(struct input_dev *dev, int effect_id, struct file *file);
 int input_ff_flush(struct input_dev *dev, struct file *file);
 
 int input_ff_create_memless(struct input_dev *dev, void *data,
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index ccf43a21d43d..cce37336ab38 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -178,6 +178,10 @@ struct input_mask {
 
 #define EVIOCSFF		_IOW('E', 0x80, struct ff_effect)	/* send a force effect to a force feedback device */
 #define EVIOCRMFF		_IOW('E', 0x81, int)			/* Erase a force effect */
+/* Take control over a force effect */
+#define EVIOCFFTAKECONTROL	_IOW('E', 0x82, int)
+/* Release control over a force effect */
+#define EVIOCFFRELEASECONTROL	_IOW('E', 0x83, int)
 #define EVIOCGEFFECTS		_IOR('E', 0x84, int)			/* Report number of effects playable at the same time */
 
 #define EVIOCGRAB		_IOW('E', 0x90, int)			/* Grab/Release device */
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 14/17] HID: haptic: add hid_haptic_change_control
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (12 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 13/17] Input: introduce EVIOCFF(TAKE|RELEASE)CONTROL Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 15/17] HID: add HID device reset callback Angela Czubak
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

Implement change_control callbacks for simple haptic device.
If anybody has requested control over an effect, do not generate it
in kernel.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/hid/hid-haptic.c | 50 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 48 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index 3d26bd5d15a4..4b3ad5168fbb 100644
--- a/drivers/hid/hid-haptic.c
+++ b/drivers/hid/hid-haptic.c
@@ -6,6 +6,7 @@
  */
 
 #include <linux/input/mt.h>
+#include <linux/mm.h>
 #include <linux/module.h>
 
 #include "hid-haptic.h"
@@ -342,6 +343,46 @@ static int hid_haptic_playback(struct input_dev *dev, int effect_id, int value)
 	return 0;
 }
 
+static int hid_haptic_change_control(struct input_dev *dev, int effect_id,
+				     struct file *file, int take)
+{
+	struct hid_haptic_device *haptic = dev->ff->private;
+	struct hid_haptic_effect_node *effect_node;
+	struct hid_haptic_effect *effect;
+	bool found = false;
+	int ret = 0;
+
+	effect = &haptic->effect[effect_id];
+	mutex_lock(&effect->control_mutex);
+	list_for_each_entry(effect_node, &effect->control, node) {
+		if (effect_node->file == file) {
+			found = true;
+			break;
+		}
+	}
+	if (take) {
+		if (!found) {
+			effect_node = kvzalloc(sizeof(struct hid_haptic_effect),
+					       GFP_KERNEL);
+			if (!effect_node) {
+				ret = -ENOMEM;
+				goto exit;
+			}
+			effect_node->file = file;
+		}
+		list_add(&effect_node->node, &effect->control);
+	} else {
+		if (found) {
+			list_del(&effect_node->node);
+			kvfree(effect_node);
+		}
+	}
+exit:
+	mutex_unlock(&effect->control_mutex);
+
+	return ret;
+}
+
 static void effect_set_default(struct ff_effect *effect)
 {
 	effect->type = FF_HID;
@@ -529,6 +570,8 @@ int hid_haptic_init(struct hid_device *hdev,
 		}
 		haptic->effect[r].input_dev = dev;
 		INIT_WORK(&haptic->effect[r].work, haptic_work_handler);
+		INIT_LIST_HEAD(&haptic->effect[r].control);
+		mutex_init(&haptic->effect[r].control_mutex);
 	}
 	haptic->stop_effect.report_buf =
 		hid_alloc_report_buf(haptic->manual_trigger_report,
@@ -558,6 +601,7 @@ int hid_haptic_init(struct hid_device *hdev,
 	ff->private = haptic;
 	ff->upload = hid_haptic_upload_effect;
 	ff->playback = hid_haptic_playback;
+	ff->change_control = hid_haptic_change_control;
 	ff->erase = hid_haptic_erase;
 	ff->destroy = hid_haptic_destroy;
 	if (!try_module_get(THIS_MODULE)) {
@@ -660,13 +704,15 @@ void hid_haptic_handle_press_release(struct hid_haptic_device *haptic)
 	else if (haptic->pressure < haptic->release_threshold)
 		haptic->pressed_state = 0;
 	if (!prev_pressed_state && haptic->pressed_state &&
-	    haptic->mode == HID_HAPTIC_MODE_KERNEL) {
+	    haptic->mode == HID_HAPTIC_MODE_KERNEL &&
+	    list_empty(&haptic->effect[HID_HAPTIC_PRESS_EFFECT_ID].control)) {
 		spin_lock_irqsave(&input->event_lock, flags);
 		input->ff->playback(input, HID_HAPTIC_PRESS_EFFECT_ID, 1);
 		spin_unlock_irqrestore(&input->event_lock, flags);
 	}
 	if (prev_pressed_state && !haptic->pressed_state &&
-	    haptic->mode == HID_HAPTIC_MODE_KERNEL) {
+	    haptic->mode == HID_HAPTIC_MODE_KERNEL &&
+	    list_empty(&haptic->effect[HID_HAPTIC_RELEASE_EFFECT_ID].control)) {
 		spin_lock_irqsave(&input->event_lock, flags);
 		input->ff->playback(input, HID_HAPTIC_RELEASE_EFFECT_ID, 1);
 		spin_unlock_irqrestore(&input->event_lock, flags);
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 15/17] HID: add HID device reset callback
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (13 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 14/17] HID: haptic: add hid_haptic_change_control Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 16/17] HID: haptic: implement HID haptic " Angela Czubak
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

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@semihalf.com>
---
 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


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 16/17] HID: haptic: implement HID haptic reset callback
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (14 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 15/17] HID: add HID device reset callback Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-13  9:39 ` [PATCH v3 17/17] HID: multitouch: Add lid handler for touchpad on Redrix chromebook Angela Czubak
  2022-05-22 17:55 ` [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
  17 siblings, 0 replies; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

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     |  7 +++++++
 drivers/hid/hid-haptic.h     |  5 +++++
 drivers/hid/hid-multitouch.c | 15 +++++++++++++++
 3 files changed, 27 insertions(+)

diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index 4b3ad5168fbb..a8d89a9235e7 100644
--- a/drivers/hid/hid-haptic.c
+++ b/drivers/hid/hid-haptic.c
@@ -226,6 +226,13 @@ static void switch_mode(struct hid_device *hdev, struct hid_haptic_device *hapti
 	haptic->mode = mode;
 }
 
+void hid_haptic_reset(struct hid_device *hdev, struct hid_haptic_device *haptic)
+{
+	if (haptic->press_ordinal_cur && haptic->release_ordinal_cur)
+		switch_mode(hdev, haptic, HID_HAPTIC_MODE_KERNEL);
+}
+EXPORT_SYMBOL_GPL(hid_haptic_reset);
+
 #ifdef CONFIG_PM
 void hid_haptic_resume(struct hid_device *hdev, struct hid_haptic_device *haptic)
 {
diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h
index 96757a4655ec..03e657094dae 100644
--- a/drivers/hid/hid-haptic.h
+++ b/drivers/hid/hid-haptic.h
@@ -82,6 +82,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 +121,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 7546c98de503..2d1b8c400c2f 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -1877,6 +1877,20 @@ 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;
+
+	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);
@@ -2284,6 +2298,7 @@ static struct hid_driver mt_driver = {
 #ifdef CONFIG_PM
 	.suspend = mt_suspend,
 	.reset_resume = mt_reset_resume,
+	.reset = mt_reset,
 	.resume = mt_resume,
 #endif
 };
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v3 17/17] HID: multitouch: Add lid handler for touchpad on Redrix chromebook
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (15 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 16/17] HID: haptic: implement HID haptic " Angela Czubak
@ 2022-05-13  9:39 ` Angela Czubak
  2022-05-15  0:07   ` marcoshalano
  2022-05-22 17:55 ` [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
  17 siblings, 1 reply; 21+ messages in thread
From: Angela Czubak @ 2022-05-13  9:39 UTC (permalink / raw)
  To: linux-input
  Cc: upstream, benjamin.tissoires, jikos, dmitry.torokhov, Angela Czubak

If user closes the lid the touchscreen gets close to the touchpad surface,
which causes interference and makes the touchpad enter noise mode.
Right after opening the lid the cursor is unresponsive because of the mode
mentioned.
To fix this issue we switch the surface off and on so that Elan's FW
performs recalibration once the lid has been opened.

Signed-off-by: Angela Czubak <acz@semihalf.com>
---
 drivers/hid/hid-multitouch.c | 220 ++++++++++++++++++++++++++++++++++-
 1 file changed, 219 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 2d1b8c400c2f..73e47fe7d773 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -32,11 +32,14 @@
  */
 
 #include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
 #include <linux/hid.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/input/mt.h>
 #include <linux/jiffies.h>
+#include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/timer.h>
 
@@ -159,6 +162,7 @@ struct mt_report_data {
 };
 
 struct mt_device {
+	struct list_head list;	/* for list of devices needing input handler */
 	struct mt_class mtclass;	/* our mt device class */
 	struct timer_list release_timer;	/* to release sticky fingers */
 	struct hid_haptic_device *haptic;	/* haptic related configuration */
@@ -173,8 +177,15 @@ struct mt_device {
 
 	struct list_head applications;
 	struct list_head reports;
+
+	struct work_struct lid_work;
+	struct mutex mode_mutex;
+	bool lid_switch;
 };
 
+static struct workqueue_struct *mt_mode_wq;
+static LIST_HEAD(mt_devices_with_lid_handler);
+
 static void mt_post_parse_default_settings(struct mt_device *td,
 					   struct mt_application *app);
 static void mt_post_parse(struct mt_device *td, struct mt_application *app);
@@ -394,6 +405,91 @@ static const struct mt_class mt_classes[] = {
 	{ }
 };
 
+static void mt_input_lid_event(struct input_handle *handle, unsigned int type,
+			     unsigned int code, int value)
+{
+	struct mt_device *td, *n;
+
+	if (type == EV_SW && code == SW_LID && !value) {
+		list_for_each_entry_safe(td, n, &mt_devices_with_lid_handler, list)
+			queue_work(mt_mode_wq, &td->lid_work);
+	}
+}
+
+struct mt_input_lid {
+	struct input_handle handle;
+};
+
+static int mt_input_lid_connect(struct input_handler *handler,
+				struct input_dev *dev,
+				const struct input_device_id *id)
+{
+	struct mt_input_lid *lid;
+	char *name;
+	int error;
+
+	lid = kzalloc(sizeof(*lid), GFP_KERNEL);
+	if (!lid)
+		return -ENOMEM;
+
+	name = kasprintf(GFP_KERNEL, "hid-mt-lid-%s", dev_name(&dev->dev));
+	if (!name) {
+		error = -ENOMEM;
+		goto err_free_lid;
+	}
+
+	lid->handle.dev = dev;
+	lid->handle.handler = handler;
+	lid->handle.name = name;
+	lid->handle.private = lid;
+
+	error = input_register_handle(&lid->handle);
+	if (error)
+		goto err_free_name;
+
+	error = input_open_device(&lid->handle);
+	if (error)
+		goto err_unregister_handle;
+
+	return 0;
+
+err_unregister_handle:
+	input_unregister_handle(&lid->handle);
+err_free_name:
+	kfree(name);
+err_free_lid:
+	kfree(lid);
+	return error;
+}
+
+static void mt_input_lid_disconnect(struct input_handle *handle)
+{
+	struct mt_input_lid *lid = handle->private;
+
+	input_close_device(handle);
+	input_unregister_handle(handle);
+
+	kfree(handle->name);
+	kfree(lid);
+}
+
+static const struct input_device_id mt_input_lid_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT,
+		.evbit = { BIT_MASK(EV_SW) },
+		.swbit = { [BIT_WORD(SW_LID)] = BIT_MASK(SW_LID) },
+	},
+	{ },
+};
+
+static struct input_handler mt_input_lid_handler = {
+	.event =	mt_input_lid_event,
+	.connect =	mt_input_lid_connect,
+	.disconnect =	mt_input_lid_disconnect,
+	.name =		"hid-mt-lid",
+	.id_table =	mt_input_lid_ids,
+};
+
 static ssize_t mt_show_quirks(struct device *dev,
 			   struct device_attribute *attr,
 			   char *buf)
@@ -548,6 +644,83 @@ static struct mt_usages *mt_allocate_usage(struct hid_device *hdev,
 	return usage;
 }
 
+static void mt_set_modes(struct hid_device *hdev, enum latency_mode latency,
+			 bool surface_switch, bool button_switch);
+
+static void lid_work_handler(struct work_struct *work)
+{
+
+	struct mt_device *td = container_of(work, struct mt_device,
+					    lid_work);
+	struct hid_device *hdev = td->hdev;
+
+	mutex_lock(&td->mode_mutex);
+	mt_set_modes(hdev, HID_LATENCY_NORMAL, false, false);
+	/* Elan's touchpad VID 323B needs this delay to handle both switch
+	 * surface off and switch surface on and trigger recalibration
+	 * properly.
+	 */
+	msleep(50);
+	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
+	mutex_unlock(&td->mode_mutex);
+}
+
+static const struct dmi_system_id mt_lid_handler_dmi_table[] = {
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Redrix"),
+		},
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "Redrix4ES"),
+		},
+	},
+	{}
+};
+
+static int mt_create_lid_handler(void)
+{
+	int error = 0;
+
+	if (!dmi_check_system(mt_lid_handler_dmi_table))
+		return 0;
+
+	mt_mode_wq = alloc_ordered_workqueue("hid-mt-lid", WQ_FREEZABLE);
+	if (mt_mode_wq == NULL)
+		return -ENOMEM;
+
+	error = input_register_handler(&mt_input_lid_handler);
+	if (error)
+		goto remove_wq;
+
+	return 0;
+
+remove_wq:
+	destroy_workqueue(mt_mode_wq);
+	mt_mode_wq = NULL;
+	return error;
+}
+
+static void mt_configure_lid_handler(struct mt_device *td)
+{
+	struct hid_device *hdev = td->hdev;
+
+	if (hdev->bus != BUS_I2C)
+		return;
+
+	td->lid_switch = true;
+	list_add_tail(&td->list, &mt_devices_with_lid_handler);
+}
+
+static void mt_destroy_lid_handler(void)
+{
+	input_unregister_handler(&mt_input_lid_handler);
+	destroy_workqueue(mt_mode_wq);
+}
+
 static struct mt_application *mt_allocate_application(struct mt_device *td,
 						      struct hid_report *report)
 {
@@ -571,6 +744,8 @@ static struct mt_application *mt_allocate_application(struct mt_device *td,
 	if (application == HID_DG_TOUCHPAD) {
 		mt_application->mt_flags |= INPUT_MT_POINTER;
 		td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
+		if (mt_mode_wq)
+			mt_configure_lid_handler(td);
 	}
 
 	mt_application->scantime = DEFAULT_ZERO;
@@ -1767,6 +1942,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	INIT_LIST_HEAD(&td->applications);
 	INIT_LIST_HEAD(&td->reports);
 
+	INIT_LIST_HEAD(&td->list);
+	INIT_WORK(&td->lid_work, lid_work_handler);
+	mutex_init(&td->mode_mutex);
+
 	if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
 		td->serial_maybe = true;
 
@@ -1830,12 +2009,18 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state)
 	struct mt_device *td = hid_get_drvdata(hdev);
 	struct hid_haptic_device *haptic = td->haptic;
 
+	/* Wait for switch on completion */
+	if (td->lid_switch)
+		flush_workqueue(mt_mode_wq);
+
+	mutex_lock(&td->mode_mutex);
 	/* High latency is desirable for power savings during S3/S0ix */
 	if ((td->mtclass.quirks & MT_QUIRK_DISABLE_WAKEUP) ||
 	    !hid_hw_may_wakeup(hdev))
 		mt_set_modes(hdev, HID_LATENCY_HIGH, false, false);
 	else
 		mt_set_modes(hdev, HID_LATENCY_HIGH, true, true);
+	mutex_unlock(&td->mode_mutex);
 
 	if (td->is_haptic_touchpad)
 		hid_haptic_suspend(hdev, haptic);
@@ -1849,7 +2034,10 @@ static int mt_reset_resume(struct hid_device *hdev)
 	struct hid_haptic_device *haptic = td->haptic;
 
 	mt_release_contacts(hdev);
+
+	mutex_lock(&td->mode_mutex);
 	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
+	mutex_unlock(&td->mode_mutex);
 
 	if (td->is_haptic_touchpad)
 		hid_haptic_resume(hdev, haptic);
@@ -1868,7 +2056,9 @@ static int mt_resume(struct hid_device *hdev)
 
 	hid_hw_idle(hdev, 0, 0, HID_REQ_SET_IDLE);
 
+	mutex_lock(&td->mode_mutex);
 	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
+	mutex_unlock(&td->mode_mutex);
 
 	if (td->is_haptic_touchpad)
 		hid_haptic_resume(hdev, haptic);
@@ -1883,7 +2073,9 @@ static int mt_reset(struct hid_device *hdev)
 	struct hid_haptic_device *haptic = td->haptic;
 
 	mt_release_contacts(hdev);
+	mutex_lock(&td->mode_mutex);
 	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
+	mutex_unlock(&td->mode_mutex);
 
 	if (td->is_haptic_touchpad)
 		hid_haptic_reset(hdev, haptic);
@@ -1899,6 +2091,8 @@ static void mt_remove(struct hid_device *hdev)
 
 	sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
 	hid_hw_stop(hdev);
+
+	list_del(&td->list);
 }
 
 /*
@@ -2302,4 +2496,28 @@ static struct hid_driver mt_driver = {
 	.resume = mt_resume,
 #endif
 };
-module_hid_driver(mt_driver);
+
+static int __init mt_init(void)
+{
+	int ret;
+
+	ret = hid_register_driver(&mt_driver);
+	if (ret)
+		return ret;
+
+	ret = mt_create_lid_handler();
+	if (ret)
+		hid_unregister_driver(&mt_driver);
+
+	return ret;
+}
+module_init(mt_init);
+
+static void __exit mt_exit(void)
+{
+	if (mt_mode_wq)
+		mt_destroy_lid_handler();
+
+	hid_unregister_driver(&mt_driver);
+}
+module_exit(mt_exit);
-- 
2.36.0.550.gb090851708-goog


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v3 17/17] HID: multitouch: Add lid handler for touchpad on Redrix chromebook
  2022-05-13  9:39 ` [PATCH v3 17/17] HID: multitouch: Add lid handler for touchpad on Redrix chromebook Angela Czubak
@ 2022-05-15  0:07   ` marcoshalano
  0 siblings, 0 replies; 21+ messages in thread
From: marcoshalano @ 2022-05-15  0:07 UTC (permalink / raw)
  To: Angela Czubak, linux-input, upstream, benjamin.tissoires, jikos,
	dmitry.torokhov, Angela Czubak

On 13/05/2022 06:39, Angela Czubak <acz@semihalf.com> wrote:
> If user closes the lid the touchscreen gets close to the touchpad surface,
> which causes interference and makes the touchpad enter noise mode.
> Right after opening the lid the cursor is unresponsive because of the mode
> mentioned.
> To fix this issue we switch the surface off and on so that Elan's FW
> performs recalibration once the lid has been opened.
> 
A non related question: this hid-haptic module is made just for newer touchpads which have a haptic response instead of regular clicks, or the haptic logic could be extended for other haptic devices, like Sony's DualSense controller triggers?

> Signed-off-by: Angela Czubak <acz@semihalf.com>
> ---
>   drivers/hid/hid-multitouch.c | 220 ++++++++++++++++++++++++++++++++++-
>   1 file changed, 219 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
> index 2d1b8c400c2f..73e47fe7d773 100644
> --- a/drivers/hid/hid-multitouch.c
> +++ b/drivers/hid/hid-multitouch.c
> @@ -32,11 +32,14 @@
>    */
>   
>   #include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/dmi.h>
>   #include <linux/hid.h>
>   #include <linux/module.h>
>   #include <linux/slab.h>
>   #include <linux/input/mt.h>
>   #include <linux/jiffies.h>
> +#include <linux/sched.h>
>   #include <linux/string.h>
>   #include <linux/timer.h>
>   
> @@ -159,6 +162,7 @@ struct mt_report_data {
>   };
>   
>   struct mt_device {
> +	struct list_head list;	/* for list of devices needing input handler */
>   	struct mt_class mtclass;	/* our mt device class */
>   	struct timer_list release_timer;	/* to release sticky fingers */
>   	struct hid_haptic_device *haptic;	/* haptic related configuration */
> @@ -173,8 +177,15 @@ struct mt_device {
>   
>   	struct list_head applications;
>   	struct list_head reports;
> +
> +	struct work_struct lid_work;
> +	struct mutex mode_mutex;
> +	bool lid_switch;
>   };
>   
> +static struct workqueue_struct *mt_mode_wq;
> +static LIST_HEAD(mt_devices_with_lid_handler);
> +
>   static void mt_post_parse_default_settings(struct mt_device *td,
>   					   struct mt_application *app);
>   static void mt_post_parse(struct mt_device *td, struct mt_application *app);
> @@ -394,6 +405,91 @@ static const struct mt_class mt_classes[] = {
>   	{ }
>   };
>   
> +static void mt_input_lid_event(struct input_handle *handle, unsigned int type,
> +			     unsigned int code, int value)
> +{
> +	struct mt_device *td, *n;
> +
> +	if (type == EV_SW && code == SW_LID && !value) {
> +		list_for_each_entry_safe(td, n, &mt_devices_with_lid_handler, list)
> +			queue_work(mt_mode_wq, &td->lid_work);
> +	}
> +}
> +
> +struct mt_input_lid {
> +	struct input_handle handle;
> +};
> +
> +static int mt_input_lid_connect(struct input_handler *handler,
> +				struct input_dev *dev,
> +				const struct input_device_id *id)
> +{
> +	struct mt_input_lid *lid;
> +	char *name;
> +	int error;
> +
> +	lid = kzalloc(sizeof(*lid), GFP_KERNEL);
> +	if (!lid)
> +		return -ENOMEM;
> +
> +	name = kasprintf(GFP_KERNEL, "hid-mt-lid-%s", dev_name(&dev->dev));
> +	if (!name) {
> +		error = -ENOMEM;
> +		goto err_free_lid;
> +	}
> +
> +	lid->handle.dev = dev;
> +	lid->handle.handler = handler;
> +	lid->handle.name = name;
> +	lid->handle.private = lid;
> +
> +	error = input_register_handle(&lid->handle);
> +	if (error)
> +		goto err_free_name;
> +
> +	error = input_open_device(&lid->handle);
> +	if (error)
> +		goto err_unregister_handle;
> +
> +	return 0;
> +
> +err_unregister_handle:
> +	input_unregister_handle(&lid->handle);
> +err_free_name:
> +	kfree(name);
> +err_free_lid:
> +	kfree(lid);
> +	return error;
> +}
> +
> +static void mt_input_lid_disconnect(struct input_handle *handle)
> +{
> +	struct mt_input_lid *lid = handle->private;
> +
> +	input_close_device(handle);
> +	input_unregister_handle(handle);
> +
> +	kfree(handle->name);
> +	kfree(lid);
> +}
> +
> +static const struct input_device_id mt_input_lid_ids[] = {
> +	{
> +		.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT,
> +		.evbit = { BIT_MASK(EV_SW) },
> +		.swbit = { [BIT_WORD(SW_LID)] = BIT_MASK(SW_LID) },
> +	},
> +	{ },
> +};
> +
> +static struct input_handler mt_input_lid_handler = {
> +	.event =	mt_input_lid_event,
> +	.connect =	mt_input_lid_connect,
> +	.disconnect =	mt_input_lid_disconnect,
> +	.name =		"hid-mt-lid",
> +	.id_table =	mt_input_lid_ids,
> +};
> +
>   static ssize_t mt_show_quirks(struct device *dev,
>   			   struct device_attribute *attr,
>   			   char *buf)
> @@ -548,6 +644,83 @@ static struct mt_usages *mt_allocate_usage(struct hid_device *hdev,
>   	return usage;
>   }
>   
> +static void mt_set_modes(struct hid_device *hdev, enum latency_mode latency,
> +			 bool surface_switch, bool button_switch);
> +
> +static void lid_work_handler(struct work_struct *work)
> +{
> +
> +	struct mt_device *td = container_of(work, struct mt_device,
> +					    lid_work);
> +	struct hid_device *hdev = td->hdev;
> +
> +	mutex_lock(&td->mode_mutex);
> +	mt_set_modes(hdev, HID_LATENCY_NORMAL, false, false);
> +	/* Elan's touchpad VID 323B needs this delay to handle both switch
> +	 * surface off and switch surface on and trigger recalibration
> +	 * properly.
> +	 */
> +	msleep(50);
> +	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
> +	mutex_unlock(&td->mode_mutex);
> +}
> +
> +static const struct dmi_system_id mt_lid_handler_dmi_table[] = {
> +	{
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Redrix"),
> +		},
> +	},
> +	{
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "Redrix4ES"),
> +		},
> +	},
> +	{}
> +};
> +
> +static int mt_create_lid_handler(void)
> +{
> +	int error = 0;
> +
> +	if (!dmi_check_system(mt_lid_handler_dmi_table))
> +		return 0;
> +
> +	mt_mode_wq = alloc_ordered_workqueue("hid-mt-lid", WQ_FREEZABLE);
> +	if (mt_mode_wq == NULL)
> +		return -ENOMEM;
> +
> +	error = input_register_handler(&mt_input_lid_handler);
> +	if (error)
> +		goto remove_wq;
> +
> +	return 0;
> +
> +remove_wq:
> +	destroy_workqueue(mt_mode_wq);
> +	mt_mode_wq = NULL;
> +	return error;
> +}
> +
> +static void mt_configure_lid_handler(struct mt_device *td)
> +{
> +	struct hid_device *hdev = td->hdev;
> +
> +	if (hdev->bus != BUS_I2C)
> +		return;
> +
> +	td->lid_switch = true;
> +	list_add_tail(&td->list, &mt_devices_with_lid_handler);
> +}
> +
> +static void mt_destroy_lid_handler(void)
> +{
> +	input_unregister_handler(&mt_input_lid_handler);
> +	destroy_workqueue(mt_mode_wq);
> +}
> +
>   static struct mt_application *mt_allocate_application(struct mt_device *td,
>   						      struct hid_report *report)
>   {
> @@ -571,6 +744,8 @@ static struct mt_application *mt_allocate_application(struct mt_device *td,
>   	if (application == HID_DG_TOUCHPAD) {
>   		mt_application->mt_flags |= INPUT_MT_POINTER;
>   		td->inputmode_value = MT_INPUTMODE_TOUCHPAD;
> +		if (mt_mode_wq)
> +			mt_configure_lid_handler(td);
>   	}
>   
>   	mt_application->scantime = DEFAULT_ZERO;
> @@ -1767,6 +1942,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
>   	INIT_LIST_HEAD(&td->applications);
>   	INIT_LIST_HEAD(&td->reports);
>   
> +	INIT_LIST_HEAD(&td->list);
> +	INIT_WORK(&td->lid_work, lid_work_handler);
> +	mutex_init(&td->mode_mutex);
> +
>   	if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
>   		td->serial_maybe = true;
>   
> @@ -1830,12 +2009,18 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state)
>   	struct mt_device *td = hid_get_drvdata(hdev);
>   	struct hid_haptic_device *haptic = td->haptic;
>   
> +	/* Wait for switch on completion */
> +	if (td->lid_switch)
> +		flush_workqueue(mt_mode_wq);
> +
> +	mutex_lock(&td->mode_mutex);
>   	/* High latency is desirable for power savings during S3/S0ix */
>   	if ((td->mtclass.quirks & MT_QUIRK_DISABLE_WAKEUP) ||
>   	    !hid_hw_may_wakeup(hdev))
>   		mt_set_modes(hdev, HID_LATENCY_HIGH, false, false);
>   	else
>   		mt_set_modes(hdev, HID_LATENCY_HIGH, true, true);
> +	mutex_unlock(&td->mode_mutex);
>   
>   	if (td->is_haptic_touchpad)
>   		hid_haptic_suspend(hdev, haptic);
> @@ -1849,7 +2034,10 @@ static int mt_reset_resume(struct hid_device *hdev)
>   	struct hid_haptic_device *haptic = td->haptic;
>   
>   	mt_release_contacts(hdev);
> +
> +	mutex_lock(&td->mode_mutex);
>   	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
> +	mutex_unlock(&td->mode_mutex);
>   
>   	if (td->is_haptic_touchpad)
>   		hid_haptic_resume(hdev, haptic);
> @@ -1868,7 +2056,9 @@ static int mt_resume(struct hid_device *hdev)
>   
>   	hid_hw_idle(hdev, 0, 0, HID_REQ_SET_IDLE);
>   
> +	mutex_lock(&td->mode_mutex);
>   	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
> +	mutex_unlock(&td->mode_mutex);
>   
>   	if (td->is_haptic_touchpad)
>   		hid_haptic_resume(hdev, haptic);
> @@ -1883,7 +2073,9 @@ static int mt_reset(struct hid_device *hdev)
>   	struct hid_haptic_device *haptic = td->haptic;
>   
>   	mt_release_contacts(hdev);
> +	mutex_lock(&td->mode_mutex);
>   	mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true);
> +	mutex_unlock(&td->mode_mutex);
>   
>   	if (td->is_haptic_touchpad)
>   		hid_haptic_reset(hdev, haptic);
> @@ -1899,6 +2091,8 @@ static void mt_remove(struct hid_device *hdev)
>   
>   	sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
>   	hid_hw_stop(hdev);
> +
> +	list_del(&td->list);
>   }
>   
>   /*
> @@ -2302,4 +2496,28 @@ static struct hid_driver mt_driver = {
>   	.resume = mt_resume,
>   #endif
>   };
> -module_hid_driver(mt_driver);
> +
> +static int __init mt_init(void)
> +{
> +	int ret;
> +
> +	ret = hid_register_driver(&mt_driver);
> +	if (ret)
> +		return ret;
> +
> +	ret = mt_create_lid_handler();
> +	if (ret)
> +		hid_unregister_driver(&mt_driver);
> +
> +	return ret;
> +}
> +module_init(mt_init);
> +
> +static void __exit mt_exit(void)
> +{
> +	if (mt_mode_wq)
> +		mt_destroy_lid_handler();
> +
> +	hid_unregister_driver(&mt_driver);
> +}
> +module_exit(mt_exit);
> 


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v3 00/17] *** Implement simple haptic HID support ***
  2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
                   ` (16 preceding siblings ...)
  2022-05-13  9:39 ` [PATCH v3 17/17] HID: multitouch: Add lid handler for touchpad on Redrix chromebook Angela Czubak
@ 2022-05-22 17:55 ` Angela Czubak
  2022-05-23  6:57   ` Jiri Kosina
  17 siblings, 1 reply; 21+ messages in thread
From: Angela Czubak @ 2022-05-22 17:55 UTC (permalink / raw)
  To: open list:HID CORE LAYER
  Cc: upstream, Benjamin Tissoires, Jiri Kosina, Dmitry Torokhov

On Fri, May 13, 2022 at 11:39 AM Angela Czubak <acz@semihalf.com> wrote:
>
> This patch series introduces changes necessary to support devices
> using simple haptic HID pages.
> Implementation attempts to follow the discussion below:
> https://www.spinics.net/lists/linux-input/msg61091.html
>
> Introduce new haptic defines as specified in HID Usage Tables.
>
> Add new force feedback effect type in order to facilitate using
> simple haptic force feedback.
>
> Add INPUT_PROP_HAPTIC_TOUCHPAD to mark touchpad exposing simple haptic
> support.
>
> Add new struct hid_haptic_device so as to gather simple haptic related
> configuration and current state of the device.
>
> Add new functions to be triggered during HID input mapping and
> configuration in order to detect simple haptic devices.
>
> Modify HID input so that haptic output reports are parsed.
>
> Initialize a haptic device.
>
> Modify FF core so that effect IDs can be shared between multiple open file
> handles.
>
> Add shared release and press effects for a simple haptic device.
>
> Calculate pressure resolution if units are grams or newtons.
>
> Add support for kernel-driven mode of simple haptic device.
>
> Toggle ABS_PRESSURE generation by input-mt on request.
>
> Implement functions allowing switching between kernel-managed mode
> and autonomous mode.
>
> Add simple haptic support for hid-multitouch driver.
>
> Implement EVIOCFF(TAKE|RELEASE)CONTROL ioctls so that userspace can take
> and release control of shared release and press effects.
>
> Fix i2c hid sending and getting report so that report IDs larger than 0xF
> are handled correctly.
>
> v2:
> - Describe INPUT_PROP_HAPTIC_TOUCHPAD in
>   Documentation/input/event-codes.rst
> - Do not extract mt_get_feature(), use hid_hw_wait() instead
> - Define HID_UNIT_GRAM and HID_UNIT_NEWTON
> - Calculate pressure sum in input-mt if INPUT_MT_TOTAL_FORCE flags set
> - Use u* instead of __u* in struct hid_haptic_device
> - Solve problems with report IDS >= 0xF as Dmitry suggests
>
> v3:
> - Get rid of INPUT_PROP_HAPTIC_TOUCHPAD property as haptic device does not
>   gave to be a touchpad
> - Introduce notion of haptic forcepads; generate haptic feedback in kernel
>   mode only for forcepads
> - Generate clicks based on maximum pressure across slots instead of the sum
> - Fix off-by-one bug in hid_haptic_upload_effect()
> - Fix resume/suspend: issue hid_haptic_resume() in mt_resume() and
>   hid_haptic_suspend() in mt_suspend()
> - Add reset callback for HID i2c devices
> - Implement reset callback for HID multitouch haptic devices
> - Implement lid handler triggering touchpad recalibration for Redrix
>
> Angela Czubak (17):
>   HID: add haptics page defines
>   Input: add FF_HID effect type
>   HID: haptic: introduce hid_haptic_device
>   HID: input: allow mapping of haptic output
>   HID: haptic: initialize haptic device
>   Input: add shared effects
>   HID: haptic: implement release and press effects
>   HID: input: calculate resolution for pressure
>   HID: haptic: add functions handling events
>   Input: MT - add INPUT_MT_MAX_FORCE flags
>   HID: haptic: add hid_haptic_switch_mode
>   HID: multitouch: add haptic multitouch support
>   Input: introduce EVIOCFF(TAKE|RELEASE)CONTROL
>   HID: haptic: add hid_haptic_change_control
>   HID: add HID device reset callback
>   HID: haptic: implement HID haptic reset callback
>   HID: multitouch: Add lid handler for touchpad on Redrix chromebook
>
>  drivers/hid/Kconfig                |  14 +
>  drivers/hid/Makefile               |   1 +
>  drivers/hid/hid-haptic.c           | 753 +++++++++++++++++++++++++++++
>  drivers/hid/hid-haptic.h           | 152 ++++++
>  drivers/hid/hid-input.c            |  18 +-
>  drivers/hid/hid-multitouch.c       | 313 +++++++++++-
>  drivers/hid/i2c-hid/i2c-hid-core.c |  21 +
>  drivers/input/evdev.c              |   6 +
>  drivers/input/ff-core.c            | 129 ++++-
>  drivers/input/input-mt.c           |  16 +-
>  include/linux/hid.h                |  31 ++
>  include/linux/input.h              |   5 +
>  include/linux/input/mt.h           |   1 +
>  include/uapi/linux/input.h         |  26 +-
>  14 files changed, 1469 insertions(+), 17 deletions(-)
>  create mode 100644 drivers/hid/hid-haptic.c
>  create mode 100644 drivers/hid/hid-haptic.h
>
> --
> 2.36.0.550.gb090851708-goog
>

Hi, could I get any feedback? :) Or should I wait for some features to
land in the upstream kernel?

Regards,
Angela

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v3 00/17] *** Implement simple haptic HID support ***
  2022-05-22 17:55 ` [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
@ 2022-05-23  6:57   ` Jiri Kosina
  0 siblings, 0 replies; 21+ messages in thread
From: Jiri Kosina @ 2022-05-23  6:57 UTC (permalink / raw)
  To: Angela Czubak
  Cc: open list:HID CORE LAYER, upstream, Benjamin Tissoires, Dmitry Torokhov

On Sun, 22 May 2022, Angela Czubak wrote:

> > This patch series introduces changes necessary to support devices
> > using simple haptic HID pages.
> > Implementation attempts to follow the discussion below:
> > https://www.spinics.net/lists/linux-input/msg61091.html
> >
> > Introduce new haptic defines as specified in HID Usage Tables.
> >
> > Add new force feedback effect type in order to facilitate using
> > simple haptic force feedback.
> >
> > Add INPUT_PROP_HAPTIC_TOUCHPAD to mark touchpad exposing simple haptic
> > support.
> >
> > Add new struct hid_haptic_device so as to gather simple haptic related
> > configuration and current state of the device.
> >
> > Add new functions to be triggered during HID input mapping and
> > configuration in order to detect simple haptic devices.
> >
> > Modify HID input so that haptic output reports are parsed.
> >
> > Initialize a haptic device.
> >
> > Modify FF core so that effect IDs can be shared between multiple open file
> > handles.
> >
> > Add shared release and press effects for a simple haptic device.
> >
> > Calculate pressure resolution if units are grams or newtons.
> >
> > Add support for kernel-driven mode of simple haptic device.
> >
> > Toggle ABS_PRESSURE generation by input-mt on request.
> >
> > Implement functions allowing switching between kernel-managed mode
> > and autonomous mode.
> >
> > Add simple haptic support for hid-multitouch driver.
> >
> > Implement EVIOCFF(TAKE|RELEASE)CONTROL ioctls so that userspace can take
> > and release control of shared release and press effects.
> >
> > Fix i2c hid sending and getting report so that report IDs larger than 0xF
> > are handled correctly.
> >
> > v2:
> > - Describe INPUT_PROP_HAPTIC_TOUCHPAD in
> >   Documentation/input/event-codes.rst
> > - Do not extract mt_get_feature(), use hid_hw_wait() instead
> > - Define HID_UNIT_GRAM and HID_UNIT_NEWTON
> > - Calculate pressure sum in input-mt if INPUT_MT_TOTAL_FORCE flags set
> > - Use u* instead of __u* in struct hid_haptic_device
> > - Solve problems with report IDS >= 0xF as Dmitry suggests
> >
> > v3:
> > - Get rid of INPUT_PROP_HAPTIC_TOUCHPAD property as haptic device does not
> >   gave to be a touchpad
> > - Introduce notion of haptic forcepads; generate haptic feedback in kernel
> >   mode only for forcepads
> > - Generate clicks based on maximum pressure across slots instead of the sum
> > - Fix off-by-one bug in hid_haptic_upload_effect()
> > - Fix resume/suspend: issue hid_haptic_resume() in mt_resume() and
> >   hid_haptic_suspend() in mt_suspend()
> > - Add reset callback for HID i2c devices
> > - Implement reset callback for HID multitouch haptic devices
> > - Implement lid handler triggering touchpad recalibration for Redrix
> >
> > Angela Czubak (17):
> >   HID: add haptics page defines
> >   Input: add FF_HID effect type
> >   HID: haptic: introduce hid_haptic_device
> >   HID: input: allow mapping of haptic output
> >   HID: haptic: initialize haptic device
> >   Input: add shared effects
> >   HID: haptic: implement release and press effects
> >   HID: input: calculate resolution for pressure
> >   HID: haptic: add functions handling events
> >   Input: MT - add INPUT_MT_MAX_FORCE flags
> >   HID: haptic: add hid_haptic_switch_mode
> >   HID: multitouch: add haptic multitouch support
> >   Input: introduce EVIOCFF(TAKE|RELEASE)CONTROL
> >   HID: haptic: add hid_haptic_change_control
> >   HID: add HID device reset callback
> >   HID: haptic: implement HID haptic reset callback
> >   HID: multitouch: Add lid handler for touchpad on Redrix chromebook
> >
> >  drivers/hid/Kconfig                |  14 +
> >  drivers/hid/Makefile               |   1 +
> >  drivers/hid/hid-haptic.c           | 753 +++++++++++++++++++++++++++++
> >  drivers/hid/hid-haptic.h           | 152 ++++++
> >  drivers/hid/hid-input.c            |  18 +-
> >  drivers/hid/hid-multitouch.c       | 313 +++++++++++-
> >  drivers/hid/i2c-hid/i2c-hid-core.c |  21 +
> >  drivers/input/evdev.c              |   6 +
> >  drivers/input/ff-core.c            | 129 ++++-
> >  drivers/input/input-mt.c           |  16 +-
> >  include/linux/hid.h                |  31 ++
> >  include/linux/input.h              |   5 +
> >  include/linux/input/mt.h           |   1 +
> >  include/uapi/linux/input.h         |  26 +-
> >  14 files changed, 1469 insertions(+), 17 deletions(-)
> >  create mode 100644 drivers/hid/hid-haptic.c
> >  create mode 100644 drivers/hid/hid-haptic.h
> >
> > --
> > 2.36.0.550.gb090851708-goog
> >
> 
> Hi, could I get any feedback? :) Or should I wait for some features to
> land in the upstream kernel?

Hi Angela,

I am in the process of reviewing the patchset, but I am not done with it 
yet. It won't land in 5.19 anyway, so if all goes good (and we also 
eventually get Ack from Dmitry at least for the Input-specific bits), we 
can potentially target 5.20 unless something substantial pops up.

Please give me/us a few more days to process this.

Thanks,

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2022-05-23  7:30 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-13  9:39 [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
2022-05-13  9:39 ` [PATCH v3 01/17] HID: add haptics page defines Angela Czubak
2022-05-13  9:39 ` [PATCH v3 02/17] Input: add FF_HID effect type Angela Czubak
2022-05-13  9:39 ` [PATCH v3 03/17] HID: haptic: introduce hid_haptic_device Angela Czubak
2022-05-13  9:39 ` [PATCH v3 04/17] HID: input: allow mapping of haptic output Angela Czubak
2022-05-13  9:39 ` [PATCH v3 05/17] HID: haptic: initialize haptic device Angela Czubak
2022-05-13  9:39 ` [PATCH v3 06/17] Input: add shared effects Angela Czubak
2022-05-13  9:39 ` [PATCH v3 07/17] HID: haptic: implement release and press effects Angela Czubak
2022-05-13  9:39 ` [PATCH v3 08/17] HID: input: calculate resolution for pressure Angela Czubak
2022-05-13  9:39 ` [PATCH v3 09/17] HID: haptic: add functions handling events Angela Czubak
2022-05-13  9:39 ` [PATCH v3 10/17] Input: MT - add INPUT_MT_MAX_FORCE flags Angela Czubak
2022-05-13  9:39 ` [PATCH v3 11/17] HID: haptic: add hid_haptic_switch_mode Angela Czubak
2022-05-13  9:39 ` [PATCH v3 12/17] HID: multitouch: add haptic multitouch support Angela Czubak
2022-05-13  9:39 ` [PATCH v3 13/17] Input: introduce EVIOCFF(TAKE|RELEASE)CONTROL Angela Czubak
2022-05-13  9:39 ` [PATCH v3 14/17] HID: haptic: add hid_haptic_change_control Angela Czubak
2022-05-13  9:39 ` [PATCH v3 15/17] HID: add HID device reset callback Angela Czubak
2022-05-13  9:39 ` [PATCH v3 16/17] HID: haptic: implement HID haptic " Angela Czubak
2022-05-13  9:39 ` [PATCH v3 17/17] HID: multitouch: Add lid handler for touchpad on Redrix chromebook Angela Czubak
2022-05-15  0:07   ` marcoshalano
2022-05-22 17:55 ` [PATCH v3 00/17] *** Implement simple haptic HID support *** Angela Czubak
2022-05-23  6:57   ` Jiri Kosina

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).