All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter
@ 2020-05-06  0:47 François-Xavier Carton
  2020-05-06  0:48 ` [PATCH 2/3] HID: gamecube-adapter: add rumble support François-Xavier Carton
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: François-Xavier Carton @ 2020-05-06  0:47 UTC (permalink / raw)
  To: linux-input; +Cc: François-Xavier Carton

The hid-gamecube-adapter driver supports Nintendo Gamecube Controller
Adapters.  They are USB devices on which up to four Nintendo Gamecube
Controllers can be plugged. The driver create independent input devices
as controllers are connected.

Signed-off-by: François-Xavier Carton <fx.carton91@gmail.com>
---
 MAINTAINERS                        |   6 +
 drivers/hid/Kconfig                |  10 +
 drivers/hid/Makefile               |   1 +
 drivers/hid/hid-gamecube-adapter.c | 386 +++++++++++++++++++++++++++++
 drivers/hid/hid-ids.h              |   1 +
 5 files changed, 404 insertions(+)
 create mode 100644 drivers/hid/hid-gamecube-adapter.c

diff --git a/MAINTAINERS b/MAINTAINERS
index d5b1878f2815..585ddcf3a6dd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7025,6 +7025,12 @@ F:	scripts/gcc-plugin.sh
 F:	scripts/Makefile.gcc-plugins
 F:	Documentation/kbuild/gcc-plugins.rst
 
+GAMECUBE ADAPTER HID DRIVER
+M: François-Xavier Carton <fx.carton91@gmail.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/hid/hid-gamecube-adapter*
+
 GASKET DRIVER FRAMEWORK
 M:	Rob Springer <rspringer@google.com>
 M:	Todd Poynor <toddpoynor@google.com>
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7c89edbd6c5a..d49e261a74f6 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -350,6 +350,16 @@ config HID_EZKEY
 	---help---
 	Support for Ezkey BTC 8193 keyboard.
 
+config HID_GAMECUBE_ADAPTER
+	tristate "Nintendo Gamecube Controller Adapter support"
+	depends on HID
+	depends on USB_HID
+	---help---
+	Support for the Nintendo Gamecube Controller Adapter.
+
+	To compile this driver as a module, choose M here: the
+	module will be called hid-gamecube-adapter.
+
 config HID_GEMBIRD
 	tristate "Gembird Joypad"
 	depends on HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index d8ea4b8c95af..9cddc4d48db8 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_HID_ELAN)		+= hid-elan.o
 obj-$(CONFIG_HID_ELECOM)	+= hid-elecom.o
 obj-$(CONFIG_HID_ELO)		+= hid-elo.o
 obj-$(CONFIG_HID_EZKEY)		+= hid-ezkey.o
+obj-$(CONFIG_HID_GAMECUBE_ADAPTER)	+= hid-gamecube-adapter.o
 obj-$(CONFIG_HID_GEMBIRD)	+= hid-gembird.o
 obj-$(CONFIG_HID_GFRM)		+= hid-gfrm.o
 obj-$(CONFIG_HID_GLORIOUS)  += hid-glorious.o
diff --git a/drivers/hid/hid-gamecube-adapter.c b/drivers/hid/hid-gamecube-adapter.c
new file mode 100644
index 000000000000..0537ece0979a
--- /dev/null
+++ b/drivers/hid/hid-gamecube-adapter.c
@@ -0,0 +1,386 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * HID driver for Nintendo Gamecube Controller Adapters
+ *
+ * Copyright (c) 2020 François-Xavier Carton <fx.carton91@gmail.com>
+ *
+ * This driver is based on:
+ *   https://github.com/ToadKing/wii-u-gc-adapter
+ *   drivers/hid/hid-wiimote-core.c
+ *   drivers/hid/hid-steam.c
+ *
+ */
+
+#include "hid-ids.h"
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/rcupdate.h>
+#include <linux/usb.h>
+#include "usbhid/usbhid.h"
+
+enum gamecube_output {
+	GC_CMD_INIT = 0x13
+};
+
+enum gamecube_input {
+	GC_INPUT_REPORT = 0x21
+};
+
+#define GC_INPUT_REPORT_SIZE 37
+
+enum gamecube_ctrl_flags {
+	GC_FLAG_EXTRAPOWER = BIT(2),
+	GC_TYPE_NORMAL = BIT(4),
+	GC_TYPE_WAVEBIRD = BIT(5),
+	GC_TYPES = GC_TYPE_NORMAL | GC_TYPE_WAVEBIRD
+};
+
+enum gamecube_btn {
+	GC_BTN_START = BIT(0),
+	GC_BTN_Z = BIT(1),
+	GC_BTN_R = BIT(2),
+	GC_BTN_L = BIT(3),
+	GC_BTN_A = BIT(8),
+	GC_BTN_B = BIT(9),
+	GC_BTN_X = BIT(10),
+	GC_BTN_Y = BIT(11),
+	GC_BTN_DPAD_LEFT = BIT(12),
+	GC_BTN_DPAD_RIGHT = BIT(13),
+	GC_BTN_DPAD_DOWN = BIT(14),
+	GC_BTN_DPAD_UP = BIT(15),
+};
+
+struct gamecube_ctrl {
+	struct input_dev __rcu *input;
+	enum gamecube_ctrl_flags flags;
+	struct gamecube_adapter *adpt;
+	struct work_struct work_connect;
+	spinlock_t flags_lock;
+};
+
+struct gamecube_adapter {
+	struct gamecube_ctrl ctrls[4];
+	struct hid_device *hdev;
+};
+
+static int gamecube_hid_send(struct hid_device *hdev, const u8 *data, size_t n)
+{
+	u8 *buf;
+	int ret;
+
+	buf = kmemdup(data, n, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+	ret = hid_hw_output_report(hdev, buf, n);
+	kfree(buf);
+	return ret;
+}
+
+static int gamecube_send_cmd_init(struct hid_device *hdev)
+{
+	const u8 initcmd[] = {GC_CMD_INIT};
+	return gamecube_hid_send(hdev, initcmd, sizeof(initcmd));
+}
+
+static const unsigned int gamecube_buttons[] = {
+	BTN_START, BTN_TR2, BTN_TR, BTN_TL,
+	BTN_SOUTH, BTN_WEST, BTN_EAST, BTN_NORTH,
+	BTN_DPAD_LEFT, BTN_DPAD_RIGHT, BTN_DPAD_DOWN, BTN_DPAD_UP
+};
+
+static const unsigned int gamecube_axes[] = {
+	ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_Z, ABS_RZ
+};
+
+static const char* gamecube_ctrl_name(enum gamecube_ctrl_flags flags)
+{
+	switch (flags & GC_TYPES) {
+	case GC_TYPE_NORMAL:
+		return "Standard Gamecube Controller";
+	case GC_TYPE_WAVEBIRD:
+		return "Wavebird Gamecube Controller";
+	}
+	return NULL;
+}
+
+static int gamecube_ctrl_create(struct gamecube_ctrl *ctrl)
+{
+	struct input_dev *input;
+	struct hid_device *hdev = ctrl->adpt->hdev;
+	const char *name;
+	unsigned int i;
+	int ret;
+
+	name = gamecube_ctrl_name(ctrl->flags);
+	if (!name) {
+		unsigned int num = ctrl - ctrl->adpt->ctrls;
+		hid_warn(hdev, "port %u: unknown controller plugged in\n", num + 1);
+		return -EINVAL;
+	}
+
+	input = input_allocate_device();
+	if (!input)
+		return -ENOMEM;
+
+	input_set_drvdata(input, ctrl);
+	input->id.bustype = hdev->bus;
+	input->id.vendor = hdev->vendor;
+	input->id.product = hdev->product;
+	input->id.version = hdev->version;
+	input->name = name;
+
+	for (i = 0; i < ARRAY_SIZE(gamecube_buttons); i++)
+		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);
+
+	ret = input_register_device(input);
+	if (ret)
+		goto err_free_device;
+
+	rcu_assign_pointer(ctrl->input, input);
+	return 0;
+
+err_free_device:
+	input_free_device(input);
+	return ret;
+}
+
+static void gamecube_ctrl_destroy(struct gamecube_ctrl *ctrl)
+{
+	struct input_dev *input;
+	rcu_read_lock();
+	input = rcu_dereference(ctrl->input);
+	rcu_read_unlock();
+	if (!input)
+		return;
+	RCU_INIT_POINTER(ctrl->input, NULL);
+	synchronize_rcu();
+	input_unregister_device(input);
+}
+
+static void gamecube_work_connect_cb(struct work_struct *work)
+{
+	struct gamecube_ctrl *ctrl = container_of(work, struct gamecube_ctrl, work_connect);
+	struct input_dev *input;
+	unsigned long irq_flags;
+	unsigned int num = ctrl - ctrl->adpt->ctrls;
+	u8 type;
+
+	spin_lock_irqsave(&ctrl->flags_lock, irq_flags);
+	type = ctrl->flags & GC_TYPES;
+	spin_unlock_irqrestore(&ctrl->flags_lock, irq_flags);
+
+	rcu_read_lock();
+	input = rcu_dereference(ctrl->input);
+	rcu_read_unlock();
+
+	if (type && input) {
+		hid_info(ctrl->adpt->hdev, "port %u: already connected\n", num + 1);
+	} else if (type) {
+		hid_info(ctrl->adpt->hdev, "port %u: controller plugged in\n", num + 1);
+		gamecube_ctrl_create(ctrl);
+	} else if (input) {
+		hid_info(ctrl->adpt->hdev, "port %u: controller unplugged\n", num + 1);
+		gamecube_ctrl_destroy(ctrl);
+	}
+}
+
+static void gamecube_ctrl_handle_report(struct gamecube_ctrl *ctrl, u8 *data)
+{
+	struct input_dev *dev;
+	u16 btns = data[1] << 8 | data[2];
+	u8 old_flags, new_flags = data[0];
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&ctrl->flags_lock, irq_flags);
+	old_flags = ctrl->flags;
+	ctrl->flags = new_flags;
+	spin_unlock_irqrestore(&ctrl->flags_lock, irq_flags);
+
+	if ((new_flags & GC_TYPES) != (old_flags & GC_TYPES)) {
+		schedule_work(&ctrl->work_connect);
+		return;
+	}
+	if (!(new_flags & GC_TYPES))
+		return;
+
+	rcu_read_lock();
+	dev = rcu_dereference(ctrl->input);
+	if (!dev)
+		goto unlock;
+
+	input_report_key(dev, BTN_START, btns & GC_BTN_START);
+	input_report_key(dev, BTN_TR2, btns & GC_BTN_Z);
+	input_report_key(dev, BTN_TR, btns & GC_BTN_R);
+	input_report_key(dev, BTN_TL, btns & GC_BTN_L);
+	input_report_key(dev, BTN_SOUTH, btns & GC_BTN_A);
+	input_report_key(dev, BTN_WEST, btns & GC_BTN_B);
+	input_report_key(dev, BTN_EAST, btns & GC_BTN_X);
+	input_report_key(dev, BTN_NORTH, btns & GC_BTN_Y);
+	input_report_key(dev, BTN_DPAD_LEFT, btns & GC_BTN_DPAD_LEFT);
+	input_report_key(dev, BTN_DPAD_RIGHT, btns & GC_BTN_DPAD_RIGHT);
+	input_report_key(dev, BTN_DPAD_DOWN, btns & GC_BTN_DPAD_DOWN);
+	input_report_key(dev, BTN_DPAD_UP, btns & GC_BTN_DPAD_UP);
+	input_report_abs(dev, ABS_X, data[3]);
+	input_report_abs(dev, ABS_Y, 255 - data[4]);
+	input_report_abs(dev, ABS_RX, data[5]);
+	input_report_abs(dev, ABS_RY, 255 - data[6]);
+	input_report_abs(dev, ABS_Z, data[7]);
+	input_report_abs(dev, ABS_RZ, data[8]);
+	input_sync(dev);
+
+unlock:
+	rcu_read_unlock();
+}
+
+static int gamecube_hid_event(struct hid_device *hdev,
+				  struct hid_report *report, u8 *raw_data, int size)
+{
+	struct gamecube_adapter *adpt = hid_get_drvdata(hdev);
+	unsigned int i;
+
+	if (size < 1)
+		return -EINVAL;
+	if (size == GC_INPUT_REPORT_SIZE && raw_data[0] == GC_INPUT_REPORT) {
+		for (i = 0; i < 4; i++)
+			gamecube_ctrl_handle_report(adpt->ctrls + i, raw_data + 1 + 9 * i);
+	} else {
+		hid_warn(hdev, "unhandled event\n");
+	}
+
+	return 0;
+}
+
+static struct gamecube_adapter* gamecube_adpt_create(struct hid_device *hdev)
+{
+	struct gamecube_adapter *adpt;
+	unsigned int i;
+
+	adpt = kzalloc(sizeof(*adpt), GFP_KERNEL);
+	if (!adpt)
+		return NULL;
+
+	adpt->hdev = hdev;
+	hid_set_drvdata(hdev, adpt);
+
+	for (i = 0; i < 4; i++) {
+		adpt->ctrls[i].adpt = adpt;
+		INIT_WORK(&adpt->ctrls[i].work_connect, gamecube_work_connect_cb);
+		spin_lock_init(&adpt->ctrls[i].flags_lock);
+	}
+
+	return adpt;
+}
+
+static void gamecube_adpt_destroy(struct gamecube_adapter* adpt)
+{
+	unsigned int i;
+
+	for (i = 0; i < 4; i++) {
+		gamecube_ctrl_destroy(adpt->ctrls + i);
+	}
+	hid_hw_close(adpt->hdev);
+	hid_hw_stop(adpt->hdev);
+	kfree(adpt);
+}
+
+/* This is needed, as by default the URB buffer size is set to 38, which is
+ * one byte too long and will result in EOVERFLOW failures.
+ */
+static int gamecube_fixup_urb_in(struct gamecube_adapter *adpt)
+{
+	struct hid_device *hdev = adpt->hdev;
+	struct usbhid_device *usbhid;
+
+	if (!hid_is_using_ll_driver(hdev, &usb_hid_driver))
+		return -EINVAL;
+	usbhid = hdev->driver_data;
+	if (usbhid->urbin->transfer_buffer_length < GC_INPUT_REPORT_SIZE)
+		return -EINVAL;
+	usbhid->urbin->transfer_buffer_length = GC_INPUT_REPORT_SIZE;
+	return 0;
+}
+
+static int gamecube_hid_probe(struct hid_device *hdev,
+				const struct hid_device_id *id)
+{
+	struct gamecube_adapter *adpt;
+	int ret;
+
+	adpt = gamecube_adpt_create(hdev);
+	if (!adpt) {
+		hid_err(hdev, "Can't alloc device\n");
+		return -ENOMEM;
+	}
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "HID parse failed\n");
+		goto err;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+	if (ret) {
+		hid_err(hdev, "HW start failed\n");
+		goto err;
+	}
+
+	ret = hid_hw_open(hdev);
+	if (ret) {
+		hid_err(hdev, "cannot start hardware I/O\n");
+		goto err_stop;
+	}
+
+	ret = gamecube_fixup_urb_in(adpt);
+	if (ret) {
+		hid_err(hdev, "failed to fix input URB\n");
+		goto err_close;
+	}
+
+	ret = gamecube_send_cmd_init(hdev);
+	if (ret < 0) {
+		hid_err(hdev, "failed to send init command\n");
+		goto err_close;
+	}
+
+	hid_info(hdev, "new adapter registered\n");
+	return 0;
+
+err_close:
+	hid_hw_close(hdev);
+err_stop:
+	hid_hw_stop(hdev);
+err:
+	kfree(adpt);
+	return ret;
+}
+
+static void gamecube_hid_remove(struct hid_device *hdev)
+{
+	struct gamecube_adapter *adpt = hid_get_drvdata(hdev);
+
+	hid_info(hdev, "adapter removed\n");
+	gamecube_adpt_destroy(adpt);
+}
+
+static const struct hid_device_id gamecube_hid_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
+			 USB_DEVICE_ID_NINTENDO_GAMECUBE_ADAPTER) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, gamecube_hid_devices);
+
+static struct hid_driver gamecube_hid_driver = {
+	.name		= "gamecube-adapter",
+	.id_table	= gamecube_hid_devices,
+	.probe		= gamecube_hid_probe,
+	.remove		= gamecube_hid_remove,
+	.raw_event	= gamecube_hid_event,
+};
+module_hid_driver(gamecube_hid_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("François-Xavier Carton <fx.carton91@gmail.com>");
+MODULE_DESCRIPTION("Driver for Nintendo Gamecube Controller Adapters");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index b18b13147a6f..1ebea811ea3b 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -882,6 +882,7 @@
 #define USB_VENDOR_ID_NINTENDO		0x057e
 #define USB_DEVICE_ID_NINTENDO_WIIMOTE	0x0306
 #define USB_DEVICE_ID_NINTENDO_WIIMOTE2	0x0330
+#define USB_DEVICE_ID_NINTENDO_GAMECUBE_ADAPTER	0x0337
 
 #define USB_VENDOR_ID_NOVATEK		0x0603
 #define USB_DEVICE_ID_NOVATEK_PCT	0x0600
-- 
2.26.2


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

* [PATCH 2/3] HID: gamecube-adapter: add rumble support
  2020-05-06  0:47 [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter François-Xavier Carton
@ 2020-05-06  0:48 ` François-Xavier Carton
  2020-05-06  0:48 ` [PATCH 3/3] HID: gamecube-adapter: make axis limits parameters François-Xavier Carton
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 9+ messages in thread
From: François-Xavier Carton @ 2020-05-06  0:48 UTC (permalink / raw)
  To: linux-input; +Cc: François-Xavier Carton

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 | 61 +++++++++++++++++++++++++++++-
 2 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index d49e261a74f6..324981308783 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 0537ece0979a..b4022ff5b4b4 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 {
@@ -54,14 +55,17 @@ enum gamecube_btn {
 struct gamecube_ctrl {
 	struct input_dev __rcu *input;
 	enum gamecube_ctrl_flags flags;
+	u8 rumble;
 	struct gamecube_adapter *adpt;
 	struct work_struct work_connect;
 	spinlock_t flags_lock;
+	spinlock_t rumble_lock;
 };
 
 struct gamecube_adapter {
 	struct gamecube_ctrl ctrls[4];
 	struct hid_device *hdev;
+	struct work_struct work_rumble;
 };
 
 static int gamecube_hid_send(struct hid_device *hdev, const u8 *data, size_t n)
@@ -83,6 +87,49 @@ 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 hid_device *hdev, struct gamecube_ctrl *ctrls)
+{
+	u8 cmd[5] = {GC_CMD_RUMBLE};
+	unsigned long flags;
+	unsigned int i;
+
+	for (i = 0; i < 4; i++) {
+		if (!(ctrls[i].flags & GC_TYPES) || !(ctrls[i].flags & GC_FLAG_EXTRAPOWER))
+			continue;
+		spin_lock_irqsave(&ctrls[i].rumble_lock, flags);
+		cmd[i + 1] = ctrls[i].rumble;
+		spin_unlock_irqrestore(&ctrls[i].rumble_lock, flags);
+	}
+	return gamecube_hid_send(hdev, cmd, sizeof(cmd));
+}
+
+static void gamecube_rumble_worker(struct work_struct *work)
+{
+	struct gamecube_adapter *adpt = container_of(work, struct gamecube_adapter,
+						  work_rumble);
+
+	gamecube_send_cmd_rumble(adpt->hdev, adpt->ctrls);
+}
+
+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_adapter *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,
@@ -134,6 +181,11 @@ static int gamecube_ctrl_create(struct gamecube_ctrl *ctrl)
 		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
+	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)
@@ -269,7 +321,11 @@ static struct gamecube_adapter* gamecube_adpt_create(struct hid_device *hdev)
 		adpt->ctrls[i].adpt = adpt;
 		INIT_WORK(&adpt->ctrls[i].work_connect, gamecube_work_connect_cb);
 		spin_lock_init(&adpt->ctrls[i].flags_lock);
+		spin_lock_init(&adpt->ctrls[i].rumble_lock);
 	}
+#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF
+	INIT_WORK(&adpt->work_rumble, gamecube_rumble_worker);
+#endif
 
 	return adpt;
 }
@@ -281,6 +337,9 @@ static void gamecube_adpt_destroy(struct gamecube_adapter* 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


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

* [PATCH 3/3] HID: gamecube-adapter: make axis limits parameters
  2020-05-06  0:47 [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter François-Xavier Carton
  2020-05-06  0:48 ` [PATCH 2/3] HID: gamecube-adapter: add rumble support François-Xavier Carton
@ 2020-05-06  0:48 ` François-Xavier Carton
  2020-05-09 23:50 ` [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter François-Xavier Carton
  2020-05-10 12:45 ` Bastien Nocera
  3 siblings, 0 replies; 9+ messages in thread
From: François-Xavier Carton @ 2020-05-06  0:48 UTC (permalink / raw)
  To: linux-input; +Cc: François-Xavier Carton

The axes do not cover the full 0-255 range, with different limit values
for each axis. The limits are made module parameters so they can be
configured.

Signed-off-by: François-Xavier Carton <fx.carton91@gmail.com>
---
 drivers/hid/hid-gamecube-adapter.c | 36 +++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 6 deletions(-)

diff --git a/drivers/hid/hid-gamecube-adapter.c b/drivers/hid/hid-gamecube-adapter.c
index b4022ff5b4b4..6a2fb908de3c 100644
--- a/drivers/hid/hid-gamecube-adapter.c
+++ b/drivers/hid/hid-gamecube-adapter.c
@@ -19,6 +19,30 @@
 #include <linux/usb.h>
 #include "usbhid/usbhid.h"
 
+static unsigned int left_stick_min = 0;
+module_param(left_stick_min, uint, 0644);
+MODULE_PARM_DESC(left_stick_min, "Minimum value for left stick.");
+
+static unsigned int left_stick_max = 255;
+module_param(left_stick_max, uint, 0644);
+MODULE_PARM_DESC(left_stick_max, "Maximum value for left stick.");
+
+static unsigned int right_stick_min = 0;
+module_param(right_stick_min, uint, 0644);
+MODULE_PARM_DESC(right_stick_min, "Minimum value for right stick (C stick).");
+
+static unsigned int right_stick_max = 255;
+module_param(right_stick_max, uint, 0644);
+MODULE_PARM_DESC(right_stick_max, "Maximum value for right stick (C stick).");
+
+static unsigned int shoulder_min = 0;
+module_param(shoulder_min, uint, 0644);
+MODULE_PARM_DESC(shoulder_min, "Minimum value for shoulders.");
+
+static unsigned int shoulder_max = 255;
+module_param(shoulder_max, uint, 0644);
+MODULE_PARM_DESC(shoulder_max, "Maximum value for shoulders.");
+
 enum gamecube_output {
 	GC_CMD_INIT = 0x13,
 	GC_CMD_RUMBLE = 0x11
@@ -136,10 +160,6 @@ static const unsigned int gamecube_buttons[] = {
 	BTN_DPAD_LEFT, BTN_DPAD_RIGHT, BTN_DPAD_DOWN, BTN_DPAD_UP
 };
 
-static const unsigned int gamecube_axes[] = {
-	ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_Z, ABS_RZ
-};
-
 static const char* gamecube_ctrl_name(enum gamecube_ctrl_flags flags)
 {
 	switch (flags & GC_TYPES) {
@@ -179,8 +199,12 @@ static int gamecube_ctrl_create(struct gamecube_ctrl *ctrl)
 
 	for (i = 0; i < ARRAY_SIZE(gamecube_buttons); i++)
 		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);
+	input_set_abs_params(input, ABS_X, left_stick_min, left_stick_max, 0, 0);
+	input_set_abs_params(input, ABS_Y, left_stick_min, left_stick_max, 0, 0);
+	input_set_abs_params(input, ABS_RX, right_stick_min, right_stick_max, 0, 0);
+	input_set_abs_params(input, ABS_RY, right_stick_min, right_stick_max, 0, 0);
+	input_set_abs_params(input, ABS_Z, shoulder_min, shoulder_max, 0, 0);
+	input_set_abs_params(input, ABS_RZ, shoulder_min, shoulder_max, 0, 0);
 #ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF
 	input_set_capability(input, EV_FF, FF_RUMBLE);
 	if (input_ff_create_memless(input, NULL, gamecube_rumble_play))
-- 
2.26.2


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

* Re: [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter
  2020-05-06  0:47 [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter François-Xavier Carton
  2020-05-06  0:48 ` [PATCH 2/3] HID: gamecube-adapter: add rumble support François-Xavier Carton
  2020-05-06  0:48 ` [PATCH 3/3] HID: gamecube-adapter: make axis limits parameters François-Xavier Carton
@ 2020-05-09 23:50 ` François-Xavier Carton
  2020-05-10 12:45 ` Bastien Nocera
  3 siblings, 0 replies; 9+ messages in thread
From: François-Xavier Carton @ 2020-05-09 23:50 UTC (permalink / raw)
  To: linux-input; +Cc: François-Xavier Carton, Jiri Kosina, Benjamin Tissoires

Hi,

I realized I forgot the cc. Could you have a look at the driver I
submitted?

Thanks,
François-Xavier Carton


On Wed, May 06, 2020 at 02:47:59AM +0200, François-Xavier Carton wrote:
> The hid-gamecube-adapter driver supports Nintendo Gamecube Controller
> Adapters.  They are USB devices on which up to four Nintendo Gamecube
> Controllers can be plugged. The driver create independent input devices
> as controllers are connected.
> 
> Signed-off-by: François-Xavier Carton <fx.carton91@gmail.com>
> ---
>  MAINTAINERS                        |   6 +
>  drivers/hid/Kconfig                |  10 +
>  drivers/hid/Makefile               |   1 +
>  drivers/hid/hid-gamecube-adapter.c | 386 +++++++++++++++++++++++++++++
>  drivers/hid/hid-ids.h              |   1 +
>  5 files changed, 404 insertions(+)
>  create mode 100644 drivers/hid/hid-gamecube-adapter.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d5b1878f2815..585ddcf3a6dd 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7025,6 +7025,12 @@ F:	scripts/gcc-plugin.sh
>  F:	scripts/Makefile.gcc-plugins
>  F:	Documentation/kbuild/gcc-plugins.rst
>  
> +GAMECUBE ADAPTER HID DRIVER
> +M: François-Xavier Carton <fx.carton91@gmail.com>
> +L: linux-input@vger.kernel.org
> +S: Maintained
> +F: drivers/hid/hid-gamecube-adapter*
> +
>  GASKET DRIVER FRAMEWORK
>  M:	Rob Springer <rspringer@google.com>
>  M:	Todd Poynor <toddpoynor@google.com>
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 7c89edbd6c5a..d49e261a74f6 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -350,6 +350,16 @@ config HID_EZKEY
>  	---help---
>  	Support for Ezkey BTC 8193 keyboard.
>  
> +config HID_GAMECUBE_ADAPTER
> +	tristate "Nintendo Gamecube Controller Adapter support"
> +	depends on HID
> +	depends on USB_HID
> +	---help---
> +	Support for the Nintendo Gamecube Controller Adapter.
> +
> +	To compile this driver as a module, choose M here: the
> +	module will be called hid-gamecube-adapter.
> +
>  config HID_GEMBIRD
>  	tristate "Gembird Joypad"
>  	depends on HID
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index d8ea4b8c95af..9cddc4d48db8 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -46,6 +46,7 @@ obj-$(CONFIG_HID_ELAN)		+= hid-elan.o
>  obj-$(CONFIG_HID_ELECOM)	+= hid-elecom.o
>  obj-$(CONFIG_HID_ELO)		+= hid-elo.o
>  obj-$(CONFIG_HID_EZKEY)		+= hid-ezkey.o
> +obj-$(CONFIG_HID_GAMECUBE_ADAPTER)	+= hid-gamecube-adapter.o
>  obj-$(CONFIG_HID_GEMBIRD)	+= hid-gembird.o
>  obj-$(CONFIG_HID_GFRM)		+= hid-gfrm.o
>  obj-$(CONFIG_HID_GLORIOUS)  += hid-glorious.o
> diff --git a/drivers/hid/hid-gamecube-adapter.c b/drivers/hid/hid-gamecube-adapter.c
> new file mode 100644
> index 000000000000..0537ece0979a
> --- /dev/null
> +++ b/drivers/hid/hid-gamecube-adapter.c
> @@ -0,0 +1,386 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * HID driver for Nintendo Gamecube Controller Adapters
> + *
> + * Copyright (c) 2020 François-Xavier Carton <fx.carton91@gmail.com>
> + *
> + * This driver is based on:
> + *   https://github.com/ToadKing/wii-u-gc-adapter
> + *   drivers/hid/hid-wiimote-core.c
> + *   drivers/hid/hid-steam.c
> + *
> + */
> +
> +#include "hid-ids.h"
> +#include <linux/device.h>
> +#include <linux/hid.h>
> +#include <linux/module.h>
> +#include <linux/rcupdate.h>
> +#include <linux/usb.h>
> +#include "usbhid/usbhid.h"
> +
> +enum gamecube_output {
> +	GC_CMD_INIT = 0x13
> +};
> +
> +enum gamecube_input {
> +	GC_INPUT_REPORT = 0x21
> +};
> +
> +#define GC_INPUT_REPORT_SIZE 37
> +
> +enum gamecube_ctrl_flags {
> +	GC_FLAG_EXTRAPOWER = BIT(2),
> +	GC_TYPE_NORMAL = BIT(4),
> +	GC_TYPE_WAVEBIRD = BIT(5),
> +	GC_TYPES = GC_TYPE_NORMAL | GC_TYPE_WAVEBIRD
> +};
> +
> +enum gamecube_btn {
> +	GC_BTN_START = BIT(0),
> +	GC_BTN_Z = BIT(1),
> +	GC_BTN_R = BIT(2),
> +	GC_BTN_L = BIT(3),
> +	GC_BTN_A = BIT(8),
> +	GC_BTN_B = BIT(9),
> +	GC_BTN_X = BIT(10),
> +	GC_BTN_Y = BIT(11),
> +	GC_BTN_DPAD_LEFT = BIT(12),
> +	GC_BTN_DPAD_RIGHT = BIT(13),
> +	GC_BTN_DPAD_DOWN = BIT(14),
> +	GC_BTN_DPAD_UP = BIT(15),
> +};
> +
> +struct gamecube_ctrl {
> +	struct input_dev __rcu *input;
> +	enum gamecube_ctrl_flags flags;
> +	struct gamecube_adapter *adpt;
> +	struct work_struct work_connect;
> +	spinlock_t flags_lock;
> +};
> +
> +struct gamecube_adapter {
> +	struct gamecube_ctrl ctrls[4];
> +	struct hid_device *hdev;
> +};
> +
> +static int gamecube_hid_send(struct hid_device *hdev, const u8 *data, size_t n)
> +{
> +	u8 *buf;
> +	int ret;
> +
> +	buf = kmemdup(data, n, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +	ret = hid_hw_output_report(hdev, buf, n);
> +	kfree(buf);
> +	return ret;
> +}
> +
> +static int gamecube_send_cmd_init(struct hid_device *hdev)
> +{
> +	const u8 initcmd[] = {GC_CMD_INIT};
> +	return gamecube_hid_send(hdev, initcmd, sizeof(initcmd));
> +}
> +
> +static const unsigned int gamecube_buttons[] = {
> +	BTN_START, BTN_TR2, BTN_TR, BTN_TL,
> +	BTN_SOUTH, BTN_WEST, BTN_EAST, BTN_NORTH,
> +	BTN_DPAD_LEFT, BTN_DPAD_RIGHT, BTN_DPAD_DOWN, BTN_DPAD_UP
> +};
> +
> +static const unsigned int gamecube_axes[] = {
> +	ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_Z, ABS_RZ
> +};
> +
> +static const char* gamecube_ctrl_name(enum gamecube_ctrl_flags flags)
> +{
> +	switch (flags & GC_TYPES) {
> +	case GC_TYPE_NORMAL:
> +		return "Standard Gamecube Controller";
> +	case GC_TYPE_WAVEBIRD:
> +		return "Wavebird Gamecube Controller";
> +	}
> +	return NULL;
> +}
> +
> +static int gamecube_ctrl_create(struct gamecube_ctrl *ctrl)
> +{
> +	struct input_dev *input;
> +	struct hid_device *hdev = ctrl->adpt->hdev;
> +	const char *name;
> +	unsigned int i;
> +	int ret;
> +
> +	name = gamecube_ctrl_name(ctrl->flags);
> +	if (!name) {
> +		unsigned int num = ctrl - ctrl->adpt->ctrls;
> +		hid_warn(hdev, "port %u: unknown controller plugged in\n", num + 1);
> +		return -EINVAL;
> +	}
> +
> +	input = input_allocate_device();
> +	if (!input)
> +		return -ENOMEM;
> +
> +	input_set_drvdata(input, ctrl);
> +	input->id.bustype = hdev->bus;
> +	input->id.vendor = hdev->vendor;
> +	input->id.product = hdev->product;
> +	input->id.version = hdev->version;
> +	input->name = name;
> +
> +	for (i = 0; i < ARRAY_SIZE(gamecube_buttons); i++)
> +		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);
> +
> +	ret = input_register_device(input);
> +	if (ret)
> +		goto err_free_device;
> +
> +	rcu_assign_pointer(ctrl->input, input);
> +	return 0;
> +
> +err_free_device:
> +	input_free_device(input);
> +	return ret;
> +}
> +
> +static void gamecube_ctrl_destroy(struct gamecube_ctrl *ctrl)
> +{
> +	struct input_dev *input;
> +	rcu_read_lock();
> +	input = rcu_dereference(ctrl->input);
> +	rcu_read_unlock();
> +	if (!input)
> +		return;
> +	RCU_INIT_POINTER(ctrl->input, NULL);
> +	synchronize_rcu();
> +	input_unregister_device(input);
> +}
> +
> +static void gamecube_work_connect_cb(struct work_struct *work)
> +{
> +	struct gamecube_ctrl *ctrl = container_of(work, struct gamecube_ctrl, work_connect);
> +	struct input_dev *input;
> +	unsigned long irq_flags;
> +	unsigned int num = ctrl - ctrl->adpt->ctrls;
> +	u8 type;
> +
> +	spin_lock_irqsave(&ctrl->flags_lock, irq_flags);
> +	type = ctrl->flags & GC_TYPES;
> +	spin_unlock_irqrestore(&ctrl->flags_lock, irq_flags);
> +
> +	rcu_read_lock();
> +	input = rcu_dereference(ctrl->input);
> +	rcu_read_unlock();
> +
> +	if (type && input) {
> +		hid_info(ctrl->adpt->hdev, "port %u: already connected\n", num + 1);
> +	} else if (type) {
> +		hid_info(ctrl->adpt->hdev, "port %u: controller plugged in\n", num + 1);
> +		gamecube_ctrl_create(ctrl);
> +	} else if (input) {
> +		hid_info(ctrl->adpt->hdev, "port %u: controller unplugged\n", num + 1);
> +		gamecube_ctrl_destroy(ctrl);
> +	}
> +}
> +
> +static void gamecube_ctrl_handle_report(struct gamecube_ctrl *ctrl, u8 *data)
> +{
> +	struct input_dev *dev;
> +	u16 btns = data[1] << 8 | data[2];
> +	u8 old_flags, new_flags = data[0];
> +	unsigned long irq_flags;
> +
> +	spin_lock_irqsave(&ctrl->flags_lock, irq_flags);
> +	old_flags = ctrl->flags;
> +	ctrl->flags = new_flags;
> +	spin_unlock_irqrestore(&ctrl->flags_lock, irq_flags);
> +
> +	if ((new_flags & GC_TYPES) != (old_flags & GC_TYPES)) {
> +		schedule_work(&ctrl->work_connect);
> +		return;
> +	}
> +	if (!(new_flags & GC_TYPES))
> +		return;
> +
> +	rcu_read_lock();
> +	dev = rcu_dereference(ctrl->input);
> +	if (!dev)
> +		goto unlock;
> +
> +	input_report_key(dev, BTN_START, btns & GC_BTN_START);
> +	input_report_key(dev, BTN_TR2, btns & GC_BTN_Z);
> +	input_report_key(dev, BTN_TR, btns & GC_BTN_R);
> +	input_report_key(dev, BTN_TL, btns & GC_BTN_L);
> +	input_report_key(dev, BTN_SOUTH, btns & GC_BTN_A);
> +	input_report_key(dev, BTN_WEST, btns & GC_BTN_B);
> +	input_report_key(dev, BTN_EAST, btns & GC_BTN_X);
> +	input_report_key(dev, BTN_NORTH, btns & GC_BTN_Y);
> +	input_report_key(dev, BTN_DPAD_LEFT, btns & GC_BTN_DPAD_LEFT);
> +	input_report_key(dev, BTN_DPAD_RIGHT, btns & GC_BTN_DPAD_RIGHT);
> +	input_report_key(dev, BTN_DPAD_DOWN, btns & GC_BTN_DPAD_DOWN);
> +	input_report_key(dev, BTN_DPAD_UP, btns & GC_BTN_DPAD_UP);
> +	input_report_abs(dev, ABS_X, data[3]);
> +	input_report_abs(dev, ABS_Y, 255 - data[4]);
> +	input_report_abs(dev, ABS_RX, data[5]);
> +	input_report_abs(dev, ABS_RY, 255 - data[6]);
> +	input_report_abs(dev, ABS_Z, data[7]);
> +	input_report_abs(dev, ABS_RZ, data[8]);
> +	input_sync(dev);
> +
> +unlock:
> +	rcu_read_unlock();
> +}
> +
> +static int gamecube_hid_event(struct hid_device *hdev,
> +				  struct hid_report *report, u8 *raw_data, int size)
> +{
> +	struct gamecube_adapter *adpt = hid_get_drvdata(hdev);
> +	unsigned int i;
> +
> +	if (size < 1)
> +		return -EINVAL;
> +	if (size == GC_INPUT_REPORT_SIZE && raw_data[0] == GC_INPUT_REPORT) {
> +		for (i = 0; i < 4; i++)
> +			gamecube_ctrl_handle_report(adpt->ctrls + i, raw_data + 1 + 9 * i);
> +	} else {
> +		hid_warn(hdev, "unhandled event\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static struct gamecube_adapter* gamecube_adpt_create(struct hid_device *hdev)
> +{
> +	struct gamecube_adapter *adpt;
> +	unsigned int i;
> +
> +	adpt = kzalloc(sizeof(*adpt), GFP_KERNEL);
> +	if (!adpt)
> +		return NULL;
> +
> +	adpt->hdev = hdev;
> +	hid_set_drvdata(hdev, adpt);
> +
> +	for (i = 0; i < 4; i++) {
> +		adpt->ctrls[i].adpt = adpt;
> +		INIT_WORK(&adpt->ctrls[i].work_connect, gamecube_work_connect_cb);
> +		spin_lock_init(&adpt->ctrls[i].flags_lock);
> +	}
> +
> +	return adpt;
> +}
> +
> +static void gamecube_adpt_destroy(struct gamecube_adapter* adpt)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < 4; i++) {
> +		gamecube_ctrl_destroy(adpt->ctrls + i);
> +	}
> +	hid_hw_close(adpt->hdev);
> +	hid_hw_stop(adpt->hdev);
> +	kfree(adpt);
> +}
> +
> +/* This is needed, as by default the URB buffer size is set to 38, which is
> + * one byte too long and will result in EOVERFLOW failures.
> + */
> +static int gamecube_fixup_urb_in(struct gamecube_adapter *adpt)
> +{
> +	struct hid_device *hdev = adpt->hdev;
> +	struct usbhid_device *usbhid;
> +
> +	if (!hid_is_using_ll_driver(hdev, &usb_hid_driver))
> +		return -EINVAL;
> +	usbhid = hdev->driver_data;
> +	if (usbhid->urbin->transfer_buffer_length < GC_INPUT_REPORT_SIZE)
> +		return -EINVAL;
> +	usbhid->urbin->transfer_buffer_length = GC_INPUT_REPORT_SIZE;
> +	return 0;
> +}
> +
> +static int gamecube_hid_probe(struct hid_device *hdev,
> +				const struct hid_device_id *id)
> +{
> +	struct gamecube_adapter *adpt;
> +	int ret;
> +
> +	adpt = gamecube_adpt_create(hdev);
> +	if (!adpt) {
> +		hid_err(hdev, "Can't alloc device\n");
> +		return -ENOMEM;
> +	}
> +
> +	ret = hid_parse(hdev);
> +	if (ret) {
> +		hid_err(hdev, "HID parse failed\n");
> +		goto err;
> +	}
> +
> +	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> +	if (ret) {
> +		hid_err(hdev, "HW start failed\n");
> +		goto err;
> +	}
> +
> +	ret = hid_hw_open(hdev);
> +	if (ret) {
> +		hid_err(hdev, "cannot start hardware I/O\n");
> +		goto err_stop;
> +	}
> +
> +	ret = gamecube_fixup_urb_in(adpt);
> +	if (ret) {
> +		hid_err(hdev, "failed to fix input URB\n");
> +		goto err_close;
> +	}
> +
> +	ret = gamecube_send_cmd_init(hdev);
> +	if (ret < 0) {
> +		hid_err(hdev, "failed to send init command\n");
> +		goto err_close;
> +	}
> +
> +	hid_info(hdev, "new adapter registered\n");
> +	return 0;
> +
> +err_close:
> +	hid_hw_close(hdev);
> +err_stop:
> +	hid_hw_stop(hdev);
> +err:
> +	kfree(adpt);
> +	return ret;
> +}
> +
> +static void gamecube_hid_remove(struct hid_device *hdev)
> +{
> +	struct gamecube_adapter *adpt = hid_get_drvdata(hdev);
> +
> +	hid_info(hdev, "adapter removed\n");
> +	gamecube_adpt_destroy(adpt);
> +}
> +
> +static const struct hid_device_id gamecube_hid_devices[] = {
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO,
> +			 USB_DEVICE_ID_NINTENDO_GAMECUBE_ADAPTER) },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(hid, gamecube_hid_devices);
> +
> +static struct hid_driver gamecube_hid_driver = {
> +	.name		= "gamecube-adapter",
> +	.id_table	= gamecube_hid_devices,
> +	.probe		= gamecube_hid_probe,
> +	.remove		= gamecube_hid_remove,
> +	.raw_event	= gamecube_hid_event,
> +};
> +module_hid_driver(gamecube_hid_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("François-Xavier Carton <fx.carton91@gmail.com>");
> +MODULE_DESCRIPTION("Driver for Nintendo Gamecube Controller Adapters");
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index b18b13147a6f..1ebea811ea3b 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -882,6 +882,7 @@
>  #define USB_VENDOR_ID_NINTENDO		0x057e
>  #define USB_DEVICE_ID_NINTENDO_WIIMOTE	0x0306
>  #define USB_DEVICE_ID_NINTENDO_WIIMOTE2	0x0330
> +#define USB_DEVICE_ID_NINTENDO_GAMECUBE_ADAPTER	0x0337
>  
>  #define USB_VENDOR_ID_NOVATEK		0x0603
>  #define USB_DEVICE_ID_NOVATEK_PCT	0x0600
> -- 
> 2.26.2
> 

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

* Re: [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter
  2020-05-06  0:47 [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter François-Xavier Carton
                   ` (2 preceding siblings ...)
  2020-05-09 23:50 ` [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter François-Xavier Carton
@ 2020-05-10 12:45 ` Bastien Nocera
  2020-05-11 14:09   ` Ethan Lee
  3 siblings, 1 reply; 9+ messages in thread
From: Bastien Nocera @ 2020-05-10 12:45 UTC (permalink / raw)
  To: François-Xavier Carton, linux-input; +Cc: Ethan Lee

On Wed, 2020-05-06 at 02:47 +0200, François-Xavier Carton wrote:
> The hid-gamecube-adapter driver supports Nintendo Gamecube Controller
> Adapters.  They are USB devices on which up to four Nintendo Gamecube
> Controllers can be plugged. The driver create independent input
> devices
> as controllers are connected.

I think Ethan might be interested in testing this, as he's been using a
user-space version of that in the past:
https://patchwork.kernel.org/patch/11530107/


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

* Re: [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter
  2020-05-10 12:45 ` Bastien Nocera
@ 2020-05-11 14:09   ` Ethan Lee
  2020-05-11 23:23     ` François-Xavier Carton
  0 siblings, 1 reply; 9+ messages in thread
From: Ethan Lee @ 2020-05-11 14:09 UTC (permalink / raw)
  To: Bastien Nocera, François-Xavier Carton, linux-input

Looks just like the SDL hidapi driver so this should be okay! The only 
thing I'm unsure of is the axis inversion, in SDL we read the axes 
directly and invert them in the SDL_GameController configuration. 
Someone also added a bunch of dead zone work after I wrote the driver, 
but unfortunately I don't know the details of that.

The meat of our driver is here...

https://hg.libsdl.org/SDL/file/4298bf108b06/src/joystick/hidapi/SDL_hidapi_gamecube.c#l226

... and the SDL_GameController config is here:

https://hg.libsdl.org/SDL/file/4298bf108b06/src/joystick/SDL_gamecontrollerdb.h#l614

The '~' char denotes an input value that is flipped after it is read 
from the joystick; the vendor/product/version matchup may cause this 
config to accidentally flip the axes twice when using the kernel driver.

-Ethan

On 5/10/20 08:45, Bastien Nocera wrote:
> On Wed, 2020-05-06 at 02:47 +0200, François-Xavier Carton wrote:
>> The hid-gamecube-adapter driver supports Nintendo Gamecube Controller
>> Adapters.  They are USB devices on which up to four Nintendo Gamecube
>> Controllers can be plugged. The driver create independent input
>> devices
>> as controllers are connected.
> I think Ethan might be interested in testing this, as he's been using a
> user-space version of that in the past:
> https://patchwork.kernel.org/patch/11530107/
>


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

* Re: [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter
  2020-05-11 14:09   ` Ethan Lee
@ 2020-05-11 23:23     ` François-Xavier Carton
  2020-05-12  0:22       ` Ethan Lee
  0 siblings, 1 reply; 9+ messages in thread
From: François-Xavier Carton @ 2020-05-11 23:23 UTC (permalink / raw)
  To: Ethan Lee; +Cc: Bastien Nocera, François-Xavier Carton, linux-input

On Mon, May 11, 2020 at 10:09:28AM -0400, Ethan Lee wrote:
> Looks just like the SDL hidapi driver so this should be okay! The only 
> thing I'm unsure of is the axis inversion, in SDL we read the axes 
> directly and invert them in the SDL_GameController configuration. 
> Someone also added a bunch of dead zone work after I wrote the driver, 
> but unfortunately I don't know the details of that.
> 
> The meat of our driver is here...
> 
> https://hg.libsdl.org/SDL/file/4298bf108b06/src/joystick/hidapi/SDL_hidapi_gamecube.c#l226
> 

Thanks for the review and pointer to the SDL driver! I think the dead
zone min/max code is a better way to solve the dead zone issue than what
I did in the 3rd commit, as it is an automated solution. I'll implement
something like this.

> ... and the SDL_GameController config is here:
> 
> https://hg.libsdl.org/SDL/file/4298bf108b06/src/joystick/SDL_gamecontrollerdb.h#l614
> 
> The '~' char denotes an input value that is flipped after it is read 
> from the joystick; the vendor/product/version matchup may cause this 
> config to accidentally flip the axes twice when using the kernel driver.
> 

I tested this on my system and the axes are not getting inverted twice.
This is because the version component of the GUID doesn't match the one
in the db file. The db file has 00016800, whereas "sdl2-jstest --list"
reports 10010000 for my gamepad. Because the GUIDs don't match, the axes
are correct in sdl2-jstest. If I add an entry with my gamepad's GUID,
then the axes are indeed inverted twice.

I don't know why the first part is different (0001 != 1001), but the
second part is different because the SDL hidapi driver adds an 'h'
(0x68) to indicate that it's a hidapi device. See:

https://hg.libsdl.org/SDL/file/4298bf108b06/src/joystick/hidapi/SDL_hidapijoystick.c#l712

So as long as this code is here, the entry in the db file won't get
picked up if the kernel driver is used.

Also, I don't know much about SDL, but looking at sdl2-jstest, it seems
that there are two APIs: joysticks and gamepads. If the db file is only
used for the gamepad API, that means that the axes won't get inverted in
the joystick API if using the SDL hidapi driver. Is that the intended
behaviour?

-François-Xavier

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

* Re: [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter
  2020-05-11 23:23     ` François-Xavier Carton
@ 2020-05-12  0:22       ` Ethan Lee
  2020-05-12  2:27         ` François-Xavier Carton
  0 siblings, 1 reply; 9+ messages in thread
From: Ethan Lee @ 2020-05-12  0:22 UTC (permalink / raw)
  To: Bastien Nocera, linux-input

Oh good, that hidapi char should indeed prevent any conflicts - forgot 
that Sam had that in there. Once this driver is merged we can add a new 
entry to the database right away.

The way the gamepad system works in SDL is that there is SDL_Joystick, 
which provides direct access to devices, and SDL_GameController, which 
tries to take known hardware from SDL_Joystick and maps them to an 
Xbox-style button layout. For us it's generally considered okay for 
joysticks to not follow any standards, since you typically only access 
them for very specific kinds of devices (flight sim controllers, stuff 
like that) and those developers will often want their data as untouched 
as humanly possible, while most game developers will only touch 
SDL_GameController, which wrangles all the quirks of each device and the 
specifics won't matter as much.

-Ethan

On 5/11/20 19:23, François-Xavier Carton wrote:
> On Mon, May 11, 2020 at 10:09:28AM -0400, Ethan Lee wrote:
>> Looks just like the SDL hidapi driver so this should be okay! The only
>> thing I'm unsure of is the axis inversion, in SDL we read the axes
>> directly and invert them in the SDL_GameController configuration.
>> Someone also added a bunch of dead zone work after I wrote the driver,
>> but unfortunately I don't know the details of that.
>>
>> The meat of our driver is here...
>>
>> https://hg.libsdl.org/SDL/file/4298bf108b06/src/joystick/hidapi/SDL_hidapi_gamecube.c#l226
>>
> Thanks for the review and pointer to the SDL driver! I think the dead
> zone min/max code is a better way to solve the dead zone issue than what
> I did in the 3rd commit, as it is an automated solution. I'll implement
> something like this.
>
>> ... and the SDL_GameController config is here:
>>
>> https://hg.libsdl.org/SDL/file/4298bf108b06/src/joystick/SDL_gamecontrollerdb.h#l614
>>
>> The '~' char denotes an input value that is flipped after it is read
>> from the joystick; the vendor/product/version matchup may cause this
>> config to accidentally flip the axes twice when using the kernel driver.
>>
> I tested this on my system and the axes are not getting inverted twice.
> This is because the version component of the GUID doesn't match the one
> in the db file. The db file has 00016800, whereas "sdl2-jstest --list"
> reports 10010000 for my gamepad. Because the GUIDs don't match, the axes
> are correct in sdl2-jstest. If I add an entry with my gamepad's GUID,
> then the axes are indeed inverted twice.
>
> I don't know why the first part is different (0001 != 1001), but the
> second part is different because the SDL hidapi driver adds an 'h'
> (0x68) to indicate that it's a hidapi device. See:
>
> https://hg.libsdl.org/SDL/file/4298bf108b06/src/joystick/hidapi/SDL_hidapijoystick.c#l712
>
> So as long as this code is here, the entry in the db file won't get
> picked up if the kernel driver is used.
>
> Also, I don't know much about SDL, but looking at sdl2-jstest, it seems
> that there are two APIs: joysticks and gamepads. If the db file is only
> used for the gamepad API, that means that the axes won't get inverted in
> the joystick API if using the SDL hidapi driver. Is that the intended
> behaviour?
>
> -François-Xavier



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

* Re: [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter
  2020-05-12  0:22       ` Ethan Lee
@ 2020-05-12  2:27         ` François-Xavier Carton
  0 siblings, 0 replies; 9+ messages in thread
From: François-Xavier Carton @ 2020-05-12  2:27 UTC (permalink / raw)
  To: Ethan Lee; +Cc: Bastien Nocera, linux-input

I see, that makes sense.


On Mon, May 11, 2020 at 08:22:44PM -0400, Ethan Lee wrote:
> Oh good, that hidapi char should indeed prevent any conflicts - forgot 
> that Sam had that in there. Once this driver is merged we can add a new 
> entry to the database right away.
> 
> The way the gamepad system works in SDL is that there is SDL_Joystick, 
> which provides direct access to devices, and SDL_GameController, which 
> tries to take known hardware from SDL_Joystick and maps them to an 
> Xbox-style button layout. For us it's generally considered okay for 
> joysticks to not follow any standards, since you typically only access 
> them for very specific kinds of devices (flight sim controllers, stuff 
> like that) and those developers will often want their data as untouched 
> as humanly possible, while most game developers will only touch 
> SDL_GameController, which wrangles all the quirks of each device and the 
> specifics won't matter as much.
> 
> -Ethan

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

end of thread, other threads:[~2020-05-12  2:26 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-06  0:47 [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter François-Xavier Carton
2020-05-06  0:48 ` [PATCH 2/3] HID: gamecube-adapter: add rumble support François-Xavier Carton
2020-05-06  0:48 ` [PATCH 3/3] HID: gamecube-adapter: make axis limits parameters François-Xavier Carton
2020-05-09 23:50 ` [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter François-Xavier Carton
2020-05-10 12:45 ` Bastien Nocera
2020-05-11 14:09   ` Ethan Lee
2020-05-11 23:23     ` François-Xavier Carton
2020-05-12  0:22       ` Ethan Lee
2020-05-12  2:27         ` François-Xavier Carton

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.