Commit ff66b8ee authored by Angela Czubak's avatar Angela Czubak Committed by Benjamin Tissoires
Browse files

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: default avatarAngela Czubak <aczubak@google.com>
Co-developed-by: default avatarJonathan Denose <jdenose@google.com>
Signed-off-by: default avatarJonathan Denose <jdenose@google.com>
Signed-off-by: default avatarBenjamin Tissoires <bentiss@kernel.org>
parent 4e584ac7
Loading
Loading
Loading
Loading
+59 −7
Original line number Diff line number Diff line
@@ -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]);