All of lore.kernel.org
 help / color / mirror / Atom feed
From: "François-Xavier Carton" <fx.carton91@gmail.com>
To: linux-input@vger.kernel.org, Jiri Kosina <jikos@kernel.org>,
	Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: "François-Xavier Carton" <fx.carton91@gmail.com>,
	"Ethan Lee" <flibitijibibo@gmail.com>,
	"Bastien Nocera" <hadess@hadess.net>
Subject: [PATCH v3 2/4] HID: gamecube-adapter: add rumble support
Date: Wed, 14 Oct 2020 03:30:21 +0200	[thread overview]
Message-ID: <20201014013023.23078-3-fx.carton91@gmail.com> (raw)
In-Reply-To: <20201014013023.23078-1-fx.carton91@gmail.com>

Add rumble support for the hid-gamecube-adapter driver. Rumble is
reported with a single output report for all four controllers.

Signed-off-by: François-Xavier Carton <fx.carton91@gmail.com>
---
 drivers/hid/Kconfig                |  8 ++++
 drivers/hid/hid-gamecube-adapter.c | 77 +++++++++++++++++++++++++++++-
 2 files changed, 84 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index c2bb95bda44d..04cbaa2625db 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -360,6 +360,14 @@ config HID_GAMECUBE_ADAPTER
 	  To compile this driver as a module, choose M here: the
 	  module will be called hid-gamecube-adapter.
 
+config HID_GAMECUBE_ADAPTER_FF
+	bool "Nintendo Gamecube Controller Adapter force feedback"
+	depends on HID_GAMECUBE_ADAPTER
+	select INPUT_FF_MEMLESS
+	help
+	  Say Y here if you want to enable force feedback support for Nintendo
+	  Gamecube Controller Adapters.
+
 config HID_GEMBIRD
 	tristate "Gembird Joypad"
 	depends on HID
diff --git a/drivers/hid/hid-gamecube-adapter.c b/drivers/hid/hid-gamecube-adapter.c
index 7193db1a0782..d0bf09ba2762 100644
--- a/drivers/hid/hid-gamecube-adapter.c
+++ b/drivers/hid/hid-gamecube-adapter.c
@@ -20,7 +20,8 @@
 #include "usbhid/usbhid.h"
 
 enum gamecube_output {
-	GC_CMD_INIT = 0x13
+	GC_CMD_INIT = 0x13,
+	GC_CMD_RUMBLE = 0x11
 };
 
 enum gamecube_input {
@@ -55,13 +56,17 @@ struct gamecube_ctrl {
 	struct input_dev __rcu *input;
 	struct gamecube_adpt *adpt;
 	enum gamecube_ctrl_flags flags;
+	u8 rumble;
 	struct work_struct work_connect;
 	spinlock_t flags_lock;
+	spinlock_t rumble_lock;
 };
 
 struct gamecube_adpt {
 	struct gamecube_ctrl ctrls[4];
 	struct hid_device *hdev;
+	struct work_struct work_rumble;
+	u8 rumble;
 };
 
 static int gamecube_hid_send(struct hid_device *hdev, const u8 *data, size_t n)
@@ -84,6 +89,61 @@ static int gamecube_send_cmd_init(struct hid_device *hdev)
 	return gamecube_hid_send(hdev, initcmd, sizeof(initcmd));
 }
 
+#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF
+static int gamecube_send_cmd_rumble(struct gamecube_adpt *adpt)
+{
+	struct gamecube_ctrl *ctrls = adpt->ctrls;
+	u8 cmd[5] = {GC_CMD_RUMBLE};
+	unsigned long flags;
+	unsigned int i;
+	int rumble_ok;
+	u8 rumble = 0;
+
+	for (i = 0; i < 4; i++) {
+		spin_lock_irqsave(&ctrls[i].flags_lock, flags);
+		rumble_ok = (ctrls[i].flags & GC_TYPES)
+			&& (ctrls[i].flags & GC_FLAG_EXTRAPOWER);
+		spin_unlock_irqrestore(&ctrls[i].flags_lock, flags);
+		if (!rumble_ok)
+			continue;
+		spin_lock_irqsave(&ctrls[i].rumble_lock, flags);
+		cmd[i + 1] = ctrls[i].rumble;
+		rumble |= (ctrls[i].rumble & 1U) << i;
+		spin_unlock_irqrestore(&ctrls[i].rumble_lock, flags);
+	}
+	if (rumble == adpt->rumble)
+		return 0;
+	adpt->rumble = rumble;
+	return gamecube_hid_send(adpt->hdev, cmd, sizeof(cmd));
+}
+
+static void gamecube_rumble_worker(struct work_struct *work)
+{
+	struct gamecube_adpt *adpt = container_of(work, struct gamecube_adpt,
+						  work_rumble);
+
+	gamecube_send_cmd_rumble(adpt);
+}
+
+static int gamecube_rumble_play(struct input_dev *dev, void *data,
+							 struct ff_effect *eff)
+{
+	struct gamecube_ctrl *ctrl = input_get_drvdata(dev);
+	struct gamecube_adpt *adpt = ctrl->adpt;
+	unsigned long flags;
+
+	if (eff->type != FF_RUMBLE)
+		return 0;
+
+	spin_lock_irqsave(&ctrl->rumble_lock, flags);
+	ctrl->rumble = (eff->u.rumble.strong_magnitude
+			|| eff->u.rumble.weak_magnitude);
+	spin_unlock_irqrestore(&ctrl->rumble_lock, flags);
+	schedule_work(&adpt->work_rumble);
+	return 0;
+}
+#endif
+
 static const unsigned int gamecube_buttons[] = {
 	BTN_START, BTN_TR2, BTN_TR, BTN_TL,
 	BTN_SOUTH, BTN_WEST, BTN_EAST, BTN_NORTH,
@@ -136,6 +196,13 @@ static int gamecube_ctrl_create(struct gamecube_ctrl *ctrl, u8 type)
 		input_set_capability(input, EV_KEY, gamecube_buttons[i]);
 	for (i = 0; i < ARRAY_SIZE(gamecube_axes); i++)
 		input_set_abs_params(input, gamecube_axes[i], 0, 255, 0, 0);
+#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF
+	if (type == GC_TYPE_NORMAL) {
+		input_set_capability(input, EV_FF, FF_RUMBLE);
+		if (input_ff_create_memless(input, NULL, gamecube_rumble_play))
+			hid_warn(hdev, "failed to create ff memless\n");
+	}
+#endif
 
 	ret = input_register_device(input);
 	if (ret)
@@ -278,7 +345,12 @@ static struct gamecube_adpt *gamecube_adpt_create(struct hid_device *hdev)
 		ctrl->adpt = adpt;
 		INIT_WORK(&ctrl->work_connect, gamecube_work_connect_cb);
 		spin_lock_init(&ctrl->flags_lock);
+		spin_lock_init(&ctrl->rumble_lock);
 	}
+#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF
+	INIT_WORK(&adpt->work_rumble, gamecube_rumble_worker);
+	adpt->rumble = 0;
+#endif
 
 	return adpt;
 }
@@ -289,6 +361,9 @@ static void gamecube_adpt_destroy(struct gamecube_adpt *adpt)
 
 	for (i = 0; i < 4; i++)
 		gamecube_ctrl_destroy(adpt->ctrls + i);
+#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF
+	cancel_work_sync(&adpt->work_rumble);
+#endif
 	hid_hw_close(adpt->hdev);
 	hid_hw_stop(adpt->hdev);
 	kfree(adpt);
-- 
2.26.2


  parent reply	other threads:[~2020-10-14  9:20 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-14  1:30 [PATCH v3 0/4] HID: gamecube-adapter François-Xavier Carton
2020-10-14  1:30 ` [PATCH v3 1/4] HID: gamecube-adapter: add nintendo gamecube adapter François-Xavier Carton
2020-10-14  1:30 ` François-Xavier Carton [this message]
2020-10-14  1:30 ` [PATCH v3 3/4] HID: gamecube-adapter: add auto calibration François-Xavier Carton
2020-10-14  1:30 ` [PATCH v3 4/4] HID: gamecube-adapter: restore input after suspend François-Xavier Carton

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=20201014013023.23078-3-fx.carton91@gmail.com \
    --to=fx.carton91@gmail.com \
    --cc=benjamin.tissoires@redhat.com \
    --cc=flibitijibibo@gmail.com \
    --cc=hadess@hadess.net \
    --cc=jikos@kernel.org \
    --cc=linux-input@vger.kernel.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.