linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] HID: hid-stadiaff: add support for Stadia force feedback
@ 2023-04-03 10:33 Fabio Baltieri
  2023-04-13 16:00 ` Benjamin Tissoires
  0 siblings, 1 reply; 3+ messages in thread
From: Fabio Baltieri @ 2023-04-03 10:33 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires; +Cc: linux-input, linux-kernel, Fabio Baltieri

Add a hid-stadiaff module to support rumble based force feedback on the
Google Stadia controller. This works using the HID output endpoint
exposed on both the USB and BLE interface.

Signed-off-by: Fabio Baltieri <fabiobaltieri@chromium.org>
---

Hi, this adds rumble support to the stadia controller using the input
interface. Up to now this has only been implemented at application level
using hidraw:

https://source.chromium.org/chromium/chromium/src/+/main:device/gamepad/hid_haptic_gamepad.cc

Tested with fftest, works both over USB and BLE.

 drivers/hid/Kconfig        |   7 ++
 drivers/hid/Makefile       |   1 +
 drivers/hid/hid-ids.h      |   1 +
 drivers/hid/hid-stadiaff.c | 132 +++++++++++++++++++++++++++++++++++++
 4 files changed, 141 insertions(+)
 create mode 100644 drivers/hid/hid-stadiaff.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 82f64fb31fda..934f73e9b800 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1031,6 +1031,13 @@ config HID_SPEEDLINK
 	help
 	Support for Speedlink Vicious and Divine Cezanne mouse.
 
+config HID_STADIA_FF
+	tristate "Google Stadia force feedback"
+	select INPUT_FF_MEMLESS
+	help
+	Say Y here if you want to enable force feedback support for the Google
+	Stadia controller.
+
 config HID_STEAM
 	tristate "Steam Controller/Deck support"
 	select POWER_SUPPLY
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 5d37cacbde33..1d900fa55890 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -120,6 +120,7 @@ obj-$(CONFIG_HID_SIGMAMICRO)	+= hid-sigmamicro.o
 obj-$(CONFIG_HID_SMARTJOYPLUS)	+= hid-sjoy.o
 obj-$(CONFIG_HID_SONY)		+= hid-sony.o
 obj-$(CONFIG_HID_SPEEDLINK)	+= hid-speedlink.o
+obj-$(CONFIG_HID_STADIA_FF)	+= hid-stadiaff.o
 obj-$(CONFIG_HID_STEAM)		+= hid-steam.o
 obj-$(CONFIG_HID_STEELSERIES)	+= hid-steelseries.o
 obj-$(CONFIG_HID_SUNPLUS)	+= hid-sunplus.o
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 63545cd307e5..cffd4ac609a0 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -525,6 +525,7 @@
 #define USB_DEVICE_ID_GOOGLE_MOONBALL	0x5044
 #define USB_DEVICE_ID_GOOGLE_DON	0x5050
 #define USB_DEVICE_ID_GOOGLE_EEL	0x5057
+#define USB_DEVICE_ID_GOOGLE_STADIA	0x9400
 
 #define USB_VENDOR_ID_GOTOP		0x08f2
 #define USB_DEVICE_ID_SUPER_Q2		0x007f
diff --git a/drivers/hid/hid-stadiaff.c b/drivers/hid/hid-stadiaff.c
new file mode 100644
index 000000000000..f974b9e24d46
--- /dev/null
+++ b/drivers/hid/hid-stadiaff.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Stadia controller rumble support.
+ *
+ * Copyright 2023 Google LLC
+ */
+
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define STADIA_FF_REPORT_ID 5
+
+struct stadiaff_device {
+	struct hid_device *hid;
+	struct hid_report *report;
+	struct work_struct work;
+};
+
+static void stadiaff_work(struct work_struct *work)
+{
+	struct stadiaff_device *stadiaff =
+		container_of(work, struct stadiaff_device, work);
+
+	hid_hw_request(stadiaff->hid, stadiaff->report, HID_REQ_SET_REPORT);
+}
+
+static int stadiaff_play(struct input_dev *dev, void *data,
+			 struct ff_effect *effect)
+{
+	struct hid_device *hid = input_get_drvdata(dev);
+	struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
+	struct hid_field *rumble_field = stadiaff->report->field[0];
+
+	rumble_field->value[0] = effect->u.rumble.strong_magnitude;
+	rumble_field->value[1] = effect->u.rumble.weak_magnitude;
+
+	schedule_work(&stadiaff->work);
+
+	return 0;
+}
+
+static int stadiaff_init(struct hid_device *hid)
+{
+	struct stadiaff_device *stadiaff;
+	struct hid_report *report;
+	struct hid_input *hidinput;
+	struct input_dev *dev;
+	int error;
+
+	if (list_empty(&hid->inputs)) {
+		hid_err(hid, "no inputs found\n");
+		return -ENODEV;
+	}
+	hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+	dev = hidinput->input;
+
+	report = hid_validate_values(hid, HID_OUTPUT_REPORT,
+				     STADIA_FF_REPORT_ID, 0, 2);
+	if (!report)
+		return -ENODEV;
+
+	stadiaff = devm_kzalloc(&hid->dev, sizeof(struct stadiaff_device),
+				GFP_KERNEL);
+	if (!stadiaff)
+		return -ENOMEM;
+
+	hid_set_drvdata(hid, stadiaff);
+
+	input_set_capability(dev, EV_FF, FF_RUMBLE);
+
+	error = input_ff_create_memless(dev, NULL, stadiaff_play);
+	if (error)
+		return error;
+
+	stadiaff->hid = hid;
+	stadiaff->report = report;
+	INIT_WORK(&stadiaff->work, stadiaff_work);
+
+	hid_info(hid, "Force Feedback for Google Stadia controller\n");
+
+	return 0;
+}
+
+static int stadia_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+	int ret;
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "parse failed\n");
+		return ret;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+	if (ret) {
+		hid_err(hdev, "hw start failed\n");
+		return ret;
+	}
+
+	stadiaff_init(hdev);
+
+	return 0;
+}
+
+static void stadia_remove(struct hid_device *hid)
+{
+	struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
+
+	cancel_work_sync(&stadiaff->work);
+	hid_hw_stop(hid);
+}
+
+static const struct hid_device_id stadia_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STADIA) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STADIA) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, stadia_devices);
+
+static struct hid_driver stadia_driver = {
+	.name = "stadia",
+	.id_table = stadia_devices,
+	.probe = stadia_probe,
+	.remove = stadia_remove,
+};
+module_hid_driver(stadia_driver);
+
+MODULE_LICENSE("GPL");
-- 
2.40.0.348.gf938b09366-goog


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

* Re: [PATCH] HID: hid-stadiaff: add support for Stadia force feedback
  2023-04-03 10:33 [PATCH] HID: hid-stadiaff: add support for Stadia force feedback Fabio Baltieri
@ 2023-04-13 16:00 ` Benjamin Tissoires
  2023-05-05  9:27   ` Fabio Baltieri
  0 siblings, 1 reply; 3+ messages in thread
From: Benjamin Tissoires @ 2023-04-13 16:00 UTC (permalink / raw)
  To: Fabio Baltieri; +Cc: Jiri Kosina, linux-input, linux-kernel

Hi,

On Apr 03 2023, Fabio Baltieri wrote:
> Add a hid-stadiaff module to support rumble based force feedback on the
> Google Stadia controller. This works using the HID output endpoint
> exposed on both the USB and BLE interface.
> 
> Signed-off-by: Fabio Baltieri <fabiobaltieri@chromium.org>
> ---
> 
> Hi, this adds rumble support to the stadia controller using the input
> interface. Up to now this has only been implemented at application level
> using hidraw:
> 
> https://source.chromium.org/chromium/chromium/src/+/main:device/gamepad/hid_haptic_gamepad.cc
> 
> Tested with fftest, works both over USB and BLE.
> 
>  drivers/hid/Kconfig        |   7 ++
>  drivers/hid/Makefile       |   1 +
>  drivers/hid/hid-ids.h      |   1 +
>  drivers/hid/hid-stadiaff.c | 132 +++++++++++++++++++++++++++++++++++++

Mind renaming this hid-google-stadiaff.c?
It's hard to know that stadia is from Google otherwise.

>  4 files changed, 141 insertions(+)
>  create mode 100644 drivers/hid/hid-stadiaff.c
> 
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 82f64fb31fda..934f73e9b800 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -1031,6 +1031,13 @@ config HID_SPEEDLINK
>  	help
>  	Support for Speedlink Vicious and Divine Cezanne mouse.
>  
> +config HID_STADIA_FF
> +	tristate "Google Stadia force feedback"
> +	select INPUT_FF_MEMLESS
> +	help
> +	Say Y here if you want to enable force feedback support for the Google
> +	Stadia controller.
> +
>  config HID_STEAM
>  	tristate "Steam Controller/Deck support"
>  	select POWER_SUPPLY
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index 5d37cacbde33..1d900fa55890 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -120,6 +120,7 @@ obj-$(CONFIG_HID_SIGMAMICRO)	+= hid-sigmamicro.o
>  obj-$(CONFIG_HID_SMARTJOYPLUS)	+= hid-sjoy.o
>  obj-$(CONFIG_HID_SONY)		+= hid-sony.o
>  obj-$(CONFIG_HID_SPEEDLINK)	+= hid-speedlink.o
> +obj-$(CONFIG_HID_STADIA_FF)	+= hid-stadiaff.o
>  obj-$(CONFIG_HID_STEAM)		+= hid-steam.o
>  obj-$(CONFIG_HID_STEELSERIES)	+= hid-steelseries.o
>  obj-$(CONFIG_HID_SUNPLUS)	+= hid-sunplus.o
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 63545cd307e5..cffd4ac609a0 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -525,6 +525,7 @@
>  #define USB_DEVICE_ID_GOOGLE_MOONBALL	0x5044
>  #define USB_DEVICE_ID_GOOGLE_DON	0x5050
>  #define USB_DEVICE_ID_GOOGLE_EEL	0x5057
> +#define USB_DEVICE_ID_GOOGLE_STADIA	0x9400
>  
>  #define USB_VENDOR_ID_GOTOP		0x08f2
>  #define USB_DEVICE_ID_SUPER_Q2		0x007f
> diff --git a/drivers/hid/hid-stadiaff.c b/drivers/hid/hid-stadiaff.c
> new file mode 100644
> index 000000000000..f974b9e24d46
> --- /dev/null
> +++ b/drivers/hid/hid-stadiaff.c
> @@ -0,0 +1,132 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Stadia controller rumble support.
> + *
> + * Copyright 2023 Google LLC
> + */
> +
> +#include <linux/hid.h>
> +#include <linux/input.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +
> +#include "hid-ids.h"
> +
> +#define STADIA_FF_REPORT_ID 5
> +
> +struct stadiaff_device {
> +	struct hid_device *hid;
> +	struct hid_report *report;
> +	struct work_struct work;
> +};
> +
> +static void stadiaff_work(struct work_struct *work)
> +{
> +	struct stadiaff_device *stadiaff =
> +		container_of(work, struct stadiaff_device, work);
> +
> +	hid_hw_request(stadiaff->hid, stadiaff->report, HID_REQ_SET_REPORT);
> +}
> +
> +static int stadiaff_play(struct input_dev *dev, void *data,
> +			 struct ff_effect *effect)
> +{
> +	struct hid_device *hid = input_get_drvdata(dev);
> +	struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
> +	struct hid_field *rumble_field = stadiaff->report->field[0];
> +
> +	rumble_field->value[0] = effect->u.rumble.strong_magnitude;
> +	rumble_field->value[1] = effect->u.rumble.weak_magnitude;
> +
> +	schedule_work(&stadiaff->work);

It seems weird that you don't have any locking in place here.
What if you are sending a report (in stadiaff_work) while having your
_play() function called at the same time?

> +
> +	return 0;
> +}
> +
> +static int stadiaff_init(struct hid_device *hid)
> +{
> +	struct stadiaff_device *stadiaff;
> +	struct hid_report *report;
> +	struct hid_input *hidinput;
> +	struct input_dev *dev;
> +	int error;
> +
> +	if (list_empty(&hid->inputs)) {
> +		hid_err(hid, "no inputs found\n");
> +		return -ENODEV;
> +	}
> +	hidinput = list_entry(hid->inputs.next, struct hid_input, list);
> +	dev = hidinput->input;
> +
> +	report = hid_validate_values(hid, HID_OUTPUT_REPORT,
> +				     STADIA_FF_REPORT_ID, 0, 2);
> +	if (!report)
> +		return -ENODEV;
> +
> +	stadiaff = devm_kzalloc(&hid->dev, sizeof(struct stadiaff_device),
> +				GFP_KERNEL);
> +	if (!stadiaff)
> +		return -ENOMEM;
> +
> +	hid_set_drvdata(hid, stadiaff);
> +
> +	input_set_capability(dev, EV_FF, FF_RUMBLE);
> +
> +	error = input_ff_create_memless(dev, NULL, stadiaff_play);
> +	if (error)
> +		return error;
> +
> +	stadiaff->hid = hid;
> +	stadiaff->report = report;
> +	INIT_WORK(&stadiaff->work, stadiaff_work);
> +
> +	hid_info(hid, "Force Feedback for Google Stadia controller\n");
> +
> +	return 0;
> +}
> +
> +static int stadia_probe(struct hid_device *hdev, const struct hid_device_id *id)
> +{
> +	int ret;
> +
> +	ret = hid_parse(hdev);
> +	if (ret) {
> +		hid_err(hdev, "parse failed\n");
> +		return ret;
> +	}
> +
> +	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
> +	if (ret) {
> +		hid_err(hdev, "hw start failed\n");
> +		return ret;
> +	}
> +
> +	stadiaff_init(hdev);
> +
> +	return 0;
> +}
> +
> +static void stadia_remove(struct hid_device *hid)
> +{
> +	struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
> +
> +	cancel_work_sync(&stadiaff->work);

What if you have a ff play event scheduled right here? Don't you need
some way of forcing the work to not be scheduled once again?

> +	hid_hw_stop(hid);
> +}
> +
> +static const struct hid_device_id stadia_devices[] = {
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STADIA) },
> +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STADIA) },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(hid, stadia_devices);
> +
> +static struct hid_driver stadia_driver = {
> +	.name = "stadia",
> +	.id_table = stadia_devices,
> +	.probe = stadia_probe,
> +	.remove = stadia_remove,
> +};
> +module_hid_driver(stadia_driver);
> +
> +MODULE_LICENSE("GPL");
> -- 
> 2.40.0.348.gf938b09366-goog
> 

Cheers,
Benjamin


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

* Re: [PATCH] HID: hid-stadiaff: add support for Stadia force feedback
  2023-04-13 16:00 ` Benjamin Tissoires
@ 2023-05-05  9:27   ` Fabio Baltieri
  0 siblings, 0 replies; 3+ messages in thread
From: Fabio Baltieri @ 2023-05-05  9:27 UTC (permalink / raw)
  To: Benjamin Tissoires; +Cc: Jiri Kosina, linux-input, linux-kernel

Hi,

On Thu, Apr 13, 2023 at 06:00:33PM +0200, Benjamin Tissoires wrote:
> >  drivers/hid/Kconfig        |   7 ++
> >  drivers/hid/Makefile       |   1 +
> >  drivers/hid/hid-ids.h      |   1 +
> >  drivers/hid/hid-stadiaff.c | 132 +++++++++++++++++++++++++++++++++++++
> 
> Mind renaming this hid-google-stadiaff.c?
> It's hard to know that stadia is from Google otherwise.

Sure thing.

> > +static int stadiaff_play(struct input_dev *dev, void *data,
> > +			 struct ff_effect *effect)
> > +{
> > +	struct hid_device *hid = input_get_drvdata(dev);
> > +	struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
> > +	struct hid_field *rumble_field = stadiaff->report->field[0];
> > +
> > +	rumble_field->value[0] = effect->u.rumble.strong_magnitude;
> > +	rumble_field->value[1] = effect->u.rumble.weak_magnitude;
> > +
> > +	schedule_work(&stadiaff->work);
> 
> It seems weird that you don't have any locking in place here.
> What if you are sending a report (in stadiaff_work) while having your
> _play() function called at the same time?

Yeah you are right, I somehow missed the whole locking story, sending a
v2 with that added.

> > +static void stadia_remove(struct hid_device *hid)
> > +{
> > +	struct stadiaff_device *stadiaff = hid_get_drvdata(hid);
> > +
> > +	cancel_work_sync(&stadiaff->work);
> 
> What if you have a ff play event scheduled right here? Don't you need
> some way of forcing the work to not be scheduled once again?

Good point, adding that as well for v2, took the pattern from other
existing drivers.

Thanks for the review,
Fabio

-- 
Fabio Baltieri

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

end of thread, other threads:[~2023-05-05  9:28 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-03 10:33 [PATCH] HID: hid-stadiaff: add support for Stadia force feedback Fabio Baltieri
2023-04-13 16:00 ` Benjamin Tissoires
2023-05-05  9:27   ` Fabio Baltieri

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).