HID: haptic: add hid_haptic_switch_mode

Function hid_haptic_switch_mode() can be used to switch between
device-controlled mode and host-controlled mode. Uploading a
WAVEFORMPRESS or WAVEFORMRELEASE effect triggers host-controlled mode if
the device is in device-controlled mode.

Signed-off-by: Angela Czubak <aczubak@google.com>
Co-developed-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Jonathan Denose <jdenose@google.com>
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
This commit is contained in:
Angela Czubak 2025-08-18 23:08:51 +00:00 committed by Benjamin Tissoires
parent 4e584ac737
commit ff66b8eebb

View file

@ -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"
@ -197,12 +198,46 @@ 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_HOST)
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;
}
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;
bool switch_modes = false;
/* If vendor range, check vendor id and page */
if (effect->u.haptic.hid_usage >= (HID_HP_VENDORWAVEFORMMIN & HID_USAGE) &&
@ -225,6 +260,16 @@ static int hid_haptic_upload_effect(struct input_dev *dev, struct ff_effect *eff
fill_effect_buf(haptic, &effect->u.haptic, &haptic->effect[effect->id],
ordinal);
if (effect->u.haptic.hid_usage == (HID_HP_WAVEFORMPRESS & HID_USAGE) ||
effect->u.haptic.hid_usage == (HID_HP_WAVEFORMRELEASE & HID_USAGE))
switch_modes = true;
/* If device is in autonomous mode, and the uploaded effect signals userspace
* wants control of the device, change modes
*/
if (switch_modes && haptic->mode == HID_HAPTIC_MODE_DEVICE)
switch_mode(hdev, haptic, HID_HAPTIC_MODE_HOST);
return 0;
}
@ -290,6 +335,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;
@ -297,20 +343,25 @@ static int hid_haptic_erase(struct input_dev *dev, int effect_id)
if (effect.u.haptic.hid_usage == (HID_HP_WAVEFORMRELEASE & HID_USAGE)) {
ordinal = haptic->release_ordinal;
if (!ordinal)
if (!ordinal) {
ordinal = HID_HAPTIC_ORDINAL_WAVEFORMNONE;
else
effect.u.haptic.hid_usage = HID_HP_WAVEFORMRELEASE &
HID_USAGE;
if (haptic->mode == HID_HAPTIC_MODE_HOST)
switch_mode(hdev, haptic, HID_HAPTIC_MODE_DEVICE);
} else
effect.u.haptic.hid_usage = HID_HP_WAVEFORMRELEASE & HID_USAGE;
fill_effect_buf(haptic, &effect.u.haptic, &haptic->effect[effect_id],
ordinal);
} else if (effect.u.haptic.hid_usage == (HID_HP_WAVEFORMPRESS & HID_USAGE)) {
ordinal = haptic->press_ordinal;
if (!ordinal)
if (!ordinal) {
ordinal = HID_HAPTIC_ORDINAL_WAVEFORMNONE;
if (haptic->mode == HID_HAPTIC_MODE_HOST)
switch_mode(hdev, haptic, HID_HAPTIC_MODE_DEVICE);
}
else
effect.u.haptic.hid_usage = HID_HP_WAVEFORMPRESS &
HID_USAGE;
effect.u.haptic.hid_usage = HID_HP_WAVEFORMPRESS & HID_USAGE;
fill_effect_buf(haptic, &effect.u.haptic, &haptic->effect[effect_id],
ordinal);
}
@ -392,6 +443,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]);