From: Rong Zhang <i@rong.moe>
To: Jiri Kosina <jikos@kernel.org>,
Benjamin Tissoires <bentiss@kernel.org>,
Angela Czubak <aczubak@google.com>,
Henrik Rydberg <rydberg@bitmath.org>,
Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
Rong Zhang <i@rong.moe>
Subject: [PATCH v3 3/9] HID: haptic, multitouch: Check haptic prerequisite properly
Date: Fri, 13 Nov 2026 01:53:56 +0800 [thread overview]
Message-ID: <20261113-hid-haptic-kmod-v3-3-e7dfa8766c0d@rong.moe> (raw)
In-Reply-To: <20261113-hid-haptic-kmod-v3-0-e7dfa8766c0d@rong.moe>
The td->is_haptic_touchpad boolean is multiplexed through during the
probe- and run-time procedures of hid-multitouch. However, the way it
flips is fragile on faulty devices.
MT touchpads have multiple HID_DG_TIPPRESSURE usages, but the value of
td->is_haptic_touchpad only depends on the last usage, and doesn't check
if all usages have the same critical annotations. Moreover, if the HID
device happens to have multiple HID_DG_TOUCHPAD applications, the haptic
device may be initialized more than once.
Fix them by storing the relevant HID_DG_TOUCHPAD input device (must have
a pressure sensor) during hid_haptic_check_pressure_unit(), with proper
sanity checks that reject annotation mismatches and duplicated input
devices. Later, hid_haptic_input_configured() continues the
initialization only if the input device matches the previously stored
one.
With that, the td->is_haptic_touchpad is converted into a one-way (false
=> true) flipping manner. All calls to hid_haptic_*() during the
probe-time procedures are now guarded by the nullability of td->haptic
instead of td->is_haptic_touchpad or no guard, preparing to skip
allocating td->haptic in !CONFIG_HID_HAPTIC builds in the following
changes.
At runtime, relevant haptic methods are called only if the event source
is the same input device as the stored one.
Fixes: 7939f787f450 ("HID: haptic: move FF initialization into .input_configured()")
Signed-off-by: Rong Zhang <i@rong.moe>
---
Changes in v2:
- Reject touchpad applications with non-force units (thanks Sashiko)
- Only call runtime methods if the event source is the same input device
as the stored one (ditto)
---
drivers/hid/hid-haptic.c | 64 ++++++++++++++++++++++++++------------------
drivers/hid/hid-haptic.h | 35 ++++++++++++++----------
drivers/hid/hid-multitouch.c | 36 ++++++++++++-------------
3 files changed, 76 insertions(+), 59 deletions(-)
diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index 9817d661f93d..84985ad61d45 100644
--- a/drivers/hid/hid-haptic.c
+++ b/drivers/hid/hid-haptic.c
@@ -48,17 +48,40 @@ void hid_haptic_feature_mapping(struct hid_device *hdev,
}
EXPORT_SYMBOL_GPL(hid_haptic_feature_mapping);
-bool hid_haptic_check_pressure_unit(struct hid_haptic_device *haptic,
+void hid_haptic_check_pressure_unit(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->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);
- return true;
+ if (hi->application != HID_DG_TOUCHPAD)
+ return;
+
+ if (IS_ERR(haptic->input_dev))
+ return;
+
+ if (field->unit != HID_UNIT_GRAM && field->unit != HID_UNIT_NEWTON)
+ goto reject;
+
+ if (!haptic->input_dev) {
+ haptic->input_dev = hi->input;
+ haptic->pressure_field = field;
+ haptic->force_resolution = input_abs_get_res(hi->input, ABS_MT_PRESSURE);
+ return;
}
- return false;
+
+ if (hid_haptic_connected_to_input(haptic, hi->input) &&
+ haptic->pressure_field->unit == field->unit &&
+ haptic->pressure_field->logical_minimum == field->logical_minimum &&
+ haptic->pressure_field->physical_minimum == field->physical_minimum &&
+ haptic->force_resolution == input_abs_get_res(hi->input, ABS_MT_PRESSURE))
+ return;
+
+reject:
+ /*
+ * Reject if:
+ * - there are multiple input devices with pressure sensors
+ * - there is any touchpad application with a non-force unit
+ * - pressure annotations mismatch among HID_DG_TIPPRESSURE usages
+ */
+ haptic->input_dev = ERR_PTR(-EPROTO);
}
EXPORT_SYMBOL_GPL(hid_haptic_check_pressure_unit);
@@ -78,28 +101,16 @@ int hid_haptic_input_mapping(struct hid_device *hdev,
}
EXPORT_SYMBOL_GPL(hid_haptic_input_mapping);
-int hid_haptic_input_configured(struct hid_device *hdev,
- struct hid_haptic_device *haptic,
- struct hid_input *hi)
+bool hid_haptic_input_configured(struct hid_device *hdev,
+ struct hid_haptic_device *haptic, struct hid_input *hi)
{
- int error;
-
- if (hi->application != HID_DG_TOUCHPAD)
- return -1;
-
- if (!haptic->auto_trigger_report || !haptic->manual_trigger_report)
- return 0;
+ if (IS_ERR_OR_NULL(haptic->input_dev) || haptic->input_dev != hi->input ||
+ !haptic->auto_trigger_report || !haptic->manual_trigger_report)
+ return false;
__set_bit(INPUT_PROP_PRESSUREPAD, hi->input->propbit);
- error = hid_haptic_init(hdev, haptic, hi->input);
- if (error) {
- dev_warn(&hdev->dev, "Cannot allocate haptic for %s\n",
- hdev->name);
- return 0;
- }
-
- return 1;
+ return hid_haptic_init(hdev, haptic, hi->input) == 0;
}
EXPORT_SYMBOL_GPL(hid_haptic_input_configured);
@@ -537,6 +548,7 @@ int hid_haptic_init(struct hid_device *hdev,
put_device:
put_device(&hdev->dev);
exit:
+ dev_warn(&hdev->dev, "Cannot allocate haptic for %s: %d\n", hdev->name, ret);
module_put(THIS_MODULE);
return ret;
}
diff --git a/drivers/hid/hid-haptic.h b/drivers/hid/hid-haptic.h
index 6332991a7844..cd4d9c74d2dc 100644
--- a/drivers/hid/hid-haptic.h
+++ b/drivers/hid/hid-haptic.h
@@ -33,12 +33,11 @@ struct hid_haptic_device {
struct mutex auto_trigger_mutex;
struct workqueue_struct *wq;
struct hid_report *manual_trigger_report;
+ struct hid_field *pressure_field;
struct mutex manual_trigger_mutex;
size_t manual_trigger_report_len;
int pressed_state;
s32 pressure_sum;
- s32 force_logical_minimum;
- s32 force_physical_minimum;
s32 force_resolution;
u32 mode;
u32 default_auto_trigger;
@@ -55,20 +54,25 @@ struct hid_haptic_device {
};
#if IS_ENABLED(CONFIG_HID_HAPTIC)
+static inline bool hid_haptic_connected_to_input(struct hid_haptic_device *haptic,
+ struct input_dev *input)
+{
+ return haptic->input_dev == input;
+}
+
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_check_pressure_unit(struct hid_haptic_device *haptic,
+void hid_haptic_check_pressure_unit(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);
-int hid_haptic_input_configured(struct hid_device *hdev,
- struct hid_haptic_device *haptic,
- struct hid_input *hi);
+bool hid_haptic_input_configured(struct hid_device *hdev,
+ struct hid_haptic_device *haptic, struct hid_input *hi);
int hid_haptic_init(struct hid_device *hdev, struct hid_haptic_device *haptic,
struct input_dev *dev);
void hid_haptic_handle_press_release(struct hid_haptic_device *haptic);
@@ -76,6 +80,12 @@ void hid_haptic_pressure_reset(struct hid_haptic_device *haptic);
void hid_haptic_pressure_increase(struct hid_haptic_device *haptic,
__s32 pressure);
#else
+static inline bool hid_haptic_connected_to_input(struct hid_haptic_device *haptic,
+ struct input_dev *input)
+{
+ return false;
+}
+
static inline
void hid_haptic_feature_mapping(struct hid_device *hdev,
struct hid_haptic_device *haptic,
@@ -83,11 +93,9 @@ void hid_haptic_feature_mapping(struct hid_device *hdev,
*usage)
{}
static inline
-bool hid_haptic_check_pressure_unit(struct hid_haptic_device *haptic,
+void hid_haptic_check_pressure_unit(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,
@@ -98,11 +106,10 @@ int hid_haptic_input_mapping(struct hid_device *hdev,
return 0;
}
static inline
-int hid_haptic_input_configured(struct hid_device *hdev,
- struct hid_haptic_device *haptic,
- struct hid_input *hi)
+bool hid_haptic_input_configured(struct hid_device *hdev,
+ struct hid_haptic_device *haptic, struct hid_input *hi)
{
- return 0;
+ return false;
}
static inline
void hid_haptic_reset(struct hid_device *hdev, struct hid_haptic_device *haptic)
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 3afca07025ea..5eea774dee44 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -605,7 +605,8 @@ static void mt_feature_mapping(struct hid_device *hdev,
break;
}
- hid_haptic_feature_mapping(hdev, td->haptic, field, usage);
+ if (td->haptic)
+ hid_haptic_feature_mapping(hdev, td->haptic, field, usage);
}
static void set_abs(struct input_dev *input, unsigned int code,
@@ -961,9 +962,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_haptic_touchpad =
- hid_haptic_check_pressure_unit(td->haptic,
- hi, field);
+ if (td->haptic)
+ hid_haptic_check_pressure_unit(td->haptic, hi, field);
MT_STORE_FIELD(p);
return 1;
case HID_DG_SCANTIME:
@@ -1085,7 +1085,7 @@ 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)
+ if (td->is_haptic_touchpad && hid_haptic_connected_to_input(td->haptic, input))
hid_haptic_pressure_reset(td->haptic);
}
@@ -1238,7 +1238,7 @@ static int mt_process_slot(struct mt_device *td, struct input_dev *input,
minor = minor >> 1;
}
- if (td->is_haptic_touchpad)
+ if (td->is_haptic_touchpad && hid_haptic_connected_to_input(td->haptic, input))
hid_haptic_pressure_increase(td->haptic, *slot->p);
x = hdev->quirks & HID_QUIRK_X_INVERT ?
@@ -1441,7 +1441,7 @@ static int mt_touch_input_configured(struct hid_device *hdev,
if (cls->is_indirect)
app->mt_flags |= INPUT_MT_POINTER;
- if (td->is_haptic_touchpad)
+ if (td->is_haptic_touchpad && hid_haptic_connected_to_input(td->haptic, input))
app->mt_flags |= INPUT_MT_TOTAL_FORCE;
if (app->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
@@ -1553,10 +1553,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;
+ if (td->haptic) {
+ ret = hid_haptic_input_mapping(hdev, td->haptic, hi, field, usage, bit, max);
+ if (ret)
+ return ret;
+ }
/* let hid-core decide for the others */
return 0;
@@ -1954,14 +1955,11 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
struct hid_report *report;
int ret;
- if (td->is_haptic_touchpad && (td->mtclass.name == MT_CLS_WIN_8 ||
- td->mtclass.name == MT_CLS_WIN_8_FORCE_MULTI_INPUT ||
- td->mtclass.name == MT_CLS_WIN_8_KEEP_LATENCY_ON_CLOSE)) {
- if (hid_haptic_input_configured(hdev, td->haptic, hi) == 0)
- td->is_haptic_touchpad = false;
- } else {
- td->is_haptic_touchpad = false;
- }
+ if (td->haptic && !td->is_haptic_touchpad &&
+ (td->mtclass.name == MT_CLS_WIN_8 ||
+ td->mtclass.name == MT_CLS_WIN_8_FORCE_MULTI_INPUT ||
+ td->mtclass.name == MT_CLS_WIN_8_KEEP_LATENCY_ON_CLOSE))
+ td->is_haptic_touchpad = hid_haptic_input_configured(hdev, td->haptic, hi);
list_for_each_entry(report, &hi->reports, hidinput_list) {
rdata = mt_find_report_data(td, report);
--
2.55.0
next prev parent reply other threads:[~2026-11-12 17:54 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-12 17:53 [PATCH v3 0/9] HID: Add hid-plugin framework & convert haptic into a plugin Rong Zhang
2026-11-12 17:53 ` [PATCH v3 1/9] HID: haptic: Fix use-after-free when draining workqueue Rong Zhang
2026-11-12 17:53 ` [PATCH v3 2/9] HID: haptic: Get rid of input_ff_destroy() in error paths Rong Zhang
2026-11-13 5:50 ` Dmitry Torokhov
2026-11-12 17:53 ` Rong Zhang [this message]
2026-11-12 17:53 ` [PATCH v3 4/9] HID: haptic, multitouch: Fix haptic device double-free and unnecessary allocation Rong Zhang
2026-11-12 17:53 ` [PATCH v3 5/9] HID: haptic: Postpone GET_REPORT to init Rong Zhang
2026-11-12 17:53 ` [PATCH v3 6/9] HID: haptic: Convert simple methods into static inline ones Rong Zhang
2026-11-12 17:54 ` [PATCH v3 7/9] HID: Add hid-plugin framework for loadable plugin modules Rong Zhang
2026-11-12 17:54 ` [PATCH v3 8/9] HID: haptic: Convert into a loadable plugin module Rong Zhang
2026-11-12 17:54 ` [PATCH v3 9/9] HID: haptic: Remove stubs in favor of inlined dead code elimination Rong Zhang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261113-hid-haptic-kmod-v3-3-e7dfa8766c0d@rong.moe \
--to=i@rong.moe \
--cc=aczubak@google.com \
--cc=bentiss@kernel.org \
--cc=dmitry.torokhov@gmail.com \
--cc=jikos@kernel.org \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=rydberg@bitmath.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®