[PATCH 3/9] HID: haptic, multitouch: Check haptic prerequisite properly
From: Rong Zhang
Date: Sun Nov 01 2026 - 12:43:11 EST
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
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.
Fixes: 7939f787f450 ("HID: haptic: move FF initialization into .input_configured()")
Signed-off-by: Rong Zhang <i@xxxxxxxx>
---
drivers/hid/hid-haptic.c | 64 ++++++++++++++++++++++++++------------------
drivers/hid/hid-haptic.h | 23 +++++++---------
drivers/hid/hid-multitouch.c | 30 ++++++++++-----------
3 files changed, 61 insertions(+), 56 deletions(-)
diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index 9cbacc827dc9..27cc8a2d9b59 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;
+
+ /* Don't care about input devices with no pressure sensor. */
+ if (field->unit != HID_UNIT_GRAM && field->unit != HID_UNIT_NEWTON)
+ return;
+
+ if (IS_ERR(haptic->input_dev))
+ return;
+
+ 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 (haptic->input_dev == 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 if:
+ * - there are multiple input devices with pressure sensors
+ * - the pressure annotation mismatches
+ */
+ haptic->input_dev = ERR_PTR(-EPROTO);
+ return;
}
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);
@@ -539,6 +550,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..1c11fcbc76f8 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;
@@ -59,16 +58,15 @@ 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);
@@ -83,11 +81,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 +94,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 4e19a0c4d238..d744ccc3e0b0 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,
@@ -962,9 +963,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:
@@ -1554,10 +1554,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;
@@ -1955,14 +1956,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